Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

JVM Languages

A Struts Tool for Previewing Forms & Generating Beans


Mar04: A Struts Tool for Previewing Forms & Generating Beans

Viewing forms and generating code

Andy is a solution architect. He can be reached at [email protected].


Apache's Jakarta Struts is rapidly becoming the de facto standard MVC (Model-View-Controller) Framework for building Java web applications. For Struts applications, there is usually an Action class associated with each JavaServer Page (JSP). If the JSP is an input form, there is also an associated ActionForm class that handles input data, unless you are using the DynaActionForm and Validator framework new to Struts 1.1. But for small to medium projects, ActionForm classes may be simpler to use.

There are a number of freely available tools to automatically generate ActionForm classes; for example, the Easy Struts plug-in for IDEs such as Eclipse and Borland's JBuilder. There are even IDEs such as Struts Studio that are specifically built for developing Struts applications. Easy Struts (http://www.easystruts.sourceforge .net.com/) generates ActionForm/Action classes as well as JSPs. Easy Form (one of the Easy Struts wizards) requires users to define the name and type of input before generating the code for ActionForm/Action classes and JSP. Struts Studio (http://www.exadel.com/) generates ActionForm and Action classes, but the generated ActionForm classes do not have any getter/setter methods for passing form parameters. They have to be added manually. Still, they are good tools for building Struts applications.

When building web applications, I usually start with storyboarding—that is, designing the overall structure of the site and some web pages before coding the business logic. I want to be able to quickly put together some web pages, including forms, using the Struts tag libraries and see how they look. However, to view a Struts form, you must go through a number of steps:

  • Define the mapping between form-beans (ActionForms) and actions (Actions) in the struts-config.xml file.
  • Code the Action and ActionForm classes for the form.

  • Compile the classes.

  • Restart the web container or the Struts ActionServlet.

It occurred to me that what I'd really like to do is simply upload and view a Struts form coded using the Struts tag libraries without updating the Struts configuration file and restarting the web container. Moreover, I'd like for the tool that does this to generate the ActionForm class(es) from the form so that it/they can be used later for production. In this article, I present a tool that does just this. The complete source code for this tool is available electronically; see "Resource Center," page 5.

Design

The main requirement of this tool is to be able to upload a JSP form coded using the Struts tag libraries and display it without additional development work. The intention is to preview Struts forms during the development cycle instead of for use in a production environment.

To do this, you must first understand how Struts processes such a request:

  • Struts checks the configuration for an Action mapping to an ActionForm.
  • If an ActionForm associated with the Action is found, it checks to see if an instance of the ActionForm with the proper scope (request or session) is present.

  • If a proper instance is found, it reuses that instance; otherwise, it instantiates a new one and saves it in the proper scope.

  • Struts calls the ActionForm's reset() method to initialize or reset its properties and displays the JSP form.

  • When the submit button is clicked, Struts populates the ActionForm properties with the input parameters using the ActionForm's setter methods.

  • If input validation is set to True, it calls the ActionForm's validate() method and invokes the associated action.

To display a JSP form using the Struts framework, associated Action and ActionForm entries have to be configured in the Struts configuration file. Two approaches for achieving this come to mind:

  • For each upload, reconfigure the Struts configuration file to add the new Action/ActionForm mapping, generate and compile the associated Action and ActionForm classes.
  • Reuse the mapping of an existing Action/ActionForm, generate and compile a new ActionForm using the existing name configured in Struts. The Action class does not need to be changed.

The first approach requires that the Struts configuration file be updated for each upload. For Struts to reread the configuration file, the Struts ActionServlet must be restarted. Another issue with this approach is that after a while the Struts configuration file is cluttered with many unwanted entries.

The second approach is simpler because it only involves generating/compiling/reloading an ActionForm class from the uploaded JSP. Both the uploaded JSP form and generated ActionForm must be renamed to match a particular Action/ActionForm mapping in the Struts configuration file.

The Struts application built using the second approach is meant to be a personal Struts productivity tool only. It is a personal tool because it requires features—autoloading of modified Java classes and autocompilation of modified JSPs—on the web container that are usually turned off in production environments. Another limitation is that for every upload, the resultant JSP and ActionForm class always use the same name. Race conditions result if multiple users are uploading JSP forms for preview at the same time. The intent is to provide a personal productivity tool to help you preview your JSP forms without reconfiguring Struts and restarting the web container. Since you are usually the only user of the application, the single-user limitation is not an issue.

Implementation

The overall organization of this tool is straightforward. The welcome page (index.jsp) redirects to the /pages/fileupload.jsp. It is a simple file upload form. The Action and ActionForm classes are called FileUploadAction and FileUploadForm, respectively. The FileUploadAction class does all the processing and performs the following tasks:

1. Uploads the file (JSP form) and saves it temporarily in a string.

2. Generates one or more ActionForm classes depending on whether there are multiple forms in the JSP and saves the class(es) as a string in the session context. Each class has getter/setter methods in addition to the reset() and validate() methods. The name of the ActionForm class generated is derived from the html:form tag's action attribute. For example, for action="UserInput.do", the ActionForm class name generated will be UserInputForm. It takes the part of the attribute value before the ".do" and appends "Form" to it.

3. Generates the UserPageForm class based on the content of the uploaded JSP. UserPage and UserPageForm are pre-configured entries in the Struts configuration file (Listing One) used in displaying the uploaded form. Even if there is more than one form in the uploaded JSP form, only one ActionForm (UserPageForm) will be created. getter/setter methods from the second form onwards are added to UserPageForm. The content is saved in /temp/ UserPageForm.java.

4. Compiles the /temp/UserPageForm.java file using the batch file /temp/buildform.bat.

5. Replaces all occurrences of the action references to "UserPage.do" and all references of ActionForm named XXXForm in the uploaded JSP form to "UserPageForm." UserPage.do and UserPageForm are the preconfigured action and ActionForm names. The modified content of the uploaded JSP form is saved in the file /pages/userpage.jsp.

6. Forwards to the /pages/viewsource.jsp.

The /pages/viewsource.jsp retrieves the string containing the generated ActionForm class(es) from the session context for display on the screen. It also provides a link to view the uploaded page. The ActionForm class(es) displayed by this page is different from the UserPageForm.java. The class(es) displayed are meant to be copied and pasted by users into ActionForm Java file(s) for use with the unmodified upload page in the production environment (after users add the validation logic). The UserPageForm.java generated and saved on the server is for use with the modified uploaded page for display purpose. Struts displays the page because it has the mapping configured in the struts- config.xml file.

When users click on the View Form link, the tool displays the uploaded JSP form. Clicking on the Submit button brings users back to the upload page.

Generating the ActionForm classes in steps 2 and 3 is done using the five classes in the Codegen package:

  • PrintfFormat.java, a utility class from Sun Microsystems that implements the C-style sprintf functionality.
  • FormElement.java, that records four pieces of information—ID, Name, Value, and Multi—for use by BeanGenerator to generate ActionForms. ID identifies the Struts HTML library tag that is to be processed. Name records the name of the input parameter for which getter/setter methods will be generated. Value contains the initial value of the parameter. Multi indicates whether the parameter is to be saved as a simple property or a property array.

  • FormObject.java, used to accumulate FormElements in a Java vector as the FormParser parses the JSP form. The FormObject is used by the BeanGenerator to access the individual FormElements to generate code.

  • FormParser.java, used to parse a JSP form written using a combination of Struts tag libraries and HTML into FormElements and saved in a FormObject. It handles Struts HTML tags including: text, password, checkbox, radio, file, button, text area, select, hidden, multibox, and form.

  • BeanGenerator.java, uses a FormParser object to parse a JSP form into FormElements contained in a FormObject and generates ActionForm beans. It provides methods to generate ActionForm beans based on the original JSP form (step 2) and a UserPageForm bean (step 3). This class has a main(). It can be used as a command-line program to generate ActionForm beans and display them on the screen. You invoke it like this: java BeanGenerator file1 file2 ...

The FileUploadAction class's execute() method uses a BeanGenerator object to generate the UserPageForm bean used for JSP form preview. It also generates one or more ActionForm beans for the uploaded JSP for display by the viewsource.jsp depending on whether there are multiple forms on the one JSP page uploaded. It spawns off another process to execute the /temp/buildform.bat file to compile the UserPageForm.java file. The buildform.bat file is for Windows only. You need to write your own shell script to compile the ActionForm bean if you are running this application on Linux or other UNIX systems.

All the Action and ActionForm Java source files are put into the application package. The Constants.java file defines string constants such as global forwards to be shared among the Action and ActionForm files.

Examples

Listing One shows the struts-config.xml file that defines:

  • Two form beans (ActionForm classes) for the application. The FileUploadForm is for uploading the JSP to the application. The UserPageForm is reserved for displaying the uploaded JSP.
  • There are two actions: FileUploadAction is invoked with the FileUploadForm, which is passed to it when a request to the URI /beanGenerator/FileUpload is made by users. beanGenerator is the name of this app. No parameter validation is to be performed. The Action forwards to /pages/viewsource.jsp. UserPageAction is invoked with the UserPageForm that is passed to it when a request to the URI /beanGenerator/UserPage is made by users, and so. The ActionForm beans are of request scope.

  • Only a base resource bundle has been defined for this application. The messages in the bundle are kept in the file application.properties. When a key is not located in the bundle, it displays the key instead of a Null string.

I used Exadel's Struts Studio Community Edition (http://www.exadel.com/) to build this application. The Struts Studio IDE allows viewing the struts configuration file either diagrammatically or as an XML file. Figure 1 shows the Struts Studio IDE with the Struts configuration displayed as a diagram.

Listing Two (MultiOrder.jsp) is the file uploaded for viewing. It contains six forms. Listing Three (/pages/userpage.jsp) is the modified uploaded page to map to the existing Struts configuration file. Listing Four (displayed by /pages/viewsource.jsp) shows the ActionForm classes generated for use with the original multiform.jsp for use in the production environment. Listing Five (UserPageForm.java) is the ActionForm class generated for use with the modified uploaded page (/pages/userpage.jsp) to display it and accept input. If the CheckOut form had any input widgets, all its getter/setter methods would have been added to the UserPageForm class. Figure 2 shows the display of this uploaded page.

A number of more complicated JSP forms have been included, together with the source code of this application, as examples and for testing the application. You should try them out and examine the code generated for the ActionForm beans as well as the modified uploaded page (/pages/userpage.jsp) and its ActionForm (/temp/UserPageForm.java) after each upload to fully understand how this application works.

Conclusion

The main limitation of the application is that it uses a fixed Action/ActionForm mapping for previewing uploaded JSP forms. Consequently, the tool can only be used by one user at a time. If more than one user is uploading a JSP form for preview, the application may be modifying the /pages/userform.jsp and /temp/UserPageForm.java files at the same time and causes errors when displaying the uploaded page.

This tool is written for Windows. To run it on nonWindows platforms, write your own shell script named "buildform.sh" for compiling the UserPageForm.java. The content of your shell script should be quite similar to that of the Windows buildform.bat file. The application (Constant.java's getBuildFileName() static method) detects the platform by looking at the platform-separator character. If the character is "/", it is assumed to be Windows and "buildform.bat" is invoked to compile the action form. Otherwise, "buildform.sh" is invoked.

DDJ

Listing One

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD 
                                Struts Configuration 1.1//EN"
                               "http://jakarta.apache.org/struts/
                                dtds/struts-config_1_1.dtd">
<struts-config>
 <data-sources/>
 <form-beans>
  <form-bean name="FileUploadForm" type="application.FileUploadForm"/>
  <form-bean name="UserPageForm" type="application.UserPageForm"/>
 </form-beans>
 <global-exceptions/>
 <global-forwards>
  <forward name="upload" path="/pages/fileupload.jsp"/>
  <forward name="view" path="/pages/userpage.jsp"/>
 </global-forwards>
 <action-mappings>
  <action name="FileUploadForm" path="/FileUpload" scope="request"
   type="application.FileUploadAction" validate="no">
   <forward name="viewsource" path="/pages/viewsource.jsp"/>
  </action>
  <action name="UserPageForm" path="/UserPage" scope="request"
   type="application.UserPageAction" validate="no"/>
 </action-mappings>
 <controller/>
 <message-resources null="false" parameter="application"/>
</struts-config>

Back to Article

Listing Two

<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<html:html locale="true">
<head>
    <title>Shopping Cart Content</title>
</head>
<body>
<h3>Shopping Cart Content</h3>
<table border="1" align="center">
<tr bgcolor="#808080">
<th>Item Id</th><th>Item Description</th><th>Unit Cost</th>
                                      <th>Quantity</th><th>Total Cost</th>
<
% for (int i = 0; i < 5; i++) { %>
<tr>
    <td><%= i %></td>
    <td>Widget <%= i %></td>
    <td><%= 10.95 + i * 10 %></td>
    <td>
        <html:form action="Order.do">
        <html:hidden property="itemId" value="Id<%= i %>" />
        <html:text property="quantity" value="1" />
        <html:submit value="Update Quantity" />
        </html:form>
    </td>
    <td><%= 10.95 + i * 10 %></td>
</tr>
<% } %>
</table>
<html:form action="CheckOut.do">
<center>
<html:submit value="Checkout" />
</center>
</html:form>
</body>
</html:html>

Back to Article

Listing Three

<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<html:html locale="true">
<head>
    <title>Shopping Cart Content</title>
</head>
<body>
<h3>Shopping Cart Content</h3>
<table border="1" align="center">
<tr bgcolor="#808080">
<th>Item Id</th><th>Item Description</th><th>Unit Cost</th>
                                      <th>Quantity</th><th>Total Cost</th>
<% for (int i = 0; i < 5; i++) { %>
<tr>
    <td><%= i %></td>
    <td>Widget <%= i %></td>
    <td><%= 10.95 + i * 10 %></td>
    <td>
        <html:form action="UserPage.do">
        <html:hidden property="itemId" value="Id<%= i %>" />
        <html:text property="quantity" value="1" />
        <html:submit value="Update Quantity" />
        </html:form>
    </td>
    <td><%= 10.95 + i * 10 %></td>
</tr>
<% } %>
</table>
<html:form action="UserPage.do">
<center>
<html:submit value="Checkout" />
</center>
</html:form>
</body>
</html:html>

Back to Article

Listing Four

// ActionForm class: OrderForm.java
// Generated by beanGenerator on: Sun Aug 10 12:29:14 EST 2003
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.upload.FormFile;

public class OrderForm extends ActionForm {
private String itemId = "";
public String getItemId() { return itemId; }
public void setItemId(String itemId) { this.itemId = itemId; }
private String quantity = "1";
public String getQuantity() { return quantity; }
public void setQuantity(String quantity) { this.quantity = quantity; }
public void reset(ActionMapping actionMapping, HttpServletRequest request) {
    // TODO: Write method body
        quantity = "1";
        itemId = "";
}
public ActionErrors validate(ActionMapping actionMapping, 
                                               HttpServletRequest request) {
    // TODO: Write method body
    throw new UnsupportedOperationException("Method not implemented");
}
}
// ActionForm class: CheckOutForm.java
// Generated by beanGenerator on: Sun Aug 10 12:29:14 EST 2003
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.upload.FormFile;

public class CheckOutForm extends ActionForm {
public void reset(ActionMapping actionMapping, HttpServletRequest request) {
    // TODO: Write method body
}
public ActionErrors validate(ActionMapping actionMapping, 
                                                HttpServletRequest request) {
    // TODO: Write method body
    throw new UnsupportedOperationException("Method not implemented");
}
}

Back to Article

Listing Five

package application;
// ActionForm class: UserPageForm.java
// Generated by beanGenerator on: Sun Aug 10 12:29:14 EST 2003
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.upload.FormFile;

public class UserPageForm extends ActionForm {
private String itemId = "";
public String getItemId() { return itemId; }
public void setItemId(String itemId) { this.itemId = itemId; }
private String quantity = "1";
public String getQuantity() { return quantity; }
public void setQuantity(String quantity) { this.quantity = quantity; }
public void reset(ActionMapping actionMapping, HttpServletRequest request) {
    // TODO: Write method body
        quantity = "1";
        itemId = "";
}
public ActionErrors validate(ActionMapping actionMapping, 
                                               HttpServletRequest request) {
    // TODO: Write method body
    throw new UnsupportedOperationException("Method not implemented");
}
}

Back to Article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.