However, the use of the IDL compiler in this way is a limiting approach for an important subset of applications. The IDL interfaces which a client program can use are determined when the client program is compiled. Hence the client code is limited to using those servers which contain objects that provide the IDL interfaces selected by the client programmer when building an application. Some application programs and tools require that they can use an indeterminate range of interfaces: interfaces which perhaps were not even conceived at the time the applications were developed. Examples include browsers, management support tools and distributed debuggers. It certainly is not desirable to limit a browsing tool to a fixed set of pre-defined interfaces.
OrbixWeb therefore supports a Dynamic Invocation Interface (DII) which allows an application to issue requests for any interface, even if that interface was unknown at the time the application was compiled. OrbixWeb DII support allows invocations to be constructed by specifying at runtime the target object reference, the operation name and the parameters. Such calls are termed "dynamic" because the IDL interfaces used by a program do not have to be "statically" determined at the time the program is designed and implemented.
Note that an OrbixWeb server receiving an incoming invocation request does not knowor carewhether the client which sent the request used the static (that is, use of proxies) or dynamic approach to compose the request.
// IDL
// A bank account.
interface Account {
readonly attribute float balance;
void makeDeposit(in float f);
void makeWithdrawal(in float f);
};
// A factory for bank accounts.
interface Bank {
exception Reject { string reason; };
// Create an account.
Account newAccount(in string owner,
inout float initialBalance) raises (Reject);
// Delete an account.
void deleteAccount(in Account a);
};
To better illustrate the use of the DII, we have extended the newAccount()
operation to take an inout
parameter denoting the initial balance.A programmer can make dynamic invocations by constructing a
Request
object and then invoking an operation on the Request
object to make the request. Class Request
is defined in the OrbixWeb IE.Iona.Orbix2.CORBA
package.In the examples that follow, we create a request for the operation
newAccount()
with the aim of dynamically invoking an operation whose static equivalent is:
// Java _BankRef bRef = ... _AccountRef aRef; aRef = bRef.newAccount("Chris", (float)1000.00);
Request
object.
Request
object with the object reference, details of the name of the operation and the parameters to the operation.
Request
:
// Java // in class Client, // in file Client.java ... IE.Iona.Orbix2.CORBA._ObjectRef bRef = CORBAObject._nil (); IE.Iona.Orbix2.CORBA._ObjectRef aRef = CORBAObject._nil (); // Initialise bRef (omitted here) ... // Create a Request Request r = new Request (bRef, "newAccount"); // Prepare the inout parameter float ioVal = (float) 1000; FloatHolder inoutFloat = new FloatHolder (ioVal); // Add parameters to the Request r.setArgFlags (_CORBA.ARG_IN); r.insertString ("Chris"); r.setArgFlags (_CORBA.ARG_INOUT); r.insertFloat (inoutFloat); // Invoke the Request r.invoke (); // Extract the out and inout parameters r.extractOutParams (); // Extract the return value aRef = r.extractObject (); ...To improve clarity, exception handling code (as described in Chapter 12) is not included in this example or most of the remaining examples in this chapter. However, developers should note that this sample code will not compile without the inclusion of OrbixWeb exception handling.
This example is unrealistic since it assumes we know the name of the operation (newAccount
). In practice, this information would be obtained in some other way, for example from the Interface Repository as explained in Chapter 20, "Interface Repository".
In the remainder of this chapter, we will describe in detail the DII programming steps outlined above. In particular, we will illustrate two alternative approaches to setting up a
Request
: the first is based on the CORBA standard specification of the DII; the second uses a set of Request
parameter insertion and extraction methods (as shown in the above sample code) and is specific to OrbixWeb. 19.2.1 Obtaining an Object Reference
Assume that there is already some server which contains a number of objects that implement the interfaces in section 19.1. The first step in using the DII is to obtain an object reference of interface type _ObjectRef
(defined in package IE.Iona.Orbix2.CORBA
) which references the target object.
// Java import IE.Iona.Orbix2._CORBA; IE.Iona.Orbix2.CORBA._ObjectRef oRef = _CORBA.Orbix.string_to_object (refStr);In this example, the variable
refStr
is a stringified object reference for the target object, perhaps retrieved from a file, a mail message, or an IDL operation call.
Request
object. These are implemented in OrbixWeb as follows:
_request()
is defined in interface _ObjectRef
. It is declared as:
// Java // in package IE.Iona.Orbix2.CORBA, // in interface _ObjectRef public IE.Iona.Orbix2.CORBA.Request _request(String operation);
_create_request()
is also defined in interface _ObjectRef
. It is declared as:
// Java // in package IE.Iona.Orbix2.CORBA, // in interface _ObjectRef public int _create_request( IE.Iona.Orbix2.CORBA.Context ctx, String operation, IE.Iona.Orbix2.CORBA.NVList arg_list, IE.Iona.Orbix2.CORBA.NamedValue result, IE.Iona.Orbix2.CORBA.RequestHolder hrequest, IE.Iona.Orbix2.CORBA.Flags req_flags) throws IE.Iona.Orbix2.CORBA.SystemException;
19.3.2 Setting up a Request using _request()
A request is set up by invoking _request()
on the target object and specifying the name of the operation which is to be dynamically invoked. In our first attempt at constructing the request, we will write the code in a verbose fashion so that the individual steps can be explained easily. We will then show a simpler more compact version of the same code. Request
using the _request()
method:
// Java IE.Iona.Orbix2.CORBA._ObjectRef oRef = IE.Iona.Orbix2._CORBA.Orbix.string_to_object (refStr);
Request
object by calling _request()
on the target object.
IE.Iona.Orbix2.CORBA.Request request = oRef._request("newAccount");
Request
. First obtain an empty NVList
which will contain the parameters to the operation request. The method create_list()
can be invoked on the _CORBA.Orbix
object to create an operation list whose length is specified in the first parameter.
NVList
is a list of NamedValue
elements. A NamedValue
contains a name and a value, where the value is of type Any
and is used in the DII to describe the arguments to a request. The Any
can be obtained using the value()
method defined on class NamedValue
.
NVList
and add the NamedValue
s is:
import IE.Iona.Orbix2._CORBA; import IE.Iona.Orbix2.CORBA.Flags; import IE.Iona.Orbix2.CORBA.NamedValue; import IE.Iona.Orbix2.CORBA.NVList; import IE.Iona.Orbix2.CORBA.NVListHolder; ... NVList argList = request.arguments (); NVListHolder argListHld = new NVListHolder (argList); NamedValue ownerArg, balanceArg; if (_CORBA.Orbix.create_list (2, argListHld)) { ownerArg = argListHld.value.add (new Flags (_CORBA.ARG_IN)); balanceArg = argListHld.value.add (new Flags (_CORBA.ARG_INOUT)); // Fill in name of operation and values of // parameters as shown below ... }
NVList.add()1
creates a NamedValue
and adds it to the NVList
. It returns a NamedValue
pseudo object reference for the newly created NamedValue
.
NVList.add()
can be a Flags
object initialised with one of the following:
_CORBA.ARG_IN
|
Input parameters (IDL's in ).
|
_CORBA.ARG_OUT
|
Output parameters (IDL's out ).
|
_CORBA.ARG_INOUT
|
Input/output parameters (IDL's inout ).
|
NamedValue
s added to the NVList
correspond, in order, to the parameters of the operation. They must be inserted in the correct order. The parameters to a request will be (dynamically) type checked by OrbixWeb on the server's node when the request arrives at the remote object.
Any
contained in each NamedValue
element of the argument list with the value that is to be passed in the operation request.
newAccount()
:
// Insert the value of the parameter // into the Any ownerArg.value().insertString ("Chris");
Any balanceValue = balanceArg.value(); // Insert the value of the parameter // into the Any balanceArg.value().insertFloat ((float)1000.00);
Request.arguments()
which returns the argument list (of type NVList
):
// Java import IE.Iona.Orbix2.CORBA._ObjectRef; import IE.Iona.Orbix2.CORBA.Flags; ... // Obtain an object reference from // string refStr _ObjectRef oRef = _CORBA.Orbix.string_to_object(refStr); // Create a Request object Request request = oRef._request("newAccount"); // Insert the first parameter into the Request (request.arguments().add (new Flags (_CORBA.ARG_IN)) .value()).insertString ("Chris"); // Insert the second parameter: (request.arguments().add (new Flags (_CORBA.ARG_INOUT)) .value()).insertFloat ((float) 1000.00);
// Java
// Send Request and get the outcome
import IE.Iona.Orbix2.CORBA.CORBAException;
...
try {
request.invoke ();
}
catch (CORBAException ce) {
...
}
Note that a Request
invocation can raise both OrbixWeb system exceptions and user-defined exceptions. The OrbixWeb classes SystemException
and UserException
both inherit from class CORBAException
, so this class is specified in the Java catch
clause in order to handle all possible invocation errors.If the invocation throws a user exception of unknown type, then the application will catch a system exception of type
UNKNOWN
.
Request._create_request()
to create a request:
// Java // in package IE.Iona.Orbix2.CORBA, // in interface _ObjectRef public int _create_request( IE.Iona.Orbix2.CORBA.Context ctx, String operation, IE.Iona.Orbix2.CORBA.NVList arg_list, IE.Iona.Orbix2.CORBA.NamedValue result, IE.Iona.Orbix2.CORBA.RequestHolder hrequest, IE.Iona.Orbix2.CORBA.Flags req_flags) throws IE.Iona.Orbix2.CORBA.SystemException;The parameters of this method are as follows:
NVList
).
NamedValue
).
RequestHolder
object which contains the new Request
object.
int
: the method _create_request()
returns zero to indicate failure and one to indicate success.The example shown below constructs a
Request
for operation newAccount()
, passing the parameters "Chris" and 1000.00
as before. The argument list is created as in section 19.3.2 using ORB.create_list()
. We use the compact syntax described in that section to add the arguments to argList
(of type NVList
):
// Java // As before allocate space for an // NVList of length 2 NVList argList = new NVList (); NVListHolder argListHld = new NVListHolder (argList); if (_CORBA.Orbix.create_list(2, argListHld) { // The first parameter to newAccount() (argListHld.value.add (new Flags (_CORBA.ARG_IN)).value()) .insertString ("Chris"); // The second parameter to newAccount() (argListHld.value.add (new Flags (_CORBA.ARG_INOUT)) .value()).insertFloat ((float) 1000.00); // Construct a Request object with // this information NamedValue result = NamedValue._nil (); Request request = Request._nil (); RequestHolder reqHld = new RequestHolder (reqHld); // Call _create_request() with no flags if (oRef._create_request( Context._nil(), "newAccount", argList, result, reqHld, 0)) { ... }
IE.Iona.Orbix2.OperationDef
) from the Interface Repository, then an alternative way to create an NVList
is to call the operation create_operation_list()
on the _CORBA.Orbix
object:
// Java // in package IE.Iona.Orbix2.CORBA, // in class ORB public int create_operation_list ( IE.Iona.Orbix2._OperationDefRef oper, IE.Iona.Orbix2.CORBA.NVListHolder hnew_list);This method returns an
NVList
, stored in the value of the parameter hnew_list
, initialised with the argument descriptions for the operation specified in operation
. The returned NVList
is of the correct length (with one element per argument), and each NamedValue
element of the list has a valid name and valid flags (denoting the argument passing mode). The value (of type Any
) of the NamedValue
has a valid type (denoting the type of the argument). The value of the argument is not filled in.The use of the Interface Repository is described in Chapter 20, "Interface Repository".
19.3.6 Adding a Context Parameter
If the IDL operation has a context
clause then a Context
object can be added to the request using the method ctx()
on class Request
:
// Java // in package IE.Iona.Orbix2.CORBA, // in class Request public void ctx (Context c);
balance
, for example, the operation name should be set to "_get_balance". For example:
// Create a Request to read attribute balance Request r = new Request (target, "_get_balance");In general, for attribute
A
, the operation name should be set to one of the following: |
to read the attribute.
|
|
to write the attribute.
|
out
or inout
parameters, then these parameters would be modified by the call, and no special action is required to access their values. Their values are contained in the NVList
argument list which can be accessed using the method Request.arguments()
.
The operation's return value (if it is not
void
) can be accessed using the method Request.result()
which returns a NamedValue
.19.3.9 Interrogating a Request
The operation name and the target object's object reference of a Request
can be determined using the methods operation()
and target()
, respectively.19.4 Alternative Ways of Using the DII
The OrbixWeb implementation of class Request
contains a set of insertion and extraction methods which can be used to add parameters values to a Request
object before operation invocations and remove results afterwards. We will illustrate the functionality of these methods in this section.19.4.1 Using Insertion Methods to Add Parameters to a Request
Class Request
includes an insertion method for each of the standard IDL types, such as long
, unsigned
long
, float
, Object
, any
, and string
. These insertion methods are named insertLong
, insertULong
, insertFloat
, and so on.Request.insertLong()
is as follows:
// Java // in package IE.Iona.Orbix2.CORBA, // in class Request public void insertLong(int l) throws SystemException;Class
Request
also provides an insert()
method, which supports the insertion of parameters of all user-defined types. Java classes generated for user-defined types inherit from the OrbixWeb class Marshalable
, so the signature for insert()
can be defined as:
// Java // in package IE.Iona.Orbix2.CORBA, // in class Request public void insert(Marshalable m) throws SystemException;The first step in using any of these insertion methods is to obtain an object reference, as described in section 19.2.1. Assume we have an object reference:
// Java IE.Iona.Orbix2.CORBA._ObjectRef oRef = ...The next step is to create a
Request
object, and to use it to define the target object, the operation name, and the parameters of the call. OrbixWeb provides Request
class constructors which simplify the creation of a Request
object. For example:
// Java Request r = new Request (oRef);In this case, the
Request
object is built for a specific target object reference. Alternatively, both the target and the operation name can be specified to the constructor:
// Java Request r = new Request (oRef, "newAccount");All of the parameters (
in
, out
, and inout
) to the request should now be given. The insertion methods do not allow the parameter passing mode to be specified implicitly, so the Request
object stores a flag value which indicates the current insertion mode. This value can be read using the argFlags()
method and set using the setArgFlags()
method.The default parameter attribute mode is
in
. If another parameter passing mode is set using setArgFlags()
, then this changes the parameter attribute mode for all subsequent parameters for this Request
object or until another call to setArgFlags()
is used.To insert the parameters to operation
newAccount()
, the programmer could do the following:
// Java // Prepare the inout parameter float ioVal = (float) 1000; FloatHolder inoutFloat = new FloatHolder (ioVal); // Add parameters to the Request r.setArgFlags (_CORBA.ARG_IN); r.insertString ("Chris"); r.setArgFlags (_CORBA.ARG_INOUT); r.insertFloat (inoutFloat);The parameters must be inserted in the correct order.
Once the parameters are inserted, the request can be made as follows:
// Java r.invoke ();
inout
and out
parameters to be updated. This can be done by calling the Request
method extractOutParams()
as follows:
// Java r.extractOutParams ();The values of the variables passed as
inout
and out
parameters will now be updated.The operation's return value can be extracted from the
Request
object using the appropriate extraction method for the return type. Class Request
provides an extraction method for each of the standard IDL types, called extractLong()
, extractULong()
, extractFloat()
, and so on. The method extractAny()
illustrates the signature format of these methods:
// Java // in package IE.Iona.Orbix2.CORBA, // in class Request public Any extractAny() throws SystemException;Class
Request
also provides an extract()
method, which supports the retrieval of return values of all user-defined types. Java classes generated for user-defined types inherit from the OrbixWeb class Marshalable
, so the signature for extract()
can be defined as:
// Java // in package IE.Iona.Orbix2.CORBA, // in class Request public void extract(Marshalable m) throws SystemException;In our example, the
Account
object returned from operation newAccount()
can be extracted from the Request
as follows:
// Java IE.Iona.Orbix2.CORBA._ObjectRef aRef; aRef = r.extractObject ();Note that this method extracts the
Account
object to type _ObjectRef
. If the IDL generated Account
class is available to the client, this object reference could be narrowed to type _AccountRef
(using method Account._narrow()
).
Request
object, a set of array insertion methods are supplied. The name of each array insertion method is similar to that of the equivalent single-element insert method, with the word Array
appended. For example, the array insertion method for type long
is named insertLongArray()
, while the generic method for the insertion of arrays of user-defined types is insertArray()
.An array insertion method takes the array value and array length as parameters. For example, the signature of
insertLongArray()
is as follows:
// Java // in package IE.Iona.Orbix2.CORBA, // in class Request public void insertLongArray(int l[], int length) throws SystemException;The extraction of array return types from a
Request
is also supported. For this purpose, class Request
includes a set of array extraction methods. These methods include extractLongArray()
, whose signature can be described as:
// Java // in package IE.Iona.Orbix2.CORBA, // in class Request public int extractLongArray(int l[],int length) throws SystemException;Note that each of the array extraction methods takes two parameters: an array of the appropriate type, and the length of the incoming array. The array parameter must be a pre-allocated array of sufficient length to hold the return value. If the length parameter is less than the length of the array value, OrbixWeb will throw a system exception of type
BAD_PARAM
. The extraction method returns the actual length of the array extracted.
Request
objects for each invocation. However, OrbixWeb provides the method reset()
, which allows a Request
variable to be reused.The method
reset()
is called on the Request
object and clears all of the Request
fields, including its target object and operation name. For example, the Request
variable r
in our example could be reused for an invocation of operation makeDeposit()
as follows:
// Java r.reset (); r.setTarget (oRef); r.setOperation ("makeDeposit");or as follows:
// Java r.reset (oRef, "makeDeposit");
invoke()
operation on a Request
, OrbixWeb supports a deferred synchronous invocation mode. This allows clients to invoke on a target object and to continue processing in parallel with the invoked operation. At a later point, the client can check to see if a response is available, and if so can obtain the response. This may be useful to improve the throughput of a client, particularly in the case of long-running invocations. To use this invocation mode, a request should be invoked in one of the following two ways:
send_deferred()
can be called on the Request
.
poll_response()
on the Request
to determine whether the operation has completed and get_response()
to determine the result.
send_oneway()
can be called on the Request
. This method must be used when the operation is a oneway
operation.
CORBA.Orbix
object. These are: ORB.se
nd_multiple_requests_oneway()
and ORB.send_multiple_requests_deferred()
.
// Java // in package IE.Iona.Orbix2.CORBA, // in class Request. public boolean inReplyPostMarshal() throws IE.Iona.Orbix2.CORBA.SystemException;It is important that this method is called on the
Request
object after all parameter values and the return value have been extracted.
NVList
, also provides a method add_value()
which takes three parameters: the name of the NamedValue
(that is, the name of the formal parameter in the IDL operation); the value, of type Any
of the NamedValue
; and a flag indicating the mode of the parameter. For example:
NamedValue ownerArg = argListHld.value.add_value("owner", ownerValue, new Flags (_CORBA.ARG_IN)); NamedValue balanceArg = argListHld.value.add_value("initialBalance", balanceValue, new Flags (_CORBA.ARG_INOUT));