Programm zum Kompilieren von java-Dateien

kwonilchang

Aktives Mitglied
Hallo zusammen!

Im Rahmen eines Projekts möchte ich innerhalb einer Webanwendung einen Compilecheck für java-Dateien anbieten. Es sollen also Dateien hochgeladen werden, die dann auf Kompilierbarkeit hin geprüft werden sollen. Hierzu habe ich folgendes Beispiel leicht angepasst:

Java:
private boolean compile(String javaFile) throws IOException {
Process p = Runtime.getRuntime().exec(“javac “ + javaFile);
try {
p.waitFor();
}
catch (InterruptedException ie) {
System.out.println(ie);
}
int ret = p.exitValue();
return ret == 0;
}

Ich habe nun folgendes Problem: Lade ich eine nicht-kompilierbare Datei hoch, zeigt mir der Check an, dass die Datei nicht kompiliert werden kann. Lade ich nun die gleiche Datei auf einem anderen Rechner hoch, bleibt die Anwendung bei p.waitFor hängen. Ich habe auch ausprobiert, mehrere zusammengehörige Dateien (main-Programm und darin benutzte Klasse) zu testen. Ergebnisse wie oben auf zwei Rechnern verschieden. Offenbar kann jedoch bei einem Fehler in einer Klasse noch erkannt werden, dass es nicht kompiliert. Sind in der Klasse aber zwei Fehler, landet man wieder in der Endlosschleife.

Ich bin ziemlich ratlos, da ich mir die verschiedenen Ergebnisse nicht erklären kann. Ich weiß auch nicht, wie ich die Endlosschleife unterbrechen/vermeiden könnte. Habe überlegt, einen zusätzlichen Prozess anzulegen, der nach einem bestimmten Timeout einfach den kompilierenden Prozess unterbricht. Das lässt sich meines Erachtens aber nicht einsetzen, da es ja möglich wäre, dass ein größeres Programm länger zum Kompilieren brauchen würde... Hat jemand einen Tipp, wie ich hier am besten vorgehen kann? Oder kann mir vielleicht jemand erklären, wie die verschiedenen Ergebnisse zustande kommen?

Danke schonmal für Eure Antworten!
 
1. Warum willst du das übers Internet machen?
2. Wenn du das so probierst, gibst du denn auch gleich die class-Datei zurück?
3. Gib mal alle Streams des Programms aus.
 
Wenn du deinen Prozess startest solltest du immer den Output- und Errorstream des Prozesses auslesen. Sonst kann es vorkommen, dass waitfor blockiert.
 
Das wird dir in dieser Art nut begrenzt was bringen, da du so nur Abhängigkeiten an die Standard-RTL verwenden kannst. Ansonsten gibt es auch noch den ScriptEngineManager (Scripting for the Java Platform). Wenn deine Anwendung in einem JDK ausgeführt wird hast du auch Java als Sprache für dynamische Kompilierung / Ausführung im Angebot. Du brauchst dann den Javac nicht "fernsteuern".
 
Guten Morgen,

danke für die Antworten bisher.

@Runtime, ElkeB: Das Kompilieren übers Internet ist eine Vorgabe. Bisher überprüfe ich nur, ob die Klasse kompilierbar ist, ohne eine class-Datei zurückzugeben. An das Ausgeben der Streams habe ich auch gedacht. Den Errorstream nutzte ich auch für die Anzeige der aufgetretenen Fehler am Ende des Checks. Vorher ist der Stream leer, bzw. wird ja wegen der Endlosschleife in waitFor blockiert. Meine Überlegung gestern war noch, in einer while-Schleife vom Errorstream zu lesen, solange bis hier eine Ausgabe gefunden werden kann. Dann den Prozess abbrechen. Wg. der Endlosschleife in waitFor funktioniert das in der Form aber auch nicht. Gibt es hier eine andere Möglichkeit, um festzustellen, ob etwas in den Stream geschrieben wurde?

@Noctarius: Mit Scripting habe ich bisher noch nie gearbeitet. Aber ich werde jetzt gleich mal nachlesen, wie das dynamische Kompilieren funktioniert.

Viele Grüße,

kwonilchang
 
Hallo nochmal,

ich habe nun mal das dynamische Kompilieren ausprobiert. Dazu habe ich die tools.jar in mein Webprojekt importiert. Lasse ich nun den Check laufen, bekomme ich den Fehler

Java:
java.lang.ClassNotFoundException: com.sun.tools.javac.Main
. Das kann ich mir im Augenblick noch nicht erklären.

