others-how to solve 404 error when running springboot war application in tomcat?

1. Purpose

In this post, I will show you how to solve 404 error when running springboot war application in tomcat, just as the following error shows:

# HTTP Status 404 – Not Found

**Type** Status Report

**Message** /demo/

**Description** The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.

The error screenshot is as follows:

The code of my springboot controller is as follows, you can see that I just declared a method named hello and also a main method of our application.

@RestController  
public class SampleController {  
    @RequestMapping(value = "/hello", method = RequestMethod.GET)  
    public String hello() {  
        String str = "An :grinning:awesome :smiley:string 😄with a few :wink:emojis!";  
        String result = EmojiParser.parseToUnicode(str);  
        return "hello to ark dynamic deploy "+result;  
    }  
}

@SpringBootApplication(exclude = JacksonAutoConfiguration.class)  
public class SpringBootArkBizApplication {  
  
   public static void main(String[] args) {  
       
      SpringApplication.run(SpringBootArkBizApplication.class, args);
   }  
}

Here is the pom.xml ,notice that we use war as the packaging method:

<?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.5.0</version>  
      <relativePath/> <!-- lookup parent from repository -->  
   </parent>  
   <groupId>com.alipay.sofa</groupId>  
   <artifactId>spring-boot-ark-biz</artifactId>  
   <version>0.0.3-SNAPSHOT</version>  
   <name>spring-boot-ark-biz</name>  
   <packaging>war</packaging>  
   <description>Demo project for Spring Boot</description>  
   <properties>      
       <java.version>8</java.version>  
   </properties>   
   
   <dependencies>      
     <dependency>                  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-starter</artifactId>  
      </dependency>  
      <dependency>  
         <groupId>org.springframework.boot</groupId>  
         <artifactId>spring-boot-starter-web</artifactId>  
      </dependency>  
      <dependency>         
         <groupId>com.vdurmont</groupId>  
         <artifactId>emoji-java</artifactId>  
         <version>5.1.1</version>  
      </dependency>
   </dependencies>  

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

After run this command:

mvn package

I got a war named demo.war, and then I deploy it in apache tomcat 9, I got this directory structure in tomat/webapps/demo:

demo/
├── META-INF
│   ├── MANIFEST.MF
│   ├── maven
│   │   └── com.alipay.sofa
│   │       └── spring-boot-ark-biz
│   │           ├── pom.properties
│   │           └── pom.xml
│   └── war-tracker
├── WEB-INF
│   ├── classes
│   │   ├── application.properties
│   │   └── com
│   │       └── alipay
│   │           └── sofa
│   │               └── springbootarkbiz
│   │                   ├── SpringBootArkBizApplication.class
│   │                   └── rest
│   │                       └── SampleController.class
│   ├── layers.idx
│   └── lib
│       ├── emoji-java-5.1.1.jar
│       ├── jackson-annotations-2.12.3.jar
│       ├── tomcat-embed-el-9.0.46.jar
│       └── tomcat-embed-websocket-9.0.46.jar
└── org
    └── springframework
        └── boot
            └── loader
                ├── ClassPathIndexFile.class
                ├── ExecutableArchiveLauncher.class
                ├── WarLauncher.class
                ├── archive
                │   ├── Archive$Entry.class
                │   └── JarFileArchive.class
                ├── data
                │   ├── RandomAccessData.class
                │   └── RandomAccessDataFile.class
                ├── jar
                │   ├── AbstractJarFile$JarFileType.class
                │   ├── StringSequence.class
                │   └── ZipInflaterInputStream.class
                ├── jarmode
                │   ├── JarMode.class
                │   ├── JarModeLauncher.class
                │   └── TestJarMode.class
                └── util
                    └── SystemPropertyUtils.class

21 directories, 108 files

And the META-INF/MANIFEST.MF content is:

➜  webapps cat demo/META-INF/MANIFEST.MF 
Manifest-Version: 1.0
Spring-Boot-Classpath-Index: WEB-INF/classpath.idx
Implementation-Title: spring-boot-ark-biz
Implementation-Version: 0.0.3-SNAPSHOT
Spring-Boot-Layers-Index: WEB-INF/layers.idx
Start-Class: com.alipay.sofa.springbootarkbiz.SpringBootArkBizApplicat
 ion
Spring-Boot-Classes: WEB-INF/classes/
Spring-Boot-Lib: WEB-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.5.0
Created-By: Maven WAR Plugin 3.3.1
Main-Class: org.springframework.boot.loader.WarLauncher

But when I run tomcat and visit http://localhost:8080/hello, I got this 404 error:

# HTTP Status 404 – Not Found

**Type** Status Report

**Message** /demo/

**Description** The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.



2. Solution

Here is the working solution

First remove the tomcat dependency from spring-boot-starter-web , because it’s privided by our running environment.

<?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.5.0</version>  
      <relativePath/> <!-- lookup parent from repository -->  
   </parent>  
   <groupId>com.alipay.sofa</groupId>  
   <artifactId>spring-boot-ark-biz</artifactId>  
   <version>0.0.3-SNAPSHOT</version>  
   <name>spring-boot-ark-biz</name>  
   <packaging>war</packaging>  
   <description>Demo project for Spring Boot</description>  
   <properties>      
       <java.version>8</java.version>  
    </properties>   
    <dependencies>      
        <dependency>         
            <groupId>org.springframework.boot</groupId>  
             <artifactId>spring-boot-starter-web</artifactId>  
             <exclusions>            
                <exclusion>                <groupId>org.springframework.boot</groupId>  
               <artifactId>spring-boot-starter-tomcat</artifactId>  
                </exclusion>         
            </exclusions>      
      </dependency>  
      <dependency>         
          <groupId>org.springframework.boot</groupId>  
         <artifactId>spring-boot-starter-tomcat</artifactId>  
         <scope>provided</scope>  
      </dependency>  
      <dependency>         
          <groupId>javax.servlet</groupId>  
         <artifactId>javax.servlet-api</artifactId>  
         <scope>provided</scope>  
      </dependency>      
      <dependency>         
          <groupId>org.apache.tomcat</groupId>  
         <artifactId>tomcat-servlet-api</artifactId>  
         <version>9.0.73</version>  
         <scope>provided</scope>  
      </dependency>  
  
      <dependency>         
          <groupId>com.vdurmont</groupId>  
         <artifactId>emoji-java</artifactId>  
         <version>5.1.1</version>  
      </dependency>  
   </dependencies>  
  
   <build>     
       <plugins>
          <plugin>  
               <groupId>org.springframework.boot</groupId>  
               <artifactId>spring-boot-maven-plugin</artifactId>  
          </plugin>
       </plugins>

   </build>  
</project>

And then change the main code of our application:

@SpringBootApplication(exclude = JacksonAutoConfiguration.class)  
public class SpringBootArkBizApplication extends SpringBootServletInitializer {  
  
   public static void main(String[] args) {  
      SpringApplicationBuilder builder = new SpringApplicationBuilder(SpringBootArkBizApplication.class).web(WebApplicationType.SERVLET);  
      ConfigurableApplicationContext context = builder.build().run(args);  
  
      System.out.println("SpringBootArkBizApplication start!");  
      System.out.println("SpringBootArkBizApplication spring boot version: " + SpringApplication.class.getPackage().getImplementationVersion());  
      System.out.println("SpringBootArkBizApplication classLoader: " + SpringBootArkBizApplication.class.getClassLoader());  
  
   }  
}

In the above code , we use SpringApplicationBuilder instead of SpringApplication, because:

With SpringApplication , most of application settings have hard coded default values like profiles and property files to use etc. You need to look at this class’s code to understand that.

With SpringApplicationBuilder , you can simply change few of these application default settings before application starts even though most of these settings have sensible default values. So with few lines of code, you can build a different applications with different settings for different purposes ( embedded deployment, external deployment , testing etc ) while your actual underlying business logic remains same.

We call the .web method of SpringApplicationBuilder,which will :

Flag to explicitly request a specific type of web application.

Then test again:

➜  webapps curl http://localhost:8080/biz/hello
hello to ark dynamic deploy An 😀awesome 😃string 😄with a few 😉emojis!%  

It works!



3. Summary

In this post, I demonstrated how to solve the 404 error when trying to deploy a springboot application in a traditional web server , e.g. Apache Tomcat, the key solution is to adjust your dependencies and your main code of your app. That’s it, thanks for your reading.