Kompilacja i uruchamianie klas z bazy danych
Dla porównania pokażę też inne, prostsze metody kompilacji
Kompilacja z pliku
Klasa R098_COMP1
package aderby.jdkjre.compilation1; import javax.tools.*; public class R098_COMP1 { public static void main(String[] args) { String file = "aderby/src/resources/compilation/c1/HelloWorld.java"; JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); compiler.run(null, null, null, file); } }
Kompilowana klasa znajduje się w folderze c1. Po uruchomieniu klasy w tym samym folderze pojawia się skompilowana klasa HelloWorld.class. Klasa jest tylko kompilowana – bez uruchamiania.
Kompilacja ze stringa
Klasa JavaSourceFromString
package aderby.jdkjre.compilation2; import java.net.*; import javax.tools.*; public class JavaSourceFromString extends SimpleJavaFileObject { private final String code; public JavaSourceFromString(String name, String code) { super(URI.create( "string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE); this.code = code; } @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) { return code; } public static JavaSourceFromString buildJavaFileObject(String className, String code) { return new JavaSourceFromString(className, code); } }
Klasa R098_COMP2
package aderby.jdkjre.compilation2; import java.util.*; import javax.tools.*; import aderby.DerbyUtil; public class R098_COMP2 { public static void main(String[] args) { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); String klasa = DerbyUtil.buildSourceString(); JavaFileObject jfo = new JavaSourceFromString("HelloWorld", klasa); Iterable<? extends JavaFileObject> toCompile = Collections.singletonList(jfo); Iterable<String> opcje = Arrays.asList("-d", "aderby/src/resources/compilation/c2"); JavaCompiler.CompilationTask task = compiler.getTask(null, null, null, opcje, null, toCompile); boolean compiled = task.call(); if(compiled){ System.out.println("Kompilacja udana"); } else{ System.out.println("Nie udało się skompilować pliku"); } } }
Po uruchomieniu klasy nowa klasa źródłowa budowana jest ze stringu, a następnie kompilowana. Skompilowana klasa pojawia się na dysku. W tym przykładzie nie jest uruchamiana. Oczywiście klasa mogłaby nie pojawiać się na dysku i zostać uruchomiona.
Kompilacja z tablicy bajtów
Klasa JavaClassFromBytes
package aderby.jdkjre.compilation3; import java.io.*; import java.net.*; import javax.tools.*; public class JavaClassFromBytes extends SimpleJavaFileObject{ private final ByteArrayOutputStream stream; public JavaClassFromBytes(String name){ super(URI.create("bytes:///" + name), Kind.CLASS); stream = new ByteArrayOutputStream(); } @Override public OutputStream openOutputStream() throws IOException { return stream; } public byte[] getBytes() { return stream.toByteArray(); } }
Klasa R098_COMP3
package aderby.jdkjre.compilation3; import aderby.DerbyUtil; import aderby.jdkjre.compilation2.JavaSourceFromString; import java.io.*; import java.util.*; import javax.tools.*; public class R098_COMP3 { public static void main(String[] args) { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); final ArrayList<JavaClassFromBytes> klasy = new ArrayList<>(); JavaFileManager fm = compiler.getStandardFileManager(null, null, null); fm = new ForwardingJavaFileManager<>(fm) { @Override public JavaFileObject getJavaFileForOutput( Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { if (className.startsWith("JP")) { JavaClassFromBytes jcfb = new JavaClassFromBytes(className); klasy.add(jcfb); return jcfb; } else { return super.getJavaFileForOutput(location, className, kind, sibling); } } }; JavaFileObject jfo = JavaSourceFromString.buildJavaFileObject( "JPHelloWorld", DerbyUtil.buildSourceString2()); JavaCompiler.CompilationTask task = compiler.getTask(null, fm, null, null, null, Collections.singletonList(jfo)); LinkedHashMap<String, byte[]> mapa = new LinkedHashMap<>(); for(JavaClassFromBytes jcfb : klasy){ mapa.put(jcfb.getName().substring(1), jcfb.getBytes()); } boolean result = task.call(); if(result){ System.out.println("Kompilacja udana"); } else{ System.out.println("kompilacja zakończona niepowodzeniem"); } } }
Po uruchomieniu Klasa źródłowa tworzona jest ze stringu. Potem zamieniana jest na łańcuch bajtów. Kompilacja następuje z łańcucha bajtów. Skompilowana klasa znajduje się jedynie w pamięci. Z pamięci może tez zostać uruchomiona. Tutaj nie pokazujemy jej uruchamiania.
Kompilacja z bazy danych
Kompilacja z bazy danych właściwie nie ma sensu. Lepiej umieścić w bazie danych skompilowane klasy, załadować je i uruchomić. Pokażę to w następnym przykładzie.
Jeśli koniecznie chcesz umieścić w bazie danych klasy źródłowe to zwróć uwagę na metody:
sourceFilesToDerby
i sourceFilesFromDerby
. Te dwie metody z uwzględnieniem trzech poprzednich przykładów oraz przykładu następnego – pozwolą ci to zrobić.
Ładowanie i uruchamianie klas z bazy danych
Klasa AriaClassLoader
package aderby.jdkjre.compilation4; import java.util.*; public class AriaClassLoader extends ClassLoader { private final LinkedHashMap<String, byte[]> classes; public AriaClassLoader(LinkedHashMap<String, byte[]> classes) { this.classes = classes; } @Override public Class<?> findClass(String name) throws ClassNotFoundException { byte[] bytes = classes.get(name); if (bytes == null) { throw new ClassNotFoundException(name + " not found"); } Class<?> cl = defineClass(name, bytes, 0, bytes.length); if (cl == null) { throw new ClassNotFoundException(name + "not found"); } return cl; } }
Klasa R098_COMP4
package aderby.jdkjre.compilation4; import java.util.*; import aderby.DerbyUtil; public class R098_COMP4 { public static void main(String[] args) { DerbyUtil.classesToDerby("aderby/src/resources/compilation/c2"); LinkedHashMap<String, byte[]> mapa = DerbyUtil.classesFromDerby(); DerbyUtil.startClass(mapa, "HelloWorld"); } }
W przykładzie umieszczamy klasę HelloWorld.class
w bazie danych. Następnie załadowujemy tę klasę do pamięci i uruchamiamy. Pliki klas są obecne jedynie w pamięci i w bazie (która może być zaszyfrowana) danych.
Uwagi
Gdy używamy serwera bazy danych plik bazy danych jest umieszczany w folderze projektu, na tym samym poziomie zagnieżdżenia co moduły projektu, ale czasami nie jest bezpośrednio widoczny. Np. w IntelliJ IDEA trzeba zajrzeć do plików na dysku i usunąć bazę danych ręcznie. Oczywiście do usunięcia możesz użyć odpowiedniej metody, która była prezentowana w niektórych z wcześniejszych przykładów.
Pliki do ściągnięcia
Aktualny (tworzony narastająco) plik module-info.java
Aktualny (tworzony narastająco) plik DerbyUtil.java
Pliki tworzone narastająco zastępują poprzednie pliki o tej samej nazwie i działają dla wszystkich wcześniej opublikowanych przykładów we wszystkich wpisach w projekcie. W przypadku pliku module-info.java
może być potrzebne skreślenie niepotrzebnych wpisów.