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
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.
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.
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.
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).
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"