Apache Struts

by Sandeep Desai (http://www.thedesai.net)

 

Apache Struts is a Model 2 Framework it implements the FrontController pattern i.e. all servlet requests go through Struts ActionServlet

 

Struts Internals

Struts ActionServlet intercepts request and forwards to RequestProcesser.process()

 

 

Struts Sequence Diagram

 

 

Messages Description

1.1.1        retrieve and return the ActionForm bean associated with the mapping, creating one if necessary

1.1.2        populates the ActionForm bean with the input fields of the HTML form

1.1.3        validates the input field values and creates error messages if validation errors

1.1.4        acquires an UserAction instance to process the request, calling the overwritten execute method of UserAction

1.1.4.1  retrieves data from the UserActionForm bean by getProperties methods

1.1.4.2  calls business services through the BusinessDelegate

1.1.4.3  populates a value object bean (optional)

1.1.4.4  forwards to the specified destination in struts-config.xml

2        the HelperBean

3        and / or from the ActionForm bean

Create struts application

WEB-INF/web.xml contains

<servlet>

    <servlet-name>action</servlet-name>

    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>

    <init-param>

        <param-name>config</param-name>

        <param-value>/WEB-INF/struts-config.xml</param-value>

    </init-param>

    <load-on-startup>1</load-on-startup>

</servlet>

 

<servlet-mapping>

    <servlet-name>action</servlet-name>

    <url-pattern>*.do</url-pattern>

</servlet-mapping>

 

<taglib>

    <taglib-uri>/tags/struts-bean</taglib-uri>

    <taglib-location>/WEB-INF/struts-bean.tld</taglib-location>

</taglib>

 <taglib>

    <taglib-uri>/tags/struts-html</taglib-uri>

     <taglib-location>/WEB-INF/struts-html.tld</taglib-location>

</taglib>

 

 

WEB-INF/struts-config.xml

 

Basic WEB-INF/struts-config.xml file

<?xml version="1.0" encoding="windows-1252" ?>

<!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>

  <action-mappings>

    <action path="/userRegistration" type="net.thedesai.mule.view.FirstAction">

       <forward name="success" path="/firstSuccess.jsp"/>

    </action>

    <action path="/page1" forward="unknown"/>

  </action-mappings>

  <message-resources parameter="hibernate.ApplicationResources"/>

</struts-config>

 

WEB-INF/struts-config.xml

<!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>

  <form-beans>

    <form-bean name="userRegistrationForm"

               type="net.thedesai.mule.view.UserRegistrationForm"/>

    <form-bean name="userRegistrationMultiForm"

               type="net.thedesai.mule.view.UserRegistrationMultiForm"/>

    <form-bean name="userRegistrationValidationForm"

               type="net.thedesai.mule.view.UserRegistrationValidationForm"/>

  </form-beans>

  <global-exceptions>

    <exception type="net.thedesai.mule.view.PhoneException"

               key="userRegistration.phone.exception617"

               path="/userRegistrationException.jsp"/>

  </global-exceptions>

  <global-forwards>

    <!-- Default forward to "Welcome" action -->

    <!-- Demonstrates using index.jsp to forward -->

    <forward name="welcome" path="/Welcome.do"/>

  </global-forwards>

  <action-mappings>

    <!-- action attributes

            attribute: specifies name of ActionForm if no attribute then name becomes attribute

            className: custom config object

            include: JSP that will handle this request (mutually exclusive with input)

            input: inputView of an action, i.e. JSP that has Form on validation failure redirect back to form

            name: identifies the ActionForm

            path: incoming request path that this action maps to

            parameter: similar to servlet init-parameters

            roles: J2EE security roles

            type: Action Handler class name

            scope: request of session scope for the ActionForm

            Unknown: Default action map to handle unknown URL

            validate: call validate on ActionForm note Action.execute() not called if validation fails

    -->

    <action path="/userRegistrationFirst"

            type="net.thedesai.mule.view.UserRegistrationAction">

      <forward name="success" path="/regSuccess1.jsp"/>

    </action>

    <action path="/userRegistration"

            type="net.thedesai.mule.view.UserRegistrationAction"

            name="userRegistrationForm" attribute="user"

            input="/userRegistration.jsp">

      <exception type="net.thedesai.mule.view.PhoneException"

                 key="userRegistration.phone.exception781"

                 path="/userRegistrationException.jsp"/>

      <forward name="success" path="/regSuccess2.jsp"/>

      <forward name="failure" path="/regFailure.jsp" redirect="false"/>

    </action>

    <action path="/userRegValidation"

            forward="/userRegistrationValidation.jsp"/>

    <action path="/userRegistrationValidation"

            type="net.thedesai.mule.view.UserRegistrationAction"

            name="userRegistrationValidationForm" attribute="user" scope="session"

            input="/userRegistrationValidation.jsp">

      <exception type="net.thedesai.mule.view.PhoneException"

                 key="userRegistration.phone.exception781"

                 path="/userRegistrationException.jsp"/>

      <forward name="success" path="/regSuccess2.jsp"/>

      <forward name="failure" path="/regFailure.jsp" redirect="false"/>

    </action>

    <action path="/Welcome" forward="/welcome.jsp"/>

    <action path="/displayAllUsers"

            type="net.thedesai.mule.view.DisplayAllUsersAction">

      <forward name="success" path="/userRegistrationList.jsp"

               redirect="false"/>

    </action>

    <action path="/userRegWizard" forward="/userRegistrationMultiPage1.jsp"/>

    <action path="/userRegistrationMultiPage1"

            type="net.thedesai.mule.view.UserRegistrationMultiAction"

            name="userRegistrationMultiForm" attribute="user" scope="session"

            input="/userRegistrationPage1.jsp">

      <exception type="net.thedesai.mule.view.UserRegistrationException"

                 key="userRegistration.exception"

                 path="/userRegistrationException.jsp"/>

      <forward name="success" path="/userRegistrationMultiPage2.jsp"/>

    </action>

    <action path="/userRegistrationMultiPage2"

            type="net.thedesai.mule.view.UserRegistrationMultiAction"

            name="userRegistrationMultiForm" attribute="user" scope="session"

            input="/userRegistrationMultiPage2.jsp">

      <exception type="net.thedesai.mule.view.UserRegistrationException"

                 key="userRegistration.exception"

                 path="/userRegistrationException.jsp"/>

      <forward name="success" path="/regSuccess2.jsp"/>

    </action>

  </action-mappings>

  <message-resources parameter="application"/>

  <plug-in className="org.apache.struts.validator.ValidatorPlugIn">

    <set-property property="pathnames"

                  value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>

  </plug-in>

</struts-config>

ActionForm

Lifecycle of an ActionForm

 

public class UserRegistrationForm extends ActionForm {

    public UserRegistrationForm() {    }

   

    public void setName(String name) { this.name = name;  }

    public String getName() { return name; }

 

    // TODO How to get get Struts to handle this exception

    public void setPhone(String phone) throws PhoneException {

        if (phone.startsWith("781"))

           throw new PhoneException();

        this.phone = phone;

    }

   

    public String getPhone() { return phone;  }

   

    private String name;

    private String phone;

 

    public void reset(ActionMapping ActionMapping, HttpServletRequest HttpServletRequest) {

        name = null;

        phone = null;      

    }

 

    public ActionErrors validate(ActionMapping mapping,

                                 HttpServletRequest request) {

        ActionErrors errors = new ActionErrors();

       

        if (name == null || name.trim().equals("")) {

            errors.add("name", new ActionError("userRegistration.name.problem"));

        }       

        return errors;

    }    

}

 

Strtus Automatic type conversion

ActionForms can be strongly typed, Struts will convert String and String Arrays into primitive and primitive arrays

Struts converts request parameters into a HashMap and then uses common BeanUtils to populate the ActionForm with the request parameters.

Recommend keeping ActionForm property as string to avoid problem where property is integer and user enters “foo” the ActionForm will get it as 0

 

ActionForms should not touch the Application Model objects if there is a one to one relationship between form DTO and model DTO use BeanUtils.copyProperties to move and convert data

 

For application with common form elements implement a super ActionForm or use mapped back ActionForms (mapped back means you use a Map for properties)

 

Session vs Request scope ActionForms

 

DynaActionForms

 

 

Struts Tags

 

In the example below address would be an Address JavaBean class with setState() method

similarly Phones would be an array Phone[] phones;

 

For user <bean:write name="user" property="name"/>

For user state <bean:write name="user" property="address.state"/> 

For user home phone <bean:write name="user" property="phones[0].number"/> 

 

 

<logic:iterate id=”lineitem” indexId=”index” name=”adminUsersForm” property=”usersList”>

 

Can do dynamic fields using dynamic properties

public class FooForm extends ActionForm() {

     private Map dynamicProps = new HashMap();

     public Object getDynamicProps(String key) {

          return dynamicProps.get(key);

}

public void setDynamicProps(String key, Object value) {

          return dynamicProps.put(key,value);

}

}

 

<html:form..>

<html:text property=”dynamicProps(bar)”>

</html:form>

 

//Access in Action as

userForm.getDynamicProps(“foo”);

 

Struts Validator

 

WEB-INF/validation.xml example

<?xml version="1.0" encoding="ISO-8859-1" ?>

 

<!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">

<form-validation>

    <!--

     This is a minimal Validator form file with a couple of examples.

-->

    <global>

        <!-- An example global constant

        <constant>

            <constant-name>postalCode</constant-name>

            <constant-value>^\d{5}\d*$</constant-value>

        </constant>

        end example-->

    </global>

    <formset>

        <!-- minlength rule defined in validation-rules.xml -->

        <form name="user" >

            <field property="name" depends="required,minlength,maxlength">

                <arg key="userRegistration.name" position="0"/>

                <var>

                    <var-name>min-length</var-name>

                    <var-value>5</var-value>

                </var>

                <var>

                    <var-name>max-length</var-name>

                    <var-value>20</var-value>

                </var>

            </field>

        </form>

        <!-- An example form -->

        <form name="logonForm">

            <field property="username" depends="required">

                <arg key="logonForm.username"/>

            </field>

            <field property="password" depends="required,mask">

                <arg key="logonForm.password"/>

                <var>

                    <var-name>mask</var-name>

                    <var-value>^[0-9a-zA-Z]*$</var-value>

                </var>

            </field>

        </form>

    </formset>

    <!-- An example formset for another locale -->

    <formset language="fr">

        <constant>

            <constant-name>postalCode</constant-name>

            <constant-value>^[0-9a-zA-Z]*$</constant-value>

        </constant>

        <!-- An example form -->

        <form name="logonForm">

            <field property="username" depends="required">

                <arg key="logonForm.username"/>

            </field>

            <field property="password" depends="required,mask">

                <arg key="logonForm.password"/>

                <var>

                    <var-name>mask</var-name>

                    <var-value>^[0-9a-zA-Z]*$</var-value>

                </var>

            </field>

        </form>

    </formset>

</form-validation>

Struts Action

Do the following in Struts Action execute()

 

 

Struts tags

Struts tags predate JSTL so use JSTL where appropriate

Add the <taglib> to web.xml

 

HTML tags

<%@ taglib uri=”struts-html” prefix=”html” %>

<%@ taglib uri=”struts-bean” prefix=”bean” %>

 

Don’t link JSPs from JSP use an Action forward

<html:link forward=”fooForward”>Foo</html:link>

Avoid using page and href attribute use forward or action

Can add parameters by

<html:link page=”foo.do” paramId=”boolProp” paramName=”myBean” paramProperty=”nested.boolProp”>Foo</html:link>

will call myBean.getNested().boolProp()

<a href=”/mystrutsapp/foo.d?boolProp=”false”>

Can all pass HashMap

<% HashMap ... pageContext.setAttribute(“newValues”, newValues); %>

<html:link action=”/foo” name=”newValues”>

 

<html:base target=”/foo/bar.jsp”> will convert to http://localhost:8080/strutsApp/foo/bar.jsp

<html:button property=”action”>Submit</html:button>

<html:cancel>Cancel</html:cancel>

<html:cancel><bean:write key=”form.cancel”/></html:cancel>

<html:checkbox property=”booleanProperty”/>

html:errors will display validation errors

<html:errors/> For formatting message add errors.header etc to resource bundle

<html:errors property=”someFieldName”/>  use this to display error next to field

<html:file ...> upload file

<html:form action=”/UserUpdate” method=”post”> ... </html:form>

<html:hidden property=”foo”>

<html:html> renders html content

<html:image> image button specify image using src or page attribute, supports localization

<html:img> renders <img> element

<html:javascript/> renders Javascript validation methods

<html:messages> display collection of messages, can use for display validation error message next to field

<html:messages id=”message” message=”true”>

     <li><%=message%></li>

</html:messages>

<html:multibox> manage array of checkboxes, works with array of strings, if string in array then checkbox selected

<html:select property=”pizzaSize”> //combo box, can select multiple value

  <html:option value=”Small”>Small<html:option>

  <html:option value=”Medium” key=”pizza.medium”/>

<html:select>

<html:password>

<html:radio> radio button

<html:reset> reset button

<html:rewrite> Useful client side Javascript based URL link similar to <html:link>

<html:submit> submit button

<html:text>  text input field

<html:textarea> text area field takes rows and cols attribute

<html:xhtml>

 

 

Bean tags (Use JSTL if tag overlaps with bean tag)

<%@ taglib uri=”struts-bean” prefix=”bean” %>

 

<bean:include id=”>

 

<bean:cookie> (Use JSTL)

<bean:header> request header (Use JSTL <jsp:getProperty>

<bean:parameter> request parameter

 

<bean:define> (use JSTL core:set)

<bean:include> (Use jsp:include)

<bean:message> (get message from resource bundle)

<bean:page id=”reqObj” property=”request”> create scriptlet variables, get request property and store in reqObj

<bean:resource>

<bean:size id=”count” name=”emps”> count items stored in array, collection or map and store in the count object

<bean:struts id=”uForm” formBean=”userForm”> copy struts object (ActionForms, ActionForward or an ActionMapping) into page scoped scriptlet variable

<bean:write name=”userReg” property=”phone” scope=”request”>

 

 

 

Logic tag  library

<%@ taglib uri=”struts-logic” prefix=”logic” %>

 

<logic:empty name=”fooBean”> (use JST EL empty operator) if scriptlet variable is null an empty string, collection or map

<logic:notEmpty>

 

<logic:equal name=”bean” property=”fooProp” value=”<%= foo%>”> set value <logic:equal>

logic: notEqual,lessThan, greaterThan, lessEqual, greaterEqual

 

<logic:forward name=”foo”> (forward to ActionForward )

  references in struts-config.xml -> <global-forwards> <forward name=”foo” path=”/foo.jsp”/> <global-forwards>

<logic:redirect> does a response.sendRedirect() avoid using this tag

iterator over collection, enumeration, iterator, map or array

print 5th to 10th (offset, length are optional attributes)

<logic:iterate id=”emp” name=”dept” property=”employees” scope=”request” offset=”5” length=”10>

     <bean:write name=”emp” property=”phone”/>

</logic:iterate>

String equals and not equals

<logic:match header=”User-Agent” value=”Mozilla”>Mozilla</logic:match>

<logic:notMatch>

 

<logic:present> check to see if headers, request parameters, cookies, JavaBeans or JavaBean properties are present and not equals to null

 

 

Common Attributes (Bold are required)

property

Associates field with property from corresponding ActionForm

accessKey

Similar to mnemonic in Swing

alt

alternative text

disabled

 

indexed

indexed property name, works only with logic:iterate tag

onblur,onchange,onclick,

ondblclick,onfocus,onkeydown, onekypress,onkeyup,onkeydown, onmousemove,onmouseout,onmouseover,

onmouseup

allows you to write event handler when element loses focus

style

in line CSS style

styleClass

CSS class

sytleId

style id

tabIndex

tab order

title

tooltip for input

titleKey

tooltip for input from resource bundle

value

label for butto or default value for input

 

Common Struts Problems and tips

 

Struts Testing

 

MockStrutsTestCase methods

getMoc

 

public class TestLoginAction extends MockStrutsTestCase {

 

     public TestLoginAction(String testName) { super(testName); }

 

public void setUp() { super.setUp(); }

public void tearDown() { super.tearDown(); }

 

     public void testSuccessfulLogin() {

        setRequestPathInfo("/login");

        addRequestParameter("username","deryl");

        addRequestParameter("password","radar");

        actionPerform();

        verifyForward("success");

        assertEquals("deryl",(String) getSession().getAttribute("authentication"));

        verifyNoActionErrors();

    }

}

Books

Links

 

Credits

·        Struts Sequence diagram, submitted diagrams by Jean-Michel Garnier on October 02. Based on an article by Jean-Michel Garnier in the http://rollerjm.free.fr web site. Copyright (c) 1999-2002 The Apache Software Foundation. All rights reserved.

Hosted by www.Geocities.ws

1