4

I'm aware that this question has been asked before:

How to use URLClassLoader to load a *.class file?

However I don't really understand due to the lack of example. I'm currently working on a project and trying to load user-given .class objects which can be located in any directories on the machine.

//Create URL to hash function class file
URL url_hashFunctionPath = new URL("file:///" + _sHashFunctionFilePath);

//Packet URL to a URL array to be used by URLClassLoader
URL[] urlA_hashFunctionPath = {url_hashFunctionPath};

//Load URL for hash function via a URL class loader
URLClassLoader urlCl_hashFunctionClassLoader = new URLClassLoader(urlA_hashFunctionPath);

//Load user hash function into class to initialize later (TEST: HARD CODE FOR NOW)
m_classUserHashClass = urlCl_hashFunctionClassLoader.loadClass(_sUserHashClassName);

The last line gave me a ClassNotFoundException, from my experiment & understanding the user-given class function has to be in the classpath?

PS: 1st time posting questions feel free to correct me where I did not follow the appropriate manner.

//SOLUTION

The solution that I arrived at with the generous help of [WillShackleford][1], this solution can load the a .class file in a given filepath. For more information refer to code and their given comments.

//The absolute file path to the class that is to be loaded (_sHashFunctionFilePath = absolute file path)
String pathToClassFile = _sHashFunctionFilePath;
System.out.println("File to class: " + _sHashFunctionFilePath);

//Declare the process builder to execute class file at run time (Provided filepath to class)
ProcessBuilder pb = new ProcessBuilder("javap", pathToClassFile);
try
{
    //Start the process builder
    Process p = pb.start();

    //Declare string to hold class name
    String classname = null;
    //Declare buffer reader to read the class file & get class name
    try(BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())))
    {
        String line;
        while(null != (line = br.readLine()))
        {
            if(line.startsWith("public class"))
            {
                classname = line.split(" ")[2];
                break;
            }
        }
        System.out.println("classname = " + classname);
    }
    catch(IOException _error)
    {

    }

    //Declare file path to directory containing class to be loaded
    String pathToPackageBase = pathToClassFile.substring(0, pathToClassFile.length() - (classname + ".class").length());
    System.out.println("pathToPackageBase = " + pathToPackageBase);

    try
    {
        //Create class to hold the class to be loaded via a URL class loader
        Class clss = new URLClassLoader(
                new URL[]{new File(pathToPackageBase).toURI().toURL()}
        ).loadClass(classname);

        //Create ab object/instance of said class
        Object test = clss.newInstance();

        //Declare & create requested method from user hash function class (Method is work & takes no arguments)
        Method method = clss.getMethod("work", null);
        method.invoke(test, null);
    }
7
  • What's the point of URL.toURI().toURL()? Commented Sep 24, 2015 at 21:22
  • Can't help you. We don't know what _sUserHashClassName is. Or _sHashFunctionFilePath for that matter, Commented Sep 24, 2015 at 21:23
  • possible duplicate of How to use URLClassLoader to load a *.class file? Commented Sep 24, 2015 at 21:23
  • @blm That's the link OP already posted. Commented Sep 24, 2015 at 21:24
  • Sorry it was meant for file.toURI().toURL(), i got mixed up thinking it was toURL() straight was a deprecated way. Commented Sep 24, 2015 at 21:26

2 Answers 2

5

In the directory /home/shackle/somedir/classes/pkg I have a file Test.class created from a java file with package pkg; eg :

package pkg;

public class Test {

    public String toString() {
        return "secret_string";
    }
}

Then I load it with :

System.out.println(new URLClassLoader(
        new URL[]{new File("/home/shackle/somedir/classes").toURI().toURL()}
).loadClass("pkg.Test").newInstance().toString());

Notice that I do not put the pkg/Test in the URL string but the load class argument has the pkg. prefix.

You can get the class name directly from the file like this:

Class clsReaderClss = ClassLoader.getSystemClassLoader().loadClass("jdk.internal.org.objectweb.asm.ClassReader");
System.out.println("clsReaderClss = " + clsReaderClss);
Constructor con = clsReaderClss.getConstructor(InputStream.class);
Object reader = con.newInstance(new FileInputStream(directFile));
Method m = clsReaderClss.getMethod("getClassName");
String name = m.invoke(reader).toString().replace('/', '.');
System.out.println("name = " + name);

An alternative that doesn't require access to internal classes.

String pathToClassFile = "/home/shackle/somedir/classes/pkg/Test.class";
ProcessBuilder pb = new ProcessBuilder("javap",pathToClassFile);
Process p = pb.start();
String classname = null;
try(BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
   String line;
   while(null != (line = br.readLine())) {
       if(line.startsWith("public class")) {
           classname = line.split(" ")[2];
           break;
       }
   }
}
System.out.println("classname = " + classname);

Class can then be loaded with:

String pathToPackageBase = pathToClassFile.substring(0, pathToClassFile.length() - (classname + ".class").length());
System.out.println("pathToPackagBase = " + pathToPackageBase);
Class clss = new URLClassLoader(
        new URL[]{new File(pathToPackageBase).toURI().toURL()}
).loadClass(classname);
System.out.println(clss.newInstance().toString());
Sign up to request clarification or add additional context in comments.

7 Comments

I understand but wouldn't that mean that I would need to have explicit knowledge of the package name which is used by class Test. (As I assume my user provides the class file hence I shouldn't have knowledge about the package name he/she is using, or is my understanding about package misguided somewhere?)
Cheers will look into that thanks, I can see from the 2 current answers that there is a lot more to this then meets the eye. Will get back to you once I done my research.
Thanks, but can i check what does this line do .getSystemClassLoader().loadClass("jdk....ClassReader");
So you this JDK required is an external library where only the "importing" side of things is required to install as an external library? Would I require to add the file in my project for future compilation if required to run on other machines?
The alternative that does not require access to internal classes, hence once you have the classname how do you instantiate an instance of said class & use its methods?
|
0

Your _sHashFunctionFilePath needs to have the package name of the target class removed from it, so the ClassLoader will look in _sHashFunctionFilePath + package.name + HashFunction.class as the path to the file. If you don't do that, the ClassLoader won't be able to find the file.

So if the target class is my.great.HashFunction in HashFunction.class, then it needs to be in a directory called my/great/ if you want to use URLClassLoader. Then, you'd use /path/to as the file:/// URL for your URLClassLoader if the .class file was actually found in /path/to/my/great/HashFunction.class.

4 Comments

Sorry I saw a lot of example mentioning packets as well using similar notations such as your example "my.great.HashFunction", I'm afraid I don't understand the purpose of these packets. (As I assume my user provides the class file hence I shouldn't have knowledge about the package name he/she is using, or is my understanding about package misguided somewhere?)
If you can't predict the package name (the term package has a definite meaning in Java), then you can't use ClassLoader.loadClass to fetch the class. Instead, you'll have to call ClassLoader.defineClass and ClassLoader.resolveClass which, as you may note, is tricky because those methods are protected. You also have to know the name of the class in advance, because you have to know a class's name to define it. You may be able to use something like BCEL to get metadata from the raw bytes of a class, and then feed that to ClassLoader.defineClass.
Cheers will look into that thanks, I can see from the 2 current answers that there is a lot more to this then meets the eye. Will get back to you once I done my research
Your best bet would be to load .jar files from contributors and not try to load .class files directly. That way, the JAR file will contain all the structure necessary, and you can even scan the JAR files for the classes that are inside of them, and ... I dunno, maybe decide which class to load.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.