78

I am trying to deploy a Spring Boot app to Tomcat, because I want to deploy to AWS. I created a WAR file, but it does not seem to run on Tomcat, even though it is visible.

Details:
0. Here is my app:

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class App {
    public static void main(String[] args) {
        SpringApplication.run(SampleController.class, args);
    }
}

@Controller
@EnableAutoConfiguration
public class SampleController {
    @RequestMapping("/help")
    @ResponseBody
    String home() {
        String input = "Hi! Please use 'tag','check' and 'close' resources.";
        return input;
    }
}

application.properties has following:

server.port=${port:7777}
  1. After reading a number of pages and question-answers I added following to my POM:

    http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0

    <groupId>com.niewlabs</groupId>
    <artifactId>highlighter</artifactId>
    <version>1.0-SNAPSHOT</version>
    
    <packaging>war</packaging>
    
    <properties>
        <java.version>1.8</java.version>
    </properties>    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.1.9.RELEASE</version>
    </parent>    
    <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>
    

  2. I ran"mvn package"and got WAR file (size 250Mb), which I put into "webapps" folder.

  3. I started Tomcat and am able to see my app listed, in my case "/highlighter-1.0-SNAPSHOT".
  4. Clicking on the link for the app results in "Status 404" page.
  5. When I run Spring Boot app just by itself, without container it runs on localhost:7777, but there is nothing there when I run it in Tomcat.

Update: There is another reference. Not sure how useful it is.

Community
  • 1
  • 1
Daniil Shevelev
  • 11,739
  • 12
  • 50
  • 73
  • 4
    Have you extended `SpringBootServletInitializer` and overridden its `configure` method? – Andy Wilkinson Jan 12 '15 at 14:55
  • No, I did not see any mention of it in the Spring Guide WAR instructions. Can you please give me a link to or details? – Daniil Shevelev Jan 12 '15 at 14:56
  • 2
    @AndyWilkinson Thank you for the hint. I found the answer in the Spring Guide [http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#howto-convert-an-existing-application-to-spring-boot]. But my app still does not run on Tomcat. – Daniil Shevelev Jan 12 '15 at 15:07
  • I followed all the steps of the documents but still, I am getting 404 . From tomcat localhost logs it seems application has been detected. `09-Nov-2019 11:19:59.676 INFO [main] org.apache.catalina.core.ApplicationContext.log 1 Spring WebApplicationInitializers detected on classpath 09-Nov-2019 11:20:12.722 INFO [main] org.apache.catalina.core.ApplicationContext.log ContextListener: contextInitialized()` – Ajay Yadav Nov 09 '19 at 06:08
  • @AjayYadav how did you solve it? – Demobilizer Dec 22 '20 at 04:03

13 Answers13

109

This guide explains in detail how to deploy Spring Boot app on Tomcat:
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-create-a-deployable-war-file

Essentially I needed to add following class:

public class WebInitializer extends SpringBootServletInitializer {   
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(App.class);
    }    
}

Also I added following property to POM:

<properties>        
    <start-class>mypackage.App</start-class>
</properties>
Erich
  • 1,573
  • 1
  • 13
  • 21
Daniil Shevelev
  • 11,739
  • 12
  • 50
  • 73
  • 7
    I would update the reference to the following one - http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-create-a-deployable-war-file "Convert jar to war maven" section points just Embedded version in your reference. Anyway thanks – nickolay.laptev Apr 16 '15 at 08:45
  • This fix worked for me! I can confirm that the documentation has been updated as suggested in the comment above. http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-create-a-deployable-war-file – Miguel Reyes Oct 14 '15 at 16:56
  • 2
    I'm not sure the addition of the property is required. It wasn't needed for my configuration and it isn't mentioned in the referenced documentation. – LJ in NJ Feb 26 '18 at 16:39
  • Since the application was running in STS without this "ServeletInitializer" file, I removed that file with glory and banged my head for 2 days wondering why it can't run on Tomcat. Thank you for this answer. By looking at this Servlet name, I remembered my mistake and after replacing that file back, it's fixed. – learner May 17 '19 at 10:43
  • was not needed for me, running Spring Boot 2.1. – Agustí Sánchez Aug 03 '19 at 23:31
