Table of Contents Previous Next
Logo
Server-Side Slice-to-Objective‑C Mapping : 20.7 Object Incarnation
Copyright © 2003-2009 ZeroC, Inc.

20.7 Object Incarnation

Having created a servant class such as the rudimentary NodeI class in Section 20.4.2, you can instantiate the class to create a concrete servant that can receive invocations from a client. However, merely instantiating a servant class is insufficient to incarnate an object. Specifically, to provide an implementation of an Ice object, you must follow these steps:
1. Instantiate a servant class.
2. Create an identity for the Ice object incarnated by the servant.
3. Inform the Ice run time of the existence of the servant.
4. Pass a proxy for the object to a client so the client can reach it.

20.7.1 Instantiating a Servant

Instantiating a servant means to allocate an instance on the heap:
NodeI *servant = [NodeI nodei:@"Fred"];
This code creates a new NodeI instance. For this example, we used the convenience constructor we saw on page 579. Of course, you are not obliged to define such a constructor but, if you do not, you must explicitly call release or autorelease on the servant.
For the delegate servants we saw on page 580, instantiation would look as follows:
Intf1AndIntf2 *delegate = [Intf1AndIntf2 intf1AndIntf2];
ICEObject *servant = [ICEObject objectWithDelegate:delegate];

20.7.2 Creating an Identity

Each Ice object requires an identity. That identity must be unique for all servants using the same object adapter.1 An Ice object identity is a structure with the following Slice definition:
["objc:prefix:ICE"]
module Ice {
    struct Identity {
        string name;
        string category;
    };
    // ...
};
The full identity of an object is the combination of both the name and category fields of the Identity structure. For now, we will leave the category field as the empty string and simply use the name field. (See Section 32.7 for a discussion of the category field.)
To create an identity, we simply assign a key that identifies the servant to the name field of the Identity structure:
ICEIdentity ident = [ICEIdentity identity:"Fred" category:nil];

20.7.3 Activating a Servant

Merely creating a servant instance does nothing: the Ice run time becomes aware of the existence of a servant only once you explicitly tell the object adapter about the servant. To activate a servant, you invoke the add operation on the object adapter. Assuming that we have access to the object adapter in the adapter variable, we can write:
[adapter add:servant identity:ident];
Note the two arguments to add: the servant and the object identity. Calling add on the object adapter adds the servant and the servant’s identity to the adapter’s servant map and links the proxy for an Ice object to the correct servant instance in the server’s memory as follows:
1. The proxy for an Ice object, apart from addressing information, contains the identity of the Ice object. When a client invokes an operation, the object identity is sent with the request to the server.
2. The object adapter receives the request, retrieves the identity, and uses the identity as an index into the servant map.
3. If a servant with that identity is active, the object adapter retrieves the servant pointer from the servant map and dispatches the incoming request into the correct method on the servant.
Assuming that the object adapter is in the active state (see Section 32.4.5), client requests are dispatched to the servant as soon as you call add.
Putting the preceding points together, we can write a simple method that instantiates and activates one of our NodeI servants. For this example, we use a simple method on our servant called activate that activates a servant in an object adapter with the passed identity:
(void) activate:(id<ICEObjectAdapter>)a
                 name:(NSString *)name
{
    ICEIdentity ident = [ICEIdentity identity:name category:nil];
    [a add:self identity:ident];
}

20.7.4 UUIDs as Identities

As we discussed in Section 2.5.1, the Ice object model assumes that object identities are globally unique. One way of ensuring that uniqueness is to use UUIDs (Universally Unique Identifiers) [14] as identities. The ICEUtil class contains a helper function to create such identities:
@interface ICEUtil : NSObject
+(id) generateUUID;
// ...
@end
When executed, this method returns a unique string such as 5029a22ce3334f8786b1cd5e0fcce509. Each call to generateUUID creates a string that differs from all previous ones. You can use a UUID such as this to create object identities. For convenience, the object adapter has an operation addWithUUID that generates a UUID and adds a servant to the servant map in a single step:
(id<ICEObjectPrx>) addWithUUID:(ICEObject*)servant
Note that the operation returns the proxy for the servant just activated.

20.7.5 Creating Proxies

Once we have activated a servant for an Ice object, the server can process incoming client requests for that object. However, clients can only access the object once they hold a proxy for the object. If a client knows the server’s address details and the object identity, it can create a proxy from a string, as we saw in our first example in page 76. However, creation of proxies by the client in this manner is usually only done to allow the client access to initial objects for bootstrapping. Once the client has an initial proxy, it typically obtains further proxies by invoking operations.
The object adapter contains all the details that make up the information in a proxy: the addressing and protocol information, and the object identity. The Ice run time offers a number of ways to create proxies. Once created, you can pass a proxy to the client as the return value or as an out-parameter of an operation invocation.

Proxies and Servant Activation

The add and addWithUUID servant activation operations on the object adapter return a proxy for the corresponding Ice object, as we saw earlier. This means we can write:
NodeI *servant = [NodeI nodei:name];
id<FSNodePrx> proxy = [FSNodePrx uncheckedCast:
                        [adapter addWithUUID:servant]];

// Pass proxy to client...
Here, addWithUUID both activates the servant and returns a proxy for the Ice object incarnated by that servant in a single step.
Note that we need to use an uncheckedCast here because addWithUUID returns a proxy of type id<ICEObjectPrx>.

Direct Proxy Creation

The object adapter offers an operation to create a proxy for a given identity:
["objc:prefix:ICE"]
module Ice {
    local interface ObjectAdapter {
        Object* createProxy(Identity id);
        // ...
    };
};
Note that createProxy creates a proxy for a given identity whether a servant is activated with that identity or not. In other words, proxies have a life cycle that is quite independent from the life cycle of servants:
ICEIdentity *ident = [ICEIdentity identity];
ident.name = [ICEUtil generateUUID];
id<ICEObjectPrx> o = [adapter createProxy:ident];
This creates a proxy for an Ice object with the identity returned by generateUUID. Obviously, no servant yet exists for that object so, if we return the proxy to a client and the client invokes an operation on the proxy, the client will receive an ObjectNotExistException. (We examine these life cycle issues in more detail in Chapter 35.)

1
The Ice object model assumes that all objects (regardless of their adapter) have a globally unique identity. See Chapter 35 for further discussion.

Table of Contents Previous Next
Logo