newspaint

Documenting Problems That Were Difficult To Find The Answer To

Category Archives: CORBA

Iterating With OpalORB

If you’re using the Perl CORBA interface, OpalORB, and you want to iterate through a list then you have to use iterator objects and process the list some elements at a time.

Here is a method I’ve used for iterating in Perl:

# list all XML files in this folder
my $numPerGrab = 25;
my ($objectItems, $objectItemIterator); # scalars to hold iteration objects
$folder->list_with_patterns(
    +[ "*.xml" ], # pattern_seq
    $numPerGrab, # number per grab
    \$objectItems,
    \$objectItemIterator
);

my $next_n_result = 1;
while ( 1 ) {
    # process this chunk of returned items
    foreach ( @{$objectItems} ) {
        printf( "  - file name \"%s\"\n", $_->name() );
    }

    last if ( ! $next_n_result ); # no more
    last if ( $objectItemIterator->_get_orb ); # iterator not valid

    # get next set of items
    $next_n_result = $objectItemIterator->next_n( $numPerGrab, \$objectItems );
}

# should probably destroy the iterator when done with it
# - but don't know if checking _repo_id is the right way to do it
if ( $objectItemIterator->{_repo_id} ) {
    $objectItemIterator->destroy();
}

The equivalent iteration done in Java would look like:

ObjectItemsHolder objectItems = new ObjectItemsHolder();
ObjectItemIteratorHolder iterator = new ObjectItemIteratorHolder();

int numPerGrab = 25;

folder.list_with_patterns(
    new String[] { "*.xml" }, /* pattern_seq - the sequence of the patterns (ORed) */
    (int)numPerGrab, /* how_many - how many objects will be returned (0 for all) */
    objectItems, /*  items - the sequence of the subitems requested */
    iterator /* iterator - an iterator to retrieve the remaining subitems */
);

ObjectItemIterator recordSet = iterator.value;
bool next_n_result = true;
while ( true ) {
    for ( ObjectItem item: objectItems.value ) {
        System.out.println( "  - " + item.info.name );
    }

    if ( ! next_n_result )
        break;

    if ( recordSet == null )
        break;

    next_n_result = recordSet.next_n( (int)numPerGrab, objectItems );
} while ( 1 );

It is worth noting that when iterating it may be faster to get smaller groups of items than larger. Initially I was trying to pull 500 filenames at a time but I found my script actually performed much faster pulling 25 filenames at a time. A little counter-intuitive but worth testing depending on your circumstances.

There are probably better patterns to use for iteration and by all means leave me a comment if you can demonstrate a better one. But this should get you going if you have no idea where to start.

How To Get Object’s IOR With OpalORB

If you’re using the Perl CORBA interface, OpalORB, and you want to know how to get a text string representing the IOR of an object you can do the following (where session is the CORBA object I want the IOR of):

my $sior = $session->_get_orb->object_to_string( $session );

This is possible because almost all objects compiled from IDLs in Perl will inherit from the CORBA::Object class which has the _get_orb() method – and this returns a CORBA::ORB class instance which has the object_to_string() method.

A way of doing this in Java is equivalently:

String ior = session.toString();

A CORBA Consumer In Perl

Lately I’ve discovered the OpalORB open source implementation of the Object Management Group (OMG) CORBA standard.

Given a set of interface definition language (IDL) files they can be compiled and the corresponding Perl classes can be used to communicate with a CORBA server.

There isn’t too much example code for this on the Internet – perhaps because the documentation for OpalORB is rather light – and perhaps because CORBA isn’t widely used these days save for some specialist applications in private industry.

Installing

Download the OpalORB tarball and extract.

You probably will need to edit Makefile and remove the spaces after the equal signs and all trailing space and comments after the variable definitions for COPY, MKDIR, DESTINATION, and TARGET.

Then, as root, run make install.

Copy your custom IDL files into /usr/local/opalORB/idl/myidl.

Compile your IDLs:

cd /usr/local/opalORB/idl/myidl
for i in *.idl; do echo ===$i===; ../idl.pl --client $i; done

