Struts: J2EE Design Strategies

Struts is a web application framework created as part of the Jakarta project at Apache. As such, it handles 35 – 40% of the plumbing code required to create clean Model2 web applications. Because it is an open source project, you can download it and start using it from http://jakarta.apache.org/struts/. For this session, I am covering the Struts setup, the controller servlet, form handling, custom tags, and internationalization, which only scrapes the top of the Struts iceberg.

To set up Struts, you must download Struts and place the struts.jar file in a location where it can be loaded by the servlet engine (in other words, it must be on the classpath for the servlet engine). As an alternative, you can place the JAR file in the web application’s lib directory, where the servlet engine will automatically load it. Next, if you plan to use any of the Struts custom tags, the .TLD files for the libraries you are using should be placed in your application’s WEB-INF directory (more about what the custom tag libraries do for you later). Next, you need to make some modifications to the WEB.XML file for your application. The changes required are shown in this listing.

Listing 5: Web.xml entries for Struts

<servlet>
      <servlet-name>action</servlet-name>
      <servlet-class>
        org.apache.struts.action.ActionServlet
      </servlet-class>
      <init-param>
        <param-name>config</param-name>
        <param-value>
          /WEB-INF/struts-config.xml
        </param-value>
      </init-param>
      <init-param>
        <param-name>debug</param-name>
        <param-value>2</param-value>
      </init-param>
      <init-param>
        <param-name>mapping</param-name>
        <param-value>
      org.apache.struts.action.RequestActionMapping
        </param-value>
      </init-param>
      <load-on-startup>2</load-on-startup>
    </servlet>
 
    <servlet-mapping>
     <servlet-name>action</servlet-name>
     <url-pattern>*.do</url-pattern>
    </servlet-mapping>
    <taglib>
      <taglib-uri>
        /WEB-INF/struts-bean.tld
      </taglib-uri>
      <taglib-location>
        /WEB-INF/struts-bean.tld
      </taglib-location>
    </taglib>
    <taglib>
      <taglib-uri>
        /WEB-INF/struts-html.tld
      </taglib-uri>
      <taglib-location>
        /WEB-INF/struts-html.tld
      </taglib-location>
    </taglib>
    <taglib>
      <taglib-uri>
        /WEB-INF/struts-logic.tld
      </taglib-uri>
      <taglib-location>
        /WEB-INF/struts-logic.tld
      </taglib-location>
    </taglib>
    <taglib>
      <taglib-uri>
        /WEB-INF/struts-template.tld
      </taglib-uri>
      <taglib-location>
        /WEB-INF/struts-template.tld
      </taglib-location>
    </taglib>

The first entry registers the Struts ActionServlet. This is the single point of entry for the application. Basically, the Struts developers have already written the only controller you will need. More about exactly what it does in a moment. Several initialization parameters are passed to the controller servlet. The first specifies where to find the struts-config.xml document. This is the mapping document used by the controller. The second parameter specifies the debug level – the higher the number, the more debugging information is sent to the servlet engine log file. The third parameter is the mapping value. This points to the class responsible for handling RequestActionMapping (more about this later as well). Last, the load-on-startup tag ensures that the servlet engine loads the Struts controller as it starts up. This saves time on the first invocation of the servlet by pre-loading it in memory. The number passed here isn’t particularly important (unless you have some dependent startup classes that depend on the controller being in memory – if so, make sure the controller servlet has a lower number).

The next entry in the WEB.XML file specifies how you want to let the servlet engine know that a Struts resource has been requested (as opposed to some other content from the web site). There are two ways to handle this: prefix mapping or extension mapping. With prefix mapping, every resource that appears after a certain prefix will be sent to the Struts controller. This is the way that servlet engines specify that a servlet has been requested (the servlet resource always appears after a "/servlet" prefix). The other alternative (which we are using here) maps all resource with a particular extension to the Struts controller. This works fine as long as you pick an extension that doesn’t interfere with any other registered file type. In this case, we are mapping all requests that end with "Do" to the Struts controller. The "Do" extension is arbitrary (it implies that a resource will "do" something), but is the one used by all the Struts examples so it is a bit of a Struts standard. The remaining additions to the WEB.XML file specify the locations of the .TLD files for the custom Struts tags. Obviously, you only have to list the tag libraries you are using for this application (although it doesn’t hurt to list all six).

The next configuration item needed for the web application is the struts-config.xml document. A sample version of this document is shown here.

Listing 6: A simple struts.config XML document

<struts-config>
    <form-beans>
        <form-bean
            name="addItem"
            type="schedule.ScheduleItem" />
    </form-beans>
    <action-mappings>
        <action
            path="/sched"
            type="schedule.ViewScheduleAction" />
        <action
            path="/schedEntry"
            type="schedule.ScheduleEntryAction" />
        <action
            path="/add"
            type="schedule.AddToScheduleAction"
            name="addItem"
            input="/ScheduleEntryView.jsp"
            validate="true" />
    </action-mappings>
</struts-config>

It contains all the mappings for the controller servlet. In other words, these are the names of the resources you can request from the web application. To understand these mappings, a discussion of Struts actions is in order.

A classic problem in software engineering is how to handle a large number of mutually exclusive choices gracefully. This same problem popped up above, when we were talking about how to create a single point of entry controller. The naïve approach creates a huge switch statement or a long cascading series of if…else statements. However, a design pattern called Command exists to solve this problem nicely. The command design pattern uses inheritance instead of decisions to handle the various possibilities. Typically, the developer creates an abstract class with a method called "execute()", which has no code. Then, all the different possible actions subclass the abstract class and add concrete code to the execute() method. The mapping then becomes the name of the class to instantiate. This pattern makes it easy to keep a list of all the commands (because they all have a common ancestor) and iterate through it. This is the pattern used by Struts to handle the request mappings.

In Struts, you never need to create a controller servlet – the Struts developers have already created it for you. Instead, you create subclasses of the Struts Action class (org.apache.struts.action.Action). These action subclasses map to a particular resource, and this is one of the items in the struts-config document. When a request comes into the controller servlet, it matches the name of the request (notice that the mapping name in the config file does not include the struts specific extension, in our case "Do) and instantiates an instance of the appropriate action class. This means that the controller servlet never has to change to add new actions to the web application. All that is required is the new action classes and an updated config file. Your action class takes on the role of the controller in the traditional Model2 architecture. In other words, it creates the bean (or beans) necessary to do work, calls methods on them, adds them to a collection, then forwards them to the appropriate display resource (typically a JSP). The following listing shows a typical small Action subclass.

Listing 7: A small Action subclass

package com.nealford.art.schedstruts.action;
import java.io.IOException;
import java.sql.SQLException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import com.nealford.art.schedstruts.boundary.ScheduleDb;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
public class ViewScheduleAction extends Action {
    private static final String ERR_POPULATE = "SQL error: can't populate dataset";
    public ActionForward execute(ActionMapping mapping,
                                 ActionForm form,
                                 HttpServletRequest request,
                                 HttpServletResponse response) throws
            IOException,
            ServletException {
        DataSource dataSource = getDataSource(request);
        ScheduleDb sb = new ScheduleDb();
        sb.setDataSource(dataSource);
        try {
            sb.populate();
        } catch (SQLException sqlx) {
            System.out.println(sqlx);
            getServlet().getServletContext().log(ERR_POPULATE, sqlx);
        }
        request.setAttribute("scheduleBean", sb);
        return mapping.findForward("success");
    }
}

As the listing shows, the first step is to create a model bean. In this application, the ScheduleBean encapsulates a connection to the database to get scheduling information. The Action creates an instance of the schedule bean, populates it, and passes it to the display JSP through the ActionForward class in Struts. This class is a convenience class created to make it easy to forward to other resources. Notice in the config document that the "sched" action is mapped to this action class. Thus, when the user requests "sched.do", the controller servlet instantiates the ViewScheduleAction class and calls its perform() method, which does the actual work. The JSP is shown here.

Listing 8: The View JSP

<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>

<html>
<head>
<title>
<bean:message key="title.view" />
</title></head>
<body>
<h2><bean:message key="prompt.listTitle" /></h2></p>
<table border="2">
<tr bgcolor="yellow">
<th><bean:message key="prompt.start" /></th>
<th><bean:message key="prompt.duration" /></th>
<th><bean:message key="prompt.text" /></th>
<th><bean:message key="prompt.eventType" /></th>
</tr>
<logic:iterate id="schedItem"
type="com.nealford.art.schedstruts.entity.ScheduleItem"
name="scheduleBean" property="list" >
<tr>
<td><bean:write name="schedItem" property="start" />
<td><bean:write name="schedItem"
 property="duration" />
 <td><bean:write name="schedItem" property="text" />
<td><bean:write name="schedItem"
property="eventType" />
</tr>
</logic:iterate>
</table>
<p>
<a href="schedEntry.do"> Add New Schedule Item</a>
</body>
</html>

The JSP pulls the scheduleBean from the request collection (where the action class placed it) and also creates another object of page scope. The main thrust of this page is to show the schedule items from the database in an HTML table. To that end, the scheduleBean has a method that returns a java.util.List of ScheduleItem objects, which encapsulate the details of a single record. It would be easy enough here to create scriptlet code in the JSP to iterate over that collection. However, one of the stated goals of using this architecture is to remove Java code from the display. This is easily facilitated via one of the custom tags created by Struts. The ScheduleView JSP uses the Struts iterate tag. This is a powerful custom tag that can iterate over any array or collection. The "id" specifies what the local name of the object pulled from the collection will be. The "type" specifies what class the object pulled from the collection will be cast as, and the "collection" is a JSP expression specifying the collection itself. Within the body of the tag, you can use the "id" field to access any of the items from the collection without worrying about type casting – the tag has already done that for you. To summarize, the iterate tag iterates over the collection, pulling each object out one at a time, casting it to the type specified and assigning it to the variable name specified in "id". The user can use this variable to call any of the methods of the objects in the collection. Obviously, this is meant to be used on homogeneous collections (or at least collections that contain objects with a common super class).

As you can see, Struts makes is quite easy to build well designed web applications. It also leverages several well-known design patterns (such as the Command pattern).