Tworząc aplikację desktopową za pomocą Javy chciałbyś mieć możliwość utworzenie pliku exe dla systemu Windows. Tak aby twoja aplikacja mogła być uruchamiana wprost bez żadnych magicznych plików batchowych. Fajnie aby wykrywała obecność JRE i kierowała do odpowiedniej witryny celem pobrania środowiska uruchomieniowego.
Takie możliwości stwarza projekt Launch4j, który opakowuje archiwum jar w plik wykonywalny exe. Dodatkowo za pomocą wtyczki dla Mavena można całą konfigurację umieścić w jednym pliku projektu - pom.xml.
W poniższym wpisie omówię krótko jak przygotować prosty projekt javowy z dodaną opcją opakowywania. Niestety oryginalny kod wtyczki zawierał pewne błędy, jednak dzięki licencji GPL mogłem zforkować (wydzielić, oddzielić, … ?) cały kod do swojego repozytorium na GitHubie. Druga sprawa, to artefakty Mavena są dostępne na Nexusie firmy SoftwareMill, aby nie trzeba ba było samemu ich budować lokalnie i instalować.
Do dzieła!
Potrzebne: linia komend, Maven 3.x, IDE wspierające Mavena
Projekt Mavena - to jest najprostsza rzecz, wystarcz użyć wtyczki Archetype do wygenerowania najprostszego projektu
mvn archetype:generate
zostanie wyświetlona bardzo długa lista dostępnych archetypów, jednak na moje potrzeby wystarczy domyślnie wybrana opcja 106 - maven-archetype-quickstart (An archetype which contains a sample Maven project.)
Choose a number: 106: Choose version: 1: 1.0-alpha-1 2: 1.0-alpha-2 3: 1.0-alpha-3 4: 1.0-alpha-4 5: 1.0 6: 1.1 Choose a number: 6: Define value for property 'groupId': : pl.org.lenart.launch4j Define value for property 'artifactId': : launch4j-demo Define value for property 'version': 1.0-SNAPSHOT: : Define value for property 'package': pl.org.lenart.launch4j: : Confirm properties configuration: groupId: pl.org.lenart.launch4j artifactId: launch4j-demo version: 1.0-SNAPSHOT package: pl.org.lenart.launch4j Y: :
I mam gotowy prosty projekt, który posłuży mi jako przykład. Otwieram pom.xml w IDE - w moim przypadku jest IntelliJ IDEA, która nie potrzebuje żadnych dodatkowych czynności, aby otworzyć projekt mavenowy - wybieram plik pom.xml i to wszystko. Np. w przypadku Eclipse IDE należy cześniej z linii komend wykonać
mvn eclipse:eclipse
aby wygenerować projekt Eclipse'a, który następnie można otworzyć za pomocą Eclipse IDE.
W raz z projektem tworzona jest przykładowa klasa App. Poniżej jest zmodyfikowany kod klasy, który wyświetla proste okno dialogowe, celem stwierdzenia, że wersja opakowana to jest ta sama aplikacja.
package pl.org.lenart.launch4j; import javax.swing.*; import java.awt.*; public class App extends JFrame { public static void main(String[] args) { App app = new App(); app.sayHello(); } public App() throws HeadlessException { setVisible(false); } private void sayHello() { JOptionPane.showMessageDialog(this, "Hello world!", "Hello", JOptionPane.INFORMATION_MESSAGE); dispose(); } }
Uruchomienie powyższej aplikacji skutkje ładnym komunikatem
Następny krok to dodanie wtyczki Launch4j i repozytorium.
Wtyczkę i jej konfigurację umieszczę w profilu, bo do codziennej pracy nie jest mi potrzebne opakowywanie archiwum jar do pliku exe. A więc profil idealnie się do tego nadaje. Taka mała uwaga co do profili w Maven 3 - jeśli w trakcie wywołania mavena podamy profil, profil domyślny nie zostanie włączony do cyklu budowania. Jest to znacząca zmiana od wersji 2.
Umieszczenie repozytorium w profilu przyspieszy proces budowania aplikacji, gdyż jedynie przy aktywnym profilu, Maven będzie przeszukiwał podane repozytorium wtyczek.
<profiles> <profile> <id>winexe</id> <build> <plugins> <plugin> <groupId>com.akathist.maven.plugins.launch4j</groupId> <artifactId>launch4j-maven-plugin</artifactId> <version>1.4</version> </plugin> </plugins> </build> <pluginRepositories> <pluginRepository> <id>softwaremill-public</id> <name>SoftwareMill Public Reposityr</name> <url>http://tools.softwaremill.pl/nexus/content/groups/public/</url> </pluginRepository> </pluginRepositories> </profile> </profiles>
Kolejny krok to podanie w jakiej fazie wtyczka ma zostać uruchomiona, w tym przypadku najlepsza jest faza pakowania całego projektu do pliku jar, a więc wystarczy dodać poniższy fragment, aby otrzymać porządane zachowanie
<executions> <execution> <id>l4j-clui</id> <phase>package</phase> <goals> <goal>launch4j</goal> </goals> </execution> </executions>
Ostatni krok to dodanie konfiguracji samej wtyczki
<configuration> <headerType>gui</headerType> <jar>${project.build.directory}/${project.artifactId}-${project.version}.jar</jar> <outfile>${project.build.directory}/app.exe</outfile> <downloadUrl>http://java.com/download</downloadUrl> <classPath> <mainClass>pl.org.lenart.launch4j.App</mainClass> <preCp>anything</preCp> </classPath> <jre> <minVersion>1.6.0</minVersion> <jdkPreference>preferJre</jdkPreference> </jre> <versionInfo> <fileVersion>1.0.0.0</fileVersion> <txtFileVersion>${project.version}</txtFileVersion> <fileDescription>Launch4j Demo App</fileDescription> <copyright>Lukasz Lenart</copyright> <productVersion>1.0.0.0</productVersion> <txtProductVersion>1.0.0.0</txtProductVersion> <productName>App</productName> <companyName>Lukasz Lenart</companyName> <internalName>app</internalName> <originalFilename>app.exe</originalFilename> </versionInfo> </configuration>
To jest tylko część parametrów jakie można podać, opis pozostałych znajduje się na stronie projektu Launch4j. Najważniejsze jest podanie głównej klasy, która ma zostać uruchomiona przez launch4j
<mainClass>pl.org.lenart.launch4j.App</mainClass>
I w sumie gotowe, pozostaje przetestować wszystko, a więc wyołuję komendę
mvn clean install
i czekam na wynik ….
[INFO] [INFO] --- maven-jar-plugin:2.3.1:jar (default-jar) @ launch4j-demo --- [INFO] Building jar: /Users/lukaszlenart/Projects/launch4j-demo/target/launch4j-demo-1.0-SNAPSHOT.jar [INFO] [INFO] --- maven-install-plugin:2.3.1:install (default-install) @ launch4j-demo --- [INFO] Installing /Users/lukaszlenart/Projects/launch4j-demo/target/launch4j-demo-1.0-SNAPSHOT.jar to /Users/lukaszlenart/.m2/repository/pl/org/lenart/launch4j/launch4j-demo/1.0-SNAPSHOT/launch4j-demo-1.0-SNAPSHOT.jar [INFO] Installing /Users/lukaszlenart/Projects/launch4j-demo/pom.xml to /Users/lukaszlenart/.m2/repository/pl/org/lenart/launch4j/launch4j-demo/1.0-SNAPSHOT/launch4j-demo-1.0-SNAPSHOT.pom [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 4.900s [INFO] Finished at: Mon May 02 09:26:12 CEST 2011 [INFO] Final Memory: 9M/81M [INFO] ------------------------------------------------------------------------
Jak widać, sam projekt buduje się bez problemów, jednak było to uruchomienie bez opakowywania do pliku exe - pasuje sprawdzić i ten element. Dodaję nazwę profilu do wywołania i czekam na wynik.
mvn clean install -Pwinexe
Tym razem można zauważyć, że została aktywowana wtyczka launch4j
[INFO] [INFO] --- maven-jar-plugin:2.3.1:jar (default-jar) @ launch4j-demo --- [INFO] Building jar: /Users/lukaszlenart/Projects/launch4j-demo/target/launch4j-demo-1.0-SNAPSHOT.jar [INFO] [INFO] --- launch4j-maven-plugin:1.4:launch4j (l4j-clui) @ launch4j-demo --- [INFO] Platform-specific work directory already exists: /Users/lukaszlenart/.m2/repository/com/akathist/maven/plugins/launch4j/launch4j-maven-plugin/1.4 [INFO] launch4j: Compiling resources [INFO] launch4j: Linking [INFO] launch4j: Wrapping [INFO] launch4j: Successfully created /Users/lukaszlenart/Projects/launch4j-demo/target/app.exe [INFO] [INFO] --- maven-install-plugin:2.3.1:install (default-install) @ launch4j-demo --- [INFO] Installing /Users/lukaszlenart/Projects/launch4j-demo/target/launch4j-demo-1.0-SNAPSHOT.jar to /Users/lukaszlenart/.m2/repository/pl/org/lenart/launch4j/launch4j-demo/1.0-SNAPSHOT/launch4j-demo-1.0-SNAPSHOT.jar [INFO] Installing /Users/lukaszlenart/Projects/launch4j-demo/pom.xml to /Users/lukaszlenart/.m2/repository/pl/org/lenart/launch4j/launch4j-demo/1.0-SNAPSHOT/launch4j-demo-1.0-SNAPSHOT.pom [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 16.539s [INFO] Finished at: Mon May 02 09:31:43 CEST 2011 [INFO] Final Memory: 10M/81M [INFO] ------------------------------------------------------------------------
Najlepsze w tym rozwiązamu jest to, że cały proces budowania i opakowywania przeprowadziłem na swoim MacBook Pro - pozostaje przetestować gotowy pliki exe na systemie Windows. A do tego wykorzystam VirtualBox.
Jak widać wszystko ładnie działa, pasuje dodać jeszcze ikonkę i inne ozdobniki. Nawet właściwości zostały dodane, tak jak były podane w konfiguracji wtyczki.
Cały projekt jest dostępny w repozytorium na GitHubie.