THE CORBA INTROSPECTION MODEL

Một phần của tài liệu sams java distributed objects (Trang 582 - 593)

In the initial section of this chapter, we looked at how objects describe themselves in a Java environment. Although useful, it’s not the only manner through which dynamic object interaction can be scripted. In this section, we look at technologies made available in a CORBA environment that allow for dynamic interaction with objects.

Note In covering dynamic interaction with objects using JDK technology, the term reflection was used. In this section, the synonym introspection is used. In general, you should not let this shift confuse you at all. For some reason, the CORBA camp and the JDK camp adopted different terms for basically the same technology.

The Interface Repository and Dynamic Interface Invocation

CORBA introspection is made possible through two complementary technologies: the Interface Repository and Dynamic Interface Invocation. In a dynamic environment, data cannot exist in a vacuum; it must be coupled with meta data. The term meta data, although drastically overused, simply refers to data that describes existing data. In the CORBA world, the IDL serves as meta data for all distributed entities. Meta data is stored in an Interface Repository (IR) and can be searched by any remote object. In addition to simply allowing access to the IDL, the IR contains a collection of operations, interfaces, and structs that all aid in describing exactly what the data is.

To create a dynamic interaction script, a remote object binds to the IR, searches out information about a target interface, and then uses Dynamic Interface Invocation (DII) to invoke methods on that remote object. In the following sections on the IR and DII, we first examine interacting with the IR and then look at using DII.

Working with the Interface Repository

The IR is a physical piece of software that must be manually started. Interaction occurs through a collection of interfaces and structs. The interface that provides the initial point

of contact is named Repository, and it contains methods that allow for the searching of interfaces and structs by both name and identifier. Performing a search on the IR returns an instance of the class org.omg.CORBA.Object , which must then be narrowed to any one of a collection of classes that define the entity being returned. These description classes are all named by combining the entity they describe with the suffix “Def.” For example, an interface is described using the class InterfaceDef, a string using StringDef, and a struct using StructDef. The following code snippet demonstrates the process of binding to an IR using the Directory Service and obtaining the

InterfaceDef object associated with the SimpleObjectI interface:

ORB orb = ORB.init();

Repository repository = RepositoryHelper.bind(orb);

InterfaceDef interfaceDef =

InterfaceDefHelper.narrow(repository.lookup("SimpleObjectI"));

In addition to the Repository and xxxinterfaces, three additional entities facilitate the process of discovering information about an entity described in IDL. The first one is the interface TypeCode, which is used to fully describe a CORBA entity. The TypeCode interface has operations that allow for the entity’s name to be discovered and for the repository ID to be discovered. It also has an attribute that’s an instance of the TCKind enum. TCKind, the second important entity, identifies the IDL data type. The third entity encountered is the IDLType interface, which is associated with an IDL element. It simply points to a TypeCode instance. The TypeCode interface is contained in Listing 32.5, and the TCKind enum is contained in Listing 32.6. As you look at the TypeCode interface, pay attention to the name() operation, which obtains the name of the entity, and the kind() operation, which obtains the appropriate TCKind instance.

LISTING 32.5 THE TypeCode INTERFACE DESCRIBES AN ENTITY IN THE CORBA UNIVERSE

interface TypeCode { exception Bounds {};

exception BadKind {};

boolean equal(in CORBA::TypeCode tc);

CORBA::TCKind kind();

CORBA::RepositoryId id() raises(CORBA::TypeCode::BadKind);

CORBA::Identifier name() raises(CORBA::TypeCode::BadKind);

unsigned long member_count()raises(CORBA::TypeCode::BadKind);

CORBA::Identifier member_name(in unsigned long index) raises(CORBA::TypeCode::BadKind, CORBA::TypeCode::Bounds);

CORBA::TypeCode member_type(in unsigned long index) raises(CORBA::TypeCode::BadKind, CORBA::TypeCode::Bounds);

any member_label(in unsigned long index) raises(CORBA::TypeCode::BadKind, CORBA::TypeCode::Bounds);

CORBA::TypeCode discriminator_type() raises(CORBA::TypeCode::BadKind);

long default_index() raises(CORBA::TypeCode::BadKind);

unsigned long length() raises(CORBA::TypeCode::BadKind);

CORBA::TypeCode content_type() raises(CORBA::TypeCode::BadKind);

long param_count();

any parameter(in long index) raises(CORBA::TypeCode::Bounds);

};

