Deploy WAR to Tomcat (Spring Boot + Angular)

Tags: , , , ,



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(AutomationwhizApp.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----------------------------------------------------------nt" +
                "Application '{}' is running! Access URLs:nt" +
                "Local: tt{}://localhost:{}{}nt" +
                "External: t{}://{}:{}{}nt" +
                "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

Answer

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>




Source: stackoverflow