New York University

Computer Science Department

Courant Institute of Mathematical Sciences

 

CORBA Activation Service - Portable Object Adapter (POA)

 

Course Title: Application Servers                                           Course Number: g22.3033-011

Instructor: Jean-Claude Franchitti                                            Session: 5

 

 

Agenda

 

·        Introduction

·        Glossary

·        Portable Object Adapter (POA) with the CORBA 2.3 IDL/Java Language Mapping

·        Portable Object Adapter (POA) with the Interoperable Object Reference - IOR

 

 


Introduction

 

The Portable Object Adapter Specification was introduced in CORBA 2.2 (released in Feb 1998). Only recently has the ORB Portability IDL/Java Language Mapping been made available. The following pages have been revised and are now based on the CORBA 2.3 IDL/Java Language Mapping (which differs from previous versions).

 

The goal of the Portable Object Adapter (POA), was to address the weaknesses in version 2.0 of the CORBA standard that negatively affected the portability of CORBA server object source code between vendor implementations. The POA concept was developed as a replacement for the Basic Object Adapter (BOA). An object adapter is the interface software that resides between a server object and the ORB. Object adapters are responsible for managing the mapping of object references to a corresponding implementation object (especially important in systems with multiple active server objects), activation and deactivation of server objects, and the invocation of methods on server objects in response to remote requests. Although the OMG envisions the availability of different object adapters for different environments, the Basic Object Adapter (BOA) was defined in the 2.0 revision of the standard to provide a baseline level of functionality for any ORB implementation. However, the standard for the BOA is ambiguous, containing little or no guidance for object activation and deactivation mechanisms, event dispatching and method upcalls, and the interface between the BOA and object implementation skeleton. These ambiguities have resulted in incompatible variations in implementation of the BOA between vendors.

 

The POA provides a facility for the flexible management of server objects. Instead of providing only one object adapter, the POA has a root object adapter from which a set of additional POA objects can be created. Each of these POA objects can be tailored with a set of policies constructed to match the particular execution characteristics of an individual server object. By splitting up the job of the object adapter into many discrete objects, the POA creates a portable mechanism for supporting a wide range of object adapter behaviors. This architecture creates additional flexibility in the CORBA architecture.

 

The POA (Portable Object Adapter) is an object, which is visible from the server object only. While clients hold object references on which they invoke methods, the ORB, the POA and the server skeleton all cooperate to decide to which function/code this request must be passed to.


 


 


Figure 1. The server structure

 

When the server is launched, it will first create a POA. Then, it will tell the POA about its servants (step 1) (servants are the IDL definitions' implementations we coded. In fact, the interface implementations). Then it will ask the POA for an object reference (step 2) which is then advertised to the outside world either with an IOR string (step 3) or by binding this object reference to a name with the Naming Service.

 


 

 


Figure 2. The server registration

 

When the client is launched, it first asks the Naming Service about the object reference by using the resolve method (step 1). Then, it invokes the object's method with this object reference. This is possible because the object reference holds enough information to locate the server. This invocation is passed to the ORB (step 2) which seeks the correct server. Once the ORB has found the correct server (thanks to the object reference), it handles the request to the server.

 


 


Figure 3. A client request : 1

 

The server will then locate the POA which created the Object reference (thanks once more to the information contained in the object reference) and pass the request (step 2) to the POA. The request will finally be passed to the correct servant by the POA (step 3).

 


 


Figure 4. A client request : 2

 

The client does not really care about all this stuff which is handled by the ORB. The client only sees an object reference. The server only sees the POA: he does not care about anything else. We have already seen how the client handles object references. Now is time to get hands dirty about the server stuff.

 

The POA interface

The POA interface is rather big but simple:

 

module PortableServer {

  // lots of useless stuff here

  interface POA {

 

    // Object activation functions

    ObjectId activate_object (in Servant p_servant)

              raises ServantAlreadyActive, WrongPolicy);

    void activate_object_with_id (in ObjectId,

                                  in Servant p_servant)

              raises (ServantAlreadyActive, ObjectAlreadyActive,

                      WrongPolicy);

    void deactivate_object (in ObjectId id)

              raises (ObjectNotActive, WrongPolicy);

   

    // Reference creation functions

    Object create_reference (in CORBA::RepositoryId repid)

              raises (WrongPolicy);

    Object create_reference_with_id (in ObjectId id,

                                     in CORBA::RepositoryId repid)

              raises (WrongPolicy);

 

    // Identity Mapping functions

    ObjectId servant_to_id (in Servant p_servant)

              raises (ServantNotActive, WrongPolicy);

    Object servant_to_reference (in Servant p_servant)

              raises (ServantNotActive, WrongPolicy);

    Servant reference_to_servant (in Object reference)

              raises (ObjectNotActive, WrongAdapter, WrongPolicy);

    ObjectId reference_to_id (in Object reference)

              raises (WrongAdapter, WrongPolicy);

    Servant id_to_servant (in ObjectId oid)

              raises (ObjectNotActive, WrongPolicy);

    Object id_to_reference (in ObjectId oid)

              raises (ObjectNotActive, WrongPolicy);

 

 

  };

};

 

