Question

I've got a really strange problem.

I'm using a URLClassLoader to dynamically import files from a directory. The code works fine if I use a literal string, and works fine if I use a variable to a literal string, but this isn't what I need.

package test;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

public class Test {
    public static void main(String[] args) {

        try {
            File subfolder = new File("C:\\temp\\");
            URL classUrl = subfolder.toURI().toURL();
            URL[] classUrls = { classUrl };
            URLClassLoader ucl = new URLClassLoader(classUrls);

            for (File f : subfolder.listFiles()) {

                String name = f.getName()
                        .substring(0, f.getName().lastIndexOf(".")).trim();
                if (name.equals("TestClass"))
                        System.out.println(name);
                try {
                    MyInterface de = (MyInterface) Class.forName("TestClass", true, ucl)
                            .newInstance();
                    de.printSomething();
                } catch (ClassNotFoundException e) {
                }

                ucl.close();

            }
        } catch (MalformedURLException e) {
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

What I need is to be able to do this:

MyInterface de = (MyInterface) Class.forName(name, true, ucl).newInstance();

But it's not working even though "name" is a valid String and does equal "TestClass".

EDIT: I get the error:

java.lang.ClassNotFoundException: TestClass
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Unknown Source)
    at test.Test.main(Test.java:25)

What's wrong?

Was it helpful?

Solution

I guess this is because of the ucl.close() inside the for loop. I added some test if there are directories: the following class works and instanciate itself if declared in the root package, and if eclipse is configure to generate .class file in "bin" directory:

public class Toto {

    public Toto(){
        // this writes "Toto" in the console if the class is well instanciated
        System.out.println("Toto");
    }

    public static void main(String[] args) {
        try {
            File subfolder = new File("bin");
            URL classUrl = subfolder.toURI().toURL();
            URL[] classUrls = { classUrl };
            URLClassLoader ucl = new URLClassLoader(classUrls);

            for (File f : subfolder.listFiles()) {
                String fileName= f.getName();
                int suffix = fileName.lastIndexOf('.');
                if(f.isDirectory() || suffix==-1){
                    continue;
                }
                String name = fileName.substring(0, suffix);
                try {
                    Class.forName(name, true, ucl).newInstance();

                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
            ucl.close();
        } catch (MalformedURLException e) {
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

OTHER TIPS

It appears your issue has to do with the fact that your class being loaded has a package. When Java loads these classes, it expects to find the directory structure related to that package. So, the following code works:

try {
        File subfolder = new File("/home/glen/TestClass");
        URL classUrl = subfolder.toURI().toURL();
        URL[] classUrls = { classUrl };
        URLClassLoader ucl = new URLClassLoader(classUrls);

        for (File f : subfolder.listFiles()[0].listFiles()) {

            System.out.println(f.getName());

            String name = f.getName()
                    .substring(0, f.getName().lastIndexOf(".")).trim();// "TestClass";
            if (name.equals("TestClass"))
                    System.out.println(name);
            try {
                MyInterface de = (MyInterface) Class.forName("test." + name, true, ucl)
                        .newInstance();
                de.printSomething();
            } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
            }

            ucl.close();

        }
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

Note how I am:

  1. Specifying the fully qualified class name, test.TestClass, in the Class.forName method
  2. Enumerating through the first subdirectory (in my case, the "test" directory) of the loading folder. The TestClass.class file is located in there. If I attempt to load directly through there, with or without specifying the package name, it fails.

In essence, the URLClassLoader requires a JAR-like directory structure, like test/TestClass.class, while having a URL root which contains the directory structure.

My working theory is that you are not just changing the name variable to a string literal, as when I do that it still works fine. Double check you're not changing anything else. Either way, I hope this points you in the right direction.

What's wrong?

The first thing that's wrong is that this isn't the real code. This code catches and ignores ClassNotFoundException:

} catch (ClassNotFoundException e) {
}

So it cannot possibly produce the output shown.

The second thing that's wrong is that when this is corrected, the code works as expected.

Cannot reproduce.

Clearly you weren't running the code you thought you were running. Java simply does not behave as claimed.

Instead of

MyInterface de = (MyInterface) Class.forName(name, true, ucl).newInstance();

use

MyInterface de = (MyInterface) Class.forName(name + "Class", true, ucl).newInstance();

works for me.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top