Creating CORBA Objects

Now that we have a better understanding of the various components of the CORBA architecture, let’s walk through the creation of CORBA objects using Java IDL. In order to distribute a Java object over the network using CORBA, you have to define your own CORBA-enabled interface and its implementation. This involves doing the following:

  • Writing an interface in the CORBA Interface Definition Language

  • Generating a Java base interface, plus a Java stub and skeleton class, using an IDL-to-Java compiler

  • Writing a server-side implementation of the Java base interface

We’ll walk through these steps one by one, starting with a quick primer on CORBA IDL, followed by the requirements for creating a Java implementation of an IDL-defined remote object.

An IDL Primer

The syntax of both Java and IDL were modeled to some extent on C++, so there are a lot of similarities between the two in terms of syntax. Interfaces in IDL are declared much like classes in C++ and, thus, classes or interfaces in Java. The major differences between IDL and Java are:

  • IDL is a declaration language. In IDL, you declare only the names and types for interfaces, data members, methods, method parameters, etc. Method implementations are created in the implementation language you choose (in this case Java), after you’ve used an IDL compiler to convert your IDL interface to your target language.

  • IDL, like C++, includes nonclass data structure definitions, like structs, unions, and enumerations.

  • Method parameters in IDL include modifiers that specify whether they are input, output, or input/output variables. In Java, all primitive data types are passed by value, and all object data types are passed by reference.

  • An IDL file can include multiple public interfaces. Only a single public class can be defined in a given Java file (although Java does allow for multiple inner classes within a single public class definition, and multiple nonpublic classes per file).

  • Modules, which are similar to Java packages, can be nested within other modules in the same IDL file, and interfaces in multiple distinct modules can be defined in the same IDL file. In Java, you can define a class only within a single package in a single Java file.

Modules

Modules are declared in IDL using the module keyword, followed by a name for the module and an opening brace that starts the module scope. Everything defined within the scope of this module (interfaces, constants, other modules) falls within the module and is referenced in other IDL modules using the syntax modulename ::x. Suppose that you want all your classes to be contained in a module called corba, which is part of a larger module called jent (an abbreviation of the title of this book). In IDL this is declared as follows:

// IDL
module jent {
  module corba {
    interface NeatExample ...
  };
};

If you want to reference the NeatExample interface in other IDL files, you use the syntax jent::corba::NeatExample, which may look familiar to readers who have done C++ programming. Java programmers should note the semicolons following the closing braces on the module definitions, which are required in IDL but not in Java. A semicolon is also required after the close of an interface definition.

Interfaces

Interfaces declared in IDL are mapped into classes or interfaces in Java. As mentioned earlier, IDL is used only to declare modules, interfaces, and their methods. Methods on IDL interfaces are always left abstract, to be defined in the programming language you use to implement the interfaces.

The declaration of an interface includes an interface header and an interface body. The header specifies the name of the interface and the interfaces it inherits from (if any). Here is an IDL interface header:

interface PrintServer : Server { ...

This header starts the declaration of an interface called PrintServer that inherits all the methods and data members defined in the Server interface. An IDL interface can inherit from multiple interfaces; simply separate the interface names with commas in the inheritance part of the header.

Data members and methods

The interface body declares all the data members (or attributes) and methods of an interface. Data members are declared using the attribute keyword. At a minimum, the declaration includes a name and a type (see Chapter 14 for a complete list of the basic data types available in IDL and the mapping to Java types). The declaration can optionally specify whether the attribute is read-only or not, using the readonly keyword. By default, every attribute you declare is readable and writable (for Java, this means that the IDL compiler generates public read and write methods for it). Here is an example declaration for a read-only string attribute:

readonly attribute string myString;

You declare a method by specifying its name, return type, and parameters, at a minimum. You can also optionally declare exceptions the method might raise, the invocation semantics of the method, and the context for the method call (see Chapter 14 for more details). Here is the declaration for a simple method that returns a string:

string parseString(in string buffer);

This declares a method called parseString( ) that accepts a single string argument and returns a string value.

A complete IDL example

Now let’s tie all these basic elements together. Here’s a complete IDL example that declares a module within another module, which itself contains several interfaces:

module OS {
  module services {
    interface Server {
      readonly attribute string serverName;
      boolean init(in string sName);
    };
    interface Printable {
      boolean print(in string header);
    };
 
    interface PrintServer : Server {
      boolean printThis(in Printable p);
    };
  };
};

The first interface, Server, has a single read-only string attribute and an init( ) method that accepts a string and returns a boolean. The Printable interface has a single print( ) method that accepts a string header. Finally, the PrintServer interface extends the Server interface (hence inheriting all its methods and attributes) and adds a printThis( ) method that accepts a Printable object and returns a boolean. In all cases, we’ve declared our method arguments as input-only (i.e., pass-by-value), using the in keyword.

Turning IDL into Java

Once you’ve described your remote interfaces in IDL, you need to generate Java classes that act as a starting point for implementing those remote interfaces in Java using an IDL-to-Java compiler. Every standard IDL-to-Java compiler (whether it’s a POA-compliant or pre-POA version) can generate the following Java classes from an IDL interface:

  • A Java interface with the same name as the IDL interface (e.g., Server). This interface includes Java method declarations that are a mapping of the operations declared in the IDL interface. In later versions of Sun’s IDL-to-Java mapping (JDK 1.3 and later), an interfaceName Operations interface (e.g., ServerOperations) is generated that contains these method declarations. The object’s mapped Java interface extends this interface. This “operations” interface was added to the standard CORBA IDL-to-Java mapping, and the IDL-to-Java compiler in JDK 1.3 was updated to reflect this. This same operations interface is extended by the server-side skeleton interfaces. In earlier versions of Java IDL (JDK 1.2), the object’s Java interface contains the method declarations directly, and there is no operations interface created.

  • A helper class whose name is the name of the IDL interface with “Helper” appended to it (e.g., ServerHelper). The primary purpose of this class is to provide a static narrow( ) method that can safely cast CORBA Object references to the Java interface type. The helper class also provides other useful static methods, such as read( ) and write( ) methods that allow you to read and write an object of the corresponding type using I/O streams.

  • A holder class whose name is the name of the IDL interface with “Holder” appended to it (e.g., ServerHolder). This class is used when objects with this interface are used as out or inout arguments in remote CORBA methods. Instead of being passed directly into the remote method, the object is wrapped with its holder before being passed. When a remote method has parameters that are declared as out or inout, the server-side method has to be able to update the argument it is passed and return the updated value. The only way to guarantee this, even for primitive Java data types, is to force out and inout arguments to be wrapped in Java holder classes, which are filled with the output value of the argument when the method returns. Also, inout arguments are initialized with the desired input value before the remote method is called in Java.

  • A client stub class, called _ interface-name Stub, that acts as a client-side implementation of the interface. This stub class implements the generated Java interface for the object, but simply knows how to convert client method calls into ORB requests that are forwarded to the actual remote object. The stub class for an interface named Server is called _ServerStub.

These classes comprise the “outward-facing” mapping of the CORBA object’s interface (the interfaces that clients of the object use directly). The IDL-to-Java compiler can also generate server-side skeleton classes you can use to implement the server-side implementation of the remote CORBA interface. In pre-POA versions of Java IDL, the IDL-to-Java compiler (idltojava in JDK 1.2,[13] idlj in JDK 1.3) is based on the ImplBase inheritance approach to creating server implementations. The pre-POA Java IDL compilers generate the server-side code in the form:

  • A server skeleton class called _ interface-name ImplBase, which is a base class for a server-side implementation of the interface. The base class can accept requests for the object from the ORB and channel return values back through the ORB to the remote client. The skeleton class for an interface named Server is called _ServerImplBase.

In POA-compliant versions of Sun’s idlj compiler, a similar _interfaceName Stub client-side interface is generated, but instead of the server-side ImplBase inheritance scheme used in earlier versions, the compiler generates:

  • A server skeleton class named interfaceNamePOA (e.g, ServerPOA), which implements a generated interfaceName Operations interface and extends the POA-related server-side interfaces. The interfaceNameOperations interface contains Java mappings of all the methods declared in the IDL definition. This class serves the same basic role as the ImplBase class generated in earlier versions of Java IDL, but it utilizes the standard POA interfaces to interact with the server-side ORB functions.

So, in addition to generating a client-side Java mapping of the IDL interface and some helper classes for the Java interface, the IDL-to-Java compiler also creates subclasses that act as an interface between a CORBA client and the ORB, between the server-side implementation and the ORB. Chapter 16 provides a complete reference for the JDK 1.3 and 1.4 versions of Sun’s idlj compiler. We will use this IDL-to-Java tool in the examples in this chapter. Remember, though, that any Java mapping of the CORBA standard should include its own IDL-to-Java compiler to generate these Java classes from the IDL interfaces you write. In addition, the Java that these tools generate should be compliant with the standard IDL mapping for Java, published by the OMG in the CORBA standard. Tools developed before the POA-compliant IDL-to-Java mapping will tend to use some non-standard server-side object adaptor styles, while those developed after the POA was introduced should generate POA-compliant server skeleton classes. So when using third-party Java-based CORBA tools and ORBs, it’s important to understand which version of the core CORBA spec, the IDL-to-Java mapping they support.

The delegation server-side model

The scheme described above (and demonstrated in Example 4-1) for the generated server-side code for a CORBA object is called an inheritance-based model. It depends on the server-side implementation directly extending a generated class (interfaceNamePOA for POA-compliant environments and _interfaceNameImplBase for pre-POA environments). There is another option, called the delegation model, available to you in terms of how you “plug” your server code into the CORBA environment.

The delegation model is based on a scheme in which an server-side delegate is generated by the IDL compiler. This delegate extends the generated skeleton class, and implements each of the mapped remote methods by delegating the incoming method request to a delegate object. This delegate object needs to implement the interfaceNameOperations interface generated by the IDL compiler, but it doesn’t have to extend a concrete or abstract base class. This can prove to be useful in cases where you have a preexisting Java class with its own inheritance scheme and want to “export” this class through CORBA for remote access. Because Java prohibits multiple inheritance, you don’t have the option of extending both the existing class and the generated skeleton class. With the delegation model, you can define a simple delegate class that extends the preexisting Java class, and implements the interfaceNameOperations interface generated by the compiler. You can then “publish” an instance of the original class by creating an instance of the new delegate class and an instance of the generated delegation-based server object, and associating the server object with your delegate.

We won’t provide full details of the delegation model here, but it’s important to realize that this option exists. It may prove useful in some situations. There are options available on the IDL-to-Java compiler to instruct it to generate delegation-based server objects (also referred to as ties). These compiler options are documented in Chapter 16.

A simple server class

The IDL interface shown in Example 4-1 is the IDL equivalent of the Account class we defined in Example 3-1. The interface, named Account, is declared within some nested modules (oreilly, jent, corba), and declares methods similar to the Account example in Chapter 3. Since this is IDL, the various method argument and return value data types are represented using IDL datatypes (e.g., string instead of String, etc.), and method arguments are declared with in, inout, or out modifiers.

Example 4-1. An Account Interface Defined in IDL

//
// [IDL] Account interface defined in IDL.
//

module oreilly {
  module jent {
    module corba {
      // Forward-declare the Account interface, for the typedefs below
      interface Account;

      // Declare some useful typedefs: a list of Accounts and of floats
      typedef sequence<Account> AccountList;
      typedef sequence<float> floatList;

      exception InsufficientFundsException {};
      
      interface Account {
        // Get the name of the account owner
        string getName(  );
        // The account balance
        float getBalance(  );
        // Withdraw funds from the account
        void withdraw(in float amt) raises (InsufficientFundsException);
        // Deposit funds to the account
        void deposit(in float amt);
        // Transfer funds from the source account to this account
        void transfer(in float amt, in Account src)
          raises (InsufficientFundsException);
        // Similar to above, but perform transfers from a series of
        // source Accounts
        void transferBatch(in floatList amts, in AccountList srcs)
          raises (InsufficientFundsException);
      };
    };
  };
};

We can run the idlj compiler on this IDL interface using the following command line (Windows version):

C:\>idlj -fall Account.idl

This command creates the five Java classes described in the previous sections: a Java version of the interface, a helper class, a holder class, a client stub, and a server skeleton. The -fall option tells the compiler to generate both client-side and server-side mapping interfaces (see Chapter 16 for complete details on the command-line arguments for idlj).

The compiler creates the Java interface shown in Example 4-2, in a file named Account.java. This interface doesn’t have much meat to it, because all of the method declarations are generated in the AccountOperations interface are extended by this interface, which is shown in Example 4-3. The interface declaration in the IDL file is mapped directly to the Account Java interface declaration, with the interface extending the AccountOperations and org.omg.CORBA.Object interfaces. The module declarations in the IDL file have been mapped into an oreilly.jent.corba package statement at the beginning of all the generated Java files. The IDL data types have been converted into the equivalent Java data types, and, since they don’t require any special handling in a remote method call, the in method parameters in IDL are mapped into regular Java input arguments.

Example 4-2. Java Mapping of the Account Interface

package oreilly.jent.corba;
/**
* oreilly/jent/corba/Account.java
* Generated by the IDL-to-Java compiler (portable), version "3.1"
* from ../Account.idl
* Friday, July 6, 2001 8:11:50 AM EDT
*/

public interface Account extends AccountOperations, 
     org.omg.CORBA.Object, org.omg.CORBA.portable.IDLEntity 
{
} // interface Account

Example 4-3. AccountOperations Java Interface

package oreilly.jent.corba;
/**
* oreilly/jent/corba/AccountOperations.java
* Generated by the IDL-to-Java compiler (portable), version "3.1"
* from ../Account.idl
* Friday, July 6, 2001 8:11:50 AM EDT
*/

public interface AccountOperations 
{

