CORBA Client Implementation

April 30, 2008 at 3:20 am (CORBA) ()

Implementing a CORBA Client

This section covers what you need to know to use CORBA objects from the Java programming language. It examines OMG IDL interfaces, the Java programming language binding for IDL interfaces, object references, and requests, how to obtain object references, and how, as a client, to create distributed objects. After reading this section and completing the exercises, you should be able to write a client using the Java programming language. Again, the stock example is used to illustrate the client’s model of CORBA.

CORBA Objects are Described by IDL Interfaces

The OMG Interface Definition Language IDL supports the specification of object interfaces. An object interface indicates the operations the object supports, but not how they are implemented. That is, in IDL there is no way to declare object state and algorithms. The implementation of a CORBA object is provided in a standard programming language, such as the Java programming language or C++. An interface specifies the contract between code using the object and the code implementing the object. Clients only depend on the interface.

IDL interfaces are programming language neutral. IDL defines language bindings for many different programming languages. This allows an object implementor to choose the appropriate programming language for the object. Similarly, it allows the developer of the client to choose the appropriate and possibly different programming language for the client. Currently, the OMG has standardized on language bindings for the C, C++, Java, Ada, COBOL, Smalltalk, Objective C, and Lisp programming languages.

So by using OMG IDL, the following can be described without regards to any particular programming language:

  • Modularized object interfaces
  • Operations and attributes that an object supports
  • Exceptions raised by an operation
  • Data types of an operation return value, its parameters, and an object’s attributes

The IDL data types are:

  • Basic data types (long, short, string, float…)
  • Constructed data types (struct, union, enum, sequence)
  • Typed object references
  • The any type, a dynamically typed value

Again, IDL says nothing about object implementations. Here’s the IDL interface for the example stock objects:

module StockObjects {

  struct Quote {
    string symbol;
    long at_time;
    double price;
    long volume;
  };

  exception Unknown{};

  interface Stock {

    // Returns the current stock quote.
    Quote get_quote() raises(Unknown);

    // Sets the current stock quote.
    void set_quote(in Quote stock_quote);

    // Provides the stock description,
    // e.g. company name.
    readonly attribute string description;
  };

  interface StockFactory {

    Stock create_stock(
      in string symbol,
      in string description
    );
  };
};

Note that the above example defines an IDL module named StockObjects, which contains the:

  • Data structure Quote
  • Exception Unknown
  • Interface Stock
  • Interface StockFactory

The module defines a scope for these names. Within the module, a data structure Quote and an exception Unknown are defined and then used in the Stock interface. The Stock interface is used in the definition of the StockFactory interface. Also note that the parameters to operations are tagged with the keywords in, out, or inout. The in keyword indicates the data are passed from the client to the object. The out keyword indicates that the data are returned from the object to the client, and inout indicates that the data are passed from the client to the object and then returned to the client.

IDL declarations are compiled with an IDL compiler and converted to their associated representations in the target programming languages according to the standard language binding. (This course uses the Java language binding in all of the examples. Later you will see the Java binding in more depth.)

Object References and Requests

Clients issue a request on a CORBA object using an object reference. An object reference identifies the distributed object that will receive the request. Here’s a Java programming language code fragment that obtains a Stock object reference and then it uses it to obtain the current price of the stock. Note that the code fragment does not directly use CORBA types; instead it uses the Java types that have been produced by the IDL to Java compiler.

Stock theStock = ...
try {
    Quote current_quote =
              theStock.get_quote();
} catch (Throwable e) {
}

Object references can be passed around the distributed object system, i.e. as parameters to operations and returned as results of requests. For example, notice that the StockFactory interface defines a create() operation that returns an instance of a Stock. Here’s a Java client code fragment that issues a request on the factory object and receives the resulting stock object reference.

StockFactory factory = ...
Stock theStock = ...
try {
   theStock = factory.create(
                "GII",
                "Global Industries Inc.");
} catch (Throwable e) {
}

