[Previous] [Contents] [Next] [IONA Technologies]


Chapter 6
Programming OrbixWeb using Java


Contents

6.1 Files Generated by the IDL Compiler
6.2 A Client Program
6.2.1 Object Location
6.2.2 Binding
6.2.3 Remote Invocations
6.3 The Server: Implementing Interfaces
6.3.1 BOAImpl Approach
6.3.2 TIE Approach
6.4 The Server: Coding the Implementation Classes
6.4.1 The BOAImpl Approach
6.4.2 Outline of the Bank Class Implementation (BOAImpl Approach)
6.4.3 The TIE Approach
6.4.4 Outline of the Bank Class Implementation (TIE Approach)
6.5 The Server: main() Method And Object Creation
6.5.1 The BOAImpl Approach
6.5.2 The TIE Approach
6.5.3 Initialisation and impl_is_ready()
6.5.4 Construction and Markers
6.6 Registration and Activation
6.7 Execution Trace
6.8 Comparison of the TIE and BOAImpl Approaches
6.8.1 Providing Different Implementations of the Same Interface
6.8.2 Providing Different Interfaces to the Same Implementation
6.9 An Example of Using Holder Classes



This chapter gives a detailed example of using OrbixWeb to write a a distributed application. Readers are advised to first read Part I "Getting Started", although this chapter is self-contained and presents details not covered Part I.

In this chapter, we will implement a simple banking application. In particular, we wish to create a server to administer and manage bank account objects. The functionality we require, for the time being, is defined by the following IDL interface definitions:

On the server side, the overall aim will be to create a server that contains a single Bank object that will accept operation calls such as newAccount() from clients. In this example, all of the objects (both Bank and Account objects) will be in a single server; although in a real system several servers may be used and objects may also exist in clients. The server in our example will be called BankSrv. Note that a server can, without difficulty, manage objects of two different interfaces.

The remainder of this chapter will cover the following programming steps:

We will also examine an alternative definition for the operation newAccount(), in order to illustrate the use of Holder classes.

In subsequent chapters, we will add further functionality to the Account and Bank interfaces defined above; for the time being, these basic interfaces are sufficient to illustrate the main points. Later chapters will allow operations to raise user-defined exceptions and add a further interface defined using inheritance.

6.1 Files Generated by the IDL Compiler

We will assume that the above IDL source file is compiled using the following IDL compiler command:

The Java code produced by the IDL compiler will be generated within the idl_demo package, which will help to avoid clashes in the global Java name space.

Once the IDL source file, bank.idl, is compiled, a number of Java source files are generated. By default, all generated files will be created in a subdirectory called java_output. In our example, the -jP compiler switch results in the creation of these files in an idl_demo directory (which is a subdirectory of java_output), in compliance with the Java convention of mapping package names to subdirectories.

For each interface defined in bank.idl (that is, interface Account and interface Bank), the IDL compiler generates seven Java source files. Each source file contains a single Java type with a specific function. For example, the following types are generated for interface Account:


     _AccountRef
    
A Java interface. The methods of this interface define the Java client view of the IDL Account interface.

     Account
    
A Java class which implements interface _AccountRef. The methods of this class provide the client with proxy functionality for the operations of the IDL interface Account.

     _AccountHolder
    
A Java class which defines a Holder type for class Account. This is required for passing Account objects as inout or out parameters to and from IDL operations.

     _AccountOperations
    
A Java interface which defines the methods which must be implemented by a server class in order to support the IDL interface Account.

     _boaimpl_Account
    
An abstract Java class which allows server-side developers to implement the Account interface using the BOAImpl approach.

     _tie_Account
    
A Java class which allows server-side developers to implement the Account interface using the TIE approach.

     _dispatcher_Account
    
A Java class used internally by OrbixWeb to dispatch incoming server requests to Account implementation objects. Application developers do not require an understanding of this class.

It is essential that the programmer understands the IDL to Java mapping, and thus understands the Java types generated by the IDL compiler. These correspond directly to the interfaces defined in the IDL source. A description of the IDL to Java mapping was presented in Chapter 5, "IDL to Java Mapping".

