Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

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

Code Block
package com.whatever.interceptors;

import com.opensymphony.webwork.WebWorkStatics;
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 xwork.xml file and insert it
into your interceptor stack.

xwork.xml snippet

Code Block
<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

Code Block
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

Code Block
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

Code Block
<%@ 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

Code Block
<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>