Change all references to Object to MyCompany::Object:

find . -type f -name '*.pm' \
  -exec perl -i.bak \
  -pe 's{(?<=[^:A-Za-z0-9.])(Object)(?=[^:A-Za-z0-9.])}{MyCompany::$1}g' {} \;

Running Your Perl Scripts

You’ll probably want to run your Perl scripts in such as way so as they’ll find your OpalORB and custom classes. To do this prepend your Perl executable with the PERL5LIB environment variable, e.g.:

PERL5LIB=/usr/local/opalORB:/usr/local/opalORB/Naming:/usr/lodcal/opalORB/idl/myidl \
  perl script

Examples

First of all let’s solve some simple problems in Perl.

Connecting to a CORBA Server and Getting a Repository/Channel/etc

You may have a IOR reference which looks something like IOR:010000002600000049444c… (for 3 or 4 lines of hexadecimal digits). If that’s the case here is how you can get your repository or channel or whatever…

use CORBA;

# compiled from my own IDL files
use MyCompany::Repository;

sub get_repository_from_ior {
    my ( $ior ) = @_;

    my $orb = CORBA::ORB_init( +[ "MyORBInstance" ] );

    my $repositoryObject = $orb->string_to_object( $ior );
    my $repository = MyCompany::Repository::_narrow( $repositoryObject );
    return( $orb, $repository );
}

Or alternatively if you have a corbaloc string you could use the following function:

use CORBA;
use CosNaming::NamingContextExt;

# compiled from my own IDL files
use MyCompany::Repository;

sub get_repository_from_iiop_and_path {
    my ( $corbaloc, $path ) = @_;

    my $orb = CORBA::ORB_init( +[ "-ORBInitRef", "MyServiceID=$corbaloc" ] );

    my $namingContextObject = $orb->resolve_initial_references('MyServiceID');
    my $namingContext = CosNaming::NamingContextExt::_narrow( $namingContextObject );

    my $name = $namingContext->to_name( $path );
    my $repositoryObject = $namingContext->resolve( $name );
    my $repository = MyCompany::Repository::_narrow( $repositoryObject );
    return( $orb, $repository );
}

Exception Handling

CORBA servers frequently express inability to perform an action through exceptions. I recommend explicitly including the Error CPAN module with debugging turned on so that you can produce a stacktrace pinpointing the line in your code that resulted in the triggered exception.

use Error qw(:try);
$Error::Debug = 1;

use strict;

my $orb = undef;

try {
    ( $orb, $repository ) = get_repository_from_iiop_and_path(
        "corbaloc:iiop:10.15.16.17:3900/NameService",
        "MyCompany/Repositories/MyDB"
    );

    my $version = $repository->get_version();
    my $version_string = $version->version_string;
    print( "My database version: $version_string\n" );
}
catch Error::Simple with {
    my $e = shift;
    print( STDERR "File " . $e->file . ", line " . $e->line . ": $e" );
    warn( $e->stacktrace );
}; # semi-colon MUST be here

# clean up before quitting
$orb->shutdown() if ( $orb );

POA Server

You might have need to receive events from a CORBA server. To this end you will have to run your own infinite-running thread that registers a class to receive messages.

It is VERY important that any dependent classes used in decoding received messages must be included in the script with the “use” pragma otherwise POA may generate exceptions about being unable to find the new method in the sequence or struct objects. To this end I recommend editing the CORBA/TypeCode.pm module and adding the following bold line in the _id_to_name() function during debugging so you can become aware of which classes you need to include in your main script:

return $$id_name{$id} if (defined $$id_name{$id});
warn( "Unknown id \"$id\"" );
###############################################################################

package ConsumerImpl;

# parent classes
use MyCompany::Consumer;
use PortableServer::ServantBase;

push( @ISA, 'MyCompany::Consumer', 'PortableServer::ServantBase' );

sub new {
    my $class = shift;
    my $self = $class->SUPER::new();
    return( $self );
}

sub msg_push {
    my $self = shift;

    my ( $event ) = @_;

    printf( "%s: %s\n", $event->name, join( ", ", @{$event->items} ) );
}

###############################################################################

package main;

# setting up naming context and channel
use CosNaming::NamingContextExt;
use MyCompany::Channel;

# POA and dependent classes (must be loaded to be recognised)
use PortableServer::POA;
use MyCompany::ext_event;
use MyCompany::ObjectInfo;

# pragmas
use Error qw(:try);
$Error::Debug = 1;

use strict;

sub get_channel_from_iiop_and_path {
    my ( $corbaloc, $path ) = @_;

    my $orb = CORBA::ORB_init( +[ "-ORBInitRef", "MyServiceID=$corbaloc" ] );

    my $namingContextObject = $orb->resolve_initial_references('MyServiceID');
    my $namingContext = CosNaming::NamingContextExt::_narrow( $namingContextObject );

    my $name = $namingContext->to_name( $path );
    my $object = $namingContext->resolve( $name );

    my $channel = MyCompany::Channel::_narrow( $channelObject );
    return( $orb, $channel );
}

my $orb = undef;

try {
    my $channel;

    ($orb, $channel) = get_channel_from_iiop_and_path(
        "corbaloc:iiop:10.15.16.17:3900/NameService",
        "MyCompany/Events/EventsService"
    );

    my $consumerImpl = ConsumerImpl->new();
    my $rootPOA = $orb->resolve_initial_references( "RootPOA" );
    my $poaManager = $rootPOA->the_POAManager();
    my $consumerId = $rootPOA->activate_object( $consumerImpl );
    $poaManager->activate();

    my $consumerRef = $rootPOA->id_to_reference( $consumerId );
    my $consumer = MyCompany::Consumer::_narrow( $consumerRef );

    $orb->run(); # never returns, but consumer will now receive messages
}
catch Error::Simple with {
    my $e = shift;
    print( STDERR "File " . $e->file . ", line " . $e->line . ": $e" );
    warn( $e->stacktrace );
};

# clean up before quitting
$orb->shutdown() if ( $orb );

TAO IDL Compiler Error About Reserved Words

I was getting the following error when compiling an IDL (interface definition language) file using tao_idl version 2.2.0:

Illegal syntax or missing declarator in parameter declaration

I couldn’t work out what the problem was until I read this blog post in which somebody had discovered the compiler complaining about the word “port” clashing with a reserved word.

In my case I had an IDL file try and declare a structure member named “alias“.

The solution was to append all instances of the variable name “alias” with an underscore, e.g. “_alias“.

Fixing Ubuntu Hosts File So CORBA Listener Knows The Right Address

The Problem

So the situation is this. I have a Java script that creates a “listener” (i.e. a server) in CORBA that will be communicated over another CORBA connection as the location to be connected to. In Wireshark I see this as as packet with the following fields:

Profile ID: TAG_INTERNET_IOP (0)
IIOP::Profile_host: 127.0.1.1
IIOP::Profile_port: 49170

Now this is no good. Any remote service that tries to connect to 127.0.1.1 will end up trying to communicate with itself (127.* is the loopback address).

The Solution

We need a way of letting Java CORBA figure out for itself what the IP address of the host we’re running our program on is.

The problem, in Ubuntu 12.04.1 at least, is that the /etc/hosts file contain the following entries by default (the problem line is highlighted in bold):

127.0.0.1       localhost
127.0.1.1       myserver.mydomain  myserver

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

What we need to do is comment out this 127.0.1.1 entry and replace it with the current IP address and uname of the host.

If, like me, you are connected by way of interface eth0 then you can perform the necessary change using the following one-liner:

MYNAME=`uname -n` \
MYIP=`ifconfig eth0 |perl -ne 'print $1 if m/:(\d+\.\d+\.\d+\.\d+)/'` \
perl -i.bak -pe "s/^(127.0.1.1.*$)/#\$1\\n\$ENV{MYIP} \$ENV{MYNAME}/" /etc/hosts

Result

This will result in a /etc/hosts file that will look something like the following:

127.0.0.1       localhost
#127.0.1.1      myserver.mydomain  myserver
10.191.22.14 myserver

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters