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 :
When running the application on IntelliJ, and calling the endpoint with Postamn, I get a 200 statuscode and a "test ok" as a response
When running the WAR on Tomcat (10.x or 11.x) , and calling the same enpoint with Postamn, I get and "error ECONNREFUSED 127.7.0.1:8081"
Please note that :
There is no web.xml file in the application
The context path is only set in the application.properties of the project, not anywhere else
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.
So if the test is deployed on Tomcat Server, the URL will be:
http://localhost:8080/project/health-check
The following two settings will not be used when deployed to Tomcat Server
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
.
<?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>
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>
<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>
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.
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);
}
}