The Holder types generated by the IDL compiler are not required in our first example, as the IDL definition includes no operations which pass Account or Bank objects as inout or out parameters. In section 6.9, we will examine a modified version of this IDL definition, in order to illustrate the use of these Holder types.

6.2 A Client Program

From the point of view of the client, the functionality provided by the banking service is defined by the IDL interface definitions. A typical client program locates a remote object, obtains a reference (binds) to the object and then invokes operations on the object.

These three concepts, object location, binding to remote objects, and remote invocations, are important concepts in distributed systems:

The three concepts of object location, binding and remote invocations are illustrated in the following application code to use the banking service:

The Java class Bank is generated by the IDL compiler. The static method Bank._bind() requests OrbixWeb to search for an object offering the Bank IDL interface. The IDL compiler generates three overloaded _bind() methods for each IDL interface. In the case of the Bank interface, these methods are defined as follows:

Detailed information on the parameters to _bind() is provided in Chapter 7, "Publishing Object References in Servers".

6.2.1 Object Location

The client does not specify a host at which to contact the server. As a result, OrbixWeb will make an implicit call to the default locator in order to find a host on which the required server has been registered. Chapter 24, "Locators", describes the functionality of the OrbixWeb locator mechanism.

The OrbixWeb _bind() methods provide two alternatives to the default locator when attempting to locate a host on which a given server resides: the first is to override the default locator with a user-defined locator implementation (as described in Chapter 24, "Locators"); the second is to locate the server host in advance of a call to _bind() and then specify the known host name to the _bind() method (as described in Chapter 7, "Publishing Object References in Servers").

The parameter markerServer allows the object marker and server name to be specified in the _bind() call. In our example, no object marker is specified, so OrbixWeb is free to select any available object which matches the remaining location parameters. The markerServer value ":BankSrv" instructs OrbixWeb to search for the required object in the BankSrv server.

6.2.2 Binding

Although OrbixWeb supports collocation of client and server in a single address space, in our example the client and server applications are distributed. Consequently, the call to _bind() in our client will construct a proxy for the specified object. The _bind() method returns a reference to the proxy object of type _BankRef. The Java methods of this reference type define the client's view of the Bank IDL interface.

There are two circumstances in which this _bind() call will not create a proxy for the specified object: if a proxy for the object already exists in the client address space, this existing proxy will be returned, and if a system exception is thrown during the _bind() call then the return value will be undefined.

Calling _bind() is not always required before communicating with a particular object: if a call to another object returns a reference to a remote object, then this will result in the creation of a proxy for it. For example, operation newAccount() returns a reference to an Account object.

We will discuss the OrbixWeb mechanism of binding client references to server objects in detail in Chapter 8, "Retrieving Object References in Clients".

6.2.3 Remote Invocations

The proxy object reference returned by _bind() provides access to remote Bank operations via the Java methods defined on interface _BankRef. The client can invoke these operations by simply calling the equivalent Java methods on the proxy object. The proxy is responsible for forwarding the invocation requests to the target server implementation object and returning results to the client.

The Java types _AccountRef and _BankRef are generated by the IDL compiler. These interfaces define the Java client view of the IDL Account and Bank interfaces.

The code for _AccountRef is as follows:

The code for _BankRef is:

Both Ref types inherit from the Java interface _ObjectRef. This is an OrbixWeb interface which defines functionality common to all IDL object reference types. Information on this extra functionality can be found in Part II, "Class and Interface Reference" of the OrbixWeb Reference Guide.

6.3 The Server: Implementing Interfaces

To implement an interface, a Java programmer must provide a Java class (in OrbixWeb terminology, an implementation class) which has a method definition for each of the operations and attributes of the interface. An implementation class must provide (at least) the operations defined in the IDL interface which it is declared to implement.

The required signatures for the methods of the implementation class are defined in a Java interface generated by the IDL compiler. The name of this Java interface is a string of the following format:

For example, the Java interface _AccountOperations defines the methods which must by provided by an implementation class for IDL interface Account. Interface _AccountOperations is generated by the IDL compiler as follows:

OrbixWeb supports two mechanisms for relating an implementation class to its IDL interface: the BOAImpl approach and the TIE approach. These will be discussed in turn in section 6.3.1 and section 6.3.2. Most server programmers use one of these approaches exclusively, but there is no difficulty mixing them in the same server.

