New York University

Computer Science Department

Courant Institute of Mathematical Sciences

 

DII, IR, DSI, and IMR

 

Course Title: Application Servers                                           Course Number: g22.3033-011

Instructor: Jean-Claude Franchitti                                            Session: 4

 

DII: Dynamic Invocation Interface

 

Client stubs used in most implementations provide the facility to statically make invocations to the target objects. A client must have an IDL stub for each interface it uses on the server. Obviously, this can be a limitation, as you won't be able to use the service offered by a newly created object unless you know its interface, and have the appropriate stubs to generate the request. This is where the Dynamic Invocation Interface comes in.

 

In essence, the DII is a generic client-side stub capable of forwarding requests to any object. This is realized through a run time interpretation of request parameters and operation identifiers.

 

Instead of using an existing request available via some specific stubs she generated with the compilers and precompilers, you can possibility create a new one. By calling a specific operation, you can obtain an object reference to a Request object, which is in fact implemented as part of the ORB. Once you have a Request objects, you can gain nearly unlimited power over how you want your request to be made. You can use all the usual services offered by the object like filling in request parameters and calling the operation represented by the request object.

 

But the DII can offer yet others facilities to developers who accept the burdens of creating step-by-step a request: beside the classic stub-like mechanism, Request objects allow deferred synchronous invocations and one-way invocations.

 

Thanks to the DII, a request can thus be invoked in one of the following ways.

 

·        The invoke call send the request and gets back the result. It works exactly the same way as it does for the static stubs.

·        The send call returns control to the program, which will decide when to issue a get_response call. This is the deferred synchronous invocations.

·        The send call can also be defined to be ``one-way'', specifying that no response is needed. This is known as the one-way invocation mechanism.

 

However, the flexibility provided by the DII can be costly. You will lose time at both the execution of the request and the development phase of your application. The code that you will have to write is sightly more complex than the RPC-like code you used when your invocation went through the compiler-generated stubs. These static invocations can be achieved in a single operation, but if you want to use the Dynamic Invocation Interface to do the same single invocation, you will have to go through the following calls:

 

  1. Object::get_interface to obtain an InterfaceDef object;
  2. InterfaceDef::describe_interface to get information about the operations supported by the object;
  3. Object::create_request to create a Request object for the required operation;
  4. Request::add_arg for each request argument;
  5. Request::invoke to actually make the request.

 

Consequently, for most applications, making requests through static stubs is far more efficient than through the DII. Nevertheless, in the dynamic networks that are being build today, the need is real for the flexibility that the DII provides.

 

In summary, the Dynamic Invocation Interface, or DII, allows client applications to use server objects without knowing the type of those objects at compile time. The DII lets a client to obtain an instance of CORBA.Object and then make invocations on that object by dynamically constructing requests. The steps for making a DII request are:

 

 

1.      Obtain a reference to the object you wish to use.

2.      Create a Request object, specifying the name of the operation you wish to invoke.

3.      Initialize the parameters for the Request, specifying the parameter's data type and how it will be used. If the Request has a return value, it must also be initialized.

4.      Send the Request and wait for the response.

 

Note: The Interface Repository must be running in order for client applications that use the DII to function properly.

Request Parameters

Before you send a Request, you first must define the type and the usage for each parameter. The type, such as String or int, defines how a parameter will be marshalled before being sent to the server and how it will be un-marshalled when a response is received from the server. A parameter's usage is defined as one of the following:

 

 

·        in - Used for input only.

·        inout - Used for input and may be modified by the server upon return.

·        out - Use only for output.

 

Note: Always initialize the parameters before sending a Request. Failure to do so will result in marshalling errors and may even cause the server to abort.

 

The DiiClient Example

Consider the Bank client program shown below, which has been adapted to use the DII.

 

 

// DiiClient.java

public class DiiClient {

    public static void main(String args[]) {

        if(args.length != 2) {

            System.out.println("Usage java Client <mgr-ior> <acct-name>\n");

            return;

        }

        String ior = args[0];

        String accountName = args[1];

        try {

            CORBA.ORB orb = CORBA.ORB.init();

            CORBA.Object object;

            { // Open the account

                object = orb.string_to_object(ior);

                CORBA.Request request = object._request("open");

                request.arguments().add_value("name",

                    new CORBA.Any().from_string(accountName), CORBA.ARG_IN.value);

                request.result().value().from_Object(null);

                request.invoke();

                object = request.result().value().to_Object();

            }

            { // Get the balance

                CORBA.Request request = object._request("balance");

                request.result().value().from_float(0f);

                request.invoke();

                float balance = request.result().value().to_float();

                System.out.println ("The balance in " + accountName +

                    "'s account is $" +  balance);

            }>

        }

        catch(CORBA.SystemException e) {

            System.out.println(e);

        }

    }

}

 

Figure 7-1 The Bank client, adapted to use the DII.

 

The first part of the program checks for the required command line arguments.

 

 

public static void main(String args[]) {

    if(args.length != 2) {

        System.out.println("Usage java Client  \n");

        return;

    }

    String ior = args[0];

    String accountName = args[1];

    // ...

}

 

The first command line argument is an Internet Object Reference, or IOR. An IOR can be created by calling the method CORBA.ORB.object_to_string on the server object.

 

The Balance Request

The second DII request is the "balance" request shown below. It is described first because it is simpler that the first request.

 

Here are the steps for the "balance" request:

 

 

·        Create a CORBA.Request, using the CORBA.Object._request method, with an operation name of "balance."

·        Define all the input values, output values, and return values with the types defined in the signature of the balance method. In this example, the only argument is the return type float.

·        Invoke the request.

·        Once the invocation completes, extracted the return value as a float.

·        Display the return value.

 

{ // Get the balance

    CORBA.Request request = object._request("balance");

    request.result().value().from_float(0f);

    request.invoke();

    float balance = request.result().value().to_float();

    System.out.println("The balance in " + accountName + "'s account is $" +

        balance);

}

 

Figure 7-2 The balance request.

 

Obtaining an Object Reference

 

The first DII request in our example, shown  below, is somewhat more complicated that the "balance" example. Here are the steps to the first DII request:

 

 

·        Use the method CORBA.ORB.string_to_object to obtain the Bank.AccountManager object reference.

·        Create a Request whose operation is named "open".

·        Set the input name argument using the CORBA.Request.add_value method. Unlike output values and return values, input and inout arguments must provided the actual value to be passed. The value accountName is used in this case, and the type of the argument is specified as well.

·        Set the type of the return value. The whole point of DII requests is to be able to invoke operations without compile time information. Since you do not have the actual type of the argument, simply use the type CORBA.Object, which is a base interface of all interfaces.

·        Invoke the DII request.

·        Extract the result object from the request.

 

{ // Open the account

    object = orb.string_to_object(ior);

    CORBA.Request request = object._request("open");

    request.arguments().add_value("name",

        new CORBA.Any().from_string(accountName), CORBA.ARG_IN.value);

    request.result().value().from_Object(null);

    request.invoke();

    object = request.result().value().to_Object();

}

 

Figure 7-3 The first DII request.

 

Sending a DII Request

The Request interface provides several methods for sending the request, once it has been properly initialized. The invoke method, which sends the request and waits for a response before returning control to your client application, is the easiest to use. The non-blocking method send_deferred allows your client to send the request and then use the poll_response method to determine when the response is available. The get_response method will block the client application until a response is received.

 

The send_oneway method can be used to send a oneway request. Oneway requests do not involve a response being send from the object implementation. The result method returns a pointer to a NamedValue object that represents the return value.

Sending Multiple Requests

A sequence of DII Request objects can be created using Request[], defined in the CORBA.ORB class. A sequence of requests can be sent using the ORB methods send_multiple_requests_oneway or send_multiple_requests_deferred. If the sequence of requests is sent as a group of oneway requests, no response is expected from the server to any of the requests.

 

Receiving Responses

If the sequence of requests are sent using send_multiple_requests_deferred, the poll_next_response and get_next_response methods are used to receive the response for each request.

 

The ORB method poll_next_response can be used to determine if a response has been received from the server. This method returns true if one or more responses are available. This method returns false if there are no responses available.

 

The ORB method get_next_response can be used to receive a response. If no response is available, this method will block until a response is received. If you do not wish your client application to block, use the poll_next_response method to determine when a response is available.

 


IR: Interface Repository

 

What Is an Interface Repository?

 

We have just seen that get_interface was a service supported by the Object interface, and therefore all object references. This operation returns a reference to an InterfaceDef object that describes the object's interface and provides operations to obtain all the information about the interface's contents. Those InterfaceDef object are thus crucial to clients that need information about previously unknown objects. The Interface Repository is in fact the location where each ORB stores persistent data for those IDL interface declarations of objects.

 

The primary function of the IR is to provide the type information necessary to issue requests using the Dynamic Invocation Interface. Through the IR's generic methods, clients can obtain the description of all the registered object interfaces, the methods they support, the parameters they require, and the exceptions they may raise.

 

Thanks to all this metadata information they provide, Interface Repositories can serve several other purposes. ORBs can use them to check operation parameter types at run time, whether a request was issued through the DII or through a stub. They can also check the correctness of the inheritance graphs. Interface browsers can manipulate the IR information to help application developers locate potentially reusable software objects. And any object that lives on the ORB can obtain self-describing interfaces just by questioning the IR.

 

Since the ORBs can effectively interoperate to forward requests or share information, the IRs can be viewed as a run-time distributed database containing self-describing metadata about any CORBA object that registered its service over the network.

 

An interface repository (IR) contains descriptions of CORBA object interfaces. The data in an IR is the same as in IDL files–descriptions of modules, interfaces, operations, and parameters–but it's organized for runtime access by clients. A client can browse an interface repository (perhaps serving as an online reference tool for developers) or can look up the interface of any object for which it has a reference (perhaps in preparation for invoking the object with the dynamic invocation interface. Reading this handout will enable you to create an interface repository and access it with VisiBroker utilities or with your own code. For more information about interface repositories, see the CORBA 2.0 Specification produced by the OMG.

 

 

An interface repository (IR) is like a database of CORBA object interface information. There may or may not be instances satisfying the interfaces in an IR. The information in an IR is equivalent to the information in an IDL file (or files), but it is represented in a way that is easier for clients to use. Summarizing, an IR enables clients to learn about or update interface descriptions at runtime.

 

Clients that use interface repositories may also use the Dynamic Invocation Interface (DII). Such clients use an interface repository to learn about an unknown object's interface, and they use the DII to invoke methods on the object. However, there is no necessary connection between an IR and the DII. For example, imagine a drag-and-drop interface browser for developers; dragging a method description from the IR browser to an editor inserts a template method invocation into the developer's source code. In this example, the IR is used without the DII.

 

If you are using Visibroker, you create an interface repository with the VisiBroker irep program, which is the IR server (implementation). You can update or populate an interface repository with the VisiBroker idl2ir program, or you can write your own IR client that inspects an interface repository, updates it, or does both.

 

What Does an Interface Repository Contain?

 

An interface repository contains hierarchies of objects whose methods divulge information about interfaces. That may at first seem surprising, since interfaces are usually thought of as describing objects. But using a collection of objects to describe interfaces makes sense in a CORBA environment, because it requires no new mechanism such as a database.

 

As an example of the kinds of objects an IR can contain, consider that IDL files can contain IDL module definitions, and modules can contain interface definitions, and interfaces can contain operation (method) definitions. Correspondingly, an interface repository can contain ModuleDef objects which can contain InterfaceDef objects, which can contain OperationDef objects. Thus, from an IR ModuleDef, you can learn what InterfaceDefs it contains. The reverse is also true–given an InterfaceDef you can learn what ModuleDef it is contained in. All other IDL constructs–including exceptions, attributes, and parameters–can be represented in an interface repository.

 

