Section G.10. Interface Declarations


G.10. Interface Declarations

An IDL interface is just a collection of data attributes and methods that define the semantics of the interface. Declaring an interface is another way to create a new data type in IDL, but unlike structs and unions, an interface can have both data members and methods that can be called on objects of its type. An interface is also a name-scoping construct, similar to a module. You can declare an IDL interface and simply include a set of constants that you want associated with that interface name. In this case, you have to specify the interface scope in order to refer to the constants from within other scopes.

An interface consists of the following elements:

 // IDL interface identifier [: inheritance spec] {      interface body }; 

The interface identifier can be any valid IDL identifier. The body of the interface can contain any of the following constructs:

  • A user-defined type (struct, union, typedef, enum)

  • A constant declaration

  • An interface-specific exception declaration

  • Data attributes

  • Methods or operations

We've already seen the syntax for the first three of these items in earlier sections of this IDL overview. They become part of an interface simply by being declared within the braces of the body of the interface. In the next few sections, we'll see how to define interface attributes and methods , and then we'll look at how inheritance of IDL interfaces works.

G.10.1. Attributes

Attributes are data members that belong to interfaces. To readers familiar with JavaBeans, declaring an attribute on an interface is roughly analogous to adding a property to a Java bean. An attribute in an IDL interface indicates that the interface provides some way to read and (in most cases) write the attribute value.

The syntax for declaring an attribute within an interface body is:

 // IDL [readonly] attribute <type spec> <identifier> [, <identifier>, ...]; 

The attribute is signified by the attribute keyword, following by a type specification for the attribute and an identifier name. You can declare multiple attributes of the same type by providing their identifiers in a comma-delimited list after the type specifier:

 // IDL attribute short coord_x, coord_y, coord_z; 

The type specifier can be any valid type, including IDL basic types, other interfaces, and user-defined types previously defined or declared in a typedef. For example:

 // IDL enum ErrorCode { BadValue, DimensionError, Overflow, Underflow };   interface AttrTest {   struct coord {     short x;     short y;   };   attribute ErrorCode lastError;   readonly attribute coord COG;   attribute string name; }; 

The optional readonly keyword can precede the attribute declaration. This indicates that the attribute can only be read externally and not directly written. This typically means that the value of this attribute is set only as a side effect of some other method(s). In our example, the COG attribute may represent the center of gravity of some geometric object, and we'll want that to be recomputed only as the result of other methods that change the geometry of the object.

G.10.2. Methods

Methods (or operations, to use the IDL vernacular) provide a way for remote clients to interact with the objects defined by an interface. A method declaration in IDL is composed of an identifier for the method, the type of data returned by the method, and a list of data arguments or parameters that the method accepts. An IDL method can also (optionally) be declared to use specific call semantics, to possibly raise certain exceptions during its execution, and to accept certain context variables from the client environment.

The syntax of a method declaration within an IDL interface is:

 // IDL [<call semantics>] <return type> <identifier> ([<param>, <param>, ...]) [<exception clause>] [<context clause>]; 

The only required elements in a method declaration are the method identifier and the return type, so an example of the simplest form of method declaration is:

 // IDL boolean doSomething(  ); 

This method simply returns a boolean flag when it is complete. It doesn't accept any arguments, uses the default call semantics, doesn't raise a nonstandard exception, and accepts no context variables from the client environment.

The return type for an IDL method can be any valid type, including user-defined types such as structs and other interfaces. If a method doesn't return any data, then the return type should be declared as void.

The identifier for a method is a valid IDL identifier. In IDL, two methods in the same interface can't have the same identifier (i.e., there is no method overloading, as there is in C++ and Java).

G.10.2.1. Arguments

The arguments to a method on an interface are declared within the parentheses following the method identifier and are separated by commas. The syntax for an individual method argument is:

 <arg direction> <arg type> <identifier> 

The identifier is any valid IDL identifier, and the argument type is any valid IDL type, including user-defined types.

