7 Replies Latest reply: Jun 24, 2010 9:32 PM by 843810 RSS

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

    843810
      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
          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
            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
              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
                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
                  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
                    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
                      Iterable<String> options = Arrays.asList(new String[] { "-verbose", "-d", "target/classes" });
                      seems to work