Spring Boot application deployment on standalone Tomcat

Spring Boot application deployment on standalone Tomcat Spring Boot allows quickly building a Spring based application skipping the boring tasks like configuring, carefully choosing and adding Spring products. It even contains embedded Jetty server. Thanks to it, application development does not require a standalone application server. It is definitely a benefit but you might what to deploy it to Tomcat. Surprisingly, it will not work out of the box. At least because of the fact that a JAR file is enough for Jetty but not for Tomcat which needs a WAR file. The changes are easy but you need to know what to do.

springSpring Boot allows quickly building a Spring based application skipping the boring tasks like configuring, carefully choosing and adding Spring products. It even contains embedded Jetty server. Thanks to it, application development does not require a standalone application server. It is definitely a benefit but you might what to deploy it to Tomcat. Surprisingly, it will not work out of the box. At least because of the fact that a JAR file is enough for Jetty but not for Tomcat which needs a WAR file. The changes are easy but you need to know what to do.

Building the first simple Spring Boot application is described in details in the Spring guides. The further instructions in this article assume, the project is configured similarly to the one from the guide.

 

Pack to WAR file

The first step that is quite easy to guess is building a WAR file instead of a JAR file. In case of a project based on Gradle, you have to add the following lines to the build.gradle file:

apply plugin: 'war'

war {
baseName = 'myApp'
version = '0.1.0'
}

The first line enables the war plugin. The rest of the code configures it.

Amend the dependencies section by commenting out entries about Jetty. Also exclude spring-boot-starter-tomcat module to not build the application with the embedded server. Add the providedRuntime clause instead. 

dependencies {
compile("org.springframework.boot:spring-boot-starter-web:1.3.3.RELEASE") {
exclude module: "spring-boot-starter-tomcat"
}
// compile("org.springframework.boot:spring-boot-starter-jetty")

providedRuntime group: 'org.springframework.boot', name: 'spring-boot-starter-tomcat', version: "1.3.3.RELEASE"
}

Then, refresh the Gradle project. New artifacts should show up in your IDE - WAR artifacts.

 

Where is Spring?

If you build the artifacts now and try to deploy to a standalone Tomcat, it should start without errors. Unfortunately, a disappointment will quickly come. If you review the logs, you will notice a lack of Spring logo which indicates that Spring did not start.

07-May-2016 10:15:24.542 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 62 ms
Connected to server
[2016-05-07 10:15:24,711] Artifact Gradle : com.dbapresents : myApp-0.1.0.war (exploded): Artifact is being deployed, please wait...
07-May-2016 10:15:25.110 INFO [RMI TCP Connection(3)-127.0.0.1] org.apache.jasper.servlet.TldScanner.scanJars At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
[2016-05-07 10:15:25,194] Artifact Gradle : com.dbapresents : myApp-0.1.0.war (exploded): Artifact is deployed successfully
[2016-05-07 10:15:25,194] Artifact Gradle : com.dbapresents : myApp-0.1.0.war (exploded): Deploy took 483 milliseconds
07-May-2016 10:15:34.540 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory C:\Program Files\Apache\apache-tomcat-9.0.0.M4\webapps\manager
07-May-2016 10:15:34.604 INFO [localhost-startStop-1] org.apache.jasper.servlet.TldScanner.scanJars At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
07-May-2016 10:15:34.617 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory C:\Program Files\Apache\apache-tomcat-9.0.0.M4\webapps\manager has finished in 76 ms

A solution is to adjust the class that contains the main method. The one with @SpringBootApplication annotation. The elements that I had to add are highlighted.

@SpringBootApplication
public class Application extends SpringBootServletInitializer {

@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {

return application.sources(Application.class);
}

public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);

System.out.println("Let's inspect the beans provided by Spring Boot:");

String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
}
}

After those two changes, it is enough to delete the already built artifacts, rebuild the project and build the artifacts again. When deploying on Tomcat, I got the following entries in the log file.

07-May-2016 10:36:03.933 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 77 ms
Connected to server
[2016-05-07 10:36:04,235] Artifact Gradle : com.dbapresents : myApp-0.1.0.war (exploded): Artifact is being deployed, please wait...
07-May-2016 10:36:06.373 INFO [RMI TCP Connection(3)-127.0.0.1] org.apache.jasper.servlet.TldScanner.scanJars At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.3.3.RELEASE)

2016-05-07 10:36:08.161  INFO 2368 --- [on(3)-127.0.0.1] c.dbapresents.myApp.Application   : Starting Application on chojrak with PID 2368 (D:\Git\myApp\out\artifacts\myApp\exploded\myApp-0.1.0.war\WEB-INF\classes started by Dba in C:\Program Files\Apache\apache-tomcat-9.0.0.M4\bin)
...

The final WAR file deploys on Tomcat successfully and Spring context starts up.

@SpringBootApplication
public class Application extends SpringBootServletInitializer {

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

public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);

System.out.println("Let's inspect the beans provided by Spring Boot:");

String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
}
}