Avatar billede jespersahner Nybegynder
07. september 2011 - 20:06 Der er 16 kommentarer og
1 løsning

CharSequenceCompiler compilation-error

JavaCompiler giver ikke mulighed for kompilering vha. en ClassLoader, hvorfor man i stedet kan anvende CharSequenceCompiler, som er en wrapper omkring JavaCompiler. Der henvises til flg. artikel:
http://www.ibm.com/developerworks/java/library/j-jcomp/index.html

Flg. simple eksempel giver dog fejl:

ClassLoader cl=(new URLClassLoader(new URL[]{(new File("c:/MyProject/build/classes/")).toURI().toURL()},
                this.getClass().getClassLoader()));           
CharSequenceCompiler compiler=new CharSequenceCompiler(cl,null);
compiler.loadClass("core.X");
System.out.println("core.X loaded!");

StringBuilder sb=new StringBuilder();
sb.append("package util;");
sb.append("import core.X;");
sb.append("public class Y {");
sb.append("    public Y() {");
sb.append("        System.out.println(\"util.Y compiled!\");");
sb.append("    }");
sb.append("}");               

DiagnosticCollector<JavaFileObject> diagnostics=new DiagnosticCollector<JavaFileObject>();
Class c=compiler.compile("util.Y",sb,diagnostics);           

for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
    System.out.println("Diagnostic: "+diagnostic.getCode());
    System.out.println("Diagnostic: "+diagnostic.getKind().toString());
    System.out.println("Diagnostic: "+diagnostic.getSource().toString());
    System.out.println("Diagnostic: "+diagnostic.getMessage(null));
}       

Compiler'en er i stand til at loade core.X vha. classloader'en cl, men util.Y kan ikke kompileres. Flg. fejl rapporteres i diagnostics:
"package core does not exist."

Nogle der kan gennemskue problemet?
Avatar billede arne_v Ekspert
07. september 2011 - 20:20 #1
1) det der skal vaere i classpath skal vaere paa disk - compileren kan ikke bruge noget der kun er i memory

2) hvis de er det i C:/MyProject/build/classes saa proev:

CharSequenceCompiler compiler=new CharSequenceCompiler(cl, new String[] { "-classpath", "C:/MyProject/build/classes"} );
Avatar billede jespersahner Nybegynder
07. september 2011 - 20:46 #2
-> arne_v:

ad 1) Ja, men ideen er vel, at classpath internt udledes af classloader'en, og de nødvendige klasser derefter loades (fra disk), som jeg forstår det. Hvad bruges classloader'en ellers til?

ad 2) Enig, burde helt sikkert virke men jeg får stadig samme fejl, mystisk. Jeg vil dog meget gerne undgå eksplicit angivelse af classpath, hvis muligt, jf. 1)
Avatar billede arne_v Ekspert
07. september 2011 - 20:54 #3
Det kan du jo have ret i.

Jeg vil lige proeve at hente den IBM kode.

Senere.
Avatar billede arne_v Ekspert
07. september 2011 - 20:54 #4
core.X ligger i C:/MyProject/build/classes/core/X.class ? (C:/MyProject/build/classes/X.class virker naturligvis ikke)
Avatar billede jespersahner Nybegynder
07. september 2011 - 22:26 #5
-> arne_v: Ja core.X ligger i C:/MyProject/build/classes/core/X.class, ellers ville første del af min kode ikke kunne loade core.X. Sagt på en anden måde: Den classloader, som er i stand til at loade core.X, er den samme classloader, som jeg anvender i CharSequenceCompiler - og classloader'en HAR endda foretaget load af core.X, når jeg kalder CharSequenceCompiler (dette er dog formentlig ikke vigtigt)
Avatar billede jespersahner Nybegynder
08. september 2011 - 00:16 #6
-> arne_v: Sorry, det gik lidt for hurtigt, det virker med CharSequenceCompiler compiler=new CharSequenceCompiler(cl, Arrays.asList(new String[] { "-classpath", "C:/MyProject/build/classes"})) - så der er hul igennem.

Men det løser egentlig ikke mit problem, idet ambitionen er at bruge classloader'en alene.
Avatar billede arne_v Ekspert
08. september 2011 - 00:24 #7
Jeg kigger lidt paa det.

Enten senere i aften eller imorgen.
Avatar billede jespersahner Nybegynder
08. september 2011 - 01:31 #8
-> arne_v: Det sætter jeg pris på, jeg håber du også selv får udbytte af det.

Måske misforstår (læs: forstår ikke) jeg classloader'ens rolle i CharSequenceCompiler. Jeg stødte på flg. artikel, som i øv. henviser til den første artikel:
http://atamur.blogspot.com/2009/10/using-built-in-javacompiler-with-custom.html
Her står bla. "The most irritating detail was: all the classes the compiler needs are here in current thread's classloader, but there's no way to tell it about this fact!" I artiklen laves så en CustomClassloaderJavaFileManager (som javac bruger), som udleder classpath ud fra classloader'en, som jeg forstår det - og måske er det så CustomClassloaderJavaFileManager jeg skal bruge.
Avatar billede arne_v Ekspert
12. september 2011 - 03:34 #9
Jeg kan ikke finde noget sted i koden hvor classpath skulle saettes op til compileren.

Du skal have de 2 elementer ind i options.

Du bliver noedt til at rette i koden.

Jeg kan se to mulige maaader at goere det paa:

A) fisk stien ud af classloaderen og tilfoej til options inde i CharSequenceCompiler

B) send sti fremfor classloader med over og tilfoej options og opret classloader inde i CharSequenceCompiler
Avatar billede jespersahner Nybegynder
18. september 2011 - 14:19 #10
->arne_v: Enig. Hvis jeg alligevel skal føje til options, kan jeg i øvrigt lige så godt anvende JavaCompiler frem for CharSequenceCompiler.

En kommentar til hvad du skrev tidligere:
"1) det der skal vaere i classpath skal vaere paa disk - compileren kan ikke bruge noget der kun er i memory"
Enig, så vidt jeg kan læse mig frem til, skal classpath være .zip, .jar eller .class. Jeg undrer mig over, at f.eks. URL ikke er en mulighed, når nu URL godt kan indgå i en classloader. Jeg har dog kunnet finde hacks, hvor man ikke ændrer i classpath men efterfølgende i System-classloader'en, så man her føjer til classpath på runtime, f.eks. http://jodd.org/doc/add-classpath-in-runtime.html - eller http://jimlife.wordpress.com/2007/12/19/java-adding-new-classpath-at-runtime/
Jeg kan godt lide grund-ideen om en "backdoor" til System-classloader'en, hvor man dynamisk kan føje klasser til, også klasser der ikke er på disk men in-memory. En sådan hack løser dog ikke alt, idet f.eks. code completion arbejder på source- og ikke class-niveau, som jeg forstår det.
Hvis man er meget insisterende på at arbejde in-memory i stedet for disk og ikke ønsker at anvende en hack, kan man måske anvende RAMdisk-funktionalitet, men så er vi lidt off-topic.
Til mig selv: God artikel "Dynamic Code Generation with Java Compiler in Java 6" http://www.polarsparc.com/pdf/JavaCompiler.pdf
Avatar billede arne_v Ekspert
19. september 2011 - 04:47 #11
Jeg tror at den IBM kode mere er en demo af mulighederne i 1.6 end det er et faerdigt software library.

Ja - class/jar/zip. Det er ikke alle classloadere som understoetter HTTP.
Avatar billede jespersahner Nybegynder
19. september 2011 - 08:57 #12
->arne_v: Jeg takker for dine input, smid gerne et svar.
Avatar billede arne_v Ekspert
19. september 2011 - 16:10 #13
svar
Avatar billede arne_v Ekspert
19. september 2011 - 16:10 #14
Du har min Java kode til memory to memory kompilering ikke?
Avatar billede jespersahner Nybegynder
19. september 2011 - 17:01 #15
->arne_v: Tænker du på din artikel "Nye features i Java 1.6/6.0"? I så fald, ja.
Avatar billede arne_v Ekspert
19. september 2011 - 20:27 #16
Den.

Og den lidt forbedrede udgave:

package test;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.URI;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;

public class JC {
    public static void dynamicCall(String clznam, String src, String cp, String metnam) throws Exception {
        SpecialClassLoader xcl = new SpecialClassLoader();
        compileMemoryMemory(src, cp, clznam, xcl, System.err);
        Class<?> c = Class.forName(clznam, true, xcl);
        Object o = c.newInstance();
        c.getMethod(metnam, new Class[] { }).invoke(o, new Object[] { });
    }
    public static void compileMemoryMemory(String src, String cp, String name, SpecialClassLoader xcl, PrintStream err) {
        JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
        DiagnosticCollector<JavaFileObject> diacol = new DiagnosticCollector<JavaFileObject>();
        StandardJavaFileManager sjfm = javac.getStandardFileManager(diacol, null, null);
        SpecialJavaFileManager xfm = new SpecialJavaFileManager(sjfm, xcl);
        CompilationTask compile = javac.getTask(null, xfm, diacol, Arrays.asList(new String[] { "-classpath", cp }), null,
                                                Arrays.asList(new JavaFileObject[] { new MemorySource(name, src) }));
        boolean status = compile.call();
        if(err != null) {
            err.println("Compile status: " + status);
            for(Diagnostic<? extends JavaFileObject> dia : diacol.getDiagnostics()) {
                err.println(dia);
            }
        }
    }
}

class MemorySource extends SimpleJavaFileObject {
    private String src;
    public MemorySource(String name, String src) {
        super(URI.create("string:///" + name.replace(".", "/") + ".java"), Kind.SOURCE);
        this.src = src;
    }
    public CharSequence getCharContent(boolean ignoreEncodingErrors) {
        return src;
    }
    public OutputStream openOutputStream() {
        throw new IllegalStateException();
    }
    public InputStream openInputStream() {
        return new ByteArrayInputStream(src.getBytes());
    }
}

class MemoryByteCode extends SimpleJavaFileObject {
    private ByteArrayOutputStream baos;
    public MemoryByteCode(String name) {
        super(URI.create("byte:///" + name.replace(".", "/") + ".class"), Kind.CLASS);
    }
    public CharSequence getCharContent(boolean ignoreEncodingErrors) {
        throw new IllegalStateException();
    }
    public OutputStream openOutputStream() {
        baos = new ByteArrayOutputStream();
        return baos;
    }
    public InputStream openInputStream() {
        throw new IllegalStateException();
    }
    public byte[] getBytes() {
        return baos.toByteArray();
    }
}

class SpecialJavaFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {
    private SpecialClassLoader xcl;
    public SpecialJavaFileManager(StandardJavaFileManager sjfm, SpecialClassLoader xcl) {
        super(sjfm);
        this.xcl = xcl;
    }
    public JavaFileObject getJavaFileForOutput(Location location, String name, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
        name = sibling.getName().substring(1, sibling.getName().length() - 5).replace("/", ".");
        MemoryByteCode mbc = new MemoryByteCode(name);
        xcl.addClass(name, mbc);
        return mbc;
    }
}

class SpecialClassLoader extends ClassLoader {
    private Map<String,MemoryByteCode> m;
    public SpecialClassLoader() {
        super(SpecialClassLoader.class.getClassLoader());
        m = new HashMap<String, MemoryByteCode>();
    }
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        MemoryByteCode mbc = m.get(name);
        if(mbc != null) {
            return defineClass(name, mbc.getBytes(), 0, mbc.getBytes().length);
        } else {
            throw new ClassNotFoundException(name);
        }
    }
    public void addClass(String name, MemoryByteCode mbc) {
        m.put(name, mbc);
    }
}
Avatar billede jespersahner Nybegynder
19. september 2011 - 22:22 #17
->arne_v: Fornemt, tak!
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester