R2CORBA Programming : Your very first R2CORBA program

This article shows you how easy it is to write R2CORBA client and server programs. I'll explore a classic CORBA programming example to provide a clear introduction to R2CORBA programming.

Introduction

R2CORBA is the reference implementation of the Ruby CORBA Language Mapping (RCLM) created by Remedy IT. Ruby is a popular scripting language created by Yukihiro Matsumoto (a.k.a. matz) featuring a very dynamic form of Object Orientation. See here for more information.

Getting started

Assuming you have a working R2CORBA installation (see f.i. Ruby Programming : Installing R2CORBA for MRI or the R2CORBA OSPortal project) on your system its time to get to the fun part; writing your very first R2CORBA program!

The only additional tool you need for this exercise is a text editor.

Let's start by creating a folder to hold our program's source code. The example we're creating is the all famous CORBA 'Hello World'-type starter application called Echo, so we'll name the folder accordingly.

$ mkdir Echo
$ cd Echo

Next is the first step of any CORBA application: define your IDL.

Define your IDL

As with any CORBA application we first have to define our IDL interface(s). We are going to implement the Echo example using Ruby, so we define an interface with an echo_string() method to retrieve an 'echoed' copy of the input string and a shutdown() method to shutdown the server.Save this definition to the file Test.idl.

/// Put the interfaces in a module, 
/// to avoid global namespace pollution
module Test
{
  /// A very simple interface
  interface Echo
  {
    /// Return an echo of the input string
    string echo_string (in string input);
    /// A method to shutdown the ORB
    /**
     * This method is used to simplify the test shutdown process
     */
    oneway void shutdown ();
  };
};

As IDL is implementation independent there is no difference here from specifying IDL for any other CORBA language mapping.
The differences are in the language mapping and stem from other features of Ruby c.q. R2CORBA, such as the highly dynamic nature of the Ruby language, as we will see in the following paragraphs.

Implement the server

Now we can implement the server based on the IDL definition or rather based on the Ruby mapping for that definition. To get that Ruby mapping we will need an IDL compiler.

Part of the R2CORBA installation is a 100% native Ruby IDL compiler which compiles implementation independent OMG IDL definitions into R2CORBA specific Ruby code. As this compiler is written in pure Ruby you cannot only use this as a commandline tool (as with traditional IDL compilers like for the C and C++ language mappings) but it also integrates seamlessly into your application's Ruby code. With Ruby this is an advantage that really pays off!

Due to the highly dynamic nature of the Ruby language it is very easy to define new classes and objects from dynamically constructed Ruby code at runtime. In other words; you can use Ruby to create string variables containing Ruby code defining new class types which you than execute (evaluate) to include them in your runtime image.

The R2CORBA IDL compiler leverages this feature to enable you to simply load IDL definitions into your application code to get their Ruby mapping equivalents activated without requiring any IDL pre-compilation. Just load the file containing the IDL definition using the CORBA.implement () method as follows:

require 'corba/poa'
CORBA.implement('Test.idl', {}, CORBA::IDL::SERVANT_INTF)

The require 'corba' loads the base R2CORBA modules defining the CORBA namespace and related classes (CORBA::ORB, CORBA::Object a.o.) and methods including the CORBA.implement class method. This method provides an interface to the R2CORBA IDL compiler component allowing applications to load Ruby mappings for OMG IDL files at runtime.

The implement method is defined as follows:

## CORBA.implement(idlfile, options = {}, genbits)
#    in string idlfile 
#        name of file containing IDL definitions
#    in hash options
#        options hash for the IDL compiler
#        - :includepaths => [] 
#          array of include paths for IDL compiler to search
#    in integer genbits
#        bitmask specifying what Ruby mappings to generate
#        bitmask can be combination (or-ed) of one or more of
#        - CORBA::IDL::CLIENT_STUB
#          specifies to generate and load client stub mappings (default)
#        - CORBA::IDL::SERVANT_INTF
#          specifies to generate and load servant mappings
#          (implies generating client stubs)

The method also behaves like the standard Ruby require in that it records loaded IDL files and will silently ignore reloading previously loaded IDL files.

As the method description tells us the CORBA.implement call above is intended to generate and load servant mappings. This defines a Ruby servant mappings for the IDL definitions in the POA namespace (according to RCLM standards). As with any CORBA mapping you need to derive a concrete servant implementation class from this base class.
In Ruby with R2CORBA you can do that as follows:

class MyEcho < POA::Test::Echo
  def initialize(orb)
    @orb = orb
  end
  def echo_string(input)
    "ECHO: #{input}"
  end
  def shutdown()
    @orb.shutdown
  end
end # of servant MyEcho

This servant implementation records the ORB instance 'running' the servant on instantiation and implements the echo_string and shutdown methods.

The echo_string implementation accepts the input string and creates a new string which prefixes the input with 'ECHO: ' and returns the new string as result.

The shutdown implementation simply calls the ORB's standard shutdown method to signal the ORB instance to stop processing request and end it's request handling loop.

Right! Now that we have the CORBA classes loaded, IDL mapped and a concrete servant class defined it's time to start up the ORB and the servant. In R2CORBA this is done (f.i.) as follows:

  • initialize an ORB instance
orb = CORBA.ORB_init('myORB')
  • resolve a RootPOA reference
obj = orb.resolve_initial_references('RootPOA')
root_poa = PortableServer::POA._narrow(obj)
  • get and activate the POAManager
poa_man = root_poa.the_POAManager
poa_man.activate
  • create and activate the servant
echo_srv = MyEcho.new(orb)
echo_oid = root_poa.activate_object(echo_srv)
  • get the stringified IOR for the activated servant and save to a file for the client to use
echo_obj = root_poa.id_to_reference(echo_oid)
echo_ior = orb.object_to_string(echo_obj)
open('server.ior', 'w') { |io|
  io.write echo_ior
}
  • run the ORB request handling loop
orb.run

This completes your server implementation. Save the combined source code to the file echo_server.rb.

Implement the client

With the server implemented we now turn to the client implementation. Compared to the server code this implementation is simpler.
Again we need to load the CORBA namespace and load Ruby mappings for the IDL definitions. Only now we just need the client stub mappings. Since this is the default we can simplify the implement call.

require 'corba'
CORBA.implement('Test.idl')

Having loaded the Ruby client stub mappings we can proceed with initializing an ORB instance as we did for the server.

orb = CORBA.ORB_init('myORB')

Next we need to get an object reference to the servant instance which the server provides.
We made the server write the stringified IOR to a file on disk so here we use that file to get an object reference to our servant.
To access the Echo interface you defined in IDL on this object reference you need to narrow this reference using the Ruby client mapping for this IDL interface.

obj = orb.string_to_object('file://server.ior')
echo_obj = Test::Echo._narrow(obj)

Now that you have a narrowed object reference on which you can invoke the echo_string() and shutdown() operations on.
First we test the echoing:

input = 'Hello world!'
puts "sending \'#{input}\' to server"
output = echo_obj.echo_string(input)
puts "server returned \'#{output}\'"

And after this we invoke the shutdown() method on the server to let it shutdown and immediately proceed shutting down/destroying our own ORB.

echo_obj.shutdown()
orb.destroy()

This completes your client implementation. Save the combined source code to the file echo_client.rb.

Running your program

It's time to put it to the test.

Open up two terminal (console) windows with the active directory set to the directory containing the three application files: Test.idl, echo_server.rb and echo_client.rb.
Start the server in the first terminal as follows:

$ ruby echo_server.rb

Now move over to the other terminal and start the client as follows:

$ ruby echo_client.rb

The terminal running the client should now show you the following output:

sending 'Hello world!' to server
server returned 'ECHO: Hello world!'

After this both terminals should have returned to their respective prompts.

That is all there is to it to write R2CORBA programs!

Your rating: None Average: 4.5 (6 votes)

Comments

Bug: echo_server.rb must require 'corba/poa' instead of 'corba'

--

server implementation has a fault

The return value of the echo_string implementation should be a string not an array with a string !!

Thanks for catching these.

Thanks for catching these.