# Java Compiler und JRE



## Blob (15. Dez 2012)

Hi,

ich muss zur Laufzeit Code kompilieren und benutze dazu die Java Compiler API. Das läuft einwandfrei, doch wenn ich außerhalb der IDE die JAR öffne, liefert mir 
	
	
	
	





```
ToolProvider.getSystemJavaCompiler()
```
 den unschönen Wert 
	
	
	
	





```
null
```
. 

JRE enthält den Compiler nicht. Habe in NetBeans nachgeschaut: Das benutzte JDK1.6 enthält die tools.jar nicht. Also habe ich das aktuelle JDK heruntergeladen, die tools.jar extrahiert und dann in NetBeans ins Projekt eingebunden (direkt über Add JAR/folder). Das macht mein Projekt recht groß, aber das kann mir egal sein. Aber weiterhin liefert 
	
	
	
	





```
ToolProvider.getSystemJavaCompiler()
```


```
null
```
. Was kann ich nun noch tun ?

Gruß,
Nils


----------



## Bernd Hohmann (15. Dez 2012)

Das kann jetzt nur was mit dem Classpath sein. Ich hab das komplette JDK installiert, da funktioniert es.


```
import javax.tools.*;

public class JavaCompilerTest {

	public static void main(String[] args) {

		JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
		System.out.println(jc.toString());
	}

}
```

funktioniert hier innerhalb von Eclipse und auch auf der Kommandozeile.

Bernd


----------



## Blob (15. Dez 2012)

Entschuldigung dass ich oben doch glatt die Tags vergessen habe, habe den Hinweis durchgelesen und doch wieder vergessen, bin es eher gewohnt mehrzeilige Codeausschnitte in Tags zu packen.

Habe das Testprojekt nun auch angelegt und sogar extra mal Eclipse installiert. Mir war zwar klar, dass das an sich wohl nichts am Fehler ändert, aber um besser vergleichen zu können ist es sicherlich hilfreich. Es ist in Eclipse genauso wie in NetBeans: 
	
	
	
	





```
com.sun.tools.javac.api.JavacTool@5d5cd49f
```
 in Eclipse/NetBeans und 
	
	
	
	





```
Exception in thread "main" java.lang.NullPointerException at test.main(test.java:9)
```
 von der Konsole aus. 

Mir geht es darum, dass der Benutzer nicht das JDK installiert haben muss, es mitzuliefern wäre aber kein Problem, falls das irgendwie was nutzt, was ich kaum glaube. Der Grund weshalb es zu der NullPointerException kommt ist eben, dass der Befehl [c]java -jar <...>.jar[/c] von der Konsole wohl von JRE ausgeht, irgendein Pfad muss da falsch sein. Aber gerade das mit den Pfaden steht ja wieder im Widerspruch zum obigen, denn Pfade heißt dass man den Pfad zur JDK-Installation angäbe, was wiederum hieße, dass der Benutzer das installiert haben müsste - gerade das will ich ja vermeiden.

Ich habe außerdem festgestellt: Wenn ich (in NetBeans wieder, aber dürfte egal sein) die tools.jar einfüge, dann tritt auch bei der Ausführung in NetBeans selbst der null-Fehler auf. Eigentlich sollte das Einfügen der tools.jar das Gegenteil bewirken, was es offensichtlich nicht tut. 

Generell: Mir ist im Prinzip egal wie ich die Java-Klassen zur Laufzeit kompiliere, ich brauche nichts weiter als eine class-Datei der Klasse am Ende, den Rest habe ich im Programm abgekapselt, ich gehe zumindest davon aus dass ich mit ClassLoadern keine Probleme haben werde. Wenn Euch also eine weitere Idee einfällt, wie man zur Laufzeit kompiliert, dann wäre das sicher auch eine Lösung, es ist glücklicherweise nur ein kleiner Teil im Programm. Nur schön wäre wenn ich bei NetBeans bleiben könnte, ich hatte gelesen, dass Eclipse zum Beispiel einen Standalone-Compiler hat. In dem sehe ich deshalb mehr eine Notlösung. 

Kleiner Nachtrag, da gerade erst gesehen: In Eclipse sieht man auf den ersten Blick, dass Eclipse zum Ausführen das Programm java nimmt, das im JDK-Verzeichnis unter bin ist. Wenn ich aber von Kommandozeile vorgehe, nehme ich entsprechend standardmäßig die der JRE und dann kracht es. Es ist bloß die Frage, wie man nun eine zufriedenstellende Lösung erreicht, aber so ist das Problem schon mal ungefähr geklärt denke ich.

Gruß,
Nils


----------



## Bernd Hohmann (15. Dez 2012)

Das Problem liegt wahrscheinlich darin, dass im Manifest des JAR (guck mal rein unter META-INF/MANIFEST.MF) nur das eigene Jar angegeben ist und tools.jar fehlt. Wenn das nicht mit im Classpath ist, schaust Du in die Röhre.

Versuch mal sowas:

java -cp main.jar:tools.jar path.to.StartClass

"tools.jar" muss dann im gleichen Verzeichnis wie Dein eigenes jar liegen.

Siehe auch: jar : Java Glossary

Bernd


----------



## Blob (15. Dez 2012)

Okay, Dein Befehl hat leider keinen Erfolg gebracht. 

Habe mal einen Blick in die Manifest-Datei geworfen:

```
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.3
Created-By: 1.6.0_24-b24 (Sun Microsystems Inc.)
Class-Path: lib/tools.jar
X-COMMENT: Main-Class will be added automatically by build
Main-Class: test2.Test2
```
Die Datei tools.jar ist auch in genau diesem Unterverzeichnis lib.

Ich sehe da "leider" keine Unstimmigkeiten, sieht so aus als wäre alles schön im Klassenpfad.

Gruß,
Nils


----------



## Bernd Hohmann (15. Dez 2012)

Also irgendwas machst Du da was ich nicht weiss.

Hier: Linux Java 1.6 mit SDK und eine Windows XP VM wo nur die JRE 1.6 installiert ist

Ich habe meinen Source von oben unter Linux kompiliert und das Classfile nach Windows geschoben.


```
T:\tmp>java JavaCompilerTest
Exception in thread "main" java.lang.NullPointerException
```

Dann hab ich die "tools.jar" von Linux nach Windows kopiert und das so gestartet:


```
T:\tmp>java -cp tools.jar;. JavaCompilerTest
com.sun.tools.javac.api.JavacTool@1bd747e
```

Ist also angekommen. Damit wäre schonmal geklärt, ob es grundsätzlich funktioniert. Vielleicht kannst Du das mal nachvollziehen.

Bernd


----------



## Lumaraf (15. Dez 2012)

Erklär mal wofür genau du den Compiler eigentlich brauchst? Ich hab so die Vermutung das eine Bytecode-Engineering Bibliothek besser für dein Problem geiegnet ist.


----------



## Blob (15. Dez 2012)

Bernd: Für Deinen Code müsste ich doch einfach den folgenden Aufruf verwenden oder täusche ich mich ? 

```
java -cp JavaCompilerTest.jar:tools.jar JavaCompilerTest
```
Dein Code heißt dass er auch mit Eclipse erzeugt wurde, ich denke dass wir so einfacher den Fehler finden können. Habe einfach als Runnable JAR exportiert und die JavaCompilerKlasse als Hauptklasse gesetzt, die tools.jar habe ich in Eclipse nicht eingebunden. Ausgabe vom Code ist wie gehabt 
	
	
	
	





```
Exception in thread "main" java.lang.NullPointerException at JavaCompilerTest.main(JavaCompilerTest.java:9)
```
.
Windows habe ich gerade keins griffbereit.

Lumaraf: Ich lade Klassen die alle von einer abstrakten erben, in der Implementierung allerdings keinerlei Einschränkungen haben, außer eben dass sie alle Methoden überschreiben müssen, gerade um Fehler in den zu ladenden Klassen zu finden eignet sich daher der Compiler recht gut finde ich.


----------



## Bernd Hohmann (15. Dez 2012)

Blob hat gesagt.:


> Bernd: Für Deinen Code müsste ich doch einfach den folgenden Aufruf verwenden oder täusche ich mich ?
> 
> ```
> java -cp JavaCompilerTest.jar:tools.jar JavaCompilerTest
> ```


Der Schein trügt. In deiner Variante kann noch das Manifest des .JAR Seiteneffekte verursachen..

Was Du aber machen kannst: Einfach mal das Manifest aus dem .jar rauslöschen (geht mit einem Tool wie File Commander total einfach - gibts auch für Linux) und damit versuchen.



Blob hat gesagt.:


> Dein Code heißt dass er auch mit Eclipse erzeugt wurde,


Nein, den Code hab ich im Editor des FileCommander händisch reingehackt.

Solche Fehler wie Deinen findet man ganz gut, wenn man das erstmal auf ein ganz primitives Beispiel reduziert.

Bernd


----------



## tröööt (15. Dez 2012)

ich habs nur grob überflogen .. ABER

1) für Compiler reicht das JRE nicht aus ... dafür MUSS das JDK installiert sein und verwendet werden
2) es reicht nicht einfach TOOLS.JAR zu kopieren ... denn es bestehen noch viele weitere abhängigkeiten zu weiteren daten und archiven innerhalb des JDK die ebenfalls alle mitkopiert werden müssten
3) soweit ich weis verwenden die tools eine bestimmte API die so nur innerhalb des JDK vorhanden ist ... im JRE aber großteils selbst nach-implementiert werden müsste

folglich :
entweder dein kunde nutzt das JDK .. oder dein code ist so nicht praktikabel umsetzbar ..

und deine antwort WARUM du das überhaupt machen willst ist absoluter käse ...
wenn du modular arbeiten willst schau dir entsprechende threads an in denen darüber diskutiert wird ... aber einen compiler zu missbrauchen weil der entwickler der klasse mistgebaut hat scheidet für den "normalen java-alltag" definitiv aus ...
sowas kann man sich gerne als entwickler leisten wenn man entsprechend mal ein proof-of-concept bringen will ... aber es sollte klar sein das eine klasse zur runtime bereits fertig entwickelt und implementiert wurde ...
da selbst was zur runtime zusammenzufrickeln ist unsinn ...

außerdem : damit das funktioniert muss der source der klassen mit ausgeliefert werden ... und das macht man in der regel nicht ...


----------



## Bernd Hohmann (15. Dez 2012)

tröööt hat gesagt.:


> 1) für Compiler reicht das JRE nicht aus ... dafür MUSS das JDK installiert sein und verwendet werden
> 2) es reicht nicht einfach TOOLS.JAR zu kopieren ... denn es bestehen noch viele weitere abhängigkeiten zu weiteren daten und archiven innerhalb des JDK die ebenfalls alle mitkopiert werden müssten
> 3) soweit ich weis verwenden die tools eine bestimmte API die so nur innerhalb des JDK vorhanden ist ... im JRE aber großteils selbst nach-implementiert werden müsste



Aua.


----------



## Blob (15. Dez 2012)

Bernd: Habe die Manifest-Datei gelöscht (nur noch die .class-Datei drinnen), leider ohne Erfolg. Das Projekt habe ich angehängt, nicht dass es wieder eine fehlerhafte Installation oder etwas derartiges ist, solche Späße habe ich schon einige Male mit Linux erlebt. Die tools.jar fehlt, die wäre zu groß gewesen - die ist bei mir im Wurzelverzeichnis.

tröööt: Nein, die Klassen müssen frei programmierbar bleiben und es führt kein Weg daran vorbei. Ich programmiere selbst schon lange genug in Sprachen wie C++ oder Pascal, ich halte von so etwas persönlich daher auch wenig, aber hier ist es die einzige Lösung. Es geht hier um flexible Programmierbarkeit, Modularität ist speziell hier was ganz anderes. Ich denke außerdem dass es sehr wohl gehen könnte, das zeigt Bernd und außerdem gibt es einige Quellen die das gleiche Problem zeigen, deren Lösungen mir leider nicht halfen.


----------



## Bernd Hohmann (15. Dez 2012)

Äh.. Nils - bist Du sicher, dass du das gegen Java 1.7 kompilieren wolltest und auch eine tools.jar von 1.7 dagegenstellst?

Bernd


----------



## Blob (15. Dez 2012)

Die Version da basiert auf einer JDK 1.7 und hat die aktuelle Version der tools.jar welche auch 1.7 ist. In NetBeans vorhin hatte ich JDK 1.6 und 1.6 tools.jar die schon hier auf Linux drauf war. Vermutlich wird es aber wohl wirklich mit den Versionen zusammenhängen. Wobei ich momentan noch nicht sehe warum.

Gruß,
Nils


----------



## Bernd Hohmann (15. Dez 2012)

Ich hab jetzt schnell mal eine Debian-VM zum Testen hochgezogen - das hängt tatsächlich am Java 7.

Mit Java 6 reicht ein reinkopieren der tools.jar in eine JRE und setzen des Classpath um alle Abhängigkeiten zu erfüllen, mit Java 7 geht das nicht mehr.

Einen Hinweis hab ich hier gefunden: Where to put tools.jar in JRE 7?  Ideas Not For Sale
Und hier: Bug ID: 7181951 ToolProvider.getSystemJavaCompiler() is null if tools.jar put to jre/lib/ext dir

Du kannst mal die Initphase von ToolProvider in Java 7 durchtracen was das Orakel geändert hat.

Bernd


----------



## Blob (15. Dez 2012)

Ich bin die Jar mit dem Debugger durchgegangen und bin direkt in den Routinen zum Werfen der Exception gelandet, mehr habe ich leider nicht gesehen.

Habe nochmal genauer nachgelesen, es wirkt so als hätte das noch keiner gelöst. Die Veränderungen die durchgeführt werden sind scheinbar immer beim Endanwender. 
Hatte auch mal auf 1.6 umgestellt, brachte auch keinen Erfolg, liegt das daran dass der Java-Interpreter Version 1.7 hat ? Kann es mir kaum vorstellen.

Gruß,
Nils


----------



## Bernd Hohmann (15. Dez 2012)

Blob hat gesagt.:


> Hatte auch mal auf 1.6 umgestellt, brachte auch keinen Erfolg, liegt das daran dass der Java-Interpreter Version 1.7 hat ? Kann es mir kaum vorstellen.



Doch. Das Problem liegt in der JRE von Java 1.7, da hilft auch kein Zurückschalten auf Kompatibilität mit 1.6.

Das reinkopieren aus Where to put tools.jar in JRE 7?  Ideas Not For Sale probiert?

Ich kann mich gerne um eine Lösung bemühen, aber solche Forschungsaufträge sind nicht für 500 EUR abzuwickeln.

Bernd


----------



## Blob (15. Dez 2012)

Das Kopieren hatte ich versucht, war aber ins falsche Verzeichnis, nun das richtige erwischt und es läuft. Aber eben, müsste das der Endanwender nicht auch tun ? Das wäre das Einzige was ich mir als "painfully" vorstellen könnte, denn das wäre in der Tat ein Problem.

Gruß,
Nils


----------



## Bernd Hohmann (15. Dez 2012)

Nils,

Du musst eh die App irgendwie ausliefern - da kannst Du auch tool.jar ausliefern und mit einem Batch den Kram reinkopieren.

Wenn die Zielplattform Linux ist sollte das mit der #!/bash kein Problem sein - und unter Windows muss man halt mal schauen wie das mit Adminrechten aussieht.

Hobby-Admin tröööt hat bestimmt jetzt schon den Rüssel voller Ideen.

Bernd


----------



## tröööt (15. Dez 2012)

@TO
hmm ... also das WARUM hab ich irgendwie immer noch nicht gerallt ... aber alleine software ausliefern die mit ner halben entwickler-basis kommt halte ich defintiv für FALSCH

wenn es ziel der software sein soll java-source zu compilen würde ich demjenigen der es nutzen will schlicht sagen : JDK NUTZEN !

@Bernd
hmm .. einfach "AUA" zu sgen ist toll .. aber mal zu begründen ... naja ... bin ja nichts anderes von dir gewohnt ...
die tatsache das oracle den bug-report einfach mit "ist so auch nicht gedacht" closed zeigt doch wie DUMM die idee an sich ist ...
wenn man vorhat java-source zu compilen soll man auch das JDK nutzen ... fertig aus ...
alleine die möglichkeit das es überhaupt geht siehr für mich nach nem schweren design-fehler von java selbst aus ...


----------



## Bernd Hohmann (15. Dez 2012)

tröööt hat gesagt.:


> @Bernd
> hmm .. einfach "AUA" zu sgen ist toll .. aber mal zu begründen .....



Damit ich von Dir eine noch dümmere Hobby-Admin Diskussion auf dem Niveau eines sich gnadenlos selbstüberschätzenden 21jährigen Kindes aufgedrückt bekomme?

Geh sterben - aber belästige mich nicht mehr.


----------



## Blob (16. Dez 2012)

Habe den Fall jetzt abgedeckt, dass was mit dem Compiler nicht stimmt und dann natürlich einfach eine Ausgabe gemacht, die beschreibt was zu tun ist um es zum Laufen zu kriegen. Mitgeliefert wird die tools.jar natürlich auch, damit das alles kein Umstand ist. Vielen Dank für die Hilfe!

tröööt: Ganz einfach um die Mächtigkeit des Programmes nicht einzuschränken, um es sinnvoll umzusetzen bräuchte ich sonst eine Skriptsprache - ob Skriptsprache oder so wie jetzt ist kein Unterschied. 
Es ist falsch seine fanatischen Philosophien vonwegen "keine dynamisch geladenen Klassen" strikt zu befolgen, irgendeine Berechtigung hat es am Ende doch alles. Dass irgendwelche Entwickler meinen dass das oben genannte kein Bug sei, zeigt in meinen Augen vielmehr wie unsauber an der Stelle das Paket ist - etwas von Version 6 auf 7 so drastisch zu ändern und Leuten ihre alten Codes kaputt zu machen sorgt für Chaos, das war's, das bringt kaum einen Vorteil, das hält höchstens ein paar Idioten davon ab in irgendeiner Situation Mist zu programmieren - das ist kein Designfehler, wer nicht weiß was er tut, weiß es nicht und soll nicht programmieren beziehungsweise aus seinen Fehlern lernen. Wegen solcher Leute die Freiheit aller einzuschränken finde ich schlimmer.
Und wenn Du von jemanden wie Bernd ernstgenommen werden willst, dann benutze Shift an den richtigen Stellen und argumentiere nicht nur fanatisch. Es kommt halt nicht sehr gut etwas nur zu überfliegen und dann in dem Tonfall zu schreiben.

Gruß,
Nils


----------



## Bernd Hohmann (16. Dez 2012)

Blob hat gesagt.:


> Habe den Fall jetzt abgedeckt, dass was mit dem Compiler nicht stimmt und dann natürlich einfach eine Ausgabe gemacht, die beschreibt was zu tun ist um es zum Laufen zu kriegen. Mitgeliefert wird die tools.jar natürlich auch, damit das alles kein Umstand ist.



Vielleicht sollte man dem Anwender einfach das JDK aufs Auge drücken - vielleicht ist das Szenario in Java 8 noch kaputter.  JRE hat ca 50MB, JDK ca. 100MB - macht heuer nicht mehr so den Unterschied.

Bernd


----------

