Netscape Internet Service Broker for Java: Programmer's Guide, Version 1.0

[Contents] [Previous] [Next]

Chapter 10
The Dynamic Skeleton Interface

This chapter describes how object servers can dynamically create object implementations at run time to service client requests. The Dynamic Skeleton Interface (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. This chapter includes the following major sections:

Overview

The Dynamic Skeleton Interface (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. 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.

Example Program

This example is used to illustrate DSI concepts in this chapter. The example uses the Bank.idl file shown below.

 The Bank.idl file used in the DSI example.

// Bank.idl
module Bank {
    interface Account {
        float balance();
    };
    interface AccountManager {
        Account open(in string name);
    };
};

Using DSI

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

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

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

  2. 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. This class offers several constructors and the invoke method, which you, the programmer, must implement. For complete details on this class, see the Netscape Internet Service Broker for Java Reference Guide.

 The DynamicImplementation base class.

abstract public class DynamicImplementation extends
org.omg.CORBA.portable.Skeleton {

    abstract void invoke(ServerRequest request);

    protected DynamicImplementation(String object_name,
                                    String repository_id) {
        ...
    }
    protected DynamicImplementation(String object_name,
                                    String[] repository_ids) {
        ...
    }

    ...
}
The following code example shows 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.

 A portion of Server.java from the DSI example.

class Account extends org.omg.CORBA.DynamicImplementation {
    Account(String name) {
        super(name, "IDL:Bank/Account:1.0");
    }
    void invoke(org.omg.CORBA.ServerRequest request) {
    }
    ...
}

class AccountManager extends org.omg.CORBA.DynamicImplementation {
    Account(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 classes 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:

Replace all non-leading instances of the delimiter "::" with "/".

  1. Add "IDL:" to the beginning of the string.

  2. Add ":1.0" to the end of the string.
For example, given an IDL interface name of Bank::AccountManager, the resulting repository identifier string is 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. This class is described in the Netscape Internet Service Broker for Java Reference Guide.

 The ServerRequest class.

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 this 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.

 The Account.invoke method.

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 = _orb().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 following code example 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.

 The AccountManager.invoke method.

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.

Create an NVList to hold the parameter list for the operation.

  1. Create Any objects for each expected parameter and add them to the NVList, setting their TypeCode and parameter type (ARG_IN or ARG_INOUT).

  2. 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. The NVList and NamedValue classes are described in The Dynamic Invocation Interface and in the Netscape Internet Service Broker for Java Reference Guide.

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.

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 can be obtained from the Interface Repository, described in Chapter 7.

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, shown below, is the same whether or not you are using DSI.

 The Server class implementation.

// Using Enterprise Server's ORB
public class Server {
    public static void main(String[] args) {
        try {
            // Initialize the ORB.
            org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init();
            // Initialize the BOA.
            org.omg.CORBA.BOA boa = orb.BOA_init();
            // Create the account manager object.
            AccountManager manager = new AccountManager("ISB_Bank");
            // Export the newly create object.
            boa.obj_is_ready(manager);
            String host =
                java.netInetAddress.getLocalHost().getHostName();
            netscape.WAI.Naming.register
                ("http://" + host + "/ISB_Bank", manager);

            // Or, if using Communicator's ORB, the call would be:
            // netscape.WAI.Naming.register
            //   ("http://" + host + "/NameService/ISB_Bank", manager);

            System.out.println(manager + " is ready.");
            // Wait for incoming requests
            boa.impl_is_ready();
        }
        catch(org.omg.CORBA.SystemException e) {
            System.err.println(e);
        }
        catch(java.net.UnknownHostException u) {
            System.err.println(u);
        }
    }
}


[Contents] [Previous] [Next]

Last Updated: 02/04/98 13:48:01


Copyright © 1997 Netscape Communications Corporation

Any sample code included above is provided for your use on an "AS IS" basis, under the Netscape License Agreement - Terms of Use