6

Nonostante gli avvertimenti di abbandonare la mia attuale linea d'azione, al momento non vedo un modo migliore per risolvere il mio problema. I devegenerarecodice Java in fase di esecuzione, quindi compilarlo, caricarlo e fare riferimento a.java classloader e compilazione runtime

Il problema è che il codice generato importa il codice che è già stato caricato dal caricatore di classi di sistema (suppongo) - ovvero, il codice presente in uno dei vasi sul mio classpath. (ho eseguito all'interno di un contenitore web Tomcat 6 su Java 6.) Si può chiedere voi stessi perché questo è un problema - ben I sicuro che non lo so - ma il fatto è che ottengo gli errori di compilazione:

/W:/.../parser/v0.5/AssignELParser.java:6: pacchetto com.xxx.yyy.zzz.configuration non esiste

seguito alcuni esempi da internet ho definito le seguenti classi:

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(); 
    } 
} 

Sembra che la classe interna LoaderImpl estendendo la classe ClassLoader e non chiamando un super costruttore esplicito dovrebbe fare riferimento come caricatore della classe padre al caricatore della classe di sistema.

In tal caso, perché viene visualizzato l'errore di compilazione "runtime", sopra? Perché non trova il codice per la classe importata?

risposta

3

Non so se può essere d'aiuto, ma hai provato a specificare esplicitamente il percorso di classe?

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(); 

e poi

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

anche io non riesco a vedere dove si passa LoaderImpl istanza al compiler. Non dovrebbe essere fatto esplicitamente?

+0

Perfetto! Grazie :) – Yaneeve