Sunday, June 14, 2015

Build a spring project into an executable standalone jar in maven

To create an executable standalone jar, which can be run by command  like java –jar myExecutable.jar, there are 2 conditions need to be fulfilled:

  • Specify the entry main class in META-INFO/MANIFEST.MF file
  • Include all dependences  in the final jar file

The second condition is not needed theoretically, but in practical will always be necessary.

If the project uses Spring with xml configuration, then there’s one more condition to fulfill.

  • Handle spring schemas for different spring packages. (see below for explanation)

The best maven plugin to handle all there three requirements are maven-shade-plugin. (some other plugin such as maven-assembly-plugin can deal with the first two requirements, but not the third one).

In this article, a hello world spring project will be package to an executable standalone jar file using maven. The way used in this article can also perfectly create executable standalone jar package, even if the project don’t use spring.

0. What you need

  • JDK
  • Spring
  • Maven 3.2.1

1. Define the class

There are 2 classes. The first one the a spring bean.

package com.shengwang.demo;

import org.springframework.stereotype.Component;

@Component
public class HelloWorldBean {
public void sayHello(String name) {
System.out.println("Hello "+name);
}
}

The second one is the main class, which get the hello world bean and call its sayHello method.

package com.shengwang.demo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class DemoMain {

public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("application-context.xml");
HelloWorldBean bean = ctx.getBean(HelloWorldBean.class);

bean.sayHello("Spring");

((ClassPathXmlApplicationContext) (ctx)).close();
}
}

2. Spring configuration


The spring xml configuration file application-context.xml is also very simple.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd">

<context:component-scan base-package="com.shengwang.demo" />

</beans>

3. Maven pom


Plugin maven-shade-plugin in the pom file is the key of this demo.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.shengwang.demo</groupId>
<artifactId>spring-executable-standalone</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>spring-executable-standalone</name>
<url>http://maven.apache.org</url>

<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
</dependencies>

<build>

<plugins>
<!-- Use JDK 7 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>

<!-- =============================================================== -->
<!-- use shade plugin to package spring project into executable jar -->
<!-- =============================================================== -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<!-- =================================================== -->
<!-- define the main entry for the output jar file -->
<!-- =================================================== -->
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.shengwang.demo.DemoMain</mainClass>
</transformer>
<!-- =================================================== -->
<!-- append all spring.handlers instead of overwriting -->
<!-- =================================================== -->
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.handlers</resource>
</transformer>
<!-- =================================================== -->
<!-- append all spring.schemas instead of overwriting -->
<!-- =================================================== -->
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.schemas</resource>
</transformer>
</transformers>
<!-- ============================================================ -->
<!-- exclude any digital signiture file from third party jar file -->
<!-- ============================================================ -->
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>

</plugins>
</build>
</project>

In the plugin, 3 transformers and 1 filter are used. Let’s explain them one by one.


The first transformer is ManifestResourceTransformer, which will modify the META-INFO/MANIFEST.MF file in the final  output jar file. This transformer is used to add Main-Class entry in the MANIFEST.MF file. If unzip the output jar, open the META-INFO/MANIFEST.MF file, we can see the added line.


image


The second and third transformers are AppendingTransformer. Why these two transformers are needed? Because every Spring package store xsd mapping file in the same direcotry hierarchy, /META-INF/spring.schemas and /META-INF/spring.handlers, see example below.


image image


For example two spring artifacts, spring-aop and spring-beans, have the exactly same files in the same directory. When using maven to package jar, these files from different dependency packages are combined in to one file by the transformers.


The filters are used to exclude the digital signature files out of the final output jar file. Some of the third party dependency may be digitally signed. These signature files must be screened out to prevent signature verification exceptions.


For most cases, the main-class entry is the only thing need to change.


4. Package


Run maven to package the project into one jar file.

mvn clean package

5. Run the output jar


Run the output jar file

java -jar spring-executable-standalone-0.0.1-SNAPSHOT.jar

The output looks like:


image

3 comments:

Powered by Blogger.

About The Author

My Photo

Has been a senior software developer, project manager for 10+ years. Dedicate himself to Alcatel-Lucent and China Telecom for delivering software solutions.

Pages

Unordered List