Tuxedo
0

Administering a Tuxedo Application at Run Time

 Previous Next Contents View as PDF  

Joint Client/Servers

This topic includes the following sections:

This chapter describes programming requirements for CORBA joint client/servers and the C++ BEAWrapper Callbacks API. For a description of the Java BEAWrapper package and the Java Callbacks interface API, see the Javadoc API.

 


Introduction

For either a BEA Tuxedo CORBA client or joint client/server (that is, a client that can receive and process object invocations), the programmer writes the client main(). The main() uses BEA Tuxedo CORBA environmental objects to establish connections, set up security, and start transactions.

BEA Tuxedo clients invoke operations on objects. In the case of DII, client code creates the DII Request object and then invokes one of two operations on the DII Request. In the case of static invocation, client code performs the invocation by performing what looks like an ordinary invocation (which ends up calling code in the generated client stub). Additionally, the client programmer uses ORB interfaces defined by OMG, and BEA Tuxedo CORBA environmental objects that are supplied with the BEA Tuxedo software, to perform functions unique to BEA Tuxedo.

For BEA Tuxedo joint client/server applications, the client code must be structured so that it can act as a server for callback BEA Tuxedo objects. Such clients do not use the TP Framework and are not subject to BEA Tuxedo system administration. Besides the programming implications, this means that CORBA joint client/servers do not have the same scalability and reliability as BEA Tuxedo CORBA servers, nor do they have the state management and transaction behavior available in the TP Framework. If a user wants to have those characteristics, the application must be structured in such a way that the object implementations are in a BEA Tuxedo CORBA server, rather than in a client.

The following sections describe the mechanisms you use to add callback support to a BEA Tuxedo client. In some cases, the mechanisms are contrasted with the BEA Tuxedo server mechanisms that use the TP Framework.

Main Program and Server Initialization

In a BEA Tuxedo server, you use the buildobjserver command to create the main program for the C++ server. (Java servers are not supported in release 8.0 and later of BEA Tuxedo.) Server main program takes care of all BEA Tuxedo- and CORBA-related initialization of the server functions. However, since you implement the Server object, you have an opportunity to customize the way in which the server application is initialized and shut down. The server main program automatically invokes methods on the Server object at the appropriate times.

In contrast, for a BEA Tuxedo CORBA joint client/server (as for a BEA Tuxedo CORBA client), you create the main program and are responsible for all initialization. You do not need to provide a Server object because you have complete control over the main program and you can provide initialization and shutdown code in any way that is convenient.

The specific initialization needed for a joint client/server is discussed in the section Servants.

Servants

Servants (method code) for joint client/servers are very similar to servants for servers. All business logic is written the same way. The differences result from not using the TP Framework. Therefore, the main difference is that you use CORBA functions directly instead of indirectly through the TP Framework.

The Server interface is used in BEA Tuxedo CORBA servers to allow the TP Framework to ask the user to create a servant for an object when the ORB receives a request for that object. However, in joint client/servers, the user program is responsible for creating a servant before any requests arrive; thus, the Server interface is not needed. Typically, the program creates a servant and then activates the object (using the servant and an ObjectId; the ObjectId is possibly system generated) before handing a reference to the object. Such an object might be used to handle callbacks. Thus, the servant already exists and the object is activated before a request for the object arrives.

Joint client/servers work a little differently depending on whether the C++ client ORB or the Java client ORB is used.

Servant Inheritance from Skeletons

In a client that supports callbacks, as well as in a server, you write a implementation class that inherits from the same skeleton class name generated by the IDL compiler (the idl command).

C++ Example of Inheritance from Skeletons

The following is a C++ example, given the IDL:

interface Hospital{  };

The skeleton generated by the idl command contains a "skeleton" class, POA_Hospital, that the user-written class inherits from, as in:

class Hospital_i : public POA_Hospital { ... };

In a server, the skeleton class inherits from the TP Framework class Tobj_ServantBase, which in turn inherits from the predefined PortableServer::ServantBase.

The inheritance tree for a callback object implementation in a joint client/server is different than that in a server. The skeleton class does not inherit from the TP Framework class Tobj_ServantBase, but instead inherits directly from PortableServer::ServantBase. This behavior is achieved by specifying the -P option in the idl command.

Not having the Tobj_ServantBase class in the inheritance tree for a servant means that the servant does not have activate_object and deactivate_object methods. In a server, these methods are called by the TP Framework to dynamically initialize and save a servant's state before invoking a method on the servant. For a client that supports callbacks, you must write code that explicitly creates a servant and initializes a servant's state.

Java Example of Inheritance from Skeletons

The following is a Java example, given the IDL:

interface Hospital{  };

The skeleton generated by idltojava contains a skeleton class, _HospitalImplBase, from which the user-written class inherits, as in:

class HospitalImpl extends _HospitalImplBase {};

In a BEA Tuxedo server application, the skeleton class inherits from the TP Framework class com.beasys.Tobj_Servant, which in turn inherits from the CORBA-defined class org.omg.PortableServer.Servant.

The inheritance tree for a callback object implementation in a joint client/server application is different from that of a client. The skeleton class does not inherit from the TP Framework class, but instead inherits from the org.omg.CORBA.DynamicImplementation class, which in turn inherits from the org.omg.CORBA.portable.ObjectImpl class.

Not having the Tobj_Servant class in the inheritance tree for a servant means that the servant does not have the activate_object and deactivate_object methods. In a BEA Tuxedo server application, these methods are invoked by the TP Framework to dynamically initialize and save a servant's state before invoking a method on the servant. For a joint client/server application, user code must explicitly create a servant and initialize a servant's state; therefore, the Tobj_Servant operations are not needed.

Callback Object Models Supported

BEA Tuxedo CORBA supports four kinds of callback objects and provides wrappers for the three that are most common. These objects correspond to three combinations of POA policies. The POA policies control both the types of objects and the types of object references that are possible.

The POA policies that are applicable are:

These objects are explained primarily in terms of their behavioral characteristics rather than in details about how the ORB and the POA handle them. Those details are discussed in the next sections, using either direct ORB and POA calls (which requires a little extra knowledge of CORBA servers) or using the BEAWrapper Callbacks interface, which hides the ORB and POA calls (for users who do not care about the details).

Note: The Transient/UserId policy combination is not considered particularly important. It is possible for users to provide for themselves by using the POA in a manner analogous to either of the persistent cases, but the BEA Tuxedo wrappers do not provide special help to do so.

Note: For BEA Tuxedo CORBA native joint client/servers, neither of the Persistent policies is supported, only the Transient policy.

Configuring Servers to Call Remote Joint Client/Server Objects

In order for a BEA Tuxedo server to call remote joint client/server objects, that is, joint client/server objects located outside the BEA Tuxedo domain, the server must be configured to enable outbound IIOP. This capability is enabled by specifying the -O (uppercase letter O) option in the IIOP Server Listener (ISL) server command. Setting the -O option enables outbound invokes (outbound IIOP) on joint client/server objects that are not connected to an IIOP Listener Handler (ISH).

You set ISL command options in the SERVERS section of the server's UBBCONFIG file. Because support for outbound IIOP requires a small amount of extra resources, the default is outbound IIOP disabled. For more information, see "Using the ISL Command to Configure Outbound IIOP" in Setting Up a BEA Tuxedo Application and "ISL(1)" in the BEA Tuxedo Command Reference.

Preparing Callback Objects Using CORBA (C++ Joint Client/Servers Only)

To set up BEA Tuxedo C++ callback objects using CORBA, the client must do the following:

  1. Establish a connection with a POA with the appropriate policies for the callback object model. (This can be the root POA, available by default, or it may require creating a new POA.)

  2. Create a servant (that is, an instance of the C++ implementation class for the interface).

  3. Inform the POA that the servant is ready to accept requests on the callback object. Technically, this means the client activates the object in the POA (that is, puts the servant and the ObjectId into the POA's Active Object Map).

  4. Tell the POA to start accepting requests from the network (that is, activate the POA itself).

  5. Create an object reference for the callback object.

  6. Give out the object reference. This usually happens by making an invocation on another object with the callback object reference as a parameter (that is, the parameter is a callback object). That other object will then invoke the callback object (perform a callback invocation) at some later time.

Assuming that the client already has obtained a reference to the ORB, performing this task takes four interactions with the ORB and the POA. It might look like the model show in Listing  11-1. In this model, only the Root POA is needed.

Listing 11-1 Transient/SystemId Model

// Create a servant for the callback Object 
Catcher_i* my_catcher_i = new Catcher_i();
// Get root POA reference and activate the POA
1 CORBA::Object_var oref =
orb->resolve_initial_references("RootPOA");
2 PortableServer::POA_var root_poa =
PortableServer::POA::_narrow(oref);
3 root_poa -> the_POAManager() -> activate();
4 PortableServer::objectId_var temp_Oid =
root_poa ->activate_object ( my_catcher_i );
5 oref = root_poa->create_reference_with_id(
temp_Oid, _tc_Catcher->id() );
6 Catcher_var my_catcher_ref = Catcher::_narrow( oref );

To use the Persistent/UserId model, there are some additional steps required when creating a POA. Further, the ObjectId is specified by the client, and this requires more steps. It might look like the model shown in Listing  11-2.

Listing 11-2 Persistent/UserId Model

    Catcher_i* my_catcher_i = new Catcher_i();
const char* oid_str = "783";
1 PortableServer::objectId_var oid =
PortableServer::string_to_objectId(oid_str);
// Find root POA
2 CORBA::Object_var oref =
orb->resolve_initial_references("RootPOA");
3 PortableServer::POA_var root_poa =
PortableServer::POA::_narrow(oref);
// Create and activate a Persistent/UserId POA
4 CORBA::PolicyList policies(2);
5 policies.length(2);
6 policies[0] = root_poa->create_lifespan_policy(
PortableServer::PERSISTENT);
7 policies[1] = root_poa->create_id_assignment_policy(
PortableServer::USER_ID );
8 PortableServer::POA_var my_poa_ref =
root_poa->create_POA(
"my_poa_ref", root_poa->the_POAManager(), policies);
9 root_poa->the_POAmanager()->activate();
// Create object reference for callback Object
10 oref = my_poa_ref -> create_reference_with_id(
oid, _tc_Catcher->id());
11 Catcher_var my_catcher_ref = Catcher::_narrow( oref );
// activate object
12 my_poa_ref -> activate_object_with_id( oid, my_catcher_i );
// Make the call passing the callback ref
foo -> register_callback ( my_catcher_ref );

All the interfaces and operations described here are standard CORBA interfaces and operations.

Preparing Callback Objects Using BEAWrapper Callbacks

You can use the BEAWrapper callbacks API with to write either C++ or Java joint client/servers.

Using BEAWrapper Callbacks With C++

Because the code required for callback objects is nearly identical for every client that supports callbacks, you may find it convenient to use the BEAWrappers provided in the library provided for joint client/servers.

The BEAWrappers are described in IDL, as shown in Listing  11-3.

Listing 11-3 BEAWrapper IDL

// File: BEAWrapper
#ifndef _BEA_WRAPPER _IDL_
#define _BEA_WRAPPER _IDL_
#include <orb.idl>
#include <PortableServer.idll>
#pragma prefix "beasys.com"
module BEAWrapper {
interface Callbacks
{
exception ServantAlreadyActive{ };
exception ObjectAlreadyActive { };
exception NotInRequest{ };

// set up transient callback Object
// -- prepare POA, activate object, return objref
Object start_transient(
in PortableServer::Servant Servant,
in CORBA::RepositoryId rep_id)
raises (ServantAlreadyActive);
       // set up persistent/systemid callback Object 
Object start_persistent_systemid(
in PortableServer::Servant servant,
in CORBA::Repository rep_id,
out string stroid)
raises (ServantAlreadyActive);
       // reinstate set up for persistent/systemid 
// callback object
Object restart_persistent_systemid(
in PortableServer::Servant servant,
in CORBA::RepositoryId rep_id,
in string stroid)
raises (ServantAlreadyActive, ObjectAlreadyActive);
       // set up persistent/userid callback Object
Object start_persistent_userid(
in PortableServer::Servant servant,
in CORBA::RepositoryId rep_id,
in string stroid)
raises (ServantAlreadyActive, ObjectAlreadyActive);
       // stop servicing a particular callback Object
// with the given servant
void stop_object( in PortableServer::Servant servant);
       //Stop all callback Object processing
void stop_all_objects();
      // get oid string for the current request
string get_string_oid() raises (NotInRequest)
;
};
}
#endif /* _BEA_WRAPPER _IDL_ */

The BEAwrappers are described in C++ as shown in Listing  11-4.

Listing 11-4 C++ Declarations (in beawrapper.h)

#ifndef _BEAWRAPPER_H_
#define _BEAWRAPPER_H_
#include <PortableServer.h>
class BEAWrapper{
class Callbacks{
public:
Callbacks (CORBA::ORB_ptr init_orb);
            CORBA::Object_ptr start_transient ( 
PortableServer::Servant servant,
const char * rep_id);
            CORBA::Object_ptr start_persistent_systemid (
PortableServer::Servant servant,
const char * rep_id,
char * & stroid);
            CORBA::Object_ptr restart_persistent_systemid (
PortableServer::Servant servant,
const char * rep_id,
const char * stroid);
            CORBA::Object_ptr start_persistent_userid (
PortableServer::Servant servant,
const char * rep_id,
const char * stroid);
              void stop_object(PortableServer::Servant servant);
              char* get_string_oid ();
              void stop_all_objects();
             ~Callbacks();
private:
              static CORBA::ORB_var orb_ptr;
              static PortableServer::POA_var root_poa;
static PortableServer::POA_var trasys_poa;
static PortableServer::POA_var persys_poa;
static PortableServer::POA_var peruser_poa;
};
};
#endif // _BEAWRAPPER_H_

The description of each operation in the BEAWrapper::Callbacks interface follows, in the order declared above.

Using BEAWrapper Callbacks With Java

Because the code to prepare for callback objects is nearly identical for every joint client/server application, and because the Java JDK ORB does not implement a POA, BEA Tuxedo provides a wrapper class in the joint client/server library that is virtually identical to the wrapper class provided in C++. This wrapper class emulates the POA policies needed to support the three types of callback objects.

Listing  11-5 shows the Java Callback wrapper interfaces.

Listing 11-5 Java Callback Wrapper Interfaces

package com.beasys.BEAWrapper;

class Callbacks{
public Callbacks ();

public Callbacks (org.omg.CORBA.Object init_orb);

public org.omg.CORBA.Object start_transient (
org.omg.PortableServer.ObjectImpl servant,
java.lang.String rep_id)
throws ServantAlreadyActive,
org.omg.CORBA.BAD_PARAMETER;

public org.omg.CORBA.Object start_persistent_systemid (
org.omg.PortableServer.ObjectImpl servant,
java.lang.String rep_id,
org.omg.CORBA.StringHolder stroid)
throws ServantAlreadyActive,
org.omg.CORBA.BAD_PARAMETER,
org.omg.CORBA.IMP_LIMIT;

public org.omg.CORBA.Object restart_persistent_systemid (
org.omg.PortableServer.ObjectImpl servant,
java.lang.String rep_id,
java.lang.String stroid)
throws ServantAlreadyActive,
ObjectAlreadyActive,
org.omg.CORBA.BAD_PARAMETER,
org.omg.CORBA.IMP_LIMIT;

public org.omg.CORBA.Object start_persistent_userid (
org.omg.PortableServer.ObjectImpl servant,
java.lang.String rep_id,
java.lang.String stroid)
throws ServantAlreadyActive,
ObjectAlreadyActive,
org.omg.CORBA.BAD_PARAMETER,
org.omg.CORBA.IMP_LIMIT;
public void stop_object(
org.omg.PortableServer.ObjectImpl
servant);

public String get_string_oid ()
throws NotInRequest;

public void stop_all_objects();
};

Java Joint Client/Server Programming Considerations

This section discusses the following Java programming topics:

Threading Considerations in the Main Program

When a program acts as both a client and a server in a Java client, as in a Java joint client/server, those two parts can execute concurrently in different threads. Since Java, as an execution environment, is inherently multithreaded, there is no reason to invoke the org.omg.CORBA.orb.work_pending and org.omg.CORBA.orb.perform_work methods from a Java client. In fact, if the Java client tries to invoke these methods, these methods throw an org.omg.CORBA.NO_IMPLEMENT exception. The client does not need to invoke the org.omg.CORBA.orb.run method. As in any multithreaded environment, any code that may execute concurrently (client and servant code for a callback) in the client application must be coded to be thread safe.

How Multiple Threads Work

In Java, the client starts up in the main thread. The client can then set up callback objects via an invocation to any of the (re)start_xxxx methods provided by the Callbacks wrapper class. The wrapper class handles registering the servant and its associated OID in the ORB's object manager. The application is then free to pass the object reference returned by the (re)start_xxxx method to an application that needs to call back to the servant.

Note: The ORB requires an explicit invocation to one of the (re)start_xxxx methods to effectively initialize the servant and create a valid object reference that can be marshaled properly to another application. This is a deviation from the base JDK 1.2 ORB behavior that allows implicit object reference creation via an internal invocation to the orb.connect method when marshaling an object reference, if the application has not yet done so.

Invocations on the callback object are handled by the ORB. As each request is received, the ORB validates the request against the object manager and spawns a thread for that request. Multiple requests can be made simultaneously to the same object because the ORB creates a new thread for each request; that is why the Servant code of the Callback must be written thread safe. As each request terminates, the thread that runs the servant also terminates.

The main client thread can make as many client invocations as necessary. An invocation to the stop_(all_)object methods merely takes the object out of the object manager's list, thereby preventing any further invocations on it. Any invocation to a stopped object fails as if it never existed.

If the client application needs to retrieve the results of a callback from another thread, the client application must use normal thread synchronization techniques to do so.

If any thread (client main or servant) in the BEA Tuxedo remote-like client application exits, all the client process activity is stopped, and the Java execution environment terminates. We recommend only to invoke the return method to terminate a thread.

Java Client ORB Initialization

A client application must initialize the ORB with the BEA-supplied properties. This is so that the ORB will utilize the BEA-supplied classes and methods that support the Callbacks wrapper class and the Bootstrap object. You can find these classes in wleclient.jar, which is installed in $TUXDIR/udataobj/java/jdk (on Solaris) or %TUXDIR%\udataobj\java\jdk (on Windows). The application must set certain system properties to do this, as shown in Listing  11-6.

Listing 11-6 System Properties Settings

Properties prop = new Properties(System.getProperties());
prop.put("org.omg.CORBA.ORBClass","com.beasys.CORBA.iiop.ORB");
prop.put("org.omg.CORBA.ORBSingletonClass",
"com.beasys.CORBA.idl.ORBSingleton");
System.setProperties(prop);
// Initialize the ORB.
ORB orb = ORB.init(args, prop);

IIOP Support

IIOP is the protocol used for communication between ORBs. IIOP allows ORBs from different vendors to interoperate. For Java server applications, a port number must be supplied at the client for persistent or user ID object reference policies.

Java Applet Support

IIOP support for applets that want to receive callbacks or callouts is limited due to applet security mechanisms. Any applet run-time environment that allows an applet to create and listen on sockets (via their proprietary environment or protocol) will be able to act as BEA Tuxedo joint client/server applications. If the applet run-time environment restricts socket communication, then the applet cannot be a joint client/server application to a BEA Tuxedo application.

Port Numbers for Persistent Object References

For a BEA Tuxedo Java remote joint client/server application to support IIOP, the object references created for the server component must contain a host and a port. For transient object references, any port is sufficient and can be obtained by the ORB dynamically; however, this is not sufficient for persistent object references.

Persistent references must be served on the same port after the ORB restarts. That is, the ORB must be prepared to accept requests on the same port with which it created the object reference. Therefore, there must be some way to configure the ORB to use a particular port.

Java clients that expect to act as servers for callbacks of persistent references must now be started with a specified port. This is done by setting the system property org.omg.CORBA.ORBPort, as in the following commands:

Windows:

java -DTOBJADDR=//host:port
-Dorg.omg.CORBA.ORBPort=xxxx
-classpath=%CLASSPATH% client

UNIX:

java -DTOBJADDR=//host:port 
-Dorg.omg.CORBA.ORBPort=xxxx
-classpath=$CLASSPATH client

Typically, a system administrator assigns the port number for the client from the user range of port numbers, rather from the dynamic range. This keeps the joint client/server applications from using conflicting ports.

If a BEA Tuxedo remote joint client/server application tries to create a persistent object reference without having set a port (as in the preceding command line), the operation raises an exception, IMP_LIMIT, informing the user that a truly persistent object reference cannot be created.

 


C++ BEAWrapper Callbacks Interface API

This C++ BEAWrapper Callbacks interface API is described in the following sections.

 

Back to Top Previous Next
Contact e-docsContact BEAwebmasterprivacy