6

Malgré les avertissements de laisser tomber mon plan d'action actuel, je ne vois actuellement pas de meilleure façon de résoudre mon problème. I doitgénérercode Java à l'exécution, puis le compiler, le charger et le référencer.java classloader et la compilation runtime

Problème est que le code généré importe le code qui a déjà été chargé par le chargeur de classe système (je suppose) - c'est-à-dire, le code présent dans l'un des jars sur mon classpath. (je lance dans un Tomcat 6 conteneur Web sur Java 6.) Vous pouvez vous demander pourquoi c'est un problème - bien je sûr ne sais pas - mais le fait est que je reçois des erreurs de compilation:

/W:/.../parser/v0.5/AssignELParser.java:6: package com.xxx.yyy.zzz.configuration n'existe pas

Après quelques exemples sur internet je définis les classes suivantes:

class MemoryClassLoader extends ChainedAction { 

    private static final Logger LOG = Logger.getLogger(MemoryClassLoader.class); 

    private LoaderImpl impl; 

    private class LoaderImpl extends ClassLoader { 

     // The compiler tool 
     private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 

     // Compiler options 
     private final Iterable<String> options = Arrays.asList("-verbose"); 

     // DiagnosticCollector, for collecting compilation problems 
     private final DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>(); 

     // Our FileManager 
     private final MemoryFileManager manager = new MemoryFileManager(this.compiler); 

     public LoaderImpl(File sourceDirectory) { 

      List<Source> list = new ArrayList<Source>(); 

      File[] files = sourceDirectory.listFiles(new FilenameFilter() { 

       @Override 
       public boolean accept(File dir, String name) { 

        return name.endsWith(Kind.SOURCE.extension); 
       } 
      }); 

      for (File file : files) { 
       list.add(new Source(file)); 
      } 

      CompilationTask task = compiler.getTask(null, manager, diagnostics, options, null, list); 
      Boolean compilationSuccessful = task.call(); 

      LOG.info("Compilation has " + ((compilationSuccessful) ? "concluded successfully" : "failed")); 

      // report on all errors to screen 
      for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) { 
       LOG.warn(diagnostic.getMessage(null)); 
      } 
     } 

     @Override 
     protected Class<?> findClass(String name) throws ClassNotFoundException { 
      synchronized (this.manager) { 
       Output output = manager.map.remove(name); 
       if (output != null) { 
        byte[] array = output.toByteArray(); 
        return defineClass(name, array, 0, array.length); 
       } 
      } 
      return super.findClass(name); 
     } 
    } 

    @Override 
    protected void run() { 

     impl = new LoaderImpl(new File(/* Some directory path */)); 

    } 
} 



class MemoryFileManager extends ForwardingJavaFileManager<JavaFileManager> { 

    final Map<String, Output> map = new HashMap<String, Output>(); 

    MemoryFileManager(JavaCompiler compiler) { 
     super(compiler.getStandardFileManager(null, null, null)); 
    } 

    @Override 
    public Output getJavaFileForOutput(Location location, String name, Kind kind, FileObject source) { 

     Output output = new Output(name, kind); 
     map.put(name, output); 

     return output; 
    } 

} 


class Output extends SimpleJavaFileObject { 

    private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 

    Output(String name, Kind kind) { 
     super(URI.create("memo:///" + name.replace('.', '/') + kind.extension), kind); 
    } 

    byte[] toByteArray() { 
     return this.baos.toByteArray(); 
    } 

    @Override 
    public ByteArrayOutputStream openOutputStream() { 
     return this.baos; 
    } 
} 



class Source extends SimpleJavaFileObject { 


    public Source(File file) { 
     super(file.toURI(), Kind.SOURCE); 
    } 


    @Override 
    public CharSequence getCharContent(boolean ignoreEncodingErrors) { 

     StringBuilder sb = new StringBuilder(""); 
     try { 
      File file = new File(uri); 
      FileReader fr = new FileReader(file); 
      BufferedReader br = new BufferedReader(fr); 

      sb = new StringBuilder((int) file.length()); 
      String line = ""; 
      while ((line = br.readLine()) != null) { 
       sb.append(line); 
       sb.append("\n"); 
      } 
     } catch (FileNotFoundException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } catch (IOException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 

     return sb.toString(); 
    } 
} 

Il semble que la classe interne LoaderImpl en étendant la classe ClassLoader et en n'appelant pas un super constructeur explicite devrait référencer en tant que chargeur de classe parent le chargeur de classe système.

Si c'est le cas, pourquoi ai-je l'erreur de compilation "runtime" - ci-dessus? Pourquoi ne trouve-t-il pas le code de la classe importée?

Répondre

3

Vous ne savez pas si cela peut vous aider, mais avez-vous essayé de spécifier explicitement classpath?

getClassPath() 
{ 
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 
    URL[] urls = ((URLClassLoader) classLoader).getURLs(); 
    StringBuilder buf = new StringBuilder(1000); 
    buf.append("."); 
    String separator = System.getProperty("path.separator"); 
    for (URL url : urls) { 
     buf.append(separator).append(url.getFile()); 
    } 
} 

classPath = buf.toString(); 

puis

options.add("-classpath"); 
options.add(getClassPath()); 

Je vois peux pas où vous passez LoaderImpl par exemple à la compiler. Ne devrait-il pas être fait explicitement?

+0

Parfait! Merci :) – Yaneeve