J2EE Design Strategies Part I

Enterprise Java is an extremely popular development platform for good reasons. It allows developers to create highly sophisticated applications that have many desirable characteristics such as scalability, high availability, etc. However, with J2EE, you are potentially building distributed applications, which is a complicated endeavor no matter how it is built. As developers come to use J2EE more and more, they are discovering some pitfalls that are both easy to fall into and easy to avoid (if you know what to look for). J2EE development comes with its share of bear traps, just waiting to snap the leg off the unwary developer.

This paper is designed to highlight several of these avoidable pitfalls. It does so from an entirely pragmatic approach. Much of the available material on design patterns and best practices is presented in a very academic manner. The aim of this paper is just the opposite — present common problems and their associated solutions. It starts with J2EE Web best practices, moves to EJB best practices, and concludes with some common worst practices that should be avoided.

Web

Web development is an important aspect of J2EE, and it has its share of potential dangers.

Singleton Servlets

A Singleton servlet sounds like an oxymoron — aren’t servlets already singleton objects? Some background is in order. First, a singleton is an object that can only be instantiated once. There are several different ways to achieve this effect in the Java language, most commonly with a static factory method. This is a common technique anytime an object reference can be reused rather than a new one instantiated. Of course, servlets already act in many ways like singleton objects — the servlet engine takes care of instantiating the servlet class for you, and generally only creates one instance of the servlet and spawns threads to handle users’ requests. Allowing the servlet engine to do this works fine in most cases. However, there are a few cases where you want the singleton-like behavior but also have to know what the instance is called. When the servlet engine creates the servlet, it assigns an internal reference to the servlet instance, and never lets the developer directly access it. This is a problem if you have a helper servlet storing configuration information, connection pool management, etc. What is needed is a way to allow the servlet engine to instantiate the servlet for us yet still be able to get to it.

Here is an example of a servlet that meets this criteria. It is a servlet that manages a homegrown connection pool.

Listing 1: Singleton Servlet for Managing Connection Pool

package webdev.exercises.workshop1;
import javax.servlet.*;
import java.sql.*;
Import webdev.utils.ConnectionPool;
public class PoolMgr extends GenericServlet 
{
    private static PoolMgr thePoolMgr;
    private ConnectionPool connectionPool;
    static final String DB_CLASS = "interbase.interclient.Driver";
    static final String DB_URL = "jdbc:interbase://localhost/e:/webdev/data/eMotherEarth.gdb";
    public PoolMgr() 
    {
    }
    public void init() throws ServletException 
    {
       try 
    {
            String dbUrl = getServletContext().getInitParameter("dbUrl");
            connectionPool = new ConnectionPool(DB_CLASS, dbUrl,
                 "sysdba", "masterkey", 5, 20, false);
            getServletContext().log("Created connection pool successfully");
       } 
    catch (SQLException sqlx) 
    {
            getServletContext()Log("Connection error", sqlx);
       }
       thePoolMgr = this;
    }
    public void service(ServletRequest req, ServletResponse res) throws javax.servlet.ServletException, 
         java.io.IOException 
    {
        //--- intentionally left blank
    }
    public static PoolMgr getPoolMgr() 
    {
        return thePoolMgr;
    }
    public ConnectionPool getConnectionPool() 
    {
        return connectionPool;
    }
}

First, note that this is a GenericServlet instead of an HttpServlet — the user never directly accesses this servlet. It exists to provide infrastructure support to the other servlets in the application. The servlet includes a static member variable that references itself (common in "normal" singleton classes). It also has a (non-static) reference to the connection pool class. In the init() method of the servlet, the connection pools is instantiated. The very last line of this method saves the reference created by the servlet engine of this instance of the servlet. The GenericServlet class includes a service() method, which is not needed here (intentionally left blank to highlight that point). The servlet includes a static method called getPoolMgr() that returns the saved instance of the class. This is how other servlets and classes can access the instance created by the servlet engine. We are using the class name (and a static member variable) to keep the reference for us. To access this pool manager from another servlet, you can use code like this:

Listing 2: Snippet of servlet that uses a singleton

Connection con = null;
try 
{
 //-- get connection from pool
 con = PoolMgr.getPoolMgr().getConnectionPool().getConnection();
 //-- do a bunch of stuff with the connection
} 
catch (SQLException sqlx) 
{
 throw new ServletException(sqlx.getMessage());
} 
finally 
{
 PoolMgr.getPoolMgr().getConnectionPool().free(con);
}

The access to the connection pool class is always done through the PoolMgr servlet’s method. Thus, you can allow the servlet engine to instantiate the object for you and still access it through the class. This type of singleton servlet is also good for holding web application-wide configuration info. In fact, it is common to have the servlet automatically created by the servlet engine. The web.xml file allows you to specify a startup order for a particular servlet. Here is the servlet definition from the web.xml file for this project.

Listing 3: Web.xml entry to auto-load the PoolMgr servlet

 
  PoolMgr 
   
    webdev.exercises.workshop1.PoolMgr 
   
  99 
 

An alternative to using a Singleton Servlet is to use a ServletContextListener, which was added as part of the servlet 2.2 specification. It and the listener event handlers for web development allow you to tie behavior to particular events. The listing below shows how to create a connection pool using a ServletContextListener.

Listing 4: StartupConfigurationListener creates a connection pool upon application startup.

package com.nealford.art.facade.emotherearth.util;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletContext;
import org.apache.commons.pool.impl.GenericKeyedObjectPool;
import java.sql.SQLException;
public class StartupConfigurationListener implements
        ServletContextListener, AttributeConstants 
{
    public void contextInitialized(ServletContextEvent sce) 
    {
        initializeDatabaseConnectionPool(sce.getServletContext());
        BoundaryFacade.getInstance().initiaizeBoundaryPool(sce.getServletContext());
    }
 
    public void contextDestroyed(ServletContextEvent sce) 
    {
    }
    private void initializeDatabaseConnectionPool(ServletContext sc) 
    {
        DBPool dbPool = null;
        try 
        {
            dbPool = createConnectionPool(sc);
        } 
        catch (SQLException sqlx) 
        {
            sc.log(new java.util.Date() + ":Connection pool error", sqlx);
        }
        sc.setAttribute(DB_POOL, dbPool);
    }
    private DBPool createConnectionPool(ServletContext sc)
            throws SQLException 
    {
        String driverClass = sc.getInitParameter(DRIVER_CLASS);
        String password = sc.getInitParameter(PASSWORD);
        String dbUrl = sc.getInitParameter(DB_URL);
        String user = sc.getInitParameter(USER);
        DBPool dbPool = null;
        dbPool = new DBPool(driverClass, dbUrl, user, password);
        return dbPool;
    }
}

The advantage of the SingletonServlet lies in the ability for non-web classes to create references to it. For example, you might have a POJO (Plain Old Java Object) that handles database access for your application. It has no way to get to any of the web collections directly because it has no access to the servlet context. Using a SingletonServlet, the class name of the servlet allows the developer to get to the underlying instance. So, even in the presence of the listener classes introduced to the web API, Singleton Servlets still have uses.

Model-View-Controller for the Web

In the beginning, there were Servlets, and it was good. They were much better than the alternatives, and allowed for scalable, robust web development. However, there was trouble in paradise. Web development partitioned itself into two camps: art school dropouts (invariably Macintosh users) who could create the beautiful look and feel for the web application, and the Java developers who made it work. The guys in the basement hand crafted the beautiful HTML and passed it to the developers who had to incorporate it into the dynamic content of the web site. For the developers, it was a thankless, tedious job, inserting all that beautiful HTML into the Java code. But, you drank lots of coffee and lived through it. Then, the unthinkable happened: the CEO got an AOL disk in the mail and visited a web site he’d never been to before. Come Monday, the commandment came down from on high: We’re completely changing the look and feel of the web site. The art school dropouts fired up their Macs and started realizing the CEO’s vision, and the developers got a sinking feeling in the pit of their stomachs. Time to do it all over again. The problem? Too much HTML in the Java code.

Then JSP’s appeared. Here was the answer to all our prayers. JSP’s have the same advantages of servlets (they are after all a type of servlet) and were much better at handling the user interface part of web design. In fact, the art school dropouts could craft the HTML, save it as JSP, and pass it right to the developers. However, all was still not well. The developers now must deal much more directly with the display characteristics of the application. Thus, the syntax of the JSP quickly becomes very cryptic, with the HTML and Java code interspersed together. The verdict: too much Java in the HTML.

Then came the Model-View-Controller design pattern for the web. If you’ve been living in a cave and aren’t familiar with this most famous of design patterns yet, here’s the capsulated version. The model represents the business logic and data in the application and resides in JavaBeans and/or Enterprise JavaBeans. The view is represented primarily by JSP pages, which have as little Java code in them as possible. In fact, all Java code should really be handled by method calls on the model beans or custom tags. The controller is the way that the view interacts with the model. In the web world, a servlet is the controller. Here is the typical scenario for web MVC. The user accesses a controller servlet. The servlet instantiates beans, calls methods on them to perform work, adds the beans with displayable information to one of the collections (for example, the request collection), and forwards the beans to a JSP that shows the user the results.

And it was good. Now, the display information is cleanly partitioned away from the "real" work of the application, which can be strictly in JavaBeans. The application could also start using regular JavaBeans, then scale up to use Enterprise JavaBeans without having to change the controller or presentation layers. This is clearly the best way to build web applications. It is easy to maintain, easy to update, and there is very little impact when one part of the system needs to change (now, the art school dropouts have to worry about the new look and feel, not the developers). This design patterns neatly modularizes the constituent parts of web applications.

Now what’s wrong? The problem with the MVC web applications (now frequently called "Model2", to distinguish it from MVC for regular applications) has to do with how you architect the web application. For example, if you create a different controller servlet for each page the user wants to visit, you end up with dozens or hundreds of servlets that look almost identical. Another problem is that these servlets, once visited, permanently reside as objects in the servlet engine. An alternative is to create one monster controller servlet to handle all the requests. The problem here is that you have to figure out a way to map the requests to different views. This is frequently done with parameters sent to the web site, identifying what command you want to execute. But, unless you are clever about it, your "uber servlet" becomes a massive set of "if…else" statements or a huge "switch…case" statement. Any changes require editing this servlet, which quickly becomes unruly and ugly. What is needed is an application framework for web development that handles most of these gory details. And that’s where Struts comes in.

No comments yet

Leave a Reply

You must be logged in to post a comment.