Looking at the TCKind enum in Listing 32.6, you’ll note a unique entry for each available CORBA entity. This allows a TCKind instance to accurately describe any available IDL entity.

LISTING 32.6 THE TCKind enum IDENTIFIES THE IDL TYPE ASSOCIATED WITH AN ENTITY

enum TCKind {

tk_null, tk_void,

tk_short, tk_long, tk_ushort, tk_ulong, tk_float, tk_double, tk_boolean, tk_char,

tk_octet, tk_any, tk_TypeCode, tk_Principal, tk_objref, tk_struct, tk_union, tk_enum, tk_string,

tk_sequence, tk_array, tk_alias, tk_except, tk_longlong, tk_ulonglong, tk_longdouble, tk_wchar, tk_wstring, tk_fixed

};

Now that you have a basic overview of the IR, we’ll write a small application that binds to an IR and obtains the definition of two specific entities. Then, after we cover DII, the example will be extended to include a dynamic invocation script.

To begin the example, Listing 32.7 contains the IDL for a basic interface and struct. This is the IDL for which we’ll query the IR.

LISTING 32.7 THE IDL DEFINITIONS FOR A BASIC INTERFACE AND STRUCT

interface SimpleObjectI { string getStringValue();

void setStringValue(in string sValue);

long getIntegerValue();

void setIntegerValue(in long iValue);

};

struct SimpleStructS { string sValue;

long iValue;

};

Given the IDL in Listing 32.7, Listing 32.8 contains the code for the IR interaction

application. Before diving into the discussion of this application, spend some time with the code. When you’re ready, you can continue reading.

LISTING 32.8 THE IRBrowser CLASS PRINTS OUT THE DETAILS OF THE IDL DESCRIBED IN LISTING 32.7 BY BINDING TO THE IR

import org.omg.CORBA.*;

import org.omg.CORBA.InterfaceDefPackage.*;

public final class IRBrowser {

public IRBrowser() {

ORB orb = ORB.init();

Repository repository = RepositoryHelper.bind(orb);

InterfaceDef interfaceDef =

InterfaceDefHelper.narrow(repository.lookup("SimpleObjectI"));

printInterfaceDetails(interfaceDef);

StructDef structDef =

StructDefHelper.narrow(repository.lookup("SimpleStructS"));

printStructDetails(structDef);

} /**

* Prints details on a unique interface */

private void printInterfaceDetails(InterfaceDef interfaceDef) {

// obtain the interface description FullInterfaceDescription desc = interfaceDef.describe_interface();

// print basic details

System.out.println("---");

System.out.println("Interface Name: "+desc.name);

System.out.println("Interface Type: "+desc.type);

System.out.println("Interface Kind: "+desc.type.kind());

// obtain and print operation details

OperationDescription[] oppDefs = desc.operations;

for(int i=0; i<oppDefs.length; i++) {

System.out.println("Interface Operation "+

(i+1)+": "+oppDefs[i].name);

}

// obtain and print attribute details

AttributeDescription[] attDefs = desc.attributes;

for(int i=0; i<attDefs.length; i++) {

System.out.println("Interface Attribute "+

(i+1)+": "+attDefs[i].name);

}

} /**

* Prints details on a unique struct

*/

private void printStructDetails(StructDef structDef) { // obtain references to all members

StructMember[] members = structDef.members();

// print details on each member

System.out.println("---");

for(int i=0;i<members.length; i++) {

System.out.println("Member "+(i+1)+" Type::

"+members[i].type);

System.out.println("Member "+(i+1)+" Kind:: "+

members[i].type.kind());

} }

public static void main(String[] args)) { IRBrowser browser = new IRBrowser();

} }

As you examine the IRBrowser code in Listing 32.8, the first section you’ll want to concentrate on is contained in the constructor. The first task performed is the process of binding to the IR. Once the IR reference is obtained, the code performs two lookup interactions. The first obtains a reference to the InterfaceDef object describing the SimpleObjectI interface:

InterfaceDef interfaceDef =

InterfaceDefHelper.narrow(repository.lookup("SimpleObjectI"));

The second lookup interaction obtains a reference to the StructDef object describing the SimpleStructS struct:

StructDef structDef =

StructDefHelper.narrow(repository.lookup("SimpleStructS"));

Once each xxxDef object is obtained, a helper method is called that’s charged with printing out the contents of its parameter. The first helper method,

printInterfaceDetails() , is passed an InterfaceDef instance and displays the name, type, and kind attributes associated with the instance. The method then continues on to print the name of each operation and attribute. For the most part, this method is rather self-explanatory; however, it does introduce two new classes. The first, OperationDescription , describes a single operation. The section on DII extends the coverage of this class. Note, however, the manner in which it’s used to obtain the operation name. The second class introduced in this method is

AttributeDescription , which is used to describe a single attribute. Again, note the manner in which the attribute name is obtained.

The second helper method, printStructDetails() , is passed a StructDef instance and displays information on each struct member. In general, there’s less

information made available about structs when compared to interfaces; however, this can be complemented with information made available by Java reflection. In the

printStructDetails() method, we first obtain an array of StructMember objects that each contain details on a single member in the struct. We then iterate through this array displaying the type and kind of each member.

Note If you haven’t done so already, you’ll need to install the Inprise ORB from the enclosed CD-ROM to run the DII examples in this chapter.

To run this application, first start the ORB by entering osagent

at a prompt. Then start the IR by entering irep IRName

at a prompt. irep is the name of the Inprise IR executable, and IRName can be replaced with any logical name that the IR might use. Once the IR is running, select File from the IR main menu; then, from the drop-down list, select Load and use the dialog box to navigate to the location of the IDL file. Finally, execute the IRBrowser application by entering

java IRBrowser at a prompt.

Using Dynamic Interface Invocation

Thus far, you’ve seen how it’s possible to interact with the IR and obtain information on both interfaces and structs. We’ll now further explore dynamic object interaction by looking at how it’s possible to dynamically invoke operations on discovered objects.

As you may have guessed, the process of dynamically invoking an operation introduces a plethora of new CORBA entities. The principal one being an interface called Request. A Request object is used to issue a dynamic operation invocation and is complemented by the following entities: NVList, InterfaceDef, FullInterfaceDescription , OperationDescription , ParameterDescription , TCKind, and Any . As luck has it, many of the classes used to support DII were introduced either in the IR section or in earlier chapters. Coverage of them in this chapter builds on earlier knowledge, thus forming a complete understanding of the entity.

Beginning our exploration of the DII entities, Listing 32.9 contains the IDL for the

Request interface. The interface exposes a few key operations and attributes, including arguments, which obtains the collection of parameters; invoke(), which invokes the represented operation; and return_value() , which obtains the operation return value.

LISTING 32.9 IDL DEFINITION FOR THE Request INTERFACE

interface Request {

readonly attribute CORBA::Object target;

readonly attribute CORBA::Identifier operation;

readonly attribute CORBA::NVList arguments;

readonly attribute CORBA::NamedValue result;

readonly attribute CORBA::Environment env;

readonly attribute CORBA::ExceptionList exceptions;

readonly attribute CORBA::ContextList contexts;

attribute CORBA::Context ctx;

any add_in_arg();

any add_named_in_arg(in string name);

any add_inout_arg();

any add_named_inout_arg(in string name);

any add_out_arg();

any add_named_out_arg(in string name);

void set_return_type(in ::CORBA::TypeCode tc);

any return_value();

void invoke();

void send_oneway();

void send_deferred();

void get_response();

boolean poll_response();

};

As you’ve probably noticed, the attribute arguments is of type NVList, which is another of the important DII entities. This interface, defined in Listing 32.10, represents a collection of name/value pairs, each representing a unique parameter instance. The workflow for using an NVList instance is to obtain it from the Request object and invoke the add_value() operation once for each candidate parameter.

LISTING 32.10 IDL FOR THE NVList INTERFACE