27

Hey make sure to do this changes to the pom.xml

<packaging>war</packaging>

in the dependencies section make sure to indicated the tomcat is provided so you dont need the embeded tomcat plugin.

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

    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
        <scope>provided</scope>
    </dependency>       

This is the whole pom.xml

<?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>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <start-class>com.example.Application</start-class>
    </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-test</artifactId>
            <scope>test</scope>
        </dependency>

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

        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <scope>provided</scope>
        </dependency>       

    </dependencies>

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


</project>

And the Application class should be like this

Application.java

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;

@SpringBootApplication
public class Application extends SpringBootServletInitializer {


    /**
     * Used when run as JAR
     */
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    /**
     * Used when run as WAR
     */
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(Application.class);
    }

}

And you can add a controller for testing MyController.java

package com.example;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class MyController {

    @RequestMapping("/hi")
    public @ResponseBody String hiThere(){
        return "hello world!";
    }
}

Then you can run the project in a tomcat 8 version and access the controller like this

http://localhost:8080/demo/hi

If for some reason you are not able to add the project to tomcat do a right click in the project and then go to the Build Path->configure build path->Project Faces

make sure only this 3 are selected

Dynamic web Module 3.1 Java 1.8 Javascript 1.0

Cesar Chavez
  • 394
  • 3
  • 7
22

I think you're confused by different paradigms here. First, war files and server deployment -- those things belong to Java Enterprise Edition (Java EE). These concepts have no real place in a spring-boot application, which follows a different model.

Spring-boot is responsible for creating an embedded container and running your services within it directly from standard jar files (although it can do a lot more). I think the intent of this model is to support micro-service development -- where each service has its own container and is completely self contained. You can use your code to generate Java EE apps too, but that would be silly considering that spring-boot is a lot easier (for certain types of application/service).

So, given this information you now have to decide what paradigm you're going to follow, and you need to follow that and only that.

Spring-boot is executable -- you just have to run the main method in the App class which you can do from the command line or using your favourite IDE or maven or gradle (tip: maven is the right answer). This will bring up a tomcat server (by default) and your service will be available within it. Given the configuration you posted above your service should be available at: http://localhost:7777/context/help -- the context is meant to be replaced with your context name, which you haven't shared.

You aren't meant to be creating a war, running tomcat, or deploying anything. None of that is necessary in spring-boot. The packaging in your pom should be jar, not war and the scope of the spring-boot-starter-tomcat should be removed -- it certainly isn't provided.

When you run your main method, the console output should tell you the context that you've registered; use that to get the URL right.

Having said all that, spring-boot has to exist in a JEE world for now (until it is widely adopted). For that reason, the spring people have documented an approach to building a war rather than an executable jar, for deployment to a servlet or JEE container. This allows a lot of the spring-boot tech to be used in environments where there are restrictions against using anything but wars (or ears). However, this is a just a response to the fact that such environments are quite common, and is not seen as a necessary, or even desirable, part of the solution.

Software Engineer
  • 15,457
  • 7
  • 74
  • 102
  • 5
    I would love to avoid deploying to Tomcat, but it seems like the easiest and simplest way to deploy my app to AWS. – Daniil Shevelev Jan 12 '15 at 15:38
  • 2
    Nah, using spring-boot and docker -- that's the easiest way to deploy anywhere, including AWS (trust me -- I'm speaking from experience) – Software Engineer Jan 12 '15 at 16:05
  • Ok, can you please give me pointers to how-tos on deploying Boot apps with Docker? – Daniil Shevelev Jan 12 '15 at 17:34
  • This article looks promising: http://thediscoblog.com/blog/2014/06/13/docker-containers-with-gradle-in-4-steps/ – Daniil Shevelev Jan 12 '15 at 17:58
  • 2
    You could look at: http://blog.adaofeliz.com/2014/11/21/first-look-spring-boot-and-docker/ – Software Engineer Jan 12 '15 at 19:51
  • 4
    I don't think this is structly true. Spring boot does allow for war files to be deployed to a container. However for the majority of the use case It is correct. So I'm tempted to both downvote and upvote the question. See https://docs.spring.io/spring-boot/docs/current/reference/html/howto-traditional-deployment.html – Wes Feb 29 '16 at 09:23
  • 2
    If you already have a bunch of existing classic spring applications running on tomcat, the spring boot war file deployment is a good way to start using spring boot without changing too much at once. – jkerak Nov 18 '16 at 00:29
  • @Engineer Dollery - saw this video on deploying a Spring Boot app with Docker. It makes things look easy. Are there any cons to using this method as a standard practice: https://www.youtube.com/watch?v=pqux6HO613M&feature=youtu.be – Casey Harrils Sep 14 '17 at 20:27
  • @CaseyHarrils - this method wouldn't scale to enterprise levels, but it's ok for playing with. At the enterprise level you'd probably want a lot more rigour than this. – Software Engineer Sep 15 '17 at 16:52
  • 1
    @Engineer Dollery Can you further clarify the statement: "At the enterprise level you'd probably want a lot more rigour than this"? What method would be suggested? TIA – Casey Harrils Sep 16 '17 at 00:18
6

After following the guide (or using Spring Initializr), I had a WAR that worked on my local computer, but didn't work remote (running on Tomcat).

There was no error message, it just said "Spring servlet initializer was found", but didn't do anything at all.

17-Aug-2016 16:58:13.552 INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet Engine: Apache Tomcat/8.5.4
17-Aug-2016 16:58:13.593 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive /opt/tomcat/webapps/ROOT.war
17-Aug-2016 16:58:16.243 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.

and

17-Aug-2016 16:58:16.301 INFO [localhost-startStop-1] org.apache.catalina.core.ApplicationContext.log 2 Spring WebApplicationInitializers detected on classpath
17-Aug-2016 16:58:21.471 INFO [localhost-startStop-1] org.apache.catalina.core.ApplicationContext.log Initializing Spring embedded WebApplicationContext
17-Aug-2016 16:58:25.133 INFO [localhost-startStop-1] org.apache.catalina.core.ApplicationContext.log ContextListener: contextInitialized()
17-Aug-2016 16:58:25.133 INFO [localhost-startStop-1] org.apache.catalina.core.ApplicationContext.log SessionListener: contextInitialized()

Nothing else happened. Spring Boot just didn't run.

Apparently I compiled the server with Java 1.8, and the remote computer had Java 1.7.

After compiling with Java 1.7, it started working.

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.7</java.version> <!-- added this line -->
    <start-class>myapp.SpringApplication</start-class>
</properties>
EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
6

Your Application.java class should extend the SpringBootServletInitializer class ex:

public class Application extends SpringBootServletInitializer {}
miken32
  • 42,008
  • 16
  • 111
  • 154
4

Solution for people using Gradle

Add plugin to build.gradle

apply plugin: 'war'

Add provided dependency to tomcat

dependencies {
    // other dependencies
    providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
}
Marcin Szymczak
  • 11,199
  • 5
  • 55
  • 63
3

public class Application extends SpringBootServletInitializer {}

just extends the SpringBootServletInitializer. It will works in your AWS/tomcat

junnyea
  • 471
  • 6
  • 8
  • 1
    Can you clarify what your answer brings that wasn't covered in either the answer that came out 3 hours before yours or the accepted answer from 2015? – Foon Sep 03 '16 at 11:29
3

I had same problem and i find out solution by following this guide . I run with goal in maven.

clean package

Its worked for me Thanq

VISHWANATH N P
  • 304
  • 1
  • 7
  • 11
2

TL;DR:

  • Make sure you have the same Tomcat version in your application dependency, and the external tomcat installation
  • Have the same Java version

I had followed all the steps to make a Spring Boot app to generate a WAR compatible with External Tomcat server. I extended SpringBootServletInitializer as well. If you have not done this already, first make these changes as explained in other answers or Official Spring Docs - Traditional Deployment

The app ran well on STS Embedded Tomcat, but would give 404 - Not Found in the external Tomcat. There was no error and catalina.bat run command also gave an info as "Deployment of war finished".

14-Apr-2021 12:38:01.996 INFO [main] org.apache.catalina.core.StandardService.startInternal Starting service [Catalina]
14-Apr-2021 12:38:01.996 INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet engine: [Apache Tomcat/10.0.5]
14-Apr-2021 12:38:02.023 INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive [C:\dev\apache-tomcat-10.0.5\webapps\server-test-1-0.0.1-SNAPSHOT.war]
14-Apr-2021 12:38:05.349 INFO [main] 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.
14-Apr-2021 12:38:05.436 INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive [C:\dev\apache-tomcat-10.0.5\webapps\server-test-1-0.0.1-SNAPSHOT.war] has finished in [3,413] ms

But typically, SpringBoot related output (like Initializing Spring DispatcherServlet 'dispatcherServlet') should also appear on this Catalina console. But that was not appearing.

The problem for me was: I had the tomcat-embed-core-9.0.44 in my maven dependency folder. But I had installed a Standalone Tomcat version 10.x.xx on my machine.

I installed a Tomcat version 9.0.xx on my machine, and the application worked fine with it.

Varshith
  • 58
  • 5
1

Update 2018-02-03 with Spring Boot 1.5.8.RELEASE.

In pom.xml, you need to tell Spring plugin when it is building that it is a war file by change package to war, like this:

<packaging>war</packaging>

Also, you have to excluded the embedded tomcat while building the package by adding this:

    <!-- to deploy as a war in tomcat -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
    </dependency>

The full runable example is in here https://www.surasint.com/spring-boot-create-war-for-tomcat/

Surasin Tancharoen
  • 5,520
  • 4
  • 32
  • 40
  • 3
    That is insufficient. The main Application class must implement SpringBootServletInitializer to be compatible for both container and standalone execution. The Op's example is missing it. – Aram Paronikyan Nov 13 '18 at 08:12
0

If your goal is to deploy your Spring Boot application to AWS, Boxfuse gives you a very easy solution.

All you need to do is:

boxfuse run my-spring-boot-app-1.0.jar -env=prod

This will:

  • Fuse a minimal OS image tailor-made for your app (about 100x smaller than a typical Linux distribution)
  • Push it to a secure online repository
  • Convert it into an AMI in about 30 seconds
  • Create and configure a new Elastic IP or ELB
  • Assign a new domain name to it
  • Launch one or more instances based on your new AMI

All images are generated in seconds and are immutable. They can be run unchanged on VirtualBox (dev) and AWS (test & prod).

All updates are performed as zero-downtime blue/green deployments and you can also enable auto-scaling with just one command.

Boxfuse also understands your Spring Boot config will automatically configure security groups and ELB health checks based upon your application.properties.

Here is a tutorial to help you get started: https://boxfuse.com/getstarted/springboot

Disclaimer: I am the founder and CEO of Boxfuse

Axel Fontaine
  • 34,542
  • 16
  • 106
  • 137
0

If you are creating a new app instead of converting an existing one, the easiest way to create WAR based spring boot application is through Spring Initializr.

It auto-generates the application for you. By default it creates Jar, but in the advanced options, you can select to create WAR. This war can be also executed directly.

enter image description here

Even easier is to create the project from IntelliJ IDEA directly:

File → New Project → Spring Initializr

Vojtech Ruzicka
  • 16,384
  • 15
  • 63
  • 66
0

it works after extending SpringBootServletInitializer class

public class Application extends SpringBootServletInitializer {}