|
|
Developing CORBA Request-Level Interceptors
Developing a CORBA request-level interceptor typically involves the following steps:
Also identify the machines on which you plan to deploy the interceptors.
The preceding steps are usually iterative. For example, the first time you build and test your interceptor, you might have only the most basic code in the interceptor that merely verifies that the interceptor is running. With subsequent builds and tests, you gradually implement the full functionality of the interceptor.
The sections that follow explain each of these steps in detail, using the sample interceptors packaged with the BEA Tuxedo software for examples.
Step 1: Identify the Interfaces of Your CORBA Application
Deploying an interceptor on a given machine constitutes a significant overhead because that interceptor will be invoked every time any application on that machine issues (in the case of a client-side interceptor) or receives (target-side interceptor) a request. Therefore, any interceptor you create must be well-matched to those applications.
For example, a security interceptor typically needs to know about what kinds of requests are of concern, and what kinds of data are being handled in the request.
Any interceptor that deals with specific requests needs to be able to extract the interface repository ID from the request. With that interface knowledge, the interceptor then has a way of knowing what kind of data is in the request, and can then handle that data in a request-specific fashion.
In addition, if a request is sent that is not of interest, the interceptor needs to be able to pass the request through quickly and efficiently.
The PersonQuery example described in PersonQuery Sample Application, uses an interceptor that determines whether the user of the PersonQuery client application can receive addresses. If the identity of the user matches specific criteria, the interceptor allows the full address number to be returned to the client. If no match exists, the interceptor returns only the string of x characters to the log file in place of the address.
Step 2: Write the Interceptor Implementation Code
To implement an interceptor:
The topics that follow discuss implementation considerations that may be typical of many interceptors. Examples from the InterceptorData interceptors, which are described in InterceptorData Sample Interceptors, are provided.
Starting the Implementation File
You can use the code fragments included in Appendix A as a place to start implementing your interceptor. You may use the code included in Appendix A, or you may copy the following starter files available at the WebLogic Enterprise Developer Center on the BEA Web site:
For information about getting these starter files from the WebLogic Enterprise Developer Center, see the Release Notes.
You can start your interceptor implementation using the sample interceptor code provided in Appendix A, where YourInterceptor represents the name of the interceptor you are implementing. The ORB will always pass nil references for the ServiceContextList and CORBA::DataOutputStream parameters. You should not use or reference those parameters. You should not test those parameters for nil because this restriction may change in a future version.
Initializing the Interceptor at Run Time
All interceptors are instantiated when the ORB is initialized. At no other time are request-level interceptors instantiated. As part of initializing, the interceptor's initialization routine must instantiate an instance of an implementation for a client interceptor, or a target interceptor, or both, depending upon what the interceptor intends to support. As mentioned earlier, a single shareable image can support both the client-side and target-side interceptors. The instances of any interceptor instantiated are then returned from the initialization routine and registered with the ORB run time.
The following code fragment is from the InterceptorData interceptor, and shows the declaration of the initialization operation invoked by the client-side ORB when that ORB is initialized:
void InterceptorDataClientInit(
CORBA::ORB_ptr TheORB,
RequestLevelInterceptor::ClientRequestInterceptor ** ClientPtr,
RequestLevelInterceptor::TargetRequestInterceptor ** TargetPtr,
CORBA::Boolean * RetStatus)
The following code fragment shows the statements to instantiate the InterceptorData client interceptor class. Note that this fragment uses a class named tracker, which is used for keeping track of each incoming client request so that it can be matched with the response returned by the target object. The tracker class is described in the section Identifying Operations in the Request.
ClientInterceptorData * client = new ClientInterceptorData(TheORB, tracker);
if (!client)
{
tmpfile << "InterceptorDataClientInit: Client alloc failed"
<< endl << endl;
*RetStatus = CORBA_FALSE;
delete tracker;
return;
}
The following code fragment shows the statements to return the interceptor class to the ORB:
*ClientPtr = client;
*TargetPtr = 0;
*RetStatus = CORBA_TRUE;
return;
Obtaining the Interface Name from a Request
If you have an interceptor that works with specific interfaces or requests, the interceptor needs a way to extract the interface ID associated with a request so that the interceptor can identify it and thus know how to handle the data in the request. For example, the InterceptorData interceptor manipulates the request parameters sent in requests from the PersonQuery application. To manipulate the request parameters, the interceptor needs to know which request is being sent.
The following code fragment from the InterceptorData sample shows the interface ID extracted from the RequestContext structure:
if (strcmp(request_context.interface_id.in(),
PersonQuery::_get_interface_name()) != 0)
return ret_status;
Identifying Operations in the Request
Using the extracted interface ID, the InterceptorData sample uses a simple switch statement to identify the operation in the client request. That way, the interceptor will know what do with the request parameters contained in the request.
The following code fragment shows the switch statement that checks for either the Exit operation or the operation to query the database for a person by name. Note the use of the parser object, which extracts operations from the request retrieved from the tracker object.
m_outfile << " Operation: " << request_context.operation << endl;
PQ parser;
PQ::op_key key = parser.MapOperation(request_context.operation.in());
switch (key)
{
default:
m_outfile << " ERROR: operation is not member of "
<< request_context.interface_id.in() << endl;
excep_val = new CORBA::BAD_OPERATION();
return Interceptors::REPLY_EXCEPTION;
case PQ::Exit:
m_outfile << endl;
return ret_status;
case PQ::ByPerson:
{
PersonQuery::Person per;
parser.GetByPerson(request_arg_stream, &per);
m_outfile << " Parameters:" << endl;
m_outfile << per << endl;
}
break;
Implementing the Interceptor's Response Operation
Extracting an interface ID out of a client request is fairly straightforward. However, it's not quite as simple to do that with a target response. If an interceptor needs to know what interface and operation is associated with the response it receives from the ORB, it needs to have special logic for tracking requests. It is the interceptor's responsibility to track requests coming from the client.
The InterceptorData samples implement a language object, called Tracker, that keeps a record of the target-bound requests, and then matches the target responses to them when those responses arrive back at the interceptor.
The client_response and target_response operations on the InterceptorData samples extract interface and operation information from the Tracker object when responses are returned from the target.
The following InterceptorData code fragment extracts the request associated with a response:
RequestInfo * req_info = m_tracker->CompleteRequest(reply_context);
if (!req_info)
{
m_outfile << " unable to find request for this reply (must not be one
we care about)" << endl << endl;
return Interceptors::RESPONSE_NO_EXCEPTION;
}
//
// This is the interface we are expecting. Now identify the operation
// being invoked, so we can parse the request parameters.
//
m_outfile << " ReplyStatus: ";
OutputReplyStatus(m_outfile, reply_context.reply_status);
m_outfile << endl;
m_outfile << " Interface: " << req_info->intf() << endl;
m_outfile << " Operation: " << req_info->op() << endl;
PQ parser;
PQ::op_key key = parser.MapOperation(req_info->op());
Now that the interceptor has obtained the request associated with the response, the interceptor can handle the data in the response appropriately.
Reading Parameters Out of a Data Input Stream
The following code fragment shows an example of how the InterceptorData sample places the request parameters from a data stream into a structure. The parameter S in the following code fragment represents a pointer to a DataInputStream structure that can be used by the interceptor implementation to retrieve the value of the reply parameters of the PersonQuery operation. The code encapsulated by the braces in this code fragment extracts the parameters of the response from the DataInputStream structure. For more information about the DataInputStream structure, see Request-Level Interceptor API.
void PQ::get_addr(CORBA::DataInputStream_ptr S,
PersonQuery::Address *addr)
{
addr->number = S->read_short();
addr->street = S->read_string();
addr->town = S->read_string();
addr->state = S->read_string();
addr->country = S->read_string();
}
Exceptions
Exceptions from interceptors returned via the excep_val parameter can only be a derived type from the CORBA::SystemException base class. (Any other exception type that the interceptor implementations return to the ORB is converted by the ORB to a CORBA::UNKNOWN exception, which is passed via the excep_val parameter.) You need to map exceptions to a CORBA::SystemException class or one of its derivatives.
Step 3: Create the Interceptor Header File
After you have created any implementation code in the interceptor implementation file, you need to provide any data or operations as needed to the interceptor header file.
The following code example shows basic information that is required in the header file for an interceptor implementation file that implements both client- and target-side interceptors.
This example also shows:
In this code example, YourInterceptor represents the name of the interceptor you are creating.
#include <CORBA.h>
#include <RequestLevelInterceptor.h>
#include <security_c.h> //for security
class YourInterceptorClient : public virtual RequestLevelInterceptor::ClientRequestInterceptor
{
private:
YourInterceptorClient() {}
CORBA::ORB_ptr m_orb;
public:
YourInterceptorClient(CORBA::ORB_ptr TheOrb);
~YourInterceptorClient() {}
Interceptors::ShutdownReturnStatus shutdown(
Interceptors::ShutdownReason reason,
CORBA::Exception_ptr & excep_val);
CORBA::String id();
void exception_occurred (
const RequestLevelInterceptor::ReplyContext & reply_context,
CORBA::Exception_ptr excep_val);
Interceptors::InvokeReturnStatus client_invoke (
const RequestLevelInterceptor::RequestContext & request_context,
RequestLevelInterceptor::ServiceContextList_ptr service_context,
CORBA::DataInputStream_ptr request_arg_stream,
CORBA::DataOutputStream_ptr reply_arg_stream,
CORBA::Exception_ptr & excep_val);
Interceptors::ResponseReturnStatus client_response (
const RequestLevelInterceptor::ReplyContext & reply_context,
RequestLevelInterceptor::ServiceContextList_ptr service_context,
CORBA::DataInputStream_ptr arg_stream,
CORBA::Exception_ptr & excep_val);
};
class YourInterceptorTarget : public virtual RequestLevelInterceptor::TargetRequestInterceptor
{
private:
YourInterceptorTarget() {}
CORBA::ORB_ptr m_orb;
SecurityLevel1::Current_ptr m_security_current; //for security
Security::AttributeTypeList * m_attributes_to_get; //for security
public:
YourInterceptorTarget(CORBA::ORB_ptr TheOrb);
~YourInterceptorTarget();
Interceptors::ShutdownReturnStatus shutdown(
Interceptors::ShutdownReason reason,
CORBA::Exception_ptr & excep_val);
CORBA::String id();
void exception_occurred (
const RequestLevelInterceptor::ReplyContext & reply_context,
CORBA::Exception_ptr excep_val);
Interceptors::InvokeReturnStatus target_invoke (
const RequestLevelInterceptor::RequestContext & request_context,
RequestLevelInterceptor::ServiceContextList_ptr service_context,
CORBA::DataInputStream_ptr request_arg_stream,
CORBA::DataOutputStream_ptr reply_arg_stream,
CORBA::Exception_ptr & excep_val);
Interceptors::ResponseReturnStatus target_response (
const RequestLevelInterceptor::ReplyContext & reply_context,
RequestLevelInterceptor::ServiceContextList_ptr service_context,
CORBA::DataInputStream_ptr arg_stream,
CORBA::Exception_ptr & excep_val);
};
Step 4: Build the Interceptor
Interceptors are built into shareable libraries. Therefore, the steps for building an interceptor are platform-specific. For details about the specific commands and options used to build interceptors on any particular platform, execute the makefile that builds the interceptor sample applications provided with the BEA Tuxedo software, and view the results of the build in the log file that results from the build.
The command to build the sample interceptors is as follows:
Windows 2000
> nmake -f makefile.nt
UNIX
> make -f makefile.mk
For more information about building and running the sample interceptors provided with the BEA Tuxedo software, see PersonQuery Sample Application.
Step 5: Test the Interceptor
Testing an interceptor requires you to perform the following tasks:
For information about registering interceptors, see Deploying CORBA Request-Level Interceptors.
|
Copyright © 2001 BEA Systems, Inc. All rights reserved.
|