Java Technology Home Page
A-Z Index

Java Developer Connection(SM)
Online Training

Downloads, APIs, Documentation
Java Developer Connection
Tutorials, Tech Articles, Training
Online Support
Community Discussion
News & Events from Everywhere
Products from Everywhere
How Java Technology is Used Worldwide
 

Writing Advanced Applications
Chapter 4: Common Object Request Broker Architecture (CORBA)

[<<BACK] [CONTENTS] [NEXT>>]

Both the Remote Method Invocation (RMI) and Enterprise JavaBeansTM auction application implementations use the JavaTM language to implement the different auction service tiers. However, you might need to integrate with applications written in C, C++ or other languages and running on a myriad of operating systems and machines.

One way to integrate with other applications is to transmit data in a common format such as 8 bit characters over a TCP/IP socket. The disadvantage is you have to spend a fair amount of time deriving a messaging protocol and mapping the various data structures to and from the common transmission format so the data can be sent and received over the TCP/IP connection.

This is exactly where Common Object Request Broker Architecture (CORBA) and its Interface Definition Language (IDL) can help. IDL provides a common format to represent an object that can be distributed to other applications. The other applications might not even understand objects, but as long as they can provide a mapping between the common IDL format and their own data representations, the applications can share data.

This chapter describes the Java language to IDL mapping scheme, and how to replace the original container-managed RegistrationBean with its CORBA server equivalent. The SellerBean.java and AuctionServlet.java programs are changed to interoperate with the CORBA RegistrationServer program.

IDL Mapping Scheme

Many programming languages provide a mapping between their data types to the common denominator IDL format, and the Java language is no exception. The Java language can send objects defined by IDL to other CORBA distributed applications, and receive objects defined by IDL from other CORBA distributed applications.

This section describes the Java language to IDL mapping scheme and, where needed, presents issues you need to take into consideration.

Quick Reference

Here is a quick reference table of the Java language to CORBA IDL data types, and the runtime exceptions thrown when conversions fail. Data types in this table that need explanation are covered below.

Java Data TypeIDL FormatRuntime Exception
byteoctet
booleanboolean
charcharDATA_CONVERSION
charwchar
doubledouble
floatfloat
intlong
intunsigned long
longlong long
longunsigned long long
shortshort
shortunsigned short
java.lang.StringstringDATA_CONVERSION
java.lang.StringwstringMARSHAL

Unsigned Values: The Java data types byte, short, int, and long are represented by 8 bit, 16 bit, 32 bit and 64 bit two's-complement integers. This means a Java short value represents the range -215 to 215 - 1 or -32768 to 32767 inclusive. The equivalent signed IDL type for a short, short, matches that range, but the unsigned IDL short type uses the range 0 to 215 or 0 to 65535.

This means that in the case of a short, if an unsigned short value greater than 32767 is passed to a program written in the Java language, the short value is represented in the Java language as a negative number. This can cause confusion in boundary tests for a value greater than 32767 or less than 0.

IDL char Types: The Java language uses 16-bit unicode, but the IDL char and string types are 8-bit characters. You can map a Java char to an 8-bit IDL char to transmit multi-byte characters if you use an array to do it. However, the IDL wide char type wchar is specifically designed for languages with multi-byte characters and allocates a fixed number of bytes as needed to contain that language set for each and every letter.

When mapping between the Java language char type and the IDL char type, the DATA_CONVERSION exception is thrown if the character does not fit into 8 bits.

IDL string Types: The IDL string type can be thought of as a sequence of IDL char types, and also raises the DATA_CONVERSION exception. The IDL wstring type is equivalent to a sequence of wchars terminated by a wchar NULL.

An IDL string and wstring type can either have a fixed size or no maximum defined sized. If you try to map a java.lang.String to a fixed size or bounded IDL string and the java.lang.String is too large, a MARSHAL exception is raised.

Setting up IDL Mappings

Java language to IDL mappings are placed in a file with a .idl extension. The file is compiled so it can be accessed by CORBA programs that need to send and receive data. This section explains how to construct the mappings for package statements and the Java data types. The section below on CORBA RegistrationServer Implementation describes how to use this information to set up an IDL mapping file for the CORBA Registration server.

Java packages and interfaces: Java package statements are equivalent to the module type in IDL. The module types can be nested, which results in generated Java classes being created in nested sub-directories.

For example, if a CORBA program contains this package statement:

  package registration;
the mappings file would have this IDL module mapping for it:
  module registration {
  };

If a CORBA program contains a package hierarchy like this

  package registration.corba;
the equivalent IDL module mapping is this:
  module registration {
    module corba {
    };
  };
Distributed classes are defined as Java interfaces and map to the IDL interface type. IDL does not define access such as public or private like you find in the Java language, but does allow inheritance from other interfaces.

This example adds the Java Registration interface to an IDL registration module.

  module registration {
     interface Registration {
     };
  }
This example adds the Java Registration interface to an IDL registration module, and indicates the Registration interface inherits from the User interface.
  module registration {
     interface Registration: User {
     };
  }

Java methods: Java methods map to IDL operations. The IDL operation looks similar to a Java method except there is no concept of access control. You also have to help the IDL compiler by specifying which parameters are in, inout or out, defined as follows:

  • in - parameter is passed into the method but not changed.
  • inout - parameter is passed into the method and might be returned changed.
  • out - parameter might be returned changed.
This IDL mapping includes the Registration and RegistrationHome interface methods to IDL operations using one IDL module type.
module registration {

  interface Registration {
    boolean verifyPassword(in string password);
    string getEmailAddress();
    string getUser();
    long adjustAccount(in double amount);
    double getBalance();
  };

  interface RegistrationHome {
    Registration findByPrimaryKey(
		   in RegistrationPK theuser) 
		   raises (FinderException);
  }
}

Java Arrays: Arrays in the Java language are mapped to the IDL array or IDL sequence type using a type definition.

This example maps the Java array double balances[10] to an IDL array type of the same size.

typedef double balances[10];
These examples map the Java array double balances[10] to an IDL sequence type. The first typedef sequence is an example of an unbounded sequence, and the second typedef sequence has the same size as the array.
typedef sequence<double> balances;
typedef sequence<double,10> balances;

Java Exception: Java exceptions are mapped to IDL exceptions. Operations use IDL exceptions by specifying them as a raises type.

This example maps the CreateException from the auction application to the IDL exception type, and adds the IDL raises type to the operation as follows. IDL exceptions follow C++ syntax, so instead of throwing an exception (as you would in the Java language), the operation raises an exception.

exception CreateException {
};

interface RegistrationHome {
  RegistrationPK create(
	           in string theuser, 
	           in string password, 
	           in string emailaddress, 
	           in string creditcard) 
	           raises (CreateException);
}

Other IDL types

These other basic IDL types do not have an exact equivalent in the Java language. Many of these should be familiar if you have used C or C++. The Java language provides a mapping for these types so a program written in the Java language can receive data from programs written in C or C++.
  • IDL attribute
  • IDL enum
  • IDL struct
  • IDL union
  • IDL Any
  • IDL Principal
  • IDL Object

IDL attribute: The IDL attribute type is similar to the get and set methods used to access fields in the JavaBeansTM software.

In the case of a value declared as an IDL attribute, the IDL compiler generates two methods of the same name as the IDL attribute. One method returns the field and the other method sets it. For example, this attribute type:

interface RegistrationPK {
  attribute string theuser;
};
defines these methods
//return user
  String theuser(); 
//set user
  void theuser(String arg); 

IDL enum: The Java language has an Enumeration class for representing a collection of data. The IDL enum type is different because it is declared as a data type and not a data collection.

The IDL enum type is a list of values that can be referenced by name instead of by their position in the list. In the example, you can see that referring to an IDL enum status code by name is more readable than referring to it by its number. This line maps static final int values in the final class LoginError. You can reference the values as you would reference a static field: LoginError.INVALID_USER.

enum LoginError {
  INVALID_USER, WRONG_PASSWORD, TIMEOUT};
Here is a version of the enum type that includes a preceding underscore that can be used in switch statements:
switch (problem) {
  case LoginError._INVALID_USER:
    System.out.println("please login again");
    break;
}

IDL struct: An IDL struct type can be compared to a Java class that has only fields, which is how it is mapped by the IDL compiler.

This example declares an IDL struct. Note that IDL types can reference other IDL types. In this example LoginError is from the enum type declared above.

struct ErrorHandler {
  LoginError errortype;
  short retries;
};

IDL union: An IDL union can represent one type from a list of types defined for that union. The IDL union maps to a Java class of the same name with a discriminator method used for determining the type of this union.

This example maps the GlobalErrors union to a Java class by the name of GlobalErrors. A default case case: DEFAULT could be added to handle any elements that might be in the LoginErrors enum type, and not specified with a case statement here.

  union GlobalErrors switch (LoginErrors) {
     case: INVALID_USER: string message;
     case: WRONG_PASSWORD: long attempts;
     case: TIMEOUT: long timeout;
  };
In a program written in the Java language, the GlobalErrors union class is created as follows:
  GlobalErrors ge = new GlobalErrors();
  ge.message("please login again");
The INVALID_USER value is retrieved like this:
  switch (ge.discriminator().value()) {
    case: LoginError._INVALID_USER:
      System.out.println(ge.message);
      break;
  }

Any type: If you do not know what type is going to be passed or returned to an operation, you can use the Any type mapping, which can represent any IDL type. The following operation returns and passes an unknown type:

  interface RegistrationHome {
    Any customSearch(Any searchField, out count);
  };
To first create a type of Any, request the type from the Object Request Broker (ORB). To set a value in a type of Any, use an insert_<type> method. To retrieve a value, use the extract_<type> method.

This example requests an object of type Any, and uses the insert_type method to set a value.

  Any sfield = orb.create_any();
  sfield.insert_long(34);
The Any type has an assigned TypeCode value that you can query using type().kind().value() on the object. The following example shows a test for the TypeCode double. This example includes a reference to the IDL TypeCode find out which type the Any object contains. The TypeCode is used for all objects. You can analyze the type of a CORBA object using the _type() or type() methods as shown here.
public Any customSearch(Any searchField, IntHolder count){
  if(searchField.type().kind().value() == TCKind._tk_double){
// return number of balances greater than supplied amount 
    double findBalance=searchField.extract_double();

Principal: The Principal type identifies the owner of a CORBA object, for example, a user name. The value can be interrogated from the request_principal field of the CORBA RequestHeader class to make the identification. More comprehensive security and authorization is available in the CORBA security service. Object: The Object type is a CORBA object. If you need to send Java objects, you have to either translate them into an IDL type or use a mechanism to serialize them when they are transferred.

CORBA in the Auction Application

The container-managed RegistrationBean from the auction application is completely replaced with a standalone CORBA RegistrationServer that implements the registration service. The CORBA RegistrationServer is built by creating and compiling an IDL mappings file so client programs can communicate with the registration server.

The SellerBean.java and AuctionServlet.java files are updated to look up the CORBA registration server.

CORBA RegistrationServer Implementation

This section describes the Registration.idl file, which maps the RegistrationHome and Registration remote interfaces from the Enterprise JavaBean auction application to their IDL equivalents and shows how to compile the Registration.idl file into CORBA registration server classes.

The CORBA registration server implements the create and findByPrimaryKey methods from the original RegistrationBean.java file, and is enhanced with the following two new methods to help illustrate CORBA callbacks and how to use the Any type.

  • findLowCreditAccounts(in ReturnResults rr), which uses a callback to return a list of accounts with a low balance.

  • any customSearch(in any searchfield, out long count), which returns a different search result depending on the search field type submitted.

IDL Mappings File

Here is the Registration.idl file that maps the data types and methods used in the RegistrationHome and Registration programs to their IDL equivalents.
module registration {

interface Registration {
   boolean verifyPassword(in string password);
   string getEmailAddress();
   string getUser();
   long adjustAccount(in double amount);
   double getBalance();
};

interface RegistrationPK {
   attribute string theuser;
};

enum LoginError {INVALIDUSER, WRONGPASSWORD, TIMEOUT};

exception CreateException {
};

exception FinderException {
};

typedef sequence<Registration> IDLArrayList;

interface ReturnResults  {
  void updateResults(in IDLArrayList results) 
	raises (FinderException);
};

interface RegistrationHome {
  RegistrationPK create(in string theuser, 
			in string password, 
                   	in string emailaddress, 
			in string creditcard) 
                           raises (CreateException);

  Registration findByPrimaryKey(
                 in RegistrationPK theuser) 
		 raises (FinderException);
  void findLowCreditAccounts(in ReturnResults rr) 
		 raises (FinderException);
  any customSearch(in any searchfield, out long count);
};
};

Compiling the IDL Mappings File

The IDL file has to be converted into Java classes that can be used in the CORBA distributed network. The Java 2 platform compiles .idl files using the program idltojava. This program will be eventually replaced with the idlj command.

The -fno-cpp arguments indicate there is no C++ compiler installed.

  idltojava -fno-cpp Registration.idl
Other Java IDL compilers should also work, for example, jidl from ORBacus can generate classes that can be used by the Java 2 ORB.

Stubs and Skeletons

Corba and RMI are similar in that compilation generates a stub file for the client and a skeleton file for the server. The stub (or proxy), and skeleton (or servant) are used to marshal and unmarshal data between the client and the server. The skeleton (or servant) is implemented by the server. In this example, the IDL RegistrationHome interface mapping generates a _RegistrationHomeImplBase class (the skeleton or servant class) that the generated RegistrationServer class extends.

When requesting a remote CORBA object or calling a remote method, the client call passes through the stub class before reaching the server. This proxy class invokes CORBA requests for the client program. The following example is the code automatically generated for the RegistrationHomeStub.java class.

  org.omg.CORBA.Request r = _request("create");
  r.set_return_type(
        registration.RegistrationPKHelper.type());
  org.omg.CORBA.Any _theuser = r.add_in_arg();

Object Request Broker

The center of the CORBA distributed network is the Object Request Broker or ORB. The ORB is involved in marshaling and unmarshaling objects between the client and server. Other services such as the Naming Service and Event Service work with the ORB.

The Java 2 platform includes an ORB in the distribution called the IDL ORB. This ORB is different from many other ORBs because it does not include a distinct Basic Object Adapter (BOA) or Portable Object Adapter (POA).

An object adapter manages the creation and lifecycle of objects in the CORBA distributed space. This can be compared to the container in the Enterprise JavaBeans server managing the lifecycle of the session and entity beans.

The AuctionServlet and SellerBean programs create and initialize a Java 2 ORB like this:

  ORB orb = ORB.init(args, null);
In the RegistrationServer program, the server object to be distributed is bound to the ORB using the connect method:
  RegistrationServer rs = new RegistrationServer();
  orb.connect(rs);
An object connected to an ORB can be removed with the disconnect method:
  orb.disconnect(rs);
Once connected to a CORBA server object, the Java 2 ORB keeps the server alive and waits for client requests to the CORBA server.
  java.lang.Object sync = new java.lang.Object();
  synchronized(sync) {
    sync.wait();
  }

Making the CORBA Server Accessible

Although this object is now being managed by the ORB, the clients do not yet have a mechanism to find the remote object. This can be solved by binding the CORBA server object to a naming service.

The Java 2 naming service is called tnameserv. The naming service by default uses port 900; however, this value can be changed by setting the argument -ORBInitialPort portnumber when starting tnameserv or setting the property org.omg.CORBA.ORBInitialPort when starting the client and server processes.

These next sections describes the main method from the RegistrationServer class.

  java.util.Properties props=System.getProperties();
  props.put("org.omg.CORBA.ORBInitialPort", "1050");
  System.setProperties(props);
  ORB orb = ORB.init(args, props);
The next lines show the initial naming reference is initialized by requesting the service called NameService. The NamingContext is retrieved and the name built up and bound to the naming service as NameComponent elements. The name in this example has a root called auction with this object being bound as RegistrationBean from that auction root. The name could be compared to a class by the name of auction.RegistrationBean.
  org.omg.CORBA.Object nameServiceObj = 
	orb.resolve_initial_references("NameService") ;
  NamingContext nctx = 
	NamingContextHelper.narrow(nameServiceObj);
  NameComponent[] fullname = new NameComponent[2];
  fullname[0] = new NameComponent("auction", "");
  fullname[1] = new NameComponent(
                      "RegistrationBean", "");

  NameComponent[] tempComponent = new NameComponent[1];
  for(int i=0; i < fullname.length-1; i++ ) {
      tempComponent[0]= fullname[i];
      try{
       nctx=nctx.bind_new_context(tempComponent);
      }catch (Exception e){ 
        System.out.println("bind new"+e);}
  }
  tempComponent[0]=fullname[fullname.length-1];
  try{
   nctx.rebind(tempComponent, rs);
  }catch (Exception e){
   System.out.println("rebind"+e);
  }

Plugging in a new ORB

The Java 2 IDL ORB does not currently include some of the services available in many other commercial ORBS such as the security or event (notification) services. You can use another ORB in the Java 2 runtime by configuring two properties and including any necessary object adapter code.

Using a new ORB in the registration server requires the org.omg.CORBA.ORBClass and org.omg.CORBA.ORBSingletonClass properties point to the appropriate ORB classes. In this example the ORBacus ORB is used instead of the Java 2 IDL ORB. To use another ORB, the code below should be plugged into the RegistrationServer.main method.

In the example code, a SingletonClass ORB is used. The SingletonClass ORB is not a full ORB, and is primarily used as a factory for TypeCodes. The call to ORB.init() in the last line creates the Singleton ORB.

  Properties props= System.getProperties();
  props.put("org.omg.CORBA.ORBClass", 
            "com.ooc.CORBA.ORB");
  props.put("org.omg.CORBA.ORBSingletonClass",
	"com.ooc.CORBA.ORBSingleton");
  System.setProperties(props);
  ORB orb = ORB.init(args, props) ;

In the Java 2 IDL, there is no distinct object adapter. As shown in the example code segment below, using the Basic Object Adapter from ORBacus requires an explicit cast to the ORBacus ORB. The Broker Object Architecture (BOA) is notified that the object is ready to be distributed by calling the impl_is_ready(null) method.

  BOA boa = ((com.ooc.CORBA.ORB)orb).BOA_init(
                                       args, props);
 ...
  boa.impl_is_ready(null);

Although both the ORBSingletonClass and ORBClass ORBs build the object name using NameComponent, you have to use a different ORBacus Naming Service. The CosNaming.Server service is started as follows where the -OAhost parameter is optional:

  java com.ooc.CosNaming.Server -OAhost localhost -OAport 1060
Once the naming service is started, the server and client programs find the naming service using the IIOP protocol to the host and port named when starting the Naming service:
  java registration.RegistrationServer 
	-ORBservice NameService 
	iiop://localhost:1060/DefaultNamingContext

Naming Service Access by CORBA Clients

CORBA clients access the naming service in a similar way to the server, except that instead of binding a name, the client resolves the name built from the NameComponents.

The AuctionServlet and SellerBean classes use the following code to look up the CORBA server:

  NameComponent[] fullname = new NameComponent[2];
  fullname[0] = new NameComponent("auction", "");
  fullname[1] = new NameComponent(
                      "RegistrationBean", "");

  RegistrationHome regRef = 
	RegistrationHomeHelper.narrow(
	                  nctx.resolve(fullname));
In the case of the ORBacus ORB, the clients also need a Basic Object Adapter if callbacks are used as in the SellerBean.auditAccounts method. The naming context helper is also configured differently for the ORBacus server started earlier:
  Object obj = 
	((com.ooc.CORBA.ORB)orb).get_inet_object (
		"localhost", 
		1060, 
		"DefaultNamingContext"); 
  NamingContext nctx = NamingContextHelper.narrow(obj);

Helper and Holder classes

References to remote objects in CORBA use a Helper class to retrieve a value from that object. A commonly used method is the Helper narrow method, which ensures the object is cast correctly.

Holder classes hold values returned when using inout or out parameters in a method. The caller first instantiates the appropriate Holder class for that type and retrieves the value from the class when the call returns. In the next example, the count value for customSearch is set and retrieved after customSearch has been called. On the server side the count value is set by calling count.value=newvalue.

  IntHolder count= new IntHolder();
  sfield=regRef.customSearch(sfield,count);
  System.out.println("count now set to "+count.value);

Garbage Collection

Unlike RMI, CORBA does not have a distributed garbage collection mechanism. References to an object are local to the client proxy and the server servant. This means each Java1 virtual machine (JVM) is free to reclaim that object and garbage collect it if there are no longer references to it. If an object is no longer needed on the server, the orb.disconnect(object) needs to be called to allow the object to be garbage collected.

CORBA Callbacks

The new findLowCreditAccounts method is called from the AuctionServlet using the Uniform Resource Locator (URL) http://localhost:7001/AuctionServlet?action=auditAccounts.

The AuctionServlet.auditAccounts method calls the SellerBean.auditAccounts method, which returns an ArrayList of Registration records.

//AuctionServlet.java
  private void auditAccounts(ServletOutputStream out,
    HttpServletRequest request) throws IOException{

//   ...

   SellerHome home = (SellerHome) ctx.lookup("seller");
   Seller si= home.create();

   if(si != null) {
     ArrayList ar=si.auditAccounts();
     for(Iterator i=ar.iterator(); i.hasNext();) {
       Registration user=(Registration)(i.next());
       addLine("<TD>"+user.getUser() +
	 "<TD><TD>"+user.getBalance()+
	 "<TD><TR>", out);
     }
          addLine("<TABLE>", out);
    }
The SellerBean object calls the CORBA RegistrationHome.findLowCreditAccounts method implemented in the RegistrationServer.java file, and passes a reference to itself. The reference is passed as the SellerBean class implements the ReturnResults inteface declared in the Registration.idl.
//SellerBean.java
  public ArrayList auditAccounts() {
    try{
      NameComponent[] fullname = new NameComponent[2];
      fullname[0] = new NameComponent("auction", "");
      fullname[1] = new NameComponent(
                          "RegistrationBean", "");

      RegistrationHome regRef = 
	  RegistrationHomeHelper.narrow(
	                     nctx.resolve(fullname));
        regRef.findLowCreditAccounts(this);
        synchronized(ready) {
          try{
           ready.wait();
          }catch (InterruptedException e){}
        }
        return (returned);
      }catch (Exception e) {
       System.out.println("error in auditAccounts "+e);
      }
      return null;
  }
The RegistrationServer.findLowCreditAccounts method retrieves user records from the database registration table that have a credit value less than three. It then returns the list of Registration records in an ArrayList by calling the SellerBean.updateResults method that it has a reference to.

//RegistrationServer.java
  public void findLowCreditAccounts(
                final ReturnResults client)  
		throws Finder Exception {
    Runnable bgthread = new Runnable() {
      public void run() {
        Connection con = null;
        ResultSet rs = null;
        PreparedStatement ps = null;
        ArrayList ar = new ArrayList();

        try{
          con=getConnection();
          ps=con.prepareStatement(
		"select theuser, 
		balance from registration 
		where balance < ?");
          ps.setDouble(1, 3.00);
          ps.executeQuery();
          rs = ps.getResultSet();
          RegistrationImpl reg=null;
          while (rs.next()) {
            try{
             reg= new RegistrationImpl();
            }catch (Exception e) {
             System.out.println("creating reg"+e);
            }
            reg.theuser = rs.getString(1);
            reg.balance = rs.getDouble(2);
            ar.add(reg);
          }
          rs.close();

          RegistrationImpl[] regarray = 
		(RegistrationImpl [])ar.toArray(
		new RegistrationImpl[0]);
          client.updateResults(regarray);
        }catch (Exception e) {
         System.out.println(
                      "findLowCreditAccounts: "+e);
          return;
        }
        finally {
        try{
            if(rs != null) {
              rs.close();
            }
            if(ps != null) {
             ps.close();
            }
            if(con != null) {
              con.close();
            }
          }catch (Exception ignore) {}
        }
      }//run
    };
  Thread t = new Thread(bgthread);
  t.start();
  }
The SellerBean.updateResults method updates the global ArrayList of Registration records returned by the RegistrationServer object and notifies the SellerBean/auditAccounts method that it can return that ArrayList of Registration records to the AuctionServlet.
  public void updateResults(Registration[] ar) 
	throws registration.FinderException {
    if(ar == null) {
          throw new registration.FinderException();
    }
    try{
      for(int i=0; i< ar.length; i++) {
        returned.add(ar[i]);
      }
    }catch (Exception e) {
     System.out.println("updateResults="+e);
     throw new registration.FinderException();
    }
     synchronized(ready) {
     ready.notifyAll();
    }
  }

Using the Any type

The RegistrationServer.customSearch method uses the IDL Any type to pass in and return results. The customSearch is called by the AuctionServlet as follows:
  http://localhost.eng.sun.com:7001/
     AuctionServlet?action=customSearch&searchfield=2
The searchfield parameter can be set to a number or a string. The AuctionServlet.customFind method passes the search field directly to the SellerBean.customFind method and retrieves a String that is then displayed to the user.
  private void customSearch(ServletOutputStream out,
               HttpServletRequest request) 
               throws IOException{

    String text = "Custom Search";
    String searchField=request.getParameter(
                                 "searchfield");

    setTitle(out, "Custom Search");
    if(searchField == null ) {
      addLine("Error: SearchField was empty", out);
      out.flush();
      return;
    }
    try{
      addLine("<BR>"+text, out);
      SellerHome home = (SellerHome) 
                           ctx.lookup("seller");
      Seller si= home.create();
      if(si != null) {
        String displayMessage=si.customFind(
                                   searchField);
        if(displayMessage != null ) {
          addLine(displayMessage+"<BR>", out);
        }
      }
    }catch (Exception e) {
     addLine("AuctionServlet customFind error",out);
     System.out.println("AuctionServlet " + 
                        "<customFind>:"+e);
    }
    out.flush();
  }
The SellerBean.customFind method calls the RegistrationHome object implemented in the RegistrationServer.java class, and depending on whether the searchField can be converted into a double or a string, inserts this value into an object of type Any. The Any object is created by a call to the ORB, orb.create_any();

The customFind method also uses an out parameter, count, of type int that returns the number of records found. The value of count is retrieved using count.value when the call returns.

//SellerBean.java
  public String customFind(String searchField) 
	   throws javax.ejb.FinderException, 
	   RemoteException{

  int total=-1;
  IntHolder count= new IntHolder();

  try{
      NameComponent[] fullname = new NameComponent[2];
      fullname[0] = new NameComponent("auction", "");
      fullname[1] = new NameComponent(
                          "RegistrationBean", "");

      RegistrationHome regRef = 
        RegistrationHomeHelper.narrow(
                            nctx.resolve(fullname));
      if(regRef == null ) {
        System.out.println(
                     "cannot contact RegistrationHome");
        throw new javax.ejb.FinderException();
      }
      Any sfield=orb.create_any();
      Double balance;
      try{
        balance=Double.valueOf(searchField);
        try {
            sfield.insert_double(balance.doubleValue());
        }catch (Exception e) {
         return("Problem with search value"+balance);
        }
       sfield=regRef.customSearch(sfield,count);
       if(sfield != null ) {
         total=sfield.extract_long();
       }
       return(total+" 
	accounts are below optimal level from" +
	count.value+" records");
     }catch (NumberFormatException e) {
      sfield.insert_string(searchField);
      Registration reg;
      if((reg=RegistrationHelper.extract(
	  	regRef.customSearch(
	  	         sfield,count))) 
	  	         != null ) {
        return("Found user "+reg.getUser() +" 
		who has email address "+
		reg.getEmailAddress());
      }else {
       return("No users found who have email address " +
		searchField);
      }
     }
    }catch(Exception e){
        System.out.println("customFind problem="+e);
        throw new javax.ejb.FinderException();
    }
  }
The return value from the call to customFind is extracted into an object of type Any and a String is constructed with the output displayed to the user. For simple types, the extract_<type> method of the Any object can be used. However, for the Registration type, the RegistrationHelper class is used.
  Registration reg =
    RegistrationHelper.extract(
                 regRef.customSearch(sfield,count))
The RegistrationServer.customSearch method determines the type of Object being passed in the searchField parameter by checking the .type().kind().value() of the Any object.
 if(searchField.type().kind().value() == 
                         TCKind._tk_double)
Finally, because the customSearch method returns an object of type Any, a call to orb.create_any() is required. For simple types like double, the insert_<type> method is used. For a Registration record, the RegistrationHelper class is used: RegistrationHelper.insert(returnResults, regarray[0]).
//RegistrationServer.java
  public Any customSearch(Any searchField, 
                          IntHolder count){
    Any returnResults= orb.create_any();

    int tmpcount=count.value;
    if(searchField.type().kind().value() == 
                          TCKind._tk_double){
// return number of balances greater 
// than supplied amount
      double findBalance=searchField.extract_double();
      Connection con = null;
      ResultSet rs = null;
      PreparedStatement ps = null;
      try{
        con=getConnection();
        ps=con.prepareStatement("select count(*) from 
                      registration where balance < ?");
        ps.setDouble(1, findBalance);
        ps.executeQuery();
        rs = ps.getResultSet();
        if(rs.next()) {
          tmpcount = rs.getInt(1);
        }
        count.value=tmpcount;
        rs.close();
       }catch (Exception e) {
                 System.out.println("custom search: "+e);
                 returnResults.insert_long(-1);
                 return(returnResults);
       }
       finally {
         try{
           if(rs != null) { rs.close(); }
           if(ps != null) { ps.close(); }
           if(con != null) { con.close(); }
         } catch (Exception ignore) {}
       }
         returnResults.insert_long(tmpcount);
         return(returnResults);
     }else if(searchField.type().kind().value() == 
		TCKind._tk_string) {
      // return email addresses that match supplied address
      String findEmail=searchField.extract_string();
      Connection con = null;
      ResultSet rs = null;
      PreparedStatement ps = null;
      ArrayList ar = new ArrayList();
      RegistrationImpl reg=null;
      try{
        con=getConnection();
        ps=con.prepareStatement("select theuser, 
           emailaddress from registration 
	   where emailaddress like ?");
        ps.setString(1, findEmail);
        ps.executeQuery();
        rs = ps.getResultSet();
        while (rs.next()) {
          reg= new RegistrationImpl();
          reg.theuser = rs.getString(1);
          reg.emailaddress = rs.getString(2);
          ar.add(reg);
        }
        rs.close();

        RegistrationImpl[] regarray = 
             (RegistrationImpl [])ar.toArray(
	     new RegistrationImpl[0]);
          RegistrationHelper.insert(
                               returnResults, 
                               regarray[0]);
          return(returnResults);
       }catch (Exception e) {
        System.out.println("custom search: "+e);
        return(returnResults);
       }
       finally {
        try{
          if(rs != null) { rs.close(); }
          if(ps != null) { ps.close(); }
          if(con != null) { con.close(); }
        } catch (Exception ignore) {}
      }
    }
    return(returnResults);
  }

Conclusion

As you can see, converting the application to use RMI or CORBA requires very little change to core programs. The main difference has been the initialization and naming service. By abstracting these two areas in your application away from the business logic you ease migration between different distributed object architectures.

_______
1 As used on this web site, the terms "Java virtual machine" or "JVM" mean a virtual machine for the Java platform.

[TOP]



[ This page was updated: 13-Oct-99 ]

Products & APIs | Developer Connection | Docs & Training | Online Support
Community Discussion | Industry News | Solutions Marketplace | Case Studies
Glossary - Applets - Tutorial - Employment - Business & Licensing - Java Store - Java in the Real World
FAQ | Feedback | Map | A-Z Index
For more information on Java technology
and other software from Sun Microsystems, call:
(800) 786-7638
Outside the U.S. and Canada, dial your country's AT&T Direct Access Number first.
Sun Microsystems, Inc.
Copyright © 1995-99 Sun Microsystems, Inc.
All Rights Reserved. Legal Terms. Privacy Policy.