SEC-936: NPE in AbstractFallbackMethodDefinitionSource

http://jira.springframework.org/browse/SEC-936. Changed to check if the value of MethodInvocation.getThis() is null to prevent NPE. MapBasedMethodDefinitionSource now ignores calls to findAttributes() with a null target class (all its entries require a class) and the fallback option in AbstractFallbackMethodDefinitionSource is used if the targetClass is null (i.e. Method.getDeclaringClass() will be used as the Class)
This commit is contained in:
Luke Taylor 2008-08-16 02:31:36 +00:00
parent 6a68a2531c
commit 3bf5e406b7
2 changed files with 108 additions and 103 deletions

View File

@ -39,15 +39,16 @@ import org.springframework.util.ObjectUtils;
public abstract class AbstractFallbackMethodDefinitionSource implements MethodDefinitionSource { public abstract class AbstractFallbackMethodDefinitionSource implements MethodDefinitionSource {
private static final Log logger = LogFactory.getLog(AbstractFallbackMethodDefinitionSource.class); 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(); private final Map attributeCache = new HashMap();
public ConfigAttributeDefinition getAttributes(Object object) throws IllegalArgumentException { public ConfigAttributeDefinition getAttributes(Object object) throws IllegalArgumentException {
Assert.notNull(object, "Object cannot be null"); Assert.notNull(object, "Object cannot be null");
if (object instanceof MethodInvocation) { if (object instanceof MethodInvocation) {
MethodInvocation mi = (MethodInvocation) object; MethodInvocation mi = (MethodInvocation) object;
return getAttributes(mi.getMethod(), mi.getThis().getClass()); Object target = mi.getThis();
return getAttributes(mi.getMethod(), target == null ? null : target.getClass());
} }
if (object instanceof JoinPoint) { if (object instanceof JoinPoint) {
@ -71,70 +72,70 @@ public abstract class AbstractFallbackMethodDefinitionSource implements MethodDe
} }
public ConfigAttributeDefinition getAttributes(Method method, Class targetClass) { public ConfigAttributeDefinition getAttributes(Method method, Class targetClass) {
// First, see if we have a cached value. // First, see if we have a cached value.
Object cacheKey = new DefaultCacheKey(method, targetClass); Object cacheKey = new DefaultCacheKey(method, targetClass);
synchronized (this.attributeCache) { synchronized (this.attributeCache) {
Object cached = this.attributeCache.get(cacheKey); Object cached = this.attributeCache.get(cacheKey);
if (cached != null) { if (cached != null) {
// Value will either be canonical value indicating there is no config attribute, // Value will either be canonical value indicating there is no config attribute,
// or an actual config attribute. // or an actual config attribute.
if (cached == NULL_CONFIG_ATTRIBUTE) { if (cached == NULL_CONFIG_ATTRIBUTE) {
return null; return null;
} }
else { else {
return (ConfigAttributeDefinition) cached; return (ConfigAttributeDefinition) cached;
} }
} }
else { else {
// We need to work it out. // We need to work it out.
ConfigAttributeDefinition cfgAtt = computeAttributes(method, targetClass); ConfigAttributeDefinition cfgAtt = computeAttributes(method, targetClass);
// Put it in the cache. // Put it in the cache.
if (cfgAtt == null) { if (cfgAtt == null) {
this.attributeCache.put(cacheKey, NULL_CONFIG_ATTRIBUTE); this.attributeCache.put(cacheKey, NULL_CONFIG_ATTRIBUTE);
} }
else { else {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Adding security method [" + cacheKey + "] with attribute [" + cfgAtt + "]"); logger.debug("Adding security method [" + cacheKey + "] with attribute [" + cfgAtt + "]");
} }
this.attributeCache.put(cacheKey, cfgAtt); this.attributeCache.put(cacheKey, cfgAtt);
} }
return cfgAtt; return cfgAtt;
} }
} }
} }
/** /**
* *
* @param method the method for the current invocation (never <code>null</code>) * @param method the method for the current invocation (never <code>null</code>)
* @param targetClass the target class for this invocation (may be <code>null</code>) * @param targetClass the target class for this invocation (may be <code>null</code>)
* @return * @return
*/ */
private ConfigAttributeDefinition computeAttributes(Method method, Class targetClass) { private ConfigAttributeDefinition computeAttributes(Method method, Class targetClass) {
// The method may be on an interface, but we need attributes from the target class. // 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. // If the target class is null, the method will be unchanged.
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass); Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
// First try is the method in the target class. // First try is the method in the target class.
ConfigAttributeDefinition attr = findAttributes(specificMethod, targetClass); ConfigAttributeDefinition attr = findAttributes(specificMethod, targetClass);
if (attr != null) { if (attr != null) {
return attr; return attr;
} }
// Second try is the config attribute on the target class. // Second try is the config attribute on the target class.
attr = findAttributes(specificMethod.getDeclaringClass()); attr = findAttributes(specificMethod.getDeclaringClass());
if (attr != null) { if (attr != null) {
return attr; return attr;
} }
if (specificMethod != method) { if (specificMethod != method || targetClass == null) {
// Fallback is to look at the original method. // Fallback is to look at the original method.
attr = findAttributes(method, method.getDeclaringClass()); attr = findAttributes(method, method.getDeclaringClass());
if (attr != null) { if (attr != null) {
return attr; return attr;
} }
// Last fallback is the class of the original method. // Last fallback is the class of the original method.
return findAttributes(method.getDeclaringClass()); return findAttributes(method.getDeclaringClass());
} }
return null; return null;
} }
@ -167,35 +168,35 @@ public abstract class AbstractFallbackMethodDefinitionSource implements MethodDe
*/ */
protected abstract ConfigAttributeDefinition findAttributes(Class clazz); protected abstract ConfigAttributeDefinition findAttributes(Class clazz);
private static class DefaultCacheKey { private static class DefaultCacheKey {
private final Method method; private final Method method;
private final Class targetClass; private final Class targetClass;
public DefaultCacheKey(Method method, Class targetClass) { public DefaultCacheKey(Method method, Class targetClass) {
this.method = method; this.method = method;
this.targetClass = targetClass; this.targetClass = targetClass;
} }
public boolean equals(Object other) { public boolean equals(Object other) {
if (this == other) { if (this == other) {
return true; return true;
} }
if (!(other instanceof DefaultCacheKey)) { if (!(other instanceof DefaultCacheKey)) {
return false; return false;
} }
DefaultCacheKey otherKey = (DefaultCacheKey) other; DefaultCacheKey otherKey = (DefaultCacheKey) other;
return (this.method.equals(otherKey.method) && return (this.method.equals(otherKey.method) &&
ObjectUtils.nullSafeEquals(this.targetClass, otherKey.targetClass)); ObjectUtils.nullSafeEquals(this.targetClass, otherKey.targetClass));
} }
public int hashCode() { public int hashCode() {
return this.method.hashCode() * 21 + (this.targetClass != null ? this.targetClass.hashCode() : 0); return this.method.hashCode() * 21 + (this.targetClass != null ? this.targetClass.hashCode() : 0);
} }
public String toString() { public String toString() {
return "CacheKey[" + (targetClass == null ? "-" : targetClass.getName()) + "; " + method + "]"; return "CacheKey[" + (targetClass == null ? "-" : targetClass.getName()) + "; " + method + "]";
} }
} }
} }

View File

@ -88,6 +88,10 @@ public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefini
* Will walk the method inheritance tree to find the most specific declaration applicable. * Will walk the method inheritance tree to find the most specific declaration applicable.
*/ */
protected ConfigAttributeDefinition findAttributes(Method method, Class targetClass) { protected ConfigAttributeDefinition findAttributes(Method method, Class targetClass) {
if (targetClass == null) {
return null;
}
return findAttributesSpecifiedAgainst(method, targetClass); return findAttributesSpecifiedAgainst(method, targetClass);
} }