Home > FAQs > Cookbook > Using Struts and XWork with JSP 2.0 and JSTL 1.1 |
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.
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.
<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.
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
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.
<%@ 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>
<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>