I'm running a Spring Boot(3.1.0) application inside a standalone Tomcat Server (10.12) where I defined a set of Context Parameters in /opt/tomcat/conf/Catalina/web1/ROOT.xml
as follows:
<?xml version="1.0" encoding="UTF-8"?>
<Context path="" docBase="/var/www/web1">
<Valve className="org.apache.catalina.valves.rewrite.RewriteValve"
resourcePath="rewrite.config"/>
<WatchedResource>WEB-INF/rewrite.config</WatchedResource>
<Parameter name="website" value="web1-dev" override="false"/>
<Parameter name="logging.file.name" value="web" override="false"/>
<Parameter name="log4j2.configurationFile" value="log4j2-dev.xml" override="false"/>
</Context>
For instance, the website
parameter should be dynamically used in the Property Configuration class:
@PropertySource(ignoreResourceNotFound = true, value =
{"classpath:/enviroment/default.properties",
"classpath:/enviroment/${website}.properties",
The Application however doesn't seem to see them during the startup phase. I've also tried to declare them in the application.yml
file:
server.servlet.context-parameters.log4j2.configurationFile=log4j2-dev.xml
but it still doesn't make any difference. It doesn't matter if I run the app locally with an embedded Tomcat or as WAR within the standalone Container.
What should I do to make these params visible/accessible to my Spring Boot app at the startup time?
The parameters are actually listed in the Spring Actuator /env endpoint:
{
"activeProfiles":[
],
"propertySources":[
{
"name":"servletContextInitParams",
"properties":{
"contextConfigLocation":{
"value":"******"
},
"website":{
"value":"******"
},
"log4j2.configurationFile":{
"value":"******"
},
"logging.file.name":{
"value":"******"
}
}
},
My idea was to read the params from the ServletContext
in the onStartup
method in the main Application.java
class, since it extends SpringBootServletInitializer
and then directly pass it to the system properties, e.g. System.setProperty("website", "web1"). The problem is that the onStartup
never gets executed.
Side note:
There are two applications (web1, web2) running parallel on the same server, both are using the same code base. I could eventually move two out of there parameters to the application.yml
since both apps currently use the same value on each stage (dev, staging, prod) but at least the logging.file.name
should differ from one another.
Any help much appreciated!
Finally I managed to pass the servlet context init params at the right stage.
The trick was to run the application as a .war file inside a standalone Tomcat container and override the
onStartup(ServletContext servletContext)
method in myApplication.java
, which subclasses theSpringBootServletInitializer
. There, I read the expected servlet context params and pass them directly to theSystem.setProperty(key, value)
method.The clue here is in the
jakarta.servlet.ServletContainerInitializer
interface and it's Spring implementationorg.springframework.web.SpringServletContainerInitializer
. It scans all implementations of theorg.springframework.web.WebApplicationInitializer
and executes theironStartup(ServletContext servletContext)
methods.It only works this way with a standalone servlet container (servlet context loaded first, then passed to the
onStartup
hook of the spring boot application).To overcome this problem when starting a spring boot application with an embedded servlet container, one has to use VM Options.
Hope this will help someone in the future.