HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML

In the beginning there were servlets, and it was good. They were much better than the alternatives, and allowed for scalable, robust Web development. But 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 handcrafted 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 sinking feelings in the pit of their stomachs. Time to do it all over again. The problem? Too much HTML in the Java code.

JavaServer Pages
Then JSPs appeared. Here was the answer to all our prayers. JSPs 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. Unfortunately, all was still not well. Now the developers 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.

Model-View-Controller Pattern
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, here's the capsulated version: the model represents the business logic and data in the application and resides in JavaBeans and/or EJBs. 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 the view interacts with the model. In the Web world a servlet is the controller.

The typical scenario for the Web MVC is: the user accesses a controller servlet, and 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 EJBs without having to change the controller or presentation layers. This is clearly the best way to build Web applications. It's easy to maintain and update, and there's 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 pattern neatly modularizes the constituent parts of Web applications.

Problems in Paradise
Now what's wrong? The problem with the MVC Web applications (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're 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's needed is an application framework for Web development that handles most of these gory details. And that's where Struts comes in.

Struts
Struts is a Web application framework created as part of the Jakarta project at Apache. It handles 35-40% of the plumbing code required to create clean Model2 Web applications. This article provides an overview of the main parts of Struts. Because Struts is an open source project, you can download it from http://jakarta.apache.org/struts/ and start using it. For this article I cover the Struts setup, the controller servlet, form handling, custom tags, and internationalization, which only scrapes the top of the Struts iceberg.

Struts Setup
To set up Struts, you must download it 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're 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 Listing 1. The first entry registers the Struts ActionServlet, which is the single point of entry for the application. Basically the Struts developers have already written the only controller you'll need. More about exactly what it does in a moment.

Controller Servlet
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 preloading 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 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).

Extension mapping (which we're using here) maps all resources 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're 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 it's the one used by all the Struts examples so it's 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 have to list only the tag libraries you're 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 in Listing 2. 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 earlier when we were talking about how to create a single point-of-entry controller. The naive approach creates a huge switch statement or a long cascading series of if...else statements. However, a design pattern called Command solves this problem nicely.

The Command design pattern uses inheritance instead of decisions to handle the 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 doesn't 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's 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). Listing 3 shows a typical small Action subclass.

As shown in Listing 3, 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 that makes 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 in Listing 4. It 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, 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).

This is the basic operation of a simple case in Struts. However, Struts provides even more services for handling forms, validation, and internationalization.

Handling Forms One of the common chores in Web applications is the handling of forms. The JSP mechanism of automatically populating the fields of a JavaBean with form post parameters goes a long way in reducing the tedious code that would otherwise have to be written. Struts has built on top of this mechanism to provide an easy way to handle populating beans in wizard-style interfaces and handling validation.

When creating a Struts form you have the option of creating a Struts form bean. This is a regular JavaBean with typical accessors and mutators for the fields of the bean (and very little other code) that subclasses from the org.apache.struts.action.ActionForm base class. Note that these beans should not connect to a database to populate themselves or provide a lot of other infrastructure code. They should be lightweight classes that basically encapsulate entity information. Other beans should handle persistence and other duties. In fact, it's typical to have one bean that acts as a collection of the entities represented by a Struts form bean (like the ScheduleBean used in the Action class discussed earlier).

One of the additional sections in the struts-config XML document allows you to declare a class as a form bean. Look at the top of the sample struts-config document in Listing 2. A form-bean declaration allows you to specify a name for the form bean and map that to a specific class (in this case, schedule.ScheduleItem). In the action mapping section of the config file you can automatically associate a form bean with an action. Notice the "add" action in the config file. The additional "name" parameter allows you to specify a form bean to use with that action. Once you have a form bean associated with an action, Struts will perform the following services for you before invoking the action method:

  • Check the user's session for an instance of the bean under the name specified in the struts-config file. If one doesn't exist yet, Struts creates one and adds it to the user's session.
  • For every request parameter that matches one of the setXXX methods of the bean, the appropriate set method will be called.
  • The updated ActionForm bean is passed to the Action as a parameter.
