VisiBroker for C++ Developer’s Guide : Using object wrappers

Using object wrappers
This section describes the object wrapper feature of VisiBroker, which allows your applications to be notified or to trap an operation request for an object.
Object wrappers overview
The VisiBroker object wrapper feature allows you to define methods that are called when a client application invokes a method on a bound object or when a server application receives an operation request. Unlike the interceptor feature which is invoked at the VisiBroker ORB level, object wrappers are invoked before an operation request has been marshaled. In fact, you can design object wrappers to return results without the operation request having ever been marshaled, sent across the network, or actually presented to the object implementation. For more information about VisiBroker Interceptors, go to “Using VisiBroker Interceptors”
Object wrappers may be installed on just the client-side, just the server-side, or they may be installed in both the client and server portions of a single application.
The following are a few examples of how you might use object wrappers in your application:
Note
Externalizing a reference to an object for which object wrappers have been installed, using the VisiBroker ORB Object's object_to_string method, will not propagate those wrappers to the recipient of the stringified reference if the recipient is a different process.
Typed and un-typed object wrappers
VisiBroker offers two kinds of object wrappers: typed and untyped. You can mix the use of both of these object wrappers within a single application. For information on typed wrappers, see “Typed object wrappers”. For information on untyped wrappers, see “Untyped object wrappers”. The following table summarizes the important distinctions between these two kinds of object wrappers.
Special idl2cpp requirements
Whenever you plan to use typed or untyped object wrappers, you must ensure that you use the -obj_wrapper option with the idl2cpp compiler when you generate the code for your applications. This will result in the generation of an Object wrapper base class for each of your interfaces.
Object wrapper example applications
The sample client and server applications used to illustrate both the typed and untyped object wrapper concepts in this section are located in the following directory:
<install_dir>\examples\vbe\interceptors\objectWrappers\
Untyped object wrappers
Untyped object wrappers allow you to define methods that are to be invoked before an operation request is processed, after an operation request is processed, or both. Untyped wrappers can be installed for client or server applications and you can also install multiple versions.
You may also mix the use of both typed and untyped object wrappers within the same client or server application.
By default, untyped object wrappers have a global scope and will be invoked for any operation request. You can design untyped wrappers so that they have no effect for operation requests on object types in which you are not interested.
Note
Unlike typed object wrappers, untyped wrapper methods do not receive the arguments that the stub or object implementation would receive nor can they prevent the invocation of the stub or object implementation.
The following figure shows how an untyped object wrapper's pre_method is invoked before the client stub method and how the post_method is invoked afterward. It also shows the calling sequence on the server-side with respect to the object implementation.
Figure 34
Using multiple, untyped object wrappers
Figure 35
Order of pre_method invocation
When a client invokes a method on a bound object, each untyped object wrapper pre_method will receive control before the client's stub routine is invoked. When a server receives an operation request, each untyped object wrapper pre_method will be invoked before the object implementation receives control. In both cases, the first pre_method to receive control will be the one belonging to the object wrapper that was registered first.
Order of post_method invocation
When a server's object implementation completes its processing, each post_method will be invoked before the reply is sent to the client. When a client receives a reply to an operation request, each post_method will be invoked before control is returned to the client. In both cases, the first post_method to receive control will be the one belonging to the object wrapper that was registered last.
Note
If you choose to use both typed and untyped object wrappers, see “Combined use of untyped and typed object wrappers” for information on the invocation order.
Using untyped object wrappers
The following are the required steps for using untyped object wrappers. Each step is discussed in further detail in the following sections.
1
2
Generate the code from your IDL specification using the idl2cpp compiler with the -obj_wrapper option.
3
4
5
Implementing an untyped object wrapper factory
The TimeWrap.h file, part of the ObjectWrappers sample applications, illustrates how to define an untyped object wrapper factory that is derived from the VISObjectWrapper::UntypedObjectWrapperFactory.
Your factory's create method will be invoked to create an untyped object wrapper whenever a client binds to an object or a server invokes a method on an object implementation. The create method receives the target object, which allows you to design your factory to not create an untyped object wrapper for those object types you wish to ignore. It also receives an enum specifying whether the object wrapper created is for the server side object implementation or the client side object.
The following code sample shows the TimingObjectWrapperFactory, which is used to create an untyped object wrapper that displays timing information for method calls. Notice the addition of the key parameter to the TimingObjectWrapperFactory constructor. This parameter is also used by the service initializer to identify the wrapper.
class TimingObjectWrapperFactory
: public VISObjectWrapper::UntypedObjectWrapperFactory
{
public:
TimingObjectWrapperFactory(VISObjectWrapper::Location loc,
const char* key)
: VISObjectWrapper::UntypedObjectWrapperFactory(loc),
_key(key) {}

// ObjectWrapperFactory operations
VISObjectWrapper::UntypedObjectWrapper_ptr create(
CORBA::Object_ptr target,
VISObjectWrapper::Location loc) {
if (_owrap == NULL) {
_owrap = new TimingObjectWrapper(_key);
}
return VISObjectWrapper::UntypedObjectWrapper::_duplicate(_owrap);
}
private:
CORBA::String_var _key;
VISObjectWrapper::UntypedObjectWrapper_var _owrap;
};
Implementing an untyped object wrapper
The following code sample shows the implementation of the TimingObjectWrapper, also defined in the TimeWrap.h file. Your untyped wrapper must be derived from the VISObjectWrapper::UntypedObjectWrapper class, and you may provide an implementation for both the pre_method or post_method methods in your untyped object wrapper.
Once your factory has been installed, either automatically by the factory's constructor or manually by invoking the VISObjectWrapper::ChainUntypedObjectWrapper::add method. An untyped object wrapper object will be created automatically whenever your client binds to an object or when your server invokes a method on an object implementation.
The pre_method shown in the following code sample invokes the TimerBegin method, defined in TimeWrap.C, which uses the Closure object to save the current time. Similarly, the post_method invokes the TimerDelta method to determine how much time has elapsed since the pre_method was called and print the elapsed time.
class TimingObjectWrapper : public VISObjectWrapper::UntypedObjectWrapper {
public:
TimingObjectWrapper(const char* key=NULL) : _key(key) {}
void pre_method(const char* operation,
CORBA::Object_ptr target,
VISClosure& closure) {
cout << "*Timing: [" << flush;
if ((char *)_key)
cout << _key << flush;
else
cout << "<no key>" << flush;
cout << "] pre_method\t" << operation << "\t->" << endl;
TimerBegin(closure, operation);
}
void post_method(const char* operation,
CORBA::Object_ptr target,
CORBA::Environment& env,
VISClosure& closure) {
cout << "*Timing: [" << flush;
if ((char *)_key)
cout << _key << flush;
else
cout << "<no key>" << flush;
cout << "] post_method\t" ;
TimerDelta(closure, operation);
}
private:
CORBA::String_var _key;
};
pre_method and post_method parameters
Both the pre_method and post_method receive the parameters shown in the following table.
post_method only parameter used to inform the user of any exceptions that might have occurred during the previous steps of the method invocation.
Creating and registering untyped object wrapper factories
An untyped object wrapper factory is automatically added to the chain of untyped wrappers whenever it is created with the base class constructor that accepts a location.
On the client side, objects will be wrapped only if untyped object wrapper factories are created and registered before the objects are bound. On the server side, untyped object wrappers factories are created and registered before an object implementation is called.
The following code sample shows a portion of the sample file UntypedClient.C which shows the creation, with automatic registration, of two untyped object wrapper factories for a client. The factories are created after the VisiBroker ORB has been initialized, but before the client binds to any objects.
int main(int argc, char* const* argv) {
try {
// Initialize the ORB.
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);
// Install untyped object wrappers
TimingObjectWrapperFactory timingfact(VISObjectWrapper::Client,
"timeclient");
TraceObjectWrapperFactory tracingfact(VISObjectWrapper::Client,
"traceclient");
// Now locate an account manager.
. . .]
The following code sample illustrates the sample file UntypedServer.C, which shows the creation and registration of untyped object wrapper factories for a server. The factories are created after the VisiBroker ORB is initialized, but before any object implementations are created.
// UntypedServer.C
#include "Bank_s.hh"
#include "BankImpl.h"
#include "TimeWrap.h"
#include "TraceWrap.h"
USE_STD_NS

