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