The direction specification indicates whether the argument is passed into the server, returned from the server, or both. The direction specification can have one of three values: in, out, or inout. An argument tagged as in is passed only from the client to the server object. An argument tagged as out is not taken from the client, but its value is set by the server and returned if the method returns successfully. An inout argument is passed in both directions; the object/data from the client is passed to the server, and the server may modify the data and have the updates returned back to the client if the method returns successfully.

For example, suppose we want to change the doSomething( ) method that we just declared, have it pass a directive to the server object in the form of a string, telling it what to do, and have another string passed in as the thing to act on. In this case, we want the first argument (the string directive) to be input-only, and we want the modified object to be passed in and returned in its changed state. So we declare the method as follows:

 boolean doSomething(in string whatToDo, inout string whatToDoItTo); 

If a method raises an exception during its execution, then the values of any out or inout arguments to the method are undefined. They may or may not have been modified by the method before the exception was raised and execution was halted.

G.10.2.2. Exceptions

If a method on an interface can raise any exceptions during its execution, then you have to declare this in IDL by adding a clause to the method declaration that lists all of the exceptions that can be raised by the method. This is similar to the tHRows clause on Java methods. The syntax for the raises clause looks like:

 // IDL raises (<exception type>, <exception type>, ...) 

Every exception that you list in this clause has to be defined earlier.

Every method that you declare on an IDL interface can potentially throw one of the standard ORB exceptions that we mentioned earlier (see Table G-4). You can't list these standard exceptions in the raises clause for your methods.

For example, suppose we define our own BadDirective exception for our doSomething( ) method, which is raised if the client passes in a string directive that the server object doesn't understand. We modify the method declaration to look like the following:

 // IDL boolean doSomething(in string whatToDo, inout string whatToDoItTo)      raises (BadDirective); 

Again, we have to declare the BadDirective exception, and any data it contains, earlier in the IDL file.

G.10.2.3. Context values

IDL supports the concept of a client context, which can contain name/value pairs that describe the client's environment in some way. You might have an authenticated username stored in the client's context, for example. The name of a value is a string, and its value is an Any object. The interface to the context is provided by the IDL Context interface, and a mapping of this interface must be provided in any language-specific binding of the CORBA standard.

You can add a context clause to your method declarations, which indicates which client context variables should be propagated to the server when the method is invoked. The server object can then query these context variables during the execution of the method. The syntax for adding a context clause to your method declaration is:

 // IDL context (<var name>, <var name>, ...) 

Each <var name> is a string literal that names the context variable to be propagated to the server when the method is called.

Suppose that when we invoke our doSomething( ) method we want to be able to log who is making the request. We'll look for a username variable in the client context and assume that it is the authenticated identity of the client. We can specify that this context variable should be included in the method call by adding a context clause to our method declaration:

 // IDL boolean doSomething(in string whatToDo, inout string whatToDoItTo)      raises (BadDirective) context ("username"); 

A Java client might use this method like so:

 // Java // Get our context Context ctx = ORB.get_default_context(  ); // Add our username to the context Any username = new Any(  ); username.insert_string("JimF"); ctx.set_one_value("username", username); // Call the remote method obj.doSomething("anything", "entity"); 

Since we declared the doSomething( ) method to include the username context variable in its invocations, this variable appears in the server's context and can be queried during execution of the method.[3]

[3] Sun's implementation of the Java IDL binding doesn't support context variables. The Context interface is available in the Java IDL API, but context clauses on IDL methods aren't represented in the generated Java code, and no context data is transferred to the server.

You might ask when this context feature can be used as opposed to adding a method argument to the method declaration. We could have just as easily added another string argument to our declaration for the doSomething( ) method:

 boolean doSomething(in string whatToDo, inout string whatToDoItTo,      in string username) raises BadDirective; 

One argument for using context variables is to make things easier on the client when certain data for a method is optional. Rather than including an explicit argument and forcing the user to add a nil value of some kind to the method call (null in Java, for example), you can make the optional data a context variable and the user can choose to set it or not. In most cases, you'll find the context variables used rarely, if at all.

G.10.2.4. Call semantics