The choice of implementation method in an OrbixWeb server does not affect the coding of client applications.

6.3.1 BOAImpl Approach

For each IDL interface, OrbixWeb generates an abstract Java class, using the string _boaimpl_ appended with the name of the interface. For example, it generates the class _boaimpl_Account for the IDL interface Account. To indicate that a Java class implements a given IDL interface, that class should inherit from the corresponding BOAImpl-class. This approach is termed the BOAImpl approach.

Each BOAImpl-class implements the corresponding Java Operations interface; for example class _boaimpl_Account implements the Java interface _AccountOperations. In this way, an implementation class which inherits and implements the abstract methods of a BOAImpl-class must implement the operations of the corresponding IDL interface.

The BOAImpl approach is shown in Figure 6.1 for the Account IDL interface. The OrbixWeb IDL compiler produces the Java interface _AccountOperations and the Java classes Account and AccountBOAImpl. The programmer defines a new class, class AccountImplementation, to implement the operations and attributes defined in the IDL interface. In addition to having methods that correspond to IDL operations and attributes, class AccountImplementation can have user defined constructors and additional members.


Figure 6.1: The BOAImpl approach to define an implementation class

In this chapter and throughout this guide, we use the convention that interface A is implemented by class AImplementation. This naming scheme does not have to be adhered to–indeed some applications may need to implement interface A a number of times.

6.3.2 TIE Approach

In the second approach, the programmer can implement the IDL operations and attributes in a class that does not inherit from the automatically generated BOAImpl-class. Instead, the programmer uses an automatically generated Java class to tie together the implementation class and the IDL interface. The second approach is termed the TIE approach. (For a comparison of the BOAImpl and TIE approaches, see section 6.8.)

To support the TIE approach, the IDL compiler generates a Java TIE-class for each IDL interface, using the string _tie_ appended with the name of the interface. An object which implements the IDL interface can be passed as a parameter to the constructor for the TIE class. To tie a Java implementation object to an IDL interface, the programmer creates a TIE object for the interface and passes an implementation object to the TIE constructor.

For example, the IDL compiler generates the TIE-class _tie_Account for the IDL interface type Account. The programmer defines a new class, class AccountImplementation, to implement the operations and attributes defined in the IDL interface. This class need not inherit from any automatically generated class, but it must implement the Java interface _AccountOperations. The programmer instantiates an object of type _tie_Account, passing an object of type AccountImplementation to the constructor. A TIE object will be created which will delegate incoming operation invocations to the methods of the programmer's AccountImplementation object.

This approach is illustrated in Figure 6.2.


Figure 6.2: Use of TIE to define an implementation class

6.4 The Server: Coding the Implementation Classes

In this section, we show a very simple implementation of the banking application illustrating both the BOAImpl and TIE approaches. These will be discussed in turn with separate examples of each, although both approaches can be mixed in the same server. We have ignored any error checking–for example placing the account into "the red"–which would be necessary in a full banking application.

We will code two implementation classes:


BankImplementation implements the Bank interface.

AccountImplementation implements the Account interface.

Note that client programmers need not be concerned with which approach to interface implementation is chosen by an implementer.

6.4.1 The BOAImpl Approach

We indicate that a class implements a specific IDL interface by inheriting from the corresponding BOAImpl-class generated by the IDL compiler. Class AccountImplementation could be written as follows:

The code for class BankImplementation is outlined below:

Classes AccountImplementation and BankImplementation redefine each of the abstract methods inherited from their respective BOAImpl classes; they may add constructors, methods and member variables.

The accounts managed by a bank are stored in a list of type java.util.Vector. This allows a BankImplementation object to manage existing accounts.

6.4.2 Outline of the Bank Class Implementation (BOAImpl Approach)

First, note that in BankImplementation.newAccount(), we will need to construct a new AccountImplementation object. The method newAccount() corresponds to an IDL operation, and its return value is of type _AccountRef:

Each BOAImpl-class generated by the IDL compiler can raise an OrbixWeb system exception in the class constructor. The class AccountImplementation inherits from _boaimpl_Account, so it is therefore necessary to enclose the construction of a new AccountImplementation object in a Java try statement, in order to catch possible system exceptions.

The method record() simply adds the new AccountImplementation object to the list of existing accounts:

The method deleteAccount() corresponds to an IDL operation, and it simply removes a specified account from the list of AccountImplementation objects:

In the code shown above, we have chosen to ignore the possibility of making the server objects persistent. This can be done by storing the account and bank data in files or in a database.

6.4.3 The TIE Approach

Using the TIE approach, an implementation class does not have to inherit from any particular base class. Instead, the implementation class must simply implement the Java Operations interface which was generated by the IDL compiler from the IDL interface definition. We notify OrbixWeb that this class implements that IDL interface by creating an object of the TIE-class, which was also generated by the IDL compiler.

The code for class AccountImplementation can be outlined as follows:

A _tie_Account object could be instantiated in the following way:

The TIE approach does not place an inheritance requirement on an implementation class. This leaves the implementation programmer free to choose the inheritance hierarchy of the implementation class, without restriction. In this way, the TIE approach can be very useful when using a pre-existing Java class as an implementation class for an IDL interface.

6.4.4 Outline of the Bank Class Implementation (TIE Approach)

Using the TIE approach, the code for the bank implementation class, BankImplementation, could now be rewritten as follows:

Note that the method newAccount() returns an object which implements Java interface _AccountRef. This IDL generated type defines the client view of the IDL interface Account. An object of type _tie_Account can be returned as type _AccountRef, as class _tie_Account inherits from the IDL generated class Account, which in turn implements Java interface _AccountRef.

6.5 The Server: main() Method And Object Creation

This section shows a sample main() method of a banking application class, using both the BOAImpl and the TIE approaches.

6.5.1 The BOAImpl Approach

The main() method for a typical server class shows the creation of a Bank object. It takes the following form:

The code instantiates a single BankImplementation object which will handle incoming requests for clients through the IDL Bank interface. The BankImplementation object is created using a default constructor (that is, a constructor with no parameters). As BOAImpl-class constructors can raise OrbixWeb system exceptions, error handling code must surround the object creation.

Having created a BankImplementation object, the server calls impl_is_ready() on the _CORBA.Orbix object (defined within the IE.Iona.Orbix2 package) to indicate that it has completed initialisation and is ready to receive operation requests on its objects.

6.5.2 The TIE Approach

The implementation of the server main() method is similar in the TIE approach. The most significant difference is that the server creates a TIE object as well as a BankImplementation object:

The BankImplementation object is created using a default constructor. Unlike the BOAImpl example, the default constructor cannot raise an OrbixWeb system exception. However, it is important to note that the default constructor for type _tie_Bank can raise an exception.

6.5.3 Initialisation and impl_is_ready()

A server is normally coded so that it initialises itself and creates an initial set of objects. It then calls _CORBA.Orbix.impl_is_ready() to indicate that it has completed its initialisation and is ready to receive operation requests on its objects. _CORBA.Orbix is a static object (of class BOA or, optionally, ORB), which is used to communicate directly with OrbixWeb, to determine or change its settings.

The impl_is_ready() method normally does not return immediately: it blocks the server until an event occurs, handles the event, and re-blocks the server to await another event.

The method impl_is_ready() is actually declared four overloaded methods, as follows:

When a server is launched by the OrbixWeb daemon process, orbixd, the server name will already be known to OrbixWeb and therefore does not need to be passed to impl_is_ready() (however, if it is passed, it must be correct). However, when a server is launched manually, the server name must be communicated to OrbixWeb, and the normal way to do this is as the first parameter to impl_is_ready(). To allow a server to be launched either automatically or manually, it is recommended, therefore, that the serverName parameter be specified.

By default, servers must be registered with OrbixWeb, using the putit command. Therefore, if an unknown server name is passed to impl_is_ready(), the call will be rejected. However, the OrbixWeb daemon (orbixd) can be configured to allow unregistered servers to be run manually (see Chapter 3, "OrbixWeb Configuration" of the OrbixWeb Reference Guide).

The impl_is_ready() method returns only when a timeout occurs or an exception occurs while waiting for or processing an event. The timeout parameter indicates the number of milliseconds to wait between events: a timeout will occur if OrbixWeb has to wait longer than the specified timeout for the next event. A timeout of zero indicates that impl_is_ready() should time out and return immediately without checking if there is any pending event. A timeout does not cause impl_is_ready() to raise an exception.

Note that a server can time out either because it has no clients for the timeout duration, or because none of its clients uses it for that period.2

The default timeout can be passed explicitly as _CORBA.IT_DEFAULT_TIMEOUT. An infinite timeout can be specified by passing _CORBA.IT_INFINITE_TIMEOUT.

6.5.4 Construction and Markers

The name of an OrbixWeb object includes its server's name, its interface, and a unique name within that server and interface.

If, in the bank application, we wanted clients to be able to find an individual Bank object given a name for it, we could assign a meaningful marker name to each Bank object, and then allow clients to specify one of these marker names when calling the Bank._bind() method. Chapter 7, "Publishing Object References in Servers" shows how to do this.

The best way for a client to obtain an object reference for a particular Account object is probably for the Bank to provide an IDL operation that takes the Account holder's name or the Account's number as a parameter and returns an Account object reference.

6.6 Registration and Activation

The last step in developing and installing our bank application is to register the BankSrv server.

The OrbixWeb Implementation Repository records the server's name and the details of the Java class which should be interpreted in order to launch the server; these details include the class name, the class path and any command line arguments which the class expects.

The Implementation Repository maintains its data in Implementation Repository entries. Every node in a network which is to run servers must have access to an Implementation Repository; but repositories can be shared using a network file system.

Registration of a server can be achieved by the putit command, which takes the following simplified form:

For example, our BankSrv server might be registered as follows:

The class idl_demo.BankServer will then be registered as the implementation code of the server called BankSrv at the current host. The putit command does not cause the indicated server class to be interpreted: the Java interpreter can be explicitly invoked on the class, or the OrbixWeb daemon will cause the class to be interpreted in response to an incoming operation invocation.

Further information on the putit command is given in Chapter 11, "Registration and Activation of Servers".

6.7 Execution Trace

Let us now consider the events that occur as our BankSrv server and our client are run. The TIE approach will be used to show the initial trace, and then the BOAImpl approach will be discussed.

First a server with name "BankSrv" is registered in the Implementation Repository.

When an invocation arrives from a client, orbixd will launch the server by invoking the java interpreter on the specified class. The server application creates a new TIE object (of type _tie_Bank) for an object of class BankImplementation, and waits on _CORBA.Orbix.impl_is_ready():

The state of the server, at the time of the impl_is_ready() call, is shown in Figure 6.3. The server is now waiting for incoming requests. If impl_is_ready() times out, the server will terminate.

Figure 6.3: Server launched

Now let us consider the client: it first binds to any Bank object, using _bind(), for example:

No object name (marker) is specified, so OrbixWeb will choose any Bank object within the chosen server (BankSrv). When the Bank._bind() call is made, the OrbixWeb daemon will launch an appropriate process by invoking the Java interpreter on the BankServer class (if a process is not already running).

We will assume that the Bank._bind() call will bind to our newly created _tie_Bank object. The result of the binding is an automatically generated proxy object in the client, which acts as a "stand-in" for the remote BankImplementation object in the server. The object reference bRef within the client is now a remote object reference as shown in Figure 6.4.


Figure 6.4: Client binds to server, TIE Approach

The client programmer will not be aware of the TIE object (nevertheless, all remote operation invocations on our BankImplementation object will go via the TIE).

The client program now proceeds by asking the bank to open a new account, and making a deposit:

When the bRef.newAccount() call is made, the method BankImplementation.newAccount() is called (via the TIE) within the bank server. This generates a new AccountImplementation object and associated TIE object. The TIE object is added to the BankImplementation object's list of existing Accounts. Finally, newAccount() returns the Account reference back to the client.

At the client side, a new proxy will be created for the Account object, and this will be referenced by the aRef variable; see Figure 6.5.


Figure 6.5: Client creates object, TIE approach