Custom Tags
These services are similar to the standard JSP behavior of handling request parameters that map to JavaBean fields. However, Struts performs more services for you, and it also comes with a collection of custom JSP tags split into four categories. One of the categories allows you to replace standard HTML tags for input elements with "smarter" Struts tags. If you use the Struts HTML tags, they'll also automatically populate the input fields on the form from the ActionForm whenever the page is visited. This makes it easy to handle wizard-style interfaces.

Notice that an ActionForm bean doesn't have to correspond to a single page. More typically, it corresponds to a single set of user information, so you can have an ActionForm that spans multiple pages. Using the Struts HTML tags, the input fields the user has already filled in will be automatically populated as the user moves back and forth between the pages of the wizard. Listing 5 provides an example of a JSP that uses the custom tags.

Listing 5 shows some other features of Struts tags as well. One of the automatic features of Struts is form validation. The struts-config file allows you to flag an action associated with a form bean to enable validations. This assumes that you've added a validation method to your form bean class. A sample validation method is shown in Listing 6. The validate() method is automatically called by the Action object after the population of the form bean fields, but before any of the code in the Action is performed. If the validate() method returns either null or an empty ActionErrors collection, processing of the Action continues normally. If errors have been returned from validate(), Struts will automatically return the user to the input form, repopulate the fields from the form bean, and print out a list of the reported errors at the top of the page (see Figure 1).

Figure 1
Figure  1:

Figure 1 is a Struts form in which the user has put in a negative duration and left the text field blank. The errors listed at the top are automatically generated via the validate() method and the <html:errors/> tag at the top of the file (the position of this tag determines where the errors will appear on the page).

You'll also notice that the error messages returned by the validate method aren't the messages that appear on the page. The strings added in the ActionError constructor map to messages in a java.util.Properties file. This properties file is automatically referenced by Struts (this is one of the parameters of the ActionServlet), and allows for easy separation of the messages from the code in the application. This means that the text of the message can be easily changed without recompiling the application. This is also how Struts handles internationalization.

Struts can be set up to look for a properties file that matches the locale encoding of the request to automatically provide text messages in the appropriate language. This is another service provided by the HTML tags. Notice in Listing 5 that the input text references fields in the properties file. Struts includes custom tags for the four categories shown in Table 1.

Table 1

Custom tags make it much easier to use Struts. In the examples above, I referenced some of the logic and HTML tags. However, because these are Struts extensions, most HTML editors and development tools don't directly support them. This is changing. Dreamweaver now has support for custom tags in general and Struts custom HTML tags specifically. You can download a plug-in that allows Dreamweaver to directly manipulate HTML elements as Struts HTML tags. This allows your HTML developers (whether they're Macintosh users or not) to easily support this application framework.

Conclusion
So, are we in Nirvana yet? Not completely, but we're getting closer all the time. Struts isn't a huge, restrictive framework. I'd estimate that it handles about 35-40% of the common drudgework of creating a Web application. On the plus side, because it doesn't try to do absolutely everything in one framework, it's very flexible. Now that tools are starting to support it, it's easier than ever to create sophisticated applications in which the presentation layer is nicely partitioned from the working part of the application.

Struts is nice because it enforces the Model2 architecture by making it hard not to follow the correct design. Applications written this way take a little longer to create up front, but the maintenance is a comparative breeze. Once developers get used to designing Web applications correctly and see the return on investment, they're happy to do things the right way.

Author Bio
Neal Ford is the vice president of technology at the DSW Group. He's also a designer and developer of applications, instructional materials, magazine articles, video presentations, and the author of Developing with Delphi: Object-Oriented Techniques and JBuilder 3 Unleashed. nford@thedswgroup.com

	



Listing 1: The additions to WEB.XML required to use 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>


Listing 2: 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>

Listing 3: A small Action subclass

package schedule;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.*;

public class ViewScheduleAction extends Action {

 public ViewScheduleAction() {

 }

 public ActionForward perform(
<x-tab></x-tab> ActionMapping mapping, ActionForm form,
<x-tab></x-tab> HttpServletRequest request,
<x-tab></x-tab> HttpServletResponse response)
 <x-tab></x-tab>throws 
  java.io.IOException,
 <x-tab></x-tab>javax.servlet.ServletException 
  {
 ScheduleBean 
  sb = new ScheduleBean();
 sb.populate();
 request.setAttribute("scheduleBean", 
  sb);
 return 
  new ActionForward("/ScheduleView.jsp");
 }
}


Listing 4: The View JSP for the schedule application
<p> <HTML>
  <HEAD>
  <%@ taglib uri="/WEB-INF/struts-logic.tld"
  prefix="logic" %>
<p> <jsp:useBean id="scheduleBean" scope="request"
  class="schedule.ScheduleBean"/>
  <jsp:useBean id="scheduleItem" scope="page"
  class="schedule.ScheduleItem"/>
  <TITLE>
  Schedule View
  </TITLE>
  </HEAD>
  <BODY>
  <H1>
  Schedule
  </H1>
<p> <table border="2">
  <tr bgcolor="green">
  <th>Start 
  Date</th>
  <th>Duration</th>
  <th>Text</th>
  <th>Event 
  Type</th>
  </tr>
<p> <logic:iterate id="schedItem"
  type="schedule.ScheduleItem"
  collection="<%= scheduleBean.getList()%>">
  <tr>
  <td><%= 
  schedItem.getStart() %></td>
  <td><%= 
  schedItem.getDuration() %></td>
  <td><%= 
  schedItem.getText() %></td>
  <td><%= 
  schedItem.getEventType() %></td>
  </tr>
  </logic:iterate>
  </table>
  <p>
<p>

<a href="schedEntry.do"> Add New Schedule Item</a><p>

</BODY>
</HTML>


Listing 5: An HTML form using the Struts HTML custom tags.
<p>
<%@ taglib uri="/WEB-INF/struts-html.tld"
 prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld"
 prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts.tld"
 prefix="struts" %>
<jsp:useBean id="scheduleItem" scope="request"
 class="schedule.ScheduleItem" />
<jsp:useBean id="scheduleBean" scope="page"
 class="schedule.ScheduleBean" />
<% pageContext.setAttribute("eventTypes",
<x-tab></x-tab>scheduleBean.getEventTypes()); %>
<p> <HEAD>
  <TITLE>
  ScheduleEntryView
  </TITLE>
  </HEAD>
  <BODY>
  <H1>
  Add Schedule Item
  </H1>
  <hr>
  <html:errors/>
  <html:form action="add.do"> 
  <table border="0" width="30%" align="left">
  <tr>
  <th align="right">
  <struts:message 
  key="prompt.duration"/>
  </th>
  <td align="left">
  <html:text 
  property="duration"
  size="16"/>
  </td>
  </tr>
  <tr>
  <th align="right">
  <struts:message 
  key="prompt.eventType"/>
  </th>
  <td align="left">
  <html:select 
  property="eventType">
  <html:options 
  collection="eventTypes"
  property="value"
  labelProperty="label"/>
  </html:select>
<p> </td>
  </tr>
  <tr>
  <th align="right">
  <struts:message 
  key="prompt.start"/>
  </th>
  <td align="left">
  <html:text 
  property="start"
  size="16"/>
  </td>
  </tr>
  <tr>
  <th align="right">
  <struts:message 
  key="prompt.text"/>
  </th>
  <td align="left">
  <html:text 
  property="text"
  size="16"/>
  </td>
  </tr>
<p> <tr>
  <td align="right">
  <struts:submit>
  <bean:message 
  key="button.submit"/>
  </struts:submit>
  </td>
  <td align="right">
  <html:reset>
  <bean:message 
  key="button.reset"/>
  </html:reset>
  </td>
  </tr>
  </table>
  </html:form>
<p>

</BODY>
</HTML>

Listing 6: The validation method for the ScheduleItem form bean


public ActionErrors validate(
 ActionMapping actionMapping,
 HttpServletRequest request) {
 ActionErrors ae = new ActionErrors();
 if (duration < 0 || duration > 31) {
 ae.add("duration", new ActionError(
 "error.invalid.duration", "8"));
 }
 if (text == null || text.length() < 1) {
 ae.add("event text",
 new ActionError("error.no.text"));
 }
 return ae;
}

  
 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: info@sys-con.com

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.