river-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Gregg Wonderly <gregg...@gmail.com>
Subject Re: Distributed objects; simpler and safer than Serializable
Date Wed, 27 Feb 2013 15:09:19 GMT
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 <jini@zeus.net.au> 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);
>        }
>    }
> 
> }
> 


Mime
View raw message