Search code examples
javaclassexceptionobjectclassloader

Custom class loader returns NoSuchMethodException in listFilesAndDirs() method call


I am trying to invoke the method listFilesAndDirs() of org.apache.commons.io.FileUtils using my own custom class loader. But it returns NoSuchMethodException.

Code used for method invoke.

MyLoader c=new MyLoader();
Class cls=c.loadClass("org.apache.commons.io.FileUtils");
//to display the available methods
Method m[] = cls.getDeclaredMethods();
for (int i = 0; i < m.length; i++)
  System.out.println(m[i].toString());

// to get a listFilesAndDirs method
Method me=cls.getMethod("listFilesAndDirs",new Class[] { File.class, IOFileFilter.class,
            IOFileFilter.class });

Code used for class loader

public class MyLoader extends ClassLoader {
private  String classPath;
public MyLoader()
{

}
private String jarFile = "D:/Project/lib/commons-io-2.4.jar";;          //Path to the jar file  
private Hashtable classes = new Hashtable(); //used to cache already defined classes  

public Class loadClass(String className) throws ClassNotFoundException {  

return findClass(className);  
}  

public Class findClass(String className) {  
//System.out.println(className+" is loaded by Custom class Loader");


byte classByte[];  
Class result = null;  

result = (Class) classes.get(className); //checks in cached classes  
if (result != null) {  
    return result;  
}  



try {
    JarFile jar = new JarFile(jarFile);  
    classPath=className.replaceAll("\\.", "/");
    JarEntry entry = jar.getJarEntry(classPath + ".class"); 
    if(entry==null)
    {
         return findSystemClass(className);  
    }
    else
    {

    InputStream is = jar.getInputStream(entry);  
    ByteArrayOutputStream byteStream = new ByteArrayOutputStream();  
    int nextValue = is.read();  
    while (-1 != nextValue) {  
        byteStream.write(nextValue);  
        nextValue = is.read();  
    }  

    classByte = byteStream.toByteArray(); 
    result = defineClass(className, classByte, 0, classByte.length, null);  
    classes.put(className, result);  
    return result;  
    }
} catch (Exception e) {  
    return null;  
}  


 } 
 }

call of cls.getDeclaredMethods() will return the method org.apache.commons.io.FileUtils.listFilesAndDirs(java.io.File,org.apache.commons.io.filefilter.IOFileFilter,org.apache.commons.io.filefilter.IOFileFilter)

but cls.getMethod("listFilesAndDirs",new Class[] { File.class, IOFileFilter.class, IOFileFilter.class }); returns the following error

java.lang.NoSuchMethodException: org.apache.commons.io.FileUtils.listFilesAndDirs(java.io.File, org.apache.commons.io.filefilter.IOFileFilter, org.apache.commons.io.filefilter.IOFileFilter) at java.lang.Class.getDeclaredMethod(Class.java:1937) at Sample.main(Sample.java:30)


Solution

  • So, getDeclaredMethods() displays that the class in question has the required method listFilesAndDirs(java.io.File,org.apache.commons.io.filefilter.IOFileFilter,org.apache.commons.io.filefilter.IOFileFilter),

    BUT

    the parameter types are different from what you pass into cls.getMethod("listFilesAndDirs",new Class[]{...}) even if it seems that they reference the proper classes.

    Your compiled code uses org.apache.commons.io.filefilter.IOFileFilter class from the context class loader while cls expects the same classes from the custom class loader.

    One way to make it work is to load IOFileFilter from the custom class loader too and then use that class as the second and third item when calling getMethod.

    Class ioFileFilterClass = c.loadClass( "org.apache.commons.io.filefilter.IOFileFilter" );
    Method me = cls.getMethod( "listFilesAndDirs", new Class[] { File.class, ioFileFilterClass, ioFileFilterClass} );
    

    It will solve this particular problem but you will have to write similar code each time you are trying to access classes and methods loaded by the custom class loader.