Zielony Smok - logo witryny

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.

Pliki po kompilacji

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.