GraalVM support

Table of Contents

Introduction

Tomcat supports using the GraalVM Native Image tool to produce a native binary including the container. This documentation page describes the build process of such an image.

Setup

The native image tool is much easier to use with single JARs, as a result the process will use the Maven shade plugin JAR packaging (fat JAR). The idea is to produce a single JAR that contains all necessary classes from Tomcat, the webapps and all additional dependencies. Although Tomcat has received compatibility fixes to support GraalVM native images, any library may not be compatible and may require replacement code (the GraalVM documentation has more details about this).

Download and install GraalVM. The first step is then to add the native-image tool.

export JAVA_HOME=/absolute...path...to/graalvm-ce-x.y.z
cd $JAVA_HOME/bin
./gu install native-image
Download the Tomcat Maven packaging from https://github.com/apache/tomcat/tree/master/res/tomcat-maven and place all the files in a folder (named tomcat-maven in this documentation.
export TOMCAT_MAVEN=/absolute...path...to/tomcat-maven
The build process now requires both Ant and Maven.

Packaging and Building

Inside the tomcat-maven folder, the directory structure is the same as for regular Tomcat. The main configuration files are placed in the conf folder, and if using the default server.xml the webapps are placed in the webapps folder.

The first step is to build the fat Tomcat JAR with all dependencies. Any JSP in the webapp must all be precompiled and packaged.

cd $TOMCAT_MAVEN
mvn package
ant -Dwebapp.name=somewebapp -f graal-webapp.ant.xml
Dependencies for the webapp should now be added to the main pom.xml, following with building the complete fat JAR.
mvn package

Native image configuration

Native images do not support any form of dynamic classloading or reflection unless it is defined explicitly in descriptors. Generating them uses a tracing agent from the GraalVM, and needs additional manual configuration in some cases.

Run the GraalVM substrate VM using the trace agent:

$JAVA_HOME/bin/java\
        -agentlib:native-image-agent=trace-output=$TOMCAT_MAVEN/target/trace-file.json\
        -Dcatalina.base=. -Djava.util.logging.config.file=conf/logging.properties\
        -jar target/tomcat-maven-1.0.jar

Now all paths from the webapp that lead to dynamic classloading (ex: Servlet access, websockets, etc) need to be accessed using a script that will exercise the webapp. Servlets may be loaded on startup instead of needing an actual access. Listeners may also be used to load additional classes on startup.

Once that is done, the VM may be shut down. The descriptors can now be generated from the trace file.

$JAVA_HOME/bin/native-image-configure generate\
        --trace-input=$TOMCAT_MAVEN/target/trace-file.json\
        --output-dir=$TOMCAT_MAVEN/target
At this point, further configuration must be made to add items that are not traced, including: base interfaces, resource bundles, BeanInfo based reflection, etc. Please refer to the Graal documentation for more information on this process.

Building the native image

If everything has been done properly, the native image can now be built using the native-image tool.

$JAVA_HOME/bin/native-image --no-server\
        --allow-incomplete-classpath --enable-https\
        --initialize-at-build-time=org.eclipse.jdt,org.apache.el.parser.SimpleNode,javax.servlet.jsp.JspFactory,org.apache.jasper.servlet.JasperInitializer,org.apache.jasper.runtime.JspFactoryImpl\
        -H:+JNI -H:+ReportUnsupportedElementsAtRuntime\
        -H:+ReportExceptionStackTraces -H:EnableURLProtocols=http,https,jar\
        -H:ConfigurationFileDirectories=$TOMCAT_MAVEN/target/\
        -H:ReflectionConfigurationFiles=$TOMCAT_MAVEN/tomcat-reflection.json\
        -H:ResourceConfigurationFiles=$TOMCAT_MAVEN/tomcat-resource.json\
        -H:JNIConfigurationFiles=$TOMCAT_MAVEN/tomcat-jni.json\
        -jar $TOMCAT_MAVEN/target/tomcat-maven-1.0.jar

Running the native image is then:

./tomcat-maven-1.0 -Djava.library.path=$JAVA_HOME/jre/lib/amd64\
        -Dcatalina.base=. -Djava.util.logging.config.file=conf/logging.properties

Compatibility

Servlets, JSPs, EL, websockets, the Tomcat container, tomcat-native, HTTP/2 are all supported out of the box in a native image. However, EL uses BeanInfo reflection which needs manual descriptor configuration. To give an example, the EL expression

${pageContext.servletContext.serverInfo}
needs full reflection information on the concrete Jasper page context class, as well as the Catalina Servlet context implementation. Graal error messages during runtime generally indicate which classes are missing reflection as the BeanInfo reported for these will be empty.

At the time of writing this documentation, JULI is not supported as the log manager configuration property is not supported by Graal, in addition to some static initializer problems, and the regular java.util.logging loggers and implementation should be used instead.

If using the default server.xml file, some Server listeners have to be removed from the configuration as they are not compatible with native images, such as a JMX listener (JMX is unsupported) and leak prevention listeners (use of internal code that does not exist in Graal).

Comments

Notice: This comments section collects your suggestions on improving documentation for Apache Tomcat.

If you have trouble and need help, read Find Help page and ask your question on the tomcat-users mailing list. Do not ask such questions here. This is not a Q&A section.

The Apache Comments System is explained here. Comments may be removed by our moderators if they are either implemented or considered invalid/off-topic.