3

I write a simple java code in a String and want to execute dynamically when the program runs, below is my code:

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;

import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;

public class CompliedClass {
    public static void main(String[] args) {
        String source = ""
                +"class Solution{"
                +"public int add(){"
                +"return 1+1;"
                +"}"
                +"}";

        //File root = new File("");
        File sourceFile = new File("./src/Solution.java");
        try {
            Files.write(sourceFile.toPath(), source.getBytes(StandardCharsets.UTF_8));
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(sourceFile.getPath());
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        if (compiler == null) {
            System.out.println("JDK required (running inside of JRE)");
        }else{
            System.out.println("you got it!");
        }

        int compilationResult = compiler.run(null, null, null,sourceFile.getPath());
        if(compilationResult == 0){
            System.out.println("Compilation is successful");
        }else{
            System.out.println("Compilation Failed");
        }

        try{
            URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { sourceFile.toURI().toURL() });
            Class<?> cls = Class.forName("Solution" , true, classLoader);
            Object instance = cls.newInstance();
            Method method = cls.getDeclaredMethod("add", null);
            System.out.println(method.invoke(instance, null));
        }catch(Exception e){
            System.out.println("something wrong");
        }

    }
}

The Problem with the code above is when I execute first time, I can not get the result, it seems the below code has an exception:

Object instance = cls.newInstance();

Then I execute the second time, it functions good, so the conclusion is when I run for the first time, the Solution class can not be found, which cause the exception below

java.lang.ClassNotFoundException: Solution

could someone help me fix this issue please?

3
  • Just wonder why not loading a jar file dynamically? Commented Feb 22, 2017 at 21:04
  • ClassLoaders load .class files, not .java files. You cannot pass sourceFile to a URLClassLoader. In fact, I’m pretty sure you can’t even pass a .class file; you need to pass the URL of a directory or .jar file which contains one or more .class files. The URLs you pass to URLClassLoader are functionally identical to a classpath. Commented Feb 22, 2017 at 21:18
  • 1
    A side note: You don't even have to write the stuff to a file. This can be done purely in-memory. See stackoverflow.com/questions/935175/convert-string-to-code/… for an example. Commented Feb 22, 2017 at 21:24

3 Answers 3

1

1) Your class should have the public modifier otherwise CompliedClass could not access to it if it not declared in the same package. Here it could work as both are in the default package but it is not advised.

2) Your error is here :

URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { sourceFile.toURI().toURL() });

Here sourceFile refers to the Solution java class file.
It should rather refer to the url of the folder containing the Solution java class file as according to the javadoc of URLClassLoader java.net.URLClassLoader.newInstance(URL[] urls), the url parameter refers to the URLs to search for classes and resources.

You can try this code with the two explained modifications:

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

        String source = "public class Solution{" + "public int add(){" + "return 1+1;" + "}" + "}";

        File folder = new File("./src");
        File sourceFile = new File(folder, "Solution.java");

        try {
            Files.write(sourceFile.toPath(), source.getBytes(StandardCharsets.UTF_8));
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(sourceFile.getPath());
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        if (compiler == null) {
            System.out.println("JDK required (running inside of JRE)");
        } else {
            System.out.println("you got it!");
        }

        int compilationResult = compiler.run(null, null, null, sourceFile.getPath());
        if (compilationResult == 0) {
            System.out.println("Compilation is successful");
        } else {
            System.out.println("Compilation Failed");
        }

        try {
            URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] {folder.toURI().toURL() });
            Class<?> cls = Class.forName("Solution", true, classLoader);
            Object instance = cls.newInstance();
            Method method = cls.getDeclaredMethod("add", null);
            System.out.println(method.invoke(instance, null));
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("something wrong");
        }

    }
}

Output :

.\src\Solution.java

you got it!

Compilation is successful

2

Sign up to request clarification or add additional context in comments.

1 Comment

Yours works fine, thanks a lot, this path problem often made me mad.
1

Your problem might be the class file for the solution .class being created in another location than the location of the .class file of the current class. Make sure the location where the .class file is created is included in the classpath of the current program and you would be good to go.

Comments

1

There are several problems with the code you have provided.

  1. The class you define as a string should be accessible by your main class. Currently it is not. The simplest way to fix it is to make it public
  2. Your class loader is trying to load class from the source code itself (the URL to "Solution.java" is provided as class path URL). URLClassLoader actually expects either URL to a jar or a folder with classes.

Please find a corrected version below:

public class CompliedClass {

  public static void main(String[] args) throws Exception {
    String classDef = ""
        + "public class Solution{"
        + "  public int add(){return 1+1;}"
        + "}";

    Path sourceFile = Paths.get("Solution.java");
    Files.write(sourceFile, classDef.getBytes());
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    if (compiler == null) {
      System.out.println("JDK required (running inside of JRE)");
      System.exit(1);
    } else {
      System.out.println("you got it!");
    }

    int compilationResult = compiler.run(null, null, null, sourceFile.toString());
    if (compilationResult == 0) {
      System.out.println("Compilation is successful");
    } else {
      System.out.println("Compilation Failed");
      System.exit(1);
    }

    URL classPath = sourceFile.toAbsolutePath().getParent().toUri().toURL();
    URLClassLoader classLoader = URLClassLoader.newInstance(new URL[]{classPath});
    Class<?> cls = Class.forName("Solution", true, classLoader);
    Object instance = cls.newInstance();
    Method method = cls.getDeclaredMethod("add", null);
    System.out.println(method.invoke(instance, null));
  }
}

Comments

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.