Avatar billede p_gaard Juniormester
25. februar 2008 - 13:35 Der er 15 kommentarer og
1 løsning

kompilering og kørsel af klasse (i memory)

Hej,

Jeg har et godt stykke tid kørt med en løsning hvor mit program skriver en java-klasse ned i en fil, kompilerer denne hvorefter den indlæser/kører vha. classloader. Det må kunne gøres smartere.

Jeg har fundet nedenstående kode som overordnet set skulle kompilere streng i memory og køre en metode, lige hvad jeg skal bruge. Jeg kan ikke få den til at virke. Problemet er en nullpointerexception på linje 90.
mht. linjen "getResult()" er der også noget galt. Evt skal denne fjernes eller der mangler noget.


Er der nogen som kender til problemet?





----------------------------------

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

import javax.tools.*;

import javax.tools.JavaFileObject.Kind;
import javax.tools.JavaCompiler.CompilationTask;

public class OnTheFlyInRAM {

static JavaFileObject generateJavaSourceCode() {

final String source =
"package just.generated;\n" +
"public class Hello {\n" +
"public static void main(String... args) {\n" +
"System.out.println(new Object() {\n" +
"public String toString() {\n" +
"return \"just hello!\";\n" +
"}\n" +
"});\n" +
"}\n" +
"}";

return new SimpleJavaFileObject(toURI("Hello.java"), JavaFileObject.Kind.SOURCE) {

@Override
public CharSequence getCharContent(boolean
ignoreEncodingErrors)
throws IOException, IllegalStateException,
UnsupportedOperationException {
return source;
}

};
}


static class RAMJavaFileObject extends SimpleJavaFileObject {

RAMJavaFileObject(String name, Kind kind) {
super(toURI(name), kind);
}

ByteArrayOutputStream baos;

@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors)
throws IOException, IllegalStateException,
UnsupportedOperationException {
throw new UnsupportedOperationException();
}

@Override
public InputStream openInputStream() throws IOException,
IllegalStateException, UnsupportedOperationException {
return new ByteArrayInputStream(baos.toByteArray());
}

@Override
public OutputStream openOutputStream() throws IOException,
IllegalStateException, UnsupportedOperationException {
return baos = new ByteArrayOutputStream();
}

}

getResult()
public static void main(String[] args) throws Exception {

JavaCompiler compiler =
ToolProvider.getSystemJavaCompiler();

final Map<String, JavaFileObject> output =
new HashMap<String, JavaFileObject>();

DiagnosticCollector<JavaFileObject> diagnostics =
new DiagnosticCollector<JavaFileObject>();

JavaFileManager jfm = new
ForwardingJavaFileManager<StandardJavaFileManager> (
compiler.getStandardFileManager(diagnostics,null,null)) {

@Override
public JavaFileObject getJavaFileForOutput(Location location,
String name,
Kind kind,
FileObject sibling) throws IOException {
JavaFileObject jfo = new RAMJavaFileObject(name, kind);
output.put(name, jfo);
return jfo;
}

};

CompilationTask task = compiler.getTask(
null, jfm, diagnostics, null, null,
Arrays.asList(generateJavaSourceCode()));

if (! task.call()) {
for(Diagnostic dm : diagnostics.getDiagnostics())
System.err.println(dm);
throw new RuntimeException("Compilation failed");
}

System.out.println("generated classes: "+ output.keySet());

ClassLoader cl = new ClassLoader() {

@Override
protected Class<?> findClass(String name) throws
ClassNotFoundException {
JavaFileObject jfo = output.get(name);
if (jfo != null) {
byte[] bytes = ((RAMJavaFileObject)
jfo).baos.toByteArray();
return defineClass(name, bytes, 0, bytes.length);
}
return super.findClass(name);
}

};

Class<?> c = Class.forName("just.generated.Hello", false, cl);
c.getMethod("main", String[].class)
.invoke(null, new Object[] {args});

}

private static URI toURI(String name) {
try {
return new URI(name);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}

}
Avatar billede arne_v Ekspert
25. februar 2008 - 15:31 #1
Det er en ny feature i Java 1.6.

Jeg har ogsaa nogle eksempler i http://www.eksperten.dk/artikler/941 paa
fil->fil og mem->fil.

Men det er en lidt tricky feature.

Vil du have mem->fil eller mem->mem ?
Avatar billede p_gaard Juniormester
25. februar 2008 - 17:42 #2
Hej Arne,

Da jeg primært står med en tekststreng bestående af f.eks:

(1) public class a {bla.bla.bla..new class b() ...}

og ikke er interesseret i nogle mellemresultater i filer eller nogle class-fil. i den anden ende må det vel være mem>mem.

Tekststrengen (klassen) skal kompileres i mem og dens metode afvikles i mem.

Jeg er yderst interesseret i hvis du har noget kode som kan løse problemet.

(mit næste problem bliver vel så at få klassen a til at se klassen b i kompilerings- og afviklingsøjeblikket)
Avatar billede arne_v Ekspert
28. februar 2008 - 05:36 #3
Det her eksempel virker hos mig:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
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 JC16 {
    public static void main(String[] args) throws Exception {
        testFileFile();
        testMemoryFile();
        testFileMemory();
        testMemoryMemory();
    }
    public static void testFileFile() throws Exception {
        PrintWriter pw = new PrintWriter(new FileWriter("Temp2.java"));
        pw.println("public class Temp2 {");
        pw.println("    public void test() {");
        pw.println("        System.out.println(\"Temp2 OK\");");
        pw.println("    }");
        pw.println("}");
        pw.close();
        compileFileFile("Temp2.java", System.err);
        Class<?> c = Class.forName("Temp2");
        Object o = c.newInstance();
        c.getMethod("test", new Class[] { }).invoke(o, new Object[] { });
    }
    public static void compileFileFile(String fnm, PrintStream err) {
        JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
        DiagnosticCollector<JavaFileObject> diacol = new DiagnosticCollector<JavaFileObject>();
        StandardJavaFileManager sjfm = javac.getStandardFileManager(diacol, null, null);
        CompilationTask compile = javac.getTask(null, sjfm, diacol, Arrays.asList(new String[] { }), null,
                                                sjfm.getJavaFileObjects(new String[] { fnm }));
        boolean status = compile.call();
        if(err != null) {
            err.println("Compile status: " + status);
            for(Diagnostic<? extends JavaFileObject> dia : diacol.getDiagnostics()) {
                err.println(dia);
            }
        }
    }
    public static void testMemoryFile() throws Exception {
        String src = "public class Temp3 { public void test() { System.out.println(\"Temp3 OK\"); } }";
        compileMemoryFile(src, "Temp3", System.err);
        Class<?> c = Class.forName("Temp3");
        Object o = c.newInstance();
        c.getMethod("test", new Class[] { }).invoke(o, new Object[] { });
    }
    public static void compileMemoryFile(String src, String name, PrintStream err) {
        JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
        DiagnosticCollector<JavaFileObject> diacol = new DiagnosticCollector<JavaFileObject>();
        StandardJavaFileManager sjfm = javac.getStandardFileManager(diacol, null, null);
        CompilationTask compile = javac.getTask(null, sjfm, diacol, Arrays.asList(new String[] { }), 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);
            }
        }
    }
    public static void testFileMemory() throws Exception {
        PrintWriter pw = new PrintWriter(new FileWriter("Temp4.java"));
        pw.println("public class Temp4 {");
        pw.println("    public void test() {");
        pw.println("        System.out.println(\"Temp4 OK\");");
        pw.println("    }");
        pw.println("}");
        pw.close();
        SpecialClassLoader xcl = new SpecialClassLoader();
        compileFileMemory("Temp4.java", xcl, System.err);
        Class<?> c = Class.forName("Temp4", true, xcl);
        Object o = c.newInstance();
        c.getMethod("test", new Class[] { }).invoke(o, new Object[] { });
    }
    public static void compileFileMemory(String fnm, 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[] { }), null,
                                                sjfm.getJavaFileObjects(new String[] { fnm }));
        boolean status = compile.call();
        if(err != null) {
            err.println("Compile status: " + status);
            for(Diagnostic<? extends JavaFileObject> dia : diacol.getDiagnostics()) {
                err.println(dia);
            }
        }
    }
    public static void testMemoryMemory() throws Exception {
        String src = "public class Temp5 { public void test() { System.out.println(\"Temp5 OK\"); } }";
        SpecialClassLoader xcl = new SpecialClassLoader();
        compileMemoryMemory(src, "Temp5", xcl, System.err);
        Class<?> c = Class.forName("Temp5", true, xcl);
        Object o = c.newInstance();
        c.getMethod("test", new Class[] { }).invoke(o, new Object[] { });
    }
    public static void compileMemoryMemory(String src, 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[] { }), 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 + ".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 + ".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 {
        MemoryByteCode mbc = new MemoryByteCode(name);
        xcl.addClass(name, mbc);
        return mbc;
    }
}

