[ 
https://issues.apache.org/jira/browse/LUCENE-1473?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Michael Busch updated LUCENE-1473:
----------------------------------

    Attachment: custom-externalizable-reader.patch

I really wouldn't want to add another backwards-compatibility
requirement to Lucene, as the others already stated. Often in the past
was ensuring backwards-compatibility the part of writing patches that
took the longest and involved the most discussions.

But maybe we can come up with a fair compromise here. What if we
change the classes that currently implement Serializable, so that they
implement Externalizable and add a suid as Jason suggests. But we make
clear in the javadocs that we don't support backwards-compatiblity
here, so e.g. a Term externalized with Lucene 2.9 can't be read with
3.0, only with 2.9.
Then we add a new class CustomExternalizableReader to util:
{code:java}
public abstract class CustomExternalizableReader {
  public abstract void readExternal(Object obj, ObjectInput in)
      throws IOException, ClassNotFoundException;
} 
{code}

add a package-private, static variable of this type to a class that
implements Externalizable and implement the deserialization code in a
default instance of such a reader. This could look like this:
{code:java}
public class SomeClass implements Externalizable {
  private int one;
  private int two;

...

  static CustomExternalizableReader extReader = new 
CustomExternalizableReader() {
    public void readExternal(Object obj, ObjectInput in) throws IOException,
        ClassNotFoundException {
      SomeClass s = (SomeClass) obj;
      long uid = in.readLong();
      if (uid != serialVersionUID) {
        throw new IOException("Wrong serialVerionUID: " + uid);
      }
      int one = in.readInt();
      int two = in.readInt();
      s.init(one, two);
    }
  };

  // initialization method for readExternal
  void init(int one, int two) {
    this.one = one;
    this.two = two;
  }
{code}

Note that I also specified an init() method. Since both init() and
extReader are both package-private, they are not protected by our
backwards-compatibility policy and we can change them in any release.

Now if in the next version of this class we add a new variable 'three'
we have to change init() and the reader:

{code:java}
public class SomeClassNewVersion implements Externalizable {
  private int one;
  private int two;
  private int three;

  static final long serialVersionUID = 2L;

  public void readExternal(ObjectInput in) throws IOException,
      ClassNotFoundException {
    extReader.readExternal(this, in);
  }

  public void writeExternal(ObjectOutput out) throws IOException {
    out.writeLong(serialVersionUID);
    out.writeInt(one);
    out.writeInt(two);
    out.writeInt(three);
  }

  /**
   * This reader can only read the externalized format created with the same
   * version of this class. If backwards-compatibility is desired, a custom
   * reader has to be implemented.
   */
  static CustomExternalizableReader extReader = new 
CustomExternalizableReader() {
    public void readExternal(Object obj, ObjectInput in) throws IOException,
        ClassNotFoundException {
      SomeClassNewVersion s = (SomeClassNewVersion) obj;
      long uid = in.readLong();
      if (uid != serialVersionUID) {
        throw new IOException("Wrong serialVerionUID: " + uid);
      }
      int one = in.readInt();
      int two = in.readInt();
      int three = in.readInt();
      s.init(one, two, three);
    }
  };

  void init(int one, int two, int three) {
    this.one = one;
    this.two = two;
    this.three = three;
  }
{code}

Now if someone tries to deserialize an object that was written with
an old Lucene version, an exception will be thrown.

But the user can simply implement an own, backwards-compatible reader:

{code:java}
    // Now the user implements their own backwards compatible reader
    SomeClassNewVersion.extReader = new CustomExternalizableReader() {
      public void readExternal(Object obj, ObjectInput in) throws IOException,
          ClassNotFoundException {
        SomeClassNewVersion c_new = (SomeClassNewVersion) obj;
        long uid = in.readLong();
        int one = in.readInt();
        int two = in.readInt();
        int three;
        if (uid == 1) {
          // old version - initialze with default value
          three = -3;
        } else {
          // new version
          three = in.readInt();
        }
        c_new.init(one, two, three);
      }
    };
{code}

With this approach we have to clearly document in the javadocs the
externalization format. Also if externalizable classes contain private
inner classes that need to be serialized, then those inner classes
have to be made package-private.

The nice thing here is that we allow backwards-compatibility, but push
the burden of maintaining it to the user.

I coded this all up as an example that I'm attaching here. Let me know
what you think, please. The patch file contains a Demo.java with a main
method that demonstrates what I'm proposing here.

> Implement standard Serialization across Lucene versions
> -------------------------------------------------------
>
>                 Key: LUCENE-1473
>                 URL: https://issues.apache.org/jira/browse/LUCENE-1473
>             Project: Lucene - Java
>          Issue Type: Bug
>          Components: Search
>    Affects Versions: 2.4
>            Reporter: Jason Rutherglen
>            Priority: Minor
>         Attachments: custom-externalizable-reader.patch, LUCENE-1473.patch, 
> LUCENE-1473.patch, LUCENE-1473.patch, LUCENE-1473.patch
>
>   Original Estimate: 8h
>  Remaining Estimate: 8h
>
> To maintain serialization compatibility between Lucene versions, 
> serialVersionUID needs to be added to classes that implement 
> java.io.Serializable.  java.io.Externalizable may be implemented in classes 
> for faster performance.

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to