interface NVList {

unsigned long count();

void add(in CORBA::Flags flags);

void add_item(in CORBA::Identifier name,in CORBA::Flags flags);

void add_value(in CORBA::Identifier name,in any value, in CORBA::Flags flags);

CORBA::NamedValue item(in unsigned long index);

void remove(in unsigned long index);

};

Once a Request objectis populated with the proper parameters, it can be invoked in any one of three manners. The first being simply invoking the invoke() operation, which causes the client to block until the server object returns a return value. The second manner is used when the process is going to take a long time. This manner uses the send_deferred() operation, which is nonblocking. Once invoked, the

send_deferred() operation will return control to the client while waiting for the return value. A client can check to see if this value is ready by invoking the poll_response() operation, which returns a true value when the value is ready. A third manner in which the operation represented by the Request object can be invoked is via the

send_oneway() operation, which immediately returns but does not wait for a return value at all. This method is very useful when invoking operations with void return values that can be performed asynchronously.

With an understanding of the manner in which DII functions, you’re now ready to begin writing some code. Listing 32.11 contains the IDL definition for an interface called SimpleObjectI and also for an interface called ObjectServerI. An

ObjectServerI instance is simply an object factory that serves empty SimpleObjectI instances.

LISTING 32.11 THE IDL THAT IS TOBE DYNAMICALLY INVOKED

interface SimpleObjectI { string getStringValue();

void setStringValue(in string sValue);

long getIntegerValue();

void setIntegerValue(in long iValue);

};

interface ObjectServerI {

SimpleObjectI obtainSimpleObject();

};

Moving from the IDL to the class that actually invokes the operations, Listing 32.12 contains the code for the DIIInvoker class.

LISTING 32.12 THE DIIInvoker CLASS DYNAMICALLY INVOKES OPERATIONS

import org.omg.CORBA.*;

import org.omg.CORBA.InterfaceDefPackage.*;

public class DIIInvoker { private ORB _orb;

private BOA _boa;

private Repository _repository;

public DIIInvoker() { _orb = ORB.init();

_boa = _orb.BOA_init();

_repository = RepositoryHelper.bind(_orb);

// bind to the object server ObjectServerI objectServer = ObjectServerIHelper.bind(_orb);

// obtain a SimpleObjectI instance

SimpleObjectI si = objectServer.obtainSimpleObject();

// invoke all operations, twice processInterface(si);

processInterface(si);

} /**

* Dynamically invokes all operations in a given interface */

private void processInterface(org.omg.CORBA.Object targetObject) {

// obtain the interface description from the target object

InterfaceDef interfaceDef = targetObject._get_interface();

FullInterfaceDescription desc = interfaceDef.describe_interface();

// obtain descriptions of all operations

OperationDescription[] oppDefs = desc.operations;

int iOppCount = oppDefs.length;

for(int i=0; i<iOppCount; i++) {

OperationDescription descActive = oppDefs[i];

Request request =

targetObject._request(descActive.name);

// obtain the arg list

NVList args = request.arguments();

// obtain descriptions of all parameters ParameterDescription[] parameters = descActive.parameters;

int iParamLength = parameters.length;

// iterate through the params adding to the NVList instance

for(int j=0; j<iParamLength; j++) {

ParameterDescription paramActive = parameters[j];

// based on the kind attribute populate the Any.

Note

// that to conserve space,

// only support for strings and longs

// is shown.

TCKind kind = paramActive.type.kind();

if(kind = TCKind.tk_string) { Any any = _orb.create_any();

any.insert_string("Some New Value");

args.add_value(paramActive.name, any,

org.omg.CORBA.ARG_IN.value);

}

else if(kind = TCKind.tk_long) { Any any = _orb.create_any();

any.insert_long(42);

args.add_value(paramActive.name, any,

org.omg.CORBA.ARG_IN.value);

}

}

// set the return type

request.set_return_type(descActive.result);

// invoke the operation request.invoke();

// display the results

System.out.println(request.result().value());

} }

public static void main(String[] args)) { DIIInvoker invoker = new DIIInvoker();

} }

As you study the DIIInvoker class, you’ll want to concentrate on the code contained in the processInterface() method. This method is passed a SimpleObjectI instance (obtained from the ObjectServerI factory) and proceeds to invoke all

