Compilieren zur Laufzeit

Kai Cierpka

Grünschnabel
Einen schönen guten Tag,

Ich habe ein kleines Problem und hoffe auf gute Vorschläge ;). Mein Programm erzeugt während der Laufzeit je nach Benutzer beliebig neue .java-Files. Diese sollen zu einem späteren Zeitpunkt (während der Laufzeit) per Betätigen eines Buttons compiliert und als Thread gestartet werden.

Mein Problem ist das Compilieren und starten des .java-Files (bzw. .class-Datei) zur Laufzeit.

Es wäre super nett von euch wenn mir einer helfen könnte.
 
Danke Thomas für die erste Hilfestellung. Da ich dies im Rahmen meiner Diplomarbeit benötige und mir etwas die Zeit wegläuft werde ich auf eine der beiden unteren Lösungen zurückgreifen müssen.
Jetzt meine nächste Frage. Gibt es zu diesen Punkten Dokumentationen oder Beispiele? Ich habe schon meine sämtlichen Javabücher durchsucht und Google bemüht jedoch ohne Erfolg.

Das einzige was ich gefunden habe ist folgendes:

Code:
import com.sun.tools.javac.Main; 
... 
String[] args = { "-d", ".", "foo.java" }; 
Main compiler = new Main(); 
compiler.compile(args);

Jedoch kann ich wegen mangelder Dokumentation nicht viel anfangen.

Wäre super nett wenn mir auch hierbei jemand helfen könnte.
 
Zuletzt bearbeitet:
Hallo!

Hier mal ein Beispiel zum dynamischen Compilieren mit dem JDT Eclipse Compiler:
Java:
   package de.tutorials;
   
   import java.io.BufferedOutputStream;
   import java.io.BufferedReader;
   import java.io.ByteArrayOutputStream;
   import java.io.FileInputStream;
   import java.io.FileOutputStream;
   import java.io.FileWriter;
   import java.io.IOException;
   import java.io.InputStream;
   import java.io.InputStreamReader;
   import java.io.Reader;
   import java.util.ArrayList;
   import java.util.HashMap;
   import java.util.List;
   import java.util.Locale;
   import java.util.Map;
   import java.util.StringTokenizer;
   
   import org.eclipse.jdt.core.compiler.IProblem;
   import org.eclipse.jdt.internal.compiler.ClassFile;
   import org.eclipse.jdt.internal.compiler.CompilationResult;
   import org.eclipse.jdt.internal.compiler.Compiler;
   import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
   import org.eclipse.jdt.internal.compiler.ICompilerRequestor;
   import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
   import org.eclipse.jdt.internal.compiler.IProblemFactory;
   import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
   import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
   import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
   import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
   import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
   import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
   
   public class JDTCompilerExample {
   
   	/**
   	 * @param args
   	 */
   	public static void main(String[] args) {
   
   		String compilationUnitContent = "package de.tutorials;\n"
 		 	+ "public class Fooooooo{\n" + "public static void burp(){\n"
 		 	+ "System.out.println(\"burrrrrrrrrrrrrrp\");" + "}\n" + "}";
   
 		final String sourceFile = "E:/eclipse/3.1/eclipse/workspace/de.tutorials/src/Fooooooo.java";
   
   		try {
   			FileWriter fw = new FileWriter(sourceFile);
   			fw.write(compilationUnitContent);
   			fw.flush();
   			fw.close();
   		} catch (IOException e1) {
   			e1.printStackTrace();
   		}
   
   		final String outputDir = "E:/eclipse/3.1/eclipse/workspace/de.tutorials/bin";
   		final String packageName = "de.tutorials";
   		final String targetClassName = "de.tutorials.Fooooooo";
   		final ClassLoader classLoader = JDTCompilerExample.class
   				.getClassLoader();
   
   		final String fileNames[] = { sourceFile };
   		final String classNames[] = { targetClassName };
   
   		final List problemList = new ArrayList();
   
   		class CompilationUnit implements ICompilationUnit {
   
   			String className;
   
   			String sourceFile;
   
   			CompilationUnit(String sourceFile, String className) {
   				this.className = className;
   				this.sourceFile = sourceFile;
   			}
   
   			public char[] getFileName() {
 				return className.toCharArray();
   			}
   
   			public char[] getContents() {
   				char[] result = null;
   				try {
 		 		InputStreamReader isReader = new InputStreamReader(
 		 		 	new FileInputStream(sourceFile));
 		 		Reader reader = new BufferedReader(isReader);
 					if (reader != null) {
 		 			char[] chars = new char[8192];
 		 			StringBuffer buf = new StringBuffer();
 						int count;
 		 			while ((count = reader.read(chars, 0, chars.length)) > 0) {
 		 		 	buf.append(chars, 0, count);
 						}
 		 			result = new char[buf.length()];
 		 		 buf.getChars(0, result.length, result, 0);
   					}
   				} catch (IOException e) {
 					e.printStackTrace();
   				}
   				return result;
   			}
   
   			public char[] getMainTypeName() {
 				int dot = className.lastIndexOf('.');
   				if (dot > 0) {
 		 		return className.substring(dot + 1).toCharArray();
   				}
 				return className.toCharArray();
   			}
   
   			public char[][] getPackageName() {
 		 	StringTokenizer izer = new StringTokenizer(className, ".");
  			 char[][] result = new char[izer.countTokens() - 1][];
 				for (int i = 0; i < result.length; i++) {
 					String tok = izer.nextToken();
 					result[i] = tok.toCharArray();
   				}
   				return result;
   			}
   		}
   
   		final INameEnvironment env = new INameEnvironment() {
   
 			public NameEnvironmentAnswer findType(char[][] compoundTypeName) {
   				String result = "";
   				String sep = "";
  			 for (int i = 0; i < compoundTypeName.length; i++) {
 					result += sep;
 		 		result += new String(compoundTypeName[i]);
 					sep = ".";
   				}
   				return findType(result);
   			}
   
   			public NameEnvironmentAnswer findType(char[] typeName,
 					char[][] packageName) {
   				String result = "";
   				String sep = "";
 				for (int i = 0; i < packageName.length; i++) {
 					result += sep;
 		 		result += new String(packageName[i]);
 					sep = ".";
   				}
   				result += sep;
 				result += new String(typeName);
   				return findType(result);
   			}
   
 			private NameEnvironmentAnswer findType(String className) {
   
   				InputStream is = null;
   				try {
 		 		if (className.equals(targetClassName)) {
 		 		 ICompilationUnit compilationUnit = new CompilationUnit(
 		 		 		sourceFile, className);
 		 			return new NameEnvironmentAnswer(compilationUnit);
   					}
 		 		String resourceName = className.replace('.', '/')
 		 		 	+ ".class";
 		 		is = classLoader.getResourceAsStream(resourceName);
 					if (is != null) {
 		 			byte[] classBytes;
 		 			byte[] buf = new byte[8192];
 		 		 ByteArrayOutputStream baos = new ByteArrayOutputStream(
 		 		 		buf.length);
 						int count;
 		 			while ((count = is.read(buf, 0, buf.length)) > 0) {
 		 		 	baos.write(buf, 0, count);
 						}
 						baos.flush();
 		 			classBytes = baos.toByteArray();
 		 			char[] fileName = className.toCharArray();
 		 		 ClassFileReader classFileReader = new ClassFileReader(
 		 		 		classBytes, fileName, true);
 		 			return new NameEnvironmentAnswer(classFileReader);
   					}
   				} catch (IOException e) {
 					e.printStackTrace();
 		 	} catch (org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException e) {
 					e.printStackTrace();
   				} finally {
 					if (is != null) {
 						try {
 		 		 	is.close();
 		 			} catch (IOException exc) {
 		 		 	// Ignore
 						}
   					}
   				}
   				return null;
   			}
   
   			private boolean isPackage(String result) {
 				if (result.equals(targetClassName)) {
 					return false;
   				}
 		 	String resourceName = result.replace('.', '/') + ".class";
 		 	InputStream is = classLoader.getResourceAsStream(resourceName);
   				return is == null;
   			}
   
   			public boolean isPackage(char[][] parentPackageName,
 					char[] packageName) {
   				String result = "";
   				String sep = "";
 				if (parentPackageName != null) {
 		 		for (int i = 0; i < parentPackageName.length; i++) {
 						result += sep;
 		 			String str = new String(parentPackageName[i]);
 						result += str;
 						sep = ".";
   					}
   				}
 				String str = new String(packageName);
 				if (Character.isUpperCase(str.charAt(0))) {
 					if (!isPackage(result)) {
 						return false;
   					}
   				}
   				result += sep;
   				result += str;
   				return isPackage(result);
   			}
   
   			public void cleanup() {
   			}
   		};
   
   		final IErrorHandlingPolicy policy = DefaultErrorHandlingPolicies
   				.proceedWithAllProblems();
   
   		final Map settings = new HashMap();
   		settings.put(CompilerOptions.OPTION_LineNumberAttribute,
   				CompilerOptions.GENERATE);
   		settings.put(CompilerOptions.OPTION_SourceFileAttribute,
   				CompilerOptions.GENERATE);
   		settings.put(CompilerOptions.OPTION_ReportDeprecation,
   				CompilerOptions.IGNORE);
   		settings.put(CompilerOptions.OPTION_LocalVariableAttribute,
   				CompilerOptions.GENERATE);
   		settings
 		 	.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_1_4);
   
   		settings.put(CompilerOptions.OPTION_TargetPlatform,
   				CompilerOptions.VERSION_1_4);
   
   		final IProblemFactory problemFactory = new DefaultProblemFactory(Locale
   				.getDefault());
   
   		final ICompilerRequestor requestor = new ICompilerRequestor() {
   			public void acceptResult(CompilationResult result) {
   				try {
 					if (result.hasProblems()) {
 		 			IProblem[] problems = result.getProblems();
 		 			for (int i = 0; i < problems.length; i++) {
 		 		 	IProblem problem = problems[i];
 		 		 	if (problem.isError()) {
 		 		 		String name = new String(problems[i]
 		 		 		 	.getOriginatingFileName());
 		 		 		problemList.add(new StringBuffer("Problem in ")
 		 		 		 	.append(name).append("\nMessage: ")
 		 		 		 	.append(problem.getMessage()).append(
 		 		 		 			"\nFound at: ").append(
 		 		 		 		 problem.getSourceLineNumber())
 		 		 		 	.toString());
   
  					  }
 						}
   					}
 					if (problemList.isEmpty()) {
 		 			ClassFile[] classFiles = result.getClassFiles();
 		 			for (int i = 0; i < classFiles.length; i++) {
 		 		 	ClassFile classFile = classFiles[i];
 		 		 	char[][] compoundName = classFile.getCompoundName();
 		 		 	String className = "";
 		 		 	String sep = "";
 		 		 	for (int j = 0; j < compoundName.length; j++) {
 		 		 		className += sep;
 		 		 		className += new String(compoundName[j]);
 		 		 		sep = ".";
  					  }
 		 		 	byte[] bytes = classFile.getBytes();
 		 		 	String outFile = outputDir + "/"
 		 		 			+ className.replace('.', '/') + ".class";
 		 		 	FileOutputStream fout = new FileOutputStream(
 		 		 			outFile);
 		 		 	BufferedOutputStream bos = new BufferedOutputStream(
 		 		 			fout);
 		 		 	bos.write(bytes);
 		 		 	bos.close();
 						}
   					}
   				} catch (IOException e) {
 					e.printStackTrace();
   				}
   			}
   		};
   
   		ICompilationUnit[] compilationUnits = new ICompilationUnit[classNames.length];
   		for (int i = 0; i < compilationUnits.length; i++) {
   			String className = classNames[i];
 			compilationUnits[i] = new CompilationUnit(fileNames[i], className);
   		}
   		Compiler compiler = new Compiler(env, policy, settings, requestor,
   				problemFactory);
   		compiler.compile(compilationUnits);
   
  //Hier probieren wir die gerade erzeugte Klasse einfach mal aus... ;-)
   		try {
 			Class.forName(targetClassName).getMethod("burp", new Class[0])
 					.invoke(null, new Object[0]);
   		} catch (Exception e) {
   			e.printStackTrace();
   		}
   
   	}
   }
Bei der Tomcat Distribution (5.5.9) findet man das passende jar (jasper-compiler-jdt.jar)
unter jakarta-tomcat-5.5.9\common\lib.

Gruß Tom
 
Super..... Danke Thomas im Voraus! Ich brauche jetzt erst einmal einen Augenblick um mich da einzuarbeiten. Falls weitere Fragen auftreten sollten werde ich sie hier posten.

MfG Kai
 
Ich habe mich in dein gepostetes Codefragment eingearbeitet und muss sagen "nice". Jedoch wirft dieses leider neue Fragen auf.

1. So wie ich das durch Test herausgefunden habe können nur Javafiles compiliert werden, welche sich im Projektordner (Eclipse-Workspace\MyProjekt) befinden. Besteht auch eine Möglichkeit, Dateien außerhalb dieses Ordners zu compilieren? Die Dateien, die es zu compilieren gibt, liegen momentan unter d:\HBot\Package\.....

2. Oder eine anderer Weg: Besteht die Möglichkeit im Projekttree neue Packages anzulegen? Das es durch rechts-klick und dann "neu -> Package" geht ist mir klar, jedoch wenn ich in einen bereits bestehendem Package während mein Programm läuft einen neuen Ordner anlege, wird dieser nach Aktualisieren des Projektes unter Eclipse wie folgt dargestellt: PackageOld.PackageNew
jedoch wäre es wünschenswert folgt aussehen: PackageOld-->PackagNew

Ich habe den oben geposteten Quellcode mit meiner Ordnerhierarchie angepasst jedoch werden Datein ausserhalb der Projektordners nicht compiliert.

Es wäre super klasse wenn mir einer helfen könnte.

MfG Kai
 
Ich kann mich der 1. Frage nur anschließen und Fragen ob man es auch irgendwie hinbekommen Dateien oder insbesondere ganze Projekte, die nicht im Eclipse workspace liegen dynamisch zu compelieren.
 
Zurück