Peter, I am really trying hard to understand what you find the most frustrating
about Serialization. I don't really see new mechanisms in use here, just a
repackaging/layering of the same things. In my history of dealing with version
compatibility with serialization, I've always done the following steps and
solved the problems I had created by not using an explicit serialVersionUID
field.
1. In general, I just change classes to have new fields and features that I
need for a new "version". I usually have at least two servers using a
particular object/class and so, deploying new -dl jar files, I end up with a
server that has old versions of classes, talking to a new client, with the new
-dl.jar files, that will bark out a Serialization error due to incompatible
serialVersionUID values.
2. In the above exception, are the serialVersionUIDs of the old version and the
new version. If there is really new compatibility issues related to how the
fields in the old version of the class, are used in the new version of the
class, there is no reason why serialVersionUID of the old class can not be used
in the new class.
3. As a general rule, I just don't initially specify a serialVersionUID in any
serializable classes, because I want to make sure I see failure between
versions, and can then "fix" the actual compatibility by either declaring that
they are compatible, or leaving them as incompatible.
4. When the classes are compatible, I just add the serialVersionUID field to
the new version of the class, and copy the old value out of the exception
message.
5. Once I've done this, the classes are compatible, and I now have to worry
about "initialization" of the new fields (if any, because it might just be new
methods that changed the serialVersionUID).
6. The most typical thing that I do, to fix the initialization issue, is what
I've discussed here before. If there is no "Object" field that is new and
uninitialized, or no "native" field, which would always have a non-default
value from the new cons activity, then I will add a new field that is the
"not-new-version-yet" indicator. It can just be a boolean, as in
private final boolean version2up = true;
so that the default cons activities will initialize it, but deserialization of
the old data, into the new class, will not. Then, I just provide
readObject(), or modify it to do the appropriate initialization/version upgrade
of the old data to the new data.
private void readObject( ObjectInputStream is ) throws IOException,
ClassNotFoundException {
is.defaultReadObject();
if( !version2up ) {
// initialize new data values and/or convert old data
values here.
}
}
This has always worked for me, and I've not really understood why everyone
always complains about versioning at this "level". Certainly, the layer below
this with object replacement and all the rest of the more dramatic object
lifecycle/version management can be used as well. The recent activity around
the Levels class, indicates times when you want to try and pass a different
"object" into the serialization stream, then what you are holding. So,
downgrading by reversing the above scenario might also be necessary in some
cases, and when that is necessary, you have to have a common root, like "Level"
is to "Levels" or else you can't make the the "old" or "don't have that class"
JVM instances do the right thing.
If you don't mind some hints into what (other) issues are the most frustrating
or unwieldy to you, that would help me understand how to look at what you are
doing here.
Gregg
On Feb 27, 2013, at 3:09 AM, Peter Firmstone <[email protected]> wrote:
> I've just uploaded this to svn, package org.apache.river.api.io
>
> This will blow your mind, how's this for simple mistake free alternative to
> Serialization?
>
> 3 examples of different construction methods are demonstrated:
>
> 1. Constructor
> 2. Static factory method
> 3. Builder
>
> All creation methods are public, fields are private and final, internal class
> state is not exposed and is free to evolve separately all invarients are
> checked during construction objects safely published on deserialisation.
>
> You can even replace your objects with alternatives that use completely
> different classes, provided referencing fields are declared to use a common
> superclass or interface.
>
> Serial form is fixed and it uses Externalizable, it's faster than
> serialization, there's no serialVersionUID required, just one little method
> to implement.
>
> We now have the ability to create immutable distributed value objects that
> are thread safe and can be evolved though public api.
>
> It took two nights to code and it worked first time!
>
> This is a game changer, plug this into JERI and we've got a perfect
> compliment to reflective proxy's.
>
> Penny for your thoughts?
>
> Peter.
>
> package tests.support;
>
> import java.io.Serializable;
> import org.apache.river.api.io.Distributed;
> import org.apache.river.api.io.SerialFactory;
>
> /**
> *
> * @author peter
> */
> public class DistributedObject implements Distributed {
>
> public static DistributedObject create(String str){
> return new DistributedObject(str);
> }
>
> private final String testString;
> /* 0 - constructor
> * 1 - static factory method
> * 2 - builder
> */
> private final int method;
>
> public DistributedObject(String str){
> testString = str;
> method = 0;
> }
>
> public DistributedObject(String str, int method){
> testString = str;
> this.method = method;
> }
>
> @Override
> public SerialFactory substitute() {
> Class[] signature = new Class[1];
> Object[] parameters = new Object[1];
> parameters[0] = testString;
> if (method == 0){
> signature[0] = String.class;
> return new SerialFactory(this.getClass(), null, signature,
> parameters );
> }
> if (method == 1){
> signature[0] = String.class;
> return new SerialFactory(this.getClass(), "create", signature,
> parameters);
> }
> if (method == 2){
> Builder builder = new Builder().setString(testString);
> return new SerialFactory(builder, "build", null, null);
> }
> return null;
> }
>
> public String toString(){
> return testString;
> }
>
> public static class Builder implements Serializable {
> private static final long serialVersionUID = 1L;
>
> private String str;
>
> public Builder(){
>
> }
>
> public Builder setString(String str){
> this.str = str;
> return this;
> }
>
> public DistributedObject build(){
> return new DistributedObject(str);
> }
> }
>
> }
>