As we saw in the preceding sections, both the Ice protocol and encoding have separate major and minor version numbers. Separate versioning of protocol and encoding has the advantage that neither depends on the other: any version of the Ice protocol can be used with any version of the encoding, so they can evolve independently. (For example, Ice protocol version 1.1 could use encoding version 2.3, and vice versa.)
The Ice versioning mechanism provides the maximum possible amount of interoperability between clients and servers that use different versions of the Ice run time. In particular, older deployed clients can communicate with newer deployed servers and vice versa, provided that the message contents use types that are understandable to both sides.
For an example, assume that a later version of Ice were to introduce a new Slice keyword and data type, such as
complex, for complex numbers. This would require a new minor version number for the encoding; let us assume that version 1.1 of the encoding is identical to the 1.0 encoding but, in addition, supports the
complex type. We now have four possible combinations of client and server encoding versions:
As you can see, interoperability is provided to the maximum extent possible. If both client and server are at version 1.1, they can obviously exchange messages and will use encoding version 1.1. For version 1.0 clients and servers, obviously only operations that do not involve
complex parameters can be invoked (because at least one of client and server do not know about the new
complex type) and messages are exchanged using encoding version 1.0.
For versioning of the protocol and encoding to be possible, all versions (present and future) of the Ice run time adhere to a few ground rules:
These ground rules ensure that all current and future versions of the Ice run time can at least identify the version and size of an encapsulation and a message. This is particularly important for message switches such as IceStorm (see
Chapter 44); by keeping the version and size information in a fixed format, it is possible to forward messages that are, for example, at version 2.0, even though the message switch itself may still be at version 1.0.
To establish whether a particular protocol version is compatible with another protocol version (or a particular encoding version is compatible with another encoding version), the following rules apply:
2.
A receiver that advertises minor version n guarantees to be able to successfully decode all minor versions less than
n. Note that this does
not imply that messages using version
n−1 can be decoded as if they were version
n: as far as their physical representation is concerned, two adjacent minor versions can be completely incompatible. However, because any receiver advertising version
n is also obliged to correctly deal with version
n−1, minor version upgrades are
semantically backward compatible, even though their physical representation may be incompatible.
3.
A sender that supports minor version n guarantees to be able to send messages using all minor versions less than
n. Moreover, the sender guarantees that if it receives a request using minor version
k (with
k£n), it will send the reply for that request using minor version
k.
Client and server must somehow agree on which version to use to exchange messages. Depending on whether the underlying transport is connection-oriented or connection-less, different mechanisms are used to negotiate a common version.
For connection-oriented transports, the client opens a connection to the server and then waits for a validate connection message (see
page 1325). The validate connection message sent by the server indicates the server’s major and highest supported minor version numbers for both protocol and encoding. If the server’s and client’s major version numbers do not match, the client side raises an
UnsupportedProtocolException or
UnsupportedEncodingException.
Assuming that the client has received a validate connection message from the server that matches the client’s major version, the client knows the highest minor version number that is supported by the server. Thereafter, the client is obliged to send no message with a minor version number higher than the server’s limit. However, the client is free to send a message with a minor version number that is less than the server’s limit.
The server does not have a‑priori knowledge of the highest minor version that is supported by the client (because there is no validate connection message from client to server). Instead, the server learns about the client version number in each individual message, by looking at the message header. That minor version indicates the minor version number that the client can accept. The scope of that minor version number is a single request-reply interaction. For example, if the client sends a request with minor version 3, the server must reply to that request with minor version 3 as well. However, the next client request might be with minor version 2, and the server must reply to that request with minor version 2.
For orderly connection closure via a close connection message, the server can use any minor version, but that minor version must not be higher than the highest minor version number that was received from the client while the connection was open.
For connection-less transports, no validate connection message exists, so the client must learn about the highest supported minor version number of the server via other means. The mechanism for this depends on whether a proxy for a connection-less endpoint is bound directly or indirectly (see
page 11):
•
For indirect proxies, the proxy itself contains no version information at all (because the proxy contains no endpoints). Instead, the client obtains the version information when it resolves the proxy’s symbolic information to one or more endpoints (via IceGrid or an equivalent service). The version information of the endpoints determines the highest minor version number that is available to the client.