![]() |
![]() |
![]() |
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 article, the first of two parts,
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. This article focuses on object references,
the basic pointer (_ptr) type, and rules for exchanging object references during
CORBA requests. The followup article will focus on the smart pointer helper (_var) type
that assists with memory management and parameter passing.
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 and has a distinct identity. An object's interface defines the operations and attributes it offers to its clients. 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 the servant base class, the proxy
object class, several helper types, and two pointer types, a basic pointer and a smart
pointer. For the Hello interface presented earlier, servant based class is named
POA_Hello, the proxy object class is named Hello, the basic pointer type
is named Hello_ptr, and the smart pointer type is named Hello_var. 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. 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, e.g. the "pointer type," the "var type," and the "out type." These
phrases refer respectively to generated _ptr, _var, and _out
types.
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()
and CORBA::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.
When an object reference is introduced into a client's address space, regardless of
the mechanism, 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.
_ptr TypeThis 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 and 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::Objects 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::Objects are also be 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.
_ptr TypesThis 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 a CORBA::Object reference.
Hello_Utils::hello_from_object() accepts a CORBA::Object reference and returns a
Hello object reference. Hello_Utils::save_hello() accepts a short integer, which
serves as a key, and a Hello 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
that object references are returned as _ptr types, that object references returned
from CORBA requests are released upon the return, and that 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, and 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 paramter and insure 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.
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. A followup to this
article, appearing early next year, will discuss the generated _var
type and its use.
Object Computing, Inc (OCI) has been providing educational services to clients, industries and universities since 1993. We offer one of the most comprehensive distributed Object Oriented training curricula in the country. These curricula focus on the fundamentals of OO technology; with close to 40 workshops in OOAD, Java, XML, C++/CORBA and Unix/Linux.
For further information regarding OCI's Educational Services programs, please visit our Educational Services section on the web or contact us at training.
The OCI CORBA News Brief is intended to promote CORBA and object technology in the development of distributed computing applications. Each issue of the CORBA News Brief will feature news and technical information about OCI's supported open-source ORBs (TAO and JacORB), case studies, and examples using CORBA, as well as information about OCI's educational offerings.
The OCI CORBA News Brief is published on a monthly basis. Send ideas for articles of interest to corba@ociweb.com.
To subscribe or unsubscribe from the CNB mailing list, send mail to majordomo@ociweb.com with the line "subscribe cnb" or "unsubscribe cnb" in the body of the message.
|
|
|