@Runtime: Ich muss gestehen, dass ich mit Threads noch nie wirklich programmiert habe... Daher hab ich jetzt auch keine genaue Vorstellung davon, was Du mit "schlafen lassen" meinst :-(. Der Prozess, denn ich über Runtime.exec() erzeuge, ist doch ein eigener Thread, oder? Hier habe ich aber keine Möglichkeit, den Prozess schlafen zu lassen, oder geht das mit wait()? Solange ich das exec nicht ausführe, sind die Streams ja auch noch leer. Vielleicht habe ich da eine falsche Vorstellung vom Ablauf in dieser Methode. Könntest Du mir hier bitte das genaue Vorgehen erklären? Oder mir sagen, unter welchen Stichpunkten ich genau googlen muss, da ich nichts passendes finden kann. Danke!
 
Damit meine ich, du sollst den Prozess starten, dann in deinem Prgramm Thread.sleep(10000) aufrufen und dann die Streams ausgeben.
 
Danke für die schnelle Antwort, Runtime. Ich hab gleich mal den ersten Versuch gemacht mit der Datei, die dauernd in der Endlosschleife hing. Hat wunderbar geklappt. Ich werds mal noch mit einer geringeren Wartezeit probieren. Ich stell mir das so vor: Ich schicke den Thread schlafen, lese den Errorstream aus. Steht was drin, beende den Check. Mein Problem ist jetzt dabei nur noch, ein vernünftigen Wert für das Schlafen zu finden. Ich möchte natürlich vermeiden, eigentlich kompilierbare große Dateien als nicht-kompilierbar darzustellen. Auf der anderen Seite soll der Benutzer aber auch nicht zu lange auf das Ergebnis warten müssen... Welche Wartezeit würdest Du mir hier empfehlen?
 
Das ist recht unsicher. Mach ein paar Dateien, die du dem Programm dazulegst. Die kannst du dann alle 10min etwa neu kompilieren, die Zeit messen und beim grössten Wert noch eine Sekunde dazurechnen.
 
Hallo nochmal,

ich habe nun mal das dynamische Kompilieren ausprobiert. Dazu habe ich die tools.jar in mein Webprojekt importiert. Lasse ich nun den Check laufen, bekomme ich den Fehler

Java:
java.lang.ClassNotFoundException: com.sun.tools.javac.Main
. Das kann ich mir im Augenblick noch nicht erklären.

Starte dein Programm mal explizit im JDK! Im JRE fehlen gewisse Klassen.
 
danke für den Hinweis. Ich hab tools.jar jetzt auch mal in jre/lib reinkopiert. Jetzt klappt der Check auch in der Webanwendung. Da muss dann wohl die tools.jar entsprechend in den classpath des Webservers eingebunden werden. Eigentlich hatte ich das Archiv direkt in den build-path meiner Webanwendung gelegt, hier wird sie aber offenbar ignoriert...
 
Hallo!

Ich muss hier nochmal nachhaken. Ich hatte ja mit dynamischer Kompilierung den Check in meiner Webanwendung zum Laufen gebracht. Teste ich einzelne Dateien, wird korrekt ausgegeben, ob kompilierbar oder nicht.

Wenn ich aber nun ganze Projekte mit packages hochlade, funktioniert der compilecheck nicht immer richtig. Ich dachte immer, ich muss zuerst die Main-Klasse kompilieren und der compiler "sucht" sich die darin verwendeten Klassen dann selbst aus dem package. Offenbar ist dies aber nicht der Fall, da ich "compile fehlgeschlagen" angezeigt bekomme, wenn ich ein komplilierbares Projekt kompilieren möchte.

Leider finde ich zur Funktionsweise des Javacompilers im Internet nix passendes. Kann mir einer von Euch bei diesem Problem weiterhelfen?

Danke schonmal und viele Grüße,

kwonilchang
 
Hm, also recht viel zu zeigen gibts da eigentlich net. Hier mal die Methode, in der compiliert wird:

Java:
private String checkData(String filename)  {
	String[] javacCmdLineArgs = { "-d", System.getProperty("user.dir"),
		filename };
	
	int status = com.sun.tools.javac.Main.compile(javacCmdLineArgs);
	
	boolean isPassed = (status == 0);
	String result = "";
	
	if (!isPassed) {
	    result = result + "Could not compile.";
	} else {
	    result = result + "Compilation was successfull.";
	}

	return result;
    }

Muss ich die Methode doch zuerst auf die verwendeten Klassen und dann auf die Hauptklasse des Projekts anwenden? Versteh das net, wenn ich in der Konsole die Hauptklasse mit javac aufrufe, klappt das copile ja auch...
 
danke für den Link. Bin aber leider immer noch nicht weitergekommen. Ich hab im args-Array das "-d" etc. durch "-sourcepath" und die einzelnen Pfade zu den Klassen ersetzt. Dies ist zwar überflüssig, weil sich die Dateien eh alle im gleichen Ordner befinden, aber ich wollts mal ausprobieren.

Klasse1 wird compiliert. Meine Main-Klasse Klasse2 legt ein Objekt der Klasse1 an. Beim Aufruf Klasse1 k = new Klasse1(); meckert der compiler nun: "cannot find symbol" bei K im new-Statement... Dies bedeutet doch, dass er die Klasse1 nicht finden kann, oder?
 

Zurück
Oben