Whole document tree Developing ServersThis section introduces Java IDL support for servers of transient CORBA objects. The Hello World example includes a transient object server. Topics in this section include:
Transient and Persistent ObjectsA transient CORBA object has the same lifetime as the execution of the server process that creates it. When a server terminates, its transient objects disappear with it and any object references held by the client become invalid. Transient objects are often used to effect asynchronous, or callback communication between applications and objects. For example, suppose a timing service notifies a client application when an interval expires; it could do so as follows:
The arrangement, in other words, is much like a callback in procedural programming. By contrast, a persistent object lives until it is explicitly destroyed. If a client has a reference to a persistent CORBA object, that reference can be used even if the object's server is not running -- an ORB daemon will start the server when the ORB receives an invocation on the object. At present, Java IDL supports only transient object servers. Although persistent object servers can't yet be written using Java IDL, Java applets and applications can be clients of persistent CORBA objects whose servers are written in other languages, such as C++, or in other Java ORB implementations. Servants and Servant Base ClassesTo implement a server for transient CORBA objects of some type, you write a Java class called a servant, which inherits from a class called the servant base; servant base classes are optionally generated by the idlj compiler. An instance of a transient CORBA object is implemented by an instance of its servant class. The servant base class is the CORBA type-specific interface between the ORB and the servant code (skeleton) for the type. It unmarshals incoming parameters, invokes servant methods, marshals results, and directs the ORB to return results to the client ORB. A servant class extends its servant base with a method for each operation in the IDL interface definition it implements. (OMG IDL attributes require one method if they are read only, two if they are writable.) For example, consider the following OMG IDL: module Notifiers { interface SimpleNotifier { void alert (in long alert_id); }; }; A servant for this type might look as follows: import Notifiers.* class SimpleNotifierServant extends _SimpleNotifierImplBase { void alert (int alert_id) { // do something about the alert ... return; } } The server class could create an instance of SimpleNotifierServant as follows: SimpleNotifierServant SimpleNotifier = new SimpleNotifierServant(); orb.connect(SimpleNotifier); The orb.connect() call registers the object and the servant with the ORB so that the ORB will invoke the servant when the CORBA object is invoked. When the server terminates, the ORB destroys its records for that server's CORBA objects so that subsequent invocations will result in an OBJECT_NOT_EXIST exception. A server can alternatively annul its CORBA objects before terminating by calling orb.disconnect(). Using Servant ObjectsA transient CORBA object is implemented by a Java servant object. A servant object can be passed as a parameter to any method that needs a CORBA object reference of that type. For example, suppose a Java program wants to learn when the price of Sun Microsystems stock rises above 150. The program has obtained a reference to a CORBA stock-watcher object that provides the following operation (shown in IDL and Java): // IDL interface StockWatcher { void set_threshold( in string stock_symbol, in short stock_price, in Notifiers::SimpleNotifier notifier_obj); }; // Java void set_threshold( String stock_symbol, short stock_price, Notifiers.SimpleNotifier notifier_obj) { // set the threshold ... } To call SimpleNotifier SUNWnotifier = new SimpleNotifierImpl(); int stockPrice = 150; orb.connect(SUNWnotifier); StockWatcher aStockWatcher = // code to get a stock-watcher reference; aStockWatcher.set_threshold ("SUNW", stockPrice, SUNWnotifier); Dynamic Skeleton InterfaceDSI allows servers to serve a servant object without prior (compile time) knowledge of the object's interface. Instead of using skeleton code compiled from the IDL interface definition, the server constructs an operation invocation dynamically. Why Use DSI?Use DSI for servers that cannot have compile-time knowledge of the implementation interface. For example, you might need a bridging application to allow CORBA clients to invoke services residing in a COM environment while allowing COM clients the same access to services residing in the CORBA realm. Each environment has its own, very different way of building requests and responses. This bridging application would use DSI to convert a CORBA client request to a format understood by a COM server. And it would use DII to convert a COM client request to a format understood by a CORBA server. The application programmer writes all the code to perform this work. Contrast this with a typical static object invocation. The server has access to the compiled skeletons for the interfaces being invoked upon. These skeletons are generated by compiling the IDL interface definitions with the idlj compiler. When the ORB receives a request, it uses the skeleton code to build the operation arguments on the server side and to send back any result. Steps to Using DSITo use DSI, implement and register a dynamic servant by doing the following:
DSI ExampleHere is DSI.idl, which defines a very simple interface to be implemented dynamically. //IDL module JavaIDL { interface DSIExample { void print_args(in string arg1, in short arg2); }; }; Here is the corresponding Java code, from the file DSIServer.java. This portion shows the implementation of //Java import java.util.*; import org.omg.CORBA.*; // Dynamic servant class implementation class DSIExampleServantImpl extends DynamicImplementation { // Store the repository ID for the interface implemented static String[] myIds = {"IDL:JavaIDL/DSIExample:1.0"}; // Create a reference to the ORB ORB orb; DSIExampleServantImpl(ORB orb) { this.orb = orb; } // Implementation of invoke() for handling dynamic requests public void invoke(ServerRequest request) { try { System.out.println("DSI: invoke called, op = "+ request.op_name()); // Create an NVList to hold the parameters NVList nvlist = orb.create_list(0); // Check if the request is for the operation // "print_args" if (request.op_name().equals("print_args") == true) { // Add first argument to NVList Any any1 = orb.create_any(); any1.insert_string(""); nvlist.add_value("arg1", any1, ARG_IN.value); // Add second argument to NVList Any any2 = orb.create_any(); any2.insert_short((short)0); nvlist.add_value("arg2", any2, ARG_IN.value); //Pass the NVList to the request, to get values request.params(nvlist); // Extract values and print arguments System.err.println("Argument 1: In value: " + nvlist.item(0).value().extract_string()); System.err.println("Argument 2: In value: " + nvlist.item(1).value().extract_short()); TypeCode result_tc = orb.get_primitive_tc(TCKind.tk_void); Any result_any = orb.create_any(); result_any.type(result_tc); // Set the void result request.result(result_any); } } catch ( Exception ex ) { ex.printStackTrace(); System.out.println("DSIExample: Exception thrown: " + ex); } } // Implement the _ids() method to return repository ID of interface public String[] _ids() { return myIds; } } // File DSIServer.java continuesHere is the remainder of DSIServer.java, showing a standard server implementation. Note that you register a dynamic servant with the ORB by using the same operation you would for a static servant. // Java // DSIServer implementation public class DSIServer { // Main public static void main(String[] args) { try { // Access and initialize the ORB org.omg.CORBA.ORB orb = ORB.init(args, null); // Create an instance of the dynamic implementation DSIExampleServantImpl servant = new DSIExampleServantImpl(orb); // Register the dynamic servant with the ORB orb.connect(servant); // Write IOR into file. // Alternatively, the naming service could be used. OutputStream f = new FileOutputStream( System.getProperty("user.home") + System.getProperty("file.separator") + "DSI.ior") ; DataOutputStream out = new DataOutputStream(f) ; String ior = orb.object_to_string(servant) ; out.writeBytes(ior) ; out.close(); System.out.println("IOR is " + ior) ; // Wait for requests from client java.lang.Object sync = new java.lang.Object(); synchronized(sync){ sync.wait(); } } catch (Exception ex) { ex.printStackTrace(); System.err.println("DSIServer: Exception thrown: " + ex); } } } Delegation-based Skeletons (Ties)Because Java allows a class to inherit implementation from only a single superclass, it is sometimes awkward to provide CORBA object implementations for objects that rightfully belong in a class hierarchy--the servant base class occupies the single available superclass position. These objects can become CORBA servants most easily by use of delegation-based skeletons. Delegation-based skeletons move the required CORBA operations from a servant base class (from which your object must inherit) to the generated Tie class. The Tie class acts as the skeleton for this object, receiving invocations from the ORB and delegating them to the servant that actually does the work. The idlj compiler is installed to the J2SDK's bin directory when you installed the Java 2 Platform, Standard Edition, v1.3. These steps are based on this version of the J2SDK.
To use ties, first compile the IDL interface using the
-ftie Compiling Frog.idl with idlj generates the standard client and
server files (servant classes, etc.) and the special files for delegation-based
skeletons: _FrogTie and _FrogOperations.
The standard implementation of this IDL looks like this:
Note that, because FrogImpl.java inherits its CORBA functionality
(such as the dispatch upcall from the ORB) from _FrogImplBase, it must contain
the implementation code for the rest of its methods; it cannot
inherit them even if they are identical to those in another class.
The same class, using a delegation-based skeleton, would look
like this:
Using ties, FrogImpl reserves its superclass slot for inheritance
of amphibian behavior while implementing the CORBA operations defined in
the IDL interface (and in _FrogOperations).
Note the difference in the orb.connect() call. With a
delegation-based skeleton, you pass a new tie class instance to the
ORB, rather than passing the actual object implementation.
Clients | Servers | Exceptions | Initialization | Naming
|