


Chapter 5 Creation of POA Hierarchies Made Simple
- Introduction
- Building A POA Hierarchy
- Server Deployment Models
- Using Orbix-proprietary Policies
- Porting to Other CORBA Products
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:
-
The PoaUtility.h file (line 1) defines several types in
the corbautil namespace (line 2).
The PoaHierarchy class inherits from the PoaUtility class (line 3).
- The constructor of the class (line 6) takes two parameters: a reference to an ORB and an enum value that specifies the server’s deployment model. The issue of server deployment models will be discussed in Section 5.3. If anything goes wrong in the constructor then it throws a corbautil::PoaUtilityException.
- The created POAs and POA Managers are stored in instance
variables (lines 24 to 28). The variable for a POA Manager is
of type PoaUtility::LabelledPOAManager,
which is a class that has two accessor operations:
const char * label(); PortableServer::POAManager_ptr mgr();
- The PoaHierarchy class defines accessor functions (lines 12 to 18) for the corresponding instance variables (lines 24 to 28).
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:
- The PoaUtility class is abstract. Its static init() operation (line 9) uses Java reflection APIs to create an instance of a concrete sub-class that is suitable for use with a specific CORBA product. Further details of this are provided in Section 5.5.2.
- Because of the abstract nature of PoaUtility, the PoaHierarchy class does not inherit from PoaUtility. Instead, PoaHierarchy declares a variable of type PoaUtility (line 9) and initializes it by calling PoaUtility.init(). There are two parameters to init(): a reference to an ORB and an int that specifies the server’s deployment model. The issue of server deployment models will be discussed in Section 5.3.
- The operations and constructor of the PoaUtility class throw an exception of type PoaUtilityException, which contains a descriptive message. Because of this, the constructor of the PoaHierarchy class also lists PoaUtilityException in its throws clause (line 7).
- The created POAs and POA Managers are stored in instance
variables (lines 49–53). The variable for a POA Manager is
of type LabelledPOAManager, which has two public
operations:
public String label(); public POAManager mgr();
- The PoaHierarchy class defines accessor functions (lines 38–44) that provide read-only access to the corresponding instance variables.
- A POA manager is created by calling the createPoaManager() operation (lines 10–13), 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 createPoa() operation (lines 15–32). 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 operation root() can be used to specify the root POA (lines 16 and 28). 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 + ...".2
- 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 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.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 created work queues are stored in instance variables
(lines 19–20). The variable for a work queue is of type
corbautil::LabelledOrbixWorkQueue, which is
a class that has two public operations:
const char * label(); IT_WorkQueue::WorkQueue_ptr wq();
- An accessor for a work queue can be defined as shown in line 12.
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
andthread_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 isthread_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:
-
To access the Orbix-proprietary functionality, you must
typecast the value returned from PoaUtility.init(orb) to
type PoaUtilityOrbixImpl (lines 10–11).
The created work queues are stored in instance variables (lines 46–47). The variable for a work queue is of type LabelledOrbixWorkQueue, which has two public operations:
public String label(); public WorkQueue wq();
- An accessor for a work queue can be defined as shown in line 40.
- You create an automatic work queue by calling
createAutoWorkQueue() (lines 21–22). The first
parameter to this operation is a label for the work
queue. The remaining parameters are
max_size
,initial_thread_count
,high_water_mark
andlow_water_mark
. These parameters are similar to those of create_work_queue() in IT_WorkQueue::AutomaticWorkQueueFactory. - A manual work queue is created through the create_manual_work_queue() operation (line 23). 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 33). If you wish, you can associate the same work queue with several POAs.
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.


