Chapter 1. Getting Started: Enabling Struts Development

Introduction

The popularity of the Struts framework has grown tremendously in the last year. Since the release of Struts 1.1 in June 2003, Struts has become one of the most popular frameworks for developing complex JSP-based web applications. Traffic on the Strut’s user’s mailing list is heavy, typically 70 messages a day.

Despite its popularity, figuring out how to get started developing with Struts is still a common topic of conversation. The recipes in this first chapter should help you jump-start your development with Struts.

Good commercial tools have been built around Struts, the primary focus here is on tools and frameworks that are open source and free (as in “free beer”). By focusing on freely available tools, developers should be more encouraged to experiment with these tools than if they had to pay to use them.

1.1. Downloading Struts

Problem

You want to start developing applications using Struts.

Solution

Download the Struts binary and source distributions from http://struts.apache.org/acquiring.html and deploy the example applications to your web container.

Discussion

A common question among developers new to Struts is which release to use. Without question, any new Struts project should use the latest best available release. At the time this was written, Struts 1.2.4 (referred to in this book as Struts 1.2) had just gone GA (General Availability).

Tip

For developers looking to gain experience with the latest and greatest Struts features, the Struts Nightly Build will provide a peek into Struts 1.3.

You will find it useful to have the binary and source distributions available. The binary distribution includes the Struts JARs and associated dependent JARs. The source distribution, on the other hand, contains the source code for the Struts framework itself, as well as the Java source for the Struts tag libraries. Just as important, the source distribution contains the Java source, deployment descriptors, HTML pages, and JSP pages for all of the supplied sample applications.

Tip

The exact content of these distributions varies depending on whether you are getting the latest Release Build or the Struts Nightly Build.

The Struts 1.2 Release Build binary distribution includes a basic README file, an installation document, and release notes. The lib folder contains the Struts JAR file, as well as the dependent JAR files from the Jakarta Commons project. The contrib folder contains contributions to the Struts distribution that aren’t considered part of the Struts core. In particular, this folder contains the JAR files and sample web applications for Struts-EL. Struts-EL allows you to use JSTL (JSP Standard Tag Library) style expression language in the Struts tags. It includes, by necessity, the JSTL tag libraries.

The Struts 1.2 Release Build source distribution contains the source code for the Struts framework, and the source and web resources for the sample applications. In addition, the contrib folder contains the source code for contributions made by the Struts community that are not, but may later become, part of the Struts core. Some contributions of particular interest include the following:

Struts-EL

JSTL Expression language support for Struts tags

Struts-Faces

A framework layer that supports integration between Struts application and Java Server Faces technology.

Scaffold

A set of base and helper classes that assist in integrating your data layer (Model) with Struts.

Once you have downloaded the source and binary distributions, you will want to deploy the example applications included with Struts, as described in Recipe 1.2.

See Also

Recipe 1.2 describes how to deploy the Struts example applications included in the Struts distribution.

If you are completely new to Struts, you will want to first check out Programming Jakarta Struts by Chuck Cavaness (O’Reilly). Additional resources can be directly from the Struts home page at http://struts.apache.org.

1.2. Deploying the Struts Example Application

Problem

You want to deploy the Struts MailReader example application to Tomcat.

Solution

If you don’t already have the Tomcat running on your box, you can download it from http://jakarta.apache.org/tomcat. This recipe assumes that you are using Tomcat 5. Set environment variables for Struts and Tomcat, copy the Struts example WAR file to Tomcat, and start Tomcat.

Warning

If you are using Struts 1.2, the WAR file for the Struts example application has been changed from struts-example.war to struts-mailreader.war.

The commands for a Windows machine are shown here:

C:>set STRUTS_HOME=c:jakarta-struts-1.1

C:>set CATALINA_HOME=c:	omcat5

C:>copy %STRUTS_HOME%webappsstruts-example.war %CATALINA_HOME%webapps
        1 file(s) copied.

C:>%CATALINA_HOME%instartup
Using CATALINA_BASE:   c:	omcat5
               Using CATALINA_HOME:   c:	omcat5
               Using CATALINA_TMPDIR: c:	omcat5	emp
               Using JAVA_HOME:       c:j2sdk1.4.2

The last command shown, %CATALINA_HOME%instartup, starts Tomcat. On Windows, you will see Tomcat startup in a separate terminal window. The output in this terminal window displays information about the applications deployed and the state of Tomcat:

               Jun 22, 2004 12:23:34 AM org.apache.catalina.core.StandardHostDeployer install
               INFO: Installing web application at context path /struts-example from URL file:c
               :/tomcat5/webapps/struts-example
Jun 22, 2004 12:23:38 AM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='org.apache.struts.util.LocalStrings', returnNull=tru
e
Jun 22, 2004 12:23:38 AM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='org.apache.struts.action.ActionResources', returnNul
l=true
Jun 22, 2004 12:23:40 AM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='org.apache.struts.webapp.example.AlternateApplicatio
nResources', returnNull=true
Jun 22, 2004 12:23:40 AM org.apache.struts.util.PropertyMessageResources <init>
INFO: Initializing, config='org.apache.struts.webapp.example.ApplicationResource
s', returnNull=true
Jun 22, 2004 12:23:40 AM org.apache.struts.webapp.example.memory.MemoryDatabaseP
lugIn init
INFO: Initializing memory database plug in from '/WEB-INF/database.xml'
Jun 22, 2004 12:23:40 AM org.apache.struts.validator.ValidatorPlugIn initResourc
es
INFO: Loading validation rules file from '/WEB-INF/validator-rules.xml'
Jun 22, 2004 12:23:41 AM org.apache.struts.validator.ValidatorPlugIn initResourc
es
INFO: Loading validation rules file from '/WEB-INF/validation.xml'
...
Jun 22, 2004 12:23:44 AM org.apache.coyote.http11.Http11Protocol start
INFO: Starting Coyote HTTP/1.1 on port 80
Jun 22, 2004 12:23:45 AM org.apache.jk.common.ChannelSocket init
INFO: JK2: ajp13 listening on /0.0.0.0:8009
Jun 22, 2004 12:23:45 AM org.apache.jk.server.JkMain start
INFO: Jk running ID=0 time=20/50  config=c:	omcat5confjk2.properties
Jun 22, 2004 12:23:45 AM org.apache.catalina.startup.Catalina start
               INFO: Server startup in 49852 ms

You can use this output to verify that the application deployed and that Tomcat successfully started and is running. In the output shown above, you can see that Tomcat deployed the struts-example.war file. In addition, the last line indicates that Tomcat is running and the length of time it took to start up.

On Unix/Linux, you would use similar commands:

$ export STRUTS_HOME=/usr/local/jakarta-struts-1.1

$ export CATALINA_HOME=/usr/local/tomcat5

$ cp $STRUTS_HOME/webapps/struts-example.war $CATALINA_HOME/webapps

$ $CATALINA_HOME/bin/startup.sh
Using CATALINA_BASE:   /usr/local/tomcat5
               Using CATALINA_HOME:   /usr/local/tomcat5
               Using CATALINA_TMPDIR: /usr/local/tomcat5/temp
               Using JAVA_HOME:       /usr/local/j2sdk1.4.2

Tomcat starts up as a background process. You can monitor the output from Tomcat using the following:

$ tail -f $CATALINA_HOME/logs/catalina.out

Other than the different operating system file paths, the output will be identical to the output on Windows shown previously.

Navigate your browser to http://localhost:8080/struts-example. You should see the page shown in Figure 1-1.

Struts example application
Figure 1-1. Struts example application

Discussion

Using and examining the struts-example web application is an excellent learning aid for Struts. Before you write your first Struts application, you should understand how the struts-example application works. The best way to do this is to deploy the application. Experiment with the interface and take the walking tour. You will want to follow along in the walking tour by using your text editor or IDE to view the source code.

Tip

You will need to download the Struts binary and source distributions to deploy the struts-example. The WAR files are included in the binary distribution. The source code is supplied in the source distribution.

In addition to the struts-example application, additional web applications demonstrate other Struts features as shown in Table 1-1.

Table 1-1. Struts 1.1 example applications

WAR file to deploy

Description

struts-blank.war

A boilerplate Struts application.

struts-documentation.war

Struts User’s Guide and tag library reference documents.

struts-example.war

The seminal Struts Mail Reader example. Demonstrates most of the basic core features and functions provided by Struts.

struts-exercise-taglib.war

An application that exercises the functionality of the Struts tag libraries.

struts-upload.war

Shows how to use Struts support for file uploads.

struts-validator.war

Demonstrates the use of the Validator with Struts.

tiles-documentation.war

Includes Tiles documentation, sample layouts and tutorials.

Struts 1.2 reorganized the example applications. Table 1-2 lists the web applications contained in the Struts 1.2 /webapps directory.

Table 1-2. Struts 1.2 example applications

WAR file to deploy

Description

struts-blank.war

A boilerplate Struts application.

struts-documentation.war

Struts User’s Guide and tag library reference documents.

struts-examples.war

Replaces the struts-exercise-taglib.war, struts-upload.war, and struts-validator.war. Combines the tag library, upload, and Validator examples into one application.

struts-mailreader.war

The seminal Struts Mail Reader example. Demonstrates most of the basic core features and functions provided by Struts.

tiles-documentation.war

Includes Tiles documentation, sample layouts and tutorials.

See Also

Recipe 1.1 discusses how to download Struts and the details different distributions that are available.

1.3. Migrating from Struts 1.0 to Struts 1.1

Problem

You need to migrate a Struts 1.0-based application to Struts 1.1.

Solution

Replace the Struts 1.0 JAR files, tag library descriptor (TLD) files, and XML DTD files with the corresponding files from Struts 1.1. If you have JSP pages that use the absolute URI from the Struts tag libraries, you’ll need to change these. Recompile your application using the new libraries and address any compilation errors.

Finally, you’ll want to modify your code that is using deprecated APIs to use the new Struts 1.1 APIs.

Discussion

While Struts 1.1 was a significant change to Struts 1.0, functionally speaking, applications based on Struts 1.0 can be migrated without much difficulty by replacing the Struts 1.0 JARs and TLDs with the corresponding files for Struts 1.1. You will need to change your use of the tag library URIs, as they have changed in Struts 1.1; this generally means changing your web.xml deployment descriptor. If you use the absolute URIs in your JSP pages, these values will need to be changed as well. Table 1-3 shows the changes to the tab library URIs.

