0

I am writing an application that will load Java scripts. I currently have a GUI which utilizes a JFileChooser to allow the user to select a script from their machine. The script file can be anywhere. It is not on the classpath. Having only a File object to represent that script file, how can I obtain a Class representation of it?

I know that to load a class you need its binary name, so in.this.format. However, the problem with that is I don't know how the script writer may have packaged it. For example, he/she may have, while developing it, put the script file in the package foo.bar. After I download this script and place it in my documents (i.e., not in foo/bar), I can't load the script without knowing that it was packaged in foo.bar. If the class name is Test and I try to create a URLClassLoader pointing to the script file by doing new URLClassLoader(new URL[] { new URL(scriptFile.toURI().toURL()) }) and I do classLoader.loadClass("Test") I will get an exception saying that the class had the wrong name, and the correct name is foo.bar.Test. But how am I supposed to know that ahead of time?

This is what I have right now:

public class ScriptClassLoader extends URLClassLoader {

    private final File script;

    public ScriptClassLoader(File script) throws MalformedURLException {
        super(new URL[] { script.toURI().toURL() });
        this.script = script;
    }

    public Class<?> load() throws ClassNotFoundException {
        String fileName = script.getName();
        String className = fileName.substring(0, fileName.indexOf(".class"));
        return loadClass(className);
    }
}

How do people load scripts at runtime that are not part of the program's classpath, and the binary name of the class is not known?

Martin Tuskevicius
  • 2,590
  • 4
  • 29
  • 46
  • 1
    This is by no means a solution, but you can catch the exception thrown, read the correct name, and then perform the read operation again. Yes, it's cheap, but it's the first thing that came into my mind in the first minute I read you issue. On the positive side, it shows that it is possible to do this in a correct way. Also, please post an [MCVE](http://stackoverflow.com/help/mcve). – user1803551 Mar 28 '14 at 20:15
  • I thought about this too, but that felt way too cheap. I may do this if more elegant solutions don't arise. – Martin Tuskevicius Mar 28 '14 at 20:35
  • There are actually many solutions, see [here](http://stackoverflow.com/questions/11016092/how-to-load-classes-at-runtime-from-a-folder-or-jar) and [here](http://stackoverflow.com/questions/20586067/how-to-load-unknown-class-from-downloaded-jar-file-during-runtime) for starters. – user1803551 Mar 28 '14 at 21:09

1 Answers1

1

If you just need to load a class from a given .class file, no matter how this classes is named, you can load the data yourself and then call ClassLoader's defineClass() method:

RandomAccessFile raf = new RandomAccessFile(script, "r");
try {
    byte[] classData = new byte[(int) raf.length()];
    raf.readFully(classData);
    return super.defineClass(null, classData, 0, classData.length);
} finally {
    raf.close();
}
apangin
  • 92,924
  • 10
  • 193
  • 247
  • In this case `ScriptClassLoader` need not even extend `URLClassLoader` - it may be inherited directly from `ClassLoader`. – apangin Mar 28 '14 at 21:51
  • How would this work if the main script file refers to other script files? If I have `MainScript` in `foo.bar` and it refers to `SomeOtherScriptClass` in `foo.bar.other`, would that method still work, if I just load `MainScript`? Or will I get a `NoDefFoundError` when the script's execution reaches code that utilizes methods in the `SomeOtherScriptClass` class? – Martin Tuskevicius Mar 28 '14 at 22:38
  • 1
    @MartinTuskevicius To resolve unknown references to other classes JVM will call your ClassLoader's `loadClass()` method. You'll have to implement it so that it will look for `SomeOtherScriptClass.class` in `foo/bar/other` or in the current directory or wherever you like. That's the price you pay if you don't place .class files where they are expected to be. – apangin Mar 28 '14 at 23:27