diff --git a/core/src/main/java/org/springframework/security/intercept/method/AbstractFallbackMethodDefinitionSource.java b/core/src/main/java/org/springframework/security/intercept/method/AbstractFallbackMethodDefinitionSource.java index f7d573df11..237617ff8f 100644 --- a/core/src/main/java/org/springframework/security/intercept/method/AbstractFallbackMethodDefinitionSource.java +++ b/core/src/main/java/org/springframework/security/intercept/method/AbstractFallbackMethodDefinitionSource.java @@ -18,11 +18,11 @@ import org.springframework.util.ObjectUtils; * Abstract implementation of {@link MethodDefinitionSource} that supports both Spring AOP and AspectJ and * caches configuration attribute resolution from: 1. specific target method; 2. target class; 3. declaring method; * 4. declaring class/interface. - * + * *

* This class mimics the behaviour of Spring's AbstractFallbackTransactionAttributeSource class. *

- * + * *

* Note that this class cannot extract security metadata where that metadata is expressed by way of * a target method/class (ie #1 and #2 above) AND the target method/class is encapsulated in another @@ -31,7 +31,7 @@ import org.springframework.util.ObjectUtils; * another proxy), move the metadata to declared methods or interfaces the proxy implements, or provide * your own replacement MethodDefinitionSource. *

- * + * * @author Ben Alex * @version $Id$ * @since 2.0 @@ -39,15 +39,16 @@ import org.springframework.util.ObjectUtils; public abstract class AbstractFallbackMethodDefinitionSource implements MethodDefinitionSource { private static final Log logger = LogFactory.getLog(AbstractFallbackMethodDefinitionSource.class); - private final static Object NULL_CONFIG_ATTRIBUTE = new Object(); + private final static Object NULL_CONFIG_ATTRIBUTE = new Object(); private final Map attributeCache = new HashMap(); public ConfigAttributeDefinition getAttributes(Object object) throws IllegalArgumentException { Assert.notNull(object, "Object cannot be null"); if (object instanceof MethodInvocation) { - MethodInvocation mi = (MethodInvocation) object; - return getAttributes(mi.getMethod(), mi.getThis().getClass()); + MethodInvocation mi = (MethodInvocation) object; + Object target = mi.getThis(); + return getAttributes(mi.getMethod(), target == null ? null : target.getClass()); } if (object instanceof JoinPoint) { @@ -59,143 +60,143 @@ public abstract class AbstractFallbackMethodDefinitionSource implements MethodDe Method method = ClassUtils.getMethodIfAvailable(declaringType, targetMethodName, types); Assert.notNull(method, "Could not obtain target method from JoinPoint: '"+ jp + "'"); - + return getAttributes(method, targetClass); } throw new IllegalArgumentException("Object must be a MethodInvocation or JoinPoint"); } - + public final boolean supports(Class clazz) { return (MethodInvocation.class.isAssignableFrom(clazz) || JoinPoint.class.isAssignableFrom(clazz)); } - + public ConfigAttributeDefinition getAttributes(Method method, Class targetClass) { - // First, see if we have a cached value. - Object cacheKey = new DefaultCacheKey(method, targetClass); - synchronized (this.attributeCache) { - Object cached = this.attributeCache.get(cacheKey); - if (cached != null) { - // Value will either be canonical value indicating there is no config attribute, - // or an actual config attribute. - if (cached == NULL_CONFIG_ATTRIBUTE) { - return null; - } - else { - return (ConfigAttributeDefinition) cached; - } - } - else { - // We need to work it out. - ConfigAttributeDefinition cfgAtt = computeAttributes(method, targetClass); - // Put it in the cache. - if (cfgAtt == null) { - this.attributeCache.put(cacheKey, NULL_CONFIG_ATTRIBUTE); - } - else { - if (logger.isDebugEnabled()) { - logger.debug("Adding security method [" + cacheKey + "] with attribute [" + cfgAtt + "]"); - } - this.attributeCache.put(cacheKey, cfgAtt); - } - return cfgAtt; - } - } + // First, see if we have a cached value. + Object cacheKey = new DefaultCacheKey(method, targetClass); + synchronized (this.attributeCache) { + Object cached = this.attributeCache.get(cacheKey); + if (cached != null) { + // Value will either be canonical value indicating there is no config attribute, + // or an actual config attribute. + if (cached == NULL_CONFIG_ATTRIBUTE) { + return null; + } + else { + return (ConfigAttributeDefinition) cached; + } + } + else { + // We need to work it out. + ConfigAttributeDefinition cfgAtt = computeAttributes(method, targetClass); + // Put it in the cache. + if (cfgAtt == null) { + this.attributeCache.put(cacheKey, NULL_CONFIG_ATTRIBUTE); + } + else { + if (logger.isDebugEnabled()) { + logger.debug("Adding security method [" + cacheKey + "] with attribute [" + cfgAtt + "]"); + } + this.attributeCache.put(cacheKey, cfgAtt); + } + return cfgAtt; + } + } } - + /** - * - * @param method the method for the current invocation (never null) - * @param targetClass the target class for this invocation (may be null) + * + * @param method the method for the current invocation (never null) + * @param targetClass the target class for this invocation (may be null) * @return */ private ConfigAttributeDefinition computeAttributes(Method method, Class targetClass) { - // The method may be on an interface, but we need attributes from the target class. - // If the target class is null, the method will be unchanged. - Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass); - // First try is the method in the target class. - ConfigAttributeDefinition attr = findAttributes(specificMethod, targetClass); - if (attr != null) { - return attr; - } - - // Second try is the config attribute on the target class. - attr = findAttributes(specificMethod.getDeclaringClass()); - if (attr != null) { - return attr; - } - - if (specificMethod != method) { - // Fallback is to look at the original method. - attr = findAttributes(method, method.getDeclaringClass()); - if (attr != null) { - return attr; - } - // Last fallback is the class of the original method. - return findAttributes(method.getDeclaringClass()); - } - return null; + // The method may be on an interface, but we need attributes from the target class. + // If the target class is null, the method will be unchanged. + Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass); + // First try is the method in the target class. + ConfigAttributeDefinition attr = findAttributes(specificMethod, targetClass); + if (attr != null) { + return attr; + } + + // Second try is the config attribute on the target class. + attr = findAttributes(specificMethod.getDeclaringClass()); + if (attr != null) { + return attr; + } + + if (specificMethod != method || targetClass == null) { + // Fallback is to look at the original method. + attr = findAttributes(method, method.getDeclaringClass()); + if (attr != null) { + return attr; + } + // Last fallback is the class of the original method. + return findAttributes(method.getDeclaringClass()); + } + return null; } - + /** * Obtains the security metadata applicable to the specified method invocation. - * + * *

* Note that the {@link Method#getDeclaringClass()} may not equal the targetClass. * Both parameters are provided to assist subclasses which may wish to provide advanced * capabilities related to method metadata being "registered" against a method even if the * target class does not declare the method (i.e. the subclass may only inherit the method). - * + * * @param method the method for the current invocation (never null) * @param targetClass the target class for the invocation (may be null) * @return the security metadata (or null if no metadata applies) */ protected abstract ConfigAttributeDefinition findAttributes(Method method, Class targetClass); - + /** * Obtains the security metadata registered against the specified class. - * + * *

* Subclasses should only return metadata expressed at a class level. Subclasses should NOT * aggregate metadata for each method registered against a class, as the abstract superclass * will separate invoke {@link #findAttributes(Method, Class)} for individual methods as - * appropriate. - * + * appropriate. + * * @param clazz the target class for the invocation (never null) * @return the security metadata (or null if no metadata applies) */ protected abstract ConfigAttributeDefinition findAttributes(Class clazz); - - private static class DefaultCacheKey { - - private final Method method; - private final Class targetClass; - - public DefaultCacheKey(Method method, Class targetClass) { - this.method = method; - this.targetClass = targetClass; - } - - public boolean equals(Object other) { - if (this == other) { - return true; - } - if (!(other instanceof DefaultCacheKey)) { - return false; - } - DefaultCacheKey otherKey = (DefaultCacheKey) other; - return (this.method.equals(otherKey.method) && - ObjectUtils.nullSafeEquals(this.targetClass, otherKey.targetClass)); - } - - public int hashCode() { - return this.method.hashCode() * 21 + (this.targetClass != null ? this.targetClass.hashCode() : 0); - } - - public String toString() { - return "CacheKey[" + (targetClass == null ? "-" : targetClass.getName()) + "; " + method + "]"; - } - } - + + private static class DefaultCacheKey { + + private final Method method; + private final Class targetClass; + + public DefaultCacheKey(Method method, Class targetClass) { + this.method = method; + this.targetClass = targetClass; + } + + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof DefaultCacheKey)) { + return false; + } + DefaultCacheKey otherKey = (DefaultCacheKey) other; + return (this.method.equals(otherKey.method) && + ObjectUtils.nullSafeEquals(this.targetClass, otherKey.targetClass)); + } + + public int hashCode() { + return this.method.hashCode() * 21 + (this.targetClass != null ? this.targetClass.hashCode() : 0); + } + + public String toString() { + return "CacheKey[" + (targetClass == null ? "-" : targetClass.getName()) + "; " + method + "]"; + } + } + } diff --git a/core/src/main/java/org/springframework/security/intercept/method/MapBasedMethodDefinitionSource.java b/core/src/main/java/org/springframework/security/intercept/method/MapBasedMethodDefinitionSource.java index 9a03c97e58..48f60260e0 100644 --- a/core/src/main/java/org/springframework/security/intercept/method/MapBasedMethodDefinitionSource.java +++ b/core/src/main/java/org/springframework/security/intercept/method/MapBasedMethodDefinitionSource.java @@ -88,6 +88,10 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini * Will walk the method inheritance tree to find the most specific declaration applicable. */ protected ConfigAttributeDefinition findAttributes(Method method, Class targetClass) { + if (targetClass == null) { + return null; + } + return findAttributesSpecifiedAgainst(method, targetClass); } @@ -179,9 +183,9 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini /** * Adds configuration attributes for a specific method, for example where the method has been - * matched using a pointcut expression. If a match already exists in the map for the method, then - * the existing match will be retained, so that if this method is called for a more general pointcut - * it will not override a more specific one which has already been added. This + * matched using a pointcut expression. If a match already exists in the map for the method, then + * the existing match will be retained, so that if this method is called for a more general pointcut + * it will not override a more specific one which has already been added. This */ public void addSecureMethod(Class javaType, Method method, ConfigAttributeDefinition attr) { RegisteredMethod key = new RegisteredMethod(method, javaType);