What you see here is an excerpt of the real POA interface: all the exception definitions and many functions definitions which are not relevant here were removed for clarity.

 

Now, let's look at these functions and their use:

 

Table 1. The POA interface

 

Function Name

Function Use

activate_object

This function will register the object implementation represented by the Servant object against the POA and will return the ObjectId which can then be used with the id_to_reference function to get the object reference associated with this interface implementation.

activate_object_with_id

This function will register the object implementation represented by the Servant object against the POA and will associate it the id parameter as an ObjectId. The ObjectId could be generated with a call to create_reference, then reference_to_id.

deactivate_object

This function will unregister an object specified by its ObjectId (obtained through one of reference_to_id or servant_to_id function).

create_reference

This function will create an Object reference from the object implementation (The Servant object).

create_reference_with_id

This function should be used only for well known objects whose ObjectId is well known...

servant_to_id

This function's signature makes things rather obvious. However, it is interesting to note that if the Servant was not activated, it will return a ServantNotActive exception which means that this function cannot be called before activating the object with one of activate_object or activate_object_with_id.

servant_to_reference

This function's signature makes things rather obvious. However, it is interesting to note that if the Servant was not activated, it will return a ServantNotActive exception which means that this function cannot be called before activating the object with one of activate_object or activate_object_with_id.

id_to_servant

This function's signature makes things rather obvious. However, it is interesting to note that if the object associated to the ObjectId was not activated, it will return an ObjectNotActivated exception which means that this function cannot be called before activating the object with one of activate_object or activate_object_with_id.

id_to_reference

This function's signature makes things rather obvious. However, it is interesting to note that if the object associated to the ObjectId was not activated, it will return an ObjectNotActivated exception which means that this function cannot be called before activating the object with one of activate_object or activate_object_with_id.

reference_to_servant

This function's signature makes things rather obvious. However, it is interesting to note that if the object associated to the object reference was not activated, it will return an ObjectNotActivated exception which means that this function cannot be called before activating the object with one of activate_object or activate_object_with_id.

reference_to_id

This function's signature makes things rather obvious. This function is different from all the other precedent functions: it will not throw any NotActivated exception which means that it may be used after a call to create_reference to generate the correct ObjectId to use for the activate_object_with_id functions.

 

Servants

All these functions show how a servant is registered against the POA. Servants are language dependent. Their structure and their use is completely mapping dependant.

 

The idea is to pass some initialization function a data structure which holds the list of function pointers for the implementation. Such a data structure is called an EPV (“Entry Point Vector”). The Servant data structure holds such an EPV.

 

A servant is thus nothing more than a structure which holds an EPV specific to the object implementation and a generic EPV called the _base_epv. The specific EPV is rather self explanatory: function pointers are listed there, waiting to be filled. The base EPV was not shown there: it is used to hold some information specific to the servant itself (in fact, a servant may be responsible for more than one object implementation).

 

POAManagers

 

