You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by rd...@apache.org on 2002/12/10 20:06:49 UTC

cvs commit: jakarta-commons/lang/src/test/org/apache/commons/lang/reflect MethodUtilsTestCase.java

rdonkin     2002/12/10 11:06:49

  Modified:    lang/src/java/org/apache/commons/lang/reflect
                        MethodUtils.java
               lang/src/test/org/apache/commons/lang/reflect
                        MethodUtilsTestCase.java
  Log:
  Consolidated methods.
  
  Revision  Changes    Path
  1.9       +111 -152  jakarta-commons/lang/src/java/org/apache/commons/lang/reflect/MethodUtils.java
  
  Index: MethodUtils.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/lang/src/java/org/apache/commons/lang/reflect/MethodUtils.java,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- MethodUtils.java	21 Nov 2002 19:38:51 -0000	1.8
  +++ MethodUtils.java	10 Dec 2002 19:06:49 -0000	1.9
  @@ -66,6 +66,7 @@
   
   import org.apache.commons.lang.ArrayUtils;
   import org.apache.commons.lang.StringUtils;
  +
   /**
    * <code>MethodUtils</code> contains utility methods for working for
    * methods by reflection.
  @@ -126,6 +127,21 @@
        * @throws IllegalArgumentException if the class or method name is null
        * @throws ReflectionException if an error occurs during reflection
        */
  +    public static Method getMethod(Class cls, String methodName, Class paramType) {
  +        Class[] paramTypes = {paramType};
  +        return getMethod(cls, methodName, paramTypes);
  +    }
  +    
  +    /**
  +     * Gets a Method by name. The method must be public.
  +     * Superclasses will be considered.
  +     *
  +     * @param cls  the class to reflect, must not be null
  +     * @param methodName  the field name to obtain
  +     * @return the Method object
  +     * @throws IllegalArgumentException if the class or method name is null
  +     * @throws ReflectionException if an error occurs during reflection
  +     */
       public static Method getMethod(Class cls, String methodName, Class[] paramTypes) {
           return getMethod(cls, methodName, paramTypes, false);
       }
  @@ -173,7 +189,24 @@
                   }
                   throw new NoSuchMethodException("The method '" + methodName + "' could not be found");
               } else {
  -                return cls.getMethod(methodName, paramTypes);
  +                // apply workarounds
  +                Method method = null;
  +                try {
  +                
  +                    method = cls.getMethod(methodName, paramTypes);
  +                    
  +                } catch(NoSuchMethodException e) {
  +                    // swallow
  +                }
  +                
  +                if (method == null) {
  +                    // use the same as beanutils for the moment
  +                    Method[] compatibles = getCompatibleMethods(cls, methodName, paramTypes);
  +                    if (compatibles.length > 0) {
  +                        method = compatibles[0];
  +                    }
  +                }
  +                return getMethod(method);
               }
       
           } catch (ReflectionException ex) {
  @@ -186,6 +219,50 @@
                   ex, "getting method", cls.getName(), null, methodName), ex);
           }
       }
  +
  +    /**
  +     * <p>Return an accessible method (that is, one that can be invoked via
  +     * reflection) that implements the specified Method.  If no such method
  +     * can be found, return <code>null</code>.</p>
  +     *
  +     * @param method The method that we wish to call
  +     */
  +    public static Method getMethod(Method method) {
  +        
  +        Method accessibleMethod = getAccessibleMethod(method);
  +        if (accessibleMethod == null) {
  +            try {
  +                //
  +                // XXX Default access superclass workaround
  +                //
  +                // When a public class has a default access superclass
  +                // with public methods, these methods are accessible.
  +                // Calling them from compiled code works fine.
  +                //
  +                // Unfortunately, using reflection to invoke these methods
  +                // seems to (wrongly) to prevent access even when the method
  +                // modifer is public.
  +                //
  +                // The following workaround solves the problem but will only
  +                // work from sufficiently privilages code. 
  +                //
  +                // Better workarounds would be greatfully accepted.
  +                //
  +                if (ReflectionUtils.isPublicScope(method)) {
  +                    method.setAccessible(true);
  +                    accessibleMethod = method;
  +                }
  +                
  +            } catch (SecurityException se) {
  +                // log but continue just in case the method.invoke works anyway
  +                log(
  +                "Cannot setAccessible on method. Therefore cannot use jvm access bug workaround.", 
  +                se);
  +            }
  +        }
  +        return (accessibleMethod);
  +
  +    }
       
       // -------------------------------------------------------------------------
       
  @@ -303,7 +380,7 @@
               args = ArrayUtils.EMPTY_OBJECT_ARRAY;
           }  
   
  -        Method method = getMatchingAccessibleMethod(
  +        Method method = getMethod(
                   object.getClass(),
                   methodName,
                   parameterTypes);
  @@ -330,62 +407,11 @@
           }
       }
   
  -    /**
  -     * <p>Return an accessible method (that is, one that can be invoked via
  -     * reflection) with given name and a single parameter.  If no such method
  -     * can be found, return <code>null</code>.
  -     * Basically, a convenience wrapper that constructs a <code>Class</code>
  -     * array for you.</p>
  -     *
  -     * @param clazz get method from this class
  -     * @param methodName get method with this name
  -     * @param parameterType taking this type of parameter
  -     */
  -    public static Method getAccessibleMethod(
  -            Class clazz,
  -            String methodName,
  -            Class parameterType) {
  -
  -        Class[] parameterTypes = {parameterType};
  -        return getAccessibleMethod(clazz, methodName, parameterTypes);
  -
  -    }
  -
  -
  -    /**
  -     * <p>Return an accessible method (that is, one that can be invoked via
  -     * reflection) with given name and parameters.  If no such method
  -     * can be found, return <code>null</code>.
  -     * This is just a convenient wrapper for
  -     * {@link #getAccessibleMethod(Method method)}.</p>
  -     *
  -     * @param clazz get method from this class
  -     * @param methodName get method with this name
  -     * @param parameterTypes with these parameters types
  -     */
  -    public static Method getAccessibleMethod(
  -            Class clazz,
  -            String methodName,
  -            Class[] parameterTypes) {
  -
  -        try {
  -            return getAccessibleMethod
  -                    (clazz.getMethod(methodName, parameterTypes));
  -        } catch (NoSuchMethodException e) {
  -            return (null);
  -        }
   
  -    }
   
  +    // -------------------------------------------------------- Private Methods
   
  -    /**
  -     * <p>Return an accessible method (that is, one that can be invoked via
  -     * reflection) that implements the specified Method.  If no such method
  -     * can be found, return <code>null</code>.</p>
  -     *
  -     * @param method The method that we wish to call
  -     */
  -    public static Method getAccessibleMethod(Method method) {
  +    private static Method getAccessibleMethod(Method method) {
   
           // Make sure we have a method to check
           if (method == null) {
  @@ -394,14 +420,20 @@
   
           // If the requested method is not public we cannot call it
           if (!Modifier.isPublic(method.getModifiers())) {
  +            log("Method is not public");
               return (null);
           }
   
           // If the declaring class is public, we are done
           Class clazz = method.getDeclaringClass();
           if (Modifier.isPublic(clazz.getModifiers())) {
  +            log("Class is public");
               return (method);
           }
  +        
  +        if (debug) {
  +            log("Method is in non-public class " + clazz);
  +        }
   
           // Check the implemented interfaces and subinterfaces
           String methodName = method.getName();
  @@ -410,13 +442,12 @@
                   getAccessibleMethodFromInterfaceNest(clazz,
                           method.getName(),
                           method.getParameterTypes());
  +
           return (method);
   
       }
   
   
  -    // -------------------------------------------------------- Private Methods
  -
       /**
        * <p>Return an accessible method (that is, one that can be invoked via
        * reflection) that implements the specified method, by scanning through
  @@ -433,7 +464,9 @@
        */
       private static Method getAccessibleMethodFromInterfaceNest
               (Class clazz, String methodName, Class parameterTypes[]) {
  -
  +        if (debug) {
  +            log("Finding accessible method " + methodName + " from interface nest");
  +        }
           Method method = null;
   
           // Search up the superclass chain
  @@ -470,38 +503,18 @@
           }
   
           // If we found a method return it
  -        if (method != null)
  +        if (method != null) {
  +            if (debug) {
  +                log("Found method in class " + method.getDeclaringClass());
  +            }
               return (method);
  -
  +        }
           // We did not find anything
           return (null);
   
  -    }
  -
  -
  -    /**
  -     * <p>Find an accessible method that matches the given name and has compatible parameters.
  -     * Compatible parameters mean that every method parameter is assignable from 
  -     * the given parameters.
  -     * In other words, it finds a method with the given name 
  -     * that will take the parameters given.<p>
  -     *
  -     * <p>This method is slightly undeterminstic since it loops 
  -     * through methods names and return the first matching method.</p>
  -     * 
  -     * <p>This method is used by 
  -     * {@link 
  -     * #invokeMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}.
  -     *
  -     * <p>This method can match primitive parameter by passing in wrapper classes.
  -     * For example, a <code>Boolean</code> will match a primitive <code>boolean</code>
  -     * parameter.
  -     *
  -     * @param clazz find method in this class
  -     * @param methodName find method with this name
  -     * @param parameterTypes find method with compatible parameters 
  -     */
  -    private static Method getMatchingAccessibleMethod(
  +    } 
  +    
  +    private static Method[] getCompatibleMethods(
                                                   Class clazz,
                                                   String methodName,
                                                   Class[] parameterTypes) {
  @@ -510,90 +523,36 @@
               log("Matching name=" + methodName + " on " + clazz);
           }
           
  -        // see if we can find the method directly
  -        // most of the time this works and it's much faster
  -        try {
  -            Method method = clazz.getMethod(methodName, parameterTypes);
  -            if (debug) {
  -                log("Found straight match: " + method);
  -                log("isPublic:" + Modifier.isPublic(method.getModifiers()));
  -            }
  -            
  -            try {
  -                //
  -                // XXX Default access superclass workaround
  -                //
  -                // When a public class has a default access superclass
  -                // with public methods, these methods are accessible.
  -                // Calling them from compiled code works fine.
  -                //
  -                // Unfortunately, using reflection to invoke these methods
  -                // seems to (wrongly) to prevent access even when the method
  -                // modifer is public.
  -                //
  -                // The following workaround solves the problem but will only
  -                // work from sufficiently privilages code. 
  -                //
  -                // Better workarounds would be greatfully accepted.
  -                //
  -                method.setAccessible(true);
  -                
  -            } catch (SecurityException se) {
  -                // log but continue just in case the method.invoke works anyway
  -                log(
  -                "Cannot setAccessible on method. Therefore cannot use jvm access bug workaround.", 
  -                se);
  -            }
  -            return method;
  -            
  -        } catch (NoSuchMethodException e) { /* SWALLOW */ }
  -        
           // search through all methods 
           int paramSize = parameterTypes.length;
           Method[] methods = clazz.getMethods();
  +        ArrayList compatibles = new ArrayList(methods.length);
           for (int i = 0, size = methods.length; i < size ; i++) {
  +            if (debug) {
  +                log("Checking: " + methods[i]);
  +            }     
               if (methods[i].getName().equals(methodName)) {	
                   // log some trace information
                   if (debug) {
  -                    log("Found matching name:");
  -                    log(methods[i]);
  +                    log("Found matching name:" + methods[i]);
                   }                
                   
                   // compare parameters
                   Class[] methodsParams = methods[i].getParameterTypes();
                   if (ReflectionUtils.isCompatible(parameterTypes, methodsParams)) {
                       // get accessible version of method
  -                    Method method = getAccessibleMethod(methods[i]);
  +                    Method method = getMethod(methods[i]);
                       if (method != null) {
  -                        if (debug) {
  -                            log(method + " accessible version of " 
  -                                        + methods[i]);
  -                        }
  -                        try {
  -                            //
  -                            // XXX Default access superclass workaround
  -                            // (See above for more details.)
  -                            //
  -                            method.setAccessible(true);
  -                            
  -                        } catch (SecurityException se) {
  -                            // log but continue just in case the method.invoke works anyway
  -                            log(
  -                            "Cannot setAccessible on method. Therefore cannot use jvm access bug workaround.", 
  -                            se);
  -                        }
  -                        return method;
  +                        compatibles.add(method);
  +                    } else {
  +                        log("Couldn't find accessible method for: " + methods[i]);
                       }
  -                    
  -                    log("Couldn't find accessible method.");
                   }
               }
           }
           
  -        // didn't find a match
  -        log("No match found.");
  -        return null;                                        
  -    }    
  +        return (Method[]) compatibles.toArray(new Method[compatibles.size()]); 
  +    }  
       
       private static void log(Object o) {
           if (debug) {
  
  
  
  1.4       +6 -6      jakarta-commons/lang/src/test/org/apache/commons/lang/reflect/MethodUtilsTestCase.java
  
  Index: MethodUtilsTestCase.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/lang/src/test/org/apache/commons/lang/reflect/MethodUtilsTestCase.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- MethodUtilsTestCase.java	21 Nov 2002 18:53:32 -0000	1.3
  +++ MethodUtilsTestCase.java	10 Dec 2002 19:06:49 -0000	1.4
  @@ -129,7 +129,7 @@
   
           // easy bit first - find a public method
           // METHOD ONE
  -        Method method = MethodUtils.getAccessibleMethod
  +        Method method = MethodUtils.getMethod
                   (TestBean.class, "setStringProperty", String.class);
   
           // check that we've found one that matches
  @@ -141,7 +141,7 @@
   
           // trickier this one - find a method in a direct interface
           // METHOD TWO
  -        method = MethodUtils.getAccessibleMethod
  +        method = MethodUtils.getMethod
                   (privateBeanFactory.create().getClass(),
                           "methodBar",
                           String.class);
  @@ -155,7 +155,7 @@
   
           // trickier this one - find a method in a indirect interface
           // METHOD THREE
  -        method = MethodUtils.getAccessibleMethod
  +        method = MethodUtils.getMethod
                   (privateBeanFactory.createSubclass().getClass(),
                           "methodBaz",
                           String.class);
  @@ -478,7 +478,7 @@
           try {
   
               // Acquire the methods we need
  -            Method currentCounterMethod = MethodUtils.getAccessibleMethod
  +            Method currentCounterMethod = MethodUtils.getMethod
                   (TestBean.class, "currentCounter",
                    new Class[0]);
               assertNotNull("currentCounterMethod exists",
  @@ -493,7 +493,7 @@
                          Modifier.isPublic(currentCounterMethod.getModifiers()));
               assertTrue("currentCounterMethod static",
                          Modifier.isStatic(currentCounterMethod.getModifiers()));
  -            Method incrementCounterMethod1 = MethodUtils.getAccessibleMethod
  +            Method incrementCounterMethod1 = MethodUtils.getMethod
                   (TestBean.class, "incrementCounter",
                    new Class[0]);
               assertNotNull("incrementCounterMethod1 exists",
  @@ -508,7 +508,7 @@
                          Modifier.isPublic(incrementCounterMethod1.getModifiers()));
               assertTrue("incrementCounterMethod1 static",
                          Modifier.isStatic(incrementCounterMethod1.getModifiers()));
  -            Method incrementCounterMethod2 = MethodUtils.getAccessibleMethod
  +            Method incrementCounterMethod2 = MethodUtils.getMethod
                   (TestBean.class, "incrementCounter",
                    new Class[] { Integer.TYPE });
               assertNotNull("incrementCounterMethod2 exists",
  
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>