Próbuję pobrać opis kilku Java Beans z pliku XML. Chciałbym dodać do nich adnotacje z @Data
z projektu lombok
, aby automatycznie włączyć konstruktor, równe, hashCode, getters, setters i toString. Chciałbym skompilować je w pamięci, wygenerować kilka instancji (z danymi z tego samego pliku XML) i dodać je do Droolsa, aby w końcu zrobić pewne rozumowanie na temat tych danych.Kompilowanie klasy Java w pamięci za pomocą adnotacji `lombok` i Java JDK 8
Niestety, nie mogę skompilować tych lekcji, dlatego proszę o pomoc!
Poniższy kod pokazuje jak programowo kompilacji klas Java w pamięci:
package example;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.ToolProvider;
public class Simple {
public static void main(String[] args) throws Exception {
String name = "Person";
String content = //
"public class " + name + " {\n" + //
" @Override\n" + //
" public String toString() {\n" + //
" return \"Hello, world!\";\n" + //
" }\n" + //
"}\n";
System.out.println(content);
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
JavaFileManager manager = new MemoryFileManager(compiler.getStandardFileManager(null, null, null));
List<String> options = new ArrayList<String>();
options.addAll(Arrays.asList("-classpath", System.getProperty("java.class.path")));
List<JavaFileObject> files = new ArrayList<JavaFileObject>();
files.add(new MemoryJavaFileObject(name, content));
compiler.getTask(null, manager, null, options, null, files).call();
Object instance = manager.getClassLoader(null).loadClass(name).newInstance();
System.out.println(instance);
}
}
gdzie MemoryFileManager
jest:
package example;
import java.io.IOException;
import java.security.SecureClassLoader;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import javax.tools.StandardJavaFileManager;
public class MemoryFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {
private MemoryJavaClassObject object;
public MemoryFileManager(StandardJavaFileManager manager) {
super(manager);
}
@Override
public ClassLoader getClassLoader(Location location) {
return new SecureClassLoader() {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] b = object.getBytes();
return super.defineClass(name, object.getBytes(), 0, b.length);
}
};
}
@Override
public JavaFileObject getJavaFileForOutput(Location location, String name, Kind kind, FileObject sibling) throws IOException {
object = new MemoryJavaClassObject(name, kind);
return object;
}
}
i MemoryJavaClassObject
jest:
package example;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import javax.tools.SimpleJavaFileObject;
public class MemoryJavaClassObject extends SimpleJavaFileObject {
protected final ByteArrayOutputStream stream = new ByteArrayOutputStream();
public MemoryJavaClassObject(String name, Kind kind) {
super(URI.create("string:///" + name.replace('.', '/') + kind.extension), kind);
}
public byte[] getBytes() {
return stream.toByteArray();
}
@Override
public OutputStream openOutputStream() throws IOException {
return stream;
}
}
i wreszcie MemoryJavaFileObject
jest :
package example;
import java.net.URI;
import javax.tools.SimpleJavaFileObject;
public class MemoryJavaFileObject extends SimpleJavaFileObject {
private CharSequence content;
protected MemoryJavaFileObject(String className, CharSequence content) {
super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
this.content = content;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return content;
}
}
Gdybym uruchomić przykład w pierwszym bloku kodu, pojawia się następujący komunikat, zgodnie z oczekiwaniami:
public class Person {
@Override
public String toString() {
return "Hello, world!";
}
}
Hello, world!
Teraz, jeśli dodać lombok.jar
do mojego projektu i obejmują następujący przykład :
package example;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.ToolProvider;
public class Lombok {
public static void main(String[] args) throws Exception {
String name = "Person";
String content = //
"import lombok.Data;\n" + //
"public @Data class " + name + " {\n" + //
" private String name;\n" + //
"}\n";
System.out.println(content);
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
JavaFileManager manager = new MemoryFileManager(compiler.getStandardFileManager(null, null, null));
List<String> options = new ArrayList<String>();
options.addAll(Arrays.asList("-classpath", System.getProperty("java.class.path")));
List<JavaFileObject> files = new ArrayList<JavaFileObject>();
files.add(new MemoryJavaFileObject(name, content));
compiler.getTask(null, manager, null, options, null, files).call();
Object instance = manager.getClassLoader(null).loadClass(name).newInstance();
System.out.println(instance);
}
}
niestety nie uzyskać spodziewanych rezultatów, ale raczej:
import lombok.Data;
public @Data class Person {
private String name;
}
/Person.java:2: warning: Can't initialize javac processor due to (most likely) a class loader problem: java.lang.NoClassDefFoundError: com/sun/tools/javac/processing/JavacProcessingEnvironment
public @Data class Person {
^
at lombok.javac.apt.Processor.init(Processor.java:84)
at lombok.core.AnnotationProcessor$JavacDescriptor.want(AnnotationProcessor.java:87)
at lombok.core.AnnotationProcessor.init(AnnotationProcessor.java:141)
at com.sun.tools.javac.processing.JavacProcessingEnvironment$ProcessorState.<init>(JavacProcessingEnvironment.java:500)
at com.sun.tools.javac.processing.JavacProcessingEnvironment$DiscoveredProcessors$ProcessorStateIterator.next(JavacProcessingEnvironment.java:597)
at com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:690)
at com.sun.tools.javac.processing.JavacProcessingEnvironment.access$1800(JavacProcessingEnvironment.java:91)
at com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1035)
at com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1176)
at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1173)
at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:859)
at com.sun.tools.javac.main.Main.compile(Main.java:523)
at com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:129)
at com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:138)
at example.Lombok.main(Lombok.java:42)
Caused by: java.lang.ClassNotFoundException: com.sun.tools.javac.processing.JavacProcessingEnvironment
at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 15 more
1 warning
[email protected]
Należy zauważyć, że klasa zostaje skompilowana, a domyślna metoda toString()
jest wykonywana, ponieważ wyświetlane są typowe wyniki. zauważyć również, że jeśli uruchomię byłego przykład, teraz mam następujące:
public class Person {
@Override
public String toString() {
return "Hello, world!";
}
}
/Person.java:1: warning: Can't initialize javac processor due to (most likely) a class loader problem: java.lang.NoClassDefFoundError: com/sun/tools/javac/processing/JavacProcessingEnvironment
public class Person {
^
at lombok.javac.apt.Processor.init(Processor.java:84)
at lombok.core.AnnotationProcessor$JavacDescriptor.want(AnnotationProcessor.java:87)
at lombok.core.AnnotationProcessor.init(AnnotationProcessor.java:141)
at com.sun.tools.javac.processing.JavacProcessingEnvironment$ProcessorState.<init>(JavacProcessingEnvironment.java:500)
at com.sun.tools.javac.processing.JavacProcessingEnvironment$DiscoveredProcessors$ProcessorStateIterator.next(JavacProcessingEnvironment.java:597)
at com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:690)
at com.sun.tools.javac.processing.JavacProcessingEnvironment.access$1800(JavacProcessingEnvironment.java:91)
at com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1035)
at com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1176)
at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1173)
at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:859)
at com.sun.tools.javac.main.Main.compile(Main.java:523)
at com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:129)
at com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:138)
at example.Simple.main(Simple.java:44)
Caused by: java.lang.ClassNotFoundException: com.sun.tools.javac.processing.JavacProcessingEnvironment
at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 15 more
1 warning
Hello, world!
Widocznie, patrząc na komunikat ostrzegawczy przeszedł przez wyjątku lombok
nie zahaczyć dany kompilator poprawnie. Niestety nie udało mi się znaleźć żadnej użytecznej informacji. Mogę tylko myśleć, że może to być lombok
niewłaściwe postępowanie z Java JDK 8. Mam rację?
Czy znasz inny sposób obejścia tego problemu?
Czy na pewno masz wersję ** JDK ** zamiast JRE i plik 'tools.jar' znajduje się w ścieżce klasy? – Holger
Witam @Holger i dziękuję za odpowiedź! Tak, mam Java SDK 8, ale jestem na Macu! I najwyraźniej brakuje 'tools.jar' na Macu (zobacz http://stackoverflow.com/questions/5616318/how-do-you-address-the-issue-of-a-missing-tools-jar-in-a -jdk-in-mac-os-x). Dzięki, że wskazałeś mi właściwy kierunek! –
To jest dziwne ... Właśnie sprawdziłem i mam 'tools.jar' w' $ JAVA_HOME/lib/tools.jar'. Jednak nie jest on obecny w środowisku Eclipse ... W rzeczywistości jest to błąd Eclipse: referencyjne środowisko Java skonfigurowane jest jako środowisko JRE. Włączenie 'tools.jar' sprawia, że wszystko działa. Dzięki jeszcze raz! –