ZOIS *
Technical Notes
ZOIS Technical Note TN-2001-10-16.
Author and Audience
This TN is intended for persons working with Java 2 Enterprise Edition
(J2EE) products and methodologies on Linux and similar platforms. UNIX
systems and Java programming skills are assumed. Written by Martin
Sullivan[1], ZOIS Limited, Cockermouth.
Abstract
Simpapp is a demonstration suite for the Tuxedo Transaction Processing
Monitor. It demonstrates source code and configuration issues with the
goal of being the simplest example possible. A Simpapp is presented
using Java 2 Enterprise Edition technologies both as an example but also
as a model for comparison.
Introduction
"Simpapp"[2] is a Tuxedo[3] application distributed as an example with that fine Transaction Processing Monitor. It demonstrates the absolute simplest Tuxedo system, addressing sample code and configuration issues. Since it is simple the only functionality it displays is to uppercase a string of characters.
As part of a larger suite of Java 2 Enterprise Edition (J2EE) programs a Simpapp has been written to use this system. It too demonstrates configuration and coding issues but differs from its Tuxedo inspiration by doing its uppercasing in SQL, to demonstrate database connectivity. Its simplicity stands in stark contrast to other J2EE examples (largely based on shops of various kinds) and it demonstrates important stateless attributes that allow systems based on this approach to scale.
This Simpapp, although portable in terms of J2EE platforms generally, is demonstrated on:
Therefore configuration issues deal with these products only.
Materials and Platform
The Simpapp J2EE demonstration runs on the Red Hat[4]
distribution of Linux (version 6.2, kernel 2.2.14). It will
run on conventional Intel based PCs and Laptops, to allow demonstrations
at remote sites. The minimum for this works seems to be a 200 MHz CPU and
64 Mbytes of main memory. The Java used was IBM's 1.3 (IBMJava2-13) for
Linux. Apache, Tomcat, JBoss and PostgreSQL were installed and configured
on this platform (the subject of a separate TN[7]).
Method
The HTML is a straightforward Form:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN"> <html><head> <link rev="made" href="http://www.zois.co.uk/people/martin_sullivan"> <title>Simpapp</title> <head><body> <h2>Simpapp</h2> <p><form method="get" action="/test/simpapp.jsp"> Input:<input type="text" name="input" size="48"> </form> </body></html> |
There are no configuration issues and this page, named suitably (say simpapp.html), is placed in the directory where Apache keeps the static HTML documents for display (/home/httpd/html on Red Hat Linux 6.2).
Java Server Pages are executable Java program elements that are embedded into HTML pages. These are stripped out and compiled up on the fly as Serverlets, Java programs linked to HTML pages, allowing dynamic content in HTML pages. In the above HTML section the "form action", the program that is executed to process the form, is given as simpapp.jsp.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<%@ page language="java" contentType="text/html" %>
<%@ page import="javax.ejb.*" %>
<%@ page import="javax.naming.*" %>
<%@ page import="javax.rmi.*" %>
<%@ page import="java.util.Properties" %>
<%@ page import="Simpapp.*" %>
<%!
SimpappHome home = null;
%>
<%
Simpapp simpapp = null;
String input = "";
String output = "";
String bottom = "";
try {
if (home == null) {
Context ic = new InitialContext ();
Object ref = ic.lookup ("Simpapp");
home = (SimpappHome)
PortableRemoteObject.narrow (ref, SimpappHome.class);
} // if
simpapp = home.create ();
input = request.getParameter ("input");
output = simpapp.upper (input);
} catch (Exception e) {
bottom =
"<br clear=all>" +
"<hr>" +
"<h3>Exception Caught</h3>" +
"<p>An Exceptional event occurred:" +
"<ul>" +
"<li>"+ e +
" <strong>(Please report this)</strong>." +
"</ul>";
try {
simpapp.remove ();
home = null;
} catch (Exception er) {}
} // catch
%>
<html><head>
<title>Simpapp</title>
</head><body>
<h2>Simpapp</h2>
<p><cite>simpapp</cite> called with "<%= input %>" got "<%= output %>".
<%= bottom %>
<br clear=all><hr>
</body></html>
|
The intention is that this TN is not a tutorial on the JSP, so
the above source is not discussed in any depth. We can however say
that this is about as simple as it gets, there is no use of JSP
Tags and the Exception handling is minimal. The JSP is used to
forward the String, input with the HTML form, to an EJB and then
construct a simple page to display the results. In this respect
the JSP acts as a client to the EJB. The only technique of note
is the use of the `<%!' Tag to ensure that the Simpapp bean's
Home interface is discovered only once.
In the demonstrator Tomcat is the Web Container, something that acts as an executable environment for Java Server Pages (JSP). To deploy this JSP it needs to be installed in the webapp/test directory in the Tomcat tree (/opt/tomcat/webapp/test, as an example). The webapp/test directory is pre-configured with the as-delivered Tomcat so that simpapp.jsp appears in the HTML tree as http://<domain>/test/simpapp.jsp, so no configuration should be necessary.
Configuration of Tomcat's environment is necessary, however, to allow the Simpapp JSP, as an EJB client, to locate the Java class that describes the Simpapp Interface and a Java Naming and Directory Interface (JNDI) end-point so the Simpapp EJB can be located. This will be discussed in the next, slightly more complicated, section. Suffice it to say that if the JSP is deployed and then triggered at this point, without this configuration, a Java Exception will be the result.
EJB's come in a number of varieties. The type used in the Simpapp example is a Stateless Session Bean (SSB). SSBs most closely resemble a conventional back-end OLTP service, program or function, such as a CICS Transaction Program. Being stateless they will allow maximum scalability. The EJB will be invoked by a client running in a Serverlet compiled out of a JSP and will connect to a database via Java Databse Connect (JDBC).
An EJB consists of a minimum of three compiled Java Class files and a small piece of configuration information written in Extensible Markup Language (XML). The three Java files are code for an `interface', describing the EJB's exposed methods, for example Simpapp.upper; a `home' Class which (as an interface) describes the Bean to the container and the `bean' Class, which actually contains the code. What follows is the code for these Java files, firstly the `interface' Class which should be in a file named Simpapp.java:
Package Simpapp;
import javax.ejb.*;
import java.rmi.*;
public interface Simpapp extends EJBObject {
public String upper (String input)
throws java.rmi.RemoteException;
} // Simpapp
|
In keeping with spirit of the rest of this demonstrator, only one method is exposed, Simpapp.upper. The Package statement has been simplified too, it should really use the correct full naming convention and a name like uk.co.zois.tn.ejb.Simpapp or similar, with an equally large directory path.
Secondly, the 'home' class, in a file called SimpappHome.java:
package Simpapp;
import javax.ejb.*;
import java.rmi.RemoteException;
public interface SimpappHome extends EJBHome {
Simpapp create () throws RemoteException, CreateException;
} // SimpappHome
|
Thirdly, the actual nitty-gritty `bean' class in a file called SimpappBean.java. Before considering a full-on do it in SQL SimpappBean in any detail look at the following SimpappBean.java, which does the work using Java's String.toUpperCase method, rather than SQL. It will give you an idea of how much you need for the JDBC:
package Simpapp;
import javax.ejb.*;
public class SimpappBean implements SessionBean {
public String upper (String input) {
return (input.toUpperCase ());
} // upper
public void ejbCreate () {}
public void ejbRemove () {}
public void ejbActivate () {}
public void ejbPassivate () {}
public void setSessionContext (SessionContext ctx) {}
} // SimpappBean
|
Here's the SQL infested JDBC effort:
package Simpapp;
import javax.ejb.*;
import java.sql.*;
import javax.naming.*;
import javax.sql.DataSource;
public class SimpappBean implements SessionBean {
static final String DATASOURCE = "java:/PostgresDS";
static final String SQL = "select upper (?) as result";
DataSource ds;
public String upper (String input) throws java.rmi.RemoteException {
try {
Connection conn = ds.getConnection ();
PreparedStatement sql = conn.prepareStatement (SQL);
sql.setString (1, input);
ResultSet results = sql.executeQuery ();
if (results.next () == false) {
System.out.println ("No rows for "+ input);
throw new java.rmi.RemoteException ("SQL failure");
} // if
String result = results.getString ("result");
results.close ();
sql.close ();
conn.close ();
if (result == null) {
System.out.println ("NULL result for "+ input);
throw new java.rmi.RemoteException ("SQL failure");
} else
return (result);
} catch (SQLException sqle) {
System.err.println ("While executing SQL: " +
sqle.getMessage ());
throw new java.rmi.RemoteException ("SQL failure");
} catch (Exception e) {
System.err.println ("Unknown Exception: " +
e.getMessage ());
throw new java.rmi.RemoteException ("Bean Exception: " +
e.getMessage ());
} // catch
} // simpapp
public void ejbCreate () {
try {
Context ctx = new InitialContext ();
ds = (DataSource) ctx.lookup (DATASOURCE);
} catch (Exception e) {
System.out.println ("While creating Bean " +
e.getMessage ());
} // catch
} // ejbCreate
public void ejbRemove () {}
public void ejbActivate () {}
public void ejbPassivate () {}
public void setSessionContext (SessionContext ctx) {}
} // SimpappBean
|
Again without this turning into a tutorial on either JDBC or EJB, there are some things which we can draw your attention to. The first is that when the EJB is created it discovers its Data Source (as part of ejbCreate). We do not connect to the database at this point (although it would seem natural to do so) for the connection mechanism is pooled and thus a low-cost resource managed within our EJB Container. If one makes and breaks connections within ejbCreate and ejbRemove the connection pool manager in JBoss 2.2 at least (known as Minerva) gets confused and problems occur (like Transactions not being committed). One may also directly connect to the database using standard JDBC techniques rather than DataSource. Using ejbCreate and ejbRemove would then be strongly indicated as a the place to do this, but the technique would mean that the EJB Container, JBoss, would be unable to manage Transactions.
The final piece of the picture is the XML which is included in the EJB to describe the properties of the EJB to the EJB Container. This XML suffices for JBoss 2.2. It goes in a file ejb-jar.xml:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar>
<description>Simpapp EJB</description>
<display-name>Simpapp EJB</display-name>
<enterprise-beans>
<session>
<ejb-name>Simpapp</ejb-name>
<home>Simpapp.SimpappHome</home>
<remote>Simpapp.Simpapp</remote>
<ejb-class>Simpapp.SimpappBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
</enterprise-beans>
</ejb-jar>
|
Now that you have your Java and your XML you need to put it all together. This is the directory structure chosen for the EJB part of the demonstrator and it needs to look like this (case sensitive and all).
<root> +--- Simpapp +--- Simpapp.java
| +--- SimpappHome.java
| +--- SimpappBean.java
|
+--- META-INF ---- ejb-jar.xml
In order for the compilation to work you need to set the CLASSPATH variable suitably. The following shell script fragment works for the author ...
CLASSPATH=${CLASSPATH}:${JBOSS_HOME}/lib/ext/ejb2.0.jar
CLASSPATH=${CLASSPATH}:${JBOSS_HOME}/lib/ext/jboss.jar
CLASSPATH=${CLASSPATH}:${JBOSS_HOME}/lib/ext/jnpserver.jar #JNDI
CLASSPATH=${CLASSPATH}:${JBOSS_HOME}/lib/ext/jta-spec1_0_1.jar #jta
CLASSPATH=${CLASSPATH}:${JBOSS_HOME}/lib/ext/jms.jar #jms
CLASSPATH=${CLASSPATH}:${JBOSS_HOME}/lib/ext/jbossmq.jar #jms
CLASSPATH=${CLASSPATH}:${JBOSS_HOME}/lib/jdbc2_0-stdext.jar #jdbc
CLASSPATH=${CLASSPATH}:${JBOSS_HOME}/client/jbosssx-client.jar
|
Change directory into the <ejb root> directory and compile the Java in one go.
javac Simpapp/*.java |
Then package the entire thing up in a `jar' file, even the source, it doesn't matter ...
jar -cf simpapp.jar Simpapp META-INF |
The resulting `jar' file (simpapp.jar) is then put in the JBoss deployment directory (deploy, /opt/jboss/deploy as an example). That, as far as JBoss is concerned, is your EJB. Other containers, universally, seem to have a more complexed build and deploy mechanism.
The Tomcat based EJB clients also need Java Naming and Directory Interface (JNDI) information to allow them to connect to their Servers. This is accomplished by having a properties file (jndi.properties) somewhere on CLASSPATH. The contents of this file specify a local address (localhost) for the JNDI service is on the same host as Tomcat (and JBoss):
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.provider.url=localhost java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces |
In the demonstrator this file lives in the same directory as the Simpapp interface class:
CLASSPATH=${CLASSPATH}:${EJB_ROOT} #Simpapp found here.
... where "EJB_ROOT" is the directory containing the Simpapp and META-INF
directories.
The Tomcat based EJB clients also need to find all the EJB `jar' files that come with JBoss. The CLASSPATH needs to be suitably set for this too. Prior to starting Tomcat then, in an rc script, the following shell fragment yeilds the correct, for us, CLASSPATH ...
CLASSPATH=${CLASSPATH}:${EJB_ROOT} #Simpapp found here.
CLASSPATH=${CLASSPATH}:${JBOSS_HOME}/lib/ext/ejb2.0.jar
CLASSPATH=${CLASSPATH}:${JBOSS_HOME}/lib/ext/jboss.jar
CLASSPATH=${CLASSPATH}:${JBOSS_HOME}/lib/ext/jnpserver.jar #jndi
CLASSPATH=${CLASSPATH}:${JBOSS_HOME}/lib/ext/jta-spec1_0_1.jar #jta
CLASSPATH=${CLASSPATH}:${JBOSS_HOME}/lib/ext/jms.jar #jms
CLASSPATH=${CLASSPATH}:${JBOSS_HOME}/lib/ext/jbossmq.jar #jms
CLASSPATH=${CLASSPATH}:${JBOSS_HOME}/lib/jdbc2_0-stdext.jar #jdbc
CLASSPATH=${CLASSPATH}:${JBOSS_HOME}/client/jbosssx-client.jar
|
With everything in place, Apache, Tomcat, JBoss and PostgreSQL running, pointing your favourite browser at simpapp.html should take you through things. If all goes to plan then you will be able to fill in your one field form and have the result displayed to you. Do not be alarmed at the initial length of time that the case conversion takes. A fair amount of first and only time processing has to take place (compiling the JSP, for example). Subsequent runs, it will be found, are a good deal quicker. If the expected message is not returned, then apart from the advice to check that everything is running and the CLASSPATH variable is set correctly, you are on your own. Consider it part of the learning exercise.
As experiments continue, it will be noted that one can deploy EJBs
with relative ease in JBoss. Simply replacing the currently running
bean with a new one deployes your new fixed or enhanced bean.
Tomcat however, seems to need
restarting to pick up new JSPs.
Conclusion
The J2EE Simpapp example demonstrates the simplest possible
HTML-JSP-EJB-RDBMS system. It allows exploration of the configuration
issues (initially with Tomcat, JBoss and PostgreSQL). It invites
comparision with other ways of doing things, particularly using the
Tuxedo Transaction Processing Manager.
References
$Date: 2002/11/20 18:21:37 $