This topic is a collection of notes on how to fix the
addresses of various CORBA objects to a specific location
on the network.
There are situations for WASP that make it difficult to
run a CORBA Name Service or other lookup system.
Yet, we still need a way for objects to locate each other
on the network;
e.g., how does the
reg object locate
the
swir mwir and
lwir objects, in order to register
band-to-band data?
We will decide, in advance, exactly where each of these
well known (that is, not transient) objects will live.
Now, that's all well and good, but it runs counter to
the typical CORBA model, wherein ports and objects are
allocated dynamically and listed in a name service.
In other words, given that you know you want the shortwave
IR camera to be found under the name
swir in an ORB
running on port 64208 on host 10.1.0.1, how do you make
it happen?
The Typical Situation
Before we get to the notes, observations, and (hopefully)
the solution, here is a little background to get everyone
on the same page.
The following diagram is a typical WASP configuration.
WASP runs as multiple processes; each process contains
a single ORB. Within that ORB, however, are an arbitrary
number of objects.
Some projects get away with "single process, single ORB, single
object" approaches; not only does that not scale well,
it is impossible on WASP (some libraries under Windows require
a single process to access all hardware, thus the different
camera objects must be served from a single process).
What We're Doing
There are several steps involved to get the "whole" solution.
First, we need to "fix" our new objects to specific addresses
on the network, and then we need to locate them at their known
addresses.
Fixing Objects
A single ORB serves all the objects registered within it.
Thus, the hostname and port number are unique to a given
ORB on a given system, and the individual objects within
it are distinguished by name. Consider all of these names:
iiop://chapman.cis.rit.edu:64208/swir
iiop://chapman.cis.rit.edu:64208/mwir
iiop://chapman.cis.rit.edu:64208/lwir
iiop://10.1.0.1:32768/swir
iiop://10.1.0.1:32768/mwir
iiop://10.1.0.1:32768/lwir
iiop://10.1.0.1:32768/vnir
iiop://vulcan:49494/reg-geo
...
Fixing the ORB Endpoint
Imagine, then, a collection of cameras being served from
vulcan in flight. When in flight, naming services are
down, so we refer to
vulcan using its assigned IP address
on the private radio network,
10.1.0.1. This, combined
with the port number, provides an
endpoint for the object
request broker in that process. All the camera objects are
registered with this ORB, and thus we've named an endpoint
for the ORB common to all the objects within it:
iiop://10.1.0.1:32768
A simple way of specifying an endpoint to TAO processes,
as we use in WASP, is via the commandline. Simply
use the
-ORBEndPoint option to introduce the endpoint
supplied as the next argument.
Then, later, the call to
CORBA::ORB_init(ac,av) will
register that endpoint with the ORB. A few notes:
- Failure to specify any endpoint results in the ORB using the default action for each protocol. For TCP, this usually means choosing a port at random, bound to the primary network address for the machine. This is the default action we've come to rely on in the past. It isn't always the correct one, especially on machines with multiple network addresses!
- Specifying
ORBEndPoint more than once results in multiple endpoints for the ORB. The ORB will listen on all specified endpoints. This is very cool, and very handy on some of the bizarre networks WASP may find itself in soon.
Instead of the command line, the environment can also be
used to provide a specific endpoint to the ORB.
Set
TAO_ORBENDPOINT to the endpoint you want to use.
Note that while you can supply the commandline arguments more
than once to specify multiple endpoints, you can only
specify a single endpoint via the environment.
On the other hand, you can supply both the environment
variable and the commandline option, and both will be
used properly.
Finally, a third way to set endpoints in the ORB is to supply
them, one at a time, to
add_endpoint() which can be
found in the
orb_params member of the ORB itself.
However, you can't call this until after the ORB exists,
yet calling it after the ORB has initialized is "too late"
and useless. Best to work with one of the above methods.
For example, if you know that your process must always use
iiop://10.1.0.1:12345 as an ORB endpoint, the following
example of code is perfectly reasonable. The gymnastics
regarding
xac and
xav is necessary because
ORB_init()
modifies the parameters it is passed.
The nice aspect of this approach is that extra endpoints
can be specified on demand on the commandline without
interfering with the existing
oep endpoint compiled in.
const ACE_TCHAR *oep[] = {
"-ORBEndPoint",
"iiop://10.1.0.1:12345",
0
};
int
main( int ac, ACE_TCHAR *av[] )
{
try {
ACE_ARGV xargs( av, (ACE_TCHAR**)oep );
int xac = xargs.argc();
ACE_TCHAR **xav = xargs.argv();
CORBA::ORB_var orb = CORBA::ORB_init( xac, xav );
...
Fixing the Object Names
Once the ORB has its endpoints fixed, the only task
that remains is associating names with objects in the ORB.
There seems to be two ways of doing this: one is to write
some sort of barely documented object adaptor, and the other
is to manipulate an IOR table maintained inside the ORB.
We'll go with the latter.
The ORB's endpoints already define the protocol (IIOP),
address (IP), and port number.
All that's left is the name associated with an object
served by that ORB.
To register this, you obtain the IOR string associated
with the object, and the supply that to the
bind
operation of the ORB's IOR Table? The table itself is
obtained (as you might guess) from a call to the resolve
initial references functionality.
For example, here's a code fragment that binds an object
to the name
Gadget in the IOR table.
At this point in the code, one assumes that the ORB has
been initialized, the POA has been activated, and the
object itself has been associated with a servant.
CORBA::Object_var obj = orb->resolve_initial_references( "IORTable" );
if( CORBA::is_nil( obj ))
fatal_error( "cannot resolve IOR table in this ORB" );
IORTable::Table_var iort = IORTable::Table::_narrow( obj );
if( CORBA::is_nil( obj ))
fatal_error( "IORTable object is not really IORTable::Table" );
CORBA::String_var myior = orb->object_to_string( myobject );
iort->bind( "Gadget", myior );
Given that snippet of code, if the ORB was previous initialized
with an endpoint of
iiop://chapman.cis.rit.edu:13579, then
remote clients could supply
iiop://chapman.cis.rit.edu:13579/Gadget
to the CORBA string-to-object function and get a connection
to
myobject in that ORB. If there are multiple endpoints configured,
any of them can be used to connect to
myobject just by appending
/Gadget to the endpoint specifier.
Locating Objects
Easy as pie. Instead of supplying raw IOR strings (either from a
file or a commandline), you can supply
iiop://host:port/name to
the exact same function.
You'll get back an object that you'll need to test with
is_nil()
just like normal, and then you can narrow it accordingly.
The amount of code to change on the client side of this hack is
almost negligible;
all the real changes are on the server side.