Using the BOAImpl approach, the final diagram would be as shown in Figure 6.6.


Figure 6.6: Client creates object, BOAImpl approach

6.8 Comparison of the TIE and BOAImpl Approaches

As can be seen from the example outlined in this chapter, the TIE and BOAImpl approaches to interface implementation impose similar overheads on the implementation programmer. However, there are two significant differences which may affect the programmer's choice of implementation strategy:

The first of these differences has important implications for the viability of the BOAImpl approach in most applications. Java does not support multiple inheritance, so the inheritance requirement which the BOAImpl approach imposes on implementation classes limits the flexibility of those classes and eliminates the possibility of reusing existing implementations when implementing derived interfaces. The TIE approach does not suffer from this restriction and, for this reason, is the recommended approach.

The creation of a TIE object for each implementation object may be a significant decision factor in applications where a large number of implementation objects are likely to be created and tight restrictions on the usage of virtual memory exist. In addition, the delegation of client invocations by TIE objects implicitly involves an additional Java method invocation for each incoming request.

Of course, it is not necessary to choose one approach exclusively, as both can be freely mixed within the same server.

In section 6.8.1 and section 6.8.2 we will examine two important aspects IDL interface implementation: the ability to provide different implementations of the same interface, and the ability to implement different interfaces with a single implementation class.

6.8.1 Providing Different Implementations of the Same Interface

Both BOAImpl and TIE approaches allow a programmer to provide a number of different implementations of the same IDL interface, that is, to provide more than one implementation class for a given IDL interface. This is an important feature, especially in a large heterogeneous distributed system. An object can then be created as an instance of any one of the implementation classes; and client programmers need not be aware of which one is chosen.

6.8.2 Providing Different Interfaces to the Same Implementation

Using the TIE approach, it is possible to have a Java implementation class which implements more than one IDL interface. This class must implement the generated Java Operations interfaces for all of the IDL interfaces it supports, and must therefore implement all of the operations defined in those IDL interfaces. This common class is simply instantiated and passed to the constructor of any TIE objects created for a supported IDL interface.

In the BOAImpl approach, it is not possible to implement different interfaces in a single implementation class, as each interface requires the implementation class to extend an IDL generated base class.

6.9 An Example of Using Holder Classes

Recall the definition of operation newAccount() (from interface Bank) in the IDL definition for the banking application:

In order to illustrate the use of Holder types, we will modify this definition as follows:

The IDL compiler maps this operation to a method of Java interface _BankRef as follows:

The out parameter of type Account maps to an OrbixWeb Holder class, idl_demo._AccountHolder, which is generated by the IDL compiler. The source code for this class appears as follows:

This Holder class simply stores a value member variable of type Account, which may be modified during the operation invocation. Our client application could now be coded as follows:

In the server, the implementation of method newAccount() will receive the Holder object for type Account and may manipulate the value field as required. For example, in this case the newAccount() method might simply instantiate a new Account implementation object as follows:

In general, Holder classes are required for inout or out parameters where the Java parameter type cannot be passed by reference or where the Java parameter value may change type during the operation invocation. In our example, the out parameter is of type Account, so the out parameter value could (conceptually) change type if the type of the object is a derived type of Account.

Note that if the Account parameter were labelled inout in the IDL definition, the value member of the Holder class would need to be instantiated before calling the newAccount() operation.

For more information on Holder types, see section 5.21.1 "Holder Classes" in Chapter 5, "IDL to Java Mapping".



1 The name of the file containing these IDL definitions is not important; it simply determines the names of the files generated by the OrbixWeb IDL compiler.



2 The system can also be instructed to make the timeout active only when the server has no current clients–that is, the server should remain running as long as there are current clients. This is supported by the method setNoHangup(), defined in class BOA.



[Roadmap] [Introduction] [GS: Applications] [GS: Applets]
[IDL] [Mapping] [Programming OrbixWeb] [Publishing Objects] [Retrieving Objects] [IIOP]
[Running Clients] [Running Servers] [Exceptions] [Inheritance] [Callbacks] [Contexts]
[API Configuration] [TypeCode] [Any] [DII] [IR] [Filters] [Smart Proxies] [Loaders] [Locators]
[Index]