MindView Inc.
[ Viewing Hints ] [ Revision History ] [ Report an Error ]
[ 1st Edition ] [ Free Newsletter ]
[ Seminars ] [ Seminars on CD ROM ] [ Consulting ]

Thinking in Java, 2nd edition, Revision 5b

©2000 by Bruce Eckel

[ Previous Chapter ] [ Short TOC ] [ Table of Contents ] [ Index ] [ Next Chapter ]

15: Enterprise programming

NOTE: Feedback for this chapter should go to dbartlett@pobox.com

Enterprise computing is about collecting and distributing information.

You do this by creating common repositories (single points of access) to that information, and allowing people to get at that information in multiple ways. So enterprise computing is creating and manipulating those common repositories, and providing ways for users to view and manipulate the information in those repositories.

In this chapter you’ll see that there are a number of different ways to achieve this goal.

Opening phrase

Introductory paragraphs

[[ Dave: you mentioned an over-arching example that would be used throughout the chapter. I would recommend the “Community Information System” (CIS?) that we worked with for the servlets seminar. The goal would be to provide various services that are valuable for a community (of course it could easily be adapted to other uses, but I think it would be easy to just keep “community” in mind for the design problem). For example:

*A possible additional example (for EJB, perhaps) is an anti-spamming system to prevent spam harvesters – either you have a password, or it actually emails the information back to you, after verifying your email address shows that you’re a community member.

Java Database Connectivity (JDBC)

It has been estimated that half of all software development involves client/server operations. A great promise of Java has been the ability to build platform-independent client/server database applications. In Java 1.1 this has come to fruition with Java DataBase Connectivity (JDBC).

One of the major problems with databases has been the feature wars between the database companies. There is a “standard” database language, Structured Query Language (SQL-92), but usually you must know which database vendor you’re working with despite the standard. JDBC is designed to be platform-independent, so you don’t need to worry about the database you’re using while you’re programming. However, it’s still possible to make vendor-specific calls from JDBC so you aren’t restricted from doing what you must.

JDBC, like many of the APIs in Java, is designed for simplicity. The method calls you make correspond to the logical operations you’d think of doing when gathering data from a database: connect to the database, create a statement and execute the query, and look at the result set.

To allow this platform independence, JDBC provides a driver manager that dynamically maintains all the driver objects that your database queries will need. So if you have three different kinds of vendor databases to connect to, you’ll need three different driver objects. The driver objects register themselves with the driver manager at the time of loading, and you can force the loading using Class.forName( ).

To open a database, you must create a “database URL” that specifies:

  1. That you’re using JDBC with “jdbc”
  2. The “subprotocol”: the name of the driver or the name of a database connectivity mechanism. Since the design of JDBC was inspired by ODBC, the first subprotocol available is the “jdbc-odbc bridge,” specified by “odbc”
  3. The database identifier. This varies with the database driver used, but it generally provides a logical name that is mapped by the database administration software to a physical directory where the database tables are located. For your database identifier to have any meaning, you must register the name using your database administration software. (The process of registration varies from platform to platform.)

All this information is combined into one string, the “database URL.” For example, to connect through the ODBC subprotocol to a database identified as “people,” the database URL could be:

String dbUrl = "jdbc:odbc:people";

If you’re connecting across a network, the database URL will also contain the information identifying the remote machine.

When you’re ready to connect to the database, you call the static method DriverManager.getConnection( ), passing it the database URL, the user name, and a password to get into the database. You get back a Connection object that you can then use to query and manipulate the database.

The following example opens a database of contact information and looks for a person’s last name as given on the command line. It selects only the names of people that have email addresses, then prints out all the ones that match the given last name:

//: c15:Lookup.java
// Looks up email addresses in a 
// local database using JDBC
import java.sql.*;

public class Lookup {
  public static void main(String[] args) {
    String dbUrl = "jdbc:odbc:people";
    String user = "";
    String password = "";
    try {
      // Load the driver (registers itself)
      Class.forName(
        "sun.jdbc.odbc.JdbcOdbcDriver");
      Connection c = DriverManager.getConnection(
        dbUrl, user, password);
      Statement s = c.createStatement();
      // SQL code:
      ResultSet r = 
        s.executeQuery(
          "SELECT FIRST, LAST, EMAIL " +
          "FROM people.csv people " +
          "WHERE " +
          "(LAST='" + args[0] + "') " +
          " AND (EMAIL Is Not Null) " +
          "ORDER BY FIRST");
      while(r.next()) {
        // Capitalization doesn't matter:
        System.out.println(
          r.getString("Last") + ", " 
          + r.getString("fIRST")
          + ": " + r.getString("EMAIL") );
      }
      s.close(); // Also closes ResultSet
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
} ///:~

You can see the creation of the database URL as previously described. In this example, there is no password protection on the database so the user name and password are empty strings.

Once the connection is made with DriverManager.getConnection( ), you can use the resulting Connection object to create a Statement object using the createStatement( ) method. With the resulting Statement, you can call executeQuery( ), passing in a string containing an SQL-92 standard SQL statement. (You’ll see shortly how you can generate this statement automatically, so you don’t have to know much about SQL.)

The executeQuery( ) method returns a ResultSet object, which is quite a bit like an iterator: the next( ) method moves the iterator to the next record in the statement, or returns false if the end of the result set has been reached. You’ll always get a ResultSet object back from executeQuery( ) even if a query results in an empty set (that is, an exception is not thrown). Note that you must call next( ) once before trying to read any record data. If the result set is empty, this first call to next( ) will return false. For each record in the result set, you can select the fields using (among other approaches) the field name as a string. Also note that the capitalization of the field name is ignored – it doesn’t matter with an SQL database. You determine the type you’ll get back by calling getInt( ), getString( ), getFloat( ), etc. At this point, you’ve got your database data in Java native format and can do whatever you want with it using ordinary Java code.

Getting the example to work

With JDBC, understanding the code is relatively simple. The confusing part is making it work on your particular system. The reason this is confusing is that it requires you to figure out how to get your JDBC driver to load properly, and how to set up a database using your database administration software.

Of course, this process can vary radically from machine to machine, but the process I used to make it work under 32-bit Windows might give you clues to help you attack your own situation.

Step 1: Find the JDBC Driver

The program above contains the statement:

Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

This implies a directory structure, which is deceiving. With this particular installation of JDK 1.1, there was no file called JdbcOdbcDriver.class, so if you looked at this example and went searching for it you’d be frustrated. Other published examples use a pseudo name, such as “myDriver.ClassName,” which is less than helpful. In fact, the load statement above for the jdbc-odbc driver (the only one that actually comes with JDK 1.1) appears in only a few places in the online documentation (in particular, a page labeled “JDBC-ODBC Bridge Driver”). If the load statement above doesn’t work, then the name might have been changed as part of a Java version change, so you should hunt through the documentation again.

If the load statement is wrong, you’ll get an exception at this point. To test whether your driver load statement is working correctly, comment out the code after the statement and up to the catch clause; if the program throws no exceptions it means that the driver is loading properly.

Step 2: Configure the database

Again, this is specific to 32-bit Windows; you might need to do some research to figure it out for your own platform.

First, open the control panel. You might find two icons that say “ODBC.” You must use the one that says “32bit ODBC,” since the other one is for backwards compatibility with 16-bit ODBC software and will produce no results for JDBC. When you open the “32bit ODBC” icon, you’ll see a tabbed dialog with a number of tabs, including “User DSN,” “System DSN,” “File DSN,” etc., in which “DSN” means “Data Source Name.” It turns out that for the JDBC-ODBC bridge, the only place where it’s important to set up your database is “System DSN,” but you’ll also want to test your configuration and create queries, and for that you’ll also need to set up your database in “File DSN.” This will allow the Microsoft Query tool (that comes with Microsoft Office) to find the database. Note that other query tools are also available from other vendors.

The most interesting database is one that you’re already using. Standard ODBC supports a number of different file formats including such venerable workhorses as DBase. However, it also includes the simple “comma-separated ASCII” format, which virtually every data tool has the ability to write. In my case, I just took my “people” database that I’ve been maintaining for years using various contact-management tools and exported it as a comma-separated ASCII file (these typically have an extension of .csv). In the “File DSN” section I chose “Add,” chose the text driver to handle my comma-separated ASCII file, and then un-checked “use current directory” to allow me to specify the directory where I exported the data file.

You’ll notice when you do this that you don’t actually specify a file, only a directory. That’s because a database is typically represented as a collection of files under a single directory (although it could be represented in other forms as well). Each file usually contains a single table, and the SQL statements can produce results that are culled from multiple tables in the database (this is called a join). A database that contains only a single table (like this one) is usually called a flat-file database. Most problems that go beyond the simple storage and retrieval of data generally require multiple tables that must be related by joins to produce the desired results, and these are called relational databases.

Step 3: Test the configuration

To test the configuration you’ll need a way to discover whether the database is visible from a program that queries it. Of course, you can simply run the JDBC program example above up to and including the statement:

Connection c = DriverManager.getConnection(
  dbUrl, user, password);

If an exception is thrown, your configuration was incorrect.

However, it’s useful to get a query-generation tool involved at this point. I used Microsoft Query that came with Microsoft Office, but you might prefer something else. The query tool must know where the database is, and Microsoft Query required that I go to the ODBC Administrator’s “File DSN” tab and add a new entry there, again specifying the text driver and the directory where my database lives. You can name the entry anything you want, but it’s helpful to use the same name you used in “System DSN.”

Once you’ve done this, you will see that your database is available when you create a new query using your query tool.

Step 4: Generate your SQL query

The query that I created using Microsoft Query not only showed me that my database was there and in good order, but it also automatically created the SQL code that I needed to insert into my Java program. I wanted a query that would search for records that had the last name that was typed on the command line when starting the Java program. So as a starting point, I searched for a specific last name, ‘Eckel’. I also wanted to display only those names that had email addresses associated with them. The steps I took to create this query were:

  1. Start a new query and use the Query Wizard. Select the “people” database. (This is the equivalent of opening the database connection using the appropriate database URL.)
  2. Select the “people” table within the database. From within the table, choose the columns FIRST, LAST, and EMAIL.
  3. Under “Filter Data,” choose LAST and select “equals” with an argument of Eckel. Click the “And” radio button.
  4. Choose EMAIL and select “Is not Null.”
  5. Under “Sort By,” choose FIRST.

The result of this query will show you whether you’re getting what you want.

Now you can press the SQL button and without any research on your part, up will pop the correct SQL code, ready for you to cut and paste. For this query, it looked like this:

SELECT people.FIRST, people.LAST, people.EMAIL
FROM people.csv people
WHERE (people.LAST='Eckel') AND 
(people.EMAIL Is Not Null)
ORDER BY people.FIRST

With more complicated queries it’s easy to get things wrong, but with a query tool you can interactively test your queries and automatically generate the correct code. It’s hard to argue the case for doing this by hand.

Step 5: Modify and paste in your query

You’ll notice that the code above looks different from what’s used in the program. That’s because the query tool uses full qualification for all of the names, even when there’s only one table involved. (When more than one table is involved, the qualification prevents collisions between columns from different tables that have the same names.) Since this query involves only one table, you can optionally remove the “people” qualifier from most of the names, like this:

SELECT FIRST, LAST, EMAIL
FROM people.csv people
WHERE (LAST='Eckel') AND 
(EMAIL Is Not Null)
ORDER BY FIRST

In addition, you don’t want this program to be hard coded to look for only one name. Instead, it should hunt for the name given as the command-line argument. Making these changes and turning the SQL statement into a dynamically-created String produces:

"SELECT FIRST, LAST, EMAIL " +
"FROM people.csv people " +
"WHERE " +
"(LAST='" + args[0] + "') " +
" AND (EMAIL Is Not Null) " +
"ORDER BY FIRST");

SQL has another way to insert names into a query called stored procedures, which is used for speed. But for much of your database experimentation and for your first cut, building your own query strings in Java is fine.

You can see from this example that by using the tools currently available – in particular the query-building tool – database programming with SQL and JDBC can be quite straightforward.

A GUI version of the lookup program

It’s more useful to leave the lookup program running all the time and simply switch to it and type in a name whenever you want to look someone up. The following program creates the lookup program as an application/applet, and it also adds name completion so the data will show up without forcing you to type the entire last name:

//: c15:VLookup.java
// GUI version of Lookup.java
// <applet code=VLookup
// width=500 height=200> </applet>
import javax.swing.*; 
import java.awt.*;
import java.awt.event.*;
import javax.swing.event.*;
import java.sql.*;
import com.bruceeckel.swing.*;

public class VLookup extends JApplet {
  String dbUrl = "jdbc:odbc:people";
  String user = "";
  String password = "";
  Statement s;
  JTextField searchFor = new JTextField(20);
  JLabel completion = 
    new JLabel("                        ");
  JTextArea results = new JTextArea(40, 20);
  public void init() {
    searchFor.getDocument().addDocumentListener(
      new SearchL());
    JPanel p = new JPanel();
    p.add(new Label("Last name to search for:"));
    p.add(searchFor);
    p.add(completion);
    Container cp = getContentPane();
    cp.setLayout(new BorderLayout());
    cp.add(p, BorderLayout.NORTH);
    cp.add(results, BorderLayout.CENTER);
    try {
      // Load the driver (registers itself)
      Class.forName(
        "sun.jdbc.odbc.JdbcOdbcDriver");
      Connection c = DriverManager.getConnection(
        dbUrl, user, password);
      s = c.createStatement();
    } catch(Exception e) {
      results.setText(e.getMessage());
    }
  }
  class SearchL implements DocumentListener {
    public void changedUpdate(DocumentEvent e){}
    public void insertUpdate(DocumentEvent e){
      textValueChanged();
    }
    public void removeUpdate(DocumentEvent e){
      textValueChanged();
    }
  }
  public void textValueChanged() {
    ResultSet r;
    if(searchFor.getText().length() == 0) {
      completion.setText("");
      results.setText("");
      return;
    }
    try {
      // Name completion:
      r = s.executeQuery(
        "SELECT LAST FROM people.csv people " +
        "WHERE (LAST Like '" +
        searchFor.getText()  + 
        "%') ORDER BY LAST");
      if(r.next()) 
        completion.setText(
          r.getString("last"));
      r = s.executeQuery(
        "SELECT FIRST, LAST, EMAIL " +
        "FROM people.csv people " +
        "WHERE (LAST='" + 
        completion.getText() +
        "') AND (EMAIL Is Not Null) " +
        "ORDER BY FIRST");
    } catch(Exception e) {
      results.setText(
        searchFor.getText() + "\n");
      results.append(e.getMessage());
      return; 
    }
    results.setText("");
    try {
      while(r.next()) {
        results.append(
          r.getString("Last") + ", " 
          + r.getString("fIRST") + 
          ": " + r.getString("EMAIL") + "\n");
      }
    } catch(Exception e) {
      results.setText(e.getMessage());
    }
  }
  public static void main(String[] args) {
    JApplet applet = new VLookup();
    JFrame frame = new JFrame("Email lookup");
//#frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
    frame.addWindowListener(new WClose()); // 1.2
    frame.add(applet);
    frame.setSize(500, 200);
    applet.init();
    applet.start();
    frame.setVisible(true);
  }
} ///:~

Much of the database logic is the same, but you can see that a TextListener is added to listen to the TextField, so that whenever you type a new character it first tries to do a name completion by looking up the last name in the database and using the first one that shows up. (It places it in the completion Label, and uses that as the lookup text.) This way, as soon as you’ve typed enough characters for the program to uniquely find the name you’re looking for, you can stop.

Why the JDBC API
seems so complex

When you browse the online documentation for JDBC it can seem daunting. In particular, in the DatabaseMetaData interface – which is just huge, contrary to most of the interfaces you see in Java – there are methods such as dataDefinitionCausesTransactionCommit( ), getMaxColumnNameLength( ), getMaxStatementLength( ), storesMixedCaseQuotedIdentifiers( ), supportsANSI92IntermediateSQL( ), supportsLimitedOuterJoins( ), and so on. What’s this all about?

As mentioned earlier, databases have seemed from their inception to be in a constant state of turmoil, primarily because the demand for database applications, and thus database tools, is so great. Only recently has there been any convergence on the common language of SQL (and there are plenty of other database languages in common use). But even with an SQL “standard” there are so many variations on that theme that JDBC must provide the large DatabaseMetaData interface so that your code can discover the capabilities of the particular “standard” SQL database that it’s currently connected to. In short, you can write simple, transportable SQL, but if you want to optimize speed your coding will multiply tremendously as you investigate the capabilities of a particular vendor’s database.

This, of course, is not Java’s fault. The discrepancies between database products are just something that JDBC tries to help compensate for. But bear in mind that your life will be easier if you can either write generic queries and not worry too much about performance, or, if you must tune for performance, know the platform you’re writing for so you don’t need to write all that investigation code.

There is more JDBC information available in the electronic documents that come as part of the Java 1.1 distribution from Sun. In addition, you can find more in the book JDBC Database Access with Java (Hamilton, Cattel, and Fisher, Addison-Wesley 1997). Other JDBC books are appearing regularly.

Servlets

Traditionally, the way to handle such a problem is to create an HTML page with a text field and a “submit” button. The user can type whatever he or she wants into the text field, and it will be submitted to the server without question. As it submits the data, the Web page also tells the server what to do with the data by mentioning the Common Gateway Interface (CGI) program that the server should run after receiving this data. This CGI program is typically written in either Perl or C (and sometimes C++, if the server supports it), and it must handle everything. First it looks at the data and decides whether it’s in the correct format. If not, the CGI program must create an HTML page to describe the problem; this page is handed to the server, which sends it back to the user. The user must then back up a page and try again. If the data is correct, the CGI program opens the data file and either adds the email address to the file or discovers that the address is already in the file. In both cases it must format an appropriate HTML page for the server to return to the user.

As Java programmers, this seems like an awkward way for us to solve the problem, and naturally, we’d like to do the whole thing in Java. First, we’ll use a Java applet to take care of data validation at the client site, without all that tedious Web traffic and page formatting. Then let’s skip the Perl CGI script in favor of a Java application running on the server. In fact, let’s skip the Web server altogether and simply make our own network connection from the applet to the Java application on the server!

As you’ll see, there are a number of issues that make this a more complicated problem than it seems. It would be ideal to write the applet using Java 1.1 but that’s hardly practical. At this writing, the number of users running Java 1.1-enabled browsers is small, and although such browsers are now commonly available, you’ll probably need to take into account that a significant number of users will be slow to upgrade. So to be on the safe side, the applet will be programmed using only Java 1.0 code. With this in mind, there will be no JAR files to combine .class files in the applet, so the applet should be designed to create as few .class files as possible to minimize download time.

The basic servlet

Introductory examples from the hands-on Java seminar which you can use or modify

Works with both GET & POST:

//: c15:ServletsRule.java
//# You must install the JSWDK from java.sun.com
//# and add servlet.jar to your classpath in
//# order to compile this file.
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;

public class ServletsRule extends HttpServlet {
  int i = 0; // Servlet "persistence"
  public void service(HttpServletRequest req, 
    HttpServletResponse res) throws IOException {
    PrintWriter out = res.getWriter();
    out.print("<HEAD><TITLE>");
    out.print("A server-side strategy");
    out.print("</TITLE></HEAD><BODY>");
    out.print("<h1>Servlets Rule! " + i++);
    out.print("</h1></BODY>");	
    out.close();    
  }
} ///:~

Easy to get HTML form data:

//: c15:EchoForm.java
//# You must install the JSWDK from java.sun.com
//# and add servlet.jar to your classpath in
//# order to compile this file.
// Dumps the name-value pairs of any HTML form
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
public class EchoForm extends HttpServlet {
  public void service(HttpServletRequest req, 
    HttpServletResponse res) throws IOException {
    res.setContentType("text/html");
    PrintWriter out = res.getWriter();
    out.print("<h1>Your form contained:</h1>");
    Enumeration flds = req.getParameterNames();
    while(flds.hasMoreElements()) {
      String field = (String)flds.nextElement();
      String value = req.getParameter(field);
      out.print(field + " = " + value + "<br>");
    }
    out.close();    
  }
} ///:~

One servlet, thread-per-request:

//: c15:ThreadServlet.java
//# You must install the JSWDK from java.sun.com
//# and add servlet.jar to your classpath in
//# order to compile this file.
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class ThreadServlet extends HttpServlet {
  int i;
  public void service(HttpServletRequest req, 
    HttpServletResponse res) throws IOException {
    res.setContentType("text/html");
    PrintWriter out = res.getWriter();
    synchronized(this) {
      try {
        Thread.currentThread().sleep(5000);
      } catch(InterruptedException e) {}
    }
    out.print("<h1>Finished " + i++ + "</h1>");
    out.close();    
  }
} ///:~


Java Server Pages

Here’s an extremely simple JSP example that uses a standard Java library call to get the current time in milliseconds, which is then divided by 1000 to produce the time in seconds. Since a JSP expression (the <%= ) is used, the result of the calculation is coerced into a String so it can be printed to the out object (which puts it int the web page):

//:! c15:jsp:ShowSeconds.jsp
<html><body>
<H1>The time in seconds is: 
<%= System.currentTimeMillis()/1000 %></H1>
</body></html>
///:~


Basic operations

//:! c15:jsp:Hello.jsp
<%-- This JSP comment will not appear in the
generated html --%>
<%-- This is a JSP directive: --%>
<%@ page import="java.util.*" %>
<%-- These are declarations: --%>
<%! long loadTime= System.currentTimeMillis(); %>
<%! Date loadDate = new Date(); %>
<%! int hitCount = 0; %>
<html><body>
<!-- This HTML comment will appear as a comment 
on the generated html page -->
<%-- The following comment has the result of a 
JSP expression inserted in the generated html;
the '=' indicates a JSP expression --%>
<!-- This page was loaded at <%= loadDate %> -->
<H1>Hello, world! It's <%= new Date() %></H1>
<H2>Here's an object: <%= new Object() %></H2>
<H2>This page has been up 
<%= (System.currentTimeMillis()-loadTime)/1000 %>
seconds</H2>
<H3>Page has been accessed <%= ++hitCount %> 
times since <%= loadDate %></H3>
<%-- A "scriptlet" which writes to the server
console. Note that a ';' is required: --%>
<% System.out.println("Goodbye"); %>
</body></html>
///:~

You cannot nest JSP comments (‘<%--’ and ‘--%>’) inside of any other JSP directives, but you can use comments outside of JSP directives to prevent them from being compiled.

Embedding blocks of code

//:! c15:jsp:PageContext.jsp 
<%--Viewing the attributes in the pageContext--%>
<%-- Note that you can include any amount of code
inside the scriptlet tags --%>
<%@ page import="java.util.*" %>
<html><body>
<%
  Enumeration e =
    pageContext.getAttributeNamesInScope(1);
  while(e.hasMoreElements()) {
    out.println("\t<li>" + 
      e.nextElement() + "</li>");
  }
%>
<H4>End of list</H4>
</body></html>
///:~


Extracting fields and values

This example first looks to see where there are any parameters in the submitted form; if not, it knows that you’re just calling the JSP for the first time and that it should generate the form (this is a convenient technique to allow you to put the form generator and its response in the same source file). Next, it displays the fields and their values (this second section will produce nothing on the initial call to the JSP).

//:! c15:jsp:DisplayFormData.jsp
<%-- Fetching the data from an HTML form --%>
<%-- Also generates the form --%>
<%@ page import="java.util.*" %>
<html><body>
<H1>DisplayFormData</H1><H3>
<%
Enumeration f = request.getParameterNames();
if(!f.hasMoreElements()) { // No fields
%>
<form method="POST" action="DisplayFormData.jsp">
<%
  for(int i = 0; i < 10; i++) {
%>
    Field<%=i%>:
    <input type="text" size="20" 
      name="Field<%=i%>" value="Value<%=i%>"><br>
<% } %>
  <INPUT TYPE=submit name=submit Value="Submit">
</form>
<% } %>

<%
Enumeration flds = request.getParameterNames();
while(flds.hasMoreElements()) {
  String field = (String)flds.nextElement();
  String value = request.getParameter(field);
%>
  <li><%= field %> = <%= value %></li>
<%
} 
%>
</H3></body></html>
///:~

Manipulating sessions in JSP

//:! c15:jsp:SessionObject.jsp
<%--Setting and getting session object values--%>
<html><body>
<H1>Session id: <%= session.getId() %></H1>
<H1>This session was created at 
<%= session.getCreationTime() %></H1>
<H3>MaxInactiveInterval= 
  <%= session.getMaxInactiveInterval() %></H3>
<% session.setMaxInactiveInterval(5); %>
<H3>MaxInactiveInterval= 
  <%= session.getMaxInactiveInterval() %></H3>
<H1>Session value for "My dog" 
<%= session.getValue("My dog") %></H1>
<% session.putValue("My dog", 
  new String("Ralph")); %>
<H1>My dog's name is 
<%= session.getValue("My dog") %></H1>
<FORM TYPE=POST ACTION=SessionObject2.jsp>
<INPUT TYPE=submit name=submit Value="Submit">
</FORM>
</body></html>
///:~
//:! c15:jsp:SessionObject2.jsp
<%--The session object carries through--%>
<html><body>
<H1>Session id: <%= session.getId() %></H1>
<H1>Session value for "My dog" 
<%= session.getValue("My dog") %></H1>
<% session.invalidate(); %>
</body></html>
///:~

Creating and modifying Cookies

//:! c15:jsp:PlayWithCookies.jsp
<%--This program has different behaviors under
 different browsers! --%>
<html><body>
<H1>Session id: <%= session.getId() %></H1>
<%
  Cookie[] cookies = request.getCookies();
  for(int i = 0; i < cookies.length; i++) { %>
Cookie name: <%= cookies[i].getName() %> <br>
value: <%= cookies[i].getValue() %><br>
Max age in seconds: 
  <%= cookies[i].getMaxAge() %><br>
<% cookies[i].setMaxAge(3); %>
Max age in seconds: 
  <%= cookies[i].getMaxAge() %><br>
<% response.addCookie(cookies[i]); %>
<% } %>
<%-- <% response.addCookie(
    new Cookie("Bob", "Car salesman")); %> --%>
</body></html>
///:~


Passing control to other servlets

//:! c15:RequestDispatcher1.jsp
<%--Using the request dispatcher forward() 
  to pass control to another servlet --%>
<html><body><H1>In RequestDispatcher 1</H1>
<% response.setContentType("text/html"); 
out.println(
  "Printing to response object from RD 1");
RequestDispatcher rd = 
  application.getRequestDispatcher(
    "/jsp/RequestDispatcher2.jsp");
rd.forward(request, response);
out.println("<H1>Hello</H1>");
%>
</body></html>
///:~
//:! c15:RequestDispatcher2.jsp
<%--Recieves a dispatched request --%>
<html><body>
<% response.setContentType("text/html"); 
out.println(
  "Printing to response object from RD 2);
%>
<H1>In RequestDispatcher 2</H1>
</body></html>
///:~
//:! c15:RequestDispatcher3.jsp
<%--Using a RequestDispatcher.include() --%>
<html><body>
<H1>In RequestDispatcher 3</H1>
<%  response.setContentType("text/html"); 
out.println(
  "Printing to response object from RD 3"); 
out.println("<H1>In RD 3</H1>");
RequestDispatcher rd =  
  application.getRequestDispatcher(
    "/jsp/RequestDispatcher2.jsp");
rd.include(request, response);
out.println("<H1>Out RD 3</H1>");
%>
<H1>Hello</H1>
</body></html>
///:~


RMI in the enterprise

Corba in the enterprise

Enterprise Java Beans (EJB)[66]

The Enterprise JavaBeans (EJB) specification defines a component model and deployment environment to simplify the lifecycle of multi-tiered distributed systems. Enterprise JavaBeans (like all Java 2, Enterprise Edition API’s) is a specification defined by JavaSoft, endorsed and implemented by many vendors. It not an actual product. An EJB compliant product, is a product that implements and adheres to the standards set out in the specification.

The objective of the EJB specification is to “make it easy to write [server side] applications”. The EJB specification achieves this objective by defining a set of design patterns for the server-side code as well as roles, which various developers, administrators, and system components play in the various phases of the lifecycle of a distributed application.

Through the use of a set of standard design patterns and roles - which decouple business logic from system infrastructure- products which adhere to the EJB specification make it possible to write enterprise applications without having to understand these low-level system infrastructure issues such as transactions, caching mechanisms, multi-threading and security.

The definition of these roles and components also gives the application developer vendor independence. EJB Vendors adhere and contribute to the one specification, hence Enterprise Java Bean components are easily transferable between vendors tools and platforms and deliver on Java’s promise of “Write Once, Run Anywhere TM ” philosophy.

Although the EJB specification is a Java specification, based on and implemented in the Java language, it also defines interoperability with non-Java Systems. It also defines interoperability with the CORBA specification.

EJB is probably the most visible of the many API’s that compromise the Java 2, Enterprise Edition specification. The classes and interfaces that make up the Enterprise Java Beans API are defined in the standard extension package javax.ejb. The latest version of the EJB specification is 1.1 and is available from http://java.sun.com/products/ejb.

Why do we need EJB?

When constructing a multi-tiered system using RMI,CORBA or any other middleware, System architects and developers are faced with many challenges such as:

All of these issues have arisen before we have even scoped the problem that we are trying to solve! As you can see, a distributed system developer has a lot to think about. The Enterprise JavaBeans specification acknowledges these issues and that all or some of them must be overcome when developing a distributed system and defines how these issues are handled.

Both RMI and CORBA allow for the development of a distributed application, without getting into a heated debate of which technology is better (which we can refer to the alien book). Distributed protocols and API's such as RMI and CORBA, allowed the Enterprise developer to create their own distributed components. However, the design of these components were/are left up to the individual developer. Which meant that components that are created by one development team for a specific server, were not reusable in another and components that were reusable, usually relied on a framework that was developed to handle dist tx's, security mechanisms and caching algorithms in a server dependant manner.

With more and more systems becoming multi-tiered and various vendors each creating there own solution to the problems outlined above, it began to lock developers into using one vendors implementation. As it was very hard to migrate code from one technology to another.

EJB further refines the architecture by formally defining how the middle tier is developed. The goal being that the developer doesn’t have to worry about coding common low-level frameworks to handle

Distributed transactions, security mechanisms and caching algorithms. And because all the code that is developed adheres to a standard, then code in one server, should be able to run in another EJB Compliant server, without requiring recompiling.

EJB allows for a market of standard enterprise components that can be sold and redistributed by domain experts.

EJB corrects the problem by defining a standard RUNTIME environment, i.e. the EJB Container. The EJB Container handles and hides the complexities from the bean developer so that the bean developer can rely on these common services being provided and focus on solving the business problem, and also avoid being locked into a single vendor’s architecture.

How do I put the ‘E’ in my existing JavaBeans?

There is much confusion about the relationship between the JavaBeans component model and the Enterprise JavaBeans specification. Whilst both the JavaBeans and Enterprise JavaBeans specifications share the same objectives in promoting reuse and portability of Java code between development and deployment tools with the use of standard design patterns, the motives behind each specification are geared to solve different problems.

The standards defined in the JavaBeans component model are designed for creating reusable components that are typically used in IDE development tools and are commonly, although not exclusively visual components.

The Enterprise JavaBeans specification defines a component model for developing server side Java code. An Enterprise JavaBean can use JavaBeans within its implementation.

Components in the Enterprise Java Beans

What is Components does EJB define? What other J2EE components are required?

EJB Server

An EJB Server is defined as an Application Server that contains and runs 1 or more EJB Containers. The definition of an interface between an EJB Server and EJB Container is currently not defined in the EJB specification. The specification suggests that both the Container and Server are the same vendor.

EJB Container

The EJB specification introduces the concept of an EJB Container (container). The Container is a runtime environment that contains components (Enterprise JavaBeans) and provides a set of standard services to the components. The EJB Containers responsibilities are tightly defined by the specification to allow for vendor neutrality. The EJB container is responsible for providing some of the technical benefits of EJB, including distributed transactions, security, life cycle management of beans, caching, threading and session management.

The EJB Container achieves these tasks by acting as the “glue” between the EJB and the client that is using the Beans services. Because of this level of indirection, the container can transparently cache beans and propagate transaction and security information without the client code or bean itself requiring any low-level code.

Enterprise JavaBeans

Enterprise Java Beans are reusable components that are developed in accordance with the Design

Patterns highlighted in the EJB specification. EJBeans can be of 2 different types, Entity Beans and Session Beans. The EJB 1.0 specification did not require Entity Beans to be implemented, this has changed in version 1.1, which makes mandatory the implementation of Entity Beans. The EJB 1.1 specification has also further clarified some ambiguous issues with Session Beans. It is the responsibility of the EJB Container to provide caching, session and lifecycle management of all EJBs so that the Bean Provider can focus on solving the business problem at hand.

EJB defines 2 design patterns or Bean types, Session Beans and Entity Beans. Figure x. shows a hierarchy of EJB Types

<author note>

show simple hierarchy chart of EJB -> Entity Bean/Session Bean -> BMP/CMP, Stateful/Stateless

</author note>

Session Beans

Session Beans are components that perform work on behalf of a client and offer these services. Session Beans can be either Stateful or Stateless and serve only one client. Session Beans represent operations on persistent data, A Stateful Session Bean maintains it data for a particular client between method invocations. A Stateless Session bean is stateless and can be reused by different clients between requests as no state is maintained within the bean.

Entity Beans

Entity Beans are components that represent data that is persistent e.g. data from a database and behavior of this data. Persistence in Entity Beans can be managed by the developer, this is known as “Bean Managed Persistence” or it can be managed by the EJB Container, possibly with the aid of an Object to Relational Mapping tool. This approach is known as “Container Managed Persistence” .Multiple clients can share entity beans, the container is required to handle multi-threaded issues. Because an Entity bean represents persistent data, the life of the Entity Bean outlives the container.

Bean Managed Persistence

Bean Managed Persistence or BMP is where the Bean Provider is responsible for implementing all of the logic required to create a new EJB, update some fields of an Entity Bean, delete an Entity Bean and find an Entity Bean from persistent store. This usually involves writing JDBC code to interact with a database. With BMP, the developer is in full control of how the Entity Bean persistence is managed.

BMP also gives flexibility where a CMP implementation may not be available e.g., if you wanted to create an EJB that wrapped some code on an existing mainframe system, you could write your persistence using CORBA. This would be something that CMP would have a lot of trouble trying to do!

Container Managed Persistence

Container Managed Persistence (CMP) is where the EJB Container handles the process of persisting Entity Beans on your behalf. CMP reduces the development time of Entity Beans as the developer can focus totally on the logic of the component rather than how it is stored and retrieved.

Bean Managed vs. Container Managed Persistence

There are some interesting points that should be considered when choosing the persistence mechanisms for your EJB application. If you choose to use CMP, you are relying on a tool to implement your persistence for you. Whilst this seems like a logical thing to do for simple beans, it may not have the ability to do complex forms of persistence such as non-jdbc persistence. Also the lack of standards defined for the Entity Bean to Relational Database mapping could mean that your Bean is forced to run in a certain Container or use a certain mapping tool. The table below highlights some differences and advantages and disadvantages of each persistence mechanism

Figure x. - Comparison of Persistence Mechanisms.

Task

Bean-Managed Persistence

Container-Managed Persistence

Notes

Required Amount of Coding

BMP requires hand coding of the 4 persistence methods, which can mean a lot of coding JDBC calls or other persistence code

EJB Container handles all of this on behalf of the user, and usually creates a set of Relational database tables for your entity beans.


Performance

Developer has fine control over persistence, e.g. update only a certain set of fields or execute a stored procedure. Developer can easily optimize the database access calls

Some CMP tools are very advanced and can provide optimizations and caching. Some CMP tools are primitive and can only


Support for different persistence stores

BMP will allow the user to change to SQL/J, when a driver is available rather than waiting for CMP vendor to implement engine using SQL/J. Also BMP can be implemented using CORBA



Portability/Vendor Independence





Things to remember -> Here is an example of an excellent design aspect of EJB. Because persistence is isolated from the remote interface, you could change the pm of your bean without harming any client applications. This means that for early prototype development and EJB design, one could use CMP and then transfer to bmp and optimize JDBC code with a DBA or specialist to improve performance.

Deployment Descriptors

The EJB component model allows for the setting of parameters at deployment time, through the use of a deployment descriptor. Deployment descriptors allow the modification of environment variables, transactional attributes and security roles to be defined after the development of the Bean. This allows the Enterprise JavaBean developer to focus totally on creating the business logic for the bean.

A deployment descriptor is used to describe the Enterprise JavaBeans that are contained in an EJB-jar file. The difference between an EJB-jar file and a normal jar file is the deployment descriptor. The deployment descriptor must be placed inside the META-INF directory of the jar file, along with the manifest file. It must also have the filename EJB-jar.xml

Prior to the EJB 1.1 specification, the deployment descriptor was a serialized object that was defined in code, compiled and placed inside the jar file. This made it very difficult to change, because it required de-serializing the objects, loading them into memory, changing the attributes, recompiling and deserializing the objects again. This was a tedious process if the deployer only wanted to change one attribute.

The EJB 1.1 specification changed the descriptor and totally removed the serialized objects in favor of XML. Moving to XML has many advantages. The deployment descriptor can be edited and read with a normal text editor or XML tool, and doesn’t required de-serialization or recompilation of the deployment object.

Most EJB Tools will contain a utility to convert from the previous format to the new XML 1.1 format.

JNDI

Java Naming and Directory Interface (JNDI) is used in Enterprise JavaBeans as the naming service. It is used to locate Enterprise JavaBeans and Home objects located on the network. JNDI makes looking up objects easier than looking up a name in a phone book. JNDI maps very closely to other naming and directory standards such as LDAP and CORBA CosNaming. For more details on JNDI see....

JTA/JTS

Java Transaction API / Java Transaction Service. The EJB Container uses JTA/JTS as its transactional API. A Bean developer can use the JTS to get access to a transaction, but most commonly transactions are defined at deployment time and the Bean developer can code their Enterprise JavaBean without having to define transactional boundaries. The EJB Container is responsible for handling the transaction whether it is local or distributed. The JTS specification is the Java mapping to the CORBA OTS (Object Transaction Service). For more details see....

CORBA

CORBA is increasingly growing as a standard in Enterprise development due to its vendor, platform and language independence. There are many similarities between the services offered by CORBA, known as the Common Object Services (COS) and the services offered in Enterprise JavaBeans. Because the Enterprise JavaBeans is a higher level specification – the wire protocol for EJB is RMI, although RMI can be implemented on top of a variety of protocols including Java Remote Method Protocol (JRMP) and CORBA’s Internet Interoperable ORB Protocol (IIOP). Because of CORBA’s maturity and robustness, it is quite common to see an EJB Container Provider develop an EJB Container on top of CORBA. Compatibility with the CORBA specification and IIOP protocol is defined in the EJB specification and the RMI/IIOP specification. The 1.1 specification quotes “The Enterprise JavaBeans architecture will be compatible with the CORBA protocols.”

Implementing an EJB Container on top of CORBA also allows access to legacy systems and applications that are written in different languages and again avoids proprietary protocols and vendor lock-in.

Roles of Enterprise Java Beans

The EJB specification defines roles for different developers in the process of creating an EJB application. It defines domains that isolate different areas of development. This allows developers of different technical and domain knowledge to work together effectively. The EJB Specification defines 6 roles for the development/deployment and management of an EJB application.

Enterprise Bean Provider

The Enterprise Bean Provider is the developer who adheres to the EJB design patterns of Entity and Session Beans to develop beans, which together solve a business problem. Enterprise JavaBeans are packaged into jar files, The jar file must also contain a Deployment Descriptor to outline the Enterprise JavaBeans contained within the jar file.

Application Assembler

The role of the Application Assembler is to assemble applications from a collection of EJB-jar files. The EJB-jar files may come from a vendor or developed from scratch, for example a company who specializes in accounting could produce and resell a set of accounting beans to take care of general accounting tasks. However, these Beans may not perform all of the tasks required so some extra Enterprise JavaBeans are developed to interact and customize the Beans into a useful application. The role of the Application Assembler is to collect all of the Enterprise JavaBeans required and assemble them into a practical, useful application.

Deployer

The Deployer’s role of the to take the collection of EJB-jar files from the Assembler and/or Bean Provider and deploy them into an EJB Container. The deployer is responsible for generating any vendor specific stubs and skeletons that may be required as well as defining transactional boundaries between Enterprise JavaBeans and security roles. The Deployer achieves these tasks with the aid of tools that are provided by the EJB Server/Container Provider.

EJB Server/Container Provider

The current version of the EJB specification does not clearly define the interface between a Container provider and a server provider and assumes the same vendor plays these roles. The role of the Server/Container provider is to provide an EJB Container that is compliant with the behavior specified in the EJB specification.

System Administrator

The role of the Systems Administrator is to oversee the most important goal of the entire system. That it is up and running. Management and Administration of EJB and Distributed systems is a crucial yet challenging problem. This is due to the fact that an “application” is not defined as a process that is running on a machine. A distributed application can consist of many different components and services all configured and interacting together correctly.

Development with Enterprise Java Beans

<authors note>

I noticed that you guys have some sort of community theme going, If you send me some details of what we should tailor to EJB, then I’ll work off that. I’m still undecided whether to show both a BMP and CMP bean, as support for CMP is fairly fresh right now. Is 1 Entity bean example enough?

</authors note>

Developing a Simple Stateless Session Bean

Bean Provider - develop the bean, home and remote interfaces

Bean Provider - create and add entries into deployment descriptor

Bean Provider - create an EJB-jar file

Deployer – generate vendor stubs

Deployer – add assembly and deployment entries into EJB-jar file

Deployer – add bean into container

Systems Admin – run it!

Developing a Simple Stateful Session Bean

Developing a BMP Entity Bean

Developing a CMP Entity Bean

Discussion Points, Interesting Issues and Further Reading

Why so many rules?

Because the EJB container is responsible for many complex tasks, it is required that Enterprise Java Bean Components that are developed adhere to a standard set of patterns. By enforcing many rules, it also allows for vendor independence between development tools, deployment tools and EJB Servers/Containers.

What is not defined in the EJB Specification

Events – Currently there is no event model specified in EJB. This will be covered in EJB 2.0

Singleton Pattern – Entity and Session Beans are geared currently towards database and e-commerce applications, and therefore do not provide suitable patterns for all required scenarios. An example of this is the Singleton Pattern. A Singleton Pattern is used when the application wants only 1 specific instance of an object. Because the EJB Container hides instances from the user, there is no guarantee that the user will always get the same instance of an object. Here is an example of where CORBA or RMI could be integrated into an EJB application as these allow the creation of Singleton objects.

Other References

Enterprise Java Beans Home Page - http://java.sun.com/products/ejb/

specification, downloads

Enterprise Java Beans Special Interest Group - http://www.mgm-edv.de/ejbsig/ejbsig.html

Books for further reading

Andreas Vogel, Tom Valesky, Richard Monson-Hafel

Accessing enterprise services

An overview of the last topics in the chapter and their objectives.

Java Naming and Directory Interface (JNDI)

Java Messaging Service (JMS)

JavaMail

Enterprise security

Summary

Summary text

Suggested books

Exercises

  1. Exercise

[66] This section was contributed by Robert Castaneda (robertc@altavista.net)

[ Previous Chapter ] [ Short TOC ] [ Table of Contents ] [ Index ] [ Next Chapter ]
Last Update:02/04/2000