Building Distributed Applications with Java and CORBA

advertisement
Building Distributed Applications with Java
and CORBA
Dr. Dobb's Journal April 1998
Eliminating problems with code distribution, versioning, and
more
By Bryan Morgan
Bryan is a senior member of the technical staff with TASC Inc. He can be reached at
bmorgan@gulf.net.
The Object Management Group (OMG) was formed to help define product-independent object
standards in the form of a distributed object architecture -- the Common Object Request Broker
Architecture (CORBA). CORBA is a vendor-independent operating system. In addition, CORBA is a
distributed architecture that currently specifies a total of 18 services (known as
"CORBAServices") for handling transactions, security, naming, object-lifecycle management, and
other tasks. In short, CORBA is a specification that defines how objects interoperate over a
network. The actual implementation of this architecture is left to individual software vendors. (For
specific information on the OMG and its activities, visit http://www.omg.org/.)
Although it's been around since 1991, interest in CORBA is growing due to ntiered client/server
development efforts. n-tiered development separates the user interface from the application logic
and the underlying data. In this manner, individual tiers can be modified or scaled without requiring
changes to other tiers. Because CORBA is inherently object based, it works well as an architecture
for building n-tiered client/server systems.
Java, however, brings forward the concept of platform-independent, object-oriented applications
or applets that can be mobile and dynamically loaded from a remote server. Using Java applets, code
doesn't actually even need to be installed on a user's machine. Instead, code can be loaded
dynamically to users as required. In theory, an n-tiered web application written in Java eliminates
the problems of code distribution and versioning. Each time users hit your site, they receive a new
version.
A CORBA Backgrounder
CORBA objects are accessed through the use of an interface, which is like a Java interface in that
it is used to describe -- not implement -- an object's attributes and methods. CORBA interfaces,
regardless of the programming language to be used, are first described using the OMG's Interface
Definition Language (IDL). IDL can only be used to define attributes, methods, and parameters to
those methods within an interface. Once the IDL has been constructed for a interface, it is
compiled using a vendor's IDL-to-some-language compiler. (For instance, Visigenic includes an IDLTo-Java compiler with its VisiBroker For Java.) In addition, the OMG also defines mappings
between IDL and languages such as C++, Java, and Cobol. Once an official mapping has been defined,
IDL compilers will produce code that can be used with any vendor's product. This means that you
could use the VisiBroker IDL compiler to produce Java code for use with Iona Technologies
OrbixWeb Object Request Broker (ORB).
One CORBA application never talks directly to another. Instead, an application issues requests for
interfaces to the ORB -- the most important piece of the software architecture -- running on the
local machine. The local ORB then passes the request to an ORB on a remote machine. The remote
ORB then locates the appropriate object and passes an object reference back to the requester. In
short, a CORBA 2.0-compliant ORB can:

Lookup and instantiate objects on remote machines.

Marshal parameters from one programming language (such as C++) to another language (such
as Java).

Handle security across your machine's local boundary.

Retrieve and publish metadata on objects on the local system for another ORB.

Invoke methods on a remote object using static method invocation described by a
downloaded stub.

Invoke methods on a remote object using dynamic method invocation.

Auto-start objects that aren't currently up and running.

Route callback methods to the appropriate local object being managed.

Communicate with other ORBs using the Internet Inter-ORB Protocol (IIOP).
Important functional pieces within an ORB include:

The Basic Object Adapter (BOA), which provides policies for activating object
implementations (shared server, unshared server, server-per-method).

The Implementation Repository, which stores information about each object located on the
server.

The Object Activation Daemon, which is responsible for activating a server upon receiving a
client's request for an object.

The Interface Repository (IR), which is an online database of metadata about ORB Object
types.

The Dynamic Invocation Interface (DII), which allows client programs to dynamically
construct requests for objects based on information contained in the IR.
All implementation details of the ORB are hidden from you. To use an ORB to access remote CORBA
objects, the application obtains a reference to the ORB object, and calls the appropriate methods
to access remote objects.
IIOP is another interesting feature of CORBA 2.0. IIOP is a wire-level protocol that resides on top
of TCP/IP. It lets one vendor's CORBA 2.0-compliant ORB exchange objects with another's. Before
IIOP, each vendor's ORB spoke a proprietary protocol and would only interoperate with other ORBs
of the same type. Obviously, for objects to coexist and work together on something like the
Internet, a standard protocol was required so that CORBA could move beyond its initial status as a
niche technology.
A CORBA application that is written using Java would have the advantages of being platformindependent, object-oriented, and dynamically loaded, while including support for legacy systems
written in C++, Smalltalk, Ada, or Cobol, and supporting scalability from the level of an NT server all
the way up to a mainframe running MVS. In addition, this application could have full access to the
variety of CORBAServices and support for the access of distributed objects running under realtime embedded operating systems.
Here, I'll examine the concepts behind CORBA application development in Java, using Borland's
JBuilder 1.0 and Visigenic's VisiBroker For Java 3.0, a CORBA 2.0-compliant Object Request Broker
(ORB). Borland's JBuilder client/server version includes a copy of the VisiBroker For Java ORB. All
the code I present can be compiled using the Java Development Kit 1.1 (http://www.javasoft.com/).
Computing with Distributed Objects
Distributed applications can currently be built using Java in either a two-tier (client and server) or
a multiple-tier architecture. If the size of the application is fairly small, little code will ever be
reused, and if the number of users is small (less than 30), a two-tier architecture will work fine and
you are free to consult another source on how to construct a Java client/server application using
JDBC. However, if one or more of those requirements are not met, you should probably look into
examining a three-tiered solution. The first tier will contain the user interface and some basic logic
for handling interactions with the user. It will instantiate and use objects located on the middle
tier (the object layer) using some type of object model. Since many applications use some type of
relational database, assume the third tier will be the database server containing all data, and any
necessary triggers and stored procedures.
Java Development with Borland JBuilder
JBuilder is a Java 1.1-only tool that includes support for JDBC, the 1.1 delegation-based event
model, Java RMI, and JavaBeans. The JBuilder development environment is similar to that of
Delphi: User interfaces can be built simply by dragging and dropping components onto a form. As
this is done, Java code is generated behind the scenes. Event handlers can also be created by
simply pointing and clicking with the mouse. JBuilder is unique in that it supports two-way editing.
While many environments generate code based on your actions in the design environment, JBuilder
actually modifies the visual form as you modify the code. This lets you avoid "sacred" areas in the
code (to avoid confusing the development environment, such areas traditionally cannot be modified).
For the purpose of illustration, I'll build a Java application for viewing and editing human-resources
information on employees of AlphaBeta Technologies, a fictional software company (the complete
source code and related files are available electronically; see "Resource Center," page 3). The
distributed architecture could be a "standard" two-tier client/server system using an Oracle
database server, or a large-scale telecommunications project that requires dozens of computers to
interoperate and share information in a real-time manner. The client application will be written in
Java and will access an HR object server via CORBA. For simplicity, the object server will hardwire
all data into memory, although in a production application, it could use JDBC to query a database
server. Users will be allowed to query for specific employees and can modify data on employees
before saving the information back to the server.
After creating a new project, selecting the File|New menu option will let you invoke the New
Application Wizard (see Figures 1 and 2). Filling in the appropriate information in the Wizard's two
dialog boxes creates a Java application class (containing the main() method) and Java frame. By
dragging and dropping a few components onto the created frame (see Figure 3), you complete the
layout of a basic employee information form. As you can see, the application supports several
operations: First Record, Next Record, Previous Record, Last Record, Update, Delete, and New
Employee. Because these buttons represent actions within this application, each ButtonDown event
triggers the calling of an appropriate method on the remote Employee object.
Building CORBA Objects Using VisiBroker for Java
VisiBroker For Java 3.0 is a CORBA 2.0-compliant ORB from Visigenic Software. This ORB has been
licensed by Netscape, Borland, Oracle, and Novell (among others). Because VisiBroker is included
with the Client/Server version of Borland's JBuilder, I'll use it to build the CORBA application.
There are only a few basic steps involved in building the CORBA objects. These steps are required
in every Java/CORBA application that uses any ORB, no matter how simple or complex the
application.
1.
Set up the development/run-time environment by modifying the PATH and by setting the
appropriate environment variables.
2. Write a definition for each object using IDL.
3. Compile the IDL code to produce client stub code and server skeleton code.
4. Write the client application code.
5. Write the server object code.
6. Compile the client and server code.
7. Start the server.
8. Run the client application.
The specific tasks required for setting up your environment (step 1) are detailed in the VisiBroker
documentation. Once completed, you design the object's interfaces using the IDL. Just as related
classes are grouped into packages in Java, IDL groups these related interfaces into modules. Listing
One is an IDL module (TheModule) that contains the interface (TheInterface), which contains a
single attribute (TheAttribute) defined to be an integer value.
Compiling this IDL module using an IDL-to-Java compiler (such as Visigenic's idl2java) results in the
Java interface in Listing Two. Each Java data type maps to a specific IDL data type (see Table 1).
Variables are declared in IDL using the IDL data type you want to correspond to the resulting Java
data type. IDL also supports user-defined data types and constructed data types (such as enums,
structs, unions, sequences, and arrays), which are converted to Java.
Methods can be declared in IDL as well. The way in which parameters are passed to these methods
is controlled by the use of the in, out, and inout keywords. The in keyword specifies that the
parameter is to be passed from the caller to the object; out specifies that the parameter is to be
passed from the object to the caller; and inout specifies that the parameter is to be passed in both
directions.
Because the server will store the sample data set in memory (in the form of multiple Employee
objects), you need to define:

A struct representing a single Employee containing information about an employee.

A sequence representing a list of Employee objects that can be iterated.

An EmployeeInfo interface representing all data and methods required by our server
object.
You already know (from the requirements of the user interface) that you need methods that
retrieve individual Employee objects, iterate through the available Employees, and modify
(add/update/delete) the Employee list. Listing Three contains the IDL necessary to satisfy these
requirements.
After running the Visigenic idl2java tool, a set of 14 Java files are created. These files represent
what, in CORBA-speak, are known as client stubs and server skeleton files. The client stubs
represent the interface from the client application to the client ORB. No implementation details
take place within these classes. The server skeletons, however, are where your actual programming
will take place. Before implementing the server class (EmployeeInfoImpl), look at Listing Four,
which is generated by the idl compiler and represents the Java representation of the EmployeeInfo
interface and associated structures and sequences.
The next step is to rename the _example_EmployeeInfo.java file (and the _example_EmployeeInfo
class contained within) to EmployeeInfoImpl. The _example_EmployeeInfo file contains a skeleton
implementation class to be filled in with details. This server class actually manages the employee
information in a private Java Vector object. Methods within this class will be called remotely from a
client. Listing Five is the completed EmployeeInfoImpl class.
To have a fully functional CORBA server, you need to register this server class with the server
ORB. To do so, a CORBA application initializes the ORB and its BOA, then registers the
EmployeeInfoImpl object with the ORB. Once this has been completed, this object can be retrieved
from any remote CORBA client. Listing Six shows the basic statements required to do this using
VisiBroker.
Because of the seamless nature of Java and CORBA integration, nearly all code on the client looks
like standard Java. Once the CORBA object has been obtained, it can be used within a Java
application just like any normal Java object. The trick lies in obtaining that object. To do so, you
need only initialize the client ORB, then retrieve the object from the server. Listing Seven shows
the code required to do this.
The infoObject variable is a private variable within the EmployeeFrame class. (EmployeeFrame was
generated by JBuilder when we designed the form.) Once you have a valid object, its methods can
be called in response to user events.
Running the Application
To get the server application up and running, execute the osagent command. OSAgent (also called
the "VisiBroker Smart Agent") provides a fault-tolerant object location service. Its job is to locate
and return an object when it is requested. After osagent is up and running, start up the server
application using the Visigenic Java interpreter, vbj. This can be done by executing: vbj Server.
Finally, start the client application using vbj as well: vbj EmployeeApp. Figure 4 shows the
EmployeeInfo application running.
Conclusion
To get started with Java and CORBA development, visit one of the ORB vendor's web sites and
download a trial version of its ORB. For a complete listing of commercial CORBA vendors, visit the
OMG at http://www.omg.org/.
DDJ
Listing One
Module TheModule{
interface TheInterface
{
long TheAttribute;
};
};
Back to Article
Listing Two
package TheModule;public interface TheInterface
{
public int TheAttribute;
}
Back to Article
Listing Three
// EMPLOYEE represents a single employee's personal informationmodule EmployeeServer
{
struct Employee
{
string Name;
string Address;
string City;
string State;
long long SSN;
float Salary;
string Title;
};
typedef sequence<Employee> EmployeeList;
interface EmployeeInfo
{
attribute EmployeeList theList;
readonly attribute long Count;
Employee getEmployee(in long index);
void deleteEmployee(in long index);
void newEmployee(in Employee newPerson);
void updateEmployee(in long index, in Employee newData);
};
};
Back to Article
Listing Four
// Employee.java - Java version of IDL struct Employeefinal public class Employee {
public java.lang.String Name;
public java.lang.String Address;
public java.lang.String City;
public java.lang.String State;
public long SSN;
public float Salary;
public java.lang.String Title;
public Employee()
{
}
public Employee(java.lang.String Name, java.lang.String Address,
java.lang.String City, java.lang.String State, long SSN,
float Salary, java.lang.String Title)
{
this.Name = Name;
this.Address = Address;
this.City = City;
this.State = State;
this.SSN = SSN;
this.Salary = Salary;
this.Title = Title;
}
public java.lang.String toString()
{
org.omg.CORBA.Any any = org.omg.CORBA.ORB.init().create_any();
EmployeeServer.EmployeeHelper.insert(any, this);
return any.toString();
}
}
// EmployeeInfo.java - Java interface version of IDL interface EmployeeInfo
public interface EmployeeInfo extends org.omg.CORBA.Object {
public void theList(EmployeeServer.Employee[] theList);
public EmployeeServer.Employee[] theList();
public int Count();
public EmployeeServer.Employee getEmployee(int index);
public void deleteEmployee(int index);
public void newEmployee(EmployeeServer.Employee newPerson);
public void updateEmployee(int index, EmployeeServer.Employee newData);
}
Back to Article
Listing Five
package EmployeeServer;public class EmployeeInfoImpl extends EmployeeServer._EmployeeInfoImplBase
{
private java.util.Vector Employees;
/** Construct a persistently named object. */
public EmployeeInfoImpl(java.lang.String name)
{
super(name);
Employees = new java.util.Vector();
}
/** Construct a transient object. */
public EmployeeInfoImpl()
{
super();
Employees = new java.util.Vector();
}
public EmployeeServer.Employee getEmployee(int index)
{
if (index <= (Employees.size() -1))
return (EmployeeServer.Employee)Employees.elementAt(index);
else
return null;
}
public void deleteEmployee(int index)
{
Employees.removeElementAt(index);
}
public void newEmployee(EmployeeServer.Employee newPerson)
{
Employees.addElement(newPerson);
}
public void updateEmployee(int index, EmployeeServer.Employee newData)
{
Employees.setElementAt(newData, index);
}
public void theList(EmployeeServer.Employee[] theList)
{
// implement attribute writer...
}
public EmployeeServer.Employee[] theList()
{
// implement attribute reader...
return null;
}
public int Count()
{
return Employees.size();
}
}
Back to Article
Listing Six
public class Server{
public static void main(String[] args)
{
//Initialize the ORB
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args, null);
//Initialize the BOA
org.omg.CORBA.BOA boa = orb.BOA_init();
//Create the EmployeeInfo object
EmployeeServer.EmployeeInfo info =
new EmployeeServer.EmployeeInfoImpl("EmployeeInfo");
//Export the object
boa.obj_is_ready(info);
System.out.println(info + " is ready.");
//Wait for incoming requests
boa.impl_is_ready();
}
}
Back to Article
Listing Seven
// Initialize the ORBorg.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init();
// Locate and retrieve EmployeeInfo object from server
EmployeeFrame.infoObject =
EmployeeServer.EmployeeInfoHelper.bind(orb, "EmployeeInfo");
Back to Article
Download