I have a program where I want the user to be able to choose a .java class file from the file system, and then have that class loaded into the program.
I'm using a JFileChooser to allow the user to select a file. Then, I tried converting that file to a URL, and using a URLClassLoader to load the class (as suggested by these answers).
The problem is that, when I want to use the loadClass()
method, I don't know the "full class name" of the class (e.g. java.lang.String
). So, I don't know how to make this method work. Is there a way to get this class name? Or is there another way to do this?
Here is a sample of my code:
// Open the file chooser
JFileChooser fileChooser = new JFileChooser();
fileChooser.showOpenDialog(null);
File obtainedFile = fileChooser.getSelectedFile();
// Create the class loader from the file
URL classPath = obtainedFile.toURI().toURL();
URLClassLoader loader = new URLClassLoader(new URL[] {classPath});
// Get the class from the loader
Class<?> theClassIWant = loader.loadClass("the file name"); // What do I put here??
Load a single class file is generally completely useless. Said class file isn't alone; it has more class files that are relevant. Even if you think 'nah, there is just one source file, do not worry about this', note that a single java file can easily generate multiple class files.
Thus, two options:
Use the usual mechanisms (META-INF/services
or META-INF/MANIFEST.MF
) to put some sort of class name in there so you know what to load. Then create a new classloader with the provided jar, load the manifest, figure out the main class, load that, and run it.
This is quite difficult - the problem is, to 'load' a class file you need to tell the loader what the fully qualified name is of that class before it is loaded. But how do you know the fully qualified name? You can surmise the class name from the file (not quite always true, but usually), but the package is a more difficult issue.
You can open the class file yourself as a binary stream and write a basic class file format parser to get the fully qualified class name. Easy for an experienced java programmer. Quite tricky for someone new to java (which I gather you are, if you think this is a good idea).
You can also use existing tools to do this, such as bytebuddy or asm.
Finally, you can try a spaghetti-at-the-wall method: Keep travelling up the directory until it works. You know it isn't working if exceptions occur.
For example, to load C:\MyDir\Whatever\com\foo\MyApp.class
, You first try creating a new classloader (see the API of URLClassLoader
which is part of core java) using as root dir C:\MyDir\Whatever\com\foo
, and then you ask it to load class MyApp
.
If that works, great (but usually trying to load package-less classes is simply a non-starter, you're not supposed to do that, the CL API probably doesn't support it, intentionally, there is no fixing that).
If it doesn't, instead try C:\MyDir\Whatever\com
, and load class foo.MyApp
. If that doesn't work, try C:\MyDir\Whatever
and load class com.foo.MyApp
, and so on.
The considerable advantage is, if there is another class sitting right next to MyApp.class
, and MyApp needs it, this will work fine.
You'll need to write a while loop (traversing the path structure using Paths.get
and p.getParent()
), catch the right exception, manipulate the path into the class name (using .replace
and +
), and, of course, create a class loader (URLClassLoader
), load classes with it (invoke loadClass
), and if you intend on running it, something like thatClass.getConstructor().newInstance()
and then thatClass.getMethod("someMethod", String.class, /* all the other args here */).invoke(theInstanceYouJustMade, "param1", /*all other params */)
to actually 'run' it, more to be found in the java.lang.reflect
package.