I have the following directory structure/config file:
src/main/resource/config:
application.yml
application-dev.yml
application-sit.yml
Note according to the "Bootiful Configuration" https://spring.io/blog/2015/01/13/configuring-it-all-out-or-12-factor-app-style-configuration-with-spring:
Spring Boot will read the properties in src/main/resources/application.properties by default. If a profile is active, it will also automatically reads in the configuration files based on the profile name, like src/main/resources/application-foo.properties where foo is the current profile. If the Snake YML library is on the classpath, then it will also automatically load YML files.
Since snake YML jar is in class path if I set --spring.profiles.active=dev
as a
program arg in eclipse run configuration and use this as my main method Ever thing works as expected:
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Application.class);
SimpleCommandLinePropertySource source = new SimpleCommandLinePropertySource(args);
// Check if the selected profile has been set as argument.
// if not the development profile will be added
addDefaultProfile(app, source);
app.run(args);
}
/**
* Set a default profile if it has not been set
*/
private static void addDefaultProfile(SpringApplication app, SimpleCommandLinePropertySource source) {
if (!source.containsProperty("spring.profiles.active")) {
app.setAdditionalProfiles(Constants.SPRING_PROFILE_DEVELOPMENT);
}
}
(Please note the main method reference above is from the following class used in my code: https://github.com/jarias/generator-jhipster-ember/blob/master/app/templates/src/main/java/package/_Application.java)
Everything works as expected for spring.profile.active=dev. Which means that both: application.yml(loaded by default) and application-dev.yml(active profile) property files are loaded and excludes application-sit.yml since sit isn't an active profile.
This embedded container works great for dev testing. However I want to release this into production by generating a war and deploy it to a standalone Tomcat8 Server.
For that I created an implementation of WebApplicationInitializer which is required by Tomcat8 server to automatically detect, bootstrap and start spring application on the standalone server.
@Configuration
public class WebAppInit implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
ApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
}
}
After deploying the war I receive the following error I attempt to start the standalone server and receive the following error :
Caused by: org.springframework.beans.factory.enter code hereBeanCreationException: Could not autowire field: private java.lang.String com.titlefeed.config.db.DbConfigJPA.databaseUrl; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder spring.data.postgres.uri' in string value "${spring.data.postgres.uri}"
Which implies the Tomcat Server/Spring isnt loading the application-dev.yml since that contains the properties: spring.data.postgres.uri
So I attempted the following two solutions
-Dspring.profiles.active=dev
to JAVA_OPTS in tomcat/bin/catalina.shspring.profiles.active=dev
to tomcat/conf/catalina.propertiesAnd neither of them worked. How can I get the standalone tomcat server to load the yml file associated with the spring.profiles.active property.
It works fine for the embedded springboot server started from eclipse but doesnt for an standalong server ?
EDIT1: M. Deinum - Implemented your suggested solution below however still got the following error:
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'spring.data.postgres.uri' in string value "${spring.data.postgres.uri}
It seems like the -Dspring.profiles.active=dev isn't getting set.
@Configuration
public class WebAppInit extends SpringBootServletInitializer {
@Override
protected WebApplicationContext createRootApplicationContext(
ServletContext servletContext) {
log.info("Properly INITALIZE spring CONTEXT");
ApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
return super.createRootApplicationContext(servletContext);
}
}
EDIT 2 ACV: - Adding "--spring.profiles.active=dev" as apart of JAVA_OPTS variable in the startup script: tomcat/bin/catalina.sh is not a viable option
E.g:
JAVA_OPTS="$JAVA_OPTS --spring.profiles.active=dev ...etc
Gives the following error:
Unrecognized option: --spring.profiles.active=dev Error: Could not create the Java Virtual Machine."
EDIT 3: Amended application.yml to include the following property
spring:
profiles:
active: dev
Redeployed the war. Went to the exploded tomcat directory location to ensure the property was present webapps/feedserver/WEB-INF/classes/config/application.yml
And the issue still occurred.
EDIT 4: Added application.properties under the tomcat exploded webdir: webapps/feedserver/WEB-INF/classes/application.properties:
spring.profiles.active=dev
spring.data.postgres.uri=jdbc:postgresql://localhost:5432/feedserver
restarted tomcat and the issue still occurred.
Its seems like its not picking up either application.properties or application.yml
EDIT 5 Used the recommended way to start the spring boot server for an external container:
@Configuration
public class WebAppInit extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
Edit 6:
I added -Dspring.profiles.active=dev to the start command args:
/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/bin/java -Djava.util.logging.config.file=/Users/shivamsinha/Desktop/Programming/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Dlog4j.rootLevel=ERROR -Dlog4j.rootAppender=console -DENV=dev -Dlog4j.configuration=/WEB-INF/classes/properties/log4j.properties -DTOMCAT_DIR=WEB-INF/classes/ -Djava.endorsed.dirs=/Users/shivamsinha/Desktop/Programming/tomcat/endorsed -classpath /Users/shivamsinha/Desktop/Programming/tomcat/bin/bootstrap.jar:/Users/shivamsinha/Desktop/Programming/tomcat/bin/tomcat-juli.jar -Dcatalina.base=/Users/shivamsinha/Desktop/Programming/tomcat -Dcatalina.home=/Users/shivamsinha/Desktop/Programming/tomcat -Djava.io.tmpdir=/Users/shivamsinha/Desktop/Programming/tomcat/temp org.apache.catalina.startup.Bootstrap -Dspring.profiles.active=dev start
However I stil get the following exception in the logs:
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private java.lang.String com.titlefeed.config.db.DbConfigJPA.databaseUrl; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'spring.data.postgres.uri' in string value "${spring.data.postgres.uri}"
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:561)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
... 68 more
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'spring.data.postgres.uri' in string value "${spring.data.postgres.uri}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:174)
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126)
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:204)
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:178)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer$2.resolveStringValue(PropertySourcesPlaceholderConfigurer.java:175)
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:801)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:955)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:942)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:533)
... 70 more
02-Sep-2015 03:15:40.472 SEVERE [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployWAR Error deploying web application archive /Users/shivamsinha/Desktop/Programming/tomcat/webapps/feedserver-1.0.0.war
java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/feedserver-1.0.0]]
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:728)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:701)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:714)
at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:917)
at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1701)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Credit: @M. Deinum
There are two options for passing spring profile args into Tomcat 8.
1. Set it as environment variable
Tomcat allows you to set environment config in CATALINA_HOME/setenv.sh
or CATALINA_BASE/setenv.sh
that are called during the start process.
setenv.sh:
export SPRING_PROFILES_ACTIVE=dev
You might also want to create a src/main/resources/banner.txt
with this line in it:
active profiles :: ${spring.profiles.active}
It won't work in your IDE (it reads from your jar/war's MANIFEST.MF file, which you won't have if you're compiling normally), but it's REALLY handy in the environments you care about -- everything BUT your local environment!
2. Add it to the start script/command before the before the executing class
I modified CATALINA_HOME/catalina.sh
added a declared variable:
SPRING_PROFILE="dev"
And at all the relevant executions added it to script:
eval "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \
-Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \
-Dcatalina.base="\"$CATALINA_BASE\"" \
-Dcatalina.home="\"$CATALINA_HOME\"" \
-Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \
-Dspring.profiles.active="\"$SPRING_PROFILE\"" \
org.apache.catalina.startup.Bootstrap "$@" start \
>> "$CATALINA_OUT" 2>&1 "&"
Obviously this isn't the recommended approach. But it works! If you do have the exact replicable steps for doing it the recommend approach feel free to post the answer and if it works i'll accept it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With