使用JBoss自定义ClassLoader

时间:2013-12-29 17:10:43

标签: java jboss classloader

我正在尝试使用JavaCompiler API在运行时动态编译和加载类。我将编译后的字节码存储在内存中。所以我使用自定义类加载器来加载类。

public class CompilerAPITest {

    static String sourceCode = "package in.test;" +
    "public class DynamicCompilationHelloWorld implements TestInterface{" +
        "public void test (){" +
            "System.out.println (\"Hello, dynamic compilation world!\");" +
            "new in.test.another.SomeClass().fun();" + 
        "}" +
    "}" ;

    public void doCompilation (){
    SimpleJavaFileObject fileObject = new DynamicJavaSourceCodeObject ("in.test.DynamicCompilationHelloWorld", sourceCode) ;
    JavaFileObject javaFileObjects[] = new JavaFileObject[]{fileObject} ;

    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

    JavaFileManager stdFileManager = new
            CompilerAPITest.ClassFileManager(compiler
                .getStandardFileManager(null, null, null));

    Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(javaFileObjects);
    DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();

            List<String> options = Arrays.asList("-cp", System.getProperty("java.class.path")
            + ":" + getPath(CompilerAPITest.class));

    CompilationTask compilerTask = compiler.getTask(null, stdFileManager, diagnostics, options, null, compilationUnits) ;
    boolean status = compilerTask.call();

    if (!status){//If compilation error occurs
        /*Iterate through each compilation problem and print it*/
        for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()){
            System.out.format("Error on line %d in %s", diagnostic.getLineNumber(), diagnostic);
        }
    }
    try {
        stdFileManager.close() ;//Close the file manager
    } catch (IOException e) {
        e.printStackTrace();
    }

    try {
        stdFileManager.getClassLoader(null)
                .loadClass("in.test.DynamicCompilationHelloWorld").asSubclass(TestInterface.class).newInstance().test();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
        //This does nothing.
    } catch (InstantiationException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

public static void main(String args[]){
    new CompilerAPITest ().doCompilation() ;
}

class DynamicJavaSourceCodeObject extends SimpleJavaFileObject{
    private String qualifiedName ;
    private String sourceCode ;

    protected DynamicJavaSourceCodeObject(String name, String code) {
        super(URI.create("string:///" +name.replaceAll("\\.", "/") + Kind.SOURCE.extension), Kind.SOURCE);
        this.qualifiedName = name ;
        this.sourceCode = code ;
    }

    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors)
            throws IOException {
        return sourceCode ;
    }

    public String getQualifiedName() {
        return qualifiedName;
    }

    public void setQualifiedName(String qualifiedName) {
        this.qualifiedName = qualifiedName;
    }

    public String getSourceCode() {
        return sourceCode;
    }

    public void setSourceCode(String sourceCode) {
        this.sourceCode = sourceCode;
    }

}

private static class ClassFileManager extends
ForwardingJavaFileManager<JavaFileManager> {
    private JavaClassObject jclassObject;
    public ClassFileManager(StandardJavaFileManager
            standardManager) {
        super(standardManager);
    }
    @Override
    public ClassLoader getClassLoader(Location location) {
        return new java.security.SecureClassLoader() {
            @Override
            protected Class<?> findClass(String name)
                    throws ClassNotFoundException {
                byte[] b = jclassObject.getBytes();
                return super.defineClass(name, jclassObject
                        .getBytes(), 0, b.length);
            }
        };
    }

    @Override
    public JavaFileObject getJavaFileForOutput(Location location,
            String className, Kind kind, FileObject sibling)
                    throws IOException {
        jclassObject = new JavaClassObject(className, kind);
        return jclassObject;
    }
}

private static class JavaClassObject extends SimpleJavaFileObject {
    protected final ByteArrayOutputStream bos =
            new ByteArrayOutputStream();
    public JavaClassObject(String name, Kind kind) {
        super(URI.create("string:///" + name.replace('.', '/')
                + kind.extension), kind);
    }
    public byte[] getBytes() {
        return bos.toByteArray();
    }
    @Override
    public OutputStream openOutputStream() throws IOException {
        return bos;
    }
  }
}

在独立设置中运行时,此工作正常。但是,当我在JBoss上运行的生产设置中调用doCompilation()时,我得到以下异常。

java.lang.NoClassDefFoundError: 
    in/test/TestInterface(wrong name: 
    in/test/DynamicCompilationHelloWorld)
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:621)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:466)
    at in.test.CompilerAPITest$ClassFileManager$1.findClass(CompilerAPITest.java:126)

这可能是什么问题?

1 个答案:

答案 0 :(得分:1)

在谷歌搜索后发现了这个问题。

需要为使用的自定义类加载器设置父类加载器。

类加载层次结构在JBoss中略有不同。这里使用了UnifiedClassLoader,除了父类之外,它还用于检查对等类加载器,然后再抛出ClassNotFoundException。因此,当使用自定义类加载器时,它需要将defineClass调用委托给UnifiedClassLoader,当它无法加载类时。

以下是自定义类加载器的示例代码段。

private static class ByteClassLoader extends ClassLoader{
    private Map<String, JavaFileObject> store = new HashMap<String, JavaFileObject>();
    public ByteClassLoader(Map<String, JavaFileObject> str)
     {
     super( ByteClassLoader.class.getClassLoader() ); // set parent
     store = str;
    }

    protected Class<?> findClass(String name)
            throws ClassNotFoundException{
        JavaFileObject jfo = store.get(name);
        if (jfo == null){
            throw new ClassNotFoundException(name);
        }

        byte[] bytes = ((JavaClassObject)jfo).getBytes();
        Class<?> cl = defineClass(name, bytes, 0, bytes.length);
        if (cl == null){
            throw new ClassNotFoundException(name);
        }
        return cl;
    }
}

加载课程,

ByteClassLoader cl = new ByteClassLoader(store);
cl.loadClass(className);

需要使用。

这是动态编译和类加载的优秀链接。 http://fivedots.coe.psu.ac.th/~ad/jg/javaArt1/onTheFlyArt1.pdf