POAs are used to dispatch the requests to their servants. However, there must be one way to handle the load on POAs and servants because we may have huge amounts of objects making requests to one object at the same time. This is achieved through the use of POAManagers. Each POA is associated to a POAManager.

 

POAManagers receive requests and send them to their POAs depending on their running state. A POAManager may be in one of 4 states: active, inactive, holding or discarding. If in the active state, requests are send to POAs, in the holding state, requests are queued till the POAManager gets back to the active state and in the discarding state, request are simply thrown out.

 

Generally, there is no need to write your own implementation of a POAManager: you can use the POAManager of the RootPOA thanks to the following interface:

 

module PortableServer {

  interface POA {

 

    POA create_POA (in string adapter_name,

                    in POAManager a_POAManager,

                    in CORBA::PolicyList policies)

              raises (AdapterAllreadyExists,

                      InvalidPolicy);

    void destroy (in boolean etherealize_objects,

                  in boolean wait_for_completion);

    POA find_poa (in string adapter_name,

                  In POAManager a_POAManager

                  In CORBA::PolicyList ListPolicies);

 

    readonly attribute POA the_parent;

    readonly attribute string the_name;

    readonly attribute POAManager the_POAManager;

  };

};

 

A POA is always created as the child of another POA: the RootPOA is the root of the POA tree. It is possible to find a particular children provided you know its name with the find_poa method. The the_parent attribute holds the parent POA, and the the_name attribute holds the POA name assigned with the create_poa method. destroy_poa will destroy the POA passed as parameter but also all its children.

 

To hold your servants, it is thus a good idea to create a child POA of the RootPOA and use it as the default_poa member of the _base_epv of your servants. You must also make sure that the POAManager used for your POA (i.e., generally the RootPOA's own POAManager) is in the active state as POAManagers are initialized in the holding state.

 

POA Policies

 

To each POA is also associated a set of Policies. If a NULL parameter is passed, the default policies are used. Here is the list of the policy types and their possible values. Default values are like this.

 

Table 2. POA Policies

 

Policy type

Possible values

Policy use

ThreadPolicy

ORB_CTRL_MODEL, SINGLE_THREAD_MODEL

In SINGLE_THREAD, only one poa thread may exist.

LifespanPolicy

TRANSIENT, PERSISTANT

In TRANSIENT mode, object implementations will disappear with their POA. Otherwise, a new POA is created when necessary.

ObjectIdUniqueness

UNIQUE_ID, MULTIPLE_ID

Each object within a given POA has a unique ObjectId.

IdAssignmentPolicy

USER_ID, SYSTEM_ID

ObjectId are either given by the user or the system. ie: you may use only one of the 2 functions for a given POA: activate_object or activate_object_with_id.

ServantRetentionPolicy

RETAIN, NON_RETAIN

In NON RETAIN mode, a request to a servant already processing another request will throw an ObjectNotExist exception.

RequestProcessingPolicy

USE_ACTIVE_OBJECT_MAP_ONLY, USE_DEFAULT_SERVANT, USE_SERVANT_MANAGER

Any request to a servant not found in the POA map will result in either a call to a default servant, an ObjectNotExist exception or a request to the Servant Manager to create the requested servant.

ImplicitActivationPolicy

IMPLICIT_ACTIVATION, NO_IMPLICIT_ACTIVATION

If NO_IMPLICIT_ACTIVATION is used, the programmer will need to activate all the created servants.

 

These policy objects should be created with the following calls, which are part of the POA interface.

 

module PortableServer {

  interface POA {

    ThreadPolicy

       create_thread_policy

         (in ThreadPolicyValue value);

    LifespanPolicy

       create_lifespan_policy

         (in LifespanPolicyValue value);

    IdUniquenessPolicy

       create_id_uniqueness_policy

         (in IdUniquenessPolicyValue value);

    IdAssignementPolicy

       create_id_assignment_policy

         (in IdAssignmentPolicyValue value);

    ImplicitActivationPolicy

       create_implicit_activation_policy

         (in ImplicitActivationPolicyValue value);

    ServantRetentionPolicy

       create_servant_retention_policy

         (in ServantRetentionPolicyValue value);

    ProcessingPolicy

       create_processing_policy

         (in ProcessingPolicyValue value);

  };

};

 

Then these policies should be put together in a CORBA::PolicyList and passed to the create_poa function to create the poa with the correct policies. This set of policies gives the programmer great power on server programming as they very precisely describe the server behavior and allow the programmer to control the server load. Here is some sample code to create a new POA using these policies. Note that when a policy type is not present in the PolicyList, the policy type is defaulted to the default value stated in the previous table.

 

 


Glossary

 

The POA Specification introduces some new vocabulary so please familiarize yourself with the following:


Servant

 

Object instances of a particular class implemented in a programming language like Java or C++.

 

Activation

 

Starting an existing CORBA object.

 

Deactivation

 

Shutting down of an active CORBA object.

 

Object ID

 

Identifier used to name an object within the scope of its OA.

 

Incarnation

 

Association of a servant body with a virtual CORBA object so that it may service requests.

 

Etherealization

 

Destruction of the association between the CORBA object and its servant.

 

Active Object Map

 

Table maintained by an OA that maps its active CORBA objects and their servants via Object IDs.




Portable Object Adapter (POA)

with the CORBA 2.3 IDL/Java Language Mapping

 

 

Changes in CORBA Inheritance approach:

 

The CORBA 2.3 IDL/Java Language Mapping also specifies changes in files generated as well as the inheritance structure. For the inheritance approach, the mapping specifies the creation of a <interface>POA class.


 



Changes in CORBA Delegation approach:

 

The CORBA 2.3 IDL/Java Language Mapping specifies the creation of a <interface>POATie class.

 


 



Portable Object Adapter (POA) with the Interoperable Object Reference (IOR)

 


Downloads:

 

The following examples were tested with Javasoft JDK 1.3 and Inprise VisiBroker for Java 4.1 on a Win98 machine.

 

Getting Started:

 

Win98 system environment settings for JavaSoft JDK1.3 for directory C:\jdk1.3:


REM
SET PATH=C:\jdk1.3\bin;%PATH%
SET CLASSPATH=C:\progra~1\javasoft\jre\1.3\lib\rt.jar;%CLASSPATH%
REM

Win98 system environment settings for Inprise VisiBroker for Java 4.1 (including Naming and Event Service). This assumes that the installation directory is C:\Inprise\vbroker:

REM VisiBroker for Java 4.1
REM
SET VBROKER_ADM=C:\Inprise\vbroker\adm
SET PATH=C:\Inprise\vbroker\bin;%PATH%
REM

Introduction:

 

This example uses a very basic IDL file.

 

Ship.idl


// Ship.idl

module Ship {

interface Aircraft {
string codeNumber();
};

interface AircraftCarrier {
Aircraft launch(in string name);
};

};


Server.java
 

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);

org.omg.CORBA.Object objPOA = null;
try {
objPOA = orb.resolve_initial_references("RootPOA");
}
catch (org.omg.CORBA.ORBPackage.InvalidName ex) {
}

org.omg.PortableServer.POA rootPOA = null;
rootPOA = (org.omg.PortableServer.POA) objPOA;

org.omg.PortableServer.POA myPOA = null;

try {
myPOA = rootPOA.create_POA("personalPOA", rootPOA.the_POAManager(),
new org.omg.CORBA.Policy[] {
rootPOA.create_id_assignment_policy(org.omg.PortableServer.IdAssignmentPolicyValue.USER_ID)
});
}
catch (java.lang.Exception ex)
{
System.err.println("Create POA Exception " + ex);
System.exit(1);
}

org.omg.PortableServer.Servant carrier = null;
try {
carrier = new AircraftCarrierImpl(myPOA);
myPOA.activate_object_with_id("Nimitz".getBytes(), carrier);
}
catch (org.omg.CORBA.SystemException se)
{
System.err.println("Activate failure " + se);
System.exit(1);
}
catch (org.omg.CORBA.UserException ue)
{
System.err.println("Activate failure " + ue);
System.exit(1);
}

