©1997-98 Barry Cornelius
Using CORBA and JDBC to produce Three Tier Systems is the name of a document used to support a talk. It assumes some knowledge of programming with Java.
The pages of this document are Copyright 1997-98 Barry Cornelius.
Barry Cornelius,
IT Service, University of Durham, Durham DH1 3LE, England
+44 191 374
4717 +44 191 374
3741
mailto:Barry.Cornelius@durham.ac.uk
Client-server computing has traditionally been done using sockets. A socket is a point of connection within a program to the Internet, and a program can start to communicate when it arranges for the socket to bind to a port of the computer that is running the program.
The Java classes
java.net.DatagramSocket
,
java.net.ServerSocket
and java.net.Socket
provide easy-to-use ways of writing programs that use sockets. For some
initial experiences of using sockets when teaching distributed
computing, see the paper that
Alan Battersby (Nottingham Trent University) presented at last year's
Java in the Computing Curriculum conference:
http://www2.ulst.ac.uk/misc/cticomp/init.html.
However, in Java, client-server computing can be performed without having to work at the level of sockets. This is because Java has several application programming interfaces (APIs) that offer higher levels of abstraction for client-server computing.
In this document, we look at two APIs: Common Object Request Broker Architecture and Java DataBase Connectivity. The latter is a way in which a Java program can use SQL to access database servers, whereas the CORBA API allows us to rise up above the socket-level details of client-server computing when we want to access objects distributed across different computers.
In the past, client-server computing has usually involved just two computers talking to one another. Often a lot of the processing is going on in the client, which often means that the client is a big program. More recently, systems have been produced that use an intermediate layer: the client just provides a user interface whereas the intermediate layer contains all the complicated application logic. The document also introduces the idea of three tier systems.
We will look at an unsophisticated three tier system. It uses a
WWW browser that downloads a Java applet (AgesDBLet
)
from a WWW server running on a computer called perseus
:
The Java applet will display three buttons and three textfields:
A click on a button will cause the Java applet to send a request (using
JavaIDL) to a Java application (AgesDBServer
) that is
running on perseus
.
This Java application will use JDBC to send an appropriate SQL
statement to a database server (msql2d
) that is running on
a computer called hercules
.
The purpose of the Java applet is to present a user interface: most of
the hard work will be done by the AgesDBServer
program. For
example, if we type a person's surname, first name and age into the
three textfields, and then click on the Add
button, the
only thing that the Java applet will do is to pass this information on
to the AgesDBServer
program. The AgesDBServer
program has an object that has a method for adding a person, a method
for removing a person, and a method for obtaining a person's details.
These methods contact the database server to update or inspect the
database. In order to provide code for these methods, we need to know
something about the JDBC API.
The Structured Query Language (SQL) is a language for use with
relational databases.
One of the most used SQL statements is the SELECT
statement: it is used to inspect a table of information. The statement:
SELECT last, first FROM ages WHERE age >= 21would look at a table called
ages
and produce a new table
containing the surnames and first names of the people having an age of
at least 21 years.
Although SQL has been evolving since the early 1980's, it was in 1990 that the SQL Access Group defined the Call Level Interface (CLI) as a standard for accessing databases. To implement it, you need a driver that can translate a CLI call into the language used to access a particular database.
Microsoft's Open Database Connectivity (ODBC), an API for Microsoft Windows that implements an extended version of the CLI, was first released in 1992. Most database vendors (including CA/Ingres, IBM, Informix, Oracle and Sybase as well as Microsoft) now support the ODBC API.
ODBC is an API written for the programming language C and, although the Java Native Interface (JNI) allows a Java program to call a C function, there are disadvantages with calling C from Java:
The JDBC API forms part of JDK 1.1.x: it is in the
java.sql
package.
It was not part of JDK 1.0.2: however, if you have a JDK 1.0.2 program,
a version of the JDBC API can be downloaded for use with JDK 1.0.2.
The JDBC API is implemented via a driver manager that can support multiple drivers connecting to different databases. A JDBC driver can either be written entirely in Java so that it can be downloaded as part of an applet or it can be implemented using native methods to bridge to existing database access libraries.
At the WWW page http://java.sun.com/products/jdbc/jdbc.drivers.html, Sun gives details of the drivers that are available from a number of vendors. On this page, they distinguish between four types of JDBC drivers:
Hughes Technologies has produced mSQL (or Mini SQL). This is a "light-weight relational database management system that has been designed to provide rapid access to data sets with as little system overhead as possible". The system is comprised of a database server that will run on most Unix platforms together with various tools that allow a user or a client application to communicate with the server. Use of mSQL in any commercial environment requires the purchase of a license from Hughes Technologies. However, free licenses are provided to organisations such as Universities, schools and registered charities. For more details about mSQL, see http://www.Hughes.com.au.
George Reese, a software engineer from Minneapolis, provides a free JDBC driver for mSQL. This can be downloaded from the WWW page http://www.imaginary.com/Java/.
An ODBC driver for use with Microsoft Windows 95/NT products, such as Access and SQL Server, can be obtained from http://support.microsoft.com/support/kb/articles/Q159/6/74.asp. Once you have an ODBC driver, then the easiest method to use is the JDBC-ODBC bridge. This is provided as part of JDK 1.1.x.
A Java program that wants to use JDBC to access a database server will normally consist of the following steps:
getConnection
method (of the class
java.sql.DriverManager
) to establish a connection to a
database. createStatement
, prepareStatement
or
prepareCall
method. prepareStatement
and prepareCall
methods, use calls of setXXX
methods to establish any
parameters that are needed by the statement. execute
, executeQuery
or
executeUpdate
to execute the statement. getConnection
. commit
or rollback
yourself.
Here is an incomplete draft of the AgesDBServer
program:
1:public class AgesDBServer { 2: public static void main(String[] args) { 3: AgesDBServant AgesDBRef = new AgesDBServant(); 4: } 5:}It creates an object of the class
AgesDBServant
. Here is
an incomplete draft of this class:
6:public class AgesDBServant { 7: public AgesDBServant() { 8: Class.forName("COM.imaginary.sql.msql.MsqlDriver"); 9: String tURLString = "jdbc:msql://hercules.dur.ac.uk:4333/bjc1"; 10: iConnection = DriverManager.getConnection(tURLString, "", ""); 11: } 12: public synchronized void addPerson(String rLast, String rFirst, String rAge){ 13: Statement tStatement = iConnection.createStatement(); 14: String tSQLString = "insert into ages values('" 15: + rFirst + "', '" + rLast + "', " + rAge + ")"; 16: int tNumberOfRows = tStatement.executeUpdate(tSQLString); 17: tStatement.close(); 18: } 19: private Connection iConnection = null; 20:}When the constructor of this class is executed, the class
COM.imaginary.sql.msql.MsqlDriver
is loaded. This class is
the main class of George Reese's JDBC-mSQL driver.
The constructor also establishes a connection with a database called
bjc1
known to an msql server operating through port 4333 on
hercules.dur.ac.uk
. The Java application's communications
with this msql server will be provided by the JDBC-mSQL driver.
Besides the constructor, this
class also provides methods called getPerson
,
addPerson
and removePerson
. Each of these
leads to a call of a method to update or inspect the database. In the
above code, only the
addPerson
method is shown. It causes the JDBC's executeUpdate
method to be executed with an argument containing an SQL
INSERT
statement like:
insert into ages values('Cornelius', 'Barry', 42)
If we now want to modify the AgesDBServer
program so that
the methods of the AgesDBRef
object are executable by
programs on other computers, we need to look at how to access remote
objects.
AgesDBServer
to make the object available CORBA is the Common Object Request Broker Architecture. It is an ongoing effort by the Object Management Group (OMG) to define interfaces for interoperable software. Work on CORBA started several years ago, and there are now over 700 members of OMG including virtually all of the computer manufacturers and most of the major software vendors.
If you are using CORBA, communications between two computers is performed using an Object Request Broker (ORB). An object in a program running on some computer (the client) can use the ORB to access the public attributes of another object in some other program perhaps on a different computer (the server) that is also using an ORB. The client object can:
Although an ORB can be running on a single computer, it is more likely to be used to aid communication between one or more client computers and a server computer. If an ORB is unable to satisfy a request from a client, then, if it is connected to other ORBs (elsewhere in the world), the request may be satisfied by an object known to a remote ORB.
One of the key aspects of CORBA is the OMG
Interface Definition Language
(IDL). This is the language that is used to describe the interface
that an object has. It is a language that looks something like C, C++
or Java. For example,
it has function declarations and interfaces;
it has types such as boolean
, char
,
string
, short
, long
,
float
and double
.
Having produced an IDL file that describes the interface that an object
has, we also need some code
for the server that creates an object that implements that interface,
and we will also need codes for clients that wish to access the
object. These codes need to be written in real programming
languages.
In order for an interface written in IDL to be usable with existing
languages, there
needs to be a mapping that says what each IDL feature is in each
language.
For example, the IDL-to-Java mapping defines that an IDL
long
is an int
in Java, an IDL
string
is a String
in Java, ... .
Currently, there are bindings for Ada, C, C++, Java and Smalltalk.
The client and server programs may be in different languages. Each program can run on any operating system written in any language so long as you have a compiler to translate the interface from IDL into that language on that operating system.
There are many vendors providing ORBs. In this document, we will be using JavaIDL from Sun Microsystems. Some other ORBs are listed in the Further information about some other ORBs section given later.
At the present time, Sun's JavaIDL is not part of the JDK: however, it can be downloaded from Sun's JavaIDL page at http://java.sun.com/products/jdk/idl/ and used with JDK 1.0.2 or JDK 1.1.x. JavaIDL will form part of JDK 1.2, and a beta release of JDK 1.2 is available from http://java.sun.com/products/jdk/1.2/. A version of JavaIDL is also supplied with Sun's Solaris 2.6 operating system.
We want the Java applet to access the methods of the
AgesDBRef
object of the Java application
(AgesDBServer
) running on perseus
.
In order to do this using CORBA, we will need an interface (written
in IDL) in the file AgesDB.idl
that defines the services
available from the object:
21:module AgesDBApp { 22: interface AgesDB { 23: void getPerson(in string rLast, out string rFirst, out string rAge); 24: void addPerson(in string rLast, in string rFirst, in string rAge); 25: void removePerson(in string rLast); 26: }; 27:};When JavaIDL's
idltojava
command is used to translate this
file into Java, it will also produce a class called
_AgesDBImplBase
in a subdirectory called
AgesDBApp
.
AgesDBServer
to make the object
available
Earlier, a draft of the class AgesDBServant
was given. We
can get AgesDBServant
to implement the above interface if
it extends the class _AgesDBImplBase
:
28:import AgesDBApp._AgesDBImplBase; 29:import org.omg.CORBA.StringHolder; 30:public class AgesDBServant extends _AgesDBImplBase { 31: public AgesDBServant() { 32: ... 33: } 34: public synchronized void getPerson(String rLast, 35: StringHolder rFirst, StringHolder rAge) { 36: ... 37: } 38: public synchronized void addPerson(String rLast, String rFirst, String rAge){ 39: ... 40: } 41: public synchronized void removePerson(String rLast) { 42: ... 43: } 44: private Connection iConnection = null; 45:}
Earlier, a draft of a Java application (AgesDBServer
) that
creates an object (AgesDBRef
) of this class was given. In
order for the methods of this object to be available to clients such as
our Java applet, AgesDBServer
can register the object with
the ORB's Naming Service. The following version of
AgesDBServer
asks the Naming Service to bind the name
AgesDB
to the object AgesDBRef
. After it has
done that, it sits waiting for clients to connect:
46:import org.omg.CosNaming.NameComponent; // AgesDBServer.java 47:import org.omg.CosNaming.NamingContext; 48:import org.omg.CosNaming.NamingContextHelper; 49:import org.omg.CORBA.ORB; 50:public class AgesDBServer { 51: public static void main(String[] args) { 52: try{ 53: ORB orb = ORB.init(args, null); 54: AgesDBServant AgesDBRef = new AgesDBServant(); 55: orb.connect(AgesDBRef); 56: org.omg.CORBA.Object objRef = 57: orb.resolve_initial_references("NameService"); 58: NamingContext ncRef = NamingContextHelper.narrow(objRef); 59: NameComponent nc = new NameComponent("AgesDB", ""); 60: NameComponent path[] = {nc}; 61: ncRef.rebind(path, AgesDBRef); 62: java.lang.Object sync = new java.lang.Object(); 63: synchronized (sync) { 64: sync.wait(); 65: } 66: } 67: catch (Exception rException) { 68: rException.printStackTrace(); 69: } 70: } 71:}
Having shown some of the code of the server, we will now look at the code of the client, the Java applet. The following WWW page assumes that all the classes we wish to download have been stored in a Java Archive:
72:<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN"> 73:<HTML><HEAD><TITLE> The AgesDBLet Program </TITLE></HEAD> 74:<BODY> 75:<APPLET ARCHIVE=AgesDBLet.jar CODE="AgesDBLet.class" WIDTH=700 HEIGHT=700> 76:<PARAM NAME=org.omg.CORBA.ORBInitialHost VALUE="perseus.dur.ac.uk"> 77:<PARAM NAME=org.omg.CORBA.ORBInitialPort VALUE=1050> 78:</APPLET> 79:</BODY></HTML>When the page is visited, the WWW browser will download all the classes in the Java Archive
AgesDBLet.jar
and then it will
start interpreting using an object of the class
AgesDBLet.class
passing to it the two parameters mentioned in the PARAM
tags.
If the Java applet wishes to access the methods of our object, then
it first has to connect to an ORB and find the object registered with
the name AgesDB
. This is done by the following code.
Because of the parameters that have been passed to the applet, the ORB
knows that we wish to use the Naming Service running on
perseus
:
80:ORB orb = ORB.init(this, null); 81:org.omg.CORBA.Object objRef = 82: orb.resolve_initial_references("NameService"); 83:NamingContext ncRef = NamingContextHelper.narrow(objRef); 84:NameComponent nc = new NameComponent("AgesDB", ""); 85:NameComponent path[] = {nc}; 86:iAgesDB = AgesDBHelper.narrow(ncRef.resolve(path));where
iAgesDB
is a private variable of the interface
AgesDB
. Having executed this code, the reference variable
iAgesDB
is a means by which the Java applet can refer to
the remote object.
The above code needs to be executed when the applet is loaded, and so
it is best placed in the applet's init
method. The
init
method also needs the following statements to add the
buttons and the textfields to the applet:
87:add(iGetButton); 88:add(iAddButton); 89:add(iRemoveButton); 90:add(iLastTextField); 91:add(iFirstTextField); 92:add(iAgeTextField);
Since this applet may be executed from a browser that only understands
JDK 1.0.2, we will override the handleEvent
method in order
to react to any clicks on the three buttons. For example, the following
code in the
handleEvent
method
will be executed if there is a click on the Add
button:
93:if (rEvent.target.equals(iAddButton)) { 94: iAgesDB.addPerson(iLastTextField.getText(), 95: iFirstTextField.getText(), iAgeTextField.getText()); 96: return true; 97:}
The client program does not have immediate access to the object on the
server. Instead, a
lot of magic takes place:
The value assigned to iAgesDB
in the AgesDBLet
program is a reference to an object that
is part of the client program.
This object is a stub that acts as a proxy for the remote
object. When the client program calls a method, say, it calls
iAgesDB.addPerson(...)
, then it is actually calling a
method called addPerson
of the stub. This method knows
nothing about how to add a person to the database. Instead, what it does
is to transmit the required operation to the server: it does this by
arranging for the method and its parameters to be marshalled
into a stream of bytes.
Within the Java application running on the server, there is the real
object. However, besides this real object
there is also a skeleton object (of class
_AgesDBImplBase
). The skeleton has a method that arranges
for any incoming requests to be unmarshalled. By this means, it finds
out what method is to be called and what arguments are to be supplied to
the call. It then calls the appropriate method of the real object with
the appropriate arguments.
If the method being called returns some result, then the skeleton's method will get the result, serialize the result, and pass the bytes back to the client computer. The method in the stub will receive these bytes, de-serialize them, and return a value to the caller.
So besides the code that you write for the client and the server, you
will also need code for the stub and the skeleton. Fortunately,
JavaIDL's
idltojava
command (which was mentioned earlier) will automatically generate the
stub and skeleton files for you.
First, compile the interface in the file AgesDB.idl
by
executing JavaIDL's idltojava
command:
setenv JAVAIDL_HOME /users/hplives/JavaIDL-1.1EA setenv PATH $JAVAIDL_HOME/bin:$PATH idltojava -fclient -fserver AgesDB.idlThis command creates a directory called
AgesDBApp
for the
stub and skeleton classes.
We need to compile AgesDBServer.java
, the Java application
of the middle tier. In order to compile this file, we will need access
to the bytecodes of the classes of the JavaIDL API. These classes are
provided in the file $JAVAIDL_HOME/lib/classes.zip
.
For a technical reason (to do with JavaStations), this file will be
unzipped, to create two directories in the current directory called
com
and org
.
Having done that, we can compile the Java application of the middle
tier with a JDK 1.1.x compiler:
javac AgesDBServer.java
Finally, compile the client program with a JDK 1.0.2 or JDK 1.1.x compiler:
javac AgesDBLet.javaThis compilation will use the JavaIDL classes in the
com
and org
directories that were created earlier. In order to
simplify the loading of these classes whenever the Java applet is
downloaded, we will use JDK 1.1.x's jar
command to build a
Java Archive that contains all of these files:
jar cf AgesDBLet.jar AgesDBLet.class AgesDBApp org com
Before running the server, we need to start JavaIDL's Naming Service
program. We can do this using JavaIDL's nameserv
command:
nameserv -ORBInitialPort 1050 &This command will produce output like:
Initial Naming Context: IOR:000000000000002849444c3a6f6d672e6f72672f436f734e616d696e672f4e616d696e67436 f6e746578743a312e3000000000010000000000000034000100000000000968657263756c657300 00805e0000001cafabcafe000000023448a50500000000000000080000000000000000 TransientNameServer: setting port for initial object references to: 1050
The server can then be started using the commands:
setenv CLASSPATH /users/dcl0bjc/public_html/Java:. java AgesDBServer -ORBInitialPort 1050 &
Having started the server, the Java applet can be run by anyone having a WWW browser that can handle Java Archives. Examples are Microsoft's Internet Explorer, Netscape's Navigator 3.0x and JDK 1.1.x's appletviewer:
appletviewer http://perseus.dur.ac.uk/~dcl0bjc/Java/ThreeTier/Ages/AgesDBLet.htmlBecause of an incompatibility, such applets do not work with Netscape's Navigator 4.0x. I have also used this applet as the applet behind one of the buttons on the desktop when using HotJava Views on a JavaStation.
The complete texts of the programs referred to in this document are in the directory located at http://www.dur.ac.uk/~dcl0bjc/Java/ThreeTier/Ages/.
Sun has a JDBC page at http://java.sun.com/products/jdk/1.1/docs/guide/jdbc/. A useful guide to JDBC is at http://java.sun.com/products/jdk/1.1/docs/guide/jdbc/getstart/introTOC.doc.html.
Sun's main JDBC page at http://java.sun.com/products/jdbc/ includes details about vendors who have JDBC drivers, an FAQ, and a link to download a JDBC for use with JDK 1.0.2.
Another JDBC FAQ is at http://javanese.yoyoweb.com/JDBC/FAQ.txt.
As mentioned above, details about mSQL can be obtained from http://www.Hughes.com.au and details
about the mSQL-JDBC driver can be obtained from http://www.imaginary.com/Java/. This latter page also contains a link to some documentation for the driver, details about the msql-jdbc
mailing list, and some details about George Reese's book Database Programming with JDBC and Java. This book is published by O'Reilly; its ISBN is ISBN 1-56592-270-0.
Netscape has a technical note about writing database applications in Java at http://developer.netscape.com/library/technote/database/web/.
Sun's main JavaIDL page is at http://java.sun.com/products/jdk/idl/. On that page, you will find links to some useful pages entitled Documentation, Examples, Download/EarlyAccess and Other references. In particular, the Download/EarlyAccess link enables you to download the Early Access Release of JavaIDL for use with JDK 1.0.2 or JDK 1.1.x.
The above code is derived from Sun's Hello
example which
is subject to the copyright and licensing information given at http://java.sun.com/products/jdk/idl/docs/examplelicense.html.
The details of the JavaIDL being released with JDK 1.2 are documented at http://java.sun.com/products/jdk/1.2/docs/guide/idl/.
In many ways, Remote Method Invocation (RMI) and CORBA are competing technologies. Sun's position about this is explained at http://java.sun.com/features/1997/nov/rmi.html.
Here are a few details about some other ORBs:
OMG has a lot of information about CORBA at http://www.omg.org/.
The CORBA FAQ is at http://www.cerfnet.com/~mpcline/Corba-FAQ/.
The Advanced Computing Laboratory at the Los Alamos National Laboratory has a page of resources about CORBA and the OMG at http://www.acl.lanl.gov/CORBA.
The Cetus Links on CORBA are at http://www.parallax.co.uk/cetus/oo_corba.html.
Netscape has a CORBA White Paper http://developer.netscape.com/library/wpapers/corba/, and other CORBA documents at http://developer.netscape.com/library/documentation/corba.html.
Sun has a WWW page describing three tier systems at http://java.sun.com/products/jdk/1.2/docs/guide/idl/jidlDistApp.html.
For more about JavaStations and HotJava Views, see http://www.sun.com/javastation/ and http://java.sun.com/products/hotjavaviews/.
One good book is Client/Server Programming with Java and CORBA (1997) by Robert Orfali and Dan Harkey published by Wiley (0-471-16351-1) http://www.corbajava.engr.sjsu.edu/Progrmg.htm. It contains extensive sections on both CORBA and JDBC, and the use of CORBA and JDBC to build multi-tier systems. They also compare CORBA with other methods: sockets, HTTP/CGI and RMI. The second edition of this book (0-471-24578-X) "with 250 pages of new material" is due out in February 1998. Working with Jeri Edwards, Orfali and Harkey have three other books in this area: Essential Client/Server Survival Guide (2nd edition 1996), Essential Distributed Objects Survival Guide (1996) and Instant CORBA (1997). See http://www.corbajava.engr.sjsu.edu/mbooks.htm.
Prashant Sridharan's book Advanced Java Networking explores the topics covered in this document. Its ISBN is 0-13-749136-0 and it is published by Prentice-Hall http://www.prenhall.com/.
Sams.net Publishing have several overlapping books on Java, and their Unleashed book keeps moving into new editions. Many of the chapters of Java 1.1 Unleashed Third Edition describe various aspects of client-server computing. Its ISBN is 1-57521-298-6. For more details, see: http://merchant.superlibrary.com:8000/catalog/hg/PRODUCT/PAGE/15752/bud/1575212986.html.
CORBA, JDBC and three tier systems are explored in more detail in my 30 page document entitled Developing Distributed Systems. This document also covers Remote Method Invocation (RMI), includes details about IORs, discusses the use of OmniBroker which is another ORB, discusses the mixing of JavaIDL and OmniBroker, and includes some ORB clients written in C++. It is available from http://www.dur.ac.uk/~dcl0bjc/Java/. This WWW page also contains other documents on Java including a 52 page tutorial on Java, an overview of the material of the tutorial, and some notes comparing Java and C++.
Many distributed systems have been built that are two tier. This is a system that has a client program communicating with a server program. Typically, the user interface is in the client and the server is there to process data, e.g., to manipulate a database. The complicated application logic (or business logic) is often put in the client (or in the server).
More recently, three tier systems have come into favour. This is where an additional tier is interposed: it is there to handle the application logic leaving the client just to handle the user interface. There are a number of advantages to this approach. For example:
In this document, we have outlined the way in which a three tier system can be built using the APIs for JDBC and CORBA.