November 18, 2002 - By Rob Martin, Senior Software Engineer
Middleware News Brief (MNB) features news and technical information about Open Source middleware technologies.
Introduction
Correct use of object references is an essential skill for CORBA developers. Object references are largely a client-side issue but there are server-side implications arising from the C++ language mapping that defines how object references are exchanged during CORBA request invocations. This is certainly not "news" but a periodic review of this topic is useful for most CORBA developers.
This Middleware News Brief article discusses the C++ language mapping for object references, addresses memory management issues related to object references, and demonstrates the correct means of exchanging object references during CORBA request invocations. The article focuses on object references, the basic pointer (_ptr
) type, and rules for exchanging object references during CORBA requests.
Object Reference Defined
An object reference is a handle to a CORBA object.
A CORBA object is a vague concept, but two aspects of CORBA objects are clear:
- Every CORBA object realizes a specific interface. An object's interface defines the operations and attributes it offers to its clients.
- Every CORBA object has a distinct identity. An object's identity informs an ORB where and how to dispatch its client's requests.
An object reference embodies the corresponding CORBA object's identity and interface, and constitutes the primary means for invoking a CORBA object's operations. (Attributes in effect define operations, so hereafter an object's interface will be described only in terms of operations.)
An object reference can take many forms. Some forms are language independent and some are language specific.
A stringified IOR is a language-independent form of object reference.
The following is a stringified reference to a CORBA object incarnated on the author's laptop computer:
IOR:010000001900000049444c3a6f63697765622e636f6d2f48656c6c6f
3a312e3000000000010000000000000060000000010102cd050000006469
6e6f00cde12e1b00000014010f005253547fb8d63dd48809000000000001
00000001000000cd02000000000000000800000001cdcdcd004f41540100
00001400000001cdcdcd01000100000000000901010000000000
The CORBA object it refers to realizes the following interface:
- #include "example_exceptions.idl"
-
- interface Hello
- {
- void say_hello();
- string ior_please();
- void take_exception() raises (ExampleExceptions::SimpleException);
- void shutdown();
- };
A corbaloc URL is another language-independent form of object reference.
The following corbaloc URL is another reference to the object denoted by the preceding stringified IOR:
corbaloc:iiop:dino:12001/Hello
Language-independent object references are essential to interoperability. They allow the exchange of object references between clients and servers deployed on heterogeneous platforms and implemented in different languages.
Language-specific forms are defined by formal language mappings to enable concrete development in the various languages supported by CORBA.
An object reference in the C++ language is a pointer to an object that proxies for the target CORBA object and has the target object's interface.
- A client invokes a request by dereferencing the pointer using native C++ syntax.
- When a client invokes a CORBA request, the proxy forwards the request to the local ORB.
- The local ORB uses identity information held by the object reference to locate the target object and dispatches the request accordingly.
- A synchronous request's reply is returned to the proxy by the local ORB and the proxy returns the reply to the client.
For each interface, the IDL compiler generates all of the following:
- The servant base class
- The proxy object class
- Several helper types
- Two pointer types: a basic pointer and a smart pointer
- The basic pointer is analogous to a native C++ pointer; the smart pointer has the same behavior as the basic pointer but provides additional features that simplify memory management and parameter passing.
For the Hello
interface presented above:
- The servant based class is named
POA_Hello
- The proxy object class is named
Hello
- The basic pointer type is named
Hello_ptr
; the smart pointer type is namedHello_var
One of the generated helper classes assists with passing object references as out mode parameters. For the Hello
interface, this helper type is named Hello_out
.
It is common practice to refer to these types in a general sense, for example, the "pointer type," the "var type," and the "out type." These phrases refer respectively to generated _ptr
, _var
, and _out
types.
Creating and Obtaining Object References
An object reference is created when the corresponding CORBA object is created.
CORBA objects are commonly created when servants are activated via PortableServer::POA::activate_object()
. However, CORBA objects can be created without activating servants via other operations.
CORBA servers often export object references in different forms using a variety of mechanisms to provide clients with access to their CORBA objects. However, object references can be exposed to clients without the server's direct participation using forms such as corbaloc URLs.
An object reference can be introduced into a client's address space in many ways.
- Clients frequently employ ORB operations such as
CORBA::ORB::resolve_initial_references()
andCORBA::ORB::string_to_object()
to obtain object references. - Clients often use a Naming Service and obtain object references via
CosNaming::NamingContext::resolve()
. - Clients may also obtain object references by invoking requests on known CORBA objects, i.e., objects already accessible in their address space that act as factories for other CORBA objects.
Regardless of the mechanism, when an object reference is introduced into a client's address space, the local ORB creates a proxy object and returns a basic pointer to that proxy.
An ORB chooses a proxy object class based on the interface type information at hand when a reference is introduced. This allows ORBs to produce object references in a type-safe manner.
Often, the available type information is sufficient only to return a CORBA::Object
reference. This is the case with operations such as CORBA::ORB::resolve_initial_references()
, CORBA::ORB::string_to_object()
, and CosNaming::NamingContext::resolve()
. When the ORB returns a CORBA::Object
reference, the client is then obligated to narrow the reference to a more specific type. Factory object interfaces typically return specific object types, which allow an ORB to create a type specific reference in a safe manner.
Clients are specifically prohibited from directly creating object references with the exception of a nil
reference.
Clients are permitted to copy object references, to narrow and widen object references to other related types, and to pass object references as parameters during CORBA request invocations.
Clients are also obligated to collaborate with the ORB in managing resources allocated to object references. This includes informing the ORB when object references are copied (duplicated) and destroyed (released).
Most ORBs employ reference counting as a memory management technique, but this is not required. The memory management interfaces and a client's obligations are independent of the ORB's implementation.
BASIC POINTER: THE _ptr
TYPE
This section demonstrates use of the basic pointer, the _ptr
type. The examples are based in part on the Hello
interface shown previously. All references are initially introduced into the client's address space using CORBA::ORB::string_to_object()
, but there are some examples that demonstrate factory object behavior.
CORBA::ORB::string_to_object()
returns CORBA::Object_ptr
types, i.e., CORBA::Object
references because there isn't sufficient type information to return a more specific type, e.g.,:
CORBA::Object_ptr obj = orb->string_to_object("file://server.ior");
The caller should always verify that the object reference returned is not nil
:
- if(CORBA::is_nil (obj))
- {
- // error: nil object reference returned
- }
The client now has a reference to a CORBA::Object
, a basic pointer to a proxy having the CORBA::Object
interface:
To gain access to something more useful than a CORBA::Object
, the client must narrow the object reference:
Hello_ptr Hello_obj = Hello::_narrow(obj);
This operation returns an object reference having the Hotel
interface or a nil
reference. A nil
reference will be returned if the target object's interface is not Hotel
. Consequently, clients should always verify that the object reference returned from a _narrow()
operation is not nil
:
- if(CORBA::is_nil (Hello_obj))
- {
- // error: nil object reference returned
- }
The client now has a reference to a Hello
object:
(For the remainder of this article, checks for nil
object references are omitted for brevity.)
A consequence of the preceding two operations is that the ORB has allocated resources for two distinct proxy objects:
- A proxy for a
CORBA::Object
- A proxy for a
Hello
object
Although both proxies are associated with the same target object, their interfaces are different and therefore they are separate proxy objects. The client has access to these objects via pointers provided by the ORB, but the ORB retains ownership, so it can manage resources in a coherent manner.
Because clients have access to resources owned by the ORB, clients must collaborate with the ORB to protect their interests in the use object references and to assist the ORB in the recovery of its resources.
To inform the ORB that an object reference is no longer in use and may be destroyed, a client calls CORBA::release()
:
CORBA::release(obj);
The ORB may destroy the obj
reference and recover its resources upon the call to CORBA::release()
or anytime thereafter. The client must assume, however, that the object reference is destroyed immediately upon the call to CORBA::release()
and treat it accordingly:
The obj
variable can be re-used to refer to other CORBA::Object
s, but it must not be used to invoke requests until it has been associated with another CORBA object.
The release of obj
has no effect on Hello_obj
because they are distinct references so the following sequence is valid:
CORBA::Object_ptr obj = orb->string_to_object("file://server.ior");
Hello_ptr Hello_obj = Hello::_narrow(obj);
CORBA::release(obj);
Hello_obj->say_hello();
Similarly, the release of Hello_obj
has no effect on obj
.
The following sequence, although nonsensical, demonstrates the point:
CORBA::Object_ptr obj = orb->string_to_object("file://server.ior");
for(int i = 0; i < 10; ++i)
{
Hello_ptr Hello_obj = Hello::_narrow(obj);
Hello_obj->say_hello();
CORBA::release(Hello_obj);
}
To copy an object reference, a client calls _duplicate()
using the appropriate proxy class. For example, to copy a Hello
object reference:
Hello_ptr Hello_obj2 = Hello::_duplicate(Hello_obj);
Hello_obj2
is a reference to target object referred to by Hello_obj
.
Other object references, including CORBA::Object
s are also copied in this manner:
CORBA::Object_ptr obj2 = CORBA::Object::_duplicate(obj);
Conceptually, the ORB performs a deep copy when object references are duplicated. Most ORBs employ reference counting, so this operation usually amounts to incrementing a proxy object's reference count.
Similarly, a release operation usually amounts to decrementing a proxy object's reference count. A proxy object continues to exist until its reference count reaches 0, at which time the object is destroyed and associated resources recovered. However, it is important to treat different object references, e.g., Hello_obj
and Hello_obj2
, as distinct references each having dedicated resources managed by the ORB.
EXCHANGING OBJECT REFERENCES USING _ptr
TYPES
This section discusses the exchange of object references using the _ptr
type.
From the client side, object references are exchanged using just the _ptr
types. Server implementations also employ _ptr
types, except for object references passed as out mode parameters where the _out
type is used.
The examples that follow are based on the Hello
interface seen previously and the following:
- #include "Hello.idl"
-
- interface Hello_Utils
- {
- Object object_from_ior(in string ior);
- Hello hello_from_object(in Object obj);
- void save_hello(in short which, in Hello hello);
- void get_hello(in short which, out Hello hello);
- void change_to(in short which, inout Hello hello);
- };
This interface was designed to demonstrate passing and returning object references to and from CORBA requests.
Hello_Utils::object_from_ior()
accepts a string form of object reference, a stringified IOR, a corbloc URL, etc., and returns aCORBA::Object
reference.Hello_Utils::hello_from_object()
accepts aCORBA::Object
reference and returns aHello
object reference.Hello_Utils::save_hello()
accepts a short integer, which serves as a key, and aHello
object reference. The object reference is retained for subsequent retrieval using the given key value.Hello_Utils::get_hello()
returns, via an out parameter, the Hello object reference associated with a given key value.Hello_Utils::change_to()
performs a similar function using an inout parameter instead of an out parameter.
In the subsequent examples, Utils_obj
is a Hello_Utils
object reference.
The first example, Hello_Utils::object_from_ior()
demonstrates returning an object reference from a CORBA request. The operation definition:
Object object_from_ior(in string ior);
maps to the following proxy class method signature:
CORBA::Object_ptr Hello_Utils::object_from_ior(const char * ior);
An IOR is passed as a C string and the operation returns the corresponding CORBA::Object
reference:
CORBA::Object_ptr obj = Utils_obj->object_from_ior("corbaloc:iiop:dino:12001/Hello");
This operation is implemented simply as (by convention, _i
is appended to Hello_Utils
to denote the Hello_Util
's servant implementation.):
CORBA::Object_ptr
Hello_Utils_i::object_from_ior(const char *ior)
throw ( CORBA::SystemException )
{
CORBA::Object_ptr obj = orb_->string_to_object(ior);
return obj;
}
This is not a particularly interesting implementation. It serves only to demonstrate the following:
- Object references are returned as
_ptr
types. - Object references returned from CORBA requests are released upon the return.
- Clients assume responsibility for object references returned by CORBA requests.
The servant implementation does not explicitly release the reference; this occurs automatically upon the operation's return. A client that receives an object reference via a CORBA request must release the reference when it is no longer required.
The client's behavior is demonstrated by the following:
CORBA::Object_ptr obj = orb->string_to_object ("file://utils.ior");
Hello_Utils_ptr Utils_obj = Hello_Utils::_narrow (obj);
CORBA::release(obj);
obj = Utils_obj->object_from_ior("corbaloc:iiop:dino:12001/Hello");
Hello_ptr Hello_obj = Hello::_narrow(obj);
CORBA::release(obj);
//
// do useful stuff with Hello_obj;
//
CORBA::release(Hello_obj);
Each object reference, obj
and Hello_obj
, is released prior to its re-use or when it is no longer needed.
Object references passed to CORBA requests are passed as _ptr
types.
The operation:
Hello hello_from_object(in Object obj);
maps to the following Hello_Utils
proxy method signature:
Hello_ptr Hello_Utils::hello_from_object(CORBA::Object_ptr obj);
A server can use an object reference passed as an in parameter for the duration of the request. However, the server isn't responsible for the resources allocated to the object reference and therefore shouldn't release it.
A simple implementation of this method is:
Hello_ptr
Hello_Utils_i::hello_from_object(CORBA::Object_ptr obj)
throw (CORBA::SystemException)
{
Hello_ptr hello_obj = Hello::_narrow(obj);
return hello_obj;
}
The server has use of the object reference obj
but doesn't have responsibility for it and so doesn't release it. Moreover, the reference is automatically released upon the operation's return, so the server must copy the reference to retain it.
Retaining an object reference beyond the lifetime of a single CORBA request is demonstrated by the following operation:
void save_hello(in short which, in Hello hello);
A simple implementation of this operation is:
void
Hello_Utils_i::save_hello(CORBA::Short which, Hello_ptr hello)
throw (CORBA::SystemException)
{
if(1 <= which && which <= 2)
{
if(!(CORBA::is_nil(hello_objs_[which - 1])))
{
CORBA::release(hello_objs_[which - 1]);
}
hello_objs_[which - 1] = Hello::_duplicate(hello);
}
}
The servant's implementation retains references to Hello
objects in a small array. The value which
, serving as a key, is used as the array index.
This operation:
- Receives a
Hello
object reference along with its key - Releases the reference previously associated with the given key, if any
- Then duplicates the new reference using the appropriate proxy class method
Using the operations seen so far, a client can first obtain and then preserve for later use a Hello
object reference as follows:
CORBA::Object_ptr obj = orb->string_to_object ("file://utils.ior");
Hello_Utils_ptr Utils_obj = Hello_Utils::_narrow (obj);
CORBA::release(obj);
obj = Utils_obj->object_from_ior("corbaloc:iiop:dino:12001/Hello");
Hello_obj = Utils_obj->hello_from_object(obj);
CORBA::release(obj);
//
// do stuff with Hello_obj
//
Utils_obj->save_hello(1, Hello_obj);
CORBA::release(Hello_obj);
Note that the client's release of the Hello
reference has no effect on the Hello_Util
server's reference to the same target object. These references are distinct and independent references. It is likely that they exist in separate address spaces, but even if that were not the case, the references are independent of one another.
The next operation demonstrates passing an object reference to a CORBA request as an out mode parameter. Conceptually, the client passes to the server a reference to a _ptr
and the server populates the reference. The client can use the reference after the request returns if no exceptions occur. If an exception occurs, the condition of the object reference is undefined, and the client should not attempt to access it.
By definition, a reference passed as an out mode parameter should be nil
when the operation is invoked. The client continues to employ _ptr
types as with the other modes. The server, however, employs the _out
helper type generated by the IDL compiler in its implementation.
The following operation:
void get_hello(in short which, out Hello hello);
maps to the following proxy class method:
void Hello_Utils::get_hello(CORBA::Short which, Hello_out hello)
throw (CORBA::SystemException)
A client invokes this operation as follows:
Hello_ptr Hello_obj = Hello::_nil();
Utils_obj->get_hello(1, Hello_obj);
Hello_obj->say_hello();
This example illustrates the point that object references exchanged via out parameters are nil
upon invocation and populated during the operation. This is enforced by the _out
type, which sets the reference to _nil
in its constructor.
A simple implementation of this operation consistent with the preceding void Hello_Utils_i::save_hello()
is:
void
Hello_Utils_i::get_hello(CORBA::Short which, Hello_out hello)
throw (CORBA::SystemException)
{
if(1 <= which && which <= 2)
{
hello = Hello::_duplicate(hello_objs_[which - 1]);
}
}
The server populates the hello
object reference by duplicating a reference previously retained by the save_hello()
operation. Duplicating the reference is essential because responsibility for references exchanged via an out parameter passes from the server to the client upon the operation's return.
The reference passed by the server is released from the server's address space so it can be transferred to the client's address space. If the server's source reference was not duplicated, the server would be left with a dangling reference.
Of course, releasing the reference is not necessary if the server doesn't wish to retain a copy for subsequent use. However, it is necessary in this example because the server retains copies of the references obtained via save_hello
.
The role of the _out
class, in this example a Hello_out
, is to simplify handling of references exchanged via an out parameter and ensure consistency with the parameter passing rules.
An object reference passed to a CORBA request as inout mode parameter is passed as a reference to a _ptr
.
The operation:
void change_to(in short which, inout Hello hello);
maps to the following proxy class method:
void Hello_Utils::change_to(CORBA::Short which, Hello_ptr & hello)
throw (CORBA::SystemException)
The server obtains responsibility for an object reference passed as inout mode parameter. It has access to the reference upon the invocation and must release the incoming reference if it is modified, which is the typical case.
Following is a simple implementation that demonstrates the rule:
void
Hello_Utils_i::change_to(CORBA::Short which, Hello_ptr & hello)
throw (CORBA::SystemException)
{
if(1 <= which && which <= 2)
{
if(!CORBA::is_nil(hello_objs_[which - 1]))
{
CORBA::release(hello);
hello = Hello::_duplicate(hello_objs_[which - 1]);
}
}
}
The important difference between this operation's implementation and the preceding example is that the server must release the incoming reference before assigning the new value. As with the preceding example, the reference assigned to the parameter hello
is first duplicated in accordance with memory management policy.
The client passes a valid reference to the request as follows:
Utils_obj->get_hello(2, Hello_obj);
Hello_obj->say_hello();
//
// Change to the first reference.
//
Utils_obj->change_to(1, Hello_obj);
Hello_obj->say_hello();
The client does not release the Hello_obj
reference prior to invoking Hello_Utils::change_to()
because this is the server's responsibility.
Summary
This article described object references in general and the C++ language mapping for object references. It also discussed a client's responsibilities regarding memory management related to object references and use of the generated _ptr
type to exchange object references between clients and servers during CORBA requests.