2011-04-28 16 views
6

Estoy tratando de usar la API del compilador de Java para compilar alguna clase de Java. Esa clase importa algunos paquetes de los archivos jar que pueden cargarse por el contexto ClassLoader, llamémosle X, que NO es el cargador de clases del sistema. Cuando ejecuto la compilación, el compilador se queja de no reconocer las importaciones. Intenté especificar FileManager para pasar el cargador de clases, pero no ayuda.compilador de Java API ClassLoader

Cuando se llama al método de compilación, imprime primero "CLASE CARGADA", por lo que el contexto ClassLoader PUEDE encontrar la clase de dependencia. Sin embargo, la compilación falla (obtengo el mensaje "Compilación FALLIDA") y durante la compilación recibo errores como este:

/path/to/my/Source.java:3: package my.dependency no existe importar my.dependency.MyClass; ^

¿Qué estoy haciendo mal? ¿Cuál es la forma correcta de pasar el cargador de clases personalizado a compilationTask? No puedo extraer las URL de ClassLoader porque no es URLClassLoader.

Mis métodos son aquí:

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 ForwardingJavaFileManager<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; 
     } 
} 

Respuesta

0

This question has the answer. Tendrá que establecer un classpath a través de una lista de opciones con el método getTask() (como se describe en detalle en la respuesta aceptada).

+1

Muchas gracias, lo comprobaré. Sin embargo, ¿no hay alguna forma de establecer solo el ClassLoader existente? –

+0

@Pavel S. - Lo dudo, ya que tiene sentido, para proporcionar a la herramienta de compilación un classpath que está separado del classpath de aplicación real. Imagine, tiene una versión de una clase cargada pero quiere usar otra para compilar las clases. Esto * requiere * separación de clase. –

0

Para cargar la clase diferente de archivo JAR puede probar con el API de reflexión es manera fácil ..refer el siguiente enlace .. http://download.oracle.com/javase/tutorial/reflect/index.html

+0

Gracias por la respuesta. Pero eso no es exactamente lo que necesito. Necesito compilar la fuente * .java y dejar que el compilador sepa cómo cargar el paquete de dependencia. –

3

El punto principal es que, mientras un cargador de clases carga clases, javac llamará al JavaFileManager#list() para obtener una lista de todos los archivos en un paquete.

Por lo tanto, para usar un cargador de clases personalizado debe modificarlo (o ampliarlo) para anular JavaFileManager#list(). Con suerte, puede reutilizar parte de la lógica utilizada para la carga de clases.

Es posible que desee utilizar sus propias implementaciones de JavaFileObject para representar objetos de clase. A continuación, deberá sobrescribir JavaFileManager#inferBinaryName() (de lo contrario, la versión de javac se bloqueará). Sus implementaciones de JavaFileObject también deben sobrescribir (al menos) JavaFileObject#openInputStream.

Éstos son algunos consejos: http://atamur.blogspot.be/2009/10/using-built-in-javacompiler-with-custom.html

Además, no hace su vida más difícil de lo que debería y extender ForwardingJavaFileManager y SimpleJavaFileObject.

Para referencia, aquí es un ejemplo de implementación:

@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; 
    } 

Dónde pkgObjects es un mapa de nombres de paquetes a JavaFileObject. La forma en que llene este mapa depende de cómo funcione su cargador de clases.