An interface repository also contains typecodes. Typecodes are not explicitly listed in IDL files, but are automatically derived from the types (long, string, struct, and so on) that are defined or mentioned in IDL files. Typecodes are used to encode and decode instances of the CORBA any type.

 

How Many Interface Repositories Can You Have?

 

Interface repositories are like other objects–you can create as many as you like. There is no mandated policy governing the creation or use of IRs. You determine how interface repositories are deployed and named at your site. You may, for example, adopt the convention that a central interface repository contains the interfaces of all "production" objects, and developers create their own IRs for testing.

 

Note: Interface repositories are writable and are not protected by access controls. An erroneous or malicious client can corrupt an IR or obtain sensitive information from it.

If you want to use the get_interface() method defined for all objects, you must have at least one interface repository server running so the ORB can look up the interface in the IR. If no interface repository is available, or if the IR that the ORB binds to has not been loaded with an interface definition for the object, get_interface() raises an exception.

 

Visibroker Example Programs

 

This section describes a simple IR client that looks up a user-specified interface in an IR and prints its operations (methods) and attributes.

 

The PrintIDL program's command-line argument is the fully qualified name of an IDL interface, such as Bank::Account. After initializing the ORB and binding to an IR, the program looks up the interface name in the IR. If the repository lookup() method fails, the program notifies the user that the IR contains no such interface. If it succeeds, the program narrows the generic object returned by lookup() to an IR InterfaceDef. From the InterfaceDef, the program obtains a FullInterfaceDescription object. Using the FullInterfaceDescription, the program iterates first through its operations and then through its attributes, printing each operation or attribute's name as it is encountered.

 

Before this program can be tested, the following conditions should exist:

 

n      The OSAgent should be up and running.

n      An interface Repository should be started using irep.

n      The Interface Repository should be loaded with an IDL file either by the command line when you start the Interface Repository (the irep command) or by using the Load command in the graphical interface.

 

Code Sample 6-1 The PrintIDL.java example.

 

CODE:

// PrintIDL.java

 

public class PrintIDL {

  public static void main(String[] args) {

      if ( args.length == 0 ) {

         System.out.println("Usage: vbj PrintIDL idlName");

         System.exit(1);

      }

      String idlName = args[0];

      org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args,null);

      org.omg.CORBA.Repository rep =

          org.omg.CORBA.RepositoryHelper.bind(orb);

      org.omg.CORBA.InterfaceDef idef =

          org.omg.CORBA.InterfaceDefHelper.narrow(rep.lookup(idlName));

      if (idef == null)

          throw new org.omg.CORBA.INTF_REPOS

              ("The idlName is not an interface: " + idlName);

          org.omg.CORBA.InterfaceDefPackage.FullInterfaceDescription intDesc =

              idef.describe_interface();

          System.out.println("Operations:");

          for(int i = 0; i < intDesc.operations.length; i++)

              System.out.println("  " + intDesc.operations[i].name);

              System.out.println("Attributes:");

          for(int i = 0; i < intDesc.attributes.length; i++)

             System.out.println("  " + intDesc.attributes[i].name);

  }

}

 


DSI: Dynamic Skeleton Interface

With the components that were presented until now, an ORB can successfully deliver a request to any object implementation it connects to via static skeletons. Using the DSI, an ORB can now issue requests to any object over the network.

 

The DSI is a feature of the CORBA 2.0 specifications, and the reason why it was introduced in the CORBA architecture was interoperability. The Dynamic Skeleton Interface allows an ORB to construct a bridge to another ORB for invocation forwarding to that remote ORB's objects. When the remote object has executed the request, the result simply comes back to the first ORB using the same bridge mechanism. Without the DSI, such a scenario between objects living on different ORBs would be possible only if the invoking ORB had a linked skeleton to the target remote object, an unlikely case for all the objects on a dynamic network.

 

The DSI is a component that will be crucial in the dynamic, distributed networks that are being built. DSI allows an object to register itself with the ORB, receive operation requests from a client, process the requests, and return the results to the client without inheriting from a skeleton class. Read this handout if you want to understand how to implement dynamic skeletons. This handout includes the following major sections:

 

Overview

 

The Dynamic Skeleton Interface, or DSI, provides a mechanism for creating an object implementation that does not inherit from a generated skeleton interface. Normally, an object implementation is derived from a skeleton class generated by the idl2java compiler. DSI allows an object to register itself with the ORB, receive operation requests from a client, process the requests, and return the results to the client without inheriting from a skeleton class.

 

Note: From the perspective of a client application, an object implemented with DSI behaves just like any other ORB object. Clients do not need to provide any special handling for object implementations that use DSI.

 

The ORB presents a client operation request to a DSI object implementation by using the object's invoke method and passing a ServerRequest object. The object implementation is responsible for determining the operation being requested, interpreting the arguments associated with the request, invoking the appropriate internal method or methods to fulfill the request, and returning the appropriate values.

 

Implementing objects with DSI requires more manual programming activity than using the normal language mapping provided by object skeletons. Nevertheless, an object implemented with DSI can be very useful in providing inter-protocol bridging or in allowing one object to offer multiple interfaces.

Using Visibroker’s idl2java Compiler

 

The idl2java compiler has a flag (-portable) which, when switched on, generates skeleton code using DSI. To understand how to do any type of DSI: create an IDL file, generate with -portable, and look at the skeleton code.

 

Example Program

 

An example program that illustrates the use of DSI is included in the dynamic_bank directory in the java_examples directory of the VisiBroker distribution. This example is used to illustrate DSI concepts.

 

IDL:

// Bank.idl

 

module Bank {

    interface Account {

        float balance();

    };

    interface AccountManager {

        Account open(in string name);

    };

};

 

To use DSI for your object implementation, follow these steps:

 

1.      Instead of extending a skeleton object, design your object implementation so that it extends the org.omg.CORBA.DynamicImplementation abstract class.

2.      Declare and implement the invoke method, which the ORB will use to dispatch client requests to your object.

3.      Register your object implementation with the BOA in the normal way, using the boa.obj_is_ready method.

 

The DynamicImplementation Class

 

Any object implementations that you wish to use DSI should be derived from the DynamicImplementation base class, shown below. This class offers several constructors and the invoke method, which you, the programmer, must implement.

 

CODE:

abstract public class DynamicImplementation extends

org.omg.CORBA.portable.Skeleton {

 

    abstract void invoke(ServerRequest request);

 

    public DynamicImplementation()

    {

    ...

    }

 

    protected DynamicImplementation(String object_name, String repository_id)

    {

    ...

    }

 

    protected DynamicImplementation(String object_name,

        String repository_ids[]) {

    ...

    }

 

    ...

}

 

The code shown below illustrates the declaration of the Account and AccountManager objects that are to be implemented with DSI. Both are derived from the DynamicImplementation class, which adds the invoke method. The ORB uses the invoke method to pass client operation requests to the object in the form of ServerRequest objects.

 

CODE:

class Account extends org.omg.CORBA.DynamicImplementation {

 

    Account(float balance) {

        super(name, "IDL:Bank/Account:1.0");

    }

    void invoke(org.omg.CORBA.ServerRequest request) {

    }

    ...

}

 

class AccountManager extends org.omg.CORBA.DynamicImplementation {

    AccountManager(String name) {

        super(name, "IDL:Bank/AccountManager:1.0");

    }

    void invoke(org.omg.CORBA.ServerRequest request) {

    }

    ...

}

 

Specifying Repository Ids

 

The first form of the DynamicImplementation class' constructor allows you to create a server object, specifying a string that represents the repository identifier of the object that is being implemented. Note that both the Account and AccountManager class declare a constructor that specifies the appropriate repository identifier. To determine the correct repository identifier to specify, start with the IDL interface name of an object and use the following steps:

 

  1. Replace all non-leading instances of the delimiter "::" with "/".
  2. Add "IDL:" to the beginning of the string.
  3. Add ":1.0" to the end of the string.

 

For example, Figure 1 below shows an IDL interface name and Figure 2 shows the resulting repository identifier string.

 

Figure 1 An IDL interface name.

 

Bank::AccountManager

 

 

Figure 8-2 The resulting repository identifier.

 

IDL:Bank/AccountManager:1.0

 

The ServerRequest Class

 

A ServerRequest object is passed as a parameter to an object implementation's invoke method. The ServerRequest object represents the operation request and provides methods for obtaining the name of the requested operation, the parameter list, and the context. It also provides methods for setting the result to be returned to the caller and for reflecting exceptions.

 

CODE:

abstract public class ServerRequest {

    abstract public java.lang.String op_name();

    abstract public org.omg.CORBA.Context ctx();

    abstract public void params(

        org.omg.CORBA.NVList params

    );

    abstract public void result(

        org.omg.CORBA.Any result

    );

    abstract public void except(

        org.omg.CORBA.Any except

    );

}

 

Implementing the Account Object

 

The Account object in our example offers only one method, so the processing done by its invoke method is fairly straightforward.

 

The invoke method first checks to see if the requested operation has the name "balance". If the name does not match, a BAD_OPERATION exception is raised. If the Account object were to offer more than one method, the invoke method would need to check for all possible operation names and use the appropriate internal methods to process the operation request.

 

Since the balance method does not accept any parameters, there is no parameter list associated with its operation request. The balance method is simply invoked and the result is packaged in an Any object that is returned to the caller, using the ServerRequest.result method.

 

CODE:

class Account extends org.omg.CORBA.DynamicImplementation {

    Account(float balance) {

        super(null, "IDL:Bank/Account:1.0");

        _balance = balance;

    }

 

    public void invoke(org.omg.CORBA.ServerRequest request) {

        // make sure the operation name is correct

        if(!request.op_name().equals("balance")) {

            throw new org.omg.CORBA.BAD_OPERATION();

        }

 

        // no parameters, so simply invoke the balance operation

        float balance = this.balance();

        // create an Any for the result

        org.omg.CORBA.Any balanceAny = org.omg.CORBA.ORB.init().create_any();

        // put in the balance,

        balanceAny.insert_float(balance);

        // set the request's result

        request.result(balanceAny);

    }

    float balance() {

        return _balance;

    }

    private float _balance;

}

 

Implementing the AccountManager Object

 

Like the Account object, the AccountManager offers only one method. However, the AccountManager.open method does accept an account name parameter. This makes the processing done by the invoke method a little more complicated. The code below shows the implementation of the AccountManager.invoke method.

 

The method first checks to see that the requested operation has the name "open". If the name does not match, a BAD_OPERATION exception is raised. If the AccountManager object were to offer more than one method, the invoke method would need to check for all possible operation names and use the appropriate internal methods to process the operation request.

 

CODE:

import java.util.*;

 

class AccountManager extends org.omg.CORBA.DynamicImplementation {

    AccountManager(String name) {

        super(name, "IDL:Bank/AccountManager:1.0");

    }

    public void invoke(org.omg.CORBA.ServerRequest request) {

        // make sure the operation name is correct

        if(!request.op_name().equals("open")) {

            throw new org.omg.CORBA.BAD_OPERATION();

        }

        // create an empty parameter list

        org.omg.CORBA.NVList params = _orb().create_list(0);

        // create an Any for the account name parameter

        org.omg.CORBA.Any nameAny = _orb().create_any();

        // set the Any's type to be a string

        nameAny.type(_orb().get_primitive_tc(org.omg.CORBA.TCKind.tk_string));

        // add "in" the parameter to the parameter list

        params.add_value("name", nameAny, org.omg.CORBA.ARG_IN.value);

        // obtain the parameter values from the request

        request.params(params);

        // get the name parameter from the Any

        String name = nameAny.extract_string();

        // invoke the open operation

        Account account = this.open(name);

        // create an Any for the result

        org.omg.CORBA.Any accountAny = _orb().create_any();

        // put the new Account object into an Any

        accountAny.insert_Object(account);

        // set the request's result

        request.result(accountAny);

    }

    public Account open(String name) {

    ...

}

 

Processing Input Parameters

 

Here are the steps that the AccountManager.invoke method uses to process the operation request's input parameters.

 

  1. Create an NVList to hold the parameter list for the operation
  2. Create Any objects for each expected parameter and add them to the NVList, setting their TypeCode and parameter type (ARG_IN or ARG_INOUT).
  3. Invoke the ServerRequest.param method, passing the NVList, to update the values for all the parameters in the list

 

Since the open method expects an account name parameter, an NVList object is created to hold the parameters contained in the ServerRequest. The NVList class implements a parameter list containing one or more NamedValue object.

 

An Any object is created to hold the account name. This Any is then added to NVList with the argument's name set to "name" and the parameter type set to ARG_IN.

 

Once the NVList has been initialized, the ServerRequest.params method is invoked to obtain the values of all of the parameters in the list.

Note: After invoking the params method, the NVList will be owned by the ORB. This means that if an object implementation modifies an ARG_INOUT parameter in the NVList, the change will automatically be apparent to the ORB.

 

An alternative to constructing the NVList for the input arguments is to use the org.omg.CORBA.ORB.create_operation_list method. This method accepts an OperationDef and returns an NVList object, completely initialized with all the necessary Any objects. The appropriate OperationDef object may be obtained from the Interface Repository.

Setting the Return Value

 

After invoking the ServerRequest.params method, the account name value can be extracted and used to create a new Account object. An Any object is created to hold the newly created Account object, which is returned to the caller by invoking the ServerRequest.result method.

 

The Server Implementation

 

The implementation of the Server class is shown below :

 

CODE:

public class Server {

 

    public static void main(String[] args) {

        // Initialize the ORB.

        org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args,null);

        // Initialize the BOA.

        org.omg.CORBA.BOA boa = orb.BOA_init();

        // Create the account manager object.

        AccountManager manager = new AccountManager("BankManager");

        // Export the newly create object.

        boa.obj_is_ready(manager);

        System.out.println(manager + " is ready.");

        // Wait for incoming requests

        boa.impl_is_ready();

    }

}

 


MICO’s Implementation Repository

 

The implementation repository is the place where information about an object implementation (also known as server) is stored. The CORBA 2 specification gives you only an idea what the implementation repository is for, but does not specify the interface to it. So the design of the implementation repository is MICO specific. Here is the IDL for MICO's implementation repository:

 

 1:  module CORBA {

 2:    /*

 3:     * Implementation Repository Entry

 4:     */

 5:    interface ImplementationDef {

 6:

 7:      enum ActivationMode {

 8:        ActivateShared, ActivateUnshared,

 9:        ActivatePerMethod,

10:        ActivatePersistent,

11:        ActivateLibrary

12:      };

13:

14:      typedef sequence<string> RepoIdList;

15:

16:      attribute ActivationMode mode;

17:      attribute RepoIdList repoids;

18:      readonly attribute string name;

19:      attribute string command;

20:    };

21:

22:    /*

23:     * Implementation Repository

24:     */

25:    interface ImplRepository {

26:      typedef sequence<ImplementationDef> ImplDefSeq;

27:

28:      ImplementationDef create (...);

29:      void destroy (in ImplementationDef impl_def);

30:      ImplDefSeq find_by_name (in string name);

31:      ImplDefSeq find_by_repoid (in string repoid);

32:      ImplDefSeq find_all ();

33:    };

34:  };

 

Interface ImplRepository defined in lines 25-33 is the implementation repository itself. It contains methods for creating, destroying and finding entries. An implementation repository entry is defined by interface ImplementationDef in lines 5-20. There is exactly one entry for each server which contains

 

·        name

·        activation mode

·        shell command or loadable module path

·        list of repository ids

 

for the sever. The name uniquely identifies the server. The activation mode tells the BOA whether the server should be activated once (shared server), once for each object instance (unshared server), once for each method invocation (per method server), or not at all (persistent server). The shell command is executed by the BOA whenever the server has to be (re)started. Activation mode library is used for loading servers into the same process as the client during runtime. Instead of a shell command you have to specify the path of the loadable server module for library activation mode. Finally there is a repository id for each IDL interface implemented by the server.

 

If you have written a server that should be activated by the BOA daemon when its service is requested you have to create an entry for that server. This can be accomplished by using the program imr. imr can be used to list all entries in the implementation repository, to show detailed information for one entry, to create a new entry, and to delete an entry.

 

The implementation repository is selected by the -ORBImplRepoAddr or -ORBImplRepoIOR options, which you usually put into your .micorc file.

Listing All Entries

Just issue the following command:

  imr list

and you will get a listing of the names of all entries in the implementation repository.

 

Details For One Entry

  imr info <name>

will show you detailed information for the entry named <name>.

Creating New Entries

  imr create <name> <mode> <command> <repoid1> <repoid2> ...

will create a new entry with name <name>. <mode> is one of

·        persistent

·        shared

·        unshared

·        permethod

·        library

<command> is the shell command that should be used to start the server. Note that all paths have to be absolute since micod's current directory is probably different from your current directory. Furthermore you have to make sure that the server is located on the same machine as micod, otherwise you have to use rsh; see below for examples. <repoid1>, <repoid2> and so on are the repository ids for the IDL interfaces implemented by the server.

Deleting Entries

  imr delete <name>

will delete the entry named <name>.

Forcing Activation of an Implementation

Registering an implementation in the implementation repository does not automatically activate the implementation. Usually a non-persistent implementation is only activated by the BOA daemon when its service is requested by a client. But sometimes you have to force activation of an implementation, for instance to make the implementation register itself with a naming service.

 

  imr activate <name> [<micod-address>]

 

will activate the implementation named <name>. To do this imr needs to know the address of BOA daemon. Usually this is the same address as for the implementation repository and you do not need to specify <micod-address>. Only if the BOA daemon is bound to an address different from the implementation repository address and different from the addresses specified using the -ORBBindAddr option you have to specify <micod-address> as a command line option to imr.

 

Examples

Assume we want to register an account server as a shared server. Furthermore assume that neither micod nor ird have been started yet, so we have to get them running first. Assuming the hostname is zirkon, you have to do the following:

 

  # create .micorc (only do that once)

  echo -ORBIfaceRepoAddr inet:zirkon:9000 > ~/.micorc

  echo -ORBImplRepoAddr inet:zirkon:9001 >> ~/.micorc

 

  # run ird

  ird -ORBIIOPAddr inet:zirkon:9000

 

  # run micod in a different shell

  micod -ORBIIOPAddr inet:zirkon:9001

 

Now we are prepared to create the implementation repository entry for account_server. Assume that this server implemented the interface Account whose repository id is IDL:Account:1.0. Assuming account_server has been copied to /usr/bin you can create the implementation repository entry using the following command:

 

  imr create Account shared /usr/bin/account_server IDL:Account:1.0

 

If account_server is located on host diamant (i.e., not on zirkon) you have to use the rsh command. This requires of course that you have entries in your .rhosts file that allow micod to execute programs on diamant. Here is the command to create the implementation repository entry:

 

  imr create Account shared "rsh diamant /usr/bin/account_server" \

    IDL:Account:1.0

 

Now you should change account_client.cc to bind to the address of micod. Note that you no longer need to know the address of the account server account_server, you only need to know the address of micod. Here is the part of account_client.cc that has to be changed:

 

  // account_client.cc

  ...

    CORBA::Object_var obj =

      orb->bind ("IDL:Account:1.0", "inet:zirkon:9001");

  ...

 

Running the recompiled client will automatically activate account_server.

 

Creating an entry for a loadable module (library activation mode) looks like this if /home/roemer/module.so is the path to the module:

 

  imr create Account library /home/roemer/module.so IDL:Account:1.0

 

Note that you have to make sure that a loadable module and a client that wants to make use of the module reside on the same machine.