I'm trying to deploy a spring boot application as a WAR into Tomcat. When I have things hard-coded, it works just fine, and there are no problems. But after my initial testing, I tried to clean things up and go back to the properties. And I'm finding that my application.properties file is not being read by the application during deployment in Tomcat. And I'm just confused.
My initial testing with Tomcat, to get the JNDI configured, was hard-coded, and everything worked just fine then:
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource() {
return (DataSource) new JndiTemplate().lookup("java:/comp/env/jdbc/myJndiLocal");
}
}
My context.xml is good and my tomcat's server.xml global resources are configured. The connection was made, and everything worked. Of course, hard-coded isn't what I want, so I switched to the properties.
Now, my problems are related to the org.springframework.boot.autoconfigure.jdbc.DataSourceProperties. I have my properties configured in Application.properties, which is in my resources directory:
spring.datasource.url=jdbc:postgresql://localhost:5432/MY_DB?currentSchema=public
spring.datasource.username=postgresql_username
spring.datasource.password=postgresql_password
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.jndiName=jdbc/myJndiLocal
And I created a datasource configuration that prepares my database connection:
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource(DataSourceProperties dataSourceProperties) {
System.out.printf(" !!!!! Datasource Config !!!!! %n" +
" url: %s%n" +
" username: %s%n" +
" password: %s%n" +
" driver: %s%n" +
" jndi: %s%n" +
" jndi full: %s%n",
dataSourceProperties.getUrl(), dataSourceProperties.getUsername(), dataSourceProperties.getPassword(),
dataSourceProperties.getDriverClassName(), dataSourceProperties.getJndiName(),
String.format("java:/comp/env/%s", dataSourceProperties.getJndiName())
);
if (dataSourceProperties.getJndiName() != null && !dataSourceProperties.getUrl().isBlank()) {
try {
return (DataSource) new JndiTemplate().lookup(String.format("java:/comp/env/%s", dataSourceProperties.getJndiName()));
}
catch (Exception e) {
System.out.println(" !!!!! JNDI Resource could not be found! Regular JDBC Configuration will be used instead. !!!!! ");
}
}
DataSourceBuilder<?> dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.driverClassName(dataSourceProperties.getDriverClassName());
dataSourceBuilder.url(dataSourceProperties.getUrl());
dataSourceBuilder.username(dataSourceProperties.getUsername());
dataSourceBuilder.password(dataSourceProperties.getPassword());
return dataSourceBuilder.build();
}
}
The first printf statement is just for current debugging, and I'll remove it later. My goal is to default to JNDI if present, otherwise use standard JDBC configuration. When running as a JAR with the embedded tomcat via my IDE, there are no problems. Properties are read into place, and everything gets configured as I expect.
!!!!! Datasource Config !!!!!
url: jdbc:postgresql://localhost:5432/MY_DB?currentSchema=public
username: postgresql_username
password: postgresql_password
driver: org.postgresql.Driver
jndi: jdbc/myJndiLocal
jndi full: java:/comp/env/jdbc/myJndiLocal
!!!!! JNDI Resource could not be found! Regular JDBC Configuration will be used instead. !!!!!
The problem now, however, is that my properties are all completely null when deployed as a WAR file into an external Tomcat.
!!!!! Datasource Config !!!!!
url: null
username: null
password: null
driver: null
jndi: null
jndi full: java:/comp/env/null
I'm using the SpringBootServletInitializer for the WAR deployment, so I have to assume that is causing some kind of issue, but I'm at a loss presently as to what it is.
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EntityScan(basePackages = {"com.mycompany.myapp.model", "com.mycompany.model"})
@EnableJpaRepositories(basePackages = {"com.mycompany.dao.jpa"})
@ComponentScan(basePackages = {
"com.mycompany.myapp",
"com.mycompany.service",
"com.mycompany.service.impl",
"com.mycompany.myapp.webapp.pages",
"com.mycompany.webapp.util",
"com.mycompany.webapp.util.migration",
"com.mycompany.myapp.annotations.validation",
"com.mycompany.annotations"
})
public class MyAppApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(MyAppApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(MyAppApplication.class, args);
}
}
I'm on the latest Spring Boot, 3.3.4, and am using the latest Tomcat, 10.1.31. I'm also utilizing Maven for the dependencies and builds. I have already confirmed that the Application.properties is inside the proper location in the WAR file - /WEB-INF/classes/Application.properties. Why are my properties not being read into the application, and what can I do to address this? I've been doing some reading and found this question, which is similar, but I'm not doing anything inside of my main method. DataSourceProperties is a default Spring class, so I would have expected it would just populate. The use of it as a parameter to my configuration method I would have expected to force a jumpstart even if it wasn't prepared automatically.
Found a resolution, although I don't really understand why it is needed, nor why it's helping. I needed to add the @PropertySource annotation to my DataSourceConfig. Once I did that, all my properties were in place, and everything loaded up perfectly, connecting to my defined JNDI.
@Configuration
@PropertySource("classpath:Application.properties")
public class DataSourceConfig {
....
}
So, the Application.properties clearly must already have been on the classpath in order for that annotation to work properly. I'm now mainly confused as to why it wasn't being used by the application? Is there something about Tomcat deployment or my current configuration that is forcing Spring to utilize its own internal defaults and ignore my configuration files? The documentation is telling me that the file should be picked up automatically.
Is your Application.properties
a typo?
The default name should be lowercase application.properties
.
In My First Answer, My Project.
If the file name of the configuration file is changed to ZZZApplication.properties
,
DemoJspApplication.java
must be modified to:
ADD @PropertySource("classpath:ZZZApplication.properties") // Specifying a custom configuration file
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@PropertySource("classpath:ZZZApplication.properties") // Specifying a custom configuration file
@SpringBootApplication
public class DemoJspApplication {
public static void main(String[] args) {
System.out.println(">>> DemoJspApplication start");
SpringApplication.run(DemoJspApplication.class, args);
System.out.println(">>> DemoJspApplication after run");
}
}
I've verified and tested it.