Serialization is a useful
feature built into Java. It converts objects into a bytestream that can
be saved to a file or transmitted over a network and then reconstructed
later into the same object (perhaps in a different Java
VM). The basics are
with some more advanced features
and the complete API is
Despite all this documentation, I recently came a across a problem not
described. What if an object being deserialized requires a reference to
some non-serializable object in the
application. That is how can an object be reconstructed when it contains part of the application’s context that can’t be serialized. For instance, what if the object has a network connection as a member variable. The network connection can not be meaningfully serialized, so when the object is deserialized it has somehow reconnect itself to the network, which requires some reference to the network context. This may sound like a contrived example, but it occurs in the Red Dwarf Server (previously known as Sun’s Darkstar project) where network connections are transparently moved around a server cluster.
The best way to solve this problem is to ensure that the serializable objects are self-contained and do not need any outside context when deserialized. Let’s assume that is not possible and move on. Next best is to hook the object to its required context immediately after the deserialization. However, this may not be practicable because the deserialized object is not easily accessible (for example, if it is deep in an object graph - which is the case in Red Dwarf and my code).
Red Dwarf solves the problem by defining a static accessor on the context class and then on the serializable object adding a serialization override method like the below:
This solution works, but I don’t like it. Creating a static context makes it hard to write tests and add a second context if required (I have been caught out by this before). It could improved by making the static context use ThreadLocal storage, but there are other ways.
Checking out the Java serialization source
I thought there may be some way of extending the deserialization
process. The main extension points seem to be the
readResolve() methods added to the serializable object. These are
found by reflection on their signature in a private method and can not
be changed. There is a method
which allows a bespoke deserialization process to be defined. However,
so many of the methods on ObjectInputStream are private that this would
be like writing a new process from scratch.
There are two other options. The ObjectInputStream can be subclassed and
contain a reference to the context object. This new class can detected
readObject(ObjectInputStream in) with
instanceof and the
context dereferenced. An alternative is to keep the subclass of
ObjectInputStream and set
enableResolveObject(true) in the constructor
(this requires the program to have security permission). This means that
after deserialization the
resolveObject(Object obj) method on the
stream subclass will be called and the return value passed as the final
result of the deserialization. Thus this method can perform extra
initialisation or even replace the given object with a different one.
The code below shows both these techniques - you probably only want to
use one at a time.