I would use the multiple classloaders approach.
(Package renaming will also work. It does seem ugly, but this is a one-off hack so beauty and correctness can take a back seat. Intermediate serialization seems risky - there was a reason you are using Kryo, and that reason will be negated by using a different intermediate form).
The overall design would be:
child classloaders: Old Kryo New Kryo <-- both with simple wrappers
\ /
\ /
\ /
\ /
|
default classloader: domain model; controller for the re-serialization
- Load the domain object classes in the default classloader
Load a Jar with the modified Kryo version and wrapper code. The wrapper has a static 'main' method with one argument: The name of the file to deserialize. Call the main method via reflection from the default classloader:
Class deserializer = deserializerClassLoader.loadClass("com.example.deserializer.Main"); Method mainIn = deserializer.getMethod("main", String.class); Object graph = mainIn.invoke(null, "/path/to/input/file");
- This method:
- Deserializes the file as one object graph
- Places the object into a shared space. ThreadLocal is a simple way, or returning it to the wrapper script.
- This method:
When the call returns, load a second Jar with the new serialization framework with a simple wrapper. The wrapper has a static 'main' method and an argument to pass the name of the file to serialize in. Call the main method via reflection from the default classloader:
Class serializer = deserializerClassLoader.loadClass("com.example.serializer.Main"); Method mainOut = deserializer.getMethod("main", Object.class, String.class); mainOut.invoke(null, graph, "/path/to/output/file");
- This method
- Retrieves the object from the ThreadLocal
- Serializes the object and writes it to the file
- This method
Considerations
In the code fragments, one classloader is created for each object serialization and deserialization. You probably want to load the classloaders only once, discover the main methods and loop over the files, something like:
for (String file: files) {
Object graph = mainIn.invoke(null, file + ".in");
mainOut.invoke(null, graph, file + ".out");
}
Do the domain objects have any reference to any Kryo class? If so, you have difficulties:
- If the reference is just a class reference, eg to call a method, then the first use of the class will load one of the two Kryo versions into the default classloader. This probably will cause problems as part of the serialization or deserialization might be performed by the wrong version of Kryo
- If the reference is used to instantiate any Kryo objects and store the reference in the domain model (class or instance members), then Kryo will actually be serializing part of itself in the model. This may be a deal-breaker for this approach.
In either case, your first approach should be to examine these references and eliminate them. One approach to ensure that you have done this is to ensure the default classloader does not have access to any Kryo version. If the domain objects reference Kryo in any way, the reference will fail (with a ClassNotFoundError if the class is referenced directly or ClassNotFoundException if reflection is used).