|  | @@ -15,63 +15,59 @@
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  package org.springframework.security.intercept.method;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -import org.springframework.security.ConfigAttributeDefinition;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -import org.apache.commons.logging.Log;
 | 
	
		
			
				|  |  | -import org.apache.commons.logging.LogFactory;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  import java.lang.reflect.Method;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  import java.util.ArrayList;
 | 
	
		
			
				|  |  | +import java.util.Collection;
 | 
	
		
			
				|  |  | +import java.util.Collections;
 | 
	
		
			
				|  |  |  import java.util.HashMap;
 | 
	
		
			
				|  |  |  import java.util.Iterator;
 | 
	
		
			
				|  |  |  import java.util.List;
 | 
	
		
			
				|  |  |  import java.util.Map;
 | 
	
		
			
				|  |  | -import java.util.Collection;
 | 
	
		
			
				|  |  | -import java.util.Collections;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +import org.apache.commons.logging.Log;
 | 
	
		
			
				|  |  | +import org.apache.commons.logging.LogFactory;
 | 
	
		
			
				|  |  | +import org.springframework.beans.factory.BeanClassLoaderAware;
 | 
	
		
			
				|  |  | +import org.springframework.security.ConfigAttributeDefinition;
 | 
	
		
			
				|  |  | +import org.springframework.util.Assert;
 | 
	
		
			
				|  |  | +import org.springframework.util.ClassUtils;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /**
 | 
	
		
			
				|  |  | - * Stores a {@link ConfigAttributeDefinition} for each method signature defined in a bean context.<p>For
 | 
	
		
			
				|  |  | - * consistency with {@link MethodDefinitionAttributes} as well as support for
 | 
	
		
			
				|  |  | - * <code>MethodDefinitionSourceAdvisor</code>, this implementation will return a
 | 
	
		
			
				|  |  | - * <code>ConfigAttributeDefinition</code> containing all configuration attributes defined against:
 | 
	
		
			
				|  |  | - *  <ul>
 | 
	
		
			
				|  |  | - *      <li>The method-specific attributes defined for the intercepted method of the intercepted class.</li>
 | 
	
		
			
				|  |  | - *      <li>The method-specific attributes defined by any explicitly implemented interface if that interface
 | 
	
		
			
				|  |  | - *      contains a method signature matching that of the intercepted method.</li>
 | 
	
		
			
				|  |  | - *  </ul>
 | 
	
		
			
				|  |  | - *  </p>
 | 
	
		
			
				|  |  | - *  <p>In general you should therefore define the <b>interface method</b>s of your secure objects, not the
 | 
	
		
			
				|  |  | - * implementations. For example, define <code>com.company.Foo.findAll=ROLE_TEST</code> but not
 | 
	
		
			
				|  |  | - * <code>com.company.FooImpl.findAll=ROLE_TEST</code>.</p>
 | 
	
		
			
				|  |  | - *
 | 
	
		
			
				|  |  | + * Stores a {@link ConfigAttributeDefinition} for a method or class signature.
 | 
	
		
			
				|  |  | + * 
 | 
	
		
			
				|  |  | + * <p>
 | 
	
		
			
				|  |  | + * This class is the preferred implementation of {@link MethodDefinitionSource} for XML-based
 | 
	
		
			
				|  |  | + * definition of method security metadata. To assist in XML-based definition, wildcard support
 | 
	
		
			
				|  |  | + * is provided.
 | 
	
		
			
				|  |  | + * </p>
 | 
	
		
			
				|  |  | + * 
 | 
	
		
			
				|  |  |   * @author Ben Alex
 | 
	
		
			
				|  |  | - * @version $Id$
 | 
	
		
			
				|  |  | + * @version $Id: MethodDefinitionMap.java 2558 2008-01-30 15:43:40Z luke_t $
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  | -public class MethodDefinitionMap extends AbstractMethodDefinitionSource {
 | 
	
		
			
				|  |  | +public class MapBasedMethodDefinitionSource extends AbstractFallbackMethodDefinitionSource implements BeanClassLoaderAware {
 | 
	
		
			
				|  |  |      //~ Static fields/initializers =====================================================================================
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    private static final Log logger = LogFactory.getLog(MethodDefinitionMap.class);
 | 
	
		
			
				|  |  | +    private static final Log logger = LogFactory.getLog(MapBasedMethodDefinitionSource.class);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      //~ Instance fields ================================================================================================
 | 
	
		
			
				|  |  | +	private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    /** Map from Method to ApplicationDefinition */
 | 
	
		
			
				|  |  | +	/** Map from RegisteredMethod to ConfigAttributeDefinition */
 | 
	
		
			
				|  |  |      protected Map methodMap = new HashMap();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    /** Map from Method to name pattern used for registration */
 | 
	
		
			
				|  |  | +    /** Map from RegisteredMethod to name pattern used for registration */
 | 
	
		
			
				|  |  |      private Map nameMap = new HashMap();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      //~ Methods ========================================================================================================
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    public MethodDefinitionMap() {
 | 
	
		
			
				|  |  | +    public MapBasedMethodDefinitionSource() {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      /**
 | 
	
		
			
				|  |  | -     * Creates the MethodDefinitionMap from a
 | 
	
		
			
				|  |  | +     * Creates the MapBasedMethodDefinitionSource from a
 | 
	
		
			
				|  |  |       * @param methodMap map of method names to <tt>ConfigAttributeDefinition</tt>s.
 | 
	
		
			
				|  |  |       */
 | 
	
		
			
				|  |  | -    public MethodDefinitionMap(Map methodMap) {
 | 
	
		
			
				|  |  | +    public MapBasedMethodDefinitionSource(Map methodMap) {
 | 
	
		
			
				|  |  |          Iterator iterator = methodMap.entrySet().iterator();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          while (iterator.hasNext()) {
 | 
	
	
		
			
				|  | @@ -80,15 +76,44 @@ public class MethodDefinitionMap extends AbstractMethodDefinitionSource {
 | 
	
		
			
				|  |  |          }        
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	/**
 | 
	
		
			
				|  |  | +	 * Implementation does not support class-level attributes.
 | 
	
		
			
				|  |  | +	 */
 | 
	
		
			
				|  |  | +	protected ConfigAttributeDefinition findAttributes(Class clazz) {
 | 
	
		
			
				|  |  | +		return null;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/**
 | 
	
		
			
				|  |  | +	 * Will walk the method inheritance tree to find the most specific declaration applicable.
 | 
	
		
			
				|  |  | +	 */
 | 
	
		
			
				|  |  | +	protected ConfigAttributeDefinition findAttributes(Method method, Class targetClass) {
 | 
	
		
			
				|  |  | +		return findAttributesSpecifiedAgainst(method, targetClass);
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	
 | 
	
		
			
				|  |  | +	private ConfigAttributeDefinition findAttributesSpecifiedAgainst(Method method, Class clazz) {
 | 
	
		
			
				|  |  | +		RegisteredMethod registeredMethod = new RegisteredMethod(method, clazz);
 | 
	
		
			
				|  |  | +		if (methodMap.containsKey(registeredMethod)) {
 | 
	
		
			
				|  |  | +			return (ConfigAttributeDefinition) methodMap.get(registeredMethod);
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		// Search superclass
 | 
	
		
			
				|  |  | +		if (clazz.getSuperclass() != null) {
 | 
	
		
			
				|  |  | +			return findAttributesSpecifiedAgainst(method, clazz.getSuperclass());
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		return null;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      /**
 | 
	
		
			
				|  |  | -     * Add configuration attributes for a secure method. Method names can end or start with <code>*</code>
 | 
	
		
			
				|  |  | -     * for matching multiple methods.
 | 
	
		
			
				|  |  | +     * Add configuration attributes for a secure method.
 | 
	
		
			
				|  |  |       *
 | 
	
		
			
				|  |  |       * @param method the method to be secured
 | 
	
		
			
				|  |  |       * @param attr required authorities associated with the method
 | 
	
		
			
				|  |  |       */
 | 
	
		
			
				|  |  | -    public void addSecureMethod(Method method, ConfigAttributeDefinition attr) {
 | 
	
		
			
				|  |  | -        logger.info("Adding secure method [" + method + "] with attributes [" + attr + "]");
 | 
	
		
			
				|  |  | +    private void addSecureMethod(RegisteredMethod method, ConfigAttributeDefinition attr) {
 | 
	
		
			
				|  |  | +    	Assert.notNull(method, "RegisteredMethod required");
 | 
	
		
			
				|  |  | +    	Assert.notNull(attr, "Configuration attribute required");
 | 
	
		
			
				|  |  | +    	if (logger.isInfoEnabled()) {
 | 
	
		
			
				|  |  | +            logger.info("Adding secure method [" + method + "] with attributes [" + attr + "]");
 | 
	
		
			
				|  |  | +    	}
 | 
	
		
			
				|  |  |          this.methodMap.put(method, attr);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -96,47 +121,41 @@ public class MethodDefinitionMap extends AbstractMethodDefinitionSource {
 | 
	
		
			
				|  |  |       * Add configuration attributes for a secure method. Method names can end or start with <code>*</code>
 | 
	
		
			
				|  |  |       * for matching multiple methods.
 | 
	
		
			
				|  |  |       *
 | 
	
		
			
				|  |  | -     * @param name class and method name, separated by a dot
 | 
	
		
			
				|  |  | +     * @param name type and method name, separated by a dot
 | 
	
		
			
				|  |  |       * @param attr required authorities associated with the method
 | 
	
		
			
				|  |  | -     *
 | 
	
		
			
				|  |  | -     * @throws IllegalArgumentException DOCUMENT ME!
 | 
	
		
			
				|  |  |       */
 | 
	
		
			
				|  |  |      public void addSecureMethod(String name, ConfigAttributeDefinition attr) {
 | 
	
		
			
				|  |  | -        int lastDotIndex = name.lastIndexOf(".");
 | 
	
		
			
				|  |  | +    	int lastDotIndex = name.lastIndexOf(".");
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          if (lastDotIndex == -1) {
 | 
	
		
			
				|  |  |              throw new IllegalArgumentException("'" + name + "' is not a valid method name: format is FQN.methodName");
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        String className = name.substring(0, lastDotIndex);
 | 
	
		
			
				|  |  |          String methodName = name.substring(lastDotIndex + 1);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        try {
 | 
	
		
			
				|  |  | -            Class clazz = Class.forName(className, true, Thread.currentThread().getContextClassLoader());
 | 
	
		
			
				|  |  | -            addSecureMethod(clazz, methodName, attr);
 | 
	
		
			
				|  |  | -        } catch (ClassNotFoundException ex) {
 | 
	
		
			
				|  |  | -            throw new IllegalArgumentException("Class '" + className + "' not found");
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | +        Assert.hasText(methodName, "Method not found for '" + name + "'");
 | 
	
		
			
				|  |  | +        
 | 
	
		
			
				|  |  | +        String typeName = name.substring(0, lastDotIndex);
 | 
	
		
			
				|  |  | +        Class type = ClassUtils.resolveClassName(typeName, this.beanClassLoader);
 | 
	
		
			
				|  |  | +        
 | 
	
		
			
				|  |  | +        addSecureMethod(type, methodName, attr);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      /**
 | 
	
		
			
				|  |  | -     * Add configuration attributes for a secure method. Method names can end or start with <code>*</code>
 | 
	
		
			
				|  |  | +     * Add configuration attributes for a secure method. Mapped method names can end or start with <code>*</code>
 | 
	
		
			
				|  |  |       * for matching multiple methods.
 | 
	
		
			
				|  |  | -     *
 | 
	
		
			
				|  |  | -     * @param clazz target interface or class
 | 
	
		
			
				|  |  | -     * @param mappedName mapped method name
 | 
	
		
			
				|  |  | +     * 
 | 
	
		
			
				|  |  | +     * @param javaType target interface or class the security configuration attribute applies to
 | 
	
		
			
				|  |  | +     * @param mappedName mapped method name, which the javaType has declared or inherited
 | 
	
		
			
				|  |  |       * @param attr required authorities associated with the method
 | 
	
		
			
				|  |  | -     *
 | 
	
		
			
				|  |  | -     * @throws IllegalArgumentException DOCUMENT ME!
 | 
	
		
			
				|  |  |       */
 | 
	
		
			
				|  |  | -    public void addSecureMethod(Class clazz, String mappedName, ConfigAttributeDefinition attr) {
 | 
	
		
			
				|  |  | -        String name = clazz.getName() + '.' + mappedName;
 | 
	
		
			
				|  |  | +    public void addSecureMethod(Class javaType, String mappedName, ConfigAttributeDefinition attr) {
 | 
	
		
			
				|  |  | +        String name = javaType.getName() + '.' + mappedName;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          if (logger.isDebugEnabled()) {
 | 
	
		
			
				|  |  | -            logger.debug("Adding secure method [" + name + "] with attributes [" + attr + "]");
 | 
	
		
			
				|  |  | +            logger.debug("Request to add secure method [" + name + "] with attributes [" + attr + "]");
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        Method[] methods = clazz.getDeclaredMethods();
 | 
	
		
			
				|  |  | +        Method[] methods = javaType.getMethods();
 | 
	
		
			
				|  |  |          List matchingMethods = new ArrayList();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          for (int i = 0; i < methods.length; i++) {
 | 
	
	
		
			
				|  | @@ -146,13 +165,14 @@ public class MethodDefinitionMap extends AbstractMethodDefinitionSource {
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          if (matchingMethods.isEmpty()) {
 | 
	
		
			
				|  |  | -            throw new IllegalArgumentException("Couldn't find method '" + mappedName + "' on " + clazz);
 | 
	
		
			
				|  |  | +            throw new IllegalArgumentException("Couldn't find method '" + mappedName + "' on '" + javaType + "'");
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          // register all matching methods
 | 
	
		
			
				|  |  |          for (Iterator it = matchingMethods.iterator(); it.hasNext();) {
 | 
	
		
			
				|  |  |              Method method = (Method) it.next();
 | 
	
		
			
				|  |  | -            String regMethodName = (String) this.nameMap.get(method);
 | 
	
		
			
				|  |  | +            RegisteredMethod registeredMethod = new RegisteredMethod(method, javaType);
 | 
	
		
			
				|  |  | +            String regMethodName = (String) this.nameMap.get(registeredMethod);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              if ((regMethodName == null) || (!regMethodName.equals(name) && (regMethodName.length() <= name.length()))) {
 | 
	
		
			
				|  |  |                  // no already registered method name, or more specific
 | 
	
	
		
			
				|  | @@ -162,8 +182,8 @@ public class MethodDefinitionMap extends AbstractMethodDefinitionSource {
 | 
	
		
			
				|  |  |                          + "] is more specific than [" + regMethodName + "]");
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                this.nameMap.put(method, name);
 | 
	
		
			
				|  |  | -                addSecureMethod(method, attr);
 | 
	
		
			
				|  |  | +                this.nameMap.put(registeredMethod, name);
 | 
	
		
			
				|  |  | +                addSecureMethod(registeredMethod, attr);
 | 
	
		
			
				|  |  |              } else {
 | 
	
		
			
				|  |  |                  logger.debug("Keeping attributes for secure method [" + method + "]: current name [" + name
 | 
	
		
			
				|  |  |                      + "] is not more specific than [" + regMethodName + "]");
 | 
	
	
		
			
				|  | @@ -172,9 +192,7 @@ public class MethodDefinitionMap extends AbstractMethodDefinitionSource {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      /**
 | 
	
		
			
				|  |  | -     * Obtains the configuration attributes explicitly defined against this bean. This method will not return
 | 
	
		
			
				|  |  | -     * implicit configuration attributes that may be returned by {@link #lookupAttributes(Method)} as it does not have
 | 
	
		
			
				|  |  | -     * access to a method invocation at this time.
 | 
	
		
			
				|  |  | +     * Obtains the configuration attributes explicitly defined against this bean.
 | 
	
		
			
				|  |  |       *
 | 
	
		
			
				|  |  |       * @return the attributes explicitly defined against this bean
 | 
	
		
			
				|  |  |       */
 | 
	
	
		
			
				|  | @@ -182,17 +200,6 @@ public class MethodDefinitionMap extends AbstractMethodDefinitionSource {
 | 
	
		
			
				|  |  |          return Collections.unmodifiableCollection(methodMap.values());
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    /**
 | 
	
		
			
				|  |  | -     * Obtains the number of configuration attributes explicitly defined against this bean. This method will
 | 
	
		
			
				|  |  | -     * not return implicit configuration attributes that may be returned by {@link #lookupAttributes(Method)} as it
 | 
	
		
			
				|  |  | -     * does not have access to a method invocation at this time.
 | 
	
		
			
				|  |  | -     *
 | 
	
		
			
				|  |  | -     * @return the number of configuration attributes explicitly defined against this bean
 | 
	
		
			
				|  |  | -     */
 | 
	
		
			
				|  |  | -    public int getMethodMapSize() {
 | 
	
		
			
				|  |  | -        return this.methodMap.size();
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |      /**
 | 
	
		
			
				|  |  |       * Return if the given method name matches the mapped name. The default implementation checks for "xxx" and
 | 
	
		
			
				|  |  |       * "xxx" matches.
 | 
	
	
		
			
				|  | @@ -245,4 +252,55 @@ public class MethodDefinitionMap extends AbstractMethodDefinitionSource {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          attributes.addAll(toMerge.getConfigAttributes());
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	public void setBeanClassLoader(ClassLoader beanClassLoader) {
 | 
	
		
			
				|  |  | +		Assert.notNull(beanClassLoader, "Bean class loader required");
 | 
	
		
			
				|  |  | +		this.beanClassLoader = beanClassLoader;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/**
 | 
	
		
			
				|  |  | +	 * @return map size (for unit tests and diagnostics)
 | 
	
		
			
				|  |  | +	 */
 | 
	
		
			
				|  |  | +	public int getMethodMapSize() {
 | 
	
		
			
				|  |  | +		return methodMap.size();
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	
 | 
	
		
			
				|  |  | +	/**
 | 
	
		
			
				|  |  | +	 * Stores both the Java Method as well as the Class we obtained the Method from. This is necessary because Method only
 | 
	
		
			
				|  |  | +	 * provides us access to the declaring class. It doesn't provide a way for us to introspect which Class the Method
 | 
	
		
			
				|  |  | +	 * was registered against. If a given Class inherits and redeclares a method (ie calls super();) the registered Class
 | 
	
		
			
				|  |  | +	 * and delcaring Class are the same. If a given class merely inherits but does not redeclare a method, the registered
 | 
	
		
			
				|  |  | +	 * Class will be the Class we're invoking against and the Method will provide details of the declared class.
 | 
	
		
			
				|  |  | +	 */
 | 
	
		
			
				|  |  | +	private class RegisteredMethod {
 | 
	
		
			
				|  |  | +		private Method method;
 | 
	
		
			
				|  |  | +		private Class registeredJavaType;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		public RegisteredMethod(Method method, Class registeredJavaType) {
 | 
	
		
			
				|  |  | +			Assert.notNull(method, "Method required");
 | 
	
		
			
				|  |  | +			Assert.notNull(registeredJavaType, "Registered Java Type required");
 | 
	
		
			
				|  |  | +			this.method = method;
 | 
	
		
			
				|  |  | +			this.registeredJavaType = registeredJavaType;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		public boolean equals(Object obj) {
 | 
	
		
			
				|  |  | +			if (this == obj) {
 | 
	
		
			
				|  |  | +				return true;
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +			if (obj != null && obj instanceof RegisteredMethod) {
 | 
	
		
			
				|  |  | +				RegisteredMethod rhs = (RegisteredMethod) obj;
 | 
	
		
			
				|  |  | +				return method.equals(rhs.method) && registeredJavaType.equals(rhs.registeredJavaType);
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +			return false;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		public int hashCode() {
 | 
	
		
			
				|  |  | +			return method.hashCode() * registeredJavaType.hashCode();
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		public String toString() {
 | 
	
		
			
				|  |  | +			return "RegisteredMethod[" + registeredJavaType.getName() + "; " + method + "]";
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  |  }
 |