Apache Struts 2 Documentation > 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
Added by jriboe, last edited by Ted Husted on Sep 01, 2006 (view change) show comment hide comment
Comment:
Update nomenclature
View page history

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>