0

I'm having a hard time setting the classpath for a directory to a package of classes. I'm trying to run a jar file that takes a directory as a command line argument. The program uses the directory to access class files in a folder and uses reflection to explore the class fields and methods.

final File folder = new File(args[0]);
classList = dirParse.listFilesForFolder(folder); 

I then go through the classList, get the name of each class, and use the Class.forName() method to access the classes.

Class c = Class.forName(className);

For the line above to work, I have to set the classpath to the address of the directory containing the classes.

I can get the program to run just fine when I'm using a directory of classes that do not belong to a package like below:

java -cp "Explorer.jar:/Users/john/Desktop/TestClass/" explorer.ExplorerDemo /Users/john/Desktop/TestClass/

However, for the following line, monopoly is a package and the program throws a ClassNotFoundException after calling Class.forName(className)

java -cp "Explorer.jar:/Users/john/Desktop/Programming\ Project/Monopoly/build/classes/monopoly/"  explorer.ExplorerDemo /Users/john/Desktop/Programming\ Project/Monopoly/build/classes/monopoly/

For testing purposes, I tried adjusting `Class.forName() call to include the package name like below:

Class c = Class.forName("monopoly."+className);

However, this also throws ClassNotFoundException.

Brosef
  • 2,945
  • 6
  • 34
  • 69

1 Answers1

0

Class.forName is a shortcut to obtaining class information within the context of ClassLoader of the current class. Javadoc states that this is equivalent to

Class.forName("Foo", true, this.getClass().getClassLoader())

Provided that you class directory is supplied as runtime parameter and is not part of the original classpath, I would suggest you instantiating custom URLClassLoader instance that will be pointing to your directory.

Sample code:

public class ReflectionClassAnalysis {

    public static void main(String[] args) throws MalformedURLException, ClassNotFoundException {

        // URLClassLoader supports both directories and jar files
        Path directory = Paths.get("/some/directory/");
        Path jar = Paths.get("/some/binary.jar");

        // You may be interested in providing parent ClassLoader for your new instance
        // You can either use current class ClassLoader like
        ClassLoader contextClassLoader = ReflectionClassAnalysis.class.getClassLoader();
        // or current thread ClassLoader
        // ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();

        URLClassLoader myClassLoader = new URLClassLoader(
                new URL[]{
                    directory.toUri().toURL(),
                    jar.toUri().toURL()
                },
                contextClassLoader
        );

        // You may use ClassLoader directly to load class meta
        Class<?> externalClass = myClassLoader.loadClass("your.class.name");
        // or supply ClassLoader to forName method
        // Class.forName("your.class.name", true, myClassLoader);

        // Do your class analysis here
    }

}

For JAR with classpath instructions please refer to: Run a JAR file from the command line and specify classpath

Aleh Maksimovich
  • 2,622
  • 8
  • 19
  • So in your approach, you wouldn't even need to set the classpath? – Brosef Nov 22 '17 at 17:58
  • No. You will set it programmatically for new ClassLoader. – Aleh Maksimovich Nov 22 '17 at 17:59
  • I'm sure your approach works, but I'd like to figure out how to do this using the classpath. – Brosef Nov 22 '17 at 18:08
  • https://stackoverflow.com/questions/18413014/run-a-jar-file-from-the-command-line-and-specify-classpath – Aleh Maksimovich Nov 22 '17 at 18:10
  • What do you mean by "directory is a package"? – Aleh Maksimovich Nov 22 '17 at 18:14
  • if the classes in the directory I want to access have some package declaration at the top such as `package monopoly;` I get the `ClassNotFoundException`. If the class files have no package declaration the program works fine. – Brosef Nov 22 '17 at 18:17
  • Seems I get it. When you specify the classpath Java creates a URLClassLoader for you (very close to my example). The URLClassLoader expects the provided directory to be a root of your class folder structure. I don't believe you can have it different. The code internally does loading by substituting `.` with `/` in class name and looking for a `.class` file at this relative path on disk. – Aleh Maksimovich Nov 22 '17 at 18:17
  • so if my 'monopoly' folder contains my classes and all the classes have `package monopoly;`, then `Class c = Class.forName("monopoly/"+className);` should work if my classpath is `/Users/john/Desktop/Programming\ Project/Monopoly/build/classes/monopoly/`, right? – Brosef Nov 22 '17 at 18:24
  • If I got it right the classpath should be just `/Users/john/Desktop/Programming\ Project/Monopoly/build/classes/` – Aleh Maksimovich Nov 22 '17 at 18:31
  • I tried that. Got this error: `java.lang.ClassNotFoundException: monopoly/GameController` – Brosef Nov 22 '17 at 18:36