Versioning

This note discusses the details of how we can introduce a versioning mechanism into the Java ORB for the purpose of fixing our current interoperability problems. The basic concept is that all ORBs that we have not yet shipped will include versioning information in every request, response, and IOR. The rest of this note discusses the exact format of the versioning information and how it can be interpreted to achieve interoperability.

Version Format

I have thought of a number of possibilities for the format of version information:

  1. single byte (as Ram suggested): I'm not sure we want to make it that limited.
  2. 4 bytes: plenty of range, or can do major+minor in the same space. Larger than 1, plus inflexible.
  3. string: very flexible encoding, but somewhat longer: typically would add at least 8 bytes, even if short.
  4. CDR encapsulation: can put any data type in there, but we would need a version encoding ID, making this too bulky (this is as big as a tagged component).
  5. self-describing sequence of octets. What I mean by this is that bit 7 in each octet is set to 1 if more octets follow, or 0 if it is the last octet.This is usually as small as case 1, while being completely open ended if we need to extend it later. Reading this from a CDR stream is simple enough: just keep calling read_byte until a result >0 is returned. Then the encoding is simply:

Note that for a version in the range 0-127, this is exactly the same as option 1.

As I stated previously, the reason I am thinking about the format of the version ID is if we need to know something like JDK version or OS in the version. Perhaps we should allow for this, but it's hard to decide how far to go. For example, we could represent a version by a string (or an equivalent octet sequence in option 5) in the following format:

   <major ORB version>:<minor ORB version>:<(java.version)>:<(os.name)>:<(os.arch)>:<(os.version>)

where (name) means the result of System.getProperty(name).

Given all of these considerations, I think we should use option 5 for the version ID. All of our ORBs will currently restrict the version to a single octet in the 0-127 range. The location of this data is discussed in "Object Key Formats".

We will use exactly the same form for the version in both the object key and in a new service context. The new service context ID should be allocated in the range 0x4e454f00-0x4e454f0f (see ptc/00-07-06.txt). So, let's just define another constant TAG_ORB_VERSION = 0x4e454f00 in ORBConstants for this service context tag. This service context should be present in all GIOP request and reply messages sent by NEW and NEWER ORBs. Its data will consist solely of a series of octets (no length is needed) encoded as above.

A service context is then encoded using a CDR encapsulation as follows:
ID (4 bytes)
Length (4 bytes)
endianness (1 byte)
1xxx
1xxx
...
0xxx

This is easy to read as follows:

  1. read the ID
  2. read the length
  3. read the octet array based on the length
  4. create an input stream from the octet array
  5. consume the endianness from the new input stream
  6. read and accumulate the version until we read a byte > 0

If we attempt to read past the end of the encapsulation, we get a marshaling error.

For the following discussion, we only need to distinguish the following cases based on the object version:

ORB Type

Definition

Encoding

Magic

FOREIGN Foreign ORB

none

N/A

OLD Old Sun ORB (JDK 1.2, RMI-IIOP Standard Extension, JDK 1.3, J2EE 1.0, 1.1, 1.2)

1

0xAFABCAFE

NEW JDK 1.3.1 ORB

2

0xAFABCAFF

NEWER JDK 1.4 or later ORB, including J2EE 1.3 or later.

3

0xAFABCB00

When the version in found in the object key, it defines the version of the ORB that created the IOR. When the version is found in a service context, it defines the version of the ORB that sent the GIOP request or reply. Note that the NEW and NEWER ORBs need different MAGIC values, since the object key structure is actually different in these cases. Also, only NEWER ORBs actually include a version in the object key: the version in the object key is implicit in the MAGIC value for OLD and NEW ORBs.

Object key formats

The current object key formats are as follows:

MAGIC = 0xAFABCAFE

Object Key Type

Field Name

Field Type

JIDL

MAGIC long
SCID long
ID sequence<octet>

POA

MAGIC long
SCID long
SERVERID long
ORBID long
POAID long
ID sequence<octet>

Proposed new formats for NEW (JDK 1.3.1) ORB. No versions in the object key because this version has a unique magic number:

MAGIC = 0xAFABCAFF

Object Key Type

Field Name

Field Type

JIDL

MAGIC long
SCID long
ID sequence<octet>

POA

MAGIC long
SCID long
SERVERID long
ORBID long
POAID long
ID sequence<octet>

New formats for NEWER (JDK 1.4 and later) ORBs:

MAGIC = 0xAFABCB00

Object Key Type

Field Name

Field Type

JIDL

MAGIC long
SCID long
ID sequence<octet>
VERSION n octets

POA

MAGIC long
SCID long
SERVERID long
ORBID string
POANAME sequence<string>
ID sequence<octet>
VERSION n octets

Note that all of these are marshaled according to CDR rules. Also, note that the previous discussion here about MAGIC was wrong: we simply MUST change the MAGIC every time any part of the object key structure changes (or else we need to introduce a new subcontract, but that is actually a larger change, and I do not want to have multiple versions of the same subcontract that differ only in trivial encoding details.)

Representing the Version

I don't want to expose the exact encoding of versions to all of the code that needs to check the version. Instead, I would like to define an ORBVersion interface as follows:

public interface ORBVersion {
	int FOREIGN = 0 ;
	int OLD = 1 ;
	int NEW = 2 ;
	int NEWER = 3 ;
	/** Return the ORB version type.
	* Always returns one of the constants defined in this interface
	*/
	int getORBType() ;
	/** Write the version to os as a series of properly encoded octets.
	void write( OutputStream os ) ;
}

This interface provides all of the version information that we currently need. It can be extended as needed in the future.

We also need a factory class:

public class ORBVersionFactory {
	private ORBVersionFactory() {} 
	/** Return the ORB version that this ORB is using.
	*/
	public static ORBVersion getORBVersion() 
	{ ... }
	/** Get the ORB version encoded in the input stream.
	*/
	public static ORBVersion create( InputStream is ) 
	{ ... }
}

The factory can be efficiently implemented rather easily by pre-allocating 4 instances of an implementation of ORBVersion. Since the ORBVersion does not need to be mutable, we can just share the pre-allocated instances as needed. Both of these ORBVersion classes should be identical in Ladybird and Merlin.

The ORB version service context also needs to be supported. To do this, we simply use the ORBVersionFactory to read the data when the service context is constructed. This simply requires adding a new ORBVersionServiceContext class to handle the service context.

Getting the ORB version from a GIOP request proceeds something like the following:

if (version service context is present)
	get version from service context
else if (request is SUN-1.0 protocol(see interoperability discussion))
	version is OLD
else
	version is FOREIGN

IOR version implementation

For getting the version from the IOR, we want something like:

ior.getProfile().getVersion()

so that the code using the version is independent of the different representations of IORs in Ladybird and Merlin. The details of how this is implemented are substantially different in the two code bases.

Implementation in Ladybird

In Ladybird, we will probably implement the versioning by going directly to the object key. We can do this as follows:

  1. Get the object key from the IOR profile.
  2. Convert the object key byte array into an InputStream.
  3. Read off the magic and the scid.
  4. Use the scid to determine whether the key is POA or JIDL.
  5. read off the rest of the data including the user ID.
  6. Hand the object key over to the ORBVersionFactory.
  7. return the ORBVersion.

I think we will just code this directly in core.Profile in Ladybird and not worry about the fact that too much of the code knows all about the object key formats. That problem is fixed in Merlin.

Implementation in Merlin/J2EE 1.3

Implementing IOR versioning in Merlin is pretty simple:

ObjectKeyTemplate
The basic interface for all object key templates. I will add a ORBVersion getVersion() method on this interface.
ObjectKeyTemplateBase
This abstract class factors out the common parts of the implementation of all object key templates. This is where getVersion will be implemented.
ObjectKeyFactory
This singleton factory class contains the code that determines what kind of object key template to create based on the magic and subcontract ids. It needs to be modified to handle two different forms of magic, and also to get the ORB version if it is present.

POAObjectKeyTemplate, JIDLObjectKeyTemplate, and WireObjectKeyTemplate are the three concrete classes that represent all possible object key templates in our ORB. They should be entirely unaffected by the version related changes.

Interoperability

We need a way to decide upon receiving a GIOP request what kind of ORB sent it. Since both NEW and NEWER ORBs will always send a Sun protocol version message, those cases are easy. Distinguishing FOREIGN and OLD is more difficult. The old (and non-standard) GIOP protocol always sent GIOP 1.0 requests with CodeSet and CodeBase components. Since these two components are not defined for GIOP 1.0, it is unlikely that a FOREIGN ORB would send such a request. Therefore, we will assume that any such message is from an OLD or NEW ORB, and NEW can be distinguished based on the version. I will refer to the OLD and NEW ORB protocol as SUN-1.0.

Then we can make everything interoperate across all versions as follows:

Invoking ORB

ORB of IOR

Receiving ORB

Detection Mechanisms

Protocol

Serialization

FOREIGN FOREIGN FOREIGN

DON'T' CARE

FOREIGN OLD OLD

DON'T CARE (probably does not interoperate)

FOREIGN NEW NEW Server: GIOP request is not SUN-1.0

compliant

compliant

FOREIGN NEW NEWER
FOREIGN NEWER NEWER
OLD FOREIGN FOREIGN

DON'T CARE (probably does not interoperate)

OLD OLD OLD

currently interoperable

OLD NEW NEW Server: GIOP request is SUN-1.0

SUN-1.0

old

OLD NEW NEWER
OLD NEWER NEWER
NEW FOREIGN FOREIGN Client: IOR does not contain magic

SUN-1.0

compliant

NEW OLD OLD Client: IOR contains old magic

old

NEW NEW NEW Client: IOR contains new magic, NEW version

compliant

NEW NEW NEWER Client: IOR contains new magic, NEW version

Server: receives target with NEW version
NEW NEWER NEWER Client: IOR contains new magic

Server: receives target with NEWER version
NEWER FOREIGN FOREIGN Client: IOR does not contain magic

compliant

compliant

NEWER OLD OLD Client: IOR contains old magic

SUN-1.0

old

NEWER NEW NEW Client: IOR contains new magic, NEW version

SUN-1.0

compliant

NEWER NEW NEWER Client: IOR contains new magic, NEW version

Server: receives target with NEW version

SUN-1.0

NEWER NEWER NEWER Client: IOR contains new magic, NEWER version

Server: receives target with NEWER version

compliant

Note that the versioning is complicated by the fact that an ORB supporting a POA may receive requests on persistent object references that were created by an ORB of an earlier version that supported the POA. We can detect this by checking the version of the target that was received in the GIOP request, which may be different from the version of the receiving ORB. We only support backwards compatibility here: it is not always possible to run a persistent server for an object reference on an ORB whose version is earlier than the version in the object reference. In particular, a persistent IOR created by a NEWER ORB cannot be served by a NEW ORB. The last two rows in the table are significant: the protocol accepted by the server is different in these two cases because the client can only chose the version based on the IOR, not on the actual version of the server.