Zwykła kompilacja

Klasa R098_COMP1
   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 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) {
        JavaSourceFromString jsfs = new JavaSourceFromString(className, code);
        return jsfs;
    }

Klasa R098_COMP2
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
String klasa = DerbyUtil.buildSourceString();
JavaFileObject jfo = new JavaSourceFromString("HelloWorld", klasa);
Iterable toCompile = Collections.singletonList(jfo);
Iterable 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.
Więcej w następnym przykładzie.

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 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 klasy = new ArrayList<>();
		JavaFileManager fm = compiler.getStandardFileManager(null, null, null);
		fm = new ForwardingJavaFileManager<>(fm) {
			@Override
			public JavaFileObject getJavaFileForOutput(
					JavaFileManager.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 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 – pozwola 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 LinkedHashMap classes;

    public AriaClassLoader(LinkedHashMap 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
DerbyUtil.classesToDerby("aderby/src/resources/compilation/c2");
LinkedHashMap 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

jdkjre.zip (zestaw klas wymienionych w tekście)
compilation.zip (zestaw zasobów wymienionych w tekście)
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.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *