Skip to content

femtoframework/maven-exec-jar-plugin

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

52 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

exec-jar-plugin for Maven

This is the exec-jar-plugin that we use at CK for building a single, signed executable jar file for Scala projects. Here is how it works:

  1. A JarClassLoader is compiled that enables class files to be loaded from jar files embedded inside of a jar file
  2. A main wrapper is generated that calls either the main-class indicated by the jar file is generated by the maven-jar-plugin or a main-class specified in the configuration of the exec-jar-plugin's execution
  3. A new jar file is generated that contains the same .class files as the jar generated by maven-jar-plugin plus all the dependent jars (including the transitive closures ie the jars that the dependent jars depend upon) and any resources and additional dependencies indicated in the configuration of the exec-jar-plugin
  4. A launcher script is generated by a template that launches the executable jar. Be careful renaming the launcher script as it uses its own name to determine which jar file to exec

Usage:

A simple example with one main-class (ie entry point)

<build>
  <plugins>
  ...
    <plugin>
      <groupId>com.creditkarma</groupId>
      <artifactId>exec-jar-plugin</artifactId>
      <version>1.0.6</version>
      <executions>
        <execution>
          <id>exec-jar</id>
          <configuration>
            <attachToBuild>true</attachToBuild>
          </configuration>
          <goals>
            <goal>exec-jar</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
    ...
  </plugins>
</build>

If this fragment of XML is put into the build section of a pom.xml, a jar is generated by the exec-jar-plugin. If the jar generated by the maven-jar-plugin is called foo-1.0.jar, then the generated executable jar will be named foo-1.0.executable.jar. In addition, a foo-1.0.executable.sh file will be generated that can launch the executable jar. In this example that launcher script will contain:

#!/bin/bash
SCRIPT_NAME=${0}
JAR_DIR_PREFIX=$(dirname "${SCRIPT_NAME}")
java -jar $JAR_DIR_PREFIX/JAR_NAME $@

Including .so or .dll files in the executable jar

Sometimes you need to use native libraries or java agents in your application. To support this, the exec-jar plugin provides to pieces of functionality. First, you can include .so and .dll files directly into the executable jar file. Do this by using the biglibs tag and including a set of files to be included into the executable jar file. Then in your launcher script, extract out the necessary .so or .dll files and they will be automagically added to your LD_LIBRARY_PATH for use by your application.

Second, the generated launcher script can be customized. To do so, just create a file called script.tpl and place it into the same directory as the pom.xml file. This allows you to customize the generated launcher script. The default launcher script is:

#!/bin/bash
SCRIPT_NAME=${0}
JAR_DIR_PREFIX=$(dirname "${SCRIPT_NAME}")
java EXTRA_ARGS -jar $JAR_DIR_PREFIX/JAR_NAME $@

In this script template you can place extra JVM arguments, javaagent calls and any other command line argument to Java that can't be passed into your application any other way. The EXTRA_ARGS part is replaced by the value you place into the extraLauncherArgs which defaults to nothing (ie the empty string).

Here is a real world example which launches with the correct extra information to start Kamon.

<build>
  <plugins>
  ...
    <plugin>
      <groupId>com.creditkarma</groupId>
      <artifactId>exec-jar-plugin</artifactId>
      <version>1.0.6</version>
      <executions>
        <execution>
          <id>exec-jar</id>
          <configuration>
            <attachToBuild>true</attachToBuild>
            <binlibs>
              <fileSet>
                <directory>${nativeDir}</directory>
                <includes>
                  <include>libsigar-amd64-linux.so</include>
                  <include>libsigar-ia64-linux.so</include>
                  <include>libsigar-x86-linux.so</include>
                  <include>libsigar-universal-macosx.dylib</include>
                  <include>libsigar-universal64-macosx.dylib</include>
                </includes>
              </fileSet>
            </binlibs>
            <extraLauncherArgs>-Dkamon.auto-start=true</extraLauncherArgs>
          </configuration>
          <goals>
            <goal>exec-jar</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
    ...
  </plugins>
</build>

and the customized script template (ie script.tpl):

#!/bin/bash
SCRIPT_NAME=${0}
JAR_DIR_PREFIX=$(dirname "${SCRIPT_NAME}")
jar xf $JAR_DIR_PREFIX/JAR_NAME lib/aspectjweaver-1.8.9.jar

java -javaagent:lib/aspectjweaver-1.8.9.jar EXTRA_ARGS -jar $JAR_DIR_PREFIX/JAR_NAME $@

An example with a custom filename

To set a custom filename, two values need to be added to the configuration:

  • classifier defaults to executable and if set it replaces executable in the name of the generated executable jar file
  • filename is the name of the executable jar file to be generated

These two values are used at different places in the jar generation process and they do not have to match but that will result in weird changes in the executable jar file name when it is published to Artifactory.

<build>
  <plugins>
  ...
    <plugin>
      <groupId>com.creditkarma</groupId>
      <artifactId>exec-jar-plugin</artifactId>
      <version>1.0.6</version>
      <executions>
        <execution>
          <id>exec-jar</id>
          <configuration>
            <attachToBuild>true</attachToBuild>
            <classifier>consumer</classifier>
            <filename>${project.build.finalName}.consumer</filename>
            <mainclass>com.creditkarma.boot.ConsumerMain</mainclass>
            <scriptTemplate>src/main/resources/myApp.sh.tpl</scriptTemplate>
          </configuration>
          <goals>
            <goal>exec-jar</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
    ...
  </plugins>
</build>

Additionally, there are several other tags included in this example:

  • mainclass is the fully qualified name of the main class that this executable jar should use
  • scriptTemplate is a template that can be overridden to allow for custom launcher scripts to be generated.

Generating multiple executable jars for multiple entry points

An example showing two similar entry points

<build>
  <plugins>
  ...
    <plugin>
      <groupId>com.creditkarma</groupId>
      <artifactId>exec-jar-plugin</artifactId>
      <version>1.0.6</version>
      <executions>
        <execution>
          <id>exec-jar</id>
          <configuration>
            <attachToBuild>true</attachToBuild>
            <classifier>consumer</classifier>
            <filename>${project.build.finalName}.consumer</filename>
            <mainclass>com.creditkarma.boot.ConsumerMain</mainclass>
            <scriptTemplate>src/main/resources/myApp.sh.tpl</scriptTemplate>
          </configuration>
          <goals>
            <goal>exec-jar</goal>
          </goals>
        </execution>
        <execution>
          <id>exec2-jar</id>
          <configuration>
            <attachToBuild>true</attachToBuild>
            <classifier>consumer2</classifier>
            <filename>${project.build.finalName}.consumer2</filename>
            <mainclass>com.creditkarma.boot.ConsumerMain2</mainclass>
            <scriptTemplate>src/main/resources/myApp.sh.tpl</scriptTemplate>
          </configuration>
          <goals>
            <goal>exec-jar</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
    ...
  </plugins>
</build>

Putting it all together

A complete example:

<build>
  <plugins>
  ...
      <plugin>
        <groupId>com.creditkarma</groupId>
        <artifactId>exec-jar-plugin</artifactId>
        <version>1.0.6</version>
        <executions>
          <execution>
            <id>consumer-exec-jar</id>
            <configuration>
              <attachToBuild>true</attachToBuild>
              <classifier>consumer</classifier>
              <filename>${project.build.finalName}.consumer</filename>
              <mainclass>com.creditkarma.boot.ConsumerMain</mainclass>
              <scriptTemplate>src/main/resources/myApp.sh.tpl</scriptTemplate>
              <binlibs>
                <fileSet>
                  <directory>${nativeDir}</directory>
                  <includes>
                    <include>libsigar-amd64-linux.so</include>
                    <include>libsigar-ia64-linux.so</include>
                    <include>libsigar-x86-linux.so</include>
                    <include>libsigar-universal-macosx.dylib</include>
                    <include>libsigar-universal64-macosx.dylib</include>
                  </includes>
                </fileSet>
              </binlibs>
            </configuration>
            <goals>
              <goal>exec-jar</goal>
            </goals>
          </execution>
          <execution>
            <id>printer-exec-jar</id>
            <configuration>
              <attachToBuild>true</attachToBuild>
              <classifier>logFilePrinter</classifier>
              <filename>${project.build.finalName}.logFilePrinter</filename>
              <mainclass>com.creditkarma.boot.LogFilePrinterMain</mainclass>
              <scriptTemplate>src/main/resources/myApp.sh.tpl</scriptTemplate>
              <binlibs>
                <fileSet>
                  <directory>${nativeDir}</directory>
                  <includes>
                    <include>libsigar-amd64-linux.so</include>
                    <include>libsigar-ia64-linux.so</include>
                    <include>libsigar-x86-linux.so</include>
                    <include>libsigar-universal-macosx.dylib</include>
                    <include>libsigar-universal64-macosx.dylib</include>
                  </includes>
                </fileSet>
              </binlibs>
            </configuration>
            <goals>
              <goal>exec-jar</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
  ...
  </plugins>
</build>

About

A executable jar plugin for maven

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Java 99.8%
  • Shell 0.2%