KoloM
KoloM

Reputation: 11

Java 21 spring-boot application running on IntelliJ is failing to run on Tomcat 10.x and 11.x

I have a multi-module Spring-Boot 3.4.2 application running with Java 21. The application is working fine when I run it on Intellij (entities persist, webservice responds well), but when I generate a war and try to run it on a standalone Tomcat server (10 and 11), the one webservice I created doesn't respond (the entities persist). What would be the problem? Is it on my configuration or on the Tomcat server?

Here is the pom.xml content (properties, dependencies and build ) :

    <properties>
        <spring-boot.version>3.4.2</spring-boot.version>
        <java.version>21</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web-services</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </dependency>
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>5.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <!--PERSISTENCE-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>6.6.8.Final</version>
        </dependency>
        <dependency>
            <groupId>jakarta.persistence</groupId>
            <artifactId>jakarta.persistence-api</artifactId>
            <version>3.1.0</version> <!-- Jakarta Persistence API -->
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.7.5</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
    </dependencies>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <mainClass>ca.project.projectApplication</mainClass>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.10.0</version>
                    <configuration>
                        <source>${java.version}</source>
                        <target>${java.version}</target>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>3.4.0</version>
                    <configuration>
                        <failOnMissingWebXml>false</failOnMissingWebXml>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>

and here is the application-properties content :

# PostgreSQL Database Connection Configuration
spring.datasource.url=jdbc:postgresql://localhost:5432/project_db
spring.datasource.username=postgres
spring.datasource.password=****
spring.datasource.driver-class-name=org.postgresql.Driver

# Hibernate Configuration
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.properties.hibernate.format_sql=true


#Hikary Configuration
spring.datasource.hikari.connectionTimeout=20000
spring.datasource.hikari.idleTimeout=30000
spring.datasource.hikari.maxLifetime=2000000
spring.datasource.hikari.maximumPoolSize=10

logging.level.org.springframework.web=DEBUG
logging.level.org.springframework.boot=DEBUG

#Context
server.servlet.context-path=/project

#Port
server.port=8081

Here is the main class content :

@SpringBootApplication
@ComponentScan(basePackages = {"ca"})
@EntityScan(basePackages = {"ca"})
@EnableJpaRepositories(basePackages = {"ca"})
public class ProjectApplication extends SpringBootServletInitializer{
    private static final Logger log = LogManager.getLogger(ProjectApplication.class);
    public static void main(String[] args) {
        SpringApplication.run(ProjectApplication.class);
        log.info("Project backend application started.");
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(ProjectApplication.class);
    }
}

And the RestController :

@RestController
@RequestMapping("/")
public class HealthCheckController {

    @Autowired
    IHealthCheckService healthCheckService;

    @GetMapping("health-check")
    public String performHealthCheck(){
        boolean isHealthy = healthCheckService.testHealth();
        if(isHealthy){
            return "test ok";
        }
        return "test ko";
    }
}

So I am trying to call this : http://localhost:8081/project/health-check

Here is the log when I am running the WAR on a Tomcat server (10.x or 11.x)

2025-03-02 22:09:55.320  INFO DESKTOP-696GCGE --- [           main] c.c.ProjectApplication                    : Starting ProjectApplication v0.0.1-SNAPSHOT using Java 21.0.6 with PID 11188 (D:\Logiciels\apache-tomcat-10.1.34\webapps\Project-backend-mainapp-0.0.1-SNAPSHOT\WEB-INF\classes started by MIANGALY in D:\Logiciels\apache-tomcat-10.1.34\bin)
2025-03-02 22:09:55.345 DEBUG DESKTOP-696GCGE --- [           main] c.c.ProjectApplication                    : Running with Spring Boot v3.4.2, Spring v6.2.2
2025-03-02 22:09:55.349  INFO DESKTOP-696GCGE --- [           main] c.c.ProjectApplication                    : The following 1 profile is active: "dev"
2025-03-02 22:09:56.658  INFO DESKTOP-696GCGE --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2025-03-02 22:09:56.701  INFO DESKTOP-696GCGE --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 19 ms. Found 0 JPA repository interfaces.
2025-03-02 22:09:58.026  WARN DESKTOP-696GCGE --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.ws.config.annotation.DelegatingWsConfiguration' of type [org.springframework.ws.config.annotation.DelegatingWsConfiguration$$SpringCGLIB$$0] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). The currently created BeanPostProcessor [annotationActionEndpointMapping] is declared through a non-static factory method on that class; consider declaring it as static instead.
2025-03-02 22:09:58.111  INFO DESKTOP-696GCGE --- [           main] .w.s.a.s.AnnotationActionEndpointMapping : Supporting [WS-Addressing August 2004, WS-Addressing 1.0]
2025-03-02 22:09:58.219  INFO DESKTOP-696GCGE --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2786 ms
2025-03-02 22:09:59.105  INFO DESKTOP-696GCGE --- [           main] o.h.j.i.u.LogHelper                      : HHH000204: Processing PersistenceUnitInfo [name: default]
2025-03-02 22:09:59.274  INFO DESKTOP-696GCGE --- [           main] o.h.Version                              : HHH000412: Hibernate ORM core version 6.6.8.Final
2025-03-02 22:09:59.391  INFO DESKTOP-696GCGE --- [           main] o.h.c.i.RegionFactoryInitiator           : HHH000026: Second-level cache disabled
2025-03-02 22:10:00.126  INFO DESKTOP-696GCGE --- [           main] o.s.o.j.p.SpringPersistenceUnitInfo      : No LoadTimeWeaver setup: ignoring JPA class transformer
2025-03-02 22:10:00.235  WARN DESKTOP-696GCGE --- [           main] c.z.h.HikariConfig                       : HikariPool-1 - idleTimeout has been set but has no effect because the pool is operating as a fixed size pool.
2025-03-02 22:10:00.237  INFO DESKTOP-696GCGE --- [           main] c.z.h.HikariDataSource                   : HikariPool-1 - Starting...
2025-03-02 22:10:01.034  INFO DESKTOP-696GCGE --- [           main] c.z.h.p.HikariPool                       : HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@1d4d84fb
2025-03-02 22:10:01.039  INFO DESKTOP-696GCGE --- [           main] c.z.h.HikariDataSource                   : HikariPool-1 - Start completed.
2025-03-02 22:10:01.209  WARN DESKTOP-696GCGE --- [           main] o.h.o.deprecation                        : HHH90000025: PostgreSQLDialect does not need to be specified explicitly using 'hibernate.dialect' (remove the property setting and it will be selected by default)
2025-03-02 22:10:01.279  INFO DESKTOP-696GCGE --- [           main] o.h.o.c.pooling                          : HHH10001005: Database info:
    Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
    Database driver: undefined/unknown
    Database version: 17.2
    Autocommit mode: undefined/unknown
    Isolation level: undefined/unknown
    Minimum pool size: undefined/unknown
    Maximum pool size: undefined/unknown
2025-03-02 22:10:03.969  INFO DESKTOP-696GCGE --- [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
2025-03-02 22:10:08.601  INFO DESKTOP-696GCGE --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2025-03-02 22:10:08.895  WARN DESKTOP-696GCGE --- [           main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2025-03-02 22:10:09.881  INFO DESKTOP-696GCGE --- [           main] c.c.ProjectApplication                    : Started ProjectApplication in 15.529 seconds (process running for 37.414)

The result :

Please note that :

I have tried running the war on Tomcat 10.1.34, 10.1.36 and 1.0.4. I have tried to add the "provided" scope to the tomcat dependency but it would fail even the Intellij run. I have tried removing the tomcat dependency, to no avail. I have tried changing the Tomcat server.xml by activating this :

<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
               maxThreads="150" SSLEnabled="true"
               maxParameterCount="1000"
               >
        <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
        <SSLHostConfig>
            <Certificate certificateKeystoreFile="conf/localhost-rsa.jks"
                         certificateKeystorePassword="changeit" type="RSA" />
        </SSLHostConfig>
    </Connector>

I also tried to run only the code with the RestController, without the entities, but it still didn't work.

Upvotes: 1

Views: 45

Answers (1)

life888888
life888888

Reputation: 3129

Quick answer:

  • Tomcat Server runs on port 8080 by default.

So if the test is deployed on Tomcat Server, the URL will be:

http://localhost:8080/project/health-check

application.properties

The following two settings will not be used when deployed to Tomcat Server

  • server.servlet.context-path=/project
  • server.port=8081

Tomcat Server runs on port 8080 by default.

The context-path default is based on your war file name. If your war file name is hello.war, its context-path will be /hello. If your war file name is project.war, its context-path will be /project.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         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>3.4.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo-springboot-web</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>demo-springboot-web</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </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>jakarta.platform</groupId>
            <artifactId>jakarta.jakartaee-api</artifactId>
            <version>10.0.0</version>
            <scope>provided</scope>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        
    </dependencies>

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

</project>

spring-boot-starter-web

Because this war is to be deployed on Tomcat Server, I don't need Spring Boot to use embedded Tomcat, so I exclude the use of embedded Tomcat.

        <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>

jakartaee-api

        <dependency>
            <groupId>jakarta.platform</groupId>
            <artifactId>jakarta.jakartaee-api</artifactId>
            <version>10.0.0</version>
            <scope>provided</scope>
        </dependency>

You do not need to include the following:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </dependency>
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>5.0.0</version>
        </dependency>

JPA

You just need to add the following two paragraphs.

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>

You do not need to set the following:

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>6.6.8.Final</version>
        </dependency>
        <dependency>
            <groupId>jakarta.persistence</groupId>
            <artifactId>jakarta.persistence-api</artifactId>
            <version>3.1.0</version> <!-- Jakarta Persistence API -->
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.7.5</version>
        </dependency>

The postgresql version will be configured by spring-boot-starter-data-jpa. hibernate-core will also be configured by spring-boot-starter-data-jpa.

Logging

Spring Boot uses SLF4J plus Logback by default. Without any additional configuration (no logback.xml), you can use it directly in your program:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

...
private static final Logger logger = LoggerFactory.getLogger(ProjectApplication.class);

...
logger.info(">>> ProjectApplication init");
...

ProjectApplication.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.servlet.support.SpringBootServletInitializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@SpringBootApplication
public class ProjectApplication extends SpringBootServletInitializer {
    private static final Logger logger = LoggerFactory.getLogger(ProjectApplication.class);
    public static void main(String[] args) {
        logger.info(">>> BEFORE - Project backend application started.");
        SpringApplication.run(ProjectApplication.class, args);
        logger.info(">>> AFTER - Project backend application started.");
    }

    public ProjectApplication(){
        logger.info(">>> ProjectApplication init");
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        logger.info(">>> SpringBootServletInitializer configure");
        return application.sources(ProjectApplication.class);
    }
}

Upvotes: 2

Related Questions