In the examples we’ve
seen so far, we’ve always assumed that the Java
interfaces for the remote objects are available at compile time. But
what happens if they aren’t? You might get a
reference to a CORBA Object
from a Naming
Service, for example, and not know what interface that object
implements, or (more likely) not have that Java interface in the
client JVM. We mentioned earlier that you can use an
org.omg.CORBA.Object
reference directly to make
requests and exchange data with its remote object -- now
we’ll briefly look at how the Dynamic Invocation
Interface (DII) makes that possible.
The CORBA standard actually defines two complementary APIs for this purpose. The DII is used by a CORBA client to make remote method requests of a server object, while the Dynamic Skeleton Interface (DSI) can be used by a server-side skeleton to forward method invocations to its server implementation object in cases where it doesn’t have the actual servant interface available. Both of these APIs provide the same essential function: a dynamic interface to an object whose interface is not known at compile time.
The DII and DSI may seem like sidebar topics in the CORBA world, but in reality they are at the heart of CORBA and how it works. When we generate Java stubs and skeletons from IDL interfaces, the generated code uses the DII and DSI to execute remote method calls. The details of how this is done are shielded from you, the developer, by the Java interface you use to interact with the remote object. But it’s still worthwhile to understand how CORBA objects implement their distributed nature, especially in situations where the Java interface for the remote object is not there and you need to deal directly with these details.
On a more pragmatic note, there are cases where you might find
yourself needing the DII and/or the DSI. In all the examples
we’ve gone through in this chapter so far,
we’ve assumed that both the server and client
JVM’s had all of the relevant interfaces available
locally. For clients, we assumed that somehow they had acquired the
IDL-generated Java interface for our objects
(Account
, etc.). It is safe to assume you have
access to the client machines directly, or can provide an easy way
for users to download the required classes themselves as part of some
installation process. Since the clients will obviously need to
acquire your client code in the first place, it’s
usually possible to provide the other CORBA client classes as well,
and the DII isn’t needed. But if not (perhaps you
want to minimize class downloads for some reason, for example), then
it may be useful to use the DII to create a client that operates
without the need for the object interfaces themselves.
In this section, we take a look at how the DII works and how you might use it in a client. We won’t cover the DSI in this book, since its practical uses are even more limited for the average developer. Note, however, that the API of the DSI is analogous to that of the DII, so you shouldn’t have much trouble mapping the following explanation to the DSI as well.
The Dynamic Invocation Interface provides abstract representations of remote method requests and their arguments. In simple terms, this means it includes objects that represent remote method requests and parameters that are passed with these method requests. Methods on these objects allow you to set the parameters to the request, make the request, and get the results. DII’s central classes are:
-
Request
A request to invoke a method on a remote object. Created by the client and issued through the ORB to the server object.
-
NamedValue
A named parameter to a method request. Conceptually, this is a name tied to an
Any
value. The name of the value must match the name of the parameter as specified in the IDL interface the remote object satisfies.-
NVList
A list of
NamedValue
parameters used to represent an argument list passed into a remote method request.-
Any
A general argument value. An
Any
object can contain the Java equivalent of any basic IDL type or anObject
that can be described in IDL.-
Context
A list of
NamedValue
objects used to specify any details of the client environment that shouldn’t be passed as method arguments.
Once you get an
org.omg.CORBA.Object
reference to a remote
object (using any of the approaches we’ve already
covered), you can create and issue a method request to the object by
building a parameter list for the method call, making a
NamedValue
object to hold the result, making a
Context
object and putting any useful
environment values in it, and then using all of these items to create
a Request
object that corresponds to a
particular method on the object. In general, the various
create_XXX( )
methods on the
ORB
interface are used to construct all of these
elements of a DII request, except for the Request
itself. That is created by calling _create_request( )
on the Object
that is the target of
the remote method call.
Example 4-9 shows a version of our
Account
client that uses DII calls to invoke the
deposit( )
or getBalance( )
methods on a remote Account
object (the case for
the withdraw( )
method is very similar to the
deposit( )
case, so it’s
omitted here for the sake of brevity.) The client is structured very
much like the client in Example 4-8, except that the
actual calls to the remote Account
object are
constructed as DII calls. For the deposit requests, the method has a
single float argument and no return value, so the call to create the
Request
includes a null
NamedValue
for the result, and an
NVList
containing a single NamedValue
holding the amount to be deposited. In the case of
requests for the Account
balance, the
getBalance( )
method has no arguments and a
single float return value, so the creation of the
Request
includes a null
NVList
for the arguments, and a
NamedValue
containing an
Any
object intended to hold a floating-point
value.
Example 4-9. Account Client DII.java
import org.omg.CORBA.*; import org.omg.CosNaming.*; public class AccountClientDII { public static void main(String args[]) { ORB myORB = ORB.init(args, null); try { // The object name passed in on the command line String name = args[0]; org.omg.CORBA.Object acctRef = null; if (name.startsWith("corbaname") || name.startsWith("corbaloc") || name.startsWith("IOR")) { System.out.println("Attempting to lookup " + args[0]); acctRef = myORB.string_to_object(args[0]); } else { System.out.println("Invalid object URL provided: " + args[0]); System.exit(1); } // Make a dynamic call to the doThis method if (acctRef != null) { // We managed to get a reference to the named account, now check the // requested transaction from the command-line String action = args[1]; float amt = 0.0f; if (action.equals("deposit")) { amt = Float.parseFloat(args[2]); } System.out.println("Got account, performing transaction..."); try { // Did user ask to do a deposit? if (action.equals("deposit")) { // The following DII code is equivalent to this: // acct.deposit(amt); // First build the argument list. In this case, there's a single // float argument to the method. NVList argList = myORB.create_list(1); Any arg1 = myORB.create_any( ); // Set the Any to hold a float value, and set the value to the // amount to be deposited. arg1.insert_float(amt); NamedValue nvArg = argList.add_value("amt", arg1, org.omg.CORBA.ARG_IN.value); // Java IDL doesn't implement the get_default_context( ) operation // on the ORB, so we just set the Context to null Context ctx = null; // Create the request to call the deposit( ) method Request depositReq = acctRef._create_request(ctx, "deposit", argList, null); // Invoke the method... depositReq.invoke( ); System.out.println("Deposited " + amt + " to account."); } else { // The following DII code is equivalent to this: // acct.balance( ); // No argument list is needed here, since the getBalance( ) method // has no arguments. But we do need a result value to hold the // returned balance Any result = myORB.create_any( ); // Set the Any to hold a float value result.insert_float(0.0f); NamedValue resultVal = myORB.create_named_value("result", result, org.omg.CORBA.ARG_OUT.value); // Java IDL doesn't implement the get_default_context( ) operation // on the ORB, so we just set the Context to null Context ctx = null; // Create the request to call getBalance( ) Request balanceReq = acctRef._create_request(ctx, "getBalance", null, resultVal); // Invoke the method... balanceReq.invoke( ); System.out.println("Current account balance: " + result.extract_float( )); } } catch (Exception e) { System.out.println("Error occurred while performing transaction:"); e.printStackTrace( ); } } else { System.out.println("Null account returned."); System.exit(1); } } catch (Exception e) { e.printStackTrace( ); } } }
Again, note that in most situations you will actually have the Java
interface for the remote object available in your client along with
its helper class, so you’ll be able to narrow the
Object
reference to a specific type and call its
methods directly. One exception might be if you’re
building some kind of software development tool, and you want to
provide a dynamic execution utility for the CORBA code being
developed. The previous example demonstrates how a CORBA method call
can be carried out at this lower level, in case you ever find it
necessary to do so. And when you’re trying to fix a
problem with your CORBA application, it’s always
better to understand what’s going on under the hood,
so to speak.
Get Java Enterprise in a Nutshell, Second Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.