Nice, Peter Sent from my iPhone
On Oct 12, 2011, at 6:57 PM, Peter Firmstone <j...@zeus.net.au> wrote: > Serialization has an undeserving bad reputation, perhaps caused by too many > developers just adding implements Serializable and accepting the default > serialized form in public API, then turning around and saying they won't > support backward compatible Serialization. > > In the implementation discussed below all objects are using just one public > API class with static factory methods, to keep it simple for user developers. > > I've been adding serialization to reference collections (just a bunch of > wrapper classes that encapsulate any collection framework interface and > perform the boilerplate of retrieving referents, wrapping them in references > and removing enqueued references from those collections, allowing the choice > of Weak, Soft, Strong references with identity, equals and comparable > semantics. > > All the following package private wrapper classes share a single serialized > form at present: > > ReferenceCollection > ReferenceList > ReferenceSet > ReferenceSortedSet > ReferenceNavigableSet > ReferenceQueue > ReferenceDeque > ReferenceBlockingQueue > ReferenceBlockingDeque > > The serial form is generated using writeReplace, and it recreates the correct > collection using ReadResolve. > > Now because each wrapper class is only publicly visible to the client as a > java collection framework (JCF) interface, the serialized form (also called a > serialization proxy), rebuilds it using the standard public api factory class > during de-serialization, based on the JCF interface it implemented. So the > remote end is free to use another implementation. > > Now there's a readResolve bug worth mentioning here, with regard to circular > references. writeReplace replaces all original object instances with your > serialized form object, but readResolve doesn't replace circular referenced > objects during de serialization. So if you're utilising readResolve to > replace your serialized form, you'll end up with a mix of the serialized form > object and your freshly constructed implementation object. You'll get > ClassCastExceptions etc... > > Bob Lee, that's Crazy Bob from JSR330 and Google Guice, came up with the idea > of having the serialization proxy and original objects share the same > interface, then having all methods redirected to the newly built object upon > de-serialization. > > So to implement that, I've got an inheritance hierarchy for the serialization > proxy, to separate each function: > > SerializationOfReferenceCollection > | > ReadResolveFixCollectionCircularReferences > | > ReferenceCollectionRefreshAfterSerialization > | > ReferenceCollectionSerialData > > Now right about now, you're probably saying 4 classes in an inheritance > hierarchy is a bit heavy for serialization? > > Well no, not when you consider: they serialize 9 classes, and of all those > classes, only one, ReferenceCollection has to implement a final writeReplace > method, while all have to implement a readObject method that throws an > exception to prevent direct de-serialization. > > So all the 9 classes are freed from the implementation of Serialization, it's > now the responsibility of the 4 classes in the serialization proxy > (Serialization builder pattern) inheritance hierarchy. > > Function of each class in the inheritance hierarchy: > > SerializationOfReferenceCollection is an abstract class with a static factory > method. > > ReadResolveFixCollectionCircularReferences implements all the JCF collection > based interfaces and redirects their calls to the ReferenceCollection > implementation built during de-serialization. > > ReferenceCollectionRefreshAfterSerialization, updates all the References > contained by the collection so they belong to the same garbage collection > ReferenceQueue and creates new References for all referents. > > ReferenceCollectionSerialData, contains the fields transferred during > serialization and implements abstract methods for the super classes to "get" > these fields. > > Now the interesting part is, I'm considering having three different > serialized form's, each with a different purpose, the client can choose from: > > 1. A Non serialization class, that prevents serialization, where a developer > want's to prevent access to serialized state. > > 2. The default serial data. > > 3. Defensive copying of serial data, to prevent stolen references to internal > state during de-serialization. > > The choice between the three serial states can be left until runtime, > the recipient of these objects when serialized doesn't have a choice which > serial form is used, only the creator of the original object does. > > Items 1 and 3 would only be used in a local sense, where a client program > might try to use serialization to gain access to internal implementation > state. > > Item 2 would be used in a genuine distributed environment, over a secure > connection, where there is no point using defensive copy's. > > I've only implemented Item 2 of course, I decided that while it is possible > to do 1 and 3 as well to demonstrate just how flexible serialization can be, > it wasn't warranted based on that alone. It will be possible to do this at > some point in future, or to change the serial form in a non compatible > manner, by adding a new serial form class, while retaining the original, so > that both the old and new serial forms can be de-serialized. > > When you apply Object design principals of responsibility, even serialization > can be flexible. > > Serialized Form lock in, is the same as inappropriate use of public fields or > other poor programming practices. Note that there are times where standard > rules don't apply like the use of public fields in Entry's, which is totally > appropriate, just as accepting the standard serial form in package private > classes is appropriate too. > > Cheers, > > Peter. >