// Write object reference to an IOR file

org.omg.CORBA.Object initRef = null;
try {
initRef = myPOA.servant_to_reference(carrier);

FileWriter output = new FileWriter("ns.ior");
output.write(orb.object_to_string(initRef));
output.close();
System.out.println("Wrote IOR to file: ns.ior");

myPOA.the_POAManager().activate();
System.out.println(carrier + " ready for launch !!!");
orb.run();
}
catch (java.lang.Exception exb)
{
System.err.println("Exception Last deep in here " + exb);
System.exit(1);
}
}
}


Client.java
 

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

public class Client
{
public static void main(String[] args)
{
org.omg.CORBA.ORB orb = null;
try {
orb = org.omg.CORBA.ORB.init(args, null);
}
catch (org.omg.CORBA.SystemException se)
{
System.err.println("ORB init failure " + se);
System.exit(1);
}

// Read IOR from file

org.omg.CORBA.Object initRef = null;
try
{
LineNumberReader input = new LineNumberReader(new FileReader("ns.ior"));
initRef = orb.string_to_object(input.readLine());
}
catch(java.io.IOException e)
{
System.out.println("Exception: " + e);
System.exit(1);
}

Ship.AircraftCarrier carrier = null;
try {
carrier = Ship.AircraftCarrierHelper.narrow(initRef);
}
catch (org.omg.CORBA.SystemException se)
{
System.err.println("AircraftCarrier narrow failure " + se);
System.exit(1);
}

// Standard program continues

String flight = args.length > 0 ? args[0]: "Ghost Rider 101";

Ship.Aircraft aircraft = null;
try {
aircraft = carrier.launch(flight);
System.out.println("Aircraft has been launched" );
}
catch (org.omg.CORBA.SystemException se)
{
System.err.println("Carrier launch failure " + se);

System.exit(1);
}

String designation = null;
try {
designation = aircraft.codeNumber();
System.out.println("Aircraft " + designation + " is airborne");
}
catch (org.omg.CORBA.SystemException se)
{
System.err.println("Get name failure " + se);
System.exit(1);
}
}
}


AircraftCarrierImpl.java
 

public class AircraftCarrierImpl extends Ship.AircraftCarrierPOA
{

private org.omg.PortableServer.POA _rootPOA;

public AircraftCarrierImpl(org.omg.PortableServer.POA rootPOA)
{
_rootPOA = rootPOA;
}

public Ship.Aircraft launch(String name)
{

org.omg.PortableServer.Servant aircraft = new AircraftImpl(name);
try {
_rootPOA.activate_object_with_id("name".getBytes(), aircraft);
}
catch (java.lang.Exception ex)
{
System.err.println("Exception 2 " + ex);
System.exit(1);
}

System.out.println(name + " on Catapult 2");

Ship.Aircraft _aircraft = null;
try {
_aircraft = Ship.AircraftHelper.narrow(_rootPOA.create_reference_with_id("name".getBytes(),
aircraft._all_interfaces(null, null)[0]));
}
catch (java.lang.Exception ex)
{
System.err.println("Exception 3 " + ex);
System.exit(1);
}
return _aircraft;
}
}


AircraftImpl.java

public class AircraftImpl extends Ship.AircraftPOA
{
private String _codeNumber;

public AircraftImpl()
{}

public AircraftImpl(String codeNumber)
{
_codeNumber = codeNumber;
}

public String codeNumber()
{
return _codeNumber;
}
}


Building the server with VisiBroker:

 

Create a directory and place Ship.idl, Server.java, AircraftCarrierImpl.java, and AircraftImpl.java into it.


prompt> idl2java Ship.idl
prompt> vbjc Server.java

Building the client with VisiBroker:

 

Place Client.java into the same directory as above


prompt> idl2java Ship.idl
prompt> vbjc Client.java

Running the program:

 

Now start the Smart Agent and server in one window, client in another window


prompt> osagent
prompt> vbj Server

prompt> vbj Client "Jolly 700"