We still need to do some work to make
the Java implementation of our IDL interface available to remote
clients. We must instantiate one or more instances of our CORBA
server implementation and connect them to an ORB on the server so
that they can receive remote method requests. Then clients need to
somehow obtain remote references to our server objects. There are two
fundamental ways that a client obtains a remote reference: it can get
an initial object reference (usually to a CORBA service of some kind,
like a Naming Service) using the
ORB.resolve_initial_references( )
method and
somehow find a reference to the remote object through method calls on
the initial object, or it can get a
“stringified” reference to the
remote object (either an Interoperable Object Reference or an
Interoperable Naming Service URL) and use the local ORB to convert it
to a live object reference.
For the first case, a remote object needs to be registered in some way with a server-side ORB, and probably with a CORBA service of some kind. Connecting your CORBA object to an ORB enables it to accept remote method requests from clients. Registering your object with a CORBA service makes it easier for remote clients to find your object in the first place.
In order for you to register a remote object, you first have to get a reference to an ORB. We’ll look at how to do this, then look at registering the remote object with a Naming Service (the most common way to “publish” remote objects in a CORBA context). Finally, we’ll look at two ways that clients can use stringified references to access your CORBA object.
Since the ORB is so central to
everything in a CORBA environment, the first thing any CORBA process
needs to do (whether it’s a CORBA server or client
process) is get a reference to an ORB that it can use to find other
objects, access CORBA services, and handle remote method calls. A
CORBA participant initializes its ORB reference by calling one of the
static init( )
methods on the
ORB
interface. Each of these methods returns an
ORB
object that can find CORBA objects and
services, among other things. The standard init( )
methods provided on an ORB are as follows
(Sun’s Java IDL supports all of these standard
initialization methods):
-
public static ORB ORB.init( )
Returns a shared (static) ORB instance, called the “singleton ORB.” Each call within the same runtime environment of this version of
init( )
returns the same ORB reference. If used within an applet context, the ORB has limited abilities. The singleton ORB is used mainly by IDL-generated classes to perform actions like createTypeCode
objects that identify the types of CORBA objects.-
public static ORB ORB.init(String[] args, Properties props)
Creates a new ORB using the given arguments and properties, as discussed in the following paragraphs.
-
public static ORB ORB.init(Applet applet, Properties props)
Creates a new ORB within an applet context. The applet’s codebase and host are used by the ORB as the source of various services, such as the Naming Service.
There are two standard properties
defined for an ORB that can be set in the call to init( )
, using either the String
arguments
array or a Properties
object. These are:
-
org.omg.CORBA.ORBSingletonClass
Specifies the class to use for the singleton ORB instance returned by the no-argument version of
init( )
.-
org.omg.CORBA.ORBClass
Creates ORB instances when the other versions of
init( )
are called.
In both cases, the class name you provide has to implement the
org.omg.CORBA.ORB
interface. You can use these
two properties to specify a custom ORB implementation, if needed. You
may want to override the default ORB implementation
(com.sun.CORBA.iiop.ORB
in Java IDL) with one of
your own that has particular performance characteristics, for
example. Or you may be running your CORBA code within an applet and
want to ensure that a valid ORB is available no matter what browser
version your applet encounters.
Sun’s Java IDL also
adds two nonstandard properties: ORBInitialHost
and ORBInitialPort
. By default, each
ORB.init( )
method initializes an ORB that looks
for its services (naming service, for example) locally. Java IDL adds
these two nonstandard properties to allow your local ORB to defer its
services (naming, trading, etc.) to a remote ORB running on a given
host and listening on a given port. Be careful before you decide to
depend on these properties in your application or applet. They are
only honored within Sun’s Java IDL implementation of
the CORBA standard. If you want your CORBA application to be portable
to any implementation of the standard IDL-to-Java binding, and you
want to use a remote Naming Service, you should stick to using a
stringified reference to the remote object, obtained through a
secondary communication channel (as we’ll discuss
shortly).
Any
of these properties can be specified within a
Properties
object, as a command-line option to a
Java application, or in a system properties file. As an example, if
you want to specify a different host to use for finding services like
the Naming Service, one way to do this is to specify the host
explicitly in the code that initializes the ORB, using a
Properties
object:
Properties orbProps = new Properties( ); orbProps.put("org.omg.CORBA.ORBInitialHost", "remote.orb.com"); ORB myOrb = ORB.init((String[])null, orbProps);
Alternately, you can take command-line arguments passed into your
Java code and pass them to the ORB.init( )
method to be parsed. Say we have a class named
InitRemote
with a main method implemented as
follows:
public class InitRemote { public static void main(String[] argv) { try { ORB myOrb = ORB.init(argv, null); ... } } }
In this case, we can specify any ORB properties on the command line
using specific argument names. Notice that when specifying these, you
should omit the org.omg.CORBA
package prefix on
the property names:
orbhost% java InitRemote -ORBInitialHost remote.orb.com
Note that you can use the second ORB.init( )
method with both a String
arguments array and a
Properties
list specified, even though the
examples here haven’t shown that.
One way to make a server object
available to remote clients is to register it with a local CORBA
Naming Service under a specific name. A remote client can then get a
reference to the root NamingContext
for the
Naming Service, and look up the server object by name.
Regardless of whether you’re using a POA-compliant CORBA environment, here are the basic steps that create and register a CORBA object with the ORB:
Initialize an ORB reference.
Create an instance of your CORBA server implementation, and get a reference from it that can be registered with the naming service.
Get a reference to the root
NamingContext
for the Naming Service from the ORB.Construct a name for the object that reflects where you want it to sit in the Naming Service hierarchy.
Bind your object to that name in the Naming Service, using the root
NamingContext
.
The mechanics of carrying out each step are slightly different in the
POA-compliant and pre-POA versions of Java IDL.
We’ll start by looking at the pre-POA version, shown
in Example 4-5. This code listing shows a utility
class named AccountInitPrePOA
, whose
main( )
method creates an instance of our
AccountImplPrePOA
implementation and then
registers the object with the Naming Service. The program starts by
getting a reference to the local ORB, as discussed in the previous
section. Then it asks the ORB for a reference to its registered
Naming Service by calling the resolve_initial_references( )
method on the ORB, using the standard name
“NameService.” This object
reference is the root NamingContext
of the
Naming Service, so we narrow the object reference using
NamingContextHelper
. Next, we build the name to
use to register the object with the Naming Service. The name of an
object in the basic CORBA Naming Service is represented by an array
of NameComponent
objects, one for each branch in
the hierarchy. Here, we’re assuming that the user
has passed in a simple string as a command-line argument, and we use
it to construct a NameComponent
array with a
single element (representing a simple one-level path for the object
in the naming hierarchy). If we wanted to register our object using a
multilevel path, we first must create the subcontexts, then register
the object (we’ll see how that works in a later
section). Once the NameComponent
array is
constructed, we call the rebind( )
method on the
NamingContext
to register our
Account
object with that name. With the object
registered, we send this thread into a perpetual wait state (by
creating a dummy String
variable and doing a
synchronized wait( )
on it). This keeps the
thread alive so the Account
object can service
client requests.
Example 4-5. Creating/Registering an Account Server Object (pre-POA)
// // Initialize a remote Account object and register it with the Naming Service // import org.omg.CORBA.*; import org.omg.CosNaming.*; public class AccountInitPrePOA { public static void main(String[] args) { try { // Initialize an ORB reference ORB myORB = ORB.init(args, null); // Create an instance of an Account server implementation Account impl = new AccountImplPrePOA(args[0]); // Register the local object with the ORB myORB.connect(impl); // Get the root name context org.omg.CORBA.Object objRef = myORB.resolve_initial_references("NameService"); NamingContext nc = NamingContextHelper.narrow(objRef); // Register the local object with the Name Service NameComponent ncomp = new NameComponent(args[0], ""); NameComponent[] name = {ncomp}; nc.rebind(name, impl); System.out.println("Registered account under name " + args[0]); // Go into a wait state, waiting for clients to connect java.lang.Object dummy = new String("I wait..."); synchronized (dummy) { dummy.wait( ); } } catch (Exception e) { System.out.println( "An error occurred while initializing server object:"); e.printStackTrace( ); } } }
Before running this utility object to create and register our CORBA object, we need to start a Naming Service someplace. A Naming Service daemon listens for Naming Service requests on a specific port and provides access to the named object directory it manages. In Java IDL, the Naming Service is started using the tnameserv command:
objhost% tnameserv &
With that done, we can run our initialization method to register our server object with the ORB:
objhost% java oreilly.jent.corba.AccountInitPrePOA JimF Registered account under name JimF
Performing these same tasks under the POA-compliant version of Java
IDL (JDK 1.4 and later) is fairly similar, except that using the
Portable Object Adaptor adds some new twists in the creation of the
server object and its reference. Example 4-6 shows
the code listing for AccountInitPOA
, which
performs the same task as AccountInitPrePOA
, but
within the POA-compliant version of Java IDL. One key difference in
this version is the additional POA operations immediately following
the creation of the Account
server object. Since
we’re dealing with an ORB using the POA, and the POA
is acting as the mediator between our server object and the ORB, we
need to use the POA to obtain a reference to our server object that
can be used for registration with the Naming Service (among other
things). So we get a reference to the POA from the ORB using the
resolve_initial_references( )
method (this time
using the standard name for the POA,
“RootPOA”). We then make sure that
the POA is active by getting its POA manager and calling its
activate( )
method -- every POA has a
POAManager
that maintains the runtime state of
the POA. Finally, we ask the POA for a usable reference to our server
object by calling its servant_to_reference( )
method and converting the resulting Object
to an
Account
reference. For more details on the
various POA interfaces and their methods, refer to Chapter 14.
Example 4-6. AccountInitPOA.java
// // CORBA example 9: Initialize a remote object, register it with a naming // service, and generate a stringified IOR. // import org.omg.CORBA.*; import org.omg.CosNaming.*; import org.omg.PortableServer.POA; public class AccountInitPOA { public static void main(String[] args) { try { // Initialize an ORB reference ORB myORB = ORB.init(args, null); // Create an instance of an Account server implementation AccountImplPOA acct = new AccountImplPOA(args[0]); // Get the root Portable Object Adapter (POA) POA rootPOA = (POA)myORB.resolve_initial_references("RootPOA"); // Activate the POA manager rootPOA.the_POAManager().activate( ); // Get an object reference from the implementation org.omg.CORBA.Object obj = rootPOA.servant_to_reference(acct); Account acctRef = (Account)AccountHelper.narrow(obj); // Get the root name context (use the INS interface, so that we can use // the simpler name construction process) org.omg.CORBA.Object objRef = myORB.resolve_initial_references("NameService"); NamingContextExt nc = NamingContextExtHelper.narrow(objRef); // Register the local object with the Name Service // Use the Interoperable Naming Service interface to simplify matters, // and to support URL-formatted names (e.g. "JohnS", // "corbaname://orbhost.com#/JohnS", etc.) NameComponent[] name = nc.to_name(args[0]); nc.rebind(name, acctRef); System.out.println("Registered account under name " + args[0]); // Go into a wait state, waiting for clients to connect myORB.run( ); } catch (Exception e) { System.out.println( "An error occurred while initializing server object:"); e.printStackTrace( ); } } }
Another difference in this version of our object initialization
utility is the actual registration of the server object with the
Naming Service. Here, since JDK 1.4 also introduced Java support for
the Interoperable Naming Service (INS) interface, we can use the
NamingContextExt
interface to make binding
objects a bit easier. We’ll get into the details of
the different Naming Service interfaces in the next section.
The last difference in this version of our initialization utility is
in how we cause the thread to wait for client requests. The CORBA 2.3
(and higher) versions of the ORB interface include a run( )
method. Calling this method causes the current thread to
wait until the ORB shuts down. This is a clean way to accomplish the
same task as our dummy.wait( )
approach in the
previous version of this utility class.
Now that we’ve seen the basic steps involved in creating and registering an object with a CORBA Naming Service, let’s look in more detail at binding objects and new contexts within the Naming Service.
As was discussed earlier, a
CORBA naming service maintains a hierarchy of named objects that can
be looked up by remote clients. Initially, a CORBA naming directory
is empty, with only its root NamingContext
and
no objects. The bind( )
method on a
NamingContext
object binds a server object to a
name within the context. The bind_new_context( )
method creates new subcontexts within a given
NamingContext
. Using a file directory analogy,
calling bind_new_context( )
on a
NamingContext
object is like making a new
subdirectory from the current directory, while calling
bind( )
puts a new file into the current
directory.
The Java IDL mapping uses arrays of
NameComponent
objects to represent the names of
subcontexts within a naming directory. Each
NameComponent
represents a component of the path
to the named object. A NameComponent
contains
id
and kind
string fields
that serve to label the component in the path. Only the
id
field is significant in determining name
uniqueness. So a NameComponent
with an
id
set to
“student” and
kind
set to an empty string conflicts with a
NameComponent
with an id
of
“student” and a kind of
“doctoral,” if both
NameComponent
objects are relative to the same
subcontext. The NameComponent
class has a
constructor that takes the id
and
kind
values as arguments.
Here’s how to create a single
NameComponent
:
NameComponent comp1 = new NameComponent("student", "doctoral");
A complete name path can be composed as an array of these objects:
NameComponent path[] = { comp1, comp2, ... };
The bind( )
method takes two arguments: an array of
NameComponent
objects as the relative name for
the object you’re putting into the Naming Service
and the server object itself. If you’re binding a
server object using the root context of the Naming Service, the name
is also the absolute name of the object in the overall naming
directory. If an object is already bound to the name, you can use the
rebind( )
method with the same arguments,
causing the existing object bound to that name to be replaced by the
new object. Note that since the Naming Service is a CORBA service
that can be accessed remotely by other CORBA clients, the objects it
contains need to be exportable to these remote clients. This means
that only org.omg.CORBA.Object
references can be
bound to names within a NamingContext
.
The following code snippet binds a few of our
Account
objects to names within the root context
of a Naming Service:
// Get the root naming context ORB myORB = ORB.init(...); org.omg.CORBA.Object ref = myORB.resolve_initial_references("NameService"); NamingContext rootNC = NamingContextHelper.narrow(ref); // Create a few Accounts Account acct1 = new AccountImplPOA("JohnSmith"); Account acct2 = new AccountImplPOA("MaryJones"); // Bind them to names in the Naming Service NameComponent name1 = new NameComponent("Smith,J", ""); NameComponent path1[] = { name1 }; NameComponent name2 = new NameComponent("Jones,M", ""); NameComponent path2[] = { name2 }; rootNC.bind(path1, ref1); rootNC.bind(path2, ref2);
Before you
can bind an object to a name with multiple components, all the
subcontexts (subdirectories) have to be created using the
bind_new_context( )
method on a
NamingContext
. The bind_new_context( )
method takes an array of
NameComponent
objects as the relative path of
the new context and a reference to the
NamingContext
object to bind to that location in
the overall directory. A new NamingContext
object can be created from an existing one by calling its
new_context( )
method. If a context already
exists at the target name, you can use the rebind_context( )
method to replace the existing context with a new one.
This is useful for emptying out an entire subcontext without removing
each object individually.
Here is an example that binds some Account
objects within various subcontexts representing different branches of
a bank, using the standard Naming Service interfaces;
NameComponent
and
NamingContext
(both from the
org.omg.CosNaming
package). This example shows
how to do this with a POA-compliant ORB -- the code is very
similar for the pre-POA versions of Java IDL:
// Initialize the ORB and get the POA and root naming context, as before ORB myORB = ORB.init(...); POA rootPOA = ...; NamingContext rootNC = ...; // Create the components to the sub-context paths NameComponent branchComp = new NameComponent("bankBranches", ""); NameComponent cambridgeComp = new NameComponent("Cambridge", ""); NameComponent bostonComp= new NameComponent("Boston", ""); // Create a new context, bind it to the path "bankBranches" NamingContext ctx = rootNC.new_context( ); NameComponent path[] = { branchComp }; rootNC.bind_context(path, ctx) // Create another context, bind it to the path "bankBranches, Cambridge" NamingContext cambridgeCtx = rootNC.new_context( ); path = { branchComp, cambridgeComp }; rootNC.bind_context(path, cambridgeCtx) // Create another context, bind it to the path "bankBranches, Boston" NamingContext bostonCtx = rootNC.new_context( ); path = { branchComp, bostonComp }; rootNC.bind_context(path, bostonCtx); // Now we can bind Accounts to a name within any of the new sub-contexts // Create a few Account server objects, and get usable client references // from the POA Account johnAcct = new AccountImplPOA("JohnSmith"); Account johnRef = AccountHelper.narrow(rootPOA.servant_to_reference(johnAcct)); Account maryAcct = new AccountImplPOA("MarkJones"); Account maryRef = AccountHelper.narrow(rootPOA.servant_to_reference(maryAcct)); // Bind each Account to a name in the appropriate branch path. Assume // that John has his account out of the Cambridge branch, Mary has hers // out of the Boston branch. NameComponent johnComp = new NameComponent("Smith,J", ""); NameComponent johnPath[] = { branchComp, cambridgeComp, johnComp }; rootNC.bind(johnPath, johnRef); NameComponent maryComp = new NameComponent("Jones,M", ""); NameComponent maryPath[] = { branchComp, bostonComp, maryComp }; rootNC.bind(maryPath, maryRef);
If you try to bind an object or a
subcontext to a name within a context that hasn’t
been created yet, an
org.omg.CosNaming.NamingContextPackage.NotFound
exception is thrown.
Note that names used in the bind( )
or
rebind( )
methods are relative to the
NamingContext
object that
they’re called on. This means we can bind
Mary’s Account
object in the
previous example to the same absolute name within the directory by
replacing the last two lines of the example with the following:
NameComponent maryRelativePath[] = { maryComp }; bostonCtx.bind(maryRelativePath, maryRef);
The bostonCtx
context is bound to the
{
"bankBranches"
,
"Boston"
}
subdirectory, so binding an object to the name
{
"Jones,M"
}
within this context is equivalent to binding it
to the full path {
"bankBranches"
,
"Boston"
,
"Jones,M"
}
from the root context. You can use similar
shorthand when binding new contexts within a directory. In other
words, you can bind a context to a relative name within a subcontext,
instead of an absolute name within the root context.
The Interoperable Naming Service (INS) interfaces make both the
binding and lookup of objects in a Naming Service a bit easier. In
our previous examples, we bound objects to specific locations in the
naming hierarchy by first creating the subcontexts (subdirectories)
we needed, then binding the remote objects to names within those
contexts. In each step, we needed to construct fully qualified names
for both the contexts and the objects we were binding. Prior to the
introduction of the INS, the only way to do this is the way shown
earlier examples: manually construct an array of
NameComponents
representing the intended path to
the object. Using the INS interfaces, and their support for new types
of stringified object references and URLs, it’s
possible to create new contexts and bind objects in a slightly
simpler way. We can convert stringified object references directly to
NameComponent
arrays using the
to_name( )
method on the
org.omg.CosNaming.NamingContextExt
interface.
For example, assuming that we’ve created the
necessary subcontexts already, we could bind John
Smith’s account to the same path as before by
specifying the full path as a string and converting it using
to_name( )
:
NamingContextExt rootNC = ...; String johnURL = "bankBranches/Cambridge/Smith,J"; NameComponent johnPath[] = rootNC.to_name(johnURL); rootNC.rebind(johnPath, johnRef);
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.