![]() ![]() ![]() ![]() ![]() ![]() ![]() |
This topic includes the following sections:
Table 2-1 outlines the development process for C++ joint client/server applications.
These steps are explained in detail in subsequent topics.
Because the callback object in a joint client/server application is not transactional and has no object management capabilities, you do not need to create an Implementation Configuration File (filename
.icf
) for it. However, you still need to create an ICF file for the BEA Tuxedo objects in your BEA Tuxedo application. For information about writing an ICF file, see Creating CORBA Server Applications.
Throughout this topic, the Chat Room sample application is used to demonstrate the development steps. A chat room is an application that allows several people at different locations to communicate with each other. Think of the chat room as a moderator whose job it is to keep track of client applications that have logged in, and to distribute messages to those client applications.
A client application logs in to the moderator, supplying a username. When messages are entered at the keyboard, the client application invokes the moderator, and passes the messages to the moderator. The moderator then distributes the messages to all the other client applications by making an invocation on the callback object.
The Chat Room sample application consists of a C++ joint client/server application and a BEA Tuxedo server application. The joint client/server application receives keyboard input and makes invocations on the moderator. The joint client/server application also sets up the callback object to listen for messages from the moderator (that is, to receive invocations from the moderator). The BEA Tuxedo server application in the Chat Room sample application implements the moderator.
Figure 2-1 illustrates how the Chat Room sample application works.
The Chat Room sample application works as follows:
signon
operation.send
operation.post
invocations from the Moderator object.
The source files for the Chat Room sample application are located in the TUXDIR\
samples\corba\chatroom
directory in the BEA Tuxedo software directory. See "Building and Running the Chat Room Sample Application" on page -23 for more information.
You use Object Management Group (OMG) Interface Definition Language (IDL) to describe available CORBA interfaces to client applications. An interface definition written in OMG IDL completely defines the CORBA interface and fully specifies each operation's arguments. OMG IDL is a purely declarative language. This means that it contains no implementation details. For more information about OMG IDL, see Creating CORBA Client Applications.
The Chat Room sample application implements the CORBA interfaces listed in Table 2-2.
Listing 2-1 shows the chatclient.idl
that defines the Listener
interface.
module ChatClient{
interface Listener {
oneway void post (in string from,
in string output_line);
};
};
Listing 2-2 shows the chatroom.idl
that defines the Moderator
and ModeratorFactory
interfaces for the Chat Room sample application. The #include
is used to resolve references to interfaces in another OMG IDL file. In the Chat Room sample application, the signon
method requires a Listener object as a parameter and its IDL file must #include
the OMG IDL file that defines the Listener interface.
#include "ChatClient.idl"
module ChatRoom {
interface Moderator {
exception IdAlreadyUsed{};
exception NoRoomLeft{};
exception IdNotKnown{};
void signon( in string who,
in ChatClient::Listener callback_ref )
raises( IdAlreadyUsed, NoRoomLeft );
void send (in string who,
in string input_line )
raises( IdNotKnown );
void signoff(in string who )
raises( IdNotKnown );
};
interface ModeratorFactory {
Moderator get_moderator( in string chatroom_name );
};
};
The interface specification defined in OMG IDL is used by the IDL compiler to generate skeletons and client stubs. Note that a joint client/sever application uses the client stub for the BEA Tuxedo object and the skeleton and client stub for the callback object.
For example, in the Chat Room sample application, the joint client/server application uses the skeleton and client stub for the Listener object (that is, the callback object) to implement the object. The joint client/server application also uses the client stubs for the Moderator
and ModeratorFactory
interfaces to invoke operations on the objects. The BEA Tuxedo server application uses the skeletons for the Moderator and ModeratorFactory objects to implement the objects and the client stub for the Listener object to invoke operations on the object.
During the development process, use the idl
command with the -P
and -i
options to compile the OMG IDL file that defines the callback object (for example, the chatclient.idl
file in the Chat Room sample application). The options work as follows:
-P
option creates a skeleton class that inherits directly from the PortableServer::ServantBase
class. Inheriting from PortableServer::ServantBase
means the joint client/server application must explicitly create a servant for the callback object and initialize the servant's state. The servant for the callback object cannot use the activate_object
and deactivate_object
methods as they are members of the PortableServer::ServantBase
class.-i
option results in the generation of an implementation template file. This file is a template for the code that implements the interfaces defined in the OMG IDL for the Listener object.
You then need to compile the OMG IDL file that defines the interfaces in the BEA Tuxedo server application (for example, the chatroom.idl
file in the Chat Room sample application). Use the idl
command with only the -i
option to compile that OMG IDL file.
Table lists the files that are created by the idl
command.
Note: | In the Chat Room sample application, the generated template files for the ChatClient.idl and ChatRoom.idl files have been renamed to reflect the objects (Listener and Moderator) they implement. In addition, the template file for the Moderator object includes the implementation for the ModeratorFactory object. |
After you compile each of the OMG IDL files, you need to write methods that implement the operations for each object. In a joint client/server application, you write the implementation file for the callback object (that is, the Listener object). You write the implementation for a callback object as you would write the implementation for any other CORBA object, except that you use the POA instead of the TP Framework. You also write implementation files for the BEA Tuxedo objects (that is, the Moderator and ModeratorFactory objects) in the BEA Tuxedo server application.
An implementation file contains the following:
com.beasys.Tobj_Servant.activate_object
and com.beasys.Tobj_Servant.deactivate_object
methods
Within the activate_object
and deactivate_object
methods, you write code that performs any particular steps related to activating or deactivating an object.
Listing 2-3 includes the implemention file for the Listener object, and Listing 2-4 includes the implementation file for the Moderator and ModeratorFactory objects.
Note: | Additional methods and data were added to the implementation file for the Moderator and ModeratorFactory objects. The template for the implementation file was created by the idl -i command. |
// This module contains the definition of the implementation class
// Listener_i
#ifndef _Listener_i_h
#define _Listener_i_h
#include "ChatClient_s.h"
class Listener_i : public POA_ChatClient::Listener {
public:
Listener_i ();
virtual ~Listener_i();
void post (
const char * from,
const char * output_line);
...
};
#endif
// This module contains the definitions of the implementation class
// Moderator and ModeratorFactory
#ifndef _Moderator_i_h
#define _Moderator_i_h
#include "ChatRoom_s.h"
const int CHATTER_LIMIT = 5;
// the most chatters allowed
class Moderator_i : public POA_ChatRoom::Moderator {
public:
//Define the operations
void signon ( const char* who,
ChatClient::Listener_ptr callback_ref);
void send ( const char * who,
const char * input_line);
void signoff ( const char * who);
//Define the Framework functions
virtual void activate_object ( const char* stroid );
virtual void deactivate_object( const char* stroid,
TobjS::DeactivateReasonValue
reason);
private:
// Define function to find name on list
int find( const char * handle );
// Define name of the chat room overseen by the Moderator
char* m_chatroom_name;
// Data for maintaining list
// Chatter[n] id
CORBA::String chatters[CHATTER_LIMIT];
// Chatter[n] callback ref
ChatClient::Listener_var callbacks[CHATTER_LIMIT];
};
class ModeratorFactory_i : public POA_ChatRoom::ModeratorFactory {
public:
ChatRoom::Moderator_ptr get_moderator ( const char*
chatroom_name );
};
#endif
During development of a joint client/server application, you write the client portion of the joint client/server application as you would write any BEA Tuxedo client application. The client application needs to include code that does the following:
Note: | Release 8.0 of the CORBA environment of the BEA Tuxedo product continues to include the BEA client environmental objects provided in earlier releases of BEA WebLogic Enterprise for use with the BEA Tuxedo 8.0 CORBA clients. BEA Tuxedo 8.0 clients should use these environmental objects to resolve initial references to bootstrapping, security, and transaction objects. In this release, support has been added for using the OMG Interoperable Naming Service (INS) to resolve initial references to bootstrapping, security, and transaction objects. For information on INS, see Chapter 4, "CORBA Bootstrapping Programming Reference" in the CORBA Programming Reference. |
The client development steps are illustrated in Listing 2-5, which includes code from the Chat Room sample application. In the Chat Room sample application, the client portion of the joint client/server application uses a factory to get an object reference to the Moderator object, and then invokes the signon
, send
, and signoff
methods on the Moderator object.
...
// Initialize the ORB
orb_ptr = CORBA::ORB_init(argc, argv, "BEA_IIOP");
// Create a Bootstrap object to establish communication with the
// domain
bootstrap = new Tobj_Bootstrap(orb_ptr,"");
// Get a FactoryFinder object, use it to find a Moderator factory,
// and get a Moderator.
// Use the Bootstrap object to find the FactoryFinder object
CORBA::Object_var var_factory_finder_oref =
bootstrap->resolve_initial_references("FactoryFinder");
// Narrow the FactoryFinder object
Tobj::FactoryFinder_var var_factory_finder =
Tobj::FactoryFinder::_narrow(var_factory_finder_oref.in());
// Use the FactoryFinder object to find a factory for the Moderator
CORBA::Object_var var_moderator_factory_oref =
var_factory_finder->find_one_factory_by_id(
"ModeratorFactory" );
// Narrow the Moderator Factory
ChatRoom::ModeratorFactory_var var_moderator_factory =
ChatRoom::ModeratorFactory::_narrow(
var_moderator_factory_oref.in() );
// Get a Moderator
// The chatroom name is passed as a command line parameter
var_moderator_oref =
var_moderator_factory->get_moderator
(var_chat_room_name.in() );
...
Since the basic steps for creating a callback object are always the same, the BEA Tuxedo product provides a Callbacks Wrapper object that simplifies the development of callback objects.
The Callbacks Wrapper object does the following:
(_transient)
(_persistent/systemid)
(_persistent/userid)
For a complete description of the object policies for callback objects, see "Object Policies for Callback Objects" on page -5.
For a complete description of the Callbacks Wrapper object and its methods, see the CORBA Programming Reference.
Listing 2-6 shows how a Callbacks Wrapper object is used in the Chat Room sample application.
...
// Use the Callbacks object to create a servant for the
// Listener object, activate the Listener object, and create an
// object reference for the Listener object.
BEAWrapper::Callbacks* callbacks =
new BEAWrapper::Callbacks( orb_ptr );
Listener_i * listener_callback_servant = new Listener_i();
CORBA::Object_var v_listener_oref=callbacks->start_transient(
listener_callback_servant,
ChatClient::_tc_Listener->id());
ChatClient::Listener_var v_listener_callback_oref =
ChatClient::Listener::_narrow(
var_listener_oref.in());
...
Once you have an object reference to a callback object, you can pass the callback object reference as a parameter to a method of a BEA Tuxedo object. In the Chat Room sample application, the Moderator object uses an object reference to the Listener object as a parameter to the signon
method. Listing 2-7 illustrates this step.
// Sign on to the Chat room using a user-defined handle and a
// reference to the Listener object (the callback object) to receive
// input from other client applications logged into the Chat room.
var_moderator_reference->signon(handle,
var_listener_callback_oref.in() );
When running remote joint client/server applications that use IIOP, the object references for the callback object must contain a host and port number, as follows.
The user specifies the port number from the user range of port numbers, rather than from the dynamic range. Assigning port numbers from the user range prevents joint client/server applications from using conflicting ports. To specify a particular port for the joint client/server application to use, include the following on the command line that starts the process for the joint client/server application:
where nnn
is the number of the port to be used by the ORB when creating invocations and listening for invocations on the callback object in the joint client/server application.
Use this command when you want the object reference for the callback object in a joint client/server application to be persistent and when you want to stop and restart the joint client/server application. If this command is not used, the ORB uses a random port. If the joint client/server application is stopped and then started, invocations to callback objects in the the joint client/server application will fail.
The port number is part of the input to the argv
argument of the CORBA::orb_init
member function. When the argv
argument is passed, the ORB reads that information, establishing the port for any object references created in that process. You can also use the bootstrap object's register_callback_port
operation for the same purpose.
For a joint client/server application to communicate with a BEA Tuxedo object in the same domain, a configuration file for the server application is needed. The configuration file should be written as part of the development of the server application. The binary version of the configuration file, the TUXCONFIG
file, must exist before the joint client/server application is started. The TUXCONFIG
file is created using the tmloadcf
command. For information about creating a TUXCONFIG
file, see Getting Started with BEA Tuxedo CORBA Applications and Setting Up a BEA Tuxedo Application.
If you are using a joint client/server application that uses IIOP version 1.0 or 1.1, the administrator needs to boot the IIOP Server Listener (ISL) with startup parameters that enable outbound IIOP to invoke callback objects not connected to an IIOP Server Handler (ISH). The -O
option of the ISL command enables outbound IIOP. Additional parameters allow administrators to obtain the optimum configuration for their BEA Tuxedo application. For more information about the ISL command, see the BEA Tuxedo Command Reference.
The final step in the development of a joint client/server application is to produce the executable program. To do this, you need to compile the code and link against the skeleton and client stub.
Use the buildobjclient
command with the -P
option to construct a joint client/server application executable. To build an executable program, the command combines the client stub for the BEA Tuxedo object, the client stub for the callback object, the skeleton for the callback object, and the implementation for the callback object with the appropriate POA libraries.
Note: | Before you can use the -P option of the buildobjclient command, you must have used the -P option of the idl command when you created the skeleton and client stub for the callback object. |
You can use the POA directly to create a callback object. You would use the POA directly when you want to use POA features and object policies not available through the Callbacks Wrapper object. For example, if you want to use the POA optimization features, you need to use the POA directly. The following topics describe how to use the POA to create callback objects with the supported object policies.
Note: | Only a subset of the POA interfaces are supported in this version of the BEA Tuxedo product. For a list of supported interfaces, see the CORBA Programming Reference. |
To use the POA to create a callback object with a transient object policy, you need to write code that does the following:
Since the root POA does not allow use of bidirectional IIOP, you need to create a child POA. The child POA can use the defaults for LifespanPolicy
(TRANSIENT)
and IDAssignmentPolicy
(SYSTEM
). You must specify a BiDirPolicy
policy of BOTH
.
IIOP version 1.2 supports reuse of the TCP/IP connection for both incoming and outgoing requests. Allowing reuse of a TCP/IP connection is the choice of the ORB. To allow reuse, you create an ORB policy object that allows reuse of a TCP/IP connection, and you use that policy object in the list of policies when initializing an ORB. The policy object is created using the CORBA::ORB::create_policy
operation. For more information about the CORBA::ORB::create_policy
operation, see the CORBA Programming Reference.
In this step, the joint client/server application activates the callback object in the POA using an object ID.
Listing 2-8 shows the portion of the Chat Room sample application that uses the POA to create the Listener object.
// Establish communication with the POA
orb_ptr = CORBA::ORB_init(argc, argv, "BEA_IIOP");
CORBA::PolicyList policy_list(1);
CORBA::Any val;
CORBA::Object_ptr o_init_poa;
o_init_poa = orb_ptr->resolve_initial_references("RootPOA");
// Narrow to get the Root POA
root_poa_ptr = PortableServer::POA::_narrow(o_init_poa);
CORBA::release(o_init_poa);
// Specify an IIOP Policy of Bidirectional for the POA
val <<= BiDirPolicy::BOTH;
CORBA::Policy_ptr bidir_pol_ptr = orb_ptr->create_policy(
BiDirPolicy::BIDIRECTIONAL_POLICY_TYPE, val);
policy_list.length ( 1 );
policy_list[0] = bidir_pol_ptr;
// Create the BiDirectional POA
bidir_poa_ptr = root_poa_ptr->create_POA("BiDirPOA",
root_poa_ptr->
the_POAManager(),
policy_list);
// Activate the POA
root_poa_ptr->the_POAManager()->activate();
// Create the Listener object
ChatClient::Listener_var v_listener_callback_ref;
// Create a servant for Listener object and activate it
listener_callback_servant = new Listener_i();
CORBA::Object_var v_listener_oref;
PortableServer::ObjectId_var temp_OId =
bidir_poa_ptr ->activate_object(listener_callback_servant );
// Create object reference for the Listener object with a
// system generated Object Id
v_listener_oref = bidir_poa_ptr->create_reference_with_id
(temp_OId,
ChatClient::_tc_Listener->id() );
v_listener_callback_ref = ChatClient::Listener::_narrow
( v_listener_oref.in() );
To use the POA to create a callback object with a Persistent/User ID object policy, you must write code that does the following:
LifespanPolicy
set to PERSISTENT
and IDAssignmentPolicy
set to USERID
.Note: | The Persistent/User ID object policy is only used with remote joint client/server applications (that is, a joint client/server application that is not in a BEA Tuxedo domain). |
Listing 2-9 shows code that performs these steps.
Note: | The code example does not use bidirectional IIOP. |
// Declare a string and convert it to an object Id.
const char* oid_string = "783";
PortableServer::ObjectID_var oid=
PortableServer::string_to_ObjectId(oid_string);
// Find the root POA
CORBA::Object_var oref =
orb_ptr->resolve_initial_references("RootPOA");
PortableServer::POA_var root_poa =
PortableServer::POA::_narrow(oref);
// Create and activate a Persistent/UserID POA
CORBA::PolicyList policies(2);
policies.length(2);
policies[0] = root_poa->create_lifespan_policy(
PortableServer::PERSISTENT);
policies[1] = root_poa->create_id_assignment_policy(
PortableServer::USER_ID );
PortableServer::POA_var poa_ref =
root_poa->create_POA("poa_ref",
root_poa->the_POAManager(),policies);
root_poa->the_POAManager()->activate();
// Create object reference for the Listener object.
oref = poa_ref->create_reference_with_id(oid,
ChatClient::_tc_Listener->id());
ChatClient::Listener_ptr Listener_oref =
ChatClient::Listener::_narrow( oref );
// Create Listener_i servant and activate the Listener object
Listener_i* my_Listener_i = new Listener_i();
poa_ref->activate_object_with_id( oid, my_Listener_i);
// Make call passing the reference to the Listener object
v_moderator_ref->signon( handle, Listener_oref);
To use the POA to create a callback object with a Persistent/System ID object policy, you need to write code that does the following:
LifespanPolicy
set to PERSISTENT
and IDAssignmentPolicy
set to the default.Note: | The Persistent/System ID object policy is only used with remote joint client/server applications (that is, a joint client/server application that is not in a BEA Tuxedo domain). |
Listing 2-10 shows code that performs these steps.
// Find the root POA
CORBA::Object_var oref=
orb_ptr->resolve_initial_references("RootPOA")
PortableServer::POA_var root_poa =
PortableServer::POA::_narrow(oref);
// Create and activate a Persistent/System ID POA
CORBA::PolicyList policies(1);
policies.length(1);
policies[0] = root_poa->create_lifespan_policy(
PortableServer::PERSISTENT);
//IDAssignmentPolicy
is the default so you do not need to specify it
PortableServer::POA_var poa_ref = root_poa->create_POA(
"poa_ref", root_poa->the_POAManager(), policies);
root_poa->the_POAManager()->activate();
// Create Listener_i servant and activate the Listener object
Listener_i* my_Listener_i = new Listener_i();
PortableServer::ObjectId_var temp_OId =
poa_ref->activate_object ( my_Listener_i );
// Create object reference for Listener object with returned
// system object Id
oref = poa_ref->create_reference_with_id(
temp_OId, ChatClient::_tc_Listener->id() );
ChatClient::Listener_var Listener_oref =
ChatClient::Listener::_narrow(oref);
// Make the call passing the reference to the Listener object
v_moderator_ref->signon( handle, Listener_oref );
A joint client/server application can first function as a client application and then switch to functioning as a server application. To do this, the joint client/server application gives complete control of the thread to the ORB by invoking the following:
If a method in the server portion of a joint client/server application invokes ORB::shutdown()
, all server activity stops and control is returned to the statement after ORB::run()
is invoked in the server portion of the joint client/server application. Only under this condition does control return to the client functionality of the joint client/server application.
Since a client application has only a single thread, the client functionality of the joint client/server application must share the central processing unit (CPU) with the server functionality of the joint client/server application. This sharing is accomplished by occasionally checking with the ORB to see if the joint client/server application has server application work to perform. Use the following code to perform the check with the ORB:
if ( orb->work_pending() ) orb->perform_work();
After the ORB completes the server application work, the ORB returns to the joint client/server application, which then performs client application functions. The joint client/server application must remember to occasionally check with the ORB; otherwise, the joint client/server application will never process any invocations.
The ORB cannot service callbacks while the joint client/server application is blocking on a request. If a joint client/server application invokes an object in another BEA Tuxedo server application, the ORB blocks while it waits for the response. While the ORB is blocking, it cannot service any callbacks, so the callbacks are queued until the request is completed.
Perform the following steps to build and run the Chat Room sample application:
The following sections describe these steps.
You need to copy the files for the Chat Room sample application into a work directory on your local machine. The files for the Chat Room sample application are located in the following directories:
drive
:
\TUXDIR
\samples\corba\chatroom
/usr/local/TUXDIR
/samples/corba/chatroom
Use the files listed in Table 2-4 to build and run the Chat Room sample application.
During the installation of the BEA Tuxedo software, the sample application files are marked read-only. Before you can edit or build the files in the Chat Room sample application, you need to change the protection attribute of the files you copied into your work directory, as follows:
prompt> attrib /S -r
drive
:
\workdirectory
\*.*
ksh prompt> chmod u+w /
workdirectory
/*.*
On UNIX operating system platforms, you also need to change the permission of ChatRoom.ksh
to give execute permission to the file, as follows:
ksh prompt> chmod +x ChatRoom.ksh
Before building and running the Chat Room sample application, you need to ensure that the TUXDIR
environment variable is set on your system. In most cases, this environment variable is set as part of the installation procedure. The TUXDIR
environment variable defines the directory path where you installed the BEA Tuxedo software. For example:
To verify that the information for the environment variables defined during installation is correct, perform the following steps:
To change the settings, perform the following steps:
ksh prompt>export TUXDIR=
directorypath
The ChatSetup
command automates the following steps:
Before running the ChatSetup
command, you need to check the following:
To build and run the sample application, enter the ChatSetup
command, as follows:
Start the server application and the system server processes in the Chat Room sample application by entering the following command:
This command starts the following server processes:
TMSYSEVT
The system EventBroker. This server process is used only by the BEA Tuxedo system.
TMFFNAME
The following three TMFFNAME
server processes are started:
TMFFNAME
server process started with the -N
and -M
options is the Master NameManager service. The NameManager service maintains a mapping of the application-supplied names to object references. This server process is used only by the BEA Tuxedo system.TMFFNAME
server process started with only the -N
option is the Slave NameManager service.TMFFNAME
server process started with the -F
option contains the FactoryFinder object.ChatRoom
The server application process for the Chat Room sample application.
ISL
Start the client application in the Chat Room sample application by entering the following command:
prompt> ChatClient
chatroom_name -ORBport nnn
where chatroom_name is the name of a chat room to which you want to connect. You can enter any value. You will be prompted for a handle to identify yourself. You can enter any value. If the handle you chose is in use, you will be prompted for another handle.
To optimize the usefulness of the Chat Room sample application, you should run a second client application using the same chat room name.
To exit the client application, enter \
.
Before using another sample application, enter the following commands to stop the Chat Room sample application and to remove unnecessary files from the work directory:
prompt> nmake -f ChatRoom.nt superclean
prompt> nmake -f ChatRoom.nt adminclean
ksh prompt> . ./Admin/setenv.ksh
ksh prompt> make -f ChatRoom.mk superclean
ksh prompt> make -f ChatRoom.nt adminclean
![]() ![]() ![]() |