This discussion is archived
7 Replies Latest reply: Jun 24, 2010 7:32 PM by 843810 RSS

jdk6.0 compiler api strange behaviour (junit test case+code)

843810 Newbie
Currently Being Moderated
Hello,
i'm giving a first try to new jdk6 compiler api.
Consider the following test case:
import junit.framework.TestCase;

public class PojoCompilerTest extends TestCase {

     private String pojoSrc = "public class Foo { " 
          + "private String name; " 
          + "Foo(){this.name = \"foo\";} " 
          + "public String getName(){return this.name;} " 
          + "public void setName(String name){this.name = name;} "
     + "}";
     
     private PojoCompiler pojoCompiler;
     
     protected void setUp() throws Exception {
          super.setUp();
          
          this.pojoCompiler = new PojoCompilerImpl();
     }
     
     public void testCompilePojo() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
          
          Class clazz = this.pojoCompiler.compile("Foo", this.pojoSrc);
          
          assertNotNull("Loaded class shoult not be null", clazz);
     }
          
}
here is the class-under-test:
public class PojoCompilerImpl implements PojoCompiler {

     private JavaCompiler compiler; 
     private DiagnosticCollector<JavaFileObject> diagnostic;
     private StandardJavaFileManager fileManager;
     
     public PojoCompilerImpl() {
          this.compiler = ToolProvider.getSystemJavaCompiler();
          this.diagnostic = new DiagnosticCollector<JavaFileObject>();
          this.fileManager = this.compiler.getStandardFileManager(this.diagnostic, null, null);
     }
     
     /* (non-Javadoc)
      * @see it.chi.infopipe.nr.model.output.PojoCompiler#compile(java.lang.String, java.lang.String)
      */
     public Class compile(String className, String source) throws ClassNotFoundException, InstantiationException, IllegalAccessException {

          
          JavaSourceFromString pojo = new JavaSourceFromString(className, source);
          
          Iterable<? extends JavaFileObject> compilationUnits= 
               Arrays.asList(new JavaSourceFromString[] {pojo});
          
          Iterable<String> options = Arrays.asList(new String[]{"-verbose","-Xlint"});
          
          boolean compilationErrors = 
               compiler.getTask(null, fileManager, this.diagnostic, options, null, compilationUnits).call();
          
          for (Diagnostic diagnostic : this.diagnostic.getDiagnostics()) {
               System.out.format("Error on line %d%s%n", diagnostic.getLineNumber(), diagnostic);
          }
               
          Class clazz = ClassLoader.getSystemClassLoader().loadClass("Foo");
          
          clazz.newInstance();
          
          return clazz;
     }

     
     /**
     * A file object used to represent source coming from a string.
     * (from javadoc)
      */
    public class JavaSourceFromString extends SimpleJavaFileObject {
        /**
         * The source code of this "file".
          */
        final String code;

        /**
         * Constructs a new JavaSourceFromString.
         *  @param name the name of the compilation unit represented by this file object
         *  @param code the source code for the compilation unit represented by this file object
          */
        JavaSourceFromString(String name, String code) {
            super(URI.create("string:///" + name.replace('.','/') + Kind.SOURCE.extension),
                  Kind.SOURCE);
            this.code = code;
        }

         @Override
        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
            return code;
        }
    }
     
}
the test case fails, but i don't get any compiler error shown or others. this is the ouput to stderr:
[parsing started string:///Foo.java from JavaSourceFromString]
[parsing completed 53ms]
[loading /usr/java/jdk1.6.0/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Object.class)]
[loading /usr/java/jdk1.6.0/lib/ct.sym(META-INF/sym/rt.jar/java/lang/String.class)]
[checking Foo]
[loading /usr/java/jdk1.6.0/lib/ct.sym(META-INF/sym/rt.jar/java/io/Serializable.class)]
[wrote Foo.class]
[total 587ms]
i'm using :
java version "1.6.0-rc"
Java(TM) SE Runtime Environment (build 1.6.0-rc-b97)
Java HotSpot(TM) Client VM (build 1.6.0-rc-b97, mixed mode, sharing)

on linux-i586

any idea ?

thanks,
Valerio
  • 1. Re: jdk6.0 compiler api strange behaviour (junit test case+code)
    843810 Newbie
    Currently Being Moderated
    i also discovered that the intended .class file is being correctly compiled.

    The problem must be in the way the class loader look for it...
  • 2. Re: jdk6.0 compiler api strange behaviour (junit test case+code)
    843810 Newbie
    Currently Being Moderated
    also, there was a small bug (an hard-coded value):
    Class clazz = ClassLoader.getSystemClassLoader().loadClass("Foo");
    to be changed in
    Class clazz = ClassLoader.getSystemClassLoader().loadClass(srcName);
    which by the way hasn't changed anyhow the result: the classLoader is still unable to find the just-compiled class.

    Message was edited by:
    ervalerio
  • 3. Re: jdk6.0 compiler api strange behaviour (junit test case+code)
    EJP Guru
    Currently Being Moderated
    srcName isn't defined in the code you've showed.

    So it can't compile.
    which by the way hasn't changed
    although you changed it?
    anyhow the result: the classLoader is still unable to find the just-compiled class.
    Impossible to say WTH you are talking about at this stage.

    And WTF is the lone Class.forName() supposed to do?
  • 4. Re: jdk6.0 compiler api strange behaviour (junit test case+code)
    843810 Newbie
    Currently Being Moderated
    sorry, one more typo. it should have been:
    Class clazz = ClassLoader.getSystemClassLoader().loadClass(className);
    So, the class is being correctly compiled: I can see Foo.class at the root directory of my project).

    The problem doesn't come from the usage of the compiler api directly.

    btw, I tried adding one compiler option so that the new class is found one classpath:
    Iterable<String> options = Arrays.asList(new String[] { "-verbose", "-d target/classes" });
    but seems like[b] -d option isn't supported (yet?) :
    java.lang.IllegalArgumentException: invalid flag: -d /home/schiavoni/workspace/infopipe-nr/target/classes
         at com.sun.tools.javac.api.JavacTool.processOptions(JavacTool.java:236)
         at com.sun.tools.javac.api.JavacTool.getTask(JavacTool.java:207)
         at com.sun.tools.javac.api.JavacTool.getTask(JavacTool.java:53)
         at it.chi.infopipe.nr.model.output.PojoCompilerImpl.compile(PojoCompilerImpl.java:53)
         at it.chi.infopipe.nr.model.output.PojoCompilerTest.testCompilePojo(PojoCompilerTest.java:24)
         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
         at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
         at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
         at java.lang.reflect.Method.invoke(Method.java:597)
         at junit.framework.TestCase.runTest(TestCase.java:164)
         at junit.framework.TestCase.runBare(TestCase.java:130)
         at junit.framework.TestResult$1.protect(TestResult.java:110)
         at junit.framework.TestResult.runProtected(TestResult.java:128)
         at junit.framework.TestResult.run(TestResult.java:113)
         at junit.framework.TestCase.run(TestCase.java:120)
         at junit.framework.TestSuite.runTest(TestSuite.java:228)
         at junit.framework.TestSuite.run(TestSuite.java:223)
         at org.junit.internal.runners.OldTestClassRunner.run(OldTestClassRunner.java:35)
         at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:38)
         at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
         at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
         at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
         at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
         at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
  • 5. Re: jdk6.0 compiler api strange behaviour (junit test case+code)
    843810 Newbie
    Currently Being Moderated
    in some old pre-release (b47) there was a setOutputDirectory method on the JavaCompilerTool object (which also doesn't seem to be availabe anymore in latest mustang relealse, b97)...

    (as shown here: http://www.javalobby.org/forums/thread.jspa?messageID=92038470 )

    so what i need is to find a way to set the output directory for any compiled class...

    Message was edited by:
    ervalerio
  • 6. Re: jdk6.0 compiler api strange behaviour (junit test case+code)
    843810 Newbie
    Currently Being Moderated
    i found my self a (ugly) workaround !

    instead of using the filemanage instance passed to the compiler, i do something like:
    if (compiler.getTask(null, fileManager, this.diagnostic, options, null,
                        compilationUnits).call()) {
                   
                   File workingDir = new File(SystemUtils.USER_DIR);
                   URL workingDirURL = workingDir.toURI().toURL();
                   
                   ClassLoader urlClassLoader = new URLClassLoader(new URL[] {workingDirURL});
                   
                   clazz = 
                        urlClassLoader.loadClass(className);
                   
                   clazz.newInstance();
                   
                   return clazz;
              }
    surely this can break somewhere/somehow..but it's a first-step !

    i'll keep the duke dollars in case someone find a more elegant solution.
  • 7. Re: jdk6.0 compiler api strange behaviour (junit test case+code)
    843810 Newbie
    Currently Being Moderated
    Iterable<String> options = Arrays.asList(new String[] { "-verbose", "-d", "target/classes" });
    seems to work