class SpecialClassLoader extends ClassLoader {
    private Map<String,MemoryByteCode> m;
    public SpecialClassLoader() {
        m = new HashMap<String, MemoryByteCode>();
    }
    protected Class<?> findClass(String name) {
        MemoryByteCode mbc = m.get(name);
        return defineClass(name, mbc.getBytes(), 0, mbc.getBytes().length);
    }
    public void addClass(String name, MemoryByteCode mbc) {
        m.put(name, mbc);
    }
}
Avatar billede arne_v Ekspert
28. februar 2008 - 05:36 #4
Du skal kigge på testMemoryMemory !
Avatar billede p_gaard Juniormester
03. marts 2008 - 14:50 #5
testMemoryMemory kører fint hos mig. Jeg har dog ikke fået den kompilerede klasse i memory til at åbne objekter af klasser beliggende på disk. Det ville være fint hvis man kunne pege på et sted hvor der ligger en række klasser.
Avatar billede arne_v Ekspert
03. marts 2008 - 15:47 #6
Proev med:

Arrays.asList(new String[] { "-classpath", "/dir/foobar.jar" })

som 4. argument til getTask.
Avatar billede p_gaard Juniormester
04. marts 2008 - 20:28 #7
Tak for det, jeg vil pr&#248;ve...
Avatar billede arne_v Ekspert
31. marts 2008 - 04:45 #8
OK ?
Avatar billede p_gaard Juniormester
16. april 2008 - 11:28 #9
Jeg vil gerne lige lade denne tråd stå åben...
Avatar billede p_gaard Juniormester
13. august 2008 - 00:47 #10
Tilbage til denne!. Jeg har i aften fået undersøgt -classpath som 4. qargument til getTask:

Arrays.asList(new String[] { "-classpath", "C:/arb/Java2/pkkcms_231/Hyperlink.class"})

får flg. fejl 

Compile status: false
null
string:///Temp5.java:16: cannot find symbol
symbol  : class Hyperlink
location: class Temp5
string:///Temp5.java:16: cannot find symbol
symbol  : class Hyperlink
location: class Temp5

Hvem kan gennemskue dette?
Avatar billede arne_v Ekspert
13. august 2008 - 02:48 #11
Den er også forkert.

Arrays.asList(new String[] { "-classpath", "C:/arb/Java2/pkkcms_231"})

eller

Arrays.asList(new String[] { "-classpath", "C:/arb/Java2"})

afhængig af om pkkcms_231 er en package.
Avatar billede p_gaard Juniormester
16. oktober 2008 - 23:45 #12
Det kører fint nu, men kun under eclipse. Når jeg kører det under tomcat i mappen pkkcms_lib .../web-inf/pkkcms_lib så får jeg fejlen:

java.lang.NoClassDefFoundError: javax/tools/DiagnosticListener

Denne fejl har jeg forsøgt at tackle ved at på forskellige måder referere til javax og tools, men uden held.

Ved du hvad jeg kan gøre?
Avatar billede arne_v Ekspert
17. oktober 2008 - 00:15 #13
Kører Tomcat med Java 1.6 som Java version ?
Avatar billede p_gaard Juniormester
17. oktober 2008 - 12:39 #14
Jeg har nu sat tomcat til java version jdk1.6.0_010 og fejlen vedr. DiagnosticListener er væk.

Til gengæld får jeg nu:

NullPointerException
      at SpecialClassLoader.findClass(JC16.java:195)
      at java.lang.classLoader.loadClass(ClassLoader.java:307)
      ...
      ...
Avatar billede p_gaard Juniormester
17. oktober 2008 - 12:44 #15
Jeg har nu sat tomcat til java version jdk1.6.0_010 og fejlen vedr. DiagnosticListener er væk. Alt fungerer ok når jeg kører fra eclipse.



Til gengæld får jeg nu output i tomcat (det ser ud til at kompileringen er udført med success):

compile-status:true
NullPointerException
      at SpecialClassLoader.findClass(JC16.java:195)
      at java.lang.classLoader.loadClass(ClassLoader.java:307)
      ...
      ...
Avatar billede p_gaard Juniormester
21. oktober 2008 - 10:28 #16
Forresten er spørgsmålet besvaret. Det går fint med at oprette klasser i memory, kompilere og kalde en metode. Sidstnævnte må være et apache/tomcat-java problem.
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

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