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 Type | IDL Format | Runtime Exception |
| byte | octet | |
| boolean | boolean | |
| char | char | DATA_CONVERSION |
| char | wchar | |
| double | double | |
| float | float | |
| int | long | |
| int | unsigned long | |
| long | long long | |
| long | unsigned long long | |
| short | short | |
| short | unsigned short | |
| java.lang.String | string | DATA_CONVERSION |
| java.lang.String | wstring | MARSHAL |
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]