  // Get the name of the account owner
  String getName (  );

  // The account balance
  float getBalance (  );

  // Withdraw funds from the account
  void withdraw (float amt) throws 
        oreilly.jent.corba.InsufficientFundsException;

  // Deposit funds to the account
  void deposit (float amt);

  // Transfer funds from the source account to this account
  void transfer (float amt, oreilly.jent.corba.Account src) 
       throws oreilly.jent.corba.InsufficientFundsException;

  // source Accounts
  void transferBatch (float[] amts, oreilly.jent.corba.Account[] srcs) 
       throws oreilly.jent.corba.InsufficientFundsException;
} // interface AccountOperations

The helper class and narrowing references

The compiler also generates a helper class, called AccountHelper. We won’t provide the full code listing for this generated class here, but it can be found in the downloadable source code examples available for this book, along with all of the other Java interfaces generated by the IDL-to-Java compiler from this IDL interface.

The helper class is a standalone utility class that doesn’t extend any other interfaces:

abstract public class AccountHelper {

As mentioned earlier, the helper class has static methods that let you read and write Account objects to and from CORBA I/O streams:

public static 
  oreilly.jent.corba.Account read (org.omg.CORBA.portable.InputStream istream)
public static void write (org.omg.CORBA.portable.OutputStream ostream, 
                             oreilly.jent.corba.Account value)

a type( ) method that provides the TypeCode for the mapped Account class:

synchronized public static org.omg.CORBA.TypeCode type (  )

and, most importantly, a narrow( ) method that safely narrows a CORBA org.omg.CORBA.Object reference into an Account reference:

public static oreilly.jent.corba.Account narrow 
    (org.omg.CORBA.Object obj)

Object narrowing is CORBA’s equivalent to directly casting object references -- we discuss why narrowing is necessary later, in Remote Object References and Narrowing. In the implementation of the narrow( ) method, the helper class converts a CORBA Object reference to a reference to a specific type. If the CORBA object can’t be narrowed to the requested type (e.g., the passed object reference is a null reference, the object’s TypeCode doesn’t match the TypeCode of the narrowed type, etc.), then the narrow( ) method throws a BAD_PARAM exception.

The holder class

The compiler generates a holder class for the Account class, which implements the CORBA Streamable interface.

public final class AccountHolder implements org.omg.CORBA.portable.
                                                         Streamable

The holder class is a wrapper used when Account objects are called for as out or inout arguments in an IDL method. All holder classes implement the Streamable interface from the org.omg.CORBA.portable package, which includes implementations of the _read( ) and _write( ) methods of the Streamable interface:

public void _read (org.omg.CORBA.portable.InputStream i)
public void _write (org.omg.CORBA.portable.OutputStream o)

This allows holders to be transmitted in remote method calls using these _read( ) and _write( ) methods; these methods handle whatever serialization the object needs. This functionality is similar to that provided by Java serialization, but CORBA needs its own scheme because it is independent of any particular language, and needs to provide this serialization even if the target language doesn’t provide this feature natively.

A holder contains a single instance of the corresponding CORBA object (an Account, in this example) as a data member:

public oreilly.jent.corba.Account value = null;

This instance is initialized in the constructor of the holder:

public AccountHolder (oreilly.jent.corba.Account initialValue)
{
  value = initialValue;
}

When a holder object is passed into a remote method call as an inout argument, its _write( ) method is invoked. This method takes the object instance contained by the holder class, serializes it, and streams it through the ORB to the remote object server. When the remote method call returns, the holder’s _read( ) method is invoked to read the (possibly updated) object from the remote object server, and the holder object replaces its internal value with the updated object.

As an example of using the holder class, let’s define another IDL interface that includes a method that uses an Account as an inout parameter:

// IDL
interface AccountManager {
  boolean updateAccount(inout Account account);
};

The AccountManagerOperations Java interface generated from this IDL interface uses the AccountHolder class as the type for the corresponding Java method parameter:

// Java
public interface AccountManagerOperations 
{
  boolean updateAccount (AccountHolder account);
} // interface AccountManagerOperations

The client stub

The idlj compiler generates a Java client stub (_AccountStub) for our CORBA interface. The client stub implements the generated Account Java interface and acts as a client-side proxy for a remote Account object:

public class _AccountStub 
  extends org.omg.CORBA.portable.ObjectImpl 
  implements oreilly.jent.corba.Account

When a client acquires a reference to a remote Account object (through any of the methods we’ll describe later, in Finding and Using Remote Objects), it actually receives an instance of this client stub class. The stub has implementations of all the methods from the interface, as mapped into the AccountOperations interface that is the parent of the Account interface. The stub class serves as a proxy for a remote server object, and each method implementation in the stub generates a request to the ORB to make a remote method call on the corresponding server-side object. The method arguments are bundled up (“marshalled”) and passed along with the request to the ORB. We’re not going to go into the details of the stub’s method implementations, because you shouldn’t have to worry much about them under normal conditions. But it is enlightening to look at an example to see how your remote objects do what they do in detail, using the core CORBA functions. As an example, here is the generated client stub implementation of the getName( ) method from our Account interface:

// Get the name of the account owner
public String getName (  )
{
  org.omg.CORBA.portable.InputStream $in = null;
  try {
    org.omg.CORBA.portable.OutputStream $out = _request ("getName", true);
    $in = _invoke ($out);
    String $result = $in.read_string (  );
    return $result;
  } catch (org.omg.CORBA.portable.ApplicationException $ex) {
    $in = $ex.getInputStream (  );
    String _id = $ex.getId (  );
    throw new org.omg.CORBA.MARSHAL (_id);
  } catch (org.omg.CORBA.portable.RemarshalException $rm) {
    return getName (  );
  } finally {
    _releaseReply ($in);
  }
} // getName

As mentioned, when a Java client gets a reference to a remote Account object, it is given one of these stub objects. The client can make method calls on the stub object, and the stub converts these calls into corresponding requests to the ORB to invoke the methods on the remote object and send back the results.

Pre-POA server skeletons

In pre-POA versions of Java IDL (JDK 1.2 and 1.3), the IDL-to-Java compiler generates a server implementation base class that follows the ImplBase scheme. An _AccountImplBase base class for the server implementation is generated:

public abstract class _AccountImplBase 
  extends org.omg.CORBA.portable.ObjectImpl
  implements oreilly.jent.corba.Account, 
                                     org.omg.CORBA.portable.InvokeHandler

This base class provides the basic “plumbing” for our server implementation. As mentioned earlier in this section, it’s principle purpose is to receive requests from remote clients through the ORB and directs them to the proper method on the server implementation class. In the ImplBase version, all this work is done by the server skeleton’s _invoke( ) method, which is called by the ORB:

public org.omg.CORBA.portable.OutputStream _invoke (String method,
    org.omg.CORBA.portable.InputStream in,
    org.omg.CORBA.portable.ResponseHandler rh)

The _invoke( ) method figures out which method is being called, unpacks the method arguments (if any) from the request, and calls the method directly on itself.

Note that the server skeleton doesn’t have implementations of the remote methods declared in the Account interface (also note that it’s declared as an abstract class for this reason). The idlj compiler doesn’t do everything for you; you still need to create a server implementation for your interface by extending this base class, and provide the logic behind the remotely accessible operations on your CORBA object.

POA server skeletons

In JDK 1.4 and later, the idlj compiler generates server implementation skeleton classes that follow the POA specification. In this model, the base class for the server implementation of our Account interface is called AccountPOA:

public abstract class AccountPOA 
  extends org.omg.PortableServer.Servant
  implements oreilly.jent.corba.AccountOperations, 
    org.omg.CORBA.portable.InvokeHandler

The structure of the class is very similar to the old ImplBase format (method requests are passed by the ORB to the _invoke( ) method, where they are delegated to the proper method on the implementation class), but the particulars of the ORB interface follow the POA specification. This helps to guarantee that your CORBA object implementations will migrate easily between different CORBA ORB implementations.

Writing the Implementation

So, we’ve written an IDL interface and generated the Java interface and support classes for it, including the client stub and the server skeleton. Now we need to create concrete server-side implementations of all of the methods on our interface. We do this by subclassing from the server-side skeleton class generated by the idlj compiler. For our example, we need to subclass either _AccountImplBase or AccountPOA (depending on whether we’re using the POA or pre-POA versions of Java IDL). We also need to implement the various methods defined on the Account IDL interface and mapped through the idlj compiler into the AccountOperations Java interface. The AccountImplPOA class in Example 4-4 shows the implementation for the POA-compliant case (the pre-POA implementation is virtually identical, with a few minor differences that we’ll mention at the end of this section). The pre-POA version won’t be shown here, but it is included in the source code examples for this book, downloadable from the O’Reilly web site.

As you look through the implementation class, you’ll notice that the method implementations are similar to the RMI version of the Account server implementation example in Chapter 3. The only real difference is that this AccountImpl class extends the generated AccountPOA class (and through it, the generated AccountOperations interface and the CORBA Servant class), while the RMI server implementation implements the RMI Account interface and extends java.rmi.server.UnicastRemoteObject. So in the same way that the two remote object schemes are analogous to each other in terms of functionality, the particulars of the server implementations in each case are analogous as well.

Example 4-4. Server Implementation of the Account CORBA Object

/**
 * AccountImplPOA: Implementation of the Account remote interface
 */

public class AccountImplPOA extends AccountPOA {
  // Our current balance
  private float mBalance = 0;
  // Name on account
  private String mName = "";
  // Create a new account with the given name
  public AccountImplPOA(String name) {
    mName = name;
  }

