Chapter 5 Creation of POA Hierarchies Made
Simple
The CORBA POA specification is considered by many to be
powerful but complex. Actually, the POA specification is powerful
and
conceptually simple. Unfortunately, verbose APIs
obscure the simple concepts that are at the heart of the POA
specification. It is these verbose APIs that are largely to blame
for the reputation that the POA specification has for being
complex. This chapter discusses a class called
PoaUtility that provides a simplification
“wrapper” around the POA APIs. The wrapper API
contains just three operations that provide all the power and
flexibility previously provided by a dozen operations of the
“raw” POA API. The
PoaUtility class does
not hide the concepts of the POA. In fact, it is just
the opposite: by replacing a dozen low-level operations with a
smaller number of higher-level operations, the wrapper allows
developers to more easily see the underlying simplicity and
elegance of the POA. Currently, the
PoaUtility class
works “out of the box” with five CORBA products:
Orbix/C++, Orbix/Java, Orbacus/C++, Orbacus/Java, and TAO. It
should be easy to extend support to other CORBA products and/or
languages.
5.1 Introduction
Many people have
mixed feelings about the POA specification. On the one hand, it
provides CORBA with a lot of power and flexibility. On the other
hand, the POA specification can
seem complex, and this
puts a lot of developers off CORBA. This is a shame because the
POA specification is conceptually simple and is actually quite
elegant. It is just that verbose APIs obscure the simple concepts
that are at the heart of the POA specification. This chapter
discusses a class called
PoaUtility that provides a
simplification “wrapper” around the POA APIs. The
wrapper API contains just three operations that provide all the
power and flexibility previously provided by a dozen operations
of the “raw” POA API. The
PoaUtility class
does
not hide the concepts of the POA. In fact, it is
just the opposite: by replacing a dozen low-level operations with
a smaller number of higher-level operations, the wrapper allows
developers to more easily see the underlying simplicity and
elegance of the POA. Some important benefits of the wrapper API
are as follows:
- The PoaUtility class reduces
the learning curve for the POA without sacrificing any of its
power and flexibility. This means that developers will require
less time to become skilled in the use of CORBA.
- Developer productivity is increased.
Realistically, developers will be able to create a server's POA
hierarchy in just a few minutes and with just a few lines of
code, without having to consult any reference
documentation. Furthermore, if the creation of a POA fails (for
example, a developer might have assigned it incompatible
policies) then the PoaUtility class throws an
exception that contains a self-explanatory message that helps
the developer to quickly diagnose the source of the
problem.
- It is common for a CORBA product to
allow a server to be deployed (1) with or (2) without
an Implementation Repository, and with both of these options
the server can (3) listen on a fixed port or
(4) listen on a port that is chosen by the operating
system. Unfortunately, CORBA does not specify the practical
details of how to choose a particular deployment model for a
server application. Instead, such details are left to
proprietary extensions provided by each CORBA product. In some
CORBA products, these proprietary extensions take the form of
command-line options or entries in a configuration file. In
other CORBA products, the proprietary extensions take the form
of additional APIs, the use of which must be hard-coded into
the source code of a server application. This is unfortunate
because it hinders source-code portability of CORBA
applications. The PoaUtility class encapsulates use of
these proprietary APIs. In doing so, the PoaUtility
class greatly enhances source-code portability of server
applications. The encapsulation also has the benefit of making
it trivial for the deployment model to be decided at deployment
time rather than being hard-coded at development time. In this
way, server applications become more flexible.
Currently, the
PoaUtility class works “out of
the box” with six CORBA products: Orbix/C++, Orbix/Java,
Orbacus/C++, Orbacus/Java, TAO and omniORB. It should be easy to
extend support to other CORBA products and/or languages.
Section
5.5 offers
some advice for readers who are interested in porting the
PoaUtility class to other CORBA products.
5.2 Building A POA
Hierarchy
We introduce the
PoaUtility class by
showing how it is typically used to construct a POA hierarchy.
Let us assume that you are writing a CORBA server with the
following characteristics:
- The server implements three interfaces:
Foo, FooFactory (a factory for creating
Foo objects) and Administration (used to
perform administration-type operations on the server).
- The server has three POAs, one for each
IDL interface. By convention, each POA has a name that is the
same as the name of the IDL interface associated with it. For
example, servants for IDL interface Foo are stored in
the POA called Foo.
- The server requires two POA managers.
One POA manager, which we shall call the core
functionality POA manager, is used to control the
dispatching of requests to the Foo and
FooFactory POAs. The other POA Manager, which we shall
call the admin functionality POA manager, is used to
control the dispatching of requests to the
Administration POA.
The above functionality can be implemented by a class
called, say,
PoaHierarchy that makes use of the
functionality provided by the
PoaUtility class. A C++
implementation of this class is presented in
Section
5.2.1,
and a Java version is presented in Section
5.2.2.
5.2.1 C++ Version
The declaration
of the C++
PoaHierarchy class is shown below.
1 #include "PoaUtility.h"
2 using namespace corbautil;
3 class PoaHierarchy : PoaUtility
4 {
5 public:
6 PoaHierarchy(CORBA::ORB_ptr orb,
7 PoaUtility::DeploymentModel deployModel)
8 throw(PoaUtilityException)
9 //——–
10 // Accessors
11 //——–
12 POAManager_ptr core_functionality()
13 { return m_core_functionality.mgr(); }
14 POAManager_ptr admin_functionality()
15 { return m_admin_functionality.mgr(); }
16 POA_ptr FooFactory() { return m_FooFactory; }
17 POA_ptr Foo() { return m_Foo; }
18 POA_ptr Administration() { return m_Administration; }
19
20 private:
21 //——–
22 // Instance variables
23 //——–
24 LabelledPOAManager m_core_functionality;
25 LabelledPOAManager m_admin_functionality;
26 POA_var m_FooFactory;
27 POA_var m_Foo;
28 POA_var m_Administration;
29
30 //——–
31 // The following are not implemented
32 //——–
33 PoaHierarchy();
34 PoaHierarchy(const PoaHierarchy &);
35 PoaHierarchy & operator=(const PoaHierarchy &);
36 };
The following points should be noted:
All the interesting functionality of the
PoaHierarchy class is implemented in its constructor,
which is shown below. Comments follow after the code.
1 #include "PoaHierarchy.h"
2
3 PoaHierarchy::PoaHierarchy(CORBA::ORB_ptr orb,
4 PoaUtility::DeploymentModel deployModel)
5 throw(PoaUtilityException)
6 : PoaUtility(orb, deployModel)
7 {
8 //——–
9 // Create the POA Managers
10 //——–
11 m_core_functionality =
12 createPoaManager("core_functionality");
13 m_admin_functionality =
14 createPoaManager("admin_functionality");
15
16 //——–
17 // Create the FooFactory POA
18 //——–
19 m_FooFactory = createPoa("FooFactory",
20 root(), m_core_functionality,
21 "user_id + persistent + use_active_object_map_only");
22
23 //——–
24 // Create the FooFactory/Foo POA
25 //——–
26 m_Foo = createPoa("Foo",
27 m_FooFactory, m_core_functionality,
28 "system_id + transient + unique_id + retain"
29 "+ use_active_object_map_only");
30
31 //——–
32 // Create the Administration POA
33 //——–
34 m_Administration = createPoa("Administration",
35 root(), m_admin_functionality,
36 "single_thread_model + persistent + user_id"
37 "+ use_active_object_map_only");
38 }
The following points should be noted:
- The constructor passes its parameters to
its parent-class constructor (line 6).
- A POA manager is created by calling the
inherited createPoaManager() operation
(lines 11–14), and passing a parameter that
specifies a unique label (name) by which the POA
manager will be known to the internals of the
PoaUtility class.
- A POA is created by calling the
inherited createPoa() operation
(lines 19–37). This operation takes four parameters.
The first parameter is the name of the POA to be created. The
second parameter is a reference to its parent POA. The
inherited operation root() can be used to specify the
root POA (lines 20 and 35). The third parameter is a
labelled POA manager that has been previously obtained by
calling createPoaManager(). The final parameter is a
string of the form "policy + policy +
...".1 This string represents a list of policy
values that should be applied when creating the POA. The names
of the policy values are lowercase equivalents of the
enum values used by the raw POA APIs, without the
module prefix. For example, the
PortableServer::PERSISTENT policy value is represented
by "persistent". The use of lowercase makes it easier
for programmers because most code is written in lowercase.
- The C++ language states that a C++
compiler concatenates adjacent string literals. For example:
"good" "bye" are concatenated to give
"goodbye". This can be used to split a long
"policy + policy + ..." string over several lines
(lines 28–29 and 36–37).
- If anything goes wrong then the
createPoaManager() and createPoa() operations
throw an exception (in the form of a
corbautil::PoaUtilityException) that provides details
of the CORBA exception thrown and the
POA/POA-manager/policy-list for which the exception applies.
Because of this, the PoaHierarchy class does
not have a separate try-catch clause around
each call to createPoaManager() and
createPoa() to diagnose the source of the problem.
Instead, the PoaHierarchy class lets such exceptions
propagate out to the main() function of the
application, where a single try-catch clause suffices.
This will be illustrated in Section 5.2.1.1.
- All the useful functionality of the
PoaUtility class is available through public
operations. Because of this, developers are not restricted to
using PoaUtility by sub-classing from it. For example,
the constructor of the PoaHierarchy class could have
declared a local variable of type PoaUtility
instead.
- The PoaUtility class has a
root() operation that can be called to specify the
root POA as a parent of a POA being created with
createPoa(). However, the root POA should not
be used for storing servants. This is because a feature of
PoaUtility is its ability to allow POAs or POA
Managers to optionally listen on fixed port numbers (this is
discussed in Section 5.3) and,
unfortunately, the PoaUtility class cannot always
configure the port number on which the root POA listens.
5.2.1.1 Using the POA Hierarchy in a
Server Application
The code below illustrates the mainline of
a server that uses the
PoaHierarchy class. Comments
follow after the code.
1 CORBA::ORB_var g_orb;
2 PoaHierarchy * g_poa_h;
3 FooFactory_impl * g_FooFactory_sv;
4 Administration_impl * g_Administration_sv;
5
6 int main(int argc, char ** argv)
7 {
8 PortableServer::ObjectId_var obj_id;
9
10 int exitStatus = 0;
11 try {
12 //——–
13 // Initialize CORBA and create the POA hierarchy
14 //——–
15 g_orb = CORBA::ORB_init(argc, argv);
16 PoaUtility::DeploymentModel deployModel = ...;
17 g_poa_h = new PoaHierarchy(g_orb, deployModel);
18
19 //——–
20 // Create and activate singleton servants
21 //——–
22 g_FooFactory_sv = new FooFactory_impl();
23 obj_id = ...;
24 g_poa_h->FooFactory()->activate_object_with_id(
25 obj_id, g_FooFactory_sv);
26 g_FooFactory_sv->_remove_ref();
27 ... // similar code for the Administration singleton
28
29 //——–
30 // Export singleton object references
31 //——–
32 ...
33
34 //——–
35 // Activate POA managers and go into the event loop
36 //——–
37 g_poa_h->core_functionality()->activate();
38 g_poa_h->admin_functionality()->activate();
39 g_orb->run();
40 }
41 catch (const CORBA::Exception & ex) {
42 cout << ex << endl;
43 exitStatus = 1;
44 }
45 catch (corbautil::PoaUtilityException & ex) {
46 cout << ex << endl;
47 exitStatus = 1;
48 }
49
50 //——–
51 // Tidy up and terminate
52 //——–
53 delete g_poa_h;
54 if (!CORBA::is_nil(g_orb)) {
55 try {
56 g_orb->destroy();
57 } catch (const CORBA::Exception & ex) {
58 cout << "orb->destroy() failed: " << ex << endl;
59 exitStatus = 1;
60 }
61 }
62 return exitStatus;
63 }
The following points should be noted:
- In a CORBA server, several objects are
typically accessed by different parts of application code. Such
objects include the ORB, the POA hierarchy and servants for
singleton interfaces. The above code has declared these as
global objects (lines 1–4), and used the prefix
"g_" to indicate that they are global variables. In
your own applications, you may wish to replace these global
variables with whatever is considered to be a politically
correct alternative.
- Once the CORBA::ORB has been
created (line 15), the PoaHierarchy object is
created (line 17), passing two parameters to its
constructor: the ORB and an enum value that specifies
the server's deployment model. This enum value might
be determined based on, say, a command-line option or an entry
in a configuration file. If creation of the POA hierarchy fails
then the constructor of PoaHierarchy throws a
corbautil::PoaUtilityException. This is caught and
printed out by a catch clause
(lines 45–48). Note that the details of creating the
POA hierarchy have been encapsulated in the
PoaHierarchy class, so the main() function
requires just one line of code to create the POA
hierarchy.
- Having created the PoaHierarchy
object, accessor operations are invoked on it to access a POA
(line 24) or POA Managers (lines–37 38). Notice
that these accessor operations do not use _duplicate()
so there is not need for the calling code to call
CORBA::release() on the returned reference.
- During graceful shutdown of the server,
the PoaHierarchy object should be deleted
(line 53).
5.2.2 Java Version
The Java
version of the
PoaHierarchy class is shown below.
1 import org.omg.CORBA.*;
2 import org.omg.PortableServer.*;
3 import com.iona.corbautil.*;
4 class PoaHierarchy
5 {
6 public PoaHierarchy(ORB orb, int deployModel)
7 throws PoaUtilityException
8 {
9 PoaUtility util = PoaUtility.init(orb, deployModel);
10 m_core_functionality = util.createPoaManager(
11 "core_functionality");
12 m_admin_functionality = util.createPoaManager(
13 "admin_functionality");
14
15 m_FooFactory = util.createPoa("FooFactory",
16 util.root(),
17 m_core_functionality,
18 "user_id + persistent +"
19 + "use_active_object_map_only");
20
21 m_Foo = util.createPoa("Foo", m_FooFactory,
22 m_core_functionality,
23 "system_id + transient +"
24 + "unique_id + retain +"
25 + "use_active_object_map_only");
26
27 m_Administration = util.createPoa("Administration",
28 util.root(),
29 m_admin_functionality,
30 "single_thread_model +"
31 + "persistent + user_id +"
32 + "use_active_object_map_only");
33 }
34
35 //——–
36 // Accessors
37 //——–
38 public POA FooFactory() { return m_FooFactory; }
39 public POA Foo() { return m_Foo; }
40 public POA Administration() { return m_Administration; }
41 public POAManager core_functionality()
42 { return m_core_functionality.mgr(); }
43 public POAManager admin_functionality()
44 { return m_admin_functionality.mgr(); }
45
46 //——–
47 // Instance variables
48 //——–
49 private LabelledPOAManager m_core_functionality;
50 private LabelledPOAManager m_admin_functionality;
51 private POA m_FooFactory;
52 private POA m_Foo;
53 private POA m_Administration;
54 }
The following points should be noted:
5.2.2.1 Using the POA Hierarchy in
a Server Application
The code below illustrates the mainline
of a server that uses the
PoaHierarchy class. Comments
follow after the code.
1 import org.omg.CORBA.*;
2 import org.omg.PortableServer.*;
3
4 public class Server
5 {
6 public static ORB orb;
7 public static PoaHierarchy poaH;
8 public static FooFactoryImpl fooFactorySv;
9 public static AdministrationImpl administrationSv;
10
11 public static void main(String args[])
12 {
13 try {
14 //——–
15 // Initialize the ORB and create the POA Hierarchy
16 //——–
17 orb = ORB.init(args, null);
18 int deployModel = ...;
19 poaH = new PoaHierarchy(orb, deployModel);
20
21 //——–
22 // Create and activate singleton servants
23 //——–
24 fooFactorySv = new FooFactoryImpl();
25 poaH.FooFactory().activate_object_with_id(
26 "FooFactory".getBytes(), fooFactorySv);
27 ... // similar code for the Administration singleton
28
29 //——–
30 // Export singleton object references
31 //——–
32 ...
33
34 //——–
35 // Activate POA Managers and go into the event loop
36 //——–
37 poaH.core_functionality().activate();
38 poaH.admin_functionality().activate();
39 orb.run();
40
41 } catch (PoaUtilityException ex) {
42 System.out.println(ex.getMessage());
43 } catch (Exception ex) {
44 System.out.println(ex.toString());
45 }
46
47 //——–
48 // Tidy up and terminate
49 //——–
50 if (orb != null) {
51 try {
52 orb.destroy();
53 } catch (Exception e) {
54 }
55 }
56 }
57 }
The following points should be noted:
- Once the CORBA::ORB has been
created (line 17), the PoaHierarchy object is
created, passing two parameters to its constructor: the ORB and
an int value that species the server's deployment
model (line 19). This deployment model value might be
determined based on, say, a command-line option or an entry in
a configuration file. If creation of the POA hierarchy fails
then the constructor of PoaHierarchy throws a
PoaUtilityException exception. This is caught and
printed out by a catch clause (lines 41–42). Note
that all the details of creating the POA hierarchy have been
encapsulated in the PoaHierarchy class, so just one
line of code (to create the PoaHierarchy object) is
all that is required in the main() function.
- Having created the PoaHierarchy
object, accessor operations are invoked on it to access a POA
(line 25) or POA Managers (lines 37–38).
5.3 Server Deployment
Models
The CORBA specification describes an
implementation repository. This term is not very
intuitive so it deserves an explanation.
Implementation
is the CORBA terminology for “server application”,
and
repository means a persistent storage area, such as
a database. Thus,
implementation repository (commonly
abbreviated to IMR) is a database that stores information about
CORBA server applications. Most of the functionality of an IMR
must be implemented in a platform-specific manner. For this
reason, the CORBA specification just specifies the high-level
functionality that an IMR must provide, and does
not
specify any of the “look and feel” of an IMR. This
means that IMRs differ widely between different CORBA products.
For example, the Orbacus IMR is an executable called
imr, while the Orbix IMR is an pair of executables: one
is called
itlocator (the locator daemon) and this is
supported by
itnode_daemon (the node daemon). When a
server application is registered with an IMR then the IMR can
(re-)launch the server. When a server is deployed in this manner,
any persistent object references that are exported by the server
do
not contain the server's host and port, but rather
they contain the host and port of the server's IMR. When a client
tries to invoke upon such an object, the client sends its first
invocation to the IMR's host and port. This gives the IMR a
chance to (re-)launch the server if it is not currently running
and then redirect the client to the server's actual host and
port. The main benefit of using an IMR to launch a server is that
the server can be re-launched automatically if it ever dies. Some
IMRs offer additional benefits. For example, the Orbix IMR can
launch several
replicas of a server, in order to provide
load balancing and fault tolerance. The benefits of having an IMR
are desirable in many circumstances. However, there are some
reasons why some people prefer to
not deploy a server
through an IMR:
- Many people learn CORBA once piece at
a time. Because of this, it is common for a person to know how
to develop a CORBA server and how to run it from the
command-line, but not (yet) be familiar with how to deploy a
server through the IMR.
- In many CORBA products the IMR is a
single point of failure. Some organizations cannot risk
deploying mission-critical applications that have single points
of failure. Such organizations often prefer to deploy CORBA
systems without using an IMR. An alternative is to develop
applications using a CORBA product (such as Orbix) that
provides a replicated IMR so that the single point of
failure is removed.
- Some organizations use a variety of
different CORBA products. For example, perhaps one internal
project is built using one CORBA product, while another
internal project (perhaps in a different department) is built
using a second brand name of CORBA product. Finally, the
organization may have bought a pre-built CORBA server from a
third-party company, and this server was built using a third
brand of CORBA product. The system administrator in such an
organization is faced with learning how to perform
administration tasks with the IMRs of each of the three CORBA
products. However, this learning curve could be reduced if some
(or all) of the servers could be deployed without an
IMR. In this case, the trade-off is to sacrifice the benefits
offered by of IMR in order to simplify administration.
An orthogonal issue for server deployment is whether the
server will listen on a fixed port or on an arbitrary port that
is chosen by the operating system. Such arbitrary ports are often
called
random,
transient (meaning temporary) or
ephemeral (meaning short-lived) ports. Use of a fixed
port is often desirable if the server is to be accessed by
clients across a firewall, or if the server contains
PERSISTENT POAs and is being deployed
without
an IMR. However, most CORBA products will, by default, have
servers listens on random ports; this is acceptable if the server
contains only
TRANSIENT POAs, or if the server contains
PERSISTENT POAs but is deployed through an IMR.
Unfortunately, CORBA does
not specify the practical
details of how to choose:
- Whether or not a server is deployed
through an IMR.
- Whether or not a server listens on a
fixed or random port.
Instead, such details are left to proprietary extensions
provided by each CORBA product. In some CORBA products, these
proprietary extensions take the form of command-line options or
entries in a configuration file. In other CORBA products, the
proprietary extensions take the form of additional APIs, the use
of which must be hard-coded into the source code of a server
application. This is unfortunate because it hinders source-code
portability of CORBA applications. However, the proprietary APIs
are typically called when creating either POAs or POA Managers.
This makes it possible for the
PoaUtility class to
encapsulate the use of such proprietary APIs, and so increase
source-code portability of server applications, while at the same
time deferring deployment decisions until deployment time rather
than prematurely deciding them during development.
5.3.1 Specifying a Server Deployment
Model with PoaUtility
The constructor of
PoaUtility takes a parameter called
deployModel
that is used to specify how the server is being deployed. The C++
definition of this parameter's legal values are shown below:
namespace corbautil {
class PoaUtility {
public:
enum DeploymentModel {
RANDOM_PORTS_NO_IMR, RANDOM_PORTS_WITH_IMR,
FIXED_PORTS_NO_IMR, FIXED_PORTS_WITH_IMR
};
static DeploymentModel
stringToDeploymentModel(const char * model)
throw(PoaUtilityException);
...
};
};
Java does not have an
enum type so the Java
deployment models are denoted as integer constants, as shown below:
package com.iona.corbautil;
abstract public class PoaUtility {
public static final int RANDOM_PORTS_NO_IMR = 0;
public static final int RANDOM_PORTS_WITH_IMR = 1;
public static final int FIXED_PORTS_NO_IMR = 2;
public static final int FIXED_PORTS_WITH_IMR = 3;
public static int stringToDeploymentModel(String model)
throws PoaUtilityException
...
};
The four values simply indicate whether or not a server
listens on random ports, and whether or not the server is deployed
through the IMR. If a CORBA product requires use of proprietary
APIs for any of these deployment options then the
PoaUtility class calls the appropriate APIs. If a CORBA
product does not require use of any proprietary APIs then the
PoaUtility class simply ignores the
deployModel
parameter. In either case, the end user will still have to use
the CORBA vendor's proprietary administration commands,
command-line options and/or configuration file entries to set up
the necessary environmental support for the chosen deployment
model. The
PoaUtility class provides a
stringToDeploymentModel() utility method that converts a
deployment model string, such as
"RANDOM_PORTS_NO_IMR"
to the corresponding
enum/
int value. This
utility method performs a case-insensitive string comparison,
which means that lower-case strings, such as
"random_ports_no_imr" are also acceptable. If an invalid
deployment model string (for example,
"foo") is passed
as a parameter then the method throws a
PoaUtilityException that contains a message of the form:
Invalid DeploymentModel "foo"
Use of this utility method makes it trivial for server
applications to obtain a deployment model from, say, a command-line
option or an entry in a runtime configuration file. This then means
that a server's deployment model can be decided at deployment time
rather than being hard-coded during development.
5.3.2 Orbix Server Deployment
If
deploying an Orbix server through the IMR then the
itadmin utility must be used to register the server with
the IMR. Also, whenever starting an IMR-deployable server (either
from the command-line or through the IMR) then you must be
consistent in specifying the same
-ORBname
<name> command-line arguments to the server.
If deploying an Orbix server so that it listens on fixed ports
then you must indicate the port number used by a POA manager
through a configuration variable of the form:
3
<label>:iiop:addr_list
where
<label> is the
label
parameter passed to
createPoaManager(). Some examples are
shown below:
core_functionality:iiop:addr_list = ["<host>:6000"];
admin_functionality:iiop:addr_list = ["<host>:6001"];
The
"<host>" string should be replaced
with the name of the computer on which the server is running.
5.3.3 Orbacus Server Deployment
If
deploying an Orbacus server through the IMR then the
imradmin utility must be used to register the server
with the IMR. Also, if you start an IMR-deployable server from
the command-line then you must specify
-ORBServerId
<name> as command-line arguments to the
server. If deploying an Orbacus server so that it listens on
fixed ports then you must indicate the port number used by a POA
manager through a configuration variable of the form:
ooc.orb.poamanager.<label>.endpoint
where
<label> is the
label
parameter passed to
createPoaManager(). Some examples are
shown below:
ooc.orb.poamanager.core_functionality.endpoint=iiop –port 6000
ooc.orb.poamanager.admin_functionality.endpoint=iiop –port 6001
5.3.4 TAO Server Deployment
If
deploying a TAO server through the IMR then the
tao_imr
utility must be used to register the server with the IMR. Also,
whenever starting an IMR-deployable server (either from the
command-line or through the IMR) then you should specify
-ORBUseIMR 1 as command-line arguments to the
server. If deploying a TAO server so that it listens on fixed
ports then you must indicate the port number used by the server
by specifying
-ORBEndPoint
<endpoint-details> as command-line arguments
to the server. An example is shown below:
my_server.exe -ORBEndPoint iiop://foo.acme.com:9999
5.3.5 omniORB Server
Deployment
OmniORB does not provide an IMR. Instead, all
servers must be started manually. By default, an omniORB server
listens on a random port. If you want an omniORB server to listen
on a fixed port then you can indicate the port number by
specifying
-ORBendPoint
<endpoint-details> as command-line arguments
to the server. An example is shown below:
my_server.exe -ORBendPoint giop:tcp:foo.acme.com:5000
Alternatively, the omniORB configuration file could contain
an entry like that shown below:
endPoint = giop:tcp:foo.acme.com:5000
5.4 Using Orbix-proprietary
Policies
The
PoaUtility class provides access to the
Orbix-proprietary policies. For example, you can specify one of
the proprietary
OBJECT_DEACTIVATION_POLICY values
(
"deliver",
"discard" or
"hold") in
the list of policies passed as a parameter to
createPoa(). Orbix also provides proprietary APIs for
creating
work queues,
4 which can then be associated with POAs.
The
PoaUtility class provides APIs to access this
functionality. C++ code that illustrates this is presented in
Section
5.4.1, and
corresponding Java code is presented in Section
5.4.2.
5.4.1 C++ Version
The
class declaration below shows how to make use of the
Orbix-proprietary work queue mechanism.
1 #include "PoaUtility.h"
2 using namespace corbautil;
3 class PoaHierarchy : public PoaUtility
4 {
5 public:
6 PoaHierarchy(CORBA::ORB_ptr orb,
7 PoaUtility::DeploymentModel deployModel)
8 throw(PoaUtilityException)
9 //——–
10 // Accessors
11 //——–
12 WorkQueue_ptr manual_wq() { return m_manual.wq(); }
13 ... // other accessors
14
15 private:
16 //——–
17 // Instance variables
18 //——–
19 LabelledOrbixWorkQueue m_auto;
20 LabelledOrbixWorkQueue m_manual;
21 ... // other instance variables
22 };
The following points should be noted:
The work queues are created and associated with POAs in the
body of the constructor, as shown below:
1 #include "PoaHierarchy.h"
2
3 PoaHierarchy::PoaHierarchy(CORBA::ORB_ptr orb
4 PoaUtility::DeploymentModel deployModel)
5 throw(PoaUtilityException)
6 : PoaUtility(orb, deployModel)
7 {
8 //——–
9 // Create the POA Managers
10 //——–
11 ...
12
13 //——–
14 // Create the work queues
15 //——–
16 m_auto = createAutoWorkQueue("auto",
17 1000, 10, 10, 10, 128);
18 m_manual = createManualWorkQueue("manual", 1000);
19
20 //——–
21 // Create the FooFactory POA
22 //——–
23 m_FooFactory = createPoa("FooFactory",
24 root(), m_core_functionality,
25 "user_id + persistent + use_active_object_map_only",
26 m_auto);
27 ...
28 }
The following points should be noted:
- An automatic work queue is created
though the createAutoWorkQueue() operation
(lines 16–17). The first parameter is a
label for the work queue. The remaining parameters to
this operation are
max_size,
initial_thread_count,
high_water_mark, low_water_mark and
thread_stack_size_kb. Most of these parameters
have meanings identical to those of
create_work_queue_with_thread_stack_size() in the
IT_WorkQueue::AutomaticWorkQueueFactory interface. The
only exception is thread_stack_size_kb, which
specifies a stack size in kilobytes (the corresponding
parameter to
create_work_queue_with_thread_stack_size() expresses
the stack size in bytes rather than kilobytes).
- A manual work queue is created through
the createManualWorkQueue() operation (line 18).
The parameters to this operation are a label for the
work queue and max_size.
- The createPoa() operation is
overloaded so it can take an extra parameter to denote a work
queue that should be associated with the POA (line 26). If
you wish, you can associate the same work queue with several
POAs.
5.4.2 Java Version
The
class declaration below shows how to make use of the
Orbix-proprietary work queue mechanism.
1 import org.omg.CORBA.*;
2 import org.omg.PortableServer.*;
3 import com.iona.corba.IT_WorkQueue.*;
4 import com.iona.corbautil.*;
5 class PoaHierarchy
6 {
7 public PoaHierarchy(ORB orb, int deployModel)
8 throws PoaUtilityException
9 {
10 PoaUtilityOrbixImpl util = (PoaUtilityOrbixImpl)
11 PoaUtility.init(orb, deployModel);
12
13 //——–
14 // Create the POA Managers
15 //——–
16 ...
17
18 //——–
19 // Create the work queues
20 //——–
21 m_auto_wq = util.createAutoWorkQueue("auto",
22 1000, 10, 10, 10);
23 m_manual_wq = util.createManualWorkQueue("manual", 1000);
24
25 //——–
26 // Create the "FooFactory" POA
27 //——–
28 m_FooFactory = util.createPoa("FooFactory",
29 util.root(),
30 m_core_functionality,
31 "user_id + transient + "
32 + "use_active_object_map_only",
33 m_auto_wq);
34 ...
35 }
36
37 //——–
38 // Accessors
39 //——–
40 public WorkQueue manual_wq() { return m_manual_wq.wq(); }
41 ...
42
43 //——–
44 // Instance variables
45 //——–
46 private LabelledOrbixWorkQueue m_auto_wq;
47 private LabelledOrbixWorkQueue m_manual_wq;
48 ...
49 }
The following points should be noted:
5.4.3 Configuration Values for Work
Queues
If the
label parameter passed to a work queue
creation operation is an empty string then the other parameter
values are used directly when creating the work queue. However,
if the
label parameter is not an empty string then the
other parameters can be overridden by runtime configuration
entries. If a class has an automatic work queue called
"auto" and a manual work queue called
"manual")
then the corresponding runtime configuration entries can be
expressed as shown below:
auto:max_size = "1000";
auto:initial_thread_count = "10";
auto:high_water_mark = "10";
auto:low_water_mark = "10";
auto:thread_stack_size_kb = "512";
manual:max_size = "1000";
5.5 Porting to Other CORBA
Products
5.5.1 C++ Version
The IDL-to-C++
mapping has one annoying hindrance to portability: it does not
define the names of CORBA-related header files. The practical
effect of this is that a developer must change
#include
directives for CORBA-related header files when porting an
application to a different CORBA product. To alleviate this
problem, the author has developed a collection of simple
“wrapper” header files that (depending on which
#define symbol as been defined)
#include
product-specific header files. This collection of wrapper header
files, which currently supports Orbix, Orbacus, TAO and omniORB,
is documented in Chapter
4
(
Portability of C++ CORBA Applications). The
PoaUtility class uses the portability header files so
that it can
#include CORBA header files in a portable
way. If you wish to port the
PoaUtility class to another
CORBA vendor's product then the first step is to enhance the
portability wrapper header files to support that CORBA vendor's
product. In practice, this should take only a few minutes of
time. Having done that, the
PoaUtility class should then
compile cleanly with the other CORBA vendors' product. However,
it will not (yet) take advantage of the CORBA vendor's
proprietary APIs for, say, getting a POA Manager to listen on a
fixed port number. To add this capability, you will need to
examine the code in
PoaUtility.cxx and add some
#if...#endif directives as required. The history of
software development has shown that
excessive use of
#if...#endif directives can result in software that is
difficult to read and maintain [
SC]. Currently,
use of
#if...#endif directives is quite localized within
the
PoaUtility class. However, if this class is extended
to support many other CORBA products in the future then
proliferation of
#if...#endif directives might prove
troublesome for code readability and maintainability.
5.5.2 Java Version
The Java
approach to producing product-specific implementations of an API
is to define the API as either an
interface or an
abstract class and then use Java's reflection APIs to
dynamically load an appropriate implementation. The canonical
example of this for CORBA programmers is
org.omg.CORBA.ORB, which is an
abstract class.
The static
init() operation on this class uses
reflection to create an instance of the subclass specified by the
org.omg.CORBA.ORBClass system property. The
PoaUtility class uses a similar approach. It has a
static operation called
init(). This operation uses
reflection to create an instance of a class using the following
algorithm:
- If the
com.iona.corbautil.PoaUtilityClass system property
exists then its value specifies the name of the class to be
instantiated.
- Otherwise, the
org.omg.CORBA.ORBClass system property is examined.
- If this property has the value
"com.iona.corba.art.artImpl.ORBImpl" then an
instance of com.iona.corbautil.PoaUtilityOrbixImpl
is created. As its name suggests, this class is an
implementation for use with Orbix. Internally, it uses
Orbix-proprietary APIs that allow POAs to listen on fixed
ports. It also defines additional operations (discussed in
Section 5.4) that provide
access to the Orbix-proprietary work queues.
- If this system property has the
value "com.ooc.CORBA.ORB" then an instance of
com.iona.corbautil.PoaUtilityOrbacusImpl is
created. As its name suggests, this class is an
implementation for use with Orbacus. Internally, it uses
Orbacus-proprietary APIs that allows a POA manager to
listen on a fixed port.
- Otherwise, an instance of
com.iona.corbautil.PoaUtilityPortableImpl is created.
This class uses only CORBA-compliant APIs so it is portable to
other CORBA vendor products, but it does not have the ability
to allow a POA manager to listen on a fixed port.
The above algorithm could be simplified to just steps 1
and 3. However, step 2 provides a better out-of-the-box
experience for Orbix and Orbacus developers because the
developers do not need to set up a system property in order for a
server to have the ability to listen on a fixed port. If you want
to produce a version of
PoaUtility that can take
advantage of the proprietary APIs of another CORBA product then
you should write a class that inherits from
com.iona.corbautil.PoaUtilityPortableImpl and redefines
whatever operations it needs to and/or adds new operations.
- 1
- The policy values string can use any
combination of whitespace, plus signs or commas as separators
between policy names. Also, leading or trailing separators are
ignored. This flexibility was chosen in order to facilitate
developers who wish to use policy value strings that, say, are
obtained from a runtime configuration file or have been
generated by a code-generation tool.
- 2
- The policy values string can use any
combination of whitespace, plus signs or commas as separators
between policy names. Also, leading or trailing separators are
ignored. This flexibility was chosen in order to facilitate
developers who wish to use policy value strings that, say, are
obtained from a runtime configuration file or have been
generated by a code-generation tool.
- 3
- Orbix allows individual POAs controlled by
the same POA Manager to use different ports; but it also allows
POAs controlled by the same POA Manager to share the same port.
The PoaUtility class keeps administration simple by
allowing the choice of port numbers to be chosen at the
relatively coarse granularity of POA Managers rather than at
the finer granularity of individual POAs.
- 4
- The discussion in this section assumes the
reader is already familiar with the concepts of work queues. If
you are not familiar with work queues then you can find details
in the Orbix Programmer's Guide and Programmer's
Reference Guide.