Published on Feb 9 2018 in Java Tomcat

We build proof-of-concept JAR and WAR Spring Boot application and show how to run it on shared cPanel server using any of our Java hosting packages.

We use JDK 8 and Maven 3.2 that are available with any Java hosting package. No IDE will be used but commands typed directly in shell for this simple task.

Check that Java and Maven are ready:

java -version
mvn -v

The below POM allows for generating JAR and WAR with only 1 change: the packaging attribute. First we POM will generate JAR. Later we will update it so that it generates a WAR i.e. add <packaging>war</packaging>. For testing WAR we will use a standalone Tomcat that runs beside.

cat > pom.xml<<EOF
<?xml version="1.0" encoding="UTF-8"?>
<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.example</groupId>
    <artifactId>SpringBootTester</artifactId>
    <version>0.1</version>
    <packaging>jar</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.2.RELEASE</version>
    </parent>

    <dependencies>

    <!-- Add Tomcat and Spring MVC -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

        <!-- Skip the following dependency if you are not going to use the application with external Tomcat. Embedded Tomcat does not need it. -->
    <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-tomcat</artifactId>
           <scope>provided</scope>
        </dependency>

    </dependencies>

    <!-- We defintely need below for Maven to process Spring Boot apps. -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
EOF

Embedded servlet container dependency is marked as ‘provided’. Generated WAR will contain dependencies marked this way in lib-provided directory. In effect you will also be able to run the application using java -jar that we will test below.

Now the Java code. @RestController will allow us to skip creation of view file (template) needed for regular @Controller. @RequestMapping("/") maps application’s root URL (not domain URL) to the hello function. If the application will be deployed as ROOT.war then application’s root URL will equal domain’s root URL.

mkdir -p src/main/java

cat > src/main/java/Example.java<<EOF
package com.example;

import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.web.bind.annotation.*;

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;

@RestController
@SpringBootApplication
public class Example extends SpringBootServletInitializer {

    @RequestMapping("/")
    String hello() {
        return "Hello World from JAR or WAR!";
    }

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

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

}
EOF

Testing embedded Tomcat via ‘spring-boot:run’

mvn spring-boot:run

Ctrl+C will shutdown the embedded server. On another terminal reach the URL:

curl http://springboot.budgetjava.com:8080/
Hello World from JAR or WAR!

Testing JAR at custom port

mvn package
java $JAVA_OPTS -Dserver.port=11188 -jar target/SpringBootTester-0.1.jar 
curl http://springboot.budgetjava.com:11188/
Hello World from JAR or WAR!

Ctrl+C will shutdown the process if it runs in foreground. Of course you can leave it running in background and logging to a file by appending &>log & at the end of the java -jar ... command.

Testing WAR in external Tomcat listening on default HTTP port

We need to modify packaging line first.

sed -i -re 's/(<packaging>)jar(<\/packaging>)/\1war\2/' pom.xml
mvn clean package
cp target/SpringBootTester-0.1.war $CATALINA_HOME/webapps

Wait until it deploys by tracing log with tail -f $CATALINA_HOME/logs/catalina.out. On another terminal check:

curl http://springboot.budgetjava.com/SpringBootTester-0.1/
Hello World from JAR or WAR!

You might also have copied it to $CATALINA_HOME/webapps/ROOT.war to have it served at domain’s root URL. See how main class changes in MANIFEST.MF depending on what you set in pom.xml:

unzip -c target/SpringBootTester-0.1.war META-INF/MANIFEST.MF | grep Class
Start-Class: com.example.Example
Main-Class: org.springframework.boot.loader.WarLauncher

unzip -c target/SpringBootTester-0.1.jar META-INF/MANIFEST.MF | grep Class
Start-Class: com.example.Example
Main-Class: org.springframework.boot.loader.JarLauncher

There are many alternative ways of presetting server.port for example in src/main/resources/application.properties:

server.port=8888

or in src/main/resources/application.yml

server:
  port: 8888

Here are the SpringBootTester-0.1.jar and SpringBootTester-0.1.war for your reference.

For SpringBoot 2.0 SpringBootServletInitializer is in the org.springframework.boot.web.servlet.support package so the complete example could look like this:

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

src/main/java/DemoApplication.java:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

@SpringBootApplication
public class DemoApplication extends SpringBootServletInitializer {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, (String[])args);
    }
}

src/main/java/controller/HelloController.java:

package com.example.demo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController

public class HelloController {
    @GetMapping(value={"/say"})
    public String sayHello() {
        return "{ \"name\":\"John Doe\"}";
    }
}

Build WAR as usual with mvn clean package and finally deploy to Tomcat’s webapps and check with curl http://domain.com/demo/say.

Get testing demo WAR demo-2.2.2-SNAPSHOT.war) and use it with Tomcat 9.x.

For SpringBoot 3.0 (get testing demo WAR demo-3.0.4-SNAPSHOT.war) please use Java 17+ and Tomcat 10.x. You may also need to comment out default servlets (default and jsp) and their mappings in Tomcat’s conf/web.xml to avoid Spring security method cannot decide pattern is mvc or not Spring Boot application exception.