The main entry point to the Ice run time is represented by the local interface Ice::Communicator. An instance of
Ice::Communicator is associated with a number of run-time resources:
Plug‑ins are objects that add features to a communicator. For example, IceSSL (see
Chapter 42) is implemented as a plug‑in. Each communicator has a plug‑in manager that implements the
Ice::PluginManager interface and provides access to the set of plug‑ins for a communicator.
Typically, servers use only a single communicator but, occasionally, multiple communicators can be useful. For example, IceBox (see
Chapter 44) uses a separate communicator for each Ice service it loads to ensure that different services cannot interfere with each other. Multiple communicators are also useful to avoid thread starvation: if one service runs out of threads, this leaves the remaining services unaffected.
module Ice {
local interface Communicator {
string proxyToString(Object* obj);
Object* stringToProxy(string str);
Object* propertyToProxy(string property);
Identity stringToIdentity(string str);
string identityToString(Identity id);
ObjectAdapter createObjectAdapter(string name);
ObjectAdapter createObjectAdapterWithEndpoints(
string name,
string endpoints);
void shutdown();
void waitForShutdown();
bool isShutdown();
void destroy();
// ...
};
// ...
};
Instead of calling proxyToString on the communicator, you can also use the
ice_toString operation on a proxy to stringify it (see
Section 32.11.2). However, you can only stringify non-null proxies that way—to stringify a null proxy, you must use
proxyToString. (The stringified representation of a null proxy is the empty string.)
Typically, an object adapter has a single transport endpoint. However, an object adapter can also offer multiple endpoints. If so, these endpoints each lead to the same set of objects and represent alternative means of accessing these objects. This is useful, for example, if a server is behind a firewall but must offer access to its objects to both internal and external clients; by binding the adapter to both the internal and external interfaces, the objects implemented in the server can be accessed via either interface.
Whereas createObjectAdapter determines its endpoints from configuration information (see
Section 32.4.6),
createObjectAdapterWithEndpoints allows you to specify the transport endpoints for the new adapter. Typically, you should use
createObjectAdapter in preference to
createObjectAdapterWithEndpoints. Doing so keeps transport-specific information, such as host names and port numbers, out of the source code and allows you to reconfigure the application by changing a property (and so avoid recompilation when a transport endpoint needs to be changed).
• Operation invocations that are in progress at the time shutdown is called are allowed to complete normally.
shutdown does
not wait for these operations to complete; when shutdown returns, you know that no new incoming requests will be dispatched, but operations that were already in progress at the time you called
shutdown may still be running. You can wait for still executing operations to complete by calling
waitForShutdown.
• Note that shutdown initiates deactivation of all object adapters associated with the communicator, so attempts to use an adapter once shutdown has completed raise an
ObjectAdapterDeactivatedException.
On the client side, waitForShutdown simply waits until another thread has called
shutdown or
destroy.
This operation returns true if shutdown has been invoked on the communicator. A return value of true does not necessarily indicate that the shutdown process has completed, only that it has been initiated. An application that needs to know whether shutdown is complete can call
waitForShutdown. If the blocking nature of
waitForShutdown is undesirable, the application can invoke it from a separate thread.
Calling destroy before leaving
main is necessary because
destroy waits for all running threads to terminate before it returns. If you leave
main without calling
destroy, you will leave
main with other threads still running; many threading packages do not allow you to do this and end up crashing your program.
If you call destroy without calling
shutdown, the call waits for all executing operation invocations to complete before it returns (that is, the implementation of
destroy implicitly calls
shutdown followed by
waitForShutdown).
shutdown (and, therefore,
destroy) deactivates all object adapters that are associated with the communicator. Since
destroy blocks until all operation invocations complete, a servant will deadlock if it invokes
destroy on its own communicator while executing a dispatched operation.
On the client side, calling destroy while operations are still executing causes those operations to terminate with a
CommunicatorDestroyedException.