2013년 10월 4일 금요일

JacORB Chapter 4

4 Getting Started

Before we explain an example in detail, we look at the general process of developing CORBA applications with JacORB. We’ll follow this roadmap when working through the example. The example can be found in demo/grid which also contains a build file so that the development steps do not have to be carried out manually every time. Still, you should know what is going on.
As this document gives only a short introduction to JacORB programming and does not cover all the details of CORBA IDL, we recommend that you also look at the other examples in the demo/ directory. These are organized so as to show how the different aspects of CORBA IDL can be used with JacORB.

4.1 JacORB development: an overview


The steps we will generally have to take are:

  1. write an IDL specification.
  2. compile this specification with the IDL compiler to generate Java classes (Java interfaces, helperand holder classes, as well as stubs and skeletons).
  3. write an implementation for the Java interface generated in step 2
  4. write a “Main” class that instantiates the server implementation and registers it with the ORB
  5. write a client class that retrieves a reference to the server object and makes remote invocations, i.e. CORBA calls.

4.2 IDL specifications


Our example uses a simple server the definition of which should be clear if you know IDL. Its interface is given in server.idl. All the source code for this example can be found in JacORB2_3/demo/grid.

// server.idl
// IDL definition of a 2-D grid:
module demo
{
module grid
{
interface MyServer
{
typedef fixed <5,2> fixedT;

readonly attribute short height; // height of the grid
readonly attribute short width; // width of the grid

// set the element [n,m] of the grid, to value:
void set(in short n, in short m, in fixedT value);

// return element [n,m] of the grid:
fixedT get(in short n, in short m);

exception MyException
{
string why;
};
short opWithException() raises( MyException );
};
};
};

4.3 Generating Java classes


Feeding this file into the IDL compiler

$ idl -d ./generated server.idl

produces a number of Java classes that represent the IDL definitions. This is done according to a set of rules known as the IDL-to-Java language mapping as standardized by the OMG. If you are interested in the details of the language mapping, i.e. which IDL language construct is mapped to which Java language construct, please consult the specifications available from http://www.omg.org. The language mapping used by the JacORB IDL compiler is the one defined in CORBA 2.3 and is explained in detail in [BVD01]. For practical usage, please consult the  examples in the demo directory.
The most important Java classes generated by the IDL compiler are the interfaces MyServer and MyServerOperations, and the stub and skeleton files _MyServerStub, MyServerPOA and MyServerPOATie. We will use these classes in the client and server as well as in the implementation of the grid’s functionality and explain each in turn.
Note that the IDL compiler will produce a directory structure for the generated code that corresponds to the module structure in the IDL file, so it would have produced a subdirectory demo/grid in the current directory had we not directed it to put this directory structure to ./generated by using the compiler’s -d switch. Where to put the source files for generated classes is a matter of taste. Some people prefer to have everything in one place (as using the -d option in this way achieves), others like to have one subdirectory for the generated source code and another for the output of the Java compiler, i.e. for the .class files.

4.4 Implementing the interface

Let’s try to actually provide an implementation of the functionality promised by the interface. The class which implements that interface is called gridImpl. Apart from providing a Java implementation for the operations listed in the IDL interface, it has to inherit from a generated class that both defines the Java type that represents the IDL type MyServer and contains the code needed to receive remote invocations and return results to remote callers. This class is MyServerPOA. You might have noticed that this approach is impractical in situations where your implementation class needs to inherit from other classes. As Java only has single inheritance for implementations, you would have to use an alternative approach — the “tie”–approach — here. The tie approach will be explained
later. Here is the Java code for the grid implementation. It uses the Java library class
java.math.BigDecimal for values of the IDL fixed–point type fixedT:

package demo.grid;

/**
* A very simple implementation of a 2-D grid
*/

import demo.grid.MyServerPackage.MyException;

public class gridImpl
extends MyServerPOA
{
protected short height = 31;
protected short width = 14;
protected java.math.BigDecimal[][] mygrid;

public gridImpl()
{
mygrid = new java.math.BigDecimal[height][width];
for( short h = 0; h < height; h++ )
{
for( short w = 0; w < width; w++ )
{
mygrid[h][w] = new java.math.BigDecimal("0.21");
}
}
}

public java.math.BigDecimal get(short n, short m)
{
if( ( n <= height ) && ( m <= width ) )
return mygrid[n][m];
else
return new java.math.BigDecimal("0.01");
}

public short height()
{
return height;
}

public void set(short n, short m, java.math.BigDecimal value)
{
if( ( n <= height ) && ( m <= width ) )
mygrid[n][m] = value;
}

public short width()
{
return width;
}

public short opWithException()
throws demo.grid.MyServerPackage.MyException
{
throw new demo.grid.MyServerPackage.MyException
("This is only a test exception,”);
}
}

4.5 Writing the Server


To actually instantiate a gridImpl object which can be accessed remotely as a CORBA object of type MyServer, you have to instantiate it in a main method of some other class and register it with a component of the CORBA architecture known as the Object Adapter. Here is the class Server which does all that is necessary to activate a CORBA object of type MyServer from a Java gridImpl object:

package demo.grid;

import java.io.*;
import org.omg.CosNaming.*;

public class Server
{
public static void main( String[] args )
{
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args, null);
try
{
org.omg.PortableServer.POA poa =
org.omg.PortableServer.POAHelper.narrow(
orb.resolve_initial_references("RootPOA"));

poa.the_POAManager().activate();

org.omg.CORBA.Object o = poa.servant_to_reference(new gridImpl());
if( args.length == 1 )
{
// write the object reference to args[0]
PrintWriter ps = new PrintWriter(
new FileOutputStream(
new File( args[0] )));
ps.println( orb.object_to_string( o ) );
ps.close();
}
else
{
// register with the naming service
NamingContextExt nc =
NamingContextExtHelper.narrow(
orb.resolve_initial_references("NameService"));
nc.bind( nc.to_name("grid.example"), o);
}
}
catch ( Exception e )
{
e.printStackTrace();
}
orb.run();
}
}

