|
|
Unsigned Values:
The Java data types
This means that in the case of a
IDL char Types:
The Java language uses 16-bit unicode, but the IDL
When mapping between the Java language
IDL string Types:
The IDL
An IDL Setting up IDL MappingsJava 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 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 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
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
This example maps the Java array 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
This example maps the exception CreateException { }; interface RegistrationHome { RegistrationPK create( in string theuser, in string password, in string emailaddress, in string creditcard) raises (CreateException); } Other IDL typesThese 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:
The IDL
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 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
The IDL 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
This example declares an IDL struct ErrorHandler { LoginError errortype; short retries; };
IDL union:
An IDL
This example maps the 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 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 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 CORBA in the Auction ApplicationThe container-managed RegistrationBean from the auction application is completely replaced with a standalone CORBA RegistrationServer that implements the registration service. The CORBARegistrationServer is built by creating and compiling
an IDL mappings file so client programs can communicate with the registration server.
The CORBA RegistrationServer ImplementationThis section describes the Registration.idl file, which maps theRegistrationHome 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
IDL Mappings FileHere is the Registration.idl file that maps the data types and methods used in theRegistrationHome 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 FileThe 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 idltojava -fno-cpp Registration.idlOther 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 SkeletonsCorba 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 IDLRegistrationHome 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
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 BrokerThe 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 AccessibleAlthough 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
These next sections describes the 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 ORBThe 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
In the example code, a 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 BOA boa = ((com.ooc.CORBA.ORB)orb).BOA_init( args, props); ... boa.impl_is_ready(null);
Although both the java com.ooc.CosNaming.Server -OAhost localhost -OAport 1060Once 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 ClientsCORBA 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 theNameComponents .
The 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 classesReferences to remote objects in CORBA use aHelper class to retrieve
a value from that object. A commonly used method is the Helper
narrow method, which ensures the object is cast correctly.
IntHolder count= new IntHolder(); sfield=regRef.customSearch(sfield,count); System.out.println("count now set to "+count.value); Garbage CollectionUnlike 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, theorb.disconnect(object) needs
to be called to allow the object to be garbage collected.
CORBA CallbacksThe newfindLowCreditAccounts method is called from
the AuctionServlet using the Uniform Resource Locator (URL)
http://localhost:7001/AuctionServlet?action=auditAccounts .
The //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 typeTheRegistrationServer.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=2The 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 //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); } ConclusionAs 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.
_______ [TOP]
|