![]() |
![]() |
|
|
Developing C++ Joint Client/Server Applications
This topic includes the following sections:
Development Process
Table 2-1 outlines the development process for C++ joint client/server applications.
Table 2-1 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.
Chat Room Sample Application
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.
Figure 2-1 How the Chat Room Sample Application Works
The Chat Room sample application works as follows:
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 for more information.
Step 1: Writing the OMG IDL
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.
Listing 2-1 OMG IDL for 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.
Listing 2-2 OMG IDL for the Moderator and ModeratorFactory Interfaces
#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 );
};
};
Step 2: Generating Skeletons and Client Stubs
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:
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 Note: 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.
Step 3: Writing the Methods That Implement the Operations for Each 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:
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.
Listing 2-3 Implementation File for the Listener Object
// 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
Listing 2-4 Implementation File for Moderator and ModeratorFactory Objects
// 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
Step 4: Writing the Client Portion of the Joint Client/Server Application
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.
Listing 2-5 Client Portion of the Chat Room Joint Client/Server Application
...
// 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() );
...
Step 5: Creating a Callback Object Using the Callbacks Wrapper Object
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:
For a complete description of the object policies for callback objects, see Object Policies for Callback Objects.
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.
Listing 2-6 Using the Callbacks Wrapper Object 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());
...
Step 6: Invoking Operations on an Object by Passing a Reference to the Callback Object
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.
Listing 2-7 Invoking the signon Method
// 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() );
Step 7: Specifying Configuration Information
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:
-ORBport nnn
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.
Step 8: Compiling Joint Client/Server Applications
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.
Using the POA to Create a 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.
Creating a Callback Object with a Transient Object Policy
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.
Listing 2-8 Using 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() );
Creating a Callback Object with a Persistent/User ID Object Policy
To use the POA to create a callback object with a Persistent/User ID object policy, you must write code that does the following:
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.
Listing 2-9 Example Code for Listener Object with Persistent/User ID Object Policy
// 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);
Creating a Callback Object with a Persistent/System ID Object Policy
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:
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.
Listing 2-10 Example Code for Listener Object with Persistent/System ID Object Policy
// 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 );
Threading Considerations for C++ Joint Client/Server Applications
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:
orb -> run();
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.
Building and Running the Chat Room Sample Application
Perform the following steps to build and run the Chat Room sample application:
The following sections describe these steps.
Copying the Files for the Chat Room Sample Application into a Work Directory
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:
Windows
drive:\TUXDIR\samples\corba\chatroom
UNIX
/usr/local/TUXDIR/samples/corba/chatroom
Use the files listed in Table 2-4 to build and run the Chat Room sample application.
Changing the Protection Attribute on the Files for 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:
Windows
prompt> attrib /S -r drive:\workdirectory\*.*
UNIX
prompt> /bin/ksh
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
Verifying the Setting of the TUXDIR Environment Variable
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:
Windows
TUXDIR=C:\TUXDIR
UNIX
TUXDIR=/usr/local/TUXDIR
To verify that the information for the environment variables defined during installation is correct, perform the following steps:
Windows
The Control Panel appears.
The System Properties window appears.
The Environment page appears.
UNIX
ksh prompt>printenv TUXDIR
To change the settings, perform the following steps:
Windows
UNIX
ksh prompt>export TUXDIR=directorypath
Executing the ChatSetup Command
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:
Windows
prompt>cd workdirectory
prompt> ChatSetup.cmd
UNIX
ksh prompt> cd workdirectory
ksh prompt> ./ChatSetup.ksh
Starting the Server Application
Start the server application and the system server processes in the Chat Room sample application by entering the following command:
prompt> tmboot -y
This command starts the following server processes:
The system EventBroker. This server process is used only by the BEA Tuxedo system.
The following three TMFFNAME server processes are started:
The server application process for the Chat Room sample application.
The IIOP Listener/Handler process.
Starting the Client Application
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 \.
Stopping the Chat Room Sample Application
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:
Windows
prompt> tmshutdown -y
prompt> Admin\setenv
prompt> nmake -f ChatRoom.nt superclean
prompt> nmake -f ChatRoom.nt adminclean
UNIX
ksh prompt> tmshutdown -y
ksh prompt> . ./Admin/setenv.ksh
ksh prompt> make -f ChatRoom.mk superclean
ksh prompt> make -f ChatRoom.nt adminclean
![]() |
![]() |
![]() |
|
Copyright © 2001 BEA Systems, Inc. All rights reserved.
|