operations. In the first step in this process, highlighted next, the InterfaceDef object is obtained from the SimpleObjectI instance. As you’ll remember from the earlier section on the IR, this object exists in the IR itself and can fully describe an interface. The InterfaceDef instance is obtained by invoking the _get_interface() operation, which all interfaces inherit from org.omg.CORBA.Object . Once the InterfaceDef instance is obtained, the FullInterfaceDescription instance is obtained and then an array of OperationDescription objects is obtained. This array of

OperationDescription objects contains a single entry for each operation represented by the interface.

InterfaceDef interfaceDef = targetObject._get_interface();

FullInterfaceDescription desc = interfaceDef.describe_interface();

OperationDescription[] oppDefs = desc.operations;

Once the array of OperationDescription objects has been obtained, the code enters a loop that first forms and then invokes a Request object for each entry in the array.

In the next section of the code a Request instance is obtained from the SimpleObjectI instance by specifying the name of the active operation as a

parameter. From the Request, we obtain the NVList instance into which we’ll place a unique entry for each parameter. Finally, we obtain an array of

ParameterDescription objects where each entry represents a unique parameter in the active operation. This next section of code is pulled out and highlighted here:

Request request = targetObject._request(descActive.name);

NVList args = request.arguments();

ParameterDescription[] parameters = descActive.parameters;

While still inside the main operation loop, we now enter an inner loop that iterates through each ParameterDescription object. In this section, we examine each parameter and, based on the value of its TCKind attribute, add an entry into the NVList object. In the following code snippet is a section of this inner loop where parameters of the IDL type string are handled. The code also supports the IDL type long (however, to conserve space, no others are shown):

TCKind kind = paramActive.type.kind();

if(kind = TCKind.tk_string) { Any any = _orb.create_any();

any.insert_string("Some New Value");

args.add_value(paramActive.name, any, org.omg.CORBA.ARG_IN.value);

}

After the NVList object is fully populated, all that’s left to do is set the return type, invoke the operation, and query for the results. This section of code is shown here:

request.set_return_type(descActive.result);

request.invoke();

System.out.println(request.result().value());

Now that you’ve stepped through the code, you’re ready to cement your understanding by executing the application. The one piece of code not shown in this chapter is for the ObjectServerI implementation. This code is rather trivial and was placed on the CD- ROM to conserve trees.

The first step in running the application is to fire up the ORB and IR and then load in the IDL used in the example. If you skipped over the IRBrowser example, flip back a few pages for coverage on starting up the first three services. To run this application, first start the ObjectServerI implementation by entering

java ObjectServer

at a command prompt. Now execute the DII example by entering java DIIInvoker

at another prompt. You;ll note that the SimpleObjectI instance has its methods invoked two times. This is done to show that the getter method invocations are actually functioning properly.

As the section on DII has shown, it’s just much easier to statically invoke an operation on an object. Therefore, the obvious question is, Why would one ever use DII? After all, the process of creating a Request object is rather laborious and adds a lot to execution time.

DII may in fact not be needed in every application you develop, but in situations where the remote object class is not always known, or where the execution path is rather vague, DII will come to the rescue. Consider, for example, a Web-based system where HTTP-POST arguments invoke IDL operations. Because HTML or JavaScript cannot directly execute IDL, you could embed the method invocation chain in a form argument, parse it out at the server, and dynamically invoke the specified methods.

FROM HERE

This chapter covered a lot of ground, potentially changing the manner in which you think about object interaction. Historically, you would obtain an object and, at compile time, know the operations that are being invoked on that object. Through the use of Java reflection or CORBA introspection and DII, it’s now possible to decide on execution paths at runtime as well as introduce new objects at runtime.

As you continue to explore the world of CORBA, you’ll find that the following chapters provide complementary material to this chapter’s topics:

• Chapter 21, “CORBA Overview”

• Chapter 22, “CORBA Architecture”

• Chapter 28, “The Portable Object Adapter (POA)”

Chapter 33: Other CORBA Facilities and

Một phần của tài liệu sams java distributed objects (Trang 582 - 593)

Tải bản đầy đủ (PDF)

(693 trang)