![]() ![]() ![]() |
||||||||||||||
![]()
Building Java Servlets with JBuilder Part II NOTE: The views and information expressed in this document represent those of its author(s) who is solely responsible for its content. Borland does not make or give any representation or warranty with respect such content.
Example: Creating a JDBC Servlet as RMI server The purpose of this topic is to help you become acquainted with using the Java Servlet API, and at the same time acquainting you with using JBuilderÆs built-in RMI support. This tutorial takes you through the steps of extending the capabilities of a servlet to provide RMI services for client agents. This will give servlet developers a wider option of distributing objectsÆ responsibilities. This example continues from Tutorial1a: creating a database servlet using Data Express. In this example you will learn how to:
See also:
A Simple Database RMI Servlet
In order for an object to be used in the RMI system, it has to implement the Remote interface. The Remote interface does not introduce any new methods it just acts as a marker for RMI objects. Also, any methods in the interface must be declared as throwing the java.rmi.RemoteException. RemoteException acts as a base class for exceptions that may occur in remote operations. Defining a Remote interface is merely having to choose which methods in your class you want exposed remotely. For SimpleServlet, weÆve chosen the method getResultTable(), and named its interface IDBQuerier. See Example 2-1. Example 2-1: IDBQuerier.java package JBExamples.database.servlets.rmi; public interface IDBQuerier extends java.rmi.Remote { public IDataTable getResultTable(String dbQuery) throws java.rmi.RemoteException; } The method getResultTable returns an instance of IDataTable. IDatatTable is being passed through the RMI system as well. We have two choices on how IDataTable is to be sent through. One choice is to have IDataTable extend the java.rmi.Remote interface, not unlike IDBQuerier. Or, we can have IDataTable extend serializable. By having IDataTable extend serializable, IDataTable would be passed by value. If IDataTable was to implement java.rmi.Remote, IDataTable would be passed by refererence. Example 2-2 will make clear which interface was implemented. Example 2-2: IDataTable.java package JBExamples.database.servlets.rmi; public interface IDataTable extends java.rmi.Remote { public String[] getColumnNames() throws java.rmi.RemoteException; public int getRowCount() throws java.rmi.RemoteException; public int getColumnCount() throws java.rmi.RemoteException; public String getDataAt(int row, int column) throws java.rmi.RemoteException; public String toHTMLString() throws java.rmi.RemoteException; public String toTableString() throws java.rmi.RemoteException; } As you can see, the interface is very similar to the IDataTable interface created in the SimpleServlet example. The only difference is that the RMI version extends from java.rmi.Remote and all the methods throw java.rmi.RemoteException. The next step is to write the implementation of these interfaces. LetÆs start with the implementation of IDataTable. Fortunately, since we are working off the code weÆve created in the SimpleServlet example, we wonÆt have to write two much. The name of the class is DataTableImpl. See Example 2-3 for DataTableImplÆs implementation code. Example 2-3: DataTableImpl.java package JBExamples.database.servlets.rmi; //Packages for RMI support import java.rmi.server.UnicastRemoteObject; import java.rmi.RemoteException; //Packages for database support import borland.sql.dataset.QueryDataSet; import borland.jbcl.dataset.DataRow; import borland.jbcl.dataset.DataSetException; import java.util.Vector; /* * A convenience class that copies the data from QueryDataSet * into a table of Strings. */ public class DataTableImpl extends UnicastRemoteObject implements IDataTable { private String[] columnNames; private Vector tableData; private int columnCount; private int rowCount; public DataTableImpl() throws RemoteException { super(); } public DataTableImpl(QueryDataSet queryDataSet) throws RemoteException, DataSetException { DataRow dataRow = new DataRow(queryDataSet); this.columnCount = dataRow.getColumnCount(); this.rowCount = queryDataSet.getRowCount(); this.columnNames = dataRow.getColumnNames( columnCount ); copyQueryData(queryDataSet, dataRow); } private void copyQueryData(QueryDataSet queryDataSet, DataRow dataRow) throws DataSetException { tableData = new Vector(); for(int i=0; i Again in comparing the IDataTableImpl class created in the SimpleServlet example with the IDataTableImpl class for this example, the only difference is that this time IDataTableImpl now extends java.rmi.server.UnicastRemoteObject class. UnicastRemoteObject is an extension of RemoteServer class, which acts as a base class for server implementation of objects in RMI. RMI does not require your remote server to derive from a RemoteServer subclass, but doing this lets your server inherit specialized implementations of some methods from Object (hashCode(), equals(), and toString()) so that they do the right thing in a remote object scenario. Now lets examine the interface in Example 2-1. Recall that this interface is needed to define methods of our JDBCRmiServlet which we want exposed to the RMI system. JDBCRmiServlet will have to implement this interface as well as extend from java.rmi.server.UnicastRemoteObject. Recall that Java does not allow multiple inheritance. By looking at SimpleServlet we see that it extends from javax.servlet.http.HttpServlet. We cannot have JDBCRmiServlet extend HttpServlet because we need it to extend from UnicastRemoteObject. The HttpServlet is a subclass of the abstract class javax.servlet.GenericServlet. GenericServlet implements javax.servlet.Servlet, javax.servlet.ServletConfig, and Serializable. To make things simple, weÆll just have our JDBCRmiServlet implement Servlet, and ServletConfig. WeÆll leave the Serializable, features up to whomever wants to implement them. The choice of having JDBCRmiServlet implement ServletConfig, is a matter of convenience to make getting the configuration data easier to get to by other servlets. For example, a servlet can implement getServletContext by writing,
public ServletContext getServletContext() { return getServletConfig().getServletContext(); } Example 2-4: JDBCRmiServlet.java package JBExamples.database.servlets.rmi; //Packages for servlet support import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import java.util.*; //Packages for database support import borland.sql.dataset.*; import borland.jbcl.dataset.DataRow; import borland.jbcl.dataset.DataSetException; //Packages for rmi support import java.rmi.registry.*; import java.rmi.server.UnicastRemoteObject; import java.rmi.*; /* * A JDBC Servlet as an RMI Server. This class performs queries on a database * and returns the results to a remote client agent. */ public class JDBCRmiServlet extends UnicastRemoteObject implements IDBQuerier, Servlet, ServletConfig { Database database1 = new Database(); public JDBCRmiServlet() throws RemoteException { super(); } private void jbInit() throws Exception{ database1.setConnection(new The log method was added to make up for not extending HttpServlet, this method is implemented to allow you to print your debug/error messages to a log file. The getServletConfig method is implemented for the Servlet interface. This returns the ServletConfig object which was passed in by the servlet runner in the init method which we implemented from the SimpleServlet example. Another new method implemented for the Servlet interface is the service method. The service method with a different signature was implemented in the SimpleServlet example. . In the SimpleServlet example weÆve implemented the service(HttpServletRequest, HttpServletResponse) method. This time we are required to implement the service(ServletRequest, ServletResponse) method. The classes, ServletRequest, and ServletResponse are superclasses of HttpServletRequest and HttpServletResponse. Fortunately the methods we invoked from HttpServletResponse, particularly getWriter() and setContentType() are defined in the ServletResponse interface. The same is true for the getParameter() method invoked from HttpServletRequest. This method is defined in the ServletRequest interface. So basically all we had to do was change the signature of the service method to take in classes of type ServletRequest, and ServletResponse. The three methods invoked in behalf of the ServletConfig interface are, getServletContext, getInitParameter, and getInitParameterNames, these methods simply passes on the invokation of the corresponding methods to the member variable, conf. The conf variable is an instance of ServletConfig which was set in the init method. JDBCRmiServlet has to bind itself into the RMI registry. This makes the JDBCRmiServlet object available for RMI lookup by clients. This is done in the init method by:
Naming.rebind("DBQuerier", this); The next step is to compile JDBCRmiServlet, and generate its RMI stubs and skeletons. The next section steps you through doing just that.
Using JBuilderÆs built-in RMI support
Invoking JDBCRmiServlet
Loading JDBCRmiServlet on init If you recall, the JDBCRmiServlet makes itself available for RMI lookup in its init method. The init method is called when the servlet is loaded by the servlet runner. RMI clients can only get to the RMI server processes if they are registered for lookup. This means the JDBCRmiServlet has to be available in the RMI registry before any RMI clients can look it up. An error will occur if JDBCRmiServlet is looked up by an RMI client before it is loaded by the servlet runner. Just having your RMI servlet available in the servlet runnerÆs servlet directory is not enough to have it available for an RMI lookup. Unless specified otherwise, a servlet is only loaded by the servlet runner when it is asked for from an HTTP client through the web server. When it is asked for, and if the servlet exists under the servlet runnerÆs servlets directory, and if it has not been loaded yet, then it is loaded, and the init method of the servlet is called. From then on until the web server is stopped, the servlet is available for servicing HTTP and RMI requests. So, in order for the RMI servlet to be available through RMI lookup, it has to be requested through the HTTP transport first. This may not be acceptable. One way to work around this is to specify that the servlet be loaded by the servlet runner when the web server starts up. This is done through the servlet runnerÆs admin. Setting this "load on init" property for a servlet, makes the servlet available for HTTP and RMI servicing as long as the web server is running.
Example: Creating a JDBC Servlet as CORBA Server The purpose of this topic is to help you become acquainted with using the Java Servlet API, and at the same time acquainting you with using JBuilderÆs built-in CORBA support. This tutorial takes you through extending the capabilities of a servlet to provide CORBA services for client agents. This will give servlet developers a wider option of distributing objectsÆ responsibilities. This example continues from Tutorial1a: creating a database servlet using Data Express. In this example you will learn how to:
See also:
A Simple Database CORBA Servlet
In order for an object to be used in the CORBA system, it has to implement the org.omg.CORBA.Object interface. The org.omg.CORBA.Object interface does not introduce any new methods it just acts as a marker for CORBA objects. Defining an org.omg.CORBA.Object interface is merely having to choose which methods in your class you want exposed remotely. For SimpleServlet, weÆve chosen the method getResultTable(), and named its interface IDBQuerier. See Example 3-1. Example 3-1: IDBQuerier.java package JBExamples.database.servlets.corba; public interface IDBQuerier extends org.omg.CORBA.Object { public IDataTable getResultTable(String dbQuery); } Example 3-2: IDataTable.java package JBExamples.database.servlets.corba; public interface IDataTable extends org.omg.CORBA.Object { public String[] getColumnNames(); public int getRowCount(); public int getColumnCount(); public String getDataAt(int row, int column); public String toHTMLString(); public String toTableString(); }
Using JBuilderÆs built-in CORBA support
With the default settings of the java2iiop compiler, the following files should be generated:
The base classes _IDataTableImplBase, and _IDBQuerierImplBase are abstract classes. The definition of _IDataTableImplBase follows. abstract public class _IDataTableImplBase extends org.omg.CORBA.portable.Skeleton implements JBExamples.database.servlets.corba.IDataTable { The _IDBQuerierImplBase class is defined similarly. _IDataTableImplBase, and _IDBQuerierImplBase are the classes which implement the IDataTable and IDBQuerier interfaces respectively. We wonÆt go into the implementation of org.omg.CORBA.portable.Skeleton. These are details that hopefully should not concern you. The org.omg.CORBA.portable.Skeleton is the interface which the server ORB will be looking for when invoking methods on the base objectÆs implementation. The next step is to extend the _IDBQuerierImplBase, and _IDataTableImplBase and implement the methods for the interfaces of their base class. LetÆs start with extending the _IDataTableImplBase class. Fortunately, since we are working off the code weÆve created in the SimpleServlet example, we wonÆt have to write two much. The name of the class is DataTableImpl. See Example 3-3 for DataTableImplÆs implementation code. Example 3-3: DataTableImpl.java package JBExamples.database.servlets.corba; import borland.sql.dataset.QueryDataSet; import borland.jbcl.dataset.DataRow; import borland.jbcl.dataset.DataSetException; import java.util.Vector; /* * A convenience class that copies the data from QueryDataSet * into a table of Strings. */ public class DataTableImpl extends _IDataTableImplBase { private String[] columnNames; private Vector tableData; private int columnCount; private int rowCount; /** Construct a transient object. */ public DataTableImpl() { super(); } public DataTableImpl(QueryDataSet queryDataSet) throws DataSetException { DataRow dataRow = new DataRow(queryDataSet); this.columnCount = dataRow.getColumnCount(); this.rowCount = queryDataSet.getRowCount(); this.columnNames = dataRow.getColumnNames( columnCount ); copyQueryData(queryDataSet, dataRow); } private void copyQueryData(QueryDataSet queryDataSet, DataRow dataRow) throws DataSetException { tableData = new Vector(); for(int i=0; i Now lets revisit the interface in Example 2-1. Recall that this interface is needed to define methods of our JDBCCorbaServlet which we want exposed to the CORBA system. JDBCCorbaServlet will have to extend the _IDBQuerierImplBase class. Recall that Java does not allow multiple inheritance. By looking at SimpleServlet we see that it extends from javax.servlet.http.HttpServlet. We cannot have JDBCCorbaServlet extend HttpServlet because we need it to extend from _IDBQuerierImplBase. The HttpServlet is a subclass of the abstract class javax.servlet.GenericServlet. GenericServlet implements javax.servlet.Servlet, javax.servlet.ServletConfig, and Serializable. To make things simple, weÆll just have our JDBCCorbaServlet implement Servlet, and ServletConfig. WeÆll leave the Serializable features up to whomever wants to implement them. The choice of having JDBCCorbaServlet implement ServletConfig, is a matter of convenience to make getting the configuration data easier to get to by other servlets. For example, a servlet can implement getServletContext by writing, public ServletContext getServletContext() { return getServletConfig().getServletContext(); } Example 3-4: JDBCCORBServlet.java package JBExamples.database.servlets.corba; //Packages for servlet support import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import java.util.*; //Packages for database support import borland.sql.dataset.*; import borland.jbcl.dataset.DataRow; import borland.jbcl.dataset.DataSetException; //Packages for CORBA support import org.omg.CORBA.*; /** * A servlet that also acts as a CORBA server for performing database * queries. */ public class JDBCCorbaServlet extends _IDBQuerierImplBase implements Servlet { Database database1 = new Database(); Thread BOAThread = null; //A Runnable member class used to listen //for CORBA requests class BOARunnable implements Runnable { BOA boa; public BOARunnable(BOA boa) { this.boa = boa; } public void run() { boa.impl_is_ready(); } } /** * The servlet runner instantiates this servlet through it's empty constructor. * This is also where we name this CORBA server. */ public JDBCCorbaServlet() { super("DBQuerier"); } private void jbInit() throws Exception { System.out.println("Connecting to database..."); database1.setConnection(new JDBCCorbaServlethas to make itself available for lookup in the CORBA system. This is done in JDBCCORBServletÆs init method. The following lines initializes the server side ORB, and initializes the Basic Object Adapter. ORB orb = ORB.init(args, null); BOA boa = orb.BOA_init(); boa.obj_is_ready(this); this.BOAThread = new Thread( new BOARunnable(boa) ); this.BOAThread.start();
Invoking JDBCCorbaServlet
Loading JDBCCorbaServlet on init If you recall, the JDBCCorbaServlet makes itself available for CORBA lookup in its init method. The init method is called when the servlet is loaded by the servlet runner. CORBA clients can only get to the CORBA server processes if they are registered for lookup. This means the JDBCCorbaServlet has to be available in the RMI registry before any CORBA clients can look it up. An error will occur if JDBCCorbaServlet is looked up by a CORBA client before it is loaded by the servlet runner. Just having your CORBA servlet available in the servlet runnerÆs servlet directory is not enough to have it available for a CORBA lookup. Unless specified otherwise, a servlet is only loaded by the servlet runner when it is asked for from an HTTP client through the web server. When it is asked for, and if the servlet exists under the servlet runnerÆs servlets directory, and if it has not been loaded yet, then it is loaded, and the init method of the servlet is called. From then on until the web server is stopped, the servlet is available for servicing HTTP and CORBA requests. So, in order for the CORBA servlet to be available through CORBA lookup, it has to be requested through the HTTP transport first. This may not be acceptable. One way to work around this is to specify that the servlet be loaded by the servlet runner when the web server starts up. This is done through the servlet runnerÆs admin. Setting this "load on init" property for a servlet, makes the servlet available for HTTP and CORBA servicing as long as the web server is running. Once the web server is stopped, the destroy method of the servlet is called. For our case, in the destroy method, the database connection is closed and our BOA thread is stopped. Continue to Part III Back To TopReturn to Part I Home Page |