Question

I have a problem regarding java.lang.NoSuchMethodError. This program is about Compiler API (JSR 199). When I create a prototype for this it run work, but when I try to make it to become library it throw NoSuchMethodError Exception.

Here is the First Prototype:

public class DynaCompTest {

    public static void main(String[] args) {
        String fullName = "HelloWorld";

        StringBuilder sourceCode = new StringBuilder();
        sourceCode.append("public class HelloWorld {\n")
            .append("\tpublic static void main(String[] args) {\n")
            .append("\t\tSystem.out.println(\"Hello World\")\n")
            .append("\t}\n")
            .append("}");

        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        JavaFileManager fileManager = new ClassFileManager(compiler.getStandardFileManager(null, null, null));

        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
        List<JavaFileObject> jFiles = new ArrayList<>();
        jFiles.add(new CharSequenceJavaFileObject(fullName, sourceCode));

        compiler.getTask(null, fileManager, diagnostics, null, null, jFiles).call();

        for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
            System.out.format("Error on line %d in %s\n", diagnostic.getLineNumber(), diagnostic);
        }
    }
}

public class CharSequenceJavaFileObject extends SimpleJavaFileObject {

    private CharSequence content;

    public CharSequenceJavaFileObject(String className, CharSequence content) {
        super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
        this.content = content;
    }

    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) {
        return content;
    }

}

public class ClassFileManager extends ForwardingJavaFileManager {

    private JavaClassObject jClassObject;

    public ClassFileManager(StandardJavaFileManager standardManager) {
        super(standardManager);
    }

    @Override
    public ClassLoader getClassLoader(Location location) {
        return new 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;
    }
}

public 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() {
        return bos;
    }
}

I changed the DynaCompTest become DynamicCompiler for the library:

public class DynamicCompiler {

    private JavaCompiler compiler;
    private JavaFileManager fileManager;
    private List<JavaFileObject> jFiles;
    private DiagnosticCollector<JavaFileObject> diagnostics;

    public DiagnosticCollector<JavaFileObject> getDiagnostics() {
        return diagnostics;
    }

    public DynamicCompiler(String className, StringBuilder sourceCode) {
        compiler = ToolProvider.getSystemJavaCompiler();
        fileManager = new ClassFileManager(compiler.getStandardFileManager(null, null, null));

        diagnostics = new DiagnosticCollector<>();
        jFiles = new ArrayList<>();
        jFiles.add(new CharSequenceJavaFileObject(className, sourceCode));
    }

    public boolean doCompilation() {
        return compiler.getTask(null, fileManager, diagnostics, null, null, jFiles).call();
    }
}

And I created Second Prototype to test the library:

public class Compiler {

    private static StringBuilder sourceCode = new StringBuilder();

    public static void main(String[] args) {
        boolean status;
        sourceCode.append("public class HelloWorld {\n")
            .append("\tpublic static void main(String[] args) {\n")
            .append("\t\tSystem.out.println(\"Hello World\");\n")
            .append("\t}\n")
            .append("}");

        DynamicCompiler compiler = new DynamicCompiler("HelloWorld", sourceCode);

        status = compiler.doCompilation();

        StringBuilder messages = new StringBuilder();
        if (!status) {
            for (Diagnostic diagnostic : compiler.getDiagnostics().getDiagnostics()) {
                messages.append("Error on line ")
                    .append(diagnostic.getLineNumber())
                    .append(" in ")
                    .append(diagnostic)
                    .append("\n");
            }
        } else {
            messages.append("BUILD SUCCESSFUL ");
        }

        System.out.println(messages.toString());
    }
}

When I test with code above it run well and print BUILD SUCCESSFUL but when I tried to make it error for example I deleted the semicolon ; like the first prototype, it throw the NoSuchMethodError Exception when access the compiler.getDiagnostics().getDiagnostics() inside the looping.

The question is, why in the First Prototype it run well when try to make an error but when I tried with my own library it become Exception?

Edit

Here is the stacktrace:

/HelloWorld.java:3: error: ';' expected
    System.out.println("Hello World")
                                     ^
1 error
Exception in thread "main" java.lang.NoSuchMethodError: org.ert.lib.DynamicCompiler.getDiagnostics()Ljavax/tools/DiagnosticCollector;
at org.ert.exp.Compiler.main(Compiler.java:28)
Java Result: 1

It should be like this:

Error on line 3 in /HelloWorld.java:3: error: ';' expected
    System.out.println("Hello World")
                                     ^

When trying to debug it, it shown an error:

public DiagnosticCollector<JavaFileObject> getDiagnostics() {
    return diagnostics; // Set Breakpoint here
}

Here is the error message:

Not able to submit breakpoint LineBreakpoint DynamicCompiler.java : 25, reason: No executable location available at line 25 in class org.ert.lib.DynamicCompiler.
Invalid LineBreakpoint DynamicCompiler.java : 25

Update

Got the problem, this problem will occur if we add the whole project instead build the jar of the library. So when I build the library jar it works. But anyone can explain why this thing happen when I try add the whole project instead the jar file?

Note

I'm using:

  • JDK 1.7 from Oracle
  • Netbeans 7.1.1
Was it helpful?

Solution 2

After I tried with Eclipse Indigo, I found that it works when add the Project or add the jar file. While in Netbeans 7.1.1 will get an error if add the Project, but works if add the jar file.

Maybe it one of the bugs of Netbeans...

Thank you for your attention...

OTHER TIPS

It seems that you have similar class exists in two different libraries(jars). e.g.

com.test.Example.class in a.jar
com.test.Example.class in b.jar

Now class loader will load the first first Example.class and it seems that you need class which is there in b.jar. Then it will not throw exception such as NoMethodFound but throw an Exception that NoSuchMethodFound because class still exists in memory but can not find required method.

Such problems can be resolved by changing library order. You need to make required library's order higher. You can do this from the eclipse

Project Setting -> Java Build Path -> Order and Export.

Hope this is helpful.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top