The ObjectMismatchTrigger
interface is
show in Example 8.1, “ObjectMismatchTrigger interface”.
Example 8.1. ObjectMismatchTrigger interface
public interface ObjectMismatchTrigger { public void writeObjectToStream( ManagedObjectStreamClass remoteClassDescriptor, ObjectOutputStream out) throws IOException; public void readObjectFromStream( ManagedObjectStreamClass remoteClassDescriptor, ObjectInputStream in) throws IOException; }
It defines these methods:
These methods are only called on the node where the new class
version is installed. In all cases, the
remoteClassDescriptor
parameter contains a description
of the old version of the class. For example, in Figure 8.1, “Object mismatch method invocation”, the
remoteClassDescriptor
parameter always contains a
description of O1
.
The ManagedObjectStreamClass
provides
access to the remote serialVersionUID
for a class
using the getSerialVersionUID()
method. The version
information can be used to perform conditional mapping based on the
actual class version on a remote node. This makes it possible to support
multiple versions across a cluster. The
ObjectMismatchTrigger
interface can use the version
information associated with the remote class to conditionally map the
differences.
![]() | |
If no |
When supporting multiple deployed versions of a class, the upgrade utility must be run against the new class version and the most current old version. Not doing this may cause unresolved version mismatches are runtime.
The ManagedObjectStreamClass
contains
an ordered array of all fields, starting with the top most parent, to
the current child class where an
ObjectMismatchTrigger
method is called. These fields
must be processed in order. This is done using the
ManagedObjectStreamClass.getFields()
method.
The general approach to field mapping using the
ManagedObjectStreamClass
class is to iterate over all
of the fields returned by the
ManagedObjectStreamClass
and map them into the new
field definitions. The ManagedObjectStreamClass
always contains the field definitions for the old class version.
An alternative approach to field mapping is to use the
ObjectInputStream.GetField
and
ObjectOutputStream.PutField
classes. These classes
provide random access to fields by name. Again, the
ObjectInputStream.GetField
and
ObjectOutputStream.PutField
always contain the field
definitions for the old class version.
![]() | |
The use of the |
See the section called “Inheritance” for details on how
inheritance affects field processing. A field mapping example is shown
in Example 8.3, “Updated class definition”. This example shows
the use of both the ManagedObjectStreamClass
and the
ObjectInputStream.GetField
class.
Classes that extend other classes, must ensure that the parent class processes the object stream before the child. This can be done a couple of ways:
Use super
to call the parent's
implementation of the ObjectMismatchTrigger
method. This will only work if the inheritance hierarchy hasn't
changed between the old and new class versions.
Directly set the parent's field values. This is the only
option if the inheritance hierarchy has changed between the old and
new class versions. This does imply that a child must have access to
all parent class fields either because they are
protected
, or there are setters available.
When an ObjectMismatchTrigger
method is
executed, the method always has complete access to any parent class
fields in the old class version.
ObjectMismatchTrigger
methods are only called
on the leaf type of an inheritance hierarchy. They are not called on any
of the parent types.
Here is an example using a call to super
to
populate a parent's fields:
// // Both old and new class versions extend Base // class Child extends Base { public void readObjectFromStream( ManagedObjectStreamClass remoteClassDesc, ObjectInputStream in) throws IOException { // // Call the parent to process its fields (like a constructor, // this must be called before reading the child fields). // super.readObjectFromStream(remoteClassDesc, in); // // Process child fields // } }
Here is another example, where the class hierarchy has changed between the old and new class version.
// VERSION 1: @Managed class InheritBase implements ObjectMismatchTrigger, Serializable { int int_val; } class InheritChild extends InheritBase { byte [] byte_array; } class Inherit extends InheritChild { String [] str_array; } // // VERSION 2: collapsed the class hierachy into a single class // @Managed class Inherit implements ObjectMismatchTrigger, Serializable { int int_val; byte [] byte_array; String [] str_array; public void readObjectFromStream( ManagedObjectStreamClass remoteClassDesc, ObjectInputStream in) throws IOException { // // Process all fields directly here, this includes // the parent class fields (InheritBase & InheritChild) // in VERSION 1 // } }
Reflection must be used to set final
fields in a class in the readObjectFromStream
method.
The Java language prohibits the setting of final
fields after an object is created - but that is exactly what is required
when mapping a final
field in the
readObjectFromStream
method.
To set a final
field in
readObjectFromStream
requires code like the
following, where field id
is defined as
final
.
try { java.lang.reflect.Field f = this.getClass().getDeclaredField("id"); f.setAccessible(true); f.set(this, val); } catch (NoSuchFieldException ex) { throw new IOException(ex); } catch (IllegalAccessException ex) { throw new IOException(ex); }
Errors processing an object stream are reported using a
com.kabira.platform.DataError
exception. This
exception contains information on the value that was in error. For
example:
com.kabira.platform.DataError: Stream Corrupted: invalid type code: 0x77, expected type code: 0x70
The type code values are defined by the Java Serialization specification Object Serialization Stream Protocol chapter. The constant definitions are also found in the java.io.ObjectStreamConstants javadoc.
The error above indicates that a TC_BLOCKDATA
type code was seen when a TC_NULL
was expected. This
error was caused by attempting to write an integral type
(out.writeBytes()
) when a string was expected
(out.writeObject()
).