Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to create class(not instance) dynamically?

Tags:

java

I have an issue about how to create class(not instance) dynamically. In my project, I need to write several similar class according to the configuration file. For exmaple, there is a JSON like this:

{                                                                                                                                                                              
    {                                                                                                                                                                          
        "lang": "python",                                                                                                                                                      
        "file": "class1.py",
        "args": ["arg1"]                                                                                                                                  
    },                                                                                                                                                                         
    {                                                                                                                                                                          
        "lang": "python",                                                                                                                                                      
        "file": "class2.py"  
        "args": ["arg2"]                                                                                                                                               
    }                                                                                                                                                                          
} 

Subsequently, I need to write two java class below:

class1:

public class Class1 extends ShellBolt implements IRichBolt {
    public Class1() {
        super("python", "class1.py");
    }

    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        declarer.declare(new Fields(arg1));
    }

    @Override
    public Map<String, Object> getComponentConfiguration() {
        return null;
    }
}

class2:

public class Class2 extends ShellBolt implements IRichBolt {

    public Class2() {
        super("python", "class2.py");
    }

    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        declarer.declare(new Fields(arg2));
    }

    @Override
    public Map<String, Object> getComponentConfiguration() {
        return null;
    }
}

But if the JSON file is added a new object:

{                                                                                                                                                                          
    "lang": "python",                                                                                                                                                      
    "file": "class3.py"   
    "args": ["arg3"]
}

I need to write a new Class with the similar structure. So, is there a way to create class dynamically? I know maybe cglib might works, but I hava no idea how to use it in my case. Thanks.

like image 997
Charlie Lin Avatar asked Oct 20 '22 16:10

Charlie Lin


1 Answers

You can use the JavaCompiler to compile any java code to a class file. You can then load the resulting class with a URLClassLoader.

An example is given in the javadocs or for a complete example you can check this question.

Applied to your Class1 it would look like the example below. Note that you need to include all the relevant imports in the code or it won't compile.

public static void main(String[] args) throws Exception {
    String packageName = "some.packagename";
    String className = packageName + ".Class1";
    String body = "package " + packageName + ";   " +
                "public class Class1 extends ShellBolt implements IRichBolt {\n" +
                "    public Class1() {\n" +
                "        super(\"python\", \"class1.py\");\n" +
                "    }\n" +
                "\n" +
                "    @Override\n" +
                "    public void declareOutputFields(OutputFieldsDeclarer declarer) {\n" +
                "        declarer.declare(new Fields(arg1));\n" +
                "    }\n" +
                "\n" +
                "    @Override\n" +
                "    public Map<String, Object> getComponentConfiguration() {\n" +
                "        return null;\n" +
                "    }\n" +
                "}";

    Path classFile = Paths.get(System.getProperty("user.home"));
    compile(className, body, classFile);
    Class<?> class1 = loadClass(className, classFile);

    Method getComponentConfiguration = class1.getDeclaredMethod("getComponentConfiguration");
    Map<String, Object> result = (Map<String, Object>) getComponentConfiguration.invoke(class1.newInstance());
}

private static Class<?> loadClass(String className, Path path) throws Exception {
    URLClassLoader loader = new URLClassLoader(new URL[]{path.toUri().toURL()}, Test1.class.getClassLoader());
    return loader.loadClass(className);
}

private static void compile(String className, String body, Path path) throws Exception {
    List<JavaSourceFromString> sourceCode = Arrays.asList(new JavaSourceFromString(className, body));

    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
    fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(path.toFile()));
    boolean ok = compiler.getTask(null, fileManager, null, null, null, sourceCode).call();

    System.out.println("compilation ok = " + ok);
}

public static class JavaSourceFromString extends SimpleJavaFileObject {
    final String code;

    JavaSourceFromString(String name, String code) {
        super(URI.create("string:///" + name.replace('.', '/') + JavaFileObject.Kind.SOURCE.extension),
                JavaFileObject.Kind.SOURCE);
        this.code = code;
    }

    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) {
        return code;
    }
}
like image 106
assylias Avatar answered Oct 23 '22 11:10

assylias