Table 1-3. Struts tag library URIs

Struts 1.0.2 Taglib URI

Struts 1.1 Taglib URI

http://jakarta.apache.org/struts/tags-bean-1.0.2

http://jakarta.apache.org/struts/tags-bean

http://jakarta.apache.org/struts/tags-html-1.0.2

http://jakarta.apache.org/struts/tags-html

http://jakarta.apache.org/struts/tags-logic-1.0.2

http://jakarta.apache.org/struts/tags-logic

http://jakarta.apache.org/struts/tags-template-1.0.2

http://jakarta.apache.org/struts/tags-template

Not Available with Struts 1.0.2

http://jakarta.apache.org/struts/tags-tiles

Not Available with Struts 1.0.2

http://jakarta.apache.org/struts/tags-nested

The most significant changes in Struts 1.1 were the Struts ActionServlet (org.apache.action.ActionServlet) and the Struts Action class (org.apache.struts.Action). Struts 1.1 introduced the concept of the RequestProcessor (org.apache.struts.action.RequestProcessor) as well. The ActionServlet delegates request handling to the request processor. With Struts 1.1, you no longer have to extend the ActionServlet for customization; instead, you subclass the RequestProcessor. If a Struts 1.0-based application did not extend the ActionServlet, then no changes are required to use the RequestProcessor. If ActionServlet was subclassed, you should extend the RequestProcessor instead.

The other primary enhancement, as mentioned, is in the Struts Action. Struts 1.1 introduced a new method, execute( ), that subclasses should implement instead of the perform() method. Example 1-1 shows a simple Action that implements the perform() method.

Example 1-1. Struts 1.0 Action
package org.apache.struts.webapp.example;

import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.struts.action.*;

public final class ExampleAction extends Action {
    public ActionForward perform(ActionMapping mapping,
                 ActionForm form,
                 HttpServletRequest request,
                 HttpServletResponse response)
            throws IOException, ServletException {

        try {
            ExampleService service = new ExampleService( );
            Service.doService( );
        }
        catch (ServiceException ex) {
            throw new ServletException( ex );
        }
        return (mapping.findForward("success"));
    }
}

Example 1-2 is the same Action using Struts 1.1.

Example 1-2. Struts 1.1 Action
package org.apache.struts.webapp.example;

import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.struts.action.*;

public final class ExampleAction extends Action {
    public ActionForward execute (ActionMapping mapping,
                 ActionForm form,
                 HttpServletRequest request,
                 HttpServletResponse response)
            throws Exception {

            ExampleService service = new ExampleService( );
            Service.doService( );

            return (mapping.findForward("success"));
    }
}

As you can see, with the Struts 1.1-based Action, the exception handling no longer needs to be performed in the method. Struts 1.1 now supports exception handling as part of the framework as will be shown in Recipe 9.1.

You aren’t required to change your Actions to use the execute( ) method, as Struts 1.1 still supports the perform( ) method; however, the method is deprecated.

Warning

If you are migrating directly from Struts 1.0 to Struts 1.2, Struts 1.1 deprecations, such as the perform( ) method, have been formally removed from the Struts 1.2 API.

Though it will continue to function as is, I recommend convert your code to use the execute( ) method as soon as you can. Doing so will reduce the work to convert to Struts 1.2. More significantly, it will allow to you take advantage of the Struts 1.1 exception-handling capability.

See Also

Recipe 9.1 details exception processing with Struts 1.1.

1.4. Upgrading from Struts 1.1 to Struts 1.2

Problem

You want to upgrade an application based on Struts 1.1 to Struts 1.2.

Solution

  1. Download the Struts 1.2 binary distribution from http://struts.apache.org/acquiring.html.

  2. Copy the JAR files and Tag Library Descriptor (TLD) files from the Struts lib folder to your application’s WEB-INF/lib folder.

  3. If you use absolute URIs for the taglib directives in your JSP pages, change these to use the new URIs shown in Table 1-4.

Table 1-4. Struts 1.1 and 1.2 Taglib URIs

Struts 1.1 Taglib URI

Struts 1.2.4 Taglib URI

http://jakarta.apache.org/struts/tags-bean

http://struts.apache.org/tags-bean

http://jakarta.apache.org/struts/tags-html

http://struts.apache.org/tags-html

http://jakarta.apache.org/struts/tags-logic

http://struts.apache.org/tags-logic

http://jakarta.apache.org/struts/tags-template

http://struts.apache.org/tags-template

http://jakarta.apache.org/struts/tags-tiles

http://struts.apache.org/tags-tiles

http://jakarta.apache.org/struts/tags-nested

http://struts.apache.org/tags-nested

  1. Change the DOCTYPE declaration at the beginning of your validation.xml file(s) to the following:

    <!DOCTYPE form-validation PUBLIC 
    "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 
    1.1.3//EN" "http://jakarta.apache.org/commons/dtds/validator_1_1_3.dtd">
  2. Change the DOCTYPE declaration at the beginning of your struts-config.xml file(s) to the following:

    <!DOCTYPE struts-config PUBLIC 
    "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" 
    "http://struts.apache.org/dtds/struts-config_1_2.dtd">
  3. Replace your use of the ActionError class with the ActionMessage class.

  4. Replace your use of the ActionErrors class with the ActionMessages class except within the validate( ) method of any custom ActionForms.

  5. Remove reliance on any init-param elements on the ActionServlet other than the config parameters. These parameters were deprecated in Struts 1.1 and are no longer supported in Struts 1.2. Instead, move these parameter values to your struts-config.xml file. Most of these parameters are replaced by attributes of the controller element.

  6. Remove reliance on the name, scope, and type attributes of the html:form tag. These attributes were deprecated in Struts 1.1 and are no longer supported in Struts 1.2.

Discussion

The formal goal of Struts 1.2 was to remove deprecated methods and complete support for modules. Though Struts 1.2 doesn’t make sweeping changes to the Struts core as Struts 1.1 did, it includes new features and enhancements that are worth the effort of upgrading. Many of these features are discussed throughout this book. Here are some of the most significant enhancements:

  • New validwhen Validator rule for complex cross-field validations (Recipe 8.4)

  • Wildcard action mappings that allow you to reuse action elements for multiple related URLs (Recipe 7.8)

  • New prebuilt actions including a new MappingDispatchAction class and a locale-switching LocaleAction (Recipes Section 6.10 and Section 12.4)

For a new application, you should use Struts 1.2. If you have an existing Struts 1.1 application, you will find that Struts 1.2 introduces a number of new and useful features. In comparison to migrating from Struts 1.0 to Struts 1.1, upgrading to Struts 1.2 is less intrusive and requires less code changes.

See Also

The Struts wiki has additional details on this upgrade. The relevant wiki page can be found at http://wiki.apache.org/struts/StrutsUpgradeNotes11to124.

1.5. Converting JSP Applications to Struts

Problem

You want to convert an existing JSP-based web application to a Struts application.

Solution

Take a refactoring-style approach by applying Struts as you add new features to your application. As you increase your Struts knowledge, you can re-architect the existing code to use Struts. If no new development is planned for the application, refactor the existing JSPs a page at a time.

Discussion

The level of difficulty to migrate an existing JSP application depends greatly on the complexity and architectural soundness of the application. If the application uses a Model 1 architecture—that is, all logic is contained in the JSP page—it could be quite a challenge. You may find that you need to redesign the application from scratch to separate out the business logic from the presentation before you begin.

If you are new to Struts, then learn Struts on development of a new application instead of attempting to retrofit Struts to an application well into development. Struts is not a technology that can easily be “bolted on” late in the development process. However, some projects have altered their architectural underpinnings in midstream. If you are in this situation, steer the project plan so Struts is applied to new development first and preferably to features not on the critical path. Once you increase your Struts knowledge, it will be easier to convert existing code.

To make this more concrete, consider a simple example consisting of three JSP pages. The first page is the main welcome page that displays a link to the second page. The second page displays two form input fields. When the user submits the form, the result of adding the values is displayed on the third JSP page.

First, Example 1-3 shows the index.jsp page that provides a link to the input page.

Example 1-3. Linking to an input page
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
   <head>
      <title>Simple Calculator</title>
   </head>
   <body>
      <a href="get_input.jsp">Calculator</a>
   </body>
</html>

Example 1-4 shows the get_input.jsp page that submits the entered values.

Example 1-4. Submitting data
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
   <head>
      <title>Add Two Numbers</title>
   </head>
   <body>
      <form action="display_result.jsp">
         Value 1: <input name="value1" type="text"/><br/>
         Value 2: <input name="value2" type="text"/>
         <p>
            <input type="submit"/>
         </p>
      </form>
   </body>
</html>

The page in Example 1-4 submits the form to a JSP page for displaying results. This target page (shown in Example 1-6) utilizes a JavaBean to hold the values received in the request from the form. Before looking at the target JSP, Example 1-5 shows the code for the JavaBean that will store this data.

Example 1-5. Simple JavaBean
package com.oreilly.strutsckbk;

public class ValueHolder {
    private int value1;
    private int value2;

    public int getValue1( ) {
        return value1;
    }

    public void setValue1(int value1) {
        this.value1 = value1;
    }

    public int getValue2( ) {
        return value2;
    }
   
    public void setValue2(int value2) {
        this.value2 = value2;
    }
}

The display_result.jsp page, shown in Example 1-6, uses the JSP setProperty tag to populate the bean with the request values. The values are outputted using request-time expressions, and the sum is calculated using a scriptlet. Finally, this calculated sum is displayed.

Example 1-6. Displaying results
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
   <head>
      <title>Add Two Numbers</title>
   </head>
   <body>
      <jsp:useBean 
            id="valueHolder"
            class="com.oreilly.strutsckbk.ValueHolder">
         <jsp:setProperty name="valueHolder" property="*"/>
      </jsp:useBean>
      The sum of <%= valueHolder.getValue1( ) %> plus
      <%= valueHolder.getValue2( ) %> is:<p>
      <% int sum = valueHolder.getValue1( ) + valueHolder.getValue2( ); %>
      <%= sum %>.<p>
      <a href="get_input.jsp">Perform another calculation</a>      
   </body>
</html>

This application demonstrates some of the more undesirable approaches to using JSP. While the use of the JavaBean is laudable, the calculation is performed within the JSP using scriptlet. This results in a mixing of business logic with presentation, and scriptlets (embedded Java code) can lead to maintainability problems for JSP applications.

Formulating a strategy for converting this application to Struts is simple. Though this example may be trivial, the strategy followed will be applicable to more complex applications. Remove as much embedded Java code as possible from the JSP pages. Start with the scriptlet (<% %>); if possible, remove the request-time expressions (<%= %>). To accomplish this with the JSP, in Example 1-6, move the sum calculation into the JavaBean (from Example 1-5) by adding a method that calculates and returns the sum:

public int getSum(  ) {
   return value1 + value2;
}

Now change the JSP to use the bean:write tag:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
   <head>
      <title>Add Two Numbers</title>
   </head>
   <body>
      <jsp:useBean 
            id="valueHolder"
            class="com.oreilly.strutsckbk.ValueHolder">
         <jsp:setProperty name="valueHolder" property="*"/>
      </jsp:useBean>
      The sum of <bean:write name="valueHolder" property="value1"/> plus
      <bean:write name="valueHolder" property="value2"/> is:<p>
      <bean:write name="valueHolder" property="sum"/>.<p>
      <a href="get_input.jsp">Perform another calculation</a>      
   </body>
</html>

This is a significant improvement over the earlier version of this mini-application.

Tip

Before continuing with more changes, now would be a good time to rebuild and redeploy the application to ensure it still works.

The next step, integrating the Struts controller into the mix, requires more work. First, create a Struts Action, like the one in Example 1-7, that is responsible for receiving the values from the form on the get_input.jsp page. The values are retrieved as request parameters which the Action marshals into a JavaBean. The bean instance is set as a servlet request attribute. The Action then forwards to the display_result.jsp page.

Example 1-7. Adding a Struts action
package com.oreilly.strutsckbk;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

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 final class ProcessInputAction extends Action {
    public ActionForward execute(ActionMapping mapping,
                 ActionForm form,
                 HttpServletRequest request,
                 HttpServletResponse response)
            throws Exception {
        ValueHolder bean = new ValueHolder( );
        bean.setValue1( Integer.parseInt(
            request.getParameter("value1")) );
        bean.setValue2( Integer.parseInt(
            request.getParameter("value2")) );
        request.setAttribute( "valueHolder", bean );
        return (mapping.findForward("success"));
    }
}

Create an ActionMapping in your struts-config.xml file for the Action:

<!-- Process input data -->
<action    path="/processInput"
           type="com.oreilly.strutsckbk.ProcessInputAction">
  <forward name="success" path="/display_results.jsp"/>
</action>

To use this Action, change the target action for the form on the get_input.jsp page:

<form action="ProcessInput.do">
  Value 1: <input name="value1" type="text"/><br/>
  Value 2: <input name="value2" type="text"/>
  <p>
    <input type="submit"/>
  </p>
</form>

Once you have developed a new feature like this, linking to the new action from existing JSP pages is simple using the specified ActionServlet mapping (e.g., *.do). You can link back from the Struts-based pages to your existing JSPs using the JSPs’ URLs.

See Also

Recipe 6.1 describes how to create a base action for additional common behavior to all of your actions. Recipe 9-1 details how to leverage the declarative exception handling of Struts. Recipe 5.6 discusses ways of integrating existing JavaBeans with Struts action forms.

1.6. Managing Struts Configuration Files

Problem

You want to avoid typographical errors in the various Struts configuration files and make editing and maintenance of these files easier.

Solution

Use a tool like Struts Console for viewing and editing the Struts configuration files.

Discussion

One of the most common causes of errors in web applications is typographical mistakes. In general, most modern software applications—Struts included—rely on configuration files and deployment descriptors in some form of ASCII text, typically XML. Using a validating XML editor can help alleviate mistakes; however, it does not eliminate the mistyped path, form-bean name, or class name.

The Struts Console, available for download at http://www.jamesholmes.com, provides a graphical editor for the Struts (struts-config.xml), Validator (validation.xml and validator-rules.xml), and Tiles (tiles-defs.xml) configuration files. Instead of hand-editing these files, you use the Swing-based editor provided by Struts Console. In addition to reducing the typos, Struts Console gives you a birds-eye view of these files for easier browsing. You will find this feature invaluable when your configuration files start to get large.

Struts Console provides “smart” graphical editors for the XML-based configuration files used in Struts development. Figure 1-2 is the view of the struts-config.xml file for the struts-example application.

Struts Console viewing the Struts sample application
Figure 1-2. Struts Console viewing the Struts sample application

The Editor tab presents two panes. The left pane contains a tree view of the elements that make up the configuration file. The right pane contains a graphical editor window. Different editors are shown depending on the element selected in the tree view. In Figure 1-2, the /saveSubscription action mapping is selected in the tree view and the Action editor displays all the details.

In a read-only window, the Source tab displays the XML source of the configuration file. As you make changes using the graphical editors, the source is updated as well. However, changes are not committed to disk until you save the file.

Warning

Because the Struts Console parses and manages actual XML data, when the file is resaved, it won’t contain any custom formatting or comments in the original file.

One aspect that sets Struts Console apart from a conventional XML editor is that the Struts console understands how the elements interrelate. For example, in Figure 1-2, a drop-down list of the available Form Beans can be displayed. Clicking the View button to the right of the Form Bean drop-down will display the selected Forms definition. The Form Bean editor will be displayed and the selected element in the left pane tree view will be updated to reflect the form bean being viewed.

As of this writing, Struts Console Version 4.4.1 supported creation and editing of the following files:

  • Struts Configuration (for Struts 1.2, 1.1, and 1.0); e.g., struts-config.xml

  • Tiles Configuration; e.g., tiles-defs.xml

  • Validator Configuration (for Struts 1.1, and 1.0); e.g., validator-rules.xml, validation.xml

  • JSP Tag Library Descriptor (TLD) (JSP Version 1.2, 1.1); e.g., struts-html.tld

In addition to its use as a standalone application, Struts Console can be plugged into most Java IDEs such as Eclipse, NetBeans, and IntelliJ IDEA.

The best recommendation before using any graphical editing tool is to become familiar with the structure and meaning of the elements with the Struts configuration files. Only then do you want to rely on using a tool that hides this structure from you. It’s similar to when you learn a new programming language—before you start relying on an IDE to do the heavy lifting, you must learn how to use the language using simple text editors and the command line. Though graphical tools are useful and will save you debugging effort in the long run, you need to understand the basics of these files so you can diagnose configuration issues when they occur.

See Also

James Holmes’s web site is where you get the Struts Console. His excellent site provides additional detail on the Struts Console. James is working on other tools, so visit him at http://www.jamesholmes.com.

Struts 1.2 includes a plug-in, the ModuleConfigVerifier, that is designed to catch errors in your Struts configuration file when your application initializes. For details on this plug-in, see http://struts.apache.org/api/org/apache/struts/plugins/ModuleConfigVerifier.html.

MyEclipse is a customized version of the Eclipse IDE. This excellent IDE provides an environment tailored to developing J2EE applications. At the time of this writing, the latest MyEclipse version was based on Eclipse 3.0, so it is quite up to date. MyEclipse is not free; however, the annual subscriptions are under $50 and well worth it if you are looking for a one-stop-shop IDE. Check it out at http://www.myeclipse.com.

Struts Studio is an ambitious undertaking bringing a commercial-grade graphical development to Struts developers. It’s located at http://www.exadel.com/products_strutsstudio.htm.

1.7. Using Ant to Build and Deploy

Problem

You want to be able to build and deploy your Struts web application in a repeatable and portable manner.

Solution

Create an Ant (http://ant.apache.org) build script and use Ant (or your IDE’s Ant integration) to compile, test, package, and deploy your application. Example 1-8 is a boilerplate Ant build file that can compile, build, and deploy a Struts application.

Example 1-8. Boilerplate Ant build file
<project name="jsc-ch01-r02" default="dist" basedir=".">
  <description>
      Jakarta Struts Cookbook - Ant Template
  </description>

  <!-- Enable access to environment variables -->
  <property environment="env"/>

  <!-- Set to use JDK 1.4 -->
  <property name="build.compiler" value="javac1.4"/>

  <!-- set global properties for this build -->
  <property name="src.dir" location="src"/>
  <property name="build.dir" location="build"/>
  <property name="dist.dir"  location="dist"/>
  <property name="server.dir" location="${env.CATALINA_HOME}"/>
  <property name="servlet.jar" 
     location="${server.dir}/common/lib/servlet-api.jar"/>
  <property name="jsp.jar" location="${server.dir}/common/lib/jsp-api.jar"/>
  <property name="struts.dist.dir" location="c:/jakarta-struts-1.1/lib"/>

  <!-- Struts -->
  <fileset id="struts.lib.files" dir="${struts.dist.dir}">
       <include name="**/*.jar"/>
  </fileset>
  <path id="struts.classpath">
       <fileset refid="struts.lib.files"/>
  </path>

  <path id="project.class.path">
    <pathelement location="${servlet.jar}"/>
    <pathelement location="${jsp.jar}"/>
    <path refid="struts.classpath"/>
  </path>

  <!-- Deployment Properties -->
  <property name="deploy.dir" location="${server.dir}/webapps"/>

  <target name="clean"
        description="clean up" >
    <!-- Delete the ${build.dir} and ${dist.dir} directory trees -->
    <delete dir="${build.dir}"/>
    <delete dir="${dist.dir}"/>
  </target>

  <target name="init">
    <!-- Create the build directory structure used by compile -->
    <mkdir dir="${build.dir}"/>
  </target>

  <target name="compile" depends="init"
        description="compile the source " >
    <!-- Compile the java code from ${src.dir} into ${build.dir} -->
    <javac srcdir="${src.dir}" destdir="${build.dir}" debug="on">
      <classpath>
          <path refid="project.class.path"/>
      </classpath>
    </javac>

    <copy todir="${build.dir}">
      <fileset dir="${src.dir}">
        <include name="**/*.properties"/>
      </fileset>
    </copy>
  </target>

  <target name="dist" depends="compile"
        description="generate the distribution" >

    <!-- Create the distribution directory -->
    <mkdir dir="${dist.dir}"/>

    <!-- Copy the build dir to WEB-INF/classes -->
    <mkdir dir="web/WEB-INF/classes"/>
      
    <copy todir="web/WEB-INF/classes">
        <fileset dir="${build.dir}"/>
    </copy>

    <!-- Put everything in ${build} into the war file -->
    <war destfile="${dist.dir}/${ant.project.name}.war" 
         webxml="web/WEB-INF/web.xml">
      <fileset dir="web" excludes="**/web.xml"/>
        <webinf dir="web/WEB-INF">
          <include name="*.xml"/>
          <exclude name="web.xml"/>
        </webinf>
      <lib dir="web/WEB-INF/lib">
        <include name="${struts.dist.dir}/**/*.jar"/>
        <include name="${struts.dist.dir}/**/*.tld"/>
      </lib>
      <classes dir="build"/>
    </war>
  </target>

  <!-- Deploy the application by copying it to the deployment directory -->
  <target name="deploy" depends="dist"
           description="deploy to server" >
      <unjar src="${dist.dir}/${ant.project.name}.war"
          dest="${deploy.dir}/${ant.project.name}"/>
  </target>

</project>

Discussion

The build file displayed in the Solution can be used with minor modifications for most Struts-based web applications. You should change the value for the name attribute of the project element to the name of your application. This project name will be used as the name of the WAR file that’s created, as well as the name of the folder to which the application will be deployed. In addition, you should set the struts.dist.dir property to the lib directory for your particular installation of the Struts distribution.

An Ant-based build will allow you to perform various development tasks:

  • Retrieving your latest code from a source control system (e.g., CVS)

  • Packaging your Struts application into a WAR file

  • Running unit tests against your application

  • Generating code and configuration files utilizing XDoclet from Ant

  • Deploying your Struts application to your application server

  • Precompiling your JSP files to detect translation errors

By using a build script to create the WAR file, you can structure the physical location of your code as well as the Struts distribution however you see fit. Then you can use the Ant script to pull it all together.

See Also

The main web site for Ant can be found at http://ant.apache.org. Also, Ant: The Definitive Guide by Jesse E. Tilly and Eric M. Burke (O’Reilly) is a great reference to use when working with Ant. Recipe 1.8 shows the use of the XDoclet tool for the generation of Struts-related files.

1.8. Generating Struts Configuration Files Using XDoclet

Problem

When you make changes to or create a new Action or ActionForm, you have to make the corresponding changes to Struts configuration files.

Solution

Use the XDoclet tool, in conjunction with Ant, to process annotations in your Java code for automatic generation of the struts-config.xml file.

Discussion

Modern software applications are commonly composed of executable code as well as text configuration files. This approach makes it easier to port your application between environments and reduces the amount of code you have to change for different deployments. However, it adds an additional burden: keeping the code and the configuration files consistent with each other.

The XDoclet tool, originally developed for use in Enterprise JavaBean development, addresses this problem. With XDoclet, the developer places annotations, similar to JavaDoc tags, in the code that describes configuration attributes related to the code. At build time, you employ custom Ant tasks that use XDoclet to process these tags and generate the corresponding XML configuration files.

For Struts, XDoclet can generate the following elements for the struts-config.xml file:

  • action elements

  • form-bean elements

In addition, XDoclet can create the field-level Struts Validator configuration, typically found in the validation.xml file. Finally, if you are mapping properties of EJB Entity Beans to Struts ActionForms, XDoclet can generate the ActionForm Java source code.

Tip

XDoclet can be downloaded from http://xdoclet.sourceforge.net. Follow the installation instructions provided to install it. You will need to have Ant installed as well.

First, you will need to add a task to your Ant build script to call the XDoclet tasks. Example 1-9 shows an Ant target that can generate the struts-config.xml file for the struts-example web application.

Example 1-9. Webdoclet Ant target
<target name="webdoclet" depends="init">
  <taskdef 
      name="webdoclet"
      classname="xdoclet.modules.web.WebDocletTask"
      classpathref="project.class.path"/>
  <webdoclet
      mergedir="${merge.dir}"
      destdir="${generated.xml.dir}"
      excludedtags="@version,@author"
      force="${xdoclet.force}">
    <fileset dir="${src.dir}">
      <exclude name="**/*Registration*.java"/>
      <include name="**/*.java"/>
    </fileset>
    <strutsconfigxml
            version="1.1"/>
  </webdoclet>
</target>

This target calls the webdoclet custom Ant task, provided by XDoclet. This task can generate several web-related artifacts including the web.xml file, the struts-config.xml file, and the validation.xml file. For Struts applications, you probably won’t need to generate the web.xml file; for Struts applications, this file doesn’t change often. In Example 1-9, the webdoclet task is being used to generate the struts-config.xml file.

Not all elements of a struts-config.xml file can or should be based on annotated source code. Elements such as global forwards, global exception handlers, message resources, and plug-ins are not associated with a specific Action or ActionForm class. XDoclet handles this by letting you place this static configuration in files located in a special directory. At build time, XDoclet merges these files with elements generated in your source code. You use the mergedir attribute to specify the location of these static files. The destdir attribute specifies the directory where the generated files will be created. Generally, you want to create a separate directory for these files and copy the files into the appropriate directory for packaging and deployment after they are generated. The excludedtags attribute specifies JavaDoc tags that are to be excluded from processing by XDoclet.

Tip

It is common to exclude the @author and @version tags.

Finally, the force attribute forces XDoclet to generate new configuration files. If this attribute’s value is false, new files are generated only if the corresponding annotated Java source files have changed.

The fileset element tells XDoclet which Java source files to process. You can use this element to indicate which source files contain XDoclet annotations. For example, the struts-example application uses two Struts configuration files: struts-config.xml and struts-config-registration.xml. As Example 1-9 shows, you can exclude the elements that go into the struts-config-registration.xml by setting the fileset element to exclude classes that contain the name “Registration”.

The strutsconfigxml element instructs XDoclet to generate the struts-config.xml file. XDoclet generates a Struts Version 1.0-compliant file by default. Therefore, you must specify the version as “1.1” if you are using Struts 1.1. It is anticipated that XDoclet will provide support for Struts 1.2 via this attribute as well.

Once you have created this target in your build file, you can add annotations to your Action and ActionForm classes. For an ActionForm, XDoclet provides the @struts.form tag for generation of a form-bean element. The following code shows how this class-level tag is used in the SubscriptionForm of the struts-example application:

/**
 * Form bean for the user profile page...
 *
 * @struts.form 
 *    name="subscriptionForm"
 */

public final class SubscriptionForm extends ActionForm  {
  ...
}

When the webdoclet target is executed, the following form-beans element will be created in the generated struts-config.xml file:

  <!-- ========== Form Bean Definitions =================================== -->
  <form-beans>
    <form-bean
      name="subscriptionForm"
      type="org.apache.struts.webapp.example.SubscriptionForm"
    />

    <!--
         If you have non XDoclet forms, define them in a file called
         struts-forms.xml and place it in your merge directory.
    -->
  </form-beans>

XDoclet generates the form-bean element using the name you specified and creates the type attribute using the fully qualified class name of the ActionForm. This feature is one of the greatest benefits of XDoclet. Attributes of your classes, such as class names, packages, and method names are available to XDoclet just as they would be when you generate Javadocs for your application. XDoclet then uses these values where appropriate for the files being generated.

Tip

If you change the class name or package of a class, XDoclet will generate the correct configuration element without any other intervention. While IDE refactoring tools can be used to handle these types of changes, using XDoclet yields a solution that can be integrated with your existing Ant build process.

You can use XDoclet to generate action elements from your Action classes. XDoclet uses the @struts.action custom tag to allow for specification of the action element. In addition, the @struts.action-forward tag can specify nested forward elements. Likewise, @struts.action-exception tag can be used to generate action-specific declarative exception handling. The LoginAction.java class of the struts-example application is shown below with the annotations required to generate the complete action element:

/**
 * Implementation of <strong>Action</strong> that validates a user logon.
 * 
 * @struts.action
 *    path="/logon"
 *    name="logonForm"
 *    scope="session"
 *    input="logon"
 * 
 * @struts.action-exception
 *    key="expired.password"
 *    type="org.apache.struts.webapp.example.ExpiredPasswordException"
 *    path="/changePassword.jsp"
 */
public final class LogonAction extends Action {
  ...
}

The syntax of the custom tags closely matches the syntax of the corresponding XML elements. If you have your struts-config.xml defined, a good approach for using XDoclet is to cut and paste the XML elements from the struts-config.xml file into the Action class. Make the changes necessary for XDoclet to recognize the tags. The following are the annotated class comments in the LogoffAction.java file that show how the @struts.action-forward tag is used:

/**
 * Implementation of <strong>Action</strong> that processes a
 * user logoff.
 *
 * @struts.action
 *    path="/logoff"
 * 
 * @struts.action-forward
 *    name="success"
 *    path="/index.jsp"
 */

public final class LogoffAction extends Action {
  ...
}

Though XDoclet can go a long way to generating your struts-config.xml file, it can’t do everything. Some action elements do not correspond to Action classes that you create. For example, the struts-example application includes the following action mapping:

<action path="/tour"
     forward="/tour.htm">
</action>

The struts-config.xml file may contain global forwards, global exceptions, a controller element, message resource elements, and plug-in elements. XDoclet doesn’t generate these elements. However, it can merge files containing these elements with the generated file to produce a complete struts-config.xml file. Table 1-5 lists the files that XDoclet expects to find in the directory specified by the mergedir attribute when creating the struts-config.xml file.

Table 1-5. Files for merging into the generated struts-config.xml

Merge file

Used for

struts-data-sources.xml

An XML document containing the optional data-sources element

struts-forms.xml

An XML unparsed entity containing form-bean elements, for additional non-XDoclet forms

global-exceptions.xml

An XML document containing the optional global-exceptions element

global-forwards.xml

An XML document containing the optional global-forwards element

struts-actions.xml

An XML unparsed entity containing action elements, for additional non-XDoclet actions

struts-controller.xml

An XML document containing the optional controller element

struts-message-resources.xml

An XML unparsed entity containing any message-resources elements

struts-plugins.xml

An XML unparsed entity containing any plug-in elements

Most developers would agree that XDoclet doesn’t do as much for Struts development as it does for EJB development. For example, if you are primarily using dynamic action forms declared in your struts-config.xml, then form-bean generation from ActionForm classes doesn’t buy you anything. However, if you have a large application with lots of action elements and your application uses Ant, then XDoclet is certainly worth considering. XDoclet also supports generation of other types of configuration documents such as Hibernate mappings.

See Also

Recipe 1.7 provides a boilerplate Ant build script to which you can add the XDoclet targets.

Complete information for XDoclet can be found at http://xdoclet.sourceforge.net.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.172.93