Knowledge Base

Notice Information in this article applies to Excelsior JET version 11.0 and above.

APPLICABILITY

The instructions laid out in this article were validated against Spring Boot 1.3.6 and Docker Engine 1.11.2. They may not fully apply to other versions.

Notice: Excelsior JET 11.3 does not support Apache Tomcat 8.5 yet.

SUMMARY

Spring Boot is a framework that facilitates rapid development of Spring-based applications. It is often used to create microservices. The Spring Boot build process normally yields an "uberjar" that can be launched as a standalone application, but it is also possible to produce a war file that can be deployed to a servlet container.

This article provides instructions for compiling a Spring Boot application down to a native executable with Excelsior JET.

In the Supplemental Downloads section, there is a git repo containing a simple Spring Boot application, with a separate commit for each step of the Details section.

PREREQUISITES

This article assumes that you have already have a Spring Boot project that can produce a jar or war file using Maven or Gradle.

DETAILS

Creating a Deployable War File

The current version of Excelsior JET does not support AOT compilation of Spring Boot applications packaged as runnable jar files. Therefore, if your project is currently set up to produce such a jar file, you need to modify it slightly as described in the rest of this subsection. Otherwise, move on to the next section.

Application Modification

To enable deployment of a Spring Boot application as a war file to a servlet container such as Apache Tomcat, you need to create a subclass of the SpringBootServletInitializer class that overrides the configure method. Typically, you would repurpose the main class (entry point) of your application:

@SpringBootApplication
public class Application extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(Application.class);
    }

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }

}

Modifying a Maven Build

To update your build configuration so that your project produces a war file, you need to change the packaging to war and mark the embedded servlet container dependency as provided (so as to exclude embedded Tomcat from the build):

    <packaging>war</packaging>
    <!-- ... -->
    <dependencies>
        <!-- ... -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <!-- ... -->
    </dependencies>

Modifying a Gradle Build

To make your Gradle build script produce a war file, you need to replace the jar plugin with the war plugin and change the configuration of the spring-boot-starter-tomcat dependency to providedRuntime:

apply plugin: 'war'
// ...
configurations {
    providedRuntime
}
dependencies {
    // ...
    providedRuntime("org.springframework.boot:spring-boot-starter-tomcat:1.3.6.RELEASE")
    // ...
}

Enabling Native Compilation with Excelsior JET

Modifying a Maven Build

Excelsior JET has a Maven plugin, so to natively compile your application it is suffieint to add a few lines to pom.xml:

<plugins>
    <!-- ... --->
    <plugin>
        <groupId>com.excelsiorjet</groupId>
        <artifactId>excelsior-jet-maven-plugin</artifactId>
        <version>0.7.0</version>
        <configuration>
            <tomcatConfiguration>
                 <tomcatHome>../apache-tomcat-7.0.62</tomcatHome>
            </tomcatConfiguration>
        </configuration>
    </plugin>
    <!-- ... --->
</plugins>

modify <tomcatHome> so that it points to a freshly unpacked copy of Tomcat, and issue the command mvn jet:build to initiate the native build. Upon sucess, you will find the compiled application in the directory <project>/target/jet/app, You can run it using the standard Tomcat launch scripts.

Modifying a Gradle Build

Excelsior JET has a Gradle plugin, so to natively compile your application it is sufficient to add a few lines to build.gradle:

buildscript {
    // ...
    ext.jetPluginVersion = '0.7.0'
    dependencies {
        // ...
        classpath "com.excelsiorjet:excelsior-jet-gradle-plugin:$jetPluginVersion"
    }
}

// ...
apply plugin: 'excelsiorJet'
excelsiorJet {
    tomcat {
        tomcatHome "../apache-tomcat-7.0.62"
    }
}

modify tomcatHome so that it points to a freshly unpacked copy of Tomcat, and issue the command gradle jetBuild. Upon success, you will find the compiled application in the directory <project>/build/jet/app, You can run it using the standard Tomcat launch scripts.

Containerizing With Docker

Create files Dockerfile and .dockerignore in the root directory of your project:

  • If using Maven:

    Dockerfile:

    FROM frolvlad/alpine-glibc:alpine-3.4
    COPY target/jet/app /app
    ENTRYPOINT ["/app/bin/catalina.sh", "run"]
    EXPOSE 8080

    .dockerignore:

    src/*
    target/*
    !target/jet/app
    pom.xml
    .git
    .gitignore
  • If using Gradle:

    Dockerfile:

    FROM frolvlad/alpine-glibc:alpine-3.4
    COPY target/jet/app /app
    ENTRYPOINT ["/app/bin/catalina.sh", "run"]
    EXPOSE 8080

    .dockerignore:

    .gradle
    build/*
    gradle
    src/*
    !build/jet/app
    build.gradle
    gradlew
    gradlew.build
    .git
    .gitignore

Important: If you are using a version control system other than Git, make sure to replace the last two lines of .dockerignore with patterns to exclude the respective files and directories.

Issue a docker build command to create a Docker image with the application:

docker build -t my-app .

To create a writeable container layer over the freshly created image and start it, issue the following command:

# Change port numbers in the `-p` option as necessary.
docker run -d --name my-service -p 8080:8080 my-app

To check whether the application has started successfully:

docker logs my-service

To shutdown the service gracefully:

docker exec my-service /app/bin/shutdown.sh

For details, refer to the official Docker documentation.

REFERENCES

SUPPLEMENTAL DOWNLOADS

Coming soon...

Article ID: 38
Last Revised On: 21-Jul-2016