  public String getName(  ) {
    return mName;
  }
  public float getBalance(  ) {
    return mBalance;
  }
  // Withdraw some funds
  public void withdraw(float amt) throws InsufficientFundsException {
    if (mBalance >= amt) {
      mBalance -= amt;
      // Log transaction...
      System.out.println("--> Withdrew " + amt 
                                         + " from account " + getName(  ));
      System.out.println("    New balance: " + getBalance(  ));
    }
    else {
      throw new InsufficientFundsException("Withdrawal request of " + amt +
                                          " exceeds balance of " + mBalance);
    }
  }   
  // Deposit some funds
  public void deposit(float amt) {
    mBalance += amt;
    // Log transaction...
    System.out.println("--> Deposited " + amt + 
                                      " into account " + getName(  ));
    System.out.println("    New balance: " + getBalance(  ));
  }
  // Move some funds from another (remote) account into this one
  public void transfer(float amt, Account src)
    throws InsufficientFundsException {
    if (checkTransfer(src, amt)) {
      src.withdraw(amt);
      this.deposit(amt);
      // Log transaction...
      System.out.println("--> Transferred " + amt + 
                                          " from account " + getName(  ));
      System.out.println("    New balance: " + getBalance(  ));
    }
    else {
      throw new InsufficientFundsException("Source account balance is less 
                                         " + "than the requested transfer.");
    }
  }
  // Make several transfers from other (remote) accounts into this one
  public void transferBatch(float[] amts, Account[] srcs)
    throws InsufficientFundsException {
    // Iterate through the accounts and the amounts to be
    // transferred from each
    for (int i = 0; i < amts.length; i++) {
      float amt = amts[i];
      Account src = srcs[i];
      // Make the transaction
      this.transfer(amt, src);
    }
  }
  // Check to see if the transfer is possible, given the source account 
  private boolean checkTransfer(Account src, float amt) {
    boolean approved = false;
    if (src.getBalance(  ) >= amt) {
      approved = true;
    }
    return approved;
  }
}


[13] Although Java IDL was a standard part of Java 1.2, Sun only offered the early-access version of its idltojava compiler, which you have to download separately from http://developer.java.sun.com/developer/earlyAccess/jdk12/idltojava.html.

Get Java Enterprise in a Nutshell, Second Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.