Home > FAQs > Cookbook > Using Struts and XWork with JSP 2.0 and JSTL 1.1 |

Apache Struts 2 Documentation
Using Struts and XWork with JSP 2.0 and JSTL 1.1
Edit Page Browse Space Add Page Add News

S2/X1 and its taglib is oriented towards OGNL, which is using a value stack
for all action properties. These values are not direct available for the
expression language of JSP2/JSTL1.1.

However, it's easy to populate the request
attribute set, with all gettable properties of an action object. You need to provide
an interceptor that does the job, by register a PreResultListener which is
invoked after the return of Action.execute() but before the rendering of the result .

The interceptor below is using Jakarta BeanUtils. It first extracts all getters
of the current action, invokes them one at the time and stores the values into a map.
Then it iterates over the map and populates the request attribute set.
The double iteration is not needed, it's just there for clarity.

## class ActionPropertyExportInterceptor

    package com.whatever.interceptors;

    import org,apache.struts2.StrutsStatics;
    import com.opensymphony.xwork.Action;
    import com.opensymphony.xwork.ActionInvocation;
    import com.opensymphony.xwork.interceptor.AroundInterceptor;
    import com.opensymphony.xwork.interceptor.PreResultListener;
    import org.apache.commons.beanutils.PropertyUtils;
    import javax.servlet.http.HttpServletRequest;
    import java.beans.PropertyDescriptor;
    import java.util.*;

    /**
     * Populates HTTP Request Attributes with all gettable properties of the current action.
     */
    public class ActionPropertyExportInterceptor extends AroundInterceptor {
        protected void before(ActionInvocation invocation) throws Exception {
            invocation.addPreResultListener( new PropertyExporter() );
        }
        protected void after(ActionInvocation dispatcher, String result) throws Exception { }

        public static class PropertyExporter implements PreResultListener {
            private static final List   ignore = Arrays.asList(new String[] {"class", "texts"}); //skip getClass,...

            //Invoked after Action.execute() but before Result
            //Calls all getters of the action and insert the values into the request
            public void beforeResult(ActionInvocation invocation, String resultCode) {
                Map                 props   = extractGetterPropertyValues( invocation.getAction() );
                HttpServletRequest  request = getRequest(invocation);
                for (Iterator it = props.entrySet().iterator(); it.hasNext();) {
                    Map.Entry   e = (Map.Entry) it.next();
                    request.setAttribute((String) e.getKey(), e.getValue());
                }
            }

            public Map extractGetterPropertyValues(Object bean) {
                PropertyDescriptor[]  descr = PropertyUtils.getPropertyDescriptors(bean);
                Map                   props = new HashMap();
                for (int i = 0; i < descr.length; i++) {
                    PropertyDescriptor d = descr[i];
                    if (d.getReadMethod() == null) continue;
                    if (ignore.contains(d.getName())) continue;

                    try {
                        props.put(d.getName(), PropertyUtils.getProperty(bean, d.getName()));
                    } catch (Exception e) { }
                }
                return props;
            }

            public HttpServletRequest getRequest(ActionInvocation invocation) {
                return (HttpServletRequest) invocation.getInvocationContext().get(WebWorkStatics.HTTP_REQUEST);
            }
        }
    }

Don't forget to declare the interceptor in your struts.xml file and insert it
into your interceptor stack.

## struts.xml snippet

    <interceptor name="export" class="com.whatever.interceptors.ActionPropertyExportInterceptor" />
    . . .
    <interceptor-stack name="standard-interceptors">
        <interceptor-ref name="timer" />
        <interceptor-ref name="logger" />
        <interceptor-ref name="params" />
    *    <interceptor-ref name="export"/>*
        <interceptor-ref name="validateParams"/>
        <interceptor-ref name="awarePlugger" />
    </interceptor-stack>

Your action need to provide getters for all properties that should be exported into the
request attribute set.

## class ViewUser

    public class ViewUser extends ActionSupport {
        private int                         id;
        private User                        user;

        public String execute() throws Exception {
            user = findUser( getId() );
            return Action.SUCCESS;
        }

        public  int   getId()          {return id;}
        public  void  setId(int id)    {this.id = id;}
    *    public  User  getUser()        {return user;}*

        private User  findUser(int id) {...}
    }

The User class might look like this

## class User

    import java.util.Date;
    public class User {
        private int     id;
        private String  firstName, lastName, email;
        private String  street, zip, city;
        private Date    date;

        public String  getFirstName() {return firstName;}
        //..._getters and setters_...
    }

Finally, using the samples above you can write your JSP2 page like this.

## ViewUser.jsp

    <%@ taglib prefix="c"   uri="http://java.sun.com/jsp/jstl/core" %>
    <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
    <%@ taglib prefix="fn"  uri="http://java.sun.com/jsp/jstl/functions" %>
    <html>
    <head>
        <title>Info about ${user.firstName}</title>
    </head>
    <body>
        <h1>Info about ${user.firstName} ${user.lastName} [OS:ID=${user.id}]</h1>
        <table border="1" cellspacing="0" cellpadding="2" width="90%" >
        <tr>
            <th>Name</th> <td>${user.firstName} ${user.lastName}</td>
        </tr>
        <tr>
            <th>Created</th> <td><fmt:formatDate value="${user.date}" pattern="yyyy-MM-dd HH:mm"/></td>
        </tr>
        <tr>
            <th>Email</th> <td>${user.email}</td>
        </tr>
        <tr>
            <th>Address</th> <td>${user.street} ${user.zip} ${fn:toUpperCase(user.city)}</td>
        </tr>
        </table>
    </body>
    </html>

## Displaying validation errors with JSTL

    <c:if test="${!empty fieldErrors || !empty actionErrors}">
      <div class="red">
        <ul>
          <c:forEach items="${fieldErrors}" var="fieldError">
            <c:forEach items="${fieldError.value}" var="error">
              <li>${error}</li>
            </c:forEach>
          </c:forEach>
          <c:forEach items="${actionErrors}" var="actionError">
            <li>${actionError}</li>
          </c:forEach>
        </ul>
      </div>
    </c:if>