开发者

Java compiler API ClassLoader

I am trying to use Java Compiler API to compile some java class. That class imports some packages from the jar files which can be loaded by context ClassLoader, let's call him X, which is NOT the system classloader. When I run the compilation, the compiler complains about not recognizing the imports. I have tried to specify the fileManager to pass the classloader, but it does not help.

When compile method is called, it first prints "CLASS LOADED", so the context ClassLoader CAN find the dependency class. However, the compilation itself fails (I get "Compilation FAILED" message) and during the compilation I get errors like this:

/path/to/my/Source.java:3: package my.dependency does not exist import my.dependency.MyClass; ^

What am I doing wrong? What's the correct way to pass custom classloader to the compilationTask? I can't extract the URLs from the ClassLoader since it's not URLClassLoader.

My methods are here:

public void compile(List<File> filesToCompile) {
       JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

       StandardJavaFileManager stdFileManager =
               compiler.getStandardFileManager(null, null, null);
       Iterable<? extends JavaFileObject> fileObjects = stdFileManager
               .getJavaFileObjectsFromFiles(filesToCompile);

       FileManagerImpl fileManager = new FileManagerImpl(stdFileManager);

       CompilationTask task = compiler.getTask(null, fileManager, null, null, null, fileObjects);
       Boolean result = task.call();
       if (result == true) {
           System.out.println("Compilation has succeeded");
       } else {
           System.out.println("Compilation FAILED");
       }
}

private final class FileManagerImpl extends F开发者_StackOverflow社区orwardingJavaFileManager<JavaFileManager> {

      public FileManagerImpl(JavaFileManager fileManager) {
           super(fileManager);
      }

      @Override
      public ClassLoader getClassLoader(JavaFileManager.Location location) {
          ClassLoader def = getContextClassLoader();
          try {
               def.loadClass("my.dependency.MyClass");
               System.out.println("CLASS LOADED");
          } catch (ClassNotFoundException ex) {
               System.out.println("NOT LOADED");
          }
          return def;
      }
}


The main point is that, while a class loader loads classes, javac will call JavaFileManager#list() to get a listing of all the files in a package.

So to use a custom class loader you need to modify (or extend) it to override JavaFileManager#list(). Hopefully you can reuse some of the logic used for class loading.

You might want to use your own implementations of JavaFileObject to represent class objects. You will then need to override JavaFileManager#inferBinaryName() (else the javac version will crash). Your implementations of JavaFileObject also needs to override (at least) JavaFileObject#openInputStream.

Here are some pointers: http://atamur.blogspot.be/2009/10/using-built-in-javacompiler-with-custom.html

Also, don't make your life harder than it should and extend ForwardingJavaFileManager and SimpleJavaFileObject.

For reference, here is an example implementation:

  @Override public Iterable<JavaFileObject> list(Location location,
    String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse)
  throws IOException
  {
    Iterable<JavaFileObject> stdResults =
      fileManager.list(location, packageName, kinds, recurse);

    if (location != StandardLocation.CLASS_PATH
    ||  !kinds.contains(JavaFileObject.Kind.CLASS))
    {
        return stdResults;
    }

    Set<JavaFileObject> additional = pkgObjects.get(packageName);

    if (additional == null || additional.isEmpty()) {
      return stdResults;
    }

    List<JavaFileObject> out = new ArrayList<>();

    for (JavaFileObject obj : additional) {
      out.add(obj);
    }
    for (JavaFileObject obj : stdResults) {
      out.add(obj);
    }

    return out;
  }

Where pkgObjects is a map from package names to JavaFileObject. The way you fill this map depends on how your class loader works.


This question has the answer. You'll have to set a classpath through an options list with the getTask() method (as described in detail in the accepted answer).


For loading the class from different jar file you could try with Reflection API it is easy way ..refer the following link http://download.oracle.com/javase/tutorial/reflect/index.html..

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