package org.springmodules.aop.framework; import java.lang.reflect.Method; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import ognl.Ognl; import ognl.OgnlException; import org.springframework.aop.AfterReturningAdvice; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.BeansException; import org.springframework.util.MethodInvoker; /** * <p>Touches configurable properties on the return value of a method invocation. * This approach is an alternative to the open session in view pattern. * * <p>If the return value is a single object the properties are touched on this * object. If the return value is an instance of java.util.Collection or Object[] * the properties are touched on every element. * * <p>If no properties are specified collections will still be iterated entirely. * * <p>Property values can be either property names or maps containing property names * as keys and lists containing property names as values. Maps entries represent bean * properties (the key of the map entries) that are collections. All elements of those collection will * be touched by the properties (the values of the map entries). This construct can * be cascaded. * * <p>OGNL expressions are also supported. These expression will be executed after the properties * have been executed on the return value of the method invocation. To support the evaluation of collections * you can use the <code>#returned</code> variable in the OGNL context which holds the return value. * * <p>To load all elements of a returned collection instance use OGNL's pseudo-property size: * * <pre> * #returned.size * </pre> * * @author Steven Devijver * @since 20-06-2005 * */ public class TouchingAfterReturningAdvice implements AfterReturningAdvice { private Object[] properties = null; private String[] ognlExpressions = null; /** * <p>The [properties] properties takes a list of property strings. * The return value of the method invocation will be touched with these * properties. * * @param properties the property strings */ public void setProperties(Object[] properties) { this.properties = properties; } /** * <p>The [ognl] property takes a list of OGNL expressions that will be * evaluated on the return value of the method invocation. * * @param ognlExpressions the OGNL expressions */ public void setOgnl(String[] ognlExpressions) { this.ognlExpressions = ognlExpressions; } public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { if (returnValue == null) { return; } if (returnValue instanceof Void) { return; } else if (returnValue instanceof Collection) { touch(((Collection)returnValue).toArray(), properties); } else if (returnValue instanceof Object[]) { touch((Object[])returnValue, properties); } else { touch(returnValue, properties); } touchOgnl(returnValue, ognlExpressions); } private static void touch(Object[] objects, Object[] properties) { for (int i = 0; objects != null && properties != null && i < objects.length; i++) { Object target = objects[i]; touch(target, properties); } } private static void touch(Object target, Object[] properties) { BeanWrapper beanWrapper = new BeanWrapperImpl(target); for (int x = 0; properties != null && x < properties.length; x++) { Object property = properties[x]; if (property instanceof String) { Object result = beanWrapper.getPropertyValue((String)property); if (result instanceof Collection) { ((Collection)result).size(); } } else if (property instanceof Map) { for (Iterator iter = ((Map)property).keySet().iterator(); iter.hasNext();) { Object key = iter.next(); Object tmpProperties = ((Map)property).get(key); if (!(key instanceof String)) { throw new IllegalArgumentException("Maps configured in the [properties] property should have a string key!"); } if (!(tmpProperties instanceof Collection)) { throw new IllegalArgumentException("Maps configured in the [properties] property should have a list value!"); } Object result = beanWrapper.getPropertyValue((String)key); if (result instanceof Collection) { touch(((Collection)result).toArray(), ((Collection)tmpProperties).toArray()); } else if (result instanceof Object[]) { touch((Object[])result, ((Collection)tmpProperties).toArray()); } else { touch(result, ((Collection)tmpProperties).toArray()); } } } else { throw new IllegalArgumentException("[properties] property should only contain string and map instances!"); } } } private static void touchOgnl(Object target, String[] ognlExpressions) { for (int i = 0; ognlExpressions != null && i < ognlExpressions.length; i++) { String ognlExpression = ognlExpressions[i]; try { Map context = new HashMap(); context.put("returned", target); Ognl.getValue(ognlExpression, context, target); } catch (OgnlException e) { throw new BeansException("Error occured while evaluating OGNL expression [" + ognlExpression + "]!", e) {}; } } } }