Re: CORBA IDL versioning, evolution, backward compatibility



There is no good answer. To maintain backward compatibility, you can
not modify the existing IDL.

Interfaces:

No additions, subtractions, or modifications to existing
operations.

Solution: Extend existing interfaces with new interfaces, and add
the new operations to the derived interface. New clients will use the
new interfaces, thus picking up the new operations. Older clients will
continue to work with the older interface.

interface MyServiceV2 : MyService {
// new operations
};

Note: Depending on the orb, you may be able to add additional
operations to the bottom of an interface, which results in less clutter
due to the elimination of the need for new interfaces. However, this is
generally non-interoperable, as all client orbs in your system must
support this capability. Existing client stubs will continue to work,
as they'll be oblivious to the new operations.

Structs: No modifications.

Solution: Create new structs by copying the fields from the old
structs (ugh), and adding the new fields, or create a "wrapper" struct
that contains the old struct. A wrapper struct is useful if you have an
adapter tier that can extract the old struct and send it to old clients
(useful for callback interfaces). However, wrapper structs become a
pain once you get a few nested levels deep. Also, if the struct
contains any "primary key" fields, they get buried in the nested
structures. One solution is to duplicate the key fields in the
outermost struct.

struct MyStructV2 {
MyStruct myStruct;
long aNewField;
};

I would avoid Anys, due to the additional overhead, loss of
semantics, and loss of compile time safety.

Enums: Enums can not be evolved.

Solution: Use typedefs with constants. New constants can be defined
with additional values without breaking existing code. The drawback is
that the server is responsible for checking that the values received
are indeed legal, as large integer values could be accidentally sent.
Also, client implementing callbacks must be prepared to deal with new
values if they haven't moved to the latest version.

// enum ProductStates { OPEN, CLOSED, HALTED {

typedef short ProductState;

interface ProductStates {
const ProductState OPEN = 1;
const ProductState CLOSED = 2;
const ProductState HALTED = 3;
};

// usage
struct ProductStruct {
short id;
string name;
ProductState state;
};


We've had decent luck with using an adapter tier, which shields the
clients from the server-side services. This allows us to internally
evolve our business services without having to expose every minor
change to the client. For major releases incorporating new client
functionality, we produce a new version of our public IDL. The adapter
tier exposes all versions of the interfaces, including those going back
to version 1. Unfortunately, we don't have complete control to force
users to migrate to the new interfaces. However, there's usually enough
value in the new features to encourage our clients to migrate at some
point, e.g. additional data carried in the callback methods.

Of course, you must deal with the issue of missing values. For example,
if you create a new operation that requires an additional parameter,
then the old operation must continue to work by substituting a default
value, either in the adapter tier (calling the server), or in the
server implementation. While some
operations may have natural default values, others may not. This
ultimately leads to the need to push clients to the new interfaces.

At one point we attempted to support 2 versions of the interfaces and
structs using custom Java class loaders, which modified the older
structs on-the-fly. Since we've implemented our own 2.3 compliant ORB,
we were able to do this under the covers. Technically, it was a
success. However, we abandoned the practice as it was a testing
nightmare, and almost encouraged developers to modify the IDL at will,
leaving the creation of the difficult conversion code to someone else.

Mark

.



Relevant Pages