|
@@ -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.
|
|
|
- *
|
|
|
+ *
|
|
|
* <p>
|
|
|
* This class mimics the behaviour of Spring's AbstractFallbackTransactionAttributeSource class.
|
|
|
* </p>
|
|
|
- *
|
|
|
+ *
|
|
|
* <p>
|
|
|
* 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 <tt>MethodDefinitionSource</tt>.
|
|
|
* </p>
|
|
|
- *
|
|
|
+ *
|
|
|
* @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 <code>null</code>)
|
|
|
- * @param targetClass the target class for this invocation (may be <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>)
|
|
|
* @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.
|
|
|
- *
|
|
|
+ *
|
|
|
* <p>
|
|
|
* Note that the {@link Method#getDeclaringClass()} may not equal the <code>targetClass</code>.
|
|
|
* 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 <code>null</code>)
|
|
|
* @param targetClass the target class for the invocation (may be <code>null</code>)
|
|
|
* @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.
|
|
|
- *
|
|
|
+ *
|
|
|
* <p>
|
|
|
* 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 <code>null</code>)
|
|
|
* @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 + "]";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
}
|