Launch4j Maven Plugin

Launch4j by Grzegorz Kowal wraps a jar file in a Windows executable to ease deployment of Java desktop applications. You can either bundle a JRE or tell Launch4j to search the hard drive for an existing one. If none is found, then Launch4j will show the user a download page. Launch4j has many features. You can create either GUI or console applications. You can show a splash screen while the JRE loads, give your application a custom icon in the Windows task bar, set a more descriptive process name (other than “java”), and set a variety of other process attributes.

You can run Launch4j on Windows, Linux, Solaris, or OS X. It has a GPL license, but the code generated as part of your executable is LGPL, so you can use it in commerical applications. The Maven Launch4j Plugin is also GPL. The code for the launch4j-maven-plugin is hosted on Github.

You specify Launch4j’s configuration through an XML file. Please see the Launch4j site for more information on this file. You can also set your configuration via the Launch4j GUI.

The Maven plugin for Launch4j lets you generate the Launch4j executable as part of the Maven build process. It supports Maven 2.0.4 and Launch4j 3.0.0-pre1. You don’t have to download Launch4j; all the necessary files are included in the plugin. (The plugin does not include Launch4j’s GUI option.) Depending on your operating system, the plugin will download an additional artifact containing platform-specific binaries that Launch4j uses to create the executable. This artifact is treated like any other Maven dependency, so it is only downloaded the first time you need it.

The Launch4j Plugin is not hosted on the standard Maven repository, but at http://www.9stmaryrd.com/maven. Therefore you must add its repository to your POM. It should be both a pluginRepository and a regular repository, like so:

<repositories>
    <repository>
        <id>akathist-repository</id>
        <name>Akathist Repository</name>
        <url>http://www.9stmaryrd.com/maven</url>
    </repository>
</repositories>
<pluginRepositories>
    <pluginRepository>
        <id>akathist-repository</id>
        <name>Akathist Repository</name>
        <url>http://www.9stmaryrd.com/maven</url>
    </pluginRepository>
</pluginRepositories>

The plugin lets you specify the Launch4j configuration in your POM. I hope to add support for external configuration files, but right now you have to use the POM. The format of this configuration is very similar to the standard Launch4j XML format. There are two main differences. First, any lists of like-named elements must appear in a wrapper element. For example, you can’t say:

<icon>logo.bin</icon>
<var>this=that</var>
<var>foo=bar</var>
<var>blep=blurp</var>

You must say:

<icon>logo.bin</icon>
<vars>
    <var>this=that</var>
    <var>foo=bar</var>
    <var>blep=blurp</var>
</vars>

Likewise for <lib> and <obj> elements.

Second, the sub-elements of the <classPath> element are a little different. This is so you can set the classpath based on your depedencies. <classPath> still takes a <mainClass> element, but it does not take <cp>. Instead, it supports these children:

<addDependencies>
If you set this to "true," the plugin will build your classpath based on all dependencies in the runtime and compile scopes. This is on by default.
<jarLocation>
If you are using the addDependencies feature, you can use this option to add a prefix before each jar's name. This is useful if you are bundling your app with the executable alongside a lib directory that contains all your jars. If that's what you're doing, you would specify <jarLocation>lib/</jarLocation>.
<preCp>
Use this to add classpath entries before the automatically-generated list. This element functions whether you have enabled <addDependencies> or not. Entries in the list should be separated by semicolons, as in a Windows-style CLASSPATH variable.
<postCp>
Use this to add classpath entries after the automatically-generated list. This element functions whether you have enabled <addDependencies> or not. Entries in the list should be separated by semicolons, as in a Windows-style CLASSPATH variable.

Other than these changes, the XML format is just like Launch4j’s standard format.

By default, the Launch4j plugin is bound to the package phase. Suppose you have a single-module project named encc. It is a console application, not a GUI. It is packaged as a jar, so the jarring runs automatically during the package phase before anything else. You want to use launch4j to create an executable and then use the assmebly plugin to bundle everything up. You could bind both launch4j and assembly to the package phase with a POM like this:

<project>
  . . .
  <groupId>com.akathist.encc</groupId>
  <artifactId>encc</artifactId>
  <packaging>jar</packaging>
  . . .
  <build>
    <plugins>
      <plugin>
        <groupId>com.akathist.maven.plugins.launch4j</groupId>
        <artifactId>launch4j-maven-plugin</artifactId>
        <executions>
          <execution>
            <id>l4j-clui</id>
            <phase>package</phase>
            <goals><goal>launch4j</goal></goals>
            <configuration>
              <headerType>console</headerType>
              <outfile>target/encc.exe</outfile>
              <jar>target/encc-1.0.jar</jar>
              <errTitle>encc</errTitle>
              <classPath>
                <mainClass>com.akathist.encc.Clui</mainClass>
                <addDependencies>false</addDependencies>
                <preCp>anything</preCp>
              </classPath>
              <jre>
                <minVersion>1.5.0</minVersion>
              </jre>
              <versionInfo>
                <fileVersion>1.2.3.4</fileVersion>
                <txtFileVersion>txt file version?</txtFileVersion>
                <fileDescription>a description</fileDescription>
                <copyright>my copyright</copyright>
                <productVersion>4.3.2.1</productVersion>
                <txtProductVersion>txt product version</txtProductVersion>
                <productName>E-N-C-C</productName>
                <internalName>ccne</internalName>
                <originalFilename>original.exe</originalFilename>
              </versionInfo>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-assembly-plugin</artifactId>
        <executions>
          <execution>
            <id>assembly</id>
            <phase>package</phase>
            <goals><goal>attached</goal></goals>
            <configuration>
              <descriptors>
                <descriptor>assembly.xml</descriptor>
              </descriptors>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  . . .
</project>

Note that when you bind the assembly plugin to a phase, you must use assembly:attached, not assembly:assembly, to prevent its forking a parallel lifecycle and running everything twice.

Or suppose your application can run in either GUI or console mode, and you want to create separate executables for each. Then your POM would look like this:

<project>
  . . .
  <groupId>com.akathist.encc</groupId>
  <artifactId>encc</artifactId>
  <packaging>jar</packaging>
  . . .
  <build>
    <plugins>
      <plugin>
        <groupId>com.akathist.maven.plugins.launch4j</groupId>
        <artifactId>launch4j-maven-plugin</artifactId>
        <executions>
          <execution>
            <id>l4j-clui</id>
            <phase>package</phase>
            <goals><goal>launch4j</goal></goals>
            <configuration>
              <headerType>console</headerType>
              <outfile>target/encc.exe</outfile>
              <jar>target/encc-1.0.jar</jar>
              <errTitle>encc</errTitle>
              <classPath>
                <mainClass>com.akathist.encc.Clui</mainClass>
                <addDependencies>false</addDependencies>
                <preCp>anything</preCp>
              </classPath>
              <jre>
                <minVersion>1.5.0</minVersion>
              </jre>
              <versionInfo>
                <fileVersion>1.2.3.4</fileVersion>
                <txtFileVersion>txt file version?</txtFileVersion>
                <fileDescription>a description</fileDescription>
                <copyright>my copyright</copyright>
                <productVersion>4.3.2.1</productVersion>
                <txtProductVersion>txt product version</txtProductVersion>
                <productName>E-N-C-C</productName>
                <internalName>ccne</internalName>
                <originalFilename>original.exe</originalFilename>
              </versionInfo>
            </configuration>
          </execution>
              <execution>
                <id>l4j-gui</id>
                <phase>package</phase>
                <goals><goal>launch4j</goal></goals>
                <configuration>
                  <headerType>gui</headerType>
                  <outfile>target/enccg.exe</outfile>
                  <jar>target/encc-1.0.jar</jar>
                  <errTitle>enccg</errTitle>
                  <classPath>
                    <mainClass>com.akathist.encc.Gui</mainClass>
                  </classPath>
                  <jre>
                    <minVersion>1.5.0</minVersion>
                  </jre>
                  <versionInfo>
                    <fileVersion>1.2.3.4</fileVersion>
                    <txtFileVersion>txt file version?</txtFileVersion>
                    <fileDescription>a description</fileDescription>
                    <copyright>my copyright</copyright>
                    <productVersion>4.3.2.1</productVersion>
                    <txtProductVersion>txt product version</txtProductVersion>
                    <productName>E-N-C-C</productName>
                    <internalName>ccne</internalName>
                    <originalFilename>original.exe</originalFilename>
                  </versionInfo>
                </configuration>
              </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-assembly-plugin</artifactId>
        <executions>
          <execution>
            <id>assembly</id>
            <phase>package</phase>
            <goals><goal>attached</goal></goals>
            <configuration>
              <descriptors>
                <descriptor>assembly.xml</descriptor>
              </descriptors>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  . . .
</project>

If you have any questions, just send me a note.