Note that issuing a request on a CORBA object is not all that different from issuing a request on a Java object in a local program. The main difference is that the CORBA objects can be anywhere. The CORBA system provides location transparency, which implies that the client cannot tell if the request is to an object in the same process, on the same machine, down the hall, or across the planet.

Another difference from a local Java object is that the life time of the CORBA object is not tied to the process in which the client executes, nor to the process in which the CORBA object executes. Object references persist; they can be saved as a string and recreated from a string.

The following Java code converts the Stock object reference to a string:

String stockString =
     orb.object_to_string(theStock);

The string can be stored or communicated outside of the distributed object system. Any client can convert the string back to an object reference and issue a request on the distributed object.

This Java code converts the string back to a Stock object reference:

org.omg.CORBA.Object obj =
    orb.string_to_object(stockString);
Stock theStock = StockHelper.narrow(obj);

Note that the resulting type of the string_to_object() method is Object, not Stock. The second line narrows the type of the object reference from Object to Stock. IDL supports a hierarchy of interfaces; the narrow() method call is an operation on the hierarchy.

IDL Type System

IDL interfaces can be defined in terms of other IDL interfaces. You previously saw a Stock interface that represents the basic behavior of a stock object.

Consider another IDL module:

module ReportingObjects {

  exception EventChannelFailure{};

  interface Reporting {

    // Receive events in push mode
    CosEventComm::PushSupplier push_events(
        in CosEventComm::PushConsumer consumer)
      raises(EventChannelFailure);

    // Receive events in pull mode
    CosEventComm::PullSupplier pull_events(
        in CosEventComm::PullConsumer consumer)
      raises(EventChannelFailure);

  };

};

The Reporting interface supports the registration of interest in events. (Don’t worry about the details of using the CORBA Event Service.)

Given the definition of the Stock interface and a Reporting interface, it is now possible to define a new ReportingStock interface in terms of Reporting and Stock.

interface ReportingStock: Reporting, Stock {
};

A ReportingStock supports all of the operations and attributes defined by the Reporting interface as well as all of those defined by the Stock interface. The ReportingStock interface inherits the Stock interface and the Reporting interface. Graphically this is represented as:

All CORBA interfaces implicitly inherit the Object interface. They all support the operations defined for Object. Inheritance of Object is implicit; there is no need to declare it.

Object references are typed by IDL interfaces. In a Java program you could type an object reference to be a ReportingStock.

ReportingStock theReportingStock;

Clients can pass this object reference to an operation expecting a supertype. For example assume there is an EventManager interface that has a register operation that takes an object reference typed by the Reporting interface.

interface EventManager {
        :
    void register(in Reporting event_supplier);
        :
};

The following is a legal request because a ReportingStock is a Reporting.

EventManager manager = ...
ReportingStock theReportingStock = ...
manager->register(theReportingStock);
// ok

However, the following is not a legal request because a Stock is not a Reporting:

EventManager manager = ...
Stock theStock = ...
manager->register(theStock); // type error
IDL Type Operations

Given that IDL interfaces can be arranged in a hierarchy, a small number of operations are defined on that hierarchy. The narrow() operation casts an object reference to a more specific type:

org.omg.CORBA.Object obj = ...
Stock theStock = StockHelper.narrow(obj);

The is_a() operation, determines if an object reference supports a particular interface:

if (obj._is_a(StockHelper.id()) ...

The id() operation defined on the helper class returns a repository id for the interface. The repository id is a string representing the interface. For the stock example, the repository id is:

IDL:StockObjects/Stock:1.0

Finally, it is possible to widen an object reference, that is cast it to a less specific interface:

Stock theStock = theReportingStock;

There are no special operations to widen an object reference. It is accomplished exactly as in the Java programming language.

Request Type Checking

The IDL compiler for Java programming language generates client-side stubs, which represent the CORBA object locally in the Java programming language. The generated code also represents in the Java programming language all of the IDL interfaces and data types used to issue requests. The client code thus depends on the generated Java code.

As you previously saw, passing an object reference typed by the Stock interface to the event manager would be illegal because the Stock interface does not inherit the Reporting interface. The Java compiler, not the IDL compiler, would catch this error at compile time.

IDL to Java Binding

The Java binding for IDL maps the various IDL constructs to corresponding Java constructs. The following table shows how the IDL constructs are represented in the Java programming language. For comparison, the C++ binding is also shown.

IDL Java C++
module package namespace
interface interface abstract class
operation method member function
attribute pair of methods pair of functions
exception exception exception

Each of the IDL data types are represented in the Java programming language as follows:

IDL Type Java Type
boolean boolean
char / wchar char
octet byte
short / unsigned short short
long / unsigned long int
long long / unsigned long long long
float float
double double
string / wstring String

When discussing data type mapping, one term you run across frequently is marshaling. Marshaling is the conversion of a language-specific data structure into the CORBA IIOP streaming format. IIOP data can then be transmitted over a network to its destination, where it is then unmarshaled from IIOP back into a language-dependent data structure.

IDL to Java Compiler

CORBA products provide an IDL compiler that converts IDL into the Java programming language. The IDL compiler available for the Java 2 SDK is called idltojava. The IDL compiler that comes with VisiBroker for Java is called idl2java.

For the stock example, the command “idltojava Stock.idl” generates the files listed below. (The VisiBroker ORB generates the same files with the exception that the stub file is called _st_Stock.java, rather than _StockStub.java.)

Stock.java The IDL interface represented as a Java interface
StockHelper.java Implements the type operations for the interface
StockHolder.java Used for out and inout parameters
_StockStub.java Implements a local object representing the remote CORBA object. This object forwards all requests to the remote object. The client does not use this class directly.

The developer compiles the IDL using the IDL compiler and then compiles the generated code using the Java compiler. The compiled code must be on the classpath of the running Java program.

Obtaining Object References

You may have noticed that there are three fundamental mechanisms in which a piece of code can obtain an object reference:

  • It can be can be passed to it as a parameter
  • It can be returned as the result of issuing a request
  • It can be obtained by converting a string into an object
  • reference

These fundamental mechanisms are supported by the ORB. Using these mechanisms, it is possible to define higher level services for locating objects in the distributed object system.

The Client’s Model of Object Creation

You may decide to export the ability to create an object to the distributed object system. You can accomplish this by defining a factory for the object. Factories are simply distributed objects that create other distributed objects.

There is nothing special about a factory. It is just another distributed object: It has an IDL interface, it is implemented in some programming language, and clients issue standard CORBA requests on factory objects.

There is no standard interface for a factory. Recall in the StockObjects example, the factory interface is:

interface StockFactory {
  Stock create_stock(
    in string stock_symbol,
    in string stock_description);
};

To create a stock object, a client simply issues a request on the factory.

Another object implementor could define an object factory differently.

Exceptions

As you have seen in the stock example, CORBA has a concept of exceptions that is very similar to that of the Java programming language; naturally, CORBA exceptions are mapped to Java exceptions. When you issue a CORBA request, you must use the Java programming language’s try and catch keywords.

There are two types of CORBA exceptions, System Exceptions and User Exceptions. System Exceptions are thrown when something goes wrong with the system–for instance, if you request a method that doesn’t exist on the server, if there’s a communication problem, or if the ORB hasn’t been initialized correctly. The Java class SystemException extends RuntimeException, so the compiler won’t complain if you forget to catch them. You need to explicitly wrap your CORBA calls in try...catch blocks in order to recover gracefully from System Exceptions.

CORBA System Exceptions can contain “minor codes” which may provide additional information about what went wrong. Unfortunately, these are vendor-specific, so you need to tailor your error recovery routines to the ORB you’re using.

User Exceptions are generated if something goes wrong inside the execution of the remote method itself. These are declared inside the IDL definition for the object, and are automatically generated by the idltojava compiler. In the stock example, Unknown is a user exception.

Since User Exceptions are subclasses of java.lang.Exception, the compiler will complain if you forget to trap them (and this is as it should be).

Post a Comment

You must be logged in to post a comment.