Previous | Contents | Index |
When an object method invocation is received by the server, the object must first be located. The server queries the DCE runtime using the information in the object reference. If the object is not registered with the runtime, and the [cxx_lookup] operation also does not find the object or is not used by the application, an rpc_x_object_not_found exception is raised by the server. This DCE exception is propagated back to the client application and can be caught in a TRY-CATCH-ENDTRY block.
Following is an example of an exception TRY-CATCH-ENDTRY block around an operation.
File: client.cxx Matrix *m; m = Matrix::bind((unsigned_char_t *) "/.:/objects/identityMatrix"); TRY m->set(0, 0, 99); /* ** send a message to object m */ CATCH (rpc_x_object_not_found) /* **handle object not found exception */ CATCH_ALL /* ** handle all other exceptions */ ENTRY |
Clients can dynamically create remote objects by invoking an operation that has been identified as an object creator function. Operations are identified as an object creator function by assigning them the ACF attribute [cxx_new] in the ACF file. This type of operation is a static member function of the interface class and can be referenced like a normal static member function of the interface. For example:
cxx_new (<MANAGERCLASS> <OPERATION>) |
The attribute also specifies a class type to instantiate on the server side of the operation call.
The operation is type checked to ensure that it returns an interface pointer type. You can assign return results from object creator functions to an interface pointer variable. Once the assignment is made, the object can be manipulated by any of the class methods defined for the interface.
Following is an example of the cxx_new ACF attribute.
File: matrix.idl [ uuid(C768C260-A5AB-11DB-832C-08002B2A1BCA) ] interface Matrix { . . . Matrix * new2x2( [in] double& v11, [in] double& v12, [in] double& v21, [in] double& v22 ); . . . File: matrix.acf interface Matrix { /* ** Add the include file to the server stub ** to declare the operation prototype */ [sstub] include "matrixmgr.h"; [cxx_new(MatrixMgr)] new2x2(); } |
The preceding example does not specify a binding handle attribute so the default auto_handle is used. However, you can specify any of the three binding handle mechanisms.
Clients use object creator functions to instantiate remote objects for their exclusive use. This is similar to how object constructors are used in normal C++ applications. When you delete the interface pointer with the C++ delete operation, a message is sent to the remote object to cause it to be deleted from the server's address space.
The [sstub] include statement in the preceding example allows the class declaration for the MatrixMgr class to be included into the server stub so the compiler will recognize the reference.
Following is an example of client usage of a dynamic object creator function.
File: client.cxx: . . . Matrix *m; m = Matrix::new2x2(1, 2, 3, 4); /* **create a dynamic object /* delete m; . . . } |
The server side of an object creator function calls the constructor for the manager class specified with the [cxx_new] attribute. This class must be derived from the interface class, and therefore, have an is-a relationship with the interface. Remote objects created in this way are registered with the DCE runtime before the object reference is passed back to the client.
Following is an example of an IDL-generated server stub code fragment for a dynamic object creator function.
#include "matrixmgr.h"; . . . idl_long_float v11; idl_long_float v12; idl_long_float v21; idl_long_float v22; Matrix *IDL_function_result; . . . <UNMARSHALL OPERATION ARGUMENTS INTO V11, V12, V21, V22> IDL_function_result = new MatrixMgr( v11, v12, v21, v22 ); <MARSHALL IDL_FUNCTION_RESULT AND RETURN TO CALLER> . . . } |
A client that creates a dynamic remote object can pass the object reference to another client. The information passed to the receiving client allows it to communicate directly with the remote object without the aid of the intermediary client.
All DCE RPC objects support a private reference-counting interface that allows effective management of object references. This interface is accessed from the DCE runtime so that, if a client passes an object reference to another client, the server of the object is notified that a second object reference has been created in the DCE namespace. This allows one of the clients to safely call the C++ delete operation on its copy of the interface pointer without affecting the integrity of the other client's object reference. The remote object remains intact until all clients have relinquished their claim to the object's reference.
Figure 13-4 shows two clients referencing the same object.
Figure 13-4 Reference Counting: Two Clients Referencing the Same Object
One of the goals of object-oriented computing is to disassociate the
programmer from the implementation details of the object. Data is
hidden and the user is allowed to send messages to the object only
through its publicly exported interface. In Object-Oriented RPC, hidden
details include the location at which an object is actually
implemented, and how to forward messages to the object.
13.3.11.1 Intraprocess Location Independence
The generated C++ interface contains pure virtual operations and cannot be instantiated. Therefore, to effectively use the interface the manager class must be derived from it that implements all of the pure virtual functions of the base class. The manager class can be used to instantiate objects while the interface class from which it is derived can be used to communicate with the object. (The manager class is described in Section 13.2.1.3.)
A manager class or a proxy class must provide the underlying support for the interface class. The interface class is the same for either programming model. If the client application developer interacts directly with the interface class, there are no special restrictions placed on the interaction with the object. The polymorphic behavior of the interface class causes the class to forward object messages to either the remote object or the local object automatically.
The following code fragment is an example of local/remote object transparency. It shows two variables of the same type bound to a local and remote object, respectively.
File: client.cxx . . . void print(Matrix *); Matrix *mlocal; Matrix *mremote; /* ** create a local MatrixMgr object and bind an interface to it */ mlocal = new MatrixMgr(1, 2, 3, 4); print(mlocal); /* ** create a remote MatrixMgr object and bind an interface to it */ mremote = Matrix::new2x2(1, 2, 3, 4); print(mremote); . . . |
Clients know the location of the object only by the way in which the
object reference is first obtained. The client might have created the
object by calling the C++ new operation on a manager class or by
invoking one of the several operations provided by the interface to
bind to a remote object.
13.3.11.2 Interprocess Location Independence
Users need not be aware of the location at which the object is implemented within the DCE environment. It does not matter whether the object is implemented in a different process on the same machine or on a different machine altogether. The underlying DCE runtime support hides from the user the tasks of dealing with different machine architectures in a heterogeneous environment.
The location of the object and how to carry out the task of sending a message to it is handled by the DCE runtime. Users invoke the methods defined in the public interface for the object to communicate with it. Object-Oriented RPC allows you to create dynamic remote objects by using an IDL operation associated with the [cxx_new] attribute or a static member function.
The user can take advantage of the auto_handle feature of IDL
to bind to any server supporting the interface or use one of the other
binding handle attributes (explicit_handle or
implicit_handle) to direct where the object is created.
13.3.11.3 Specifying Local Objects as Parameters
Clients can pass local objects to remote procedures as [in] parameters by linking the client application with the server stub and linking the server application with the client stub.
The DCE runtime recognizes that a local object is being passed as a parameter to a remote procedure and automatically registers the process as a server of the object's interface. Linking the server stub into the application allows a remote process to invoke operations in the local process. The remote application must be linked with the client stub in order to carry out remoteness of object method invocations. The distinction between the client and server applications breaks down in this situation, and both processes can be thought of as peers.
If the client stub is not linked into the server application and the server needs to reference a remote object, an rpc_x_no_client_stub exception is raised in the server process and propagated to the client. Similarly, if the server stub is not linked into the client application and the client passes the reference of a local object to a remote process, an rpc_x_no_server_stub exception is raised in the client process. |
Default attributes are used when the DCE runtime registers the process
as an object interface server. For example, the server is registered to
listen on all available protocols. The application developer can also
register the interface explicitly before a local object is passed to a
remote procedure. The runtime will recognize this situation and not
override it.
13.3.12 Offering Multiple Object Implementations of a Common Interface
A manager object must be derived from the interface class and fully implement all the nonstatic operations defined in the IDL definition. In most circumstances, users do not interact with the manager object directly but interact with the interface to the manager object. This allows different manager classes to be available to clients through a common interface type. Users can bind different variables of the same interface type to different object implementations in the same application.
The code fragment in this section uses the same interface for a 2x3 matrix and a 3x2 matrix. The implementations of the two matrices are entirely different. However, the interface to both is the same, allowing the multiply() operation to accept both as parameters (one being identified by the this pointer) and produce a third matrix of size 2x2.
Following is an example of a common interface for different object implementations.
File: matrix.idl [ uuid(C768C260-A5AB-11DB-832C-08002B2A1BCA) ] interface Matrix { . . . Matrix * new2x3( [in] double& v11, [in] double& v12, [in] double& v13, [in] double& v21, [in] double& v22, [in] double& v23 ); Matrix * new3x2( [in] double& v11, [in] double& v12, [in] double& v21, [in] double& v22, [in] double& v31, [in] double& v32 ); Matrix *multiply( [in] Matrix *m ); . . . } File: matrix.acf interface Matrix { /* ** Add the include file to the server stub ** to declare the class definitions for ** matrix2x3 and matrix3x2 */ [sstub] include "matrix2x3", "matrix3x2"; [cxx_new(matrix2x3)] new2x3(); [cxx_new(matrix3x2)] new3x2(); } File: client.cxx: . . . Matrix *m1, *m2, *m3; m1 = Matrix::new2x3( // m1 is an interface to a 2x3 matrix 1, 2, 3 3, 4, 6 ); m2 = Matrix::new3x2( // m2 is an interface to a 3x2 matrix 1, 2, 3, 4, 5, 6 ); m3 = m1->multiply(m2); // m3 is an interface to a 2x2 matrix . . . |
You can enable the C++ reference operator for parameter types in an IDL file compiled with the -lang cxx option. These parameters are passed normally within the application. The DCE runtime is responsible for making the argument value available to the remote procedure. This is carried out by treating reference parameters as pointer variables.
When a reference to a variable is passed by a client to a remote procedure and the remote procedure changes the value of the variable, the new value is reflected in the client's address space when the remote procedure call returns. This is analogous to passing a reference parameter to any C++ function in an application. (See Section 12.3.9 for an example of a dynamic object creator operation.)
This chapter and the next two chapters describe enhancements to the IDL compiler supported by Digital DCE for OpenVMS VAX and OpenVMS Alpha. (The previous chapter describes Object-Oriented RPC, which contains enhancements to the IDL compiler that support the C++ language.)
This chapter describes the following enhancements:
The -standard IDL compiler command option allows you to specify portable or extended features of the OSF DCE. This option is useful when you perform builds.
The standard_type argument specifies what IDL features to enable. If you do not specify this argument, the compiler generates warning messages for all features that are not available in the previous version of OSF DCE.
You can specify one of the following values for the standard_type argument:
portable | Allows only the language features available in OSF DCE. |
dce_v10 | Synonymous with the portable argument. |
dec_v10 | Allows all language features supported by the -standard dce_v10 argument, plus a set of Digital extensions to its products. |
extended | Allows all language features supported in the current version of the compiler. This is the default. |
dce_v11 | Synonymous with the extended argument. |
The following example command line compiles the IDL interface test.idl and enables extended features of the OSF DCE:
% idl test.idl -standard extended |
By default, the OpenVMS DCE IDL compiler does not generate stub auxiliary (AUX) files.
For applications that use certain data types or certain features of RPC, the current OSF DCE IDL compiler generates stub auxiliary files that contain support routines that are called by the stubs. The Digital DCE implementation of the IDL compiler does not need those support routines, and, by default, does not generate stub auxiliary files.
However, if you are porting an RPC application from a platform on which the IDL compiler generates stub auxiliary files, and you do not want to modify your build procedures, you can set the IDL compiler to generate stub auxiliary files. To tell the IDL compiler to generate empty auxiliary files, define the symbol IDL_GEN_AUX_FILES with the following command:
$ IDL_GEN_AUX_FILES:== 1 |
The IDL compiler supports the use of DEC Language-Sensitive Editor (LSE) templates on OpenVMS to help you develop your interfaces more efficiently. LSE is a multilanguage, advanced text editor that enhances program development.
LSE provides the following features:
While writing your program, you can use the COMPILE and REVIEW commands to compile your code and review compilation errors without leaving the editing session. The IDL compiler generates a file of compile-time diagnostic information that LSE uses to review compilation errors.
The LSE COMPILE command issues the appropriate command in a subprocess to invoke the IDL compiler and appends the /DIAGNOSTIC qualifier to the compile command.
The LSE REVIEW command displays any diagnostic messages that result from a compilation. LSE displays the compilation errors in one window, with the corresponding source code displayed in a second window. This multiwindow capability allows you to review your errors while examining the associated source code. This eliminates tedious steps in the error-correction process and helps ensure that you fix all errors before running the compilation process again.
See the LSE documentation for complete information on using LSE.
14.4 Binding Handle Callout
The binding handle callout feature lets you specify a routine that is automatically called from an IDL-generated client stub routine, in order to modify the binding handle.
You can typically use this feature to augment the binding handle with security context, for example, so that authenticated RPC calls are used between client and server.
This feature is particularly targeted at clients that use automatic
binding via the auto_handle ACF attribute. For automatic
binding, it is the client stub rather than the client application code
that obtains a server binding handle. The binding handle q~callout
feature lets you modify binding handles obtained by the client stub.
Without this feature, you cannot modify the binding handles before the
client stub attempts to initiate a remote procedure call to the
selected server.
14.4.1 Attribute Configuration File
To select the binding handle callout feature, create an attribute configuration file (ACF) for the interface (if necessary) and place the binding_callout attribute on the interface. An example follows:
[auto_handle, binding_callout(my_bh_callout)] interface bindcall { } |
The binding_callout attribute has the following general form:
[binding_callout(<IDENTIFIER> )] |
You can specify the binding_callout attribute only once per
interface; it applies to all operations in that interface.
14.4.2 Generated Header File
The IDL-generated header file for the interface contains a function prototype for the binding callout routine. In the previous example, bindcall.h contains a declaration similar to the following declaration:
void my_bh_callout( rpc_binding_handle_t *p_binding, rpc_if_handle_t interface_handle, error_status_t *p_st ); |
Each client stub routine in the IDL-generated client stub module calls
the binding callout routine just before initiating the remote procedure
call to the server. In the previous example, each client stub routine
contains a call to my_bh_callout and passes the three
arguments that are described in the following section.
14.4.4 Binding Callout Routine
The arguments to the binding callout routine are:
Input/Output:
rpc_binding_handle_t *p_binding |
A pointer to a server binding handle for the remote procedure call. Generally, the binding callout routine augments this binding handle with additional context, such as for security.
Input:
rpc_if_handle_t interface_handle |
The interface handle used to resolve a partial binding or for the binding callout routine to distinguish interfaces.
Output:
error_status_t *p_st |
An error status code returned by the binding callout routine.
Previous | Next | Contents | Index |