A Struts Tool for Previewing Forms & Generating Beans

Struts is becoming a de facto standard framework for building Java web applications.


March 01, 2004
URL:http://www.drdobbs.com/jvm/a-struts-tool-for-previewing-forms-gene/184405587

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:

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:

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:

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:

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:

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

Mar04: A Struts Tool for Previewing Forms & Generating Beans

Figure 1: The Struts Studio IDE with the struts configuration displayed as a diagram.

Mar04: A Struts Tool for Previewing Forms & Generating Beans

Figure 2: Display of uploaded page.

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.