2 Replies Latest reply: Aug 14, 2009 6:38 PM by 843810 RSS

    Add jar for in-memory compilation by Eclipse Compiler

    843810
      Hi,

      I have a question regarding loading a jar file by the compiler to dynamically compile with a source file. I hope someone can probably offer me an idea on what has been missing or wrong with the source codes I have written for my application.

      I am using Eclipse compiler to dynamically compile a class. In the class, I want it to make a reference to a jar file for compilation dynamically.

      Here is the source of a test class I wrote:
      import javax.servlet.http.HttpServlet;
       
      class MyServlet extends HttpServlet {
       
      }
      The import statement refers to the class javax.servlet.http.HttpServlet from the jar file C:\\Program Files\\Apache Software Foundation\\Tomcat 6.0\\lib\\servlet-api.jar placed in the local file system.

      In the method called compileClass (shown below), I used the path of the jar file to add to the option -classpath as suggested in another thread (http://forums.sun.com/thread.jspa?threadID=5306520&start=0&tstart=0).
      private static CompileClassResult compileClass(Writer out, String className, String classSource) {
                try {
                     JavaCompiler javac = new EclipseCompiler();
                     StandardJavaFileManager sjfm = javac.getStandardFileManager(null, null, null);
                     SpecialClassLoader scl = new SpecialClassLoader();
       
                     SpecialJavaFileManager fileManager = new SpecialJavaFileManager(sjfm, scl);
                     List<String> options = new ArrayList<String>();
                     options.addAll(Arrays.asList("-classpath", "C:\\Program Files\\Apache Software Foundation\\Tomcat 6.0\\lib\\servlet-api.jar"));
                     
                     List<MemorySource> compilationUnits = Arrays.asList(new MemorySource(className, classSource));
                     DiagnosticListener<JavaFileObject> diagnosticListener = null;
                     Iterable<String> classes = null;
                     if (out == null) {
                          out = new PrintWriter(System.err);
                     }
                     JavaCompiler.CompilationTask compile = javac.getTask(out, fileManager, diagnosticListener, options, classes, compilationUnits);
                     boolean res = compile.call();
                     if (res) {
                          //Need to modify the api to return an array of two elements - one classes and other bytecodes for all classes in the same class file.
                          return CompileClassResult.newInstance(scl.findClasses(), scl.findByteCodes());
                     }
                } catch (Exception e) {
                     e.printStackTrace();               
                }
                return null;
           }
      I also extended the class ForwardingJavaFileManager as suggested in the thread mentioned above and have it delegated to the StandardJavaFileManager sent to the compiler mentioned in the method compileClass above. The extended class (called SpecialJavaFileManager) is as follows:
      public class SpecialJavaFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {
           private SpecialClassLoader xcl;
           
           public SpecialJavaFileManager(StandardJavaFileManager sjfm, SpecialClassLoader xcl) {
                super(sjfm);
                System.out.println("SpecialJavaFileManager");
                this.xcl = xcl;
           }
           
           public JavaFileObject getJavaFileForOutput(Location location, String name, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
                System.out.println("getJavaFileForOutput");
                MemoryByteCode mbc = new MemoryByteCode(name);
                xcl.addClass(name, mbc);
                return mbc;
           }
           
           public Iterable<JavaFileObject> list(JavaFileManager.Location loc, String pkg, Set kinds, boolean recurse) throws IOException {
                System.out.println("list ");
              List<JavaFileObject> result = new ArrayList<JavaFileObject>();
              for (JavaFileObject f : super.list(loc, pkg, kinds, recurse)) {
                   System.out.println(f);
                  result.add(f);
              }
                return result;
           }
      }
      I run the application and the result shows that it didn't load the jar file into the memory as expected. From the output (below) I got, it doesn't seem to invoke the method list(...) in the class SpecialJavaFileManager.
      SpecialJavaFileManager
      ----------
      1. ERROR in \MyServlet.java (at line 1)
           import javax.servlet.http.*;
                  ^^^^^^^^^^^^^
      The import javax.servlet cannot be resolved
      ----------
      2. ERROR in \MyServlet.java (at line 3)
           class MyServlet extends HttpServlet {
                                   ^^^^^^^^^^^
      HttpServlet cannot be resolved to a type
      Does somebody know what I probably have missed or done wrong in the codes?

      Thanks.
      Jonathan