If you don't specify any call semantics at the start of your method declaration, then the default semantics are "at most once." This means that if a method call returns with no exceptions, then the method was called a single time on the server object. If an exception is raised, then the method is called at most once (the exception occurred either before the method was invoked or during execution of the method).

You can choose to use alternate call semantics for your method by including a call attribute at the start of your method declaration. In the current CORBA standard, only a single alternative is available, which is called "best-effort" semantics. In this case, whether the method call returns successfully or not, there's no guarantee that the method was actually invoked on the server object. The difference between the default semantics and best-effort semantics is roughly equivalent to the difference between TCP and UDP IP network connections and their handling of data packets.

Specify best-effort call semantics by adding the keyword oneway to the start of your method declaration:

 // IDL oneway void tryToDoSomething(in whatToDo); 

If you specify that a method is oneway, then the return type of the method has to be void, and it can't have any out or inout arguments. The method is effectively called asynchronously, so the client can't synchronously receive return data from the server object.

G.10.3. Interface Inheritance

You can inherit attributes and methods from other IDL interfaces by deriving your interface from them. The syntax for declaring the inheritance of an interface in its header is:

 interface identifier : <parent interface>, <parent interface>, ... { 

The parent interfaces can be any predefined interfaces, in the same module as this interface or in different modules. If the parent interfaces are from other modules, you need to use the :: scope specifier to identify them.

G.10.3.1. Method and attribute inheritance

A derived interface inherits all of the attributes and methods from its parent interfaces. Although IDL allows for multiple inheritance, it's illegal to have two inherited attributes or methods with the same identifier. You also can't declare an attribute or method within your interface with the same name as an inherited attribute or method (i.e., overload a method or attribute). So if you have two interfaces declared as follows:

 // IDL interface A {      boolean f(int float x); }; interface B {      void f(  ); }; 

you can't define a new interface that derives from both of these interfaces, since the definition of the method f( ) is ambiguous. Notice that, unlike C++ and Java, IDL uses only the name for the method as its unique identifier, and not the entire method signature. This rule is a result of IDL's multilanguage support, since some languages may be similarly limited.

G.10.3.2. Constant, type, and exception inheritance

A derived interface also inherits any constants, user-defined types, or exceptions defined in its parent interfaces. They can be referred to in the derived interface as if they had been defined within the interface. If we define the following base interface, for example:

 // IDL interface Server {      exception ServiceInterrupted {};      boolean doSomething(in string what) raises (ServiceInterrupted); }; 

we can use the ServiceInterrupted exception that we defined within the Server interface in another interface by naming its scope:

 // IDL interface PrintServer {      boolean printSomething(in string what)           raises (Server::ServiceInterrupted); }; 

Alternatively, we could derive the PrintServer from the Server interface, and then the exception can be used as if it existed in the PrintServer scope:

 // IDL interface PrintServer : Server {      boolean printSomething(in string what) raises (ServiceInterrupted); }; 

It is legal to define constants, types, and exceptions in a derived interface using the same names as those in its parent interfaces. If you do this, though, you need to refer to them unambiguously in your interface declaration, using fully scoped names if necessary. If you declare your own ServiceInterrupted exception in the PrintServer interface, for example, you need to provide a scope for the exception in the raises clause, in order for the IDL compiler to know which version you're referring to:

 // IDL interface PrintServer : Server {      exception ServiceInterrupted { string printerName; };      boolean printSomething(in string what)           raises (PrintServer::ServiceInterrupted); 

If you don't provide a scope, the IDL compiler throws back an error about ServiceInterrupted being ambiguous.

G.10.3.3. IDL early binding

It's important to realize that IDL does "early binding" of constants, user-defined types, and exceptions as it compiles your IDL. This means that the definition of a constant, type, or exception is bound to a particular reference within an interface as it's encountered in your IDL filenot after all definitions have been examined. If we have the following IDL definitions, for example:

 // IDL struct Coord {      short x;      short y; };   interface GeometricObj {      attribute Coord cog; };   interface GeometricObj3D : GeometricObj {      struct Coord {           short x;           short y;           short z;      };      attribute Coord cog3D; }; 

then the cog attribute in the GeometricObj interface is the global Coord type (with x and y members only), since at the time that the cog attribute is encountered in the IDL file, this is the binding definition for Coord. The GeometricObj3D interface inherits this attribute with this type, but the cog3D attribute declared in the GeometricObj3D interface is the GeometricObj3D::Coord type (with x, y, and z members), since at that point, the Coord struct within the GeometricObj3D scope is defined and is the default reference of the relative Coord type used in the cog3D declaration.

G.10.4. Mapping Interfaces to Java

As you might expect, each interface that you define in IDL is mapped to a public interface in Java. Helper and holder classes are also generated for each interfacethe names of these interfaces are generated using the identifier of the IDL interface, with Helper and Holder appended to it.

The Java interface extends the org.omg.CORBA.Object interface. Any inheritance specification that you provide in your IDL interface is mapped directly to interface inheritance in Java, using extends clauses. So our earlier PrintServer example, inheriting from Server, is mapped into a Java interface that begins with the following:

 // Java public interface GeometricObj3D     extends org.omg.CORBA.Object, GeometricObj { ... 

G.10.4.1. Helper and holder classes

The helper class generated for an interface includes a static narrow( ) method that allows you to safely downcast a CORBA Object reference to a reference of the interface type. If the Object isn't of the expected type, then an org.omg.CORBA.BAD_PARAM exception is thrown. The helper class also includes other static methods that let you read or write objects of the interface type over I/O streams or insert/extract an object of this type from an Any value.

The holder class is used whenever the interface is used as the type for an out or inout method argument. The holder class is responsible for marshaling the contents of the object to the server object for the method call (for inout arguments), and then unmarshaling the (possibly updated) return value. The holder class has a constructor defined that lets you wrap the holder around an existing instance of the original interface, and it has a public value member that lets you access the object argument both before and after the method call. You can see more details on using holder classes in Chapter 14.

G.10.4.2. Attributes

Each attribute that you declare on the IDL interface is mapped into two accessor methods, each with the same name as the attribute. So an attribute declared within an IDL interface as follows:

 // IDL attribute string name; 

is mapped into these two methods on the corresponding Java interface:

 // Java String name(  ); void name(String n); 

If you include the readonly tag on your IDL attribute declaration, the Java interface has only the read accessor method and not the update accessor.

G.10.4.3. Methods

Methods declared on your IDL interface are mapped one-to-one to methods on the Java interface. The return values and any in arguments are mapped directly to their corresponding types in Java. Any out or inout arguments in the IDL method are mapped to their holder classes in the Java method. This includes basic IDL types, which have their own holder classes defined for them in the standard Java mapping. So this IDL method:

 // IDL boolean setPrintServer(in PrintServer server,      out PrintServer previousServer,      out long requestsHandled); 

be mapped into the following Java method on the corresponding interface:

 // Java boolean setPrintServer(PrintServer server,      PrintServerHolder previousServer,      IntHolder requestsHandled); 

Notice that the last argument is declared a long in IDL, which is mapped to int in Java. The IntHolder class is therefore used in the mapped Java method.

To use this method, we have to create holder objects for the output parameters, then check their values after the method call.

 // Java PrintServer newServer = ...; PrintServerHolder prevHolder = new PrintServerHolder(  ); IntHolder numReqHolder = new IntHolder(  ); xxx.setPrintServer(newServer, prevHolder, numReqHolder); int numReq = numReqHolder.value; PrintServer prevServer = prevHolder.value; 

We don't need to initialize the contents of the holders, since they are being used for out parameters. If they are used for inout parameters, we have to initialize their contents at construction time or set their value members directly.

If there is a raises clause on your IDL method declaration, then it is mapped to an equivalent throws clause on the Java method. The context clause and call semantics (oneway) on an IDL method declaration affect only the implementation of the generated Java method, not its signature.



Java Enterprise in a Nutshell
Java Enterprise in a Nutshell (In a Nutshell (OReilly))
ISBN: 0596101422
EAN: 2147483647
Year: 2004
Pages: 269

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net