Local Interfaces

Middleware News Brief (MNB) features news and technical information about Open Source middleware technologies.

Introduction

The Object Management Group (OMG) defines the Interface Definition Language (IDL) as part of the CORBA specification. The OMG has always used IDL to not only define unconstrained (potentially remote) interfaces, but also those local to a particular process (or part of CORBA's API). Originally, this was done in an informal manner with what was described as Pseudo IDL (PIDL). For example, in CORBA 2.4 the PortableServer::ServantManager object has its interface defined with the following IDL:

interface ServantManager{ };

The specification then explicitly states that this interface is locality constrained, which alerts the reader that this is not true IDL but PIDL.

The local keyword was added in the CORBA Component Model (CCM) Specification (orbos/99-07-1) and later included in the core CORBA 2.4 specification. This keyword can appear in IDL before the interface keyword and indicates that the interface can only be implemented by objects in the current process.

The CORBA 2.5 and 2.6 specifications converted many of the existing locality constrained interfaces to local interfaces. This includes interceptors, Codec, RT CORBA interfaces, POA, POAManager, policies, servant managers, and Current. For example, the servant manager and servant locator interfaces are now defined as:

  1. local interface ServantManager{ };
  2.  
  3. local interface ServantLocator : ServantManager {
  4. native Cookie;
  5. Servant preinvoke(in ObjectId oid,
  6. in POA adapter,
  7. in CORBA::Identifier operation,
  8. out Cookie the_cookie)
  9. raises (ForwardRequest);
  10. void postinvoke(in ObjectId oid,
  11. in POA adapter,
  12. in CORBA::Identifier operation,
  13. in Cookie the_cookie,
  14. in Servant the_servant
  15. );
  16. };

Programs that implement and use local interfaces can often do so without regard for the fact that these interfaces are local, but there are some important differences that you should be aware of.

The next section of this paper covers the details of local interfaces and how they can be used in IDL and your applications. The remaining sections focus on implementing and using a local interface using C++, as well as some issues involved in doing so. We also discuss how this affects the way servant managers are defined.

All example code in the section was tested using OCI TAO 1.2a, which is available from http://www.theaceorb.com.

Local Interfaces in IDL

Each interface in IDL is defined as either a local interface or an unconstrained interface based on whether it includes or does not include the local keyword. The CORBA specification defines the following special rules related to the definition and use of local interfaces in IDL:

  • A local interface may inherit from other local or unconstrained interfaces.
  • An unconstrained interface may not inherit from a local interface.
  • A valuetype may support a local interface.
  • Any IDL type may appear as a parameter, an attribute, a return type, or an exception declaration of a local interface.
  • The following are local types:
    • Local interfaces
    • Any non-interface type constructed using a local type
      • For example, a struct, union, or exception with a member that is a local interface is also itself a local type.
  • A local type may be used as a parameter, attribute, return type, or exception declaration of a local interface or of a valuetype.
  • A local type may not appear as a parameter, attribute, return type, or exception declaration of an unconstrained interface or as a state member of a valuetype.

Basically, these rules ensure that a local reference can never leave a process. The CORBA specification states that the usage of client-side language mappings for local types shall be identical to those of equivalent unconstrained types except for the following restrictions:

  • Any attempt to marshal a local object reference results in a MARSHAL exception. This means:
    • Local types cannot be marshaled
    • References to local objects cannot be converted to strings
    • CORBA::Any objects containing local types cannot be marshaled
  • The Dynamic Invocation Interface (DII) is not supported for local references.
  • Asynchronous Method Invocations are not supported for local references.
  • Invocations on local objects are not ORB mediated. Specifically:
    • Parameter copy semantics are not honored
    • Interceptors are not invoked
    • The execution context of a local object does not have Current object contexts that are distinct from those of the caller
    • Implementations of local interfaces are responsible for providing the parameter copy semantics expected by clients.
  • Local objects have no inherent identities beyond their implementations' identities as programming objects. The lifecycle of the implementation is the same as the lifecycle of the reference.
  • Instances of local objects defined as part of OMG specifications shall be exposed through the CORBA::ORB::resolve_initial_references operation or through some other local object obtained from resolve_initial_references.

ORB implementations are also required to minimize the overhead associated with invocations on local objects. Local objects are implemented in a language mapping-specific nature but always via CORBA::LocalObject, which derives from CORBA::Object. Many of the normal operations allowed by the CORBA::Object interface are inappropriate for local objects and throw the NO_IMPLEMENT exception. These operations include:

  • get_interface
  • get_domain_managers
  • get_policy
  • get_client_policy
  • set_policy_overrides
  • get_policy_overrides
  • validate_connection
  • get_component.

Local Objects in the C++ Mapping and TAO

settings blue

Sidebar

The changes to support local objects have not been integrated and released in the C++ Mapping specification. This section assumes that the basic C++ mapping from the original CCM specification (orbos/99-07-01) is incorporated into the next C++ mapping release but also discusses some likely changes that may occur before the mapping is approved. It also discusses OCI TAO 1.2a's local object implementation and where it differs from the existing specifications.

You implement local objects in C++ by deriving from both CORBA::LocalObject and the C++ class that the interface maps to (the same class name as the interface name). The CORBA::LocalObject class implements the same _add_ref() and _remove_ref() member functions that the ServantBase class also defines. The original CCM specification states that the default implementations of these functions do nothing. TAO implements these as the specification states, but it is likely that the eventual C++ Mapping will specify that reference counting is done by default.

The original CCM specification neglects to state how local object references can be obtained given a local object. Because TAO is implemented so that _ptr types are C++ pointers, you can avoid this issue by directly assigning a pointer to the servant to _ptr and _var types. This should work for any ORB implementation that maps _ptr types to C++ pointers, but may not work with all ORB implementations. See the examples below for details of how the mapping may address this issue.

LOCAL OBJECTS IN THE JAVA MAPPING

The Java language mapping for local objects is included in the latest IDL to Java Language Mapping Specification (formal/01-06-06). Several open issues remain with this mapping, but the basics of the Java mapping are analogous with the C++ mapping and consistent with the Java mapping of unconstrained interfaces.

Implementations of local objects extend a special base interface, org.omg.CORBA.LocalObject instead of org.omg.CORBA.Object, as unconstrained objects do. Holder and helper classes are generated for local interfaces according to the normal mapping rules.

SERVANT LOCATORS AND ACTIVATORS IN TAO

Because servant managers (locators and activators) are now defined as local interfaces, the implementation and use of these objects are dependent on the local object language mapping. A straightforward interpretation of the local object mapping for servant managers leads to backward compatibility problems in the implementation, activation, and use of these objects. It is possible that the eventual C++ mapping may be defined in such a way as to be backward compatible with existing code. TAO is implemented using the normal local object mapping for servant managers and the rest of this section assumes this mapping.

Traditionally, these objects were implemented by deriving from POA_PortableServer::ServantLocator or POA_PortableServer::ServantActivator. With local objects your implementations should derive from CORBA::LocalObject and either PortableServer::ServantLocator or ;PortableServer::ServantActivator.

Here is a sample servant locator definition:

  1. class Messenger_Locator_i : public virtual PortableServer::ServantLocator,
  2. public virtual CORBA::LocalObject
  3. {
  4. public:
  5. Messenger_Locator_i();
  6.  
  7. virtual PortableServer::Servant preinvoke (
  8. const PortableServer::ObjectId &oid,
  9. PortableServer::POA_ptr poa,
  10. const char * operation,
  11. void * & cookie)
  12. throw (CORBA::SystemException, PortableServer::ForwardRequest);
  13.  
  14. virtual void postinvoke (const PortableServer::ObjectId & oid,
  15. PortableServer::POA_ptr poa,
  16. const char * operation,
  17. void * cookie,
  18. PortableServer::Servant servant)
  19. throw (CORBA::SystemException);
  20. };

The implementations of preinvoke() and postinvoke() remain the same as before the use of local objects.

Another difference is the activation of servant managers as local objects. Previously, the developer needed to activate the servant manager as if it was an unconstrained CORBA object (using _this() or another POA-related operation). Because local objects cannot be activated via a POA, this code now fails to compile and should be removed.

Lastly, you need to convert the servant type to an object reference so it can be passed to the POA. Once again, the normal POA operations for generation of object references cannot be used with local objects. In TAO, we can simply use an implicit cast to get from a pointer to the local object to the corresponding _var type.

Here is some sample code for initializing a servant locator and setting it on the POA:

  1. Messenger_Locator_i* locator_object = new Messenger_Locator_i;
  2. Messenger_Locator_var locator = locator_object;
  3. childPOA->set_servant_manager(locator.in());

This works because TAO implements the _ptr type as a pointer to the interface class.

Unfortunately, this assumption is not true of all ORB implementations and means that the above code is not portable to all C++ ORBs. Currently, it is planned that the C++ mapping will introduce a static member function in each local object that returns a _ptr.

This should make the code look something like this:

  1. Messenger_Locator_i* locator_object = new Messenger_Locator_i;
  2. Messenger_Locator_var locator =
  3. PortableServer::ServantLocator::_create_reference(*locator_object);
  4. childPOA->set_servant_manager(locator.in());

The Messenger_Locator_i implementation above requires the developer to explicitly delete the memory associated with the servant locator if you want to reclaim that memory.

TAO developers have three choices if they want to automatically recover this memory:

  1. Implement their own reference counting in _add_ref() and _remove_ref().
  2. Implement _add_ref() and _remove_ref() using CORBA::Object::_incr_refcnt() and CORBA::Object::_decr_refcnt().
  3. Derive from TAO_Local_RefCounted_Object instead of CORBA::LocalObject.

Number 1 above is the only current portable option as the other two depend on TAO-specific functions inCORBA::Object and a TAO-specific class, respectively. The future C++ mapping should either make reference counting the default for all local objects or provide a standard, reference-counted, local object class.

Local Object Example Using TAO

This section walks through a simple local object example from definition of the IDL to use of the interface. The actual application is intended to demonstrate the concepts and not necessarily reflect an actual application's use of local interfaces. Here is our application's IDL:

  1. local interface Messenger
  2. {
  3. boolean send_message (in string user_name,
  4. in string subject,
  5. inout string message);
  6. };

We implement this object using the following class:

  1. class Messenger_i : public virtual Messenger,
  2. public virtual CORBA::LocalObject
  3. {
  4. public:
  5. Messenger_i (void);
  6.  
  7. virtual ~Messenger_i (void);
  8.  
  9. virtual CORBA::Boolean send_message (const char * user_name,
  10. const char * subject,
  11. char *& message)
  12. throw(CORBA::SystemException);
  13. };

You may want to add reference counting as discussed in the previous section on servant managers. Since the C++ memory mapping for this function's arguments are essentially symmetric for the client and server sides, you can implement the send_message member function using the normal C++ server-side rules:

  1. CORBA::Boolean Messenger_i::send_message (const char * user_name,
  2. const char * subject,
  3. char *& message)
  4. throw(CORBA::SystemException)
  5. {
  6. cout << "Message from: " << user_name << endl;
  7. cout << "Subject: " << subject << endl;
  8. cout << "Message: " << message << endl;
  9. return 1;
  10. }

You can now create a local object.

Note that an ORB is not required to use a local object:

  1. int main (int argc, char* argv[]) {
  2. try {
  3. Messenger_i *messenger = new Messenger_i;
  4. Messenger_var messenger_obj (messenger);
  5. CORBA::String_var message = CORBA::string_dup ("Howdy");
  6.  
  7. messenger_obj->send_message ("the_dude", "Test", message.inout());
  8. } catch (...) {
  9. // Handle exceptions raised by local objects
  10. }
  11. }

This code assumes an ORB implementation that maps Messenger_ptr to Messenger* (as discussed in the previous example).

Summary

Local objects are an important mechanism in CORBA and are required to utilize many advanced CORBA features such as servant managers, portable interceptors, and policies. Almost all CORBA developers will use local objects, and many are going to need to define their own local interfaces and implement them. Although some questions remain as to the exact details of the C++ mapping, enough information is available to write code that should comply to the formal mapping with minimal modifications.