int main(int argc, char* const* argv) {
try {
// Initialize the ORB.
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);
// Initialize the POA.
CORBA::Object_var obj = orb->resolve_initial_references("RootPOA");
PortableServer::POA_var rootPoa = PortableServer::POA::_narrow(obj);
CORBA::PolicyList policies;
policies.length(1);
policies[(CORBA::ULong)0] = rootPoa->create_lifespan_policy(
PortableServer::PERSISTENT);
// Get the POA Manager.
PortableServer::POAManager_var poa_manager = rootPoa->the_POAManager();
// Create myPOA With the Right Policies.
PortableServer::POA_var myPOA = rootPoa->create_POA("bank_ow_poa",
poa_manager,
policies);
// Install Untyped Object Wrappers for Account Manager.
TimingObjectWrapperFactory timingfact(VISObjectWrapper::Server,
"timingserver");
TraceObjectWrapperFactory tracingfact(VISObjectWrapper::Server,
"traceserver");
// Create the Account Manager Servant.
AccountManagerImpl managerServant;
// Decide on ID for Servant.
PortableServer::ObjectId_var managerId =
PortableServer::string_to_ObjectId("BankManager");
// Activate the Servant with the ID on myPOA.
myPOA->activate_object_with_id(managerId, &managerServant);
// Activate the POA Manager.
rootPoa->the_POAManager()->activate();
cout << "Manager is ready." << endl;
// Wait for Incoming Requests.
orb->run();
} catch(const CORBA::Exception& e) {
cerr << e << endl;
return 1;
}
return 0;
}
Removing untyped object wrappers
The VISObjectWrapper::ChainUntypedObjectWrapperFactory class remove method can be used to remove an untyped object wrapper factory from a client or server application. You must specify a location when removing a factory. This means that if you have added a factory with a location of VISObjectWrapper::Both, you can selectively remove it from the Client location, the Server location, or Both.
Note
Removing one or more object wrapper factories from a client will not affect objects of that class that are already bound by the client. Only subsequently bound objects will be affected. Removing object wrapper factories from a server will not affect object implementations that have already been created. Only subsequently created object implementations will be affected.
Typed object wrappers
When you implement a typed object wrapper for a particular class, you define the processing that is to take place when a method is invoked on a bound object. The following figure shows how an object wrapper method on the client is invoked before the client stub class method and how an object wrapper on the server-side is invoked before the server's implementation method.
Note
Your typed object wrapper implementation is not required to implement all methods offered by the object it is wrapping.
You may also mix the use of both typed and untyped object wrappers within the same client or server application. For more information, see “Combined use of untyped and typed object wrappers”.
Figure 36
Using multiple, typed object wrappers
You can implement and register more than one typed object wrapper for a particular class of object, as shown in the following figure.
On the client side, the first object wrapper registered is client_wrapper_1, so its methods will be the first to receive control. After performing its processing, the client_wrapper_1 method may pass control to the next object's method in the chain or it may return control to the client.
On the server side, the first object wrapper registered is server_wrapper_1, so its methods will be the first to receive control. After performing its processing, the server_wrapper_1 method may pass control to the next object's method in the chain or it may return control to the servant.
Figure 37
Order of invocation
The methods for a typed object wrapper that are registered for a particular class will receive all of the arguments that are normally passed to the stub method on the client side or to the skeleton on the server side. Each object wrapper method can pass control to the next wrapper method in the chain by invoking the parent class' method, <interface_name>ObjectWrapper::<method_name>. If an object wrapper wishes to return control without calling the next wrapper method in the chain, it can return with the appropriate return value.
A typed object wrapper method's ability to return control to the previous method in the chain allows you to create a wrapper method that never invokes a client stub or object implementation. For example, you can create an object wrapper method that caches the results of a frequently requested operation. In this scenario, the first invocation of a method on the bound object results in an operation request being sent to the object implementation. As control flows back through the object wrapper method, the result is stored. On subsequent invocations of the same method, the object wrapper method can simply return the cached result without actually issuing the operation request to the object implementation.
If you choose to use both typed and untyped object wrappers, see “Combined use of untyped and typed object wrappers” for information on the invocation order.
Typed object wrappers with co-located client and servers
When the client and server are both packaged in the same process, the first object wrapper method to receive control will belong to the first client-side object wrapper that was installed. The following figure illustrates the invocation order.
Figure 38
Using typed object wrappers
The following are the required steps for using typed object wrappers. Each step is discussed in further detail in the following sections.
1
2
Generate the code from your IDL specification using the idl2cpp compiler with the -obj_wrapper option.
3
Derive your typed object wrapper class from the <interface_name>ObjectWrapper class generated by the compiler, and provide an implementation of those methods you wish to wrap.
4
Implementing typed object wrappers
You derive typed object wrappers from the <interface_name>ObjectWrapper class that is generated by the idl2cpp compiler.
The following code sample shows the implementation of a typed object wrapper for the Account interface from the file BankWrap.h.
Notice that this class is derived from the AccountObjectWrapper interface and provides a simple caching implementation of the balance method, which provides these processing steps:
1
Check the _inited flag to see if this method has been invoked before.
2
If this is the first invocation, the balance method on the next object in the chain is invoked and the result is saved to _balance, the _inited flag is set to true, and the value is returned.
3
class CachingAccountObjectWrapper : public Bank::AccountObjectWrapper {
public:
CachingAccountObjectWrapper() : _inited((CORBA::Boolean)0) {}
CORBA::Float balance() {
cout << "+ CachingAccountObjectWrapper: Before Calling Balance" << endl;
if (! _inited) {
_balance = Bank::AccountObjectWrapper::balance();
_inited = 1;
} else {
cout << "+ CachingAccountObjectWrapper: Returning Cached Value" <<
endl;
}
cout << "+ CachingAccountObjectWrapper: After Calling Balance" << endl;
return _balance;
}
. . .
};
Registering typed object wrappers for a client
A typed object wrapper is registered on the client-side by invoking the <interface_name>::add method that is generated for the class by the idl2cpp compiler. Client-side object wrappers must be registered after the ORB_init method has been called, but before any objects are bound. The following code sample shows a portion of the TypedClient.java file that creates and registers a typed object wrapper.
. . .
int main(int argc, char* const* argv) {
try {
// Initialize the ORB.
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);

// Install Typed Object Wrappers for Account.
Bank::AccountObjectWrapper::add(orb,
CachingAccountObjectWrapper::factory,
VISObjectWrapper::Client);

// Get the Manager ID.
PortableServer::ObjectId_var managerId =
PortableServer::string_to_ObjectId("BankManager");
// Locate an Account Manager.
Bank::AccountManager_var manager =
Bank::AccountManager::_bind("/bank_ow_poa", managerId);
. . .
The VisiBroker ORB keeps track of any object wrappers that have been registered for it on the client side. When a client invokes the _bind method to bind to an object of that type, the necessary object wrappers will be created. If a client binds to more than one instance of a particular class of object, each instance will have its own set of wrappers.
Registering typed object wrappers for a server
As with a client application, a typed object wrapper is registered on the server side by invoking the <interface_name>::add method. Server side, typed object wrappers must be registered after the ORB_init method has been called, but before an object implementation services a request. The following code sample shows a portion of the TypedServer.C file that installs a typed object wrapper.
// TypedServer.C
#include "Bank_s.hh"
#include "BankImpl.h"
#include "BankWrap.h"
USE_STD_NS
int main(int argc, char* const* argv) {
try {
// Initialize the ORB.
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);
// Initialize the POA.
CORBA::Object_var obj = orb->resolve_initial_references("RootPOA");
PortableServer::POA_var rootPoa = PortableServer::POA::_narrow(obj);
CORBA::PolicyList policies;
policies.length(1);
policies[(CORBA::ULong)0] = rootPoa->create_lifespan_policy(
PortableServer::PERSISTENT);
// Get the POA Manager.
PortableServer::POAManager_var poa_manager = rootPoa->the_POAManager();
// Create myPOA With the Right Policies.
PortableServer::POA_var myPOA = rootPoa->create_POA("bank_ow_poa",
poa_manager,
policies);
// Install Typed Object Wrappers for Account Manager.
Bank::AccountManagerObjectWrapper::add(orb,
SecureAccountManagerObjectWrapper::factory,
VISObjectWrapper::Server);
Bank::AccountManagerObjectWrapper::add(orb,
CachingAccountManagerObjectWrapper::factory,
VISObjectWrapper::Server);
// Create the Account Manager Servant.
AccountManagerImpl managerServant;
// Decide on ID for Servant.
PortableServer::ObjectId_var managerId =
PortableServer::string_to_ObjectId("BankManager");
// Activate the Servant with the ID on myPOA.
myPOA->activate_object_with_id(managerId, &managerServant);
// Activate the POA Manager.
rootPoa->the_POAManager()->activate();
cout << "Manager is ready." << endl;
// Wait for Incoming Requests.
Orb>run();
} catch(const CORBA::Exception& e) {
cerr << e << endl;
return 1;
}
return 0;
}
If a server creates more than one instance of a particular class of object, a set of wrappers will be created for each instance.
Removing typed object wrappers
The <interface_name>ObjectWrapper::remove method that is generated for a class by the idl2cpp compiler allows you to remove a typed object wrapper from a client or server application. You must specify a location when removing a factory. This means that if you have added a factory with a location of VISObjectWrapper::Both, you can selectively remove it from the Client location, the Server location, or Both.
Note
Removing one or more object wrappers from a client will not affect objects of that class that are already bound by the client. Only subsequently bound objects will be affected. Removing object wrappers from a server will not affect object implementations that have already serviced requests. Only subsequently created object implementations will be affected.
Combined use of untyped and typed object wrappers
If you choose to use both typed and untyped object wrappers in your application, all pre_method methods defined for the untyped wrappers will be invoked prior to any typed object wrapper methods defined for an object. Upon return, all typed object wrapper methods defined for the object will be invoked prior to any post_method methods defined for the untyped wrappers.
The sample applications Client.C and Server.C make use of a sophisticated design that allows you to use command-line properties to specify which, if any, typed and untyped object wrappers are to be used.
Command-line arguments for typed wrappers
The following table shows the command-line arguments you can use to enable the use of typed object wrappers for the sample bank applications implemented in Client.C and Server.C.
Initializer for typed wrappers
The typed wrappers are created in the BankInit::update initializer, defined in objectWrappers/BankWrap.C. This initializer will be invoked when the ORB_init method is invoked and will handle the installation of various typed object wrappers, based on the command-line properties you specify.
The following code sample shows how the initializer uses a set of PropStruct objects to track the command-line options that have been specified and then add or remove AccountObjectWrapper objects for the appropriate locations.
. . .
static const CORBA::ULong kNumTypedAccountProps = 2;
static PropStruct
TypedAccountProps[kNumTypedAccountProps] =
{ { "BANKaccountCacheClnt", CachingAccountObjectWrapper::factory,
VISObjectWrapper::Client },
{ "BANKaccountCacheSrvr", CachingAccountObjectWrapper::factory,
VISObjectWrapper::Server }
};
static const CORBA::ULong kNumTypedAccountManagerProps = 4;
static PropStruct TypedAccountManagerProps[kNumTypedAccountManagerProps] =
{ { "BANKmanagerCacheClnt", CachingAccountManagerObjectWrapper::factory,
VISObjectWrapper::Client },
{ "BANKmanagerSecurityClnt", SecureAccountManagerObjectWrapper::factory,
VISObjectWrapper::Client },
{ "BANKmanagerCacheSrvr", CachingAccountManagerObjectWrapper::factory,
VISObjectWrapper::Server },
{ "BANKmanagerSecuritySrvr", SecureAccountManagerObjectWrapper::factory,
VISObjectWrapper::Server },
};
void BankInit::update(int& argc, char* const* argv) {
if (argc > 0) {
init(argc, argv, "-BANK");
CORBA::ULong i;

for (i=0; i < kNumTypedAccountProps; i++) {
CORBA::String_var arg(getArgValue(TypedAccountProps[i].propname));
if (arg && strlen(arg) > 0) {
if (atoi((char*) arg)) {
Bank::AccountObjectWrapper::add(_orb,
TypedAccountProps[i].fact,
TypedAccountProps[i].loc);
} else {
Bank::AccountObjectWrapper::remove(_orb,
TypedAccountProps[i]Fact,
TypedAccountProps[i].loc);
}
}
}
for (i=0; i < kNumTypedAccountManagerProps; i++) {
CORBA::String_var arg(
getArgValue(TypedAccountManagerProps[i].propname));
if (arg && strlen(arg) > 0) {
if (atoi((char*) arg)) {
Bank::AccountManagerObjectWrapper::add(_orb,
TypedAccountManagerProps[i]Fact,
TypedAccountManagerProps[i].loc);
} else {
Bank::AccountManagerObjectWrapper::remove(_orb,
TypedAccountManagerProps[i]Fact,
TypedAccountManagerProps[i].loc);
}
}
}
}
}
Command-line arguments for untyped wrappers
The following table shows the command-line arguments you can use to enable the use of untyped object wrappers for the sample bank applications implemented in Client.C and Server.C.
Initializers for untyped wrappers
The untyped wrappers are created and registered in the TraceWrapInit::update andTimingWrapInit::update methods, defined in BankWrappers/TraceWrap.C and TimeWrap.C. These initializers will be invoked when the ORB_init method is invoked and will handle the installation of various untyped object wrappers.
The following code sample shows a portion of the TraceWrap.C file, which will install the appropriate untyped object wrapper factories, based on the command-line properties you specify.
TraceWrapInit::update(int& argc, char* const* argv) {
if (argc > 0) {
init(argc, argv, "-TRACEWRAP");
VISObjectWrapper::Location loc;
const char* propname;
LIST(VISObjectWrapper::UntypedObjectWrapperFactory_ptr) *list;

for (CORBA::ULong i=0; i < 3; i++) {
switch (i) {
case 0:

loc = VISObjectWrapper::Client;
propname = "TRACEWRAPclient";
list = &_clientfacts;
break;
case 1:
loc = VISObjectWrapper::Server;
propname = "TRACEWRAPserver";
list = &_serverfacts;
break;
case 2:
loc = VISObjectWrapper::Both;
propname = "TRACEWRAPboth";
list = &_bothfacts;
break;
}
CORBA::String_var getArgValue(property_value(propname));
if (arg && strlen(arg) > 0) {
int numNew = atoi((char*) arg);
char key_buf[256];
for (CORBA::ULong j=0; j < numNew; j++) {
sprintf(key_buf, "%s-%d", propname, list->size());
list->add(new TraceObjectWrapperFactory(loc,
(const char*) key_buf));
}
}
}
}
}
Executing the sample applications
Before executing the sample applications, make sure that an osagent is running on your network. For more information, see , “Using the Smart Agent.” You can then execute the server application without any tracing or timing object wrappers by using the following command:
prompt> Server
Note
The server is designed as a co-located application. It implements both the server and a client.
From another window, you can execute the client application without any tracing or timing object wrappers to query the balance in a user's account using the following command:
prompt> Client John
You can also execute the following command if you want a default name to be used:
prompt> Client
Turning on timing and tracing object wrappers
To execute the client with untyped timing and tracing object wrappers enabled, use the following command:
prompt> Client -TRACEWRAPclient 1 -TIMINGWRAPclient 1
To execute the server with untyped wrappers for timing and tracing enabled, use the following command:
prompt> Server -TRACEWRAPserver 1 -TIMINGWRAPserver 1
Turning on caching and security object wrappers
To execute the client with the typed wrappers for caching and security enabled, use this command:
prompt> Client -BANKaccountCacheClnt 1 _BANKmanagerCacheClnt 1 \
-BANKmanagerSecurityClnt 1
To execute the server with typed wrappers for caching and security enabled, use the following command:
prompt> Server -BANKaccountCacheSrvr 1 -BANKmanagerCacheSrvr 1 \
-BANKmanagerSecuritySrvr 1
Turning on typed and untyped wrappers
To execute the client with all typed and untyped wrappers enabled, use the following command:
prompt> Client -BANKaccountCacheClnt 1 -BANKmanagerCacheClnt 1 \
-BANKmanagerSecurityClnt 1 \
-TRACEWRAPclient 1 -TIMINGWRAPclient 1
To execute the server with all typed and untyped wrappers enabled, use the following command:
prompt> Server BANKaccountCacheSrvr 1 BANKmanagerCacheSrvr 1 \
-BANKmanagerSecuritySrvr 1 \ -TRACEWRAPserver 1 -TIMINGWRAPserver 1
Executing a Co-located client and server
The following command will execute a Co-located server and client with all typed wrappers enabled, the untyped wrapper enabled for just the client, and the untyped tracing wrapper for just the server:
prompt> Server -BANKaccountCacheClnt 1 -BANKaccountCacheSrvr 1 \
-BANKmanagerCacheClnt 1 -BANKmanagerCacheSrvr 1 \
-BANKmanagerSecurityClnt 1 \
-BANKmanagerSecuritySrvr 1 \
-TRACEWRAPboth 1 \
-TIMINGWRAPboth 1