In this chapter, we go through three examples to illustrate the practical steps to use omniORB2. By going through the source code of each example, the essential concepts and APIs are introduced. If you have no previous experience with using CORBA, you should study this chapter in detail. There are pointers to other essential documents you should be familiar with.
If you have experience with using other ORBs, you should still go through this chapter because it provides important information about the features and APIs that are necessarily omniORB2 specific. For instance, the object implementation skeleton is covered in section 2.4.2.
Our example is an object which has only one method. The method simply echos the argument string. We have to:
The source code of this example is included in the last section of this chapter. A makefile written to be used under the OMNI Development Environment (ODE) [Richardson96b] is also included.
We define an object interface, called Echo, as follows:
interface Echo { string echoString(in string mesg); };
If you are new to IDL, you can learn about its syntax in Chapter 3 of the CORBA specification 2.0 [OMG99a].
For the moment, you only need to know that the interface consists of a single operation, echoString, which takes a string as an argument and returns a copy of the same string.
The interface is written in a file, called echo.idl. If you are using ODE, all IDL files should have the same extension- .idl and should be placed in the idl directory of your export tree. This is done so that the stub code will be generated automatically and kept up-to-date with your IDL file.
For simplicity, the interface is defined in the global IDL namespace. This practice should be avoided for the sake of object reusuability. If every CORBA developer defines their interfaces in the global IDL namespace, there is a danger of name clashes between two independently defined interfaces. Therefore, it is better to qualify your interfaces by defining them inside module names. Of course, this does not eliminate the chance of a name clash unless some form of naming convention is agreed globally. Nevertheless, a well-chosen module name can help a lot.
From the IDL file, we use the IDL compiler to produce the C++ mapping of the interface. The IDL compiler for omniORB2 is called omniidl2. Given the IDL file, omniidl2 produces two stub files: a C++ header file and a C++ source file. For example, from the file echo.idl, the following files are produced:
If you are using ODE, you don't need to invoke omniidl2 explicitly. In the example file dir.mk, we have the following line:
CORBA_INTERFACES = echo
That is all we need to instruct ODE to generate the stubs. Remember, you won't find the stubs in your working directory because all stubs are written into the stub directory at the top level of your build tree.
The C++ stubs conform to the mapping defined in the CORBA 2.0 specification (chapter 16-18). It is important to understand the mapping before you start writing any serious CORBA applications.
Before going any further, it is worth knowing what the mapping looks like.
The use of an object interface denotes an object reference. For the example interface Echo, the C++ mapping for its object reference is Echo_ptr. The type is defined in echo.hh. The relevant section of the code is reproduced below:
class Echo; typedef Echo* Echo_ptr; class Echo : public virtual omniObject, public virtual CORBA::Object { public: virtual char * echoString ( const char * mesg ) = 0; static Echo_ptr _nil(); static Echo_ptr _duplicate(Echo_ptr); static Echo_ptr _narrow(CORBA::Object_ptr); ... // methods generated for internal use };
In a compliant application, the operations defined in an object interface should only be invoked via an object reference. This is done by using arrow (``'') on an object reference. For example, the call to the operation echoString would be written as objechoString(mesg).
It should be noted that the concrete type of an object reference is opaque, i.e. you must not make any assumption about how an object reference is implemented. In our example, even though Echo_ptr is implemented as a pointer to the class Echo, it should not be used as a C++ pointer, i.e. conversion to void*, arithmetic operations, and relational operations, including test for equality using operation== must not be performed on the type.
In addition to echoString, the mapping also defines three static member functions in the class Echo: _nil, _duplicate, and _narrow. Note that these are operations on an object reference.
The _nil function returns a nil object reference of the Echo interface. The following call is guaranteed to return TRUE:
CORBA::Boolean true_result = CORBA::is_nil(Echo::_nil());
Remember, CORBA::is_nil() is the only compliant way to check if an object reference is nil. You should not use the equality operator==.
The _duplicate function returns a new object reference of the Echo interface. The new object reference can be used interchangeably with the old object reference to perform an operation on the same object.
All CORBA objects inherit from the generic object CORBA::Object. CORBA::Object_ptr is the object reference for CORBA::Object. Any object reference is therefore conceptually inherited from CORBA::Object_ptr. In other words, an object reference such as Echo_ptr can be used in places where a CORBA::Object_ptr is expected.
The _narrow function takes an argument of the type CORBA::Object_ptr and returns a new object reference of the Echo interface. If the actual (runtime) type of the argument object reference can be widened to Echo_ptr, _narrow will return a valid object reference. Otherwise it will return a nil object reference.
To indicate that an object reference will no longer be accessed, you can call the CORBA::release operation. Its signature is as follows:
class CORBA { static void release(CORBA::Object_ptr obj); ... // other methods };
You should not use an object reference once you have called CORBA::release. This is because the associated resources may have been deallocated. Notice that we are referring to the resources associated with the object reference and not the object implementation. Here is a concrete example, if the implementation of an object resides in a different address space, then a call to CORBA::release will only caused the resources associated with the object reference in the current address space to be deallocated. The object implementation in the other address space is unaffected.
As described above, the equality operator== should not be used on object references. To test if two object references are equivalent, the member function _is_equivalent of the generic object CORBA::Object can be used. Here is an example of its usage:
Echo_ptr A; ... // initialised A to a valid object reference Echo_ptr B = A; CORBA::Boolean true_result = A->_is_equivalent(B); // Note: the above call is guaranteed to be TRUE
You have now been introduced to most of the operations that can be invoked via Echo_ptr. The generic object CORBA::Object provides a few more operations and all of them can be invoked via Echo_ptr. These operations deal mainly with CORBA's dynamic interfaces. You do not have to understand them in order to use the C++ mapping provided via the stubs. For details, please read the CORBA specification [OMG99a] chapter 17.
Since object references must be released explicitly, their usage is prone to error and can lead to memory leakage. The mapping defines the object reference variable type to make life easier. In our example, the variable type Echo_var is defined.
The Echo_var is more convenient to use because it will automatically release its object reference when it is deallocated or when assigned a new object reference. For many operations, mixing data of type Echo_var and Echo_ptr is possible without any explicit operations or castings . For instance, the operation echoString can be called using the arrow (``'') on a Echo_var, as one can do with a Echo_ptr.
The usage of Echo_var is illustrated below:
Echo_var a; Echo_ptr p = ... // somehow obtain an object reference a = p; // a assumes ownership of p, must not use p anymore Echo_var b = a; // implicit _duplicate p = ... // somehow obtain another object reference a = Echo::_duplicate(p); // release old object reference // a now holds a copy of p.
Unlike the client side of an object, i.e. the use of object references, the CORBA specification 2.0 deliberately leave many of the necessary functionalities to implement an object unspecified. As a consequence, it is very unlikely the implementation code of an object on top of two different ORBs can be identical. However, most of the code are expected to be portable. In particular, the body of an operation implementation can normally be ported with no or little modification.
OmniORB2 uses C++ inheritance to provide the skeleton code for object implementation. For each object interface, a skeleton class is generated. In our example, the skeleton class _sk_Echo is generated for the Echo IDL interface. An object implementation can be written by creating an implementation class that derives from the skeleton class.
The skeleton class _sk_Echo is defined in echo.hh. The relevant section of the code is reproduced below.
class _sk_Echo : public virtual Echo { public: _sk_Echo(const omniORB::objectKey& k); virtual char * echoString ( const char * mesg ) = 0; Echo_ptr _this(); void _obj_is_ready(BOA_ptr); void _dispose(); BOA_ptr _boa(); omniORB::objectKey _key(); ... // methods generated for internal use };
The code fragment shows the only member functions that can be used in the object implementation code. Other member functions are generated for internal use only. Unless specified otherwise, the description below is omniORB2 specific. The functions are:
You define an implementation class to provide the object implementation. There is little constraint on how you design your implementation class except that it has to inherit from the stubs' skeleton class and to implement all the abstract functions defined in the skeleton class. Each of these abstract functions corresponds to an operation of the interface. They are hooks for the ORB to perform upcalls to your implementation.
Here is a simple implementation of the Echo object.
class Echo_i : public virtual _sk_Echo { public: Echo_i() {} virtual ~Echo_i() {} virtual char * echoString(const char *mesg); }; char * Echo_i::echoString(const char *mesg) { char *p = CORBA::string_dup(mesg); return p; }
There are three points to note here:
Here is an example of how a Echo_ptr object reference is used.
void hello(CORBA::Object_ptr obj) { Echo_var e = Echo::_narrow(obj); // line 1 if (CORBA::is_nil(e)) { // line 2 cerr << "hello: cannot invoke on a nil object reference.\n" << endl; return; } CORBA::String_var src = (const char*) "Hello!"; // line 3 CORBA::String_var dest; // line 4 dest = e->echoString(src); // line 5 cerr << "I said,\"" << src << "\"." << " The Object said,\"" << dest <<"\"" << endl; }
Briefly, the function hello accepts a generic object reference. The object reference ( obj) is narrowed to Echo_ptr. If the object reference returned by Echo::_narrow is not nil, the operation echoString is invoked. Finally, both the argument to and the return value of echoString are printed to cerr.
The example also illustrates how T_var types are used. As it was explained in the previous section, T_var types take care of storage allocation and release automatically when variables of the type are assigned to or when the variables go out of scope.
In line 1, the variable e takes over the storage responsibility of the object reference returned by Echo::_narrow. The object reference is released by the destructor of e. It is called automatically when the function returns. Line 2 and 5 shows how a Echo_var variable is used. As said earlier, Echo_var type can be used interchangeably with Echo_ptr type.
The argument and the return value of echoString are stored in CORBA::String_var variable src and dest respectively. The strings managed by the variables are deallocated by the destructor of CORBA::String_var. It is called automatically when the function returns. Line 5 shows how CORBA::String_var variables are used. They can be used in place of a string (for which the mapping is char* ). As used in line 3, assigning a constant string ( const char*) to a CORBA::String_var causes the string to be copied. On the otherhand, assigning a char* to a CORBA::String_var, as used in line 5, causes the latter to assume the ownership of the string.
Under the C++ mapping, T_var types are provided for all the non-basic data types. It is obvious that one should use automatic variables whenever possible both to avoid memory leak and to maximise performance. However, when one has to allocate data items on the heap, it is a good practice to use the T_var types to manage the heap storage.
Having introduced the client and the object implementation, we can now describe how to link up the two via the ORB. In this section, we describe an example in which both the client and the object implementation are in the same address space. In the next two sections, we shall describe the case where the two are in different address spaces.
The code for this example is reproduced below:
int main(int argc, char **argv) { CORBA::ORB_ptr orb = CORBA::ORB_init(argc,argv,"omniORB2"); // line 1 CORBA::BOA_ptr boa = orb->BOA_init(argc,argv,"omniORB2_BOA"); // line 2 Echo_i *myobj = new Echo_i(); // line 3 myobj->_obj_is_ready(boa); // line 4 boa->impl_is_ready(0,1); // line 5 Echo_ptr myobjRef = myobj->_this(); // line 6 hello(myobjRef); // line 7 CORBA::release(myobjRef); // line 8 myobj->_dispose(); // line 9 boa->destory(); // line 10 orb->NP_destory(); // line 11 return 0; }
The example illustrates several important interactions among the ORB, the object implementation and the client. Here are the details:
These two calls are particularly important when writing a CORBA DLL on Windows NT that is to be used from ActiveX. If these calls are absent, the application will hang when the CORBA DLL is unloaded.
One of the important characteristic of an object reference is that it is completely location transparent. A client can invoke on the object using its object reference without any need to know whether the object is colocated in the same address space or resided in a different address space.
In case of colocated client and object implementation, omniORB2 is able to short-circuit the client calls to direct calls on the implementation methods. The cost of an invocation is reduced to that of a function call. This optimisation is applicable not only to object references returned by the _this function but to any object references that are passed around within the same address space or received from other address spaces via IIOP calls.
Once an application calls _dispose on an object implementation, the pointer to the object should not be used any more. At the time the _dispose call is made, there may be other threads invoking on the object, omniORB2 ensures that all these calls are completed before removing the object from its internal tables and releasing the resources associated with it. The storage associated with the object is released by omniORB2 using the delete operator. This is why all object implementation should be initialised using the new operator (section 2.5).
The disposal of an object implementation by omniORB2 may also be deferred when colocated clients continue to hold on to copies of the object's reference. This behavior is to prevent the short-circuited calls from the clients to fail unpredictably.
To summarise, an application can make no assumption as to when the object is disposed by omniORB2 after the _dispose call returns. If it is necessary to have better control on when to stop serving incoming requests, the work should be done by the object implementation itself, such as by keeping track of the current serving state.
In this example, the client and the object implementation reside in two different address spaces. The code of this example is almost the same as the previous example. The only difference is the extra work need to be done to pass the object reference from the object implementation to the client.
The simplest (and quite primitive) way to pass an object reference between two address spaces is to produce a stringified version of the object reference and to pass this string to the client as a command-line argument. The string is then converted by the client into a proper object reference. This method is used in this example. In the next example, we shall introduce a better way of passing the object reference using the COS Naming Service.
The main function of the object implementation side is reproduced below. The full listing ( eg2_impl.cc) can be found at the end of this chapter.
int main(int argc, char **argv) { CORBA::ORB_ptr orb = CORBA::ORB_init(argc,argv,"omniORB2"); CORBA::BOA_ptr boa = orb->BOA_init(argc,argv,"omniORB2_BOA"); Echo_i *myobj = new Echo_i(); myobj->_obj_is_ready(boa); { Echo_var myobjRef = myobj->_this(); CORBA::String_var p; p = orb->object_to_string(myobjRef); //line 1 cerr << "'" << (char*)p << "'" << endl; } boa->impl_is_ready(); // block here indefinitely // See the explanation in example 1 return 0; }
The stringified object reference is obtained by calling the ORB's function _object_to_string (line 1). This is a sequence starting with the signature ``IOR:'' and followed by a hexadecimal string. All CORBA 2.0 compliant ORBs are able to convert the string into its internal representation of a so-called Interoperable Object Reference (IOR). The IOR contains the location information and a key to uniquely identify the object implementation in its own address space. From the IOR, an object reference can be constructed.
The stringified object reference is passed to the client as a command-line argument. The client uses the ORB's function string_to_object to convert the string into a generic object reference ( CORBA::Object_ptr). The relevant section of the code is reproduced below. The full listing ( eg2_clt.cc) can be found at the end of this chapter.
try { CORBA::Object_var obj = orb->string_to_object(argv[1]); hello(obj); } catch(CORBA::COMM_FAILURE& ex) { ... // code to handle communication failure }
When omniORB2 detects an error condition, it may raise a system exception. The CORBA specification defines a series of exceptions covering most of the error conditions that an ORB may encounter. The client may choose to catch these exceptions and recover from the error condition. For instance, the code fragment, shown in section 2.8.2, catches the system exception COMM_FAILURE which indicates that communication with the object implementation in another address space has failed.
All system exceptions inherit from the class CORBA::SystemException. With compilers that support RTTI, a single catch CORBA::SystemException will catch all the different system exceptions thrown by omniORB2.
When omniORB2 detects an internal inconsistency that is most likely to be caused by a bug in the runtime, it raises the exception omniORB::fatalException. When this exception is raised, it is not sensible to proceed with any operation that involves the ORB's runtime. It is best to exit the program immediately. The exception structure carries by omniORB::fatalException contains the exact location (the file name and the line number) where the exception is raised. You are strongly encourage to file a bug report and point out the location.
It may be obvious but it has to stated that an object implementation exists only for the duration of the process's lifetime. When the same program is run again, a different instance of the object implementation is created. More significantly, the IOR, and hence the object reference, of this instance is different from that of the previous run.
For instance, if you look at the stringified object reference produced by the program eg2_impl in different runs, they are all different. The implication is that you cannot store away the stringified object reference and expect to be able to use it again later when the original program run has terminated.
For system services and other applications, it may be desirable to have ``persistent'' object implementations. The objects are ``persistent'' in the sense that they can be contacted using the same IOR when they are instantiated in different program runs. To provide this functionality, omniORB2 needs to be provided with two pieces of information: the (network) location and the object key. The details of how this can be done will be described in the later part of this manual.
Alternatively, an indirection from textual pathnames to object references can be used. Applications can register object implementations at runtime to a naming service and bind them to fixed pathnames. Clients can bind to the object implementations at runtime by asking the naming service to resolve the pathnames to the object references. CORBA defines a naming service, which is a component of the Common Object Services (COS) [OMG99b], that can be used for this purpose. The next section describes an example of how to use the COS Naming Service.
In this example, the object implementation uses the COS Naming Service [OMG99b] to pass on the object reference to the client. This method is by-far more practical than using stringified object references. The full listing of the object implementation ( eg3_impl.cc) and the client ( eg3_clt.cc) can be found at the end of this chapter.
The object reference is bound to the pathname `` test/ Echo''. The pathname consists of the context test and the object name Echo. Both the context and the object name has an attribute kind. This attribute is a string that is intended to be used to describe the name in a syntax-independent way. The naming service does not interpret, assign, or manage these values. However both the name and the kind attribute must match for a name lookup to succeed. In this example, the kind values for test and Echo are chosen to be ``my_context'' and ``Object'' respectively. This is an arbitrary choice for there is no standardised set of kind values.
The initial contact with the Naming Service can be established via what we called the root context. The object reference to the root context is provided by the ORB and can be obtained by calling resolve_initial_references. The following code fragment shows how it is used:
CORBA::ORB_ptr orb = CORBA::ORB_init(argc,argv,"omniORB2"); CORBA::Object_var initServ; initServ = orb->resolve_initial_references("NameService"); CosNaming::NamingContext_var rootContext; rootContext = CosNaming::NamingContext::_narrow(initServ);
Remember, omniORB2 constructs its internal list of initial references at initialisation time using the information provided in the configuration file omniORB.cfg. If this file is not present, the internal list will be empty and resolve_initial_references will raise a CORBA::ORB::InvalidName exception.
It is beyond the scope of this chapter to describe in detail the Naming Service interface. You should consult the CORBAservices specification [OMG99b] (chapter 3). The code listed in eg3_impl.cc and eg3_clt.cc are good examples of how the service can be used. Please spend time to study the examples carefully.
Since 2.6.0, a new command-line option ( -t) has been added to omniidl2. When this flag is specified, omniidl2 generates an extra template class for each interface. This template class can be used to tie a C++ class to the skeleton class of the interface.
The source code in eg3_tieimpl.cc at the end of this chapter illustrates how the template class can be used. The code is almost identical to eg3_impl.cc with only a few changes.
Firstly, the implementation class Echo_i does not inherit from any stub classes. This is the main benefit of using the template class because there are applications in which it is difficult to require every implementation class to subclass from CORBA classes.
Secondly, the instantiation of a CORBA object now involves creating an instance of the implementation class and an instance of the template. Here is the relevant code fragment:
class Echo_i { ... }; Echo_i *myimpl = new Echo_i(); _tie_Echo<Echo_i,1> *myobj = new _tie_Echo<Echo_i,1>(myimpl); myobj->_obj_is_ready(boa);
For interface Echo, the name of its tie implementation template is _tie_Echo. The first template parameter is the implementation class that contains an implementation of each of the operations defined in the interface. The second template parameter is a boolean flag. When the flag is TRUE (1), as it is in this example, the ORB would call delete on the implementation object ( myimpl) when _dispose is invoked on myobj. When the flag is FALSE (0), delete would not be called. Instantiating this template with the flag set to FALSE is useful when the same implementation class is used to implement multiple interfaces. In this situation, the same implementation would be used as the argument to a number of tie template instantiations. Provided that only one of the instantiation has the flag set to TRUE, the object implementation would not be deleted more than once!
// echo_i.cc - This source code demonstrates an implmentation of the // object interface Echo. It is part of the three examples // used in Chapter 2 "The Basics" of the omniORB2 user guide. // #include <string.h> #include "echo.hh" class Echo_i : public virtual _sk_Echo { public: Echo_i() {} virtual ~Echo_i() {} virtual char * echoString(const char *mesg); }; char * Echo_i::echoString(const char *mesg) { char *p = CORBA::string_dup(mesg); return p; }
// greeting.cc - This source code demonstrates the use of an object // reference by a client to perform an operation on an // object. It is part of the three examples used // in Chapter 2 "The Basics" of the omniORB2 user guide. // #include <iostream.h> #include "echo.hh" void hello(CORBA::Object_ptr obj) { Echo_var e = Echo::_narrow(obj); if (CORBA::is_nil(e)) { cerr << "hello: cannot invoke on a nil object reference.\n" << endl; return; } CORBA::String_var src = (const char*) "Hello!"; CORBA::String_var dest; dest = e->echoString(src); cerr << "I said,\"" << src << "\"." << " The Object said,\"" << dest <<"\"" << endl; }
// eg1.cc - This is the source code of example 1 used in Chapter 2 // "The Basics" of the omniORB2 user guide. // // In this example, both the object implementation and the // client are in the same process. // // Usage: eg1 // #include <iostream.h> #include "echo.hh" #include "echo_i.cc" #include "greeting.cc" int main(int argc, char **argv) { CORBA::ORB_ptr orb = CORBA::ORB_init(argc,argv,"omniORB2"); CORBA::BOA_ptr boa = orb->BOA_init(argc,argv,"omniORB2_BOA"); Echo_i *myobj = new Echo_i(); // Note: all implementation objects must be instantiated on the // heap using the new operator. myobj->_obj_is_ready(boa); // Tell the BOA the object is ready to serve. // This call is omniORB2 specific. // // This call is equivalent to the following call sequence: // Echo_ptr myobjRef = myobj->_this(); // boa->obj_is_ready(myobjRef); // CORBA::release(myobjRef); boa->impl_is_ready(0,1); // Tell the BOA we are ready and to return immediately once it has // done its stuff. It is omniORB2 specific to call impl_is_ready() // with the extra 2nd argument- CORBA::Boolean NonBlocking, // which is set to TRUE (1) in this case. Echo_ptr myobjRef = myobj->_this(); // Obtain an object reference. // Note: always use _this() to obtain an object reference from the // object implementation. hello(myobjRef); CORBA::release(myobjRef); // Dispose of the object reference. myobj->_dispose(); // Dispose of the object implementation. // This call is omniORB2 specific. // Note: *never* call the delete operator or the dtor of the object // directly because the BOA needs to be informed. // // This call is equivalent to the following call sequence: // Echo_ptr myobjRef = myobj->_this(); // boa->dispose(myobjRef); // CORBA::release(myobjRef); boa->destory(); orb->NP_destory(); return 0; }
// eg2_impl.cc - This is the source code of example 2 used in Chapter 2 // "The Basics" of the omniORB2 user guide. // // This is the object implementation. // // Usage: eg2_impl // // On startup, the object reference is printed to cerr as a // stringified IOR. This string should be used as the argument to // eg2_clt. // #include <iostream.h> #include "omnithread.h" #include "echo.hh" #include "echo_i.cc" int main(int argc, char **argv) { CORBA::ORB_ptr orb = CORBA::ORB_init(argc,argv,"omniORB2"); CORBA::BOA_ptr boa = orb->BOA_init(argc,argv,"omniORB2_BOA"); Echo_i *myobj = new Echo_i(); myobj->_obj_is_ready(boa); { Echo_var myobjRef = myobj->_this(); CORBA::String_var p = orb->object_to_string(myobjRef); cerr << "'" << (char*)p << "'" << endl; } boa->impl_is_ready(); // Tell the BOA we are ready. The BOA's default behaviour is to block // on this call indefinitely. // Call boa->impl_shutdown() from another thread would unblock the // main thread from impl_is_ready(). // // To properly shutdown the BOA and the ORB, add the following calls // after impl_is_ready() returns. // // boa->destroy(); // orb->NP_destory(); return 0; }
// eg2_clt.cc - This is the source code of example 2 used in Chapter 2 // "The Basics" of the omniORB2 user guide. // // This is the client. The object reference is given as a // stringified IOR on the command line. // // Usage: eg2_clt <object reference> // #include <iostream.h> #include "echo.hh" #include "greeting.cc" extern void hello(CORBA::Object_ptr obj); int main (int argc, char **argv) { CORBA::ORB_ptr orb = CORBA::ORB_init(argc,argv,"omniORB2"); CORBA::BOA_ptr boa = orb->BOA_init(argc,argv,"omniORB2_BOA"); if (argc < 2) { cerr << "usage: eg2_clt <object reference>" << endl; return 1; } try { CORBA::Object_var obj = orb->string_to_object(argv[1]); hello(obj); } catch(CORBA::COMM_FAILURE& ex) { cerr << "Caught system exception COMM_FAILURE, unable to contact the " << "object." << endl; } catch(omniORB::fatalException& ex) { cerr << "Caught omniORB2 fatalException. This indicates a bug is caught " << "within omniORB2.\nPlease send a bug report.\n" << "The exception was thrown in file: " << ex.file() << "\n" << " line: " << ex.line() << "\n" << "The error message is: " << ex.errmsg() << endl; } catch(...) { cerr << "Caught a system exception." << endl; } orb->NP_destory(); return 0; }
// eg3_impl.cc - This is the source code of example 3 used in Chapter 2 // "The Basics" of the omniORB2 user guide. // // This is the object implementation. // // Usage: eg3_impl // // On startup, the object reference is registered with the // COS naming service. The client uses the naming service to // locate this object. // // The name which the object is bound to is as follows: // root [context] // | // text [context] kind [my_context] // | // Echo [object] kind [Object] // #include <iostream.h> #include "omnithread.h" #include "echo.hh" #include "echo_i.cc" static CORBA::Boolean bindObjectToName(CORBA::ORB_ptr,CORBA::Object_ptr); int main(int argc, char **argv) { CORBA::ORB_ptr orb = CORBA::ORB_init(argc,argv,"omniORB2"); CORBA::BOA_ptr boa = orb->BOA_init(argc,argv,"omniORB2_BOA"); Echo_i *myobj = new Echo_i(); myobj->_obj_is_ready(boa); { Echo_var myobjRef = myobj->_this(); if (!bindObjectToName(orb,myobjRef)) { return 1; } } boa->impl_is_ready(); // Tell the BOA we are ready. The BOA's default behaviour is to block // on this call indefinitely. // Call boa->impl_shutdown() from another thread would unblock the // main thread from impl_is_ready(). // // To properly shutdown the BOA and the ORB, add the following calls // after impl_is_ready() returns. // // boa->destroy(); // orb->NP_destory(); return 0; } static CORBA::Boolean bindObjectToName(CORBA::ORB_ptr orb,CORBA::Object_ptr obj) { CosNaming::NamingContext_var rootContext; try { // Obtain a reference to the root context of the Name service: CORBA::Object_var initServ; initServ = orb->resolve_initial_references("NameService"); // Narrow the object returned by resolve_initial_references() // to a CosNaming::NamingContext object: rootContext = CosNaming::NamingContext::_narrow(initServ); if (CORBA::is_nil(rootContext)) { cerr << "Failed to narrow naming context." << endl; return 0; } } catch(CORBA::ORB::InvalidName& ex) { cerr << "Service required is invalid [does not exist]." << endl; return 0; } try { // Bind a context called "test" to the root context: CosNaming::Name contextName; contextName.length(1); contextName[0].id = (const char*) "test"; // string copied contextName[0].kind = (const char*) "my_context"; // string copied // Note on kind: The kind field is used to indicate the type // of the object. This is to avoid conventions such as that used // by files (name.type -- e.g. test.ps = postscript etc.) CosNaming::NamingContext_var testContext; try { // Bind the context to root, and assign testContext to it: testContext = rootContext->bind_new_context(contextName); } catch(CosNaming::NamingContext::AlreadyBound& ex) { // If the context already exists, this exception will be raised. // In this case, just resolve the name and assign testContext // to the object returned: CORBA::Object_var tmpobj; tmpobj = rootContext->resolve(contextName); testContext = CosNaming::NamingContext::_narrow(tmpobj); if (CORBA::is_nil(testContext)) { cerr << "Failed to narrow naming context." << endl; return 0; } } // Bind the object (obj) to testContext, naming it Echo: CosNaming::Name objectName; objectName.length(1); objectName[0].id = (const char*) "Echo"; // string copied objectName[0].kind = (const char*) "Object"; // string copied // Bind obj with name Echo to the testContext: try { testContext->bind(objectName,obj); } catch(CosNaming::NamingContext::AlreadyBound& ex) { testContext->rebind(objectName,obj); } // Note: Using rebind() will overwrite any Object previously bound // to /test/Echo with obj. // Alternatively, bind() can be used, which will raise a // CosNaming::NamingContext::AlreadyBound exception if the name // supplied is already bound to an object. // Amendment: When using OrbixNames, it is necessary to first try bind // and then rebind, as rebind on it's own will throw a NotFoundexception if // the Name has not already been bound. [This is incorrect behaviour - // it should just bind]. } catch (CORBA::COMM_FAILURE& ex) { cerr << "Caught system exception COMM_FAILURE, unable to contact the " << "naming service." << endl; return 0; } catch (omniORB::fatalException& ex) { throw; } catch (...) { cerr << "Caught a system exception while using the naming service."<< endl; return 0; } return 1; }
// eg3_clt.cc - This is the source code of example 3 used in Chapter 2 // "The Basics" of the omniORB2 user guide. // // This is the client. It uses the COSS naming service // to obtain the object reference. // // Usage: eg3_clt // // // On startup, the client lookup the object reference from the // COS naming service. // // The name which the object is bound to is as follows: // root [context] // | // text [context] kind [my_context] // | // Echo [object] kind [Object] // #include <iostream.h> #include "echo.hh" #include "greeting.cc" extern void hello(CORBA::Object_ptr obj); static CORBA::Object_ptr getObjectReference(CORBA::ORB_ptr orb); int main (int argc, char **argv) { CORBA::ORB_ptr orb = CORBA::ORB_init(argc,argv,"omniORB2"); CORBA::BOA_ptr boa = orb->BOA_init(argc,argv,"omniORB2_BOA"); try { CORBA::Object_var obj = getObjectReference(orb); hello(obj); } catch(CORBA::COMM_FAILURE& ex) { cerr << "Caught system exception COMM_FAILURE, unable to contact the " << "object." << endl; } catch(omniORB::fatalException& ex) { cerr << "Caught omniORB2 fatalException. This indicates a bug is caught " << "within omniORB2.\nPlease send a bug report.\n" << "The exception was thrown in file: " << ex.file() << "\n" << " line: " << ex.line() << "\n" << "The error message is: " << ex.errmsg() << endl; } catch(...) { cerr << "Caught a system exception." << endl; } orb->NP_destory(); return 0; } static CORBA::Object_ptr getObjectReference(CORBA::ORB_ptr orb) { CosNaming::NamingContext_var rootContext; try { // Obtain a reference to the root context of the Name service: CORBA::Object_var initServ; initServ = orb->resolve_initial_references("NameService"); // Narrow the object returned by resolve_initial_references() // to a CosNaming::NamingContext object: rootContext = CosNaming::NamingContext::_narrow(initServ); if (CORBA::is_nil(rootContext)) { cerr << "Failed to narrow naming context." << endl; return CORBA::Object::_nil(); } } catch(CORBA::ORB::InvalidName& ex) { cerr << "Service required is invalid [does not exist]." << endl; return CORBA::Object::_nil(); } // Create a name object, containing the name test/context: CosNaming::Name name; name.length(2); name[0].id = (const char*) "test"; // string copied name[0].kind = (const char*) "my_context"; // string copied name[1].id = (const char*) "Echo"; name[1].kind = (const char*) "Object"; // Note on kind: The kind field is used to indicate the type // of the object. This is to avoid conventions such as that used // by files (name.type -- e.g. test.ps = postscript etc.) CORBA::Object_ptr obj; try { // Resolve the name to an object reference, and assign the reference // returned to a CORBA::Object: obj = rootContext->resolve(name); } catch(CosNaming::NamingContext::NotFound& ex) { // This exception is thrown if any of the components of the // path [contexts or the object] aren't found: cerr << "Context not found." << endl; return CORBA::Object::_nil(); } catch (CORBA::COMM_FAILURE& ex) { cerr << "Caught system exception COMM_FAILURE, unable to contact the " << "naming service." << endl; return CORBA::Object::_nil(); } catch(omniORB::fatalException& ex) { throw; } catch (...) { cerr << "Caught a system exception while using the naming service."<< endl; return CORBA::Object::_nil(); } return obj; }
// eg3_tieimpl.cc - This example is similar to eg3_impl.cc except that // the tie implementation skeleton is used. // // This is the object implementation. // // Usage: eg3_tieimpl // // On startup, the object reference is registered with the // COS naming service. The client uses the naming service to // locate this object. // // The name which the object is bound to is as follows: // root [context] // | // text [context] kind [my_context] // | // Echo [object] kind [Object] // #include <iostream.h> #include "omnithread.h" #include "echo.hh" // Real implementation, notice that it does not inherit from any stub class class Echo_i { public: Echo_i() {} virtual ~Echo_i() {} virtual char * echoString(const char *mesg); }; char * Echo_i::echoString(const char *mesg) { char *p = CORBA::string_dup(mesg); return p; } static CORBA::Boolean bindObjectToName(CORBA::ORB_ptr,CORBA::Object_ptr); int main(int argc, char **argv) { CORBA::ORB_ptr orb = CORBA::ORB_init(argc,argv,"omniORB2"); CORBA::BOA_ptr boa = orb->BOA_init(argc,argv,"omniORB2_BOA"); Echo_i *myimpl = new Echo_i(); _tie_Echo<Echo_i,1> *myobj = new _tie_Echo<Echo_i,1>(myimpl); myobj->_obj_is_ready(boa); { Echo_var myobjRef = myobj->_this(); if (!bindObjectToName(orb,myobjRef)) { return 1; } } boa->impl_is_ready(); // Tell the BOA we are ready. The BOA's default behaviour is to block // on this call indefinitely. return 0; } static CORBA::Boolean bindObjectToName(CORBA::ORB_ptr orb,CORBA::Object_ptr obj) { CosNaming::NamingContext_var rootContext; try { // Obtain a reference to the root context of the Name service: CORBA::Object_var initServ; initServ = orb->resolve_initial_references("NameService"); // Narrow the object returned by resolve_initial_references() // to a CosNaming::NamingContext object: rootContext = CosNaming::NamingContext::_narrow(initServ); if (CORBA::is_nil(rootContext)) { cerr << "Failed to narrow naming context." << endl; return 0; } } catch(CORBA::ORB::InvalidName& ex) { cerr << "Service required is invalid [does not exist]." << endl; return 0; } try { // Bind a context called "test" to the root context: CosNaming::Name contextName; contextName.length(1); contextName[0].id = (const char*) "test"; // string copied contextName[0].kind = (const char*) "my_context"; // string copied // Note on kind: The kind field is used to indicate the type // of the object. This is to avoid conventions such as that used // by files (name.type -- e.g. test.ps = postscript etc.) CosNaming::NamingContext_var testContext; try { // Bind the context to root, and assign testContext to it: testContext = rootContext->bind_new_context(contextName); } catch(CosNaming::NamingContext::AlreadyBound& ex) { // If the context already exists, this exception will be raised. // In this case, just resolve the name and assign testContext // to the object returned: CORBA::Object_var tmpobj; tmpobj = rootContext->resolve(contextName); testContext = CosNaming::NamingContext::_narrow(tmpobj); if (CORBA::is_nil(testContext)) { cerr << "Failed to narrow naming context." << endl; return 0; } } // Bind the object (obj) to testContext, naming it Echo: CosNaming::Name objectName; objectName.length(1); objectName[0].id = (const char*) "Echo"; // string copied objectName[0].kind = (const char*) "Object"; // string copied // Bind obj with name Echo to the testContext: try { testContext->bind(objectName,obj); } catch(CosNaming::NamingContext::AlreadyBound& ex) { testContext->rebind(objectName,obj); } // Note: Using rebind() will overwrite any Object previously bound // to /test/Echo with obj. // Alternatively, bind() can be used, which will raise a // CosNaming::NamingContext::AlreadyBound exception if the name // supplied is already bound to an object. // Amendment: When using OrbixNames, it is necessary to first try bind // and then rebind, as rebind on it's own will throw a NotFoundexception if // the Name has not already been bound. [This is incorrect behaviour - // it should just bind]. } catch (CORBA::COMM_FAILURE& ex) { cerr << "Caught system exception COMM_FAILURE, unable to contact the " << "naming service." << endl; return 0; } catch (omniORB::fatalException& ex) { throw; } catch (...) { cerr << "Caught a system exception while using the naming service."<< endl; return 0; } return 1; }
# dir.mk - This is the makefile to compile all the example programs used in # Chapter 2 The Basics. # This makefile is to be used under the OMNI development environment. # CXXSRCS = greeting.cc eg1.cc \ eg2_impl.cc eg2_clt.cc \ eg3_impl.cc eg3_clt.cc DIR_CPPFLAGS = $(CORBA_CPPFLAGS) CORBA_INTERFACES = echo eg1 = $(patsubst %,$(BinPattern),eg1) eg2_impl = $(patsubst %,$(BinPattern),eg2_impl) eg2_clt = $(patsubst %,$(BinPattern),eg2_clt) eg3_impl = $(patsubst %,$(BinPattern),eg3_impl) eg3_clt = $(patsubst %,$(BinPattern),eg3_clt) all:: $(eg1) $(eg2_impl) $(eg2_clt) $(eg3_impl) $(eg3_clt) clean:: $(RM) $(eg1) $(eg2_impl) $(eg2_clt) $(eg3_impl) $(eg3_clt) $(eg1): eg1.o $(CORBA_STUB_OBJS) $(CORBA_LIB_DEPEND) @(libs="$(CORBA_LIB)"; $(CXXExecutable)) $(eg2_impl): eg2_impl.o $(CORBA_STUB_OBJS) $(CORBA_LIB_DEPEND) @(libs="$(CORBA_LIB)"; $(CXXExecutable)) $(eg2_clt): eg2_clt.o $(CORBA_STUB_OBJS) $(CORBA_LIB_DEPEND) @(libs="$(CORBA_LIB)"; $(CXXExecutable)) $(eg3_impl): eg3_impl.o $(CORBA_STUB_OBJS) $(CORBA_LIB_DEPEND) @(libs="$(CORBA_LIB)"; $(CXXExecutable)) $(eg3_clt): eg3_clt.o $(CORBA_STUB_OBJS) $(CORBA_LIB_DEPEND) @(libs="$(CORBA_LIB)"; $(CXXExecutable))