Aug18
JavaFX mit Java Runtime als exe ausliefern

Eine Java Desktop Anwendung bereit zu stellen kann eine Herausforderung sein. Einfach nur das jar File bereit zu stellen ist einfach. Die Probleme liegen dann allerdings beim Anwender. Wie wird die Anwendung gestartet, ist die korrekte Java Runtime auf dem Rechner installiert, etc. Besser wäre es, eine ausführbare Datei incl. der kompletten Java Runtime bereit zu stellen. Wie das geht, möchte ich in diesem Artikel beleuchten.

Problemstellung

Wie eingangs bereits erwähnt, ist es nicht komfortabel, dem Anwender einfach nur ein jar File bereit zu stellen. Im schlimmsten Fall muss die Anwendung über die Kommandozeile mit Parametern aufgerufen werden, im einfachsten Fall reicht ein Doppelclick auf das jar File. Bleibt trotzdem die Frage, ob auf dem Zielrechner eine zur Anwendung passende Java Runtime installiert ist.  Nutzt die Anwendung Java 8 Sprachfeatures und auf dem Rechner ist nur Java 7 installiert, war es das schon.

Besser ist, der Anwender bekommt ein komplettes Paket, incl. Java Runtime, mit einer ausführbaren Datei, bei Windows eine exe. In diesem Fall muss er nur die exe starten und sich nicht um irgendwelche Voraussetzungen kümmern.

Problemlösung

JavaFX bietet eine Deployment Option, die sich Native Packaging oder Self-Contained Application Package nennt. Dabei handelt es sich um ein Paket, das den Applikations-Code und die notwendige Java Runtime enthält. Dieses komplette Paket wird an den Anwender ausgeliefert. Konflikte mit Java Runtimes, die bereits auf dem Zielrechner installiert sind, gibt es keine.

Die Erstellung von so einem Paket geht mit e(fx)clipse recht einfach und wird im Artikel von code.makery erklärt. Ich möchte auf den eigentlichen Erstellungsprozess nicht näher eingehen und an dieser Stelle auf den Artikel verweisen.

formatIch selbst habe keine Installer, wie im Artikel beschrieben, erstellt, sondern nur ein Paket mit Runtime und ausführbarer exe. Dieses Paket wird als zip-File bereit gestellt und muss vom Anwender nur in ein beliebiges Verzeichnis entpackt werden. Vorteil ist, man benötigt keine Zusatzsoftware für die Erstellung der Installer.
Um so ein Paket zu erstellen muss in der build.fxbuild Datei auf der Overview Seite das Packaging Format „all“ gewählt werden. Das ist etwas verwirrend, da es auch die Option exe gibt.

Im folgenden möchte ich von meinen Erfahrungen und Fallstricken berichten.

Fallstricke

build fehlerhaft

Mein größtes Problem war, dass der build nicht durch lief und folgenden Fehler gebracht hat:

[taskdef] Could not load definitions from resource com/sun/javafx/tools/ant/antlib.xml. It could not be found.

Ursache war, dass ant ein JDK benötigt, Eclipse aber nur mit einer JRE lief. Damit wird auch ant mit der JRE ausgeführt, was nicht reicht. Man muss Eclipse mit einem JDK ausführen oder dem ant build Prozess ein JDK zuweisen.
select-jdkAm einfachsten startet man den build erstmalig aus der build.fxbuild Datei. Dazu in der Overview Seite rechts oben den Punkt „Generate ant build.xml and run“ anklicken. Läuft Eclipse bzw. das Projekt mit einer JRE kommt ein Hinweis mit der Frage, ob man ein JDK auswählen möchte. Jetzt Ja auswählen und im Auswahlfenster ein JDK auswählen. Das JDK muss vorher in Eclipse unter dem Menü Window > Preferences > Java > Installed JREs > Execution Environments eingerichtet sein.
external toolsDie Frage nach dem JDK kommt allerdings nur einmal. Sollte man hier eine falsche Wahl getroffen haben, oder OK geklickt haben, ohne ein JDK zu selektieren, wird später ant immer mit dieser Auswahl oder mit der JRE ausgeführt falls man kein JDK gewählt hat. Man kann aber auch nachträglich ein JDK zuweisen. Dazu mit der rechten Maustaste auf die Datei build.xml klicken und unter „Run As > External Tools Configurations…“ im Tab JRE ein JDK auswählen.

Änderungen in build.xml gehen verloren

Die build.xml wird aus den Angaben in der build.fxbuild generiert. Leider können einige Informationen nicht in der build.fxbuild eingetragen werden (z.B. Resourcen wie Icons oder Property Dateien). Diese müssen nachträglich direkt in der build.xml eingetragen werden. Aber Achtung, generiert man jetzt die build.xml aus der build.fxbuild neu, werden diese Ergänzungen überschrieben.
Die Generierung der build.xml aus der build.fxbuild eignet sich also nur initial. Anschließend sollten Änderungen immer direkt in der build.xml vorgenommen werden und der Build über das Context-Menü der build.xml mit „Run As > Ant Build“ gestartet werden.

Bibliotheken fehlen in Distribution

Bei der Generierung der build.xml aus der build.fxbuild werden alle jar Files, die im build-Path sind als library in der build.xml eingetragen. Kommen nachträglich weitere jar Files hinzu, müssen diese in der build.xml hinzugefügt werden.
Nach Vorlage der bisherigen jar Files die eigenen Ergänzen. Achtung, sie müssen an zwei Stellen eingetragen werden. Einmal nach dem Eintrag  <target name=“setup-staging-area“>

<copy todir="externalLibs">
	<fileset dir="D:\Git\E-D-Footprints-Watcher\E-D-Footprints-Watcher\lib">
		<filename name="json-simple-1.1.1.jar"/>
	</fileset>
</copy>

und dann bei

<!-- Copy project-libs references -->
<copy todir="build/libs">
	<fileset dir="externalLibs">
		<include name="json-simple-1.1.1.jar"/>
		<include name="httpclient-4.4.1.jar"/>
		<include name="httpcore-4.4.1.jar"/>
		<include name="commons-logging-1.2.jar"/>
	</fileset>
</copy>

Alte Files noch im Paket

Ich hatte das Problem, dass alte, nicht mehr benötigte Files immer noch im Paket enthalten waren. Ursache war, dass der Zielordner des builds beim build nicht gelöscht wurde und somit alte Files erhalten blieben. Um den Zielordner zu löschen im build.xml den Eintrag  <target name=“setup-staging-area“> suchen und mit einem delete den deploy und dist Ordner löschen.

<target name="setup-staging-area">
	<delete dir="externalLibs" />
	<delete dir="project" />
	<delete dir="projectRefs" />
	<delete dir="dist" />
	<delete dir="deploy" />

Ergänzungen in der build.xml

Config-Dateien

Nahezu jede Anwendung benötigt Config-Dateien, z.B. als Property Dateien. Diese müssen direkt in der build.xml angegeben werden. Ich habe dazu im Projekt Root zwei neue Ordner angelegt:

  • config: enthält die Konfiguration für den lokalen Entwicklertest
  • distConfig: enthält die Konfiguration, die mit der Distribution ausgeliefert wird

In der Distribution soll die Konfiguration im Ordner config liegen. Wir müssen also im build einen Ordner config anlegen und die Dateien aus dem Ordner {projektRoot}/distConfig dort rein kopieren. Dazu werden folgende Ergänzungen in der build.xml gemacht:
In der build.xml den Eintrag <target name=“do-deploy“ depends=“setup-staging-area, do-compile, init-fx-tasks“> suchen und wie folgt ergänzen:

<target name="do-deploy" depends="setup-staging-area, do-compile, init-fx-tasks">
	<mkdir dir="dist/config" />
	<copy todir="dist/config" >
	    <fileset dir="../distConfig" />
	</copy>

Jetzt noch den Eintrag <fx:resources id=“appRes“> suchen und um den config Ordner Ergänzen.

<fx:resources id="appRes">
	<fx:fileset dir="dist" includes="E-D-Footprints-Watcher-FX-Client.jar"/>
	<fx:fileset dir="dist" includes="libs/*"/>
	<fx:fileset dir="dist" includes="config/**"/>
</fx:resources>

Versions-File in Distribution

Ziel ist es, in der Distribution ein Text-File zu haben, in dem die Versionsnummer steht. Eine Versionsnummer wurde in der build.fxbuild angegeben und durch die Generierung fest in die build.xml hinterlegt. Diese muss dort bei jeder neuen Version manuell angepasst werden, sie wird aber nur in das MANIFEST.MF File im jar File geschrieben.

Um die Versionsnummer nicht an zwei Stellen in der build.xml pflegen zu müssen, legen wir eine Property in der build.xml an und referenzieren auf diese bei der Erstellung des Versionsfiles und der Erstellung des Manifest.

Die build.xml wie folgt ergänzen.

Direkt am Anfang nach dem root-Tag die Property anlegen

<?xml version="1.0" encoding="UTF-8"?>
<project name="E-D-Footprints-Watcher-FX-Client" default="do-deploy" basedir="." xmlns:fx="javafx:com.sun.javafx.tools.ant">
	<property name="version" value="1.3.2"/>

Den Eintrag  <target name=“do-deploy“ depends=“setup-staging-area, do-compile, init-fx-tasks“> suchen und wie folgt ergänzen.

<target name="do-deploy" depends="setup-staging-area, do-compile, init-fx-tasks">
    <propertyfile file="dist/version.txt" comment="Application version file">
        <entry key="Implementation-Version" value="${version}"/>
    </propertyfile>

Den Eintrag  <fx:resources id=“appRes“> ergänzen um das version.txt File

<fx:resources id="appRes">
	<fx:fileset dir="dist" includes="E-D-Footprints-Watcher-FX-Client.jar"/>
	<fx:fileset dir="dist" includes="version.txt"/>
	<fx:fileset dir="dist" includes="libs/*"/>
	<fx:fileset dir="dist" includes="config/**"/>
</fx:resources>

Und zu guter letzt im Manifest Eintrag die Versionsnummer durch die Property ersetzen.

<manifest>
	<attribute name="Implementation-Vendor" value="Marco Michel"/>
	<attribute name="Implementation-Title" value="E-D-Footprints-Watcher"/>
	<attribute name="Implementation-Version" value="${version}"/>
	<attribute name="JavaFX-Feature-Proxy" value="None"/>
</manifest>

Schreibe einen Kommentar

Anmelden um einen Kommentar abzugeben.

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

*