After initializing the ORB we need to obtain a reference to the object adapter—the POA—by asking the ORB for it. The ORB knows about a few initial references that can be retrieved using simple names like “RootPOA”. The returned object is an untyped reference of type CORBA.Object and thus needs to be narrowed to the correct type using a static method narrow() in the helper class for the type in question. We now have to activate the POA because any POA is created in “holding” state in which it does not process incoming requests. After calling activate() on the POA’s POAManager object, the POA is in an active state and can now be asked to create a CORBA object reference from a Java object also know as a Servant.
In order to make the newly created CORBA object accessible, we have to make its object reference available. This is done using a publicly accessible directory service, the naming server. A reference to the naming service is obtained by calling orb.resolve initial references("NameService") on the ORB and narrowing the reference using the narrow() method found in class org.omg.CosNaming.NamingContextExtHelper. Having done this, you should call the bind() operation on the name server. The name for the object which has to be supplied as an argument to bind() is not simply a string. Rather, you need to provide a sequence of CosNaming.NameComponents that represent the name. In the example, we chose to use an extended Name Server interface that provides us with a more convenient conversion operation from strings to Names.

4.6 Writing a client

Finally, let’s have a look at the client class which invokes the server operations:

package demo.grid;

import org.omg.CosNaming.*;

public class Client
{
public static void main(String args[])
{
try
{
MyServer grid;
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args,null);

if(args.length==1 )
{
// args[0] is an IOR-string
grid = MyServerHelper.narrow(orb.string_to_object(args[0]));
}
else
{
NamingContextExt nc =
NamingContextExtHelper.narrow(
orb.resolve_initial_references("NameService"));

grid = MyServerHelper.narrow(
nc.resolve(nc.to_name("grid.example")));
}

short x = grid.height();
System.out.println("Height = " + x);

short y = grid.width();
System.out.println("Width = " + y);

x -= 1;
y -= 1;

System.out.println("Old value at (" + x + "," + y +"): " +
grid.get( x,y));

System.out.println("Setting (" + x + "," + y +") to 470.11");

grid.set( x, y, new java.math.BigDecimal("470.11"));

System.out.println("New value at (" + x + "," + y +"): " +
grid.get( x,y));
try
{
grid.opWithException();
}
catch (jacorb.demo.grid.MyServerPackage.MyException ex)
{
System.out.println("MyException, reason: " + ex.why);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}

After initializing the ORB, the client obtains a reference to the “grid” service by locating the reference using the name service. Again, resolving the name is done by getting a reference to the naming service by calling orb.resolve initial references("NameService") and querying the name server for the "grid" object by calling resolve(). The argument to the resolve operation is, again, a string that is converted to a Name. The result is an object reference of type org.omg.CORBA.Object which has to be narrowed to the type we are expecting, i.e. MyServer.
After compiling everything we’re now ready to actually run the server and the client on different (virtual) machines. Make sure the name server is running before starting either the server or the client. If it isn’t, type something like:

$ ns /home/me/public_html/NS_Ref

where /home/me/public_html/NS_Ref is the name of a locally writable file which can be read by using the URL given in both the remote client and server code. (This is to avoid using a well–known address for the name server, so both client and server look up the location of the name server via the URL and later communicate with it directly.) You can now launch the server:

$ jaco demo.grid.Server

The client can be invoked on any machine you like:

$ jaco demo.grid.Client

Running the client after starting the server produces the following output on your terminal:

Height = 31
Width = 14
Old value at (30,13): 0.21
Setting (30,13) to 470.11
New value at (30,13): 470.11
MyException, reason: This is only a test exception, no harm done :-)
done.

4.6.1 The Tie Approach

If your implementation class cannot inherit from the generated servant class MyServerPOA because, e.g., you need to inherit from another base class, you can use the tie approach. Put simply, it replaces inheritance by delegation. Instead of inheriting from the generated base class, your implementation needs to implement the generated operations interface MyServerOperations:

package demo.grid;

import demo.grid.MyServerPackage.MyException;

public class gridOperationsImpl
implements MyServerOperations
{
...
}

Your server is then written as follows:

package demo.grid;

import java.io.*;
import org.omg.CosNaming.*;

public class TieServer
{
public static void main( String[] args )
{
org.omg.CORBA.ORB orb =
org.omg.CORBA.ORB.init(args, null);
try
{
org.omg.PortableServer.POA poa =
org.omg.PortableServer.POAHelper.narrow(
orb.resolve_initial_references("RootPOA"));

// use the operations implementation and wrap it in
// a tie object

org.omg.CORBA.Object o =
poa.servant_to_reference(
new MyServerPOATie( new gridOperationsImpl()) );

poa.the_POAManager().activate();

if( args.length == 1 )
{
// write the object reference to args[0]
PrintWriter ps = new PrintWriter(
new FileOutputStream(new File( args[0] )));
ps.println( orb.object_to_string( o ) );
ps.close();
}
else
{
NamingContextExt nc =
NamingContextExtHelper.narrow(
orb.resolve_initial_references("NameService"));
NameComponent [] name = new NameComponent[1];
name[0] = new NameComponent("grid", "whatever");
nc.bind( name, o );
}
}
catch ( Exception e )
{
e.printStackTrace();
}
orb.run();
}

}

댓글 없음: