|
|
This chapter contains the following topics:
Table 2-1 outlines the development process for C++ joint client/server applications.
Development Process
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 WLE objects in your WLE application. For information about writing an ICF file, see Creating CORBA C++ 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. The chat room can be thought of 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 user name. 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 WLE 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 WLE 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:
Chat Room Sample Application
Figure 2-1 How the Chat Room Sample Application Works
The source files for the Chat Room sample application are located in the WLEdir\ samples\corba\chatroom directory in the WLE software directory. See Building and Running the Chat Room Sample Application 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.
Interface |
Description |
Operation |
---|---|---|
Listing 2-1 shows the chatclient.idl
that defines the Listener interface.
Listing 2-1
OMG IDL for the Listener Interface
module ChatClient{ 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, therefore, must use the #include
to reference the OMG IDL file that defines the Listener interface.
Listing 2-2
OMG IDL for the Moderator and ModeratorFactory Interfaces
#include "ChatClient.idl" interface ModeratorFactory { 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 WLE 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 for the Moderator and ModeratorFactory to invoke operations on the objects. The WLE 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:
interface Listener {
oneway void post (in string from,
in string output_line);
};
};
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 );
};
Moderator get_moderator( in string chatroom_name );
};
};Step 2: Generating Skeletons and Client Stubs
You then need to compile the OMG IDL file that defines the interfaces in the WLE 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 2-3 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.
Table 2-3 Files Produced by the idl
Command
File |
File in the Chat Room Sample Application Created by the idl Command |
Description |
---|---|---|
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 WLE objects (that is, the Moderator and ModeratorFactory objects) in the WLE server application.
An implementation file contains the following:
Step 3: Writing the Methods That Implement Each Object's Operations
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 #ifndef _Listener_i_h class Listener_i : public POA_ChatClient::Listener { #endif Listing 2-4
Implementation File for Moderator and ModeratorFactory Objects
//This module contains the definition of the implementation class #ifndef _Moderator_i_h //Chatter[n] id During development of a joint client/server application, you write the client portion of the joint client/server application as you would write any WLE client application. The client application needs to include code that does the following:
//Listener_i
#define _Listener_i_h
#include "ChatClient_s.h
public:
Listener_i ();
virtual ~Listener_i();
void post (
const char * from,
const char * output_line);
...
};
//Moderator and ModeratorFactory
#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
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 );
};
#endifStep 4: Writing the Client Portion of the Joint Client/Server Application
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 sign_on() , send() , and sign_off() 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
//WLE 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 WLE 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 C++ 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 BEAWrapper::Callbacks* callbacks = 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 WLE object. In the Chat Room sample application, the Moderator object uses an object reference to the Listener object as a parameter to the sign_on
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 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.
//Listener object, activate the Listener object, and create an
//object reference for the Listener object.
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 a WLE Object By Passing a Reference to the Callback Object
//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
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 WLE object in the same WLE domain, a configuration file for the WLE server application is needed. The configuration file should be written as part of the development of the WLE 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 and the Administration Guide.
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 WLE application. For more information about the ISL command, see the Administration Guide.
The final step in the development of a joint client/server application is to produce the executable. 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 form an executable, the command combines the client stub for the WLE 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:
To use the -P
option of the buildobjclient
command, you need to 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 WLE version 4.2. For a list of support interfaces, see the CORBA C++ 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:
Step 8: Compiling Joint Client/Server Applications
Using the POA to Create a Callback Object
Creating a Callback Object with a Transient Object Policy
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 need to 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 C++ 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() );
To use the POA to create a callback object with a Persistent/User ID object policy, you need to 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 WLE 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);
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 WLE 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 );
A joint client/server application may first function as a client application and then switch to functioning as a server application. To do this, the joint client/server application turns complete control of the thread to the ORB by making the following invocation:
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.
You should be aware that 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 WLE 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:
Windows NT
drive:\WLEdir \samples\corba\chatroom
UNIX
/usr/local/WLEdir /samples/corba/chatroom
You will use the files listed in Table 2-4 to build and run the Chat Room sample application.
During the installation of the WLE 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 NT
prompt>attrib -r
drive:
\workdirectory
\*.*
UNIX
prompt>/bin/ksh
ksh prompt>chmod u+w /
workdirectory
/*.*
On the UNIX operating system platform, 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 WLE software. For example:
Windows NT
TUXDIR=c:\WLEDir
UNIX
TUXDIR=/usr/local/WLEDir
To verify that the information for the environment variables defined during installation is correct, perform the following steps:
Windows NT
Changing the Protection Attribute on the Files for the Chat Room Sample Application
Verifying the Setting of the TUXDIR Environment Variable
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 NT
UNIX
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:
Windows NT
prompt>cd
workdirectory
prompt>ChatSetup.cmd
UNIX
ksh prompt>cd
workdirectory
ksh prompt>./ChatSetup.ksh
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:
Starting the Server Application
The system event broker. This server process is used only by the WLE system.
The following three TMFFNAME server processes are started:
The server application process for the Chat Room sample application.
The IIOP Listener/Handler process.
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:
Windows NT
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
Starting the Client Application
Stopping the Chat Room Sample Application
|
Copyright © 1999 BEA Systems, Inc. All rights reserved.
|