Callbacks from servers to clients are commonly used in distributed applications, often for notification purposes (such as the completion of a long-running calculation or a change to a database record). Unfortunately, supporting callbacks in a complicated network environment presents its own set of problems, as described in
Section 42.2.1. Ice overcomes these obstacles using a Glacier2 router and bidirectional connections.
While a regular unrouted connection allows requests to flow in only one direction (from client to server), a bidirectional connection enables requests to flow in both directions. This capability is necessary to circumvent the network restrictions discussed in
Section 42.2.1, namely, client-side firewalls that prevent a server from establishing an independent connection directly to the client. By sending callback requests over the existing connection from the client to the server (more accurately, from the client to the router), we have created a virtual connection back to the client.
Figure 42.6 illustrates the steps involved in making a callback using Glacier2.
The arrows in Figure 42.6 indicate the flow of requests; notice that two connections are used between the router and the server. Since the server is unaware of the router, it does not use routed proxies, and therefore does not use bidirectional connections.
It is also possible for applications to manually configure bidirectional connections without the use of a router. See
Section 36.7 for more information on bidirectional connections.
When a client terminates, it closes its connection to the router. If a server later attempts to make a callback to the client, the attempt fails because the router has no connection to the client over which to forward the request. This situation is no worse than if the server attempted to contact the client directly, which would be prevented by the client firewall. However, this illustrates the inherent limitation of bidirectional connections: the lifetime of a client’s callback proxy is bounded by the lifetime of the client’s router session.
In order for the router to support callbacks from servers, it needs to have endpoints in the private network. The configuration file shown below adds the property
Glacier2.Server.Endpoints:
A client that receives callbacks is also a server, and therefore must have an object adapter. Typically, an object adapter has endpoints in the local network, but those endpoints are of no use to a server in our restricted network environment. We really want the client’s callback proxy to contain the router’s server endpoints, and we accomplish that by configuring the client’s object adapter with a proxy for the router
1. We supply the router’s proxy by creating the object adapter with
createObjectAdapterWithRouter, or by defining an object adapter property as shown below:
For each object adapter, the Ice run time maintains a list of endpoints that are embedded in proxies created by that adapter (see
Section 32.4.6). Normally, this list simply contains the local endpoints defined for the object adapter but, when the adapter is configured with a router, the list only contains the router’s server endpoints. When using a router, this object adapter allows the client to service callback requests via the router. Because the adapter only contains the router’s server endpoints, this means that, if the client also wants to service requests via local (non-routed) endpoints, the client must create a separate adapter for these requests.
Glacier2 assigns a unique category to each client for use in the identities of the client’s callback objects. The client creates proxies that contain this identity category for back-end servers to use when making callback requests to the client. This category serves two purposes:
A client can obtain its assigned category by calling getCategoryForClient on the
Router interface as shown in the C++ example below:
If a router client intends to receive callbacks and make nested twoway invocations, it is important that the client be configured correctly. When using the thread pool concurrency model, you must increase the size of the client thread pool to at least two threads. See
Section 36.7.5 for more information.
If the client’s session times out, the next invocation raises ConnectionLostException. The client can recover from this situation by re-creating the session and re-creating the callback adapter (adding all the callback servants to the ASM of the re-created adapter).
The demo/Glacier2/callback example illustrates the use of callbacks with Glacier2. The
README file in the directory provides instructions on running the example, and comments in the configuration file describe the properties in detail.