I was prompted to add this by someone because of a reply I posted on the list today discussing this problem. I hope someone finds it useful!

A question that comes up rather often is how can you initialize an ActionForm before an Action is called? In other words, consider the case where a user clicks a link on a page... another page is to be shown that contains a form, and you want that form to be backed by an ActionForm. Further, you want to do something a bit more complex like populate it from a database.

Some people find it a bit convoluted to have to have a special Action to do that, or a method of a DispatchAction, and the extra mappings that go along with it in either case.

There are a number of solutions to this problem. SetupItems for example is one, but that has some significant drawbacks. There are others with which I am less familiar, and others should feel free to describe those here.

I will describe one option that uses the DependencyFilter in Java Web Parts (JWP). You can find this project at http://javawebparts.sourceforge.net (take a look at the Javadocs link, the Filter package specifically).

Simply put, the DependencyFilter is a "hybrid" IoC implementation in servlet filter form. It's "hybrid" in the sense that it doesn't directly inject created objects into a client object. It isn't as powerful as Spring, not by a long shot, but it is simple to use and is completely declarative (i.e., driven by XML config file), so it does have some benefits. Here is an example of using it to preinitialize a Struts ActionForm...

* First, begin with a simple blank Struts app like that which ships with Struts. Edit the struts-config.xml file to include this snippet:

...
<form-beans>
  <form-bean name="testForm1" type="app.blank.TestForm" />
</form-beans>
<action-mappings>
  <action path="/test" type="app.blank.TestAction" name="testForm1"
scope="session" validate="false" input="index.jsp">
    <forward name="defaultForward" path="/test.jsp" />
  </action>
</action-mappings>
...

* Next, create index.jsp which contains nothing but a simple form submitted to /test like so:

<html>
  <head></head>
  <body>
    <form name="test" method="post" action="test.do">
      <input type="submit" name="submit" value="Submit">
    </form>
  </body>
</html>

That could just as easily have been a link, I just happen to have done it this way when I made the post to the list today :)

* Next, create TestAction.java, which does nothing but returns defaultForward:

package app.blank;
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 class TestAction extends Action {
  public ActionForward execute(ActionMapping mapping, ActionForm inForm, HttpServletRequest request, HttpServletResponse response) throws Exception {
    return mapping.findForward("defaultForward");
  }
}

* Next, create test.jsp to in essence display the contents of the ActionForm, very simply:

<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html>
  <head></head>
  <body>
    <html:form action="test">
      <html:text property="field1" />
    </html:form>
  </body>
</html>

* Next, create TestForm.java:

package app.blank;
import org.apache.struts.action.ActionForm;
public class TestForm extends ActionForm {
  private String field1;
  public void setField1(String field1) {
    this.field1 = field1;
  }
  public String getField1() {
    return field1;
  }
}

* Next, add the javawebparts_filter.jar file to your project in WEB-INF/lib. This depends on Commons Beanutils, Digester and Logging, so you should be all set without explicitly adding anything else.

* Next, add the following entry to web.xml to "activate" the filter:

<filter>
  <filter-name>DependencyFilter</filter-name>
  <filter-class>javawebparts.filter.DependencyFilter</filter-class>
   <init-param>
    <param-name>configFile</param-name>
    <param-value>/WEB-INF/dependencies_config.xml</param-value>
  </init-param>
  <init-param>
    <param-name>createSession</param-name>
    <param-value>true</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>DependencyFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

Note the createSession parameter... if you require more control over your session creation, you can set this to false. Note however that you won't be able to define any dependencies in session scope if you do (that's only logical!).

* Lastly, create the file dependencies_config.xml and place it in WEB-INF with the following contents:

<dependencies>
  <dependency scope="session" name="testForm1">
    <className>app.blank.TestForm</className>
    <initProp name="field1" value="Hello from DependencyFilter!" />
  </dependency>
</dependencies>

This describes a single dependency called testForm1, which will be created and populated and placed in session scope. If it already exists in session scope, it will be left alone.

At this point you should be all set! Deploy and access the application and click the button on the first page. You will find yourself on the second page with the words "Hello from DependencyFilter!" in the edit box.

Voila! The ActionForm was pre-initialized!

Of course, DependencyFilter can be used for things other than ActionForms, and it has a fair amount of flexibility to offer in terms of initializing the objects. Have a look! But, as far as preinitializing ActionForms without having to add Actions and mappings to your application goes, it does the job pretty well :)

PreInitializeActionForm (last edited 2009-09-20 23:12:12 by localhost)