3

I am trying to deploy the Spring Boot Application with WAR packaging to Tomcat 10. The application gets deployed successfully, however, when I try to access the endpoints it results in 404 Not Found.

WAR File: application.war

http://localhost:8080/application/api/abc -> 404 Not Found (Spring Boot Endpoint)
http://localhost:8080/application/abc -> 404 Not Found (Angular URL which calls /api/abc)

Tomcat webapps/application folder consists of following and index.html (Angular) have <base href="/">

enter image description here


As discussed here, I have added a class AppServletInitializer which extends SpringBootServletInitializer as follow

@Configuration
public class AppServletInitializer extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(SpringApp.class);
    }
} 

In pom.xml, I have added the dependency for spring-boot-starter-tomcat, tomcat-embed-jasper and set packaging option as war. I have shared the simplified (removed dependency, plugins. will add the same if required)

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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.name</groupId>
    <artifactId>application</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>My Application</name>

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

    <properties>
        <!-- Build properties -->
        <maven.version>3.3.9</maven.version>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <project.testresult.directory>${project.build.directory}/test-results</project.testresult.directory>
        <maven.build.timestamp.format>yyyyMMddHHmmss</maven.build.timestamp.format>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
        <argLine>-Djava.security.egd=file:/dev/./urandom -Xmx256m</argLine>
        <m2e.apt.activation>jdt_apt</m2e.apt.activation>
        <run.addResources>false</run.addResources>

        <jhipster-dependencies.version>3.0.5</jhipster-dependencies.version>
        <spring-boot.version>2.1.8.RELEASE</spring-boot.version>
        <spring.version>5.1.9.RELEASE</spring.version>
        <hibernate.version>5.3.11.Final</hibernate.version>
        <javassist.version>3.23.2-GA</javassist.version>
        <maven-clean-plugin.version>3.1.0</maven-clean-plugin.version>
        <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
        <maven-javadoc-plugin.version>3.1.1</maven-javadoc-plugin.version>
        <maven-resources-plugin.version>3.1.0</maven-resources-plugin.version>
        <maven-war-plugin.version>3.2.3</maven-war-plugin.version>
        <properties-maven-plugin.version>1.0.0</properties-maven-plugin.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>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <defaultGoal>spring-boot:run</defaultGoal>
        <finalName>application</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-enforcer-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <mainClass>${start-class}</mainClass>
                    <fork>true</fork>
                </configuration>
            </plugin>
        </plugins>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>${maven-compiler-plugin.version}</version>
                </plugin>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <version>${spring-boot.version}</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>

    <profiles>
        <profile>
            <id>dev</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-devtools</artifactId>
                    <optional>true</optional>
                </dependency>
            </dependencies>
        </profile>
        <profile>
            <id>prod</id>
            <build>
                <plugins>
                    <plugin>
                        <artifactId>maven-clean-plugin</artifactId>
                        <configuration>
                            <filesets>
                                <fileset>
                                    <directory>target/classes/static/</directory>
                                </fileset>
                            </filesets>
                        </configuration>
                    </plugin>
                    <plugin>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-maven-plugin</artifactId>
                        <configuration>
                            <mainClass>${start-class}</mainClass>
                        </configuration>
                        <executions>
                            <execution>
                                <goals>
                                    <goal>build-info</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                    <!--                    <plugin>-->
                    <!--                        <groupId>org.apache.maven.plugins</groupId>-->
                    <!--                        <artifactId>maven-war-plugin</artifactId>-->
                    <!--                        <version>${maven-war-plugin.version}</version>-->
                    <!--                        <executions>-->
                    <!--                            <execution>-->
                    <!--                                <goals>-->
                    <!--                                    <goal>war</goal>-->
                    <!--                                </goals>-->
                    <!--                                <phase>package</phase>-->
                    <!--                            </execution>-->
                    <!--                        </executions>-->
                    <!--                        <configuration>-->
                    <!--                            &lt;!&ndash;<warSourceIncludes>WEB-INF/**,META-INF/**</warSourceIncludes>&ndash;&gt;-->
                    <!--                            <failOnMissingWebXml>false</failOnMissingWebXml>-->
                    <!--                        </configuration>-->
                    <!--                    </plugin>-->
                </plugins>
            </build>
        </profile>
    </profiles>
</project>

Application.java

import io.github.jhipster.config.JHipsterConstants;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.core.env.Environment;

@SpringBootApplication
@EnableConfigurationProperties({LiquibaseProperties.class, ApplicationProperties.class, AppServletInitializer.class})
public class Application extends SpringBootServletInitializer implements InitializingBean {

    private static final Logger log = LoggerFactory.getLogger(Application.class);

    private final Environment env;

    public Application(Environment env) {
        this.env = env;
    }

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

    @Override
    public void afterPropertiesSet() throws Exception {
        Collection<String> activeProfiles = Arrays.asList(env.getActiveProfiles());
        if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) {
            log.error("You have misconfigured your application! It should not run " +
                "with both the 'dev' and 'prod' profiles at the same time.");
        }
        if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_CLOUD)) {
            log.error("You have misconfigured your application! It should not " +
                "run with both the 'dev' and 'cloud' profiles at the same time.");
        }
    }


    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        DefaultProfileUtil.addDefaultProfile(app);
        Environment env = app.run(args).getEnvironment();
        logApplicationStartup(env);
    }

    private static void logApplicationStartup(Environment env) {
        String protocol = "http";
        if (env.getProperty("server.ssl.key-store") != null) {
            protocol = "https";
        }
        String serverPort = env.getProperty("server.port");
        String contextPath = env.getProperty("server.servlet.context-path");
        if (StringUtils.isBlank(contextPath)) {
            contextPath = "/";
        }
        String hostAddress = "localhost";
        try {
            hostAddress = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            log.warn("The host name could not be determined, using `localhost` as fallback");
        }
        log.info("\n----------------------------------------------------------\n\t" +
                "Application '{}' is running! Access URLs:\n\t" +
                "Local: \t\t{}://localhost:{}{}\n\t" +
                "External: \t{}://{}:{}{}\n\t" +
                "Profile(s): \t{}\n----------------------------------------------------------",
            env.getProperty("spring.application.name"),
            protocol,
            serverPort,
            contextPath,
            protocol,
            hostAddress,
            serverPort,
            contextPath,
            env.getActiveProfiles());
    }
}

application.yml

server:
  servlet:
    context-path: /application

We are using Angular on the client-side and resources are located in the static folder.
Resource Location

Environment: Spring Boot 2.1.8, Angular 8, Java 11,

I have already referred, unfortunately, none of them is working for me. Any help would be Appreciated

  1. Create a Deployable War File
  2. Spring Boot War Deploy
  3. Can not access deployed WAR file on Tomcat
Romil Patel
  • 12,879
  • 7
  • 47
  • 76
  • Not really an answer, just a quick idea: open start.spring.io and create a "sample project" that produces an artifact with packaging WAR and your java and spring boot version. Compile it and try to deploy on tomcat, make sure it works as expected (so that you'll be sure that tomcat is ok), then try to compare the created artifacts and see the differences. – Mark Bramnik Mar 05 '20 at 05:57
  • Are you using the `@SpringBootApplication` annotation in your main class? Your main class should extend `SpringBootServletInitializer` and implement a main to actually start the SpringApplication... – Carsten Mar 05 '20 at 06:25
  • 1
    You can try what Mark has said. Also, check if it's working in an another version of Tomcat. 9 or 8. 10 is the latest release it's still in alpha stage. – Shiva kumar Mar 05 '20 at 06:55
  • Make sure your `server.contextPath` config correctly, Refer this https://stackoverflow.com/questions/28089129/spring-boot-deployed-in-tomcat-gives-404-but-works-stand-alone – hrdkisback Mar 05 '20 at 07:22
  • Thanks, @MarkBramnik. I have created the sample application with WAR packaging which results in 404 Not Found with Tomcat 10, However, it works fine with Tomcat 9. I have also tried to deploy my application with Tomcat 9 but unfortunately, the same issue persists. – Romil Patel Mar 05 '20 at 08:52
  • @Carsten, I have `@SpringBootApplication` in the main class, I have updated the question with it. – Romil Patel Mar 05 '20 at 08:53
  • Thanks, @hrdkisback. I have updated the question with `application.yml` with related configurations – Romil Patel Mar 05 '20 at 08:55
  • Is there a reason to have your `spring-boot-starter-tomcat` set as provided ? Can you provide it from your tomcat ? Try to run your app with Tomcat and the Spring Boot Launcher from Intellij and compare the outputs. If some lines are missing in your logs, that probably means some libs are missing. – Synops Mar 05 '20 at 09:19
  • Open environment.ts file, check base URL of your backend project. – Garima Mar 05 '20 at 06:25
  • We have `'http://' + window.location.hostname + ':8080/application'` as baseURL in environment.ts – Romil Patel Mar 05 '20 at 08:58

1 Answers1

1

The configuration was correct in the Project. As mentioned in the comment, there might be some issue with Tomcat 10 as it was in the alpha version. I have used Tomcat 9 it was working fine.

The configuration are as below.

Step 1: Change the packing option to war in pom.xml

<packaging>war</packaging>

Step 2: extends SpringBootServletInitializer

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

/**
 * This is a helper Java class that provides an alternative to creating a {@code web.xml}.
 * This will be invoked only when the application is deployed to a Servlet container like Tomcat, JBoss etc.
 */
public class ApplicationWebXml extends SpringBootServletInitializer {

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

Step 3: Create WAR file

mvn clean install 

And there we go! We can access the endpoints at

http://localhost:8080/context-path/endpoint OR
http://localhost:8080/war-filename/endpoint

We can also create the profile for war as below

<properties>
    <maven-war-plugin.version>3.2.3</maven-war-plugin.version>
</properties>

     <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>${maven-war-plugin.version}</version>
                    <executions>
                        <execution>
                            <goals>
                                <goal>war</goal>
                            </goals>
                            <phase>package</phase>
                        </execution>
                    </executions>
                    <configuration>
                        <warSourceIncludes>WEB-INF/**,META-INF/**</warSourceIncludes>
                        <failOnMissingWebXml>false</failOnMissingWebXml>
                        <warSourceDirectory>target/classes/static/</warSourceDirectory>
                        <webResources>
                            <resource>
                                <directory>src/main/webapp</directory>
                                <includes>
                                    <include>WEB-INF/**</include>
                                </includes>
                            </resource>
                        </webResources>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>


    <profiles>
        <profile>
            <id>war</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-war-plugin</artifactId>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>


Community
  • 1
  • 1
Romil Patel
  • 12,879
  • 7
  • 47
  • 76
  • I am having a similar issue, wonder if you can take a look at https://stackoverflow.com/questions/69509053/angular-spring-boot-war-deployment-in-tomcat-is-giving-401 – ananth Oct 09 '21 at 17:40