Load different properties for development and deployment - spring

I have a very common use case -- connect to different databases when my program is in development mode, in test mode, or in deployment mode.
The way I am doing it now, is I configure a data source, and pass it ${...} properties via bean:property tag.
However to get the ${...}, i am doing
<context:property-placeholder properties-ref="myProperties" />
and in the bottom of the xml config, I have
<beans profile=test>
<util:properties id=myProperties>
</util>
</beans>
<beans profile=dev,default>
<util:properties id=myProperties>
</beans>
<beans profile=prod>
<util:properties id="myProperties>
</beans>
This seems inefficient, overly verbose, and prone to error. All spring properties tutorials tell me that context:property-placeholder is Environment aware, and Environment is responsible for profiles so how do I simplify this? It is intuitive to me that there is a simpler way, I just can't figure it out.
Really, what I am looking for is to specify profile on context:properties-placeholder, or something like that.

I solved that problem once (a long time before Spring supports profiles): spring property substitution for test and production
nowadays a would still use property files but, but I would select them by profiles. There are a lot of ways to do this:
The simplest one is:
<context:property-placeholder
location="classpath*:META-INF/spring/config-${spring.profiles.active}.properties" />
an other is:
<beans profile="normal">
<context:property-placeholder
location="classpath*:META-INF/spring/config-normal.properties"/>
</beans>
<beans profile="test">
<context:property-placeholder
location="classpath*:META-INF/spring/config-test.properties"/>
</beans>
The first approach has the drawback, that when more than one profile is activated then only the properties for the first profile gets loaded. I am not sure what will happen with the second approach when having more than one profiles.
For the first approach I found this solution, but I have not tested it:
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath*:META-INF/spring/*_${spring.profiles.active}.properties</value>
</list>
</property>
</bean>

While profiles are certainly a solution to that problem I think that this approach opens another big door to issues that you discover only on the target platform.
In my projects I have always externalized the properties and turned as many properties as possible into runtime parameters.
Just imagine having to bundle Jenkins/Sonar/etc again as your platform will not be part of a profile with properties residing in the classpath. I don't think that then these would be successful projects ;)
As for spring you can use the 'file://' protocol in a propertyconfigurer allowing to superseed a "dedault" property coming from the classpath. So you have two configurer tags with an order parameter and other properties. Here's an example:
<jee:jndi-lookup id="configDirectory" jndi-name="configDirectory"
resource-ref="true" default-value="." />
<jee:jndi-lookup id="datasource" jndi-name="jdbc/datasource"
expected-type="javax.sql.DataSource" default-ref="localDatasource" />
<!-- Allows fetching properties from multiple locations: -->
<!-- external definition -> file://${configDirectory}/root-context.properties
-> declared in context.xml -->
<!-- standard web application bundle -> /WEB-INF/spring/root-context.properties -->
<!-- testing -> classpath:root-context.properties -->
<context:property-placeholder location="${configDirectory:.}/context.properties"
order="9" ignore-resource-not-found="true" ignore-unresolvable="true" />
<context:property-placeholder
location="/WEB-INF/spring/context.properties,
classpath:context.properties"
order="10" ignore-resource-not-found="true" ignore-unresolvable="true" />
<context:property-placeholder location="classpath:spring/default.properties"
order="100" />
Like this we are able to build it locally, run our unit and integration tests during maven build, run the build on UAT and if all that is ok copy the build from UAT to PROD without having to modify the war file.
In the properties we define all the parametersthat cannot be changed at runtime, which is essentially the Hibernate parameters plus some others.
All the rest is stored in the database as simple system parameters (key-value pairs). There are a lot of properties that do not need to be fixed. This includes: LDAP, MailSender, folder definitons like tempdir and others.
As the datasource is one of the very first beans to be initiated this works pretty nice in the projects I am running currently, and we are still discovering more properties to be pushed into the database.

Please read:
https://examples.javacodegeeks.com/enterprise-java/spring/load-environment-configurations-and-properties-with-spring-example/
<context:property-placeholder location="
classpath:application.properties,
classpath:application${spring.profiles.active}.properties"
ignore-unresolvable="true"/>
mvn clean install -Dspring.profiles.active="profile_name".

Related

how can I configure RedisHttpSessionConfigure to fallback if redis is not online

I'm using spring-session + redis as documented here:
http://docs.spring.io/spring-session/docs/current/reference/html5/guides/httpsession-xml.html
How can I configure RedisHttpSessionConfigure such that for local development, redis is not needed and the application will simply default to the container session handling?
Generally this isn't recommended because you are differing your development environment from your production environment. It should be quite trivial to point your dev machine to a Redis instance.
If you need to support it, you can use Spring profiles. For example, with XML you can use something like:
<beans profile="dev">
<bean id="springSessionRepositoryFilter" class="org.springframework.web.filter.CharacterEncodingFilter"/>
</beans>
<beans profile="production">
<context:annotation-config/>
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>
<bean class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"/>
</beans>
The key is to ensure that your dev environment also has a Bean that implements Filter named springSessionRepositoryFilter. In this example, I used CharacterEncodingFilter which should do nothing since the encoding property is not set but feel free to replace with whatever you like.
The next thing you will need to do is activate your environments. For example, you can use
-Dspring.profiles.active="production"

Spring more than one profile and ${spring.profiles.active}

In spring 4.1.2.RELEASE, we have 2 active profiles in web.xml
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>Production,Customer1</param-value>
</context-param>
And we want to dynamically load some property files as below:
<util:properties id="accountPolicy"
location="classpath:/configs/${spring.profiles.active}/sample.properties" />
The ${spring.profiles.active} is not working, may be because there are two profiles, I tried some lookups like: ${spring.profiles.active[1]} but no luck !
Any comments
Updated:
It seems that ${spring.profiles.active} is an comma seperated list I try below:
<util:properties id="signConditions"
location="classpath:/configs/#{ {'${spring.profiles.active}'.split(',')}.get(1) }/sample.properties" />
But the error seems that there will be an XML parsser error:
org.springframework.expression.ParseException:
Expression 'classpath:/configs/#{ {'Production,Customer1'.split('' # 19: No ending suffix '}' for expression starting at character 19: #{ {'Production,Customer1'.split('
This did the job:
<util:properties id="signConditions"
location="classpath:/configs/#{environment.getActiveProfiles()[1]}/sample.properties" />
I believe the more proper way is to do something like:
<beans profile="Production">
<!-- some other stuff for Production profile -->
</beans>
<beans profile="Customer1">
<util:properties id="accountPolicy"
location="classpath:/configs/Customer1/sample.properties" />
<!-- some other stuff for Customer1 profile -->
</bean>
Profiles are supposed to be used as Profiles of configurations in app context, instead of like a property for replacement (as what you are doing)
Edit base on my comment:
What you are looking for is not a proper use case of Spring profile feature (at least not now). What you are trying to do is having property place holder work base on a system property. However, activation of profiles can be done through other way. Which means, you can turn on a profile without that spring.profiles.active system property. What you are doing is not reliable.
If it is fine for you to pass in system properties, why not do something like:
Have a profile called Customer, which denote for deployment to customers which will involve account policy (and other stuff)
Pass in a system property, for example, with key = 'customerCode' and value being an identifier for a customer.
By doing so, what you need to do is
<beans profile="Production">
<!-- some other stuff for Production profile -->
</beans>
<beans profile="Customer">
<util:properties id="accountPolicy"
location="classpath:/configs/${customerCode}/sample.properties" />
<!-- some other stuff for Customer1 profile -->
</bean>
and system properties you need for your application should looks like: -Dspring.profiles.active="Production,Customer" -DcustomerCode=Customer1
Then you have proper use of profiles, and no need to duplicate accountPolicy for each customer.

Good example of Spring Configuration using java.util.prefs or Commons Configuration

One application I'm working on has several URLs and other information that is instance specific. The first pass uses a typical Spring PropertyPlaceholderConfigurer with a properties file:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:application.properties"/>
</bean>
The main issue with this is of course the property file is an artifact that must be checked in, and for starting a new instance would require updating that artifact. For a streamline deployment, I would like to have the ApplicationContext bootstrap itself based on database table(s). I have seen solutions like this forum post, does anyone here know of better tools or is this defacto approach to this problem? I would also like to be able to update/reload the settings at runtime using JMX or other facilities, but having to restart the app after changes to the database would still be a better solution to the current one.
The way we did it was to put some configuration information in the environment and then pull the relevant info from there.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="searchSystemEnvironment" value="true" />
</bean>
If configuration changes then the app will need to be restarted. Can also put all the different configurations into the environment and nest the variables like the following:
<bean id="db" class="org.DataSource"
p:databaseServer="${${MODE}_DBSERVER}"
p:databaseName="${${MODE}_DBNAME}" />
where $MODE = dev, qa, etc.

external config based on context path

I would like to deploy multiple independent copies of a particular web-app on the same tomcat server under different context paths. Each web-app will need different configuration settings (database name, password, etc), but I would like to keep the wars exactly identical.
My plan was to have the app figure out its context path on startup, then read a specific .properties file outside of tomcat identified by the context path. For example, if a war was deployed to {tomcat path}/webapps/pineapple, then I would want to read /config/pineapple.properties
I've been trying to find a way to inject an instance of ServletContext via spring (3), but all the advice I've seen so far use the deprecated ServletContextFactoryBean.
Is there a better way to get the context path injected or better way to load external files based on the context path?
With the help of ServletContextAttributeFactoryBean and Spring EL, you can reference ServletContext init parameters (<context-param> in web.xml) like that:
#{contextAttributes.myKey}
This allows you to use PropertyPlaceHolderConfigurer and load property files from arbitrary, user-defined locations:
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="#{contextParameters.APP_HOME}/conf/app.properties"/>
</bean>
The corresponding definition of the ServletContext init parameter in Tomcat's context.xml:
<Parameter name="APP_HOME" value="file:/test" override="false"/>
Or in your app's web.xml:
<context-param>
<param-name>APP_HOME</param-name>
<param-value>file:/test</param-value>
</context-param>
This should be the solution.
<bean name="envConfig" class="EnvironmentConfiguration">
<property name="locations">
<list>
<value>file:///#{servletContext.contextPath}.properties</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>
Extend Propertyplaceholderconfigurer to use DB to pick up the values. Example here
Load the actual values of the settings (database name, password etc) to the db as part of seed data
When your web-app's app ctx is being initialized, the properties are resolved from the DB
This is the approach we have been following and works great. If you can switch to Spring 3.1 then it has support for Environment Profiles which may be useful for you.

global properties in spring

is it possible to define in a spring context file, and one or more properties that
can be accessed in <bean> elements.
The example below illustrates best what I need - I want to define the property FOO once and then reference it multiple times in my various <bean> definitions:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<properties>
<property name="FOO" value="BAR">
</properties>
<bean name="TEST" class="mytest">
<property name="MYFOO" value="${FOO}"/>
</bean>
<bean name="TEST1" class="mytest1">
<property name="MYFOO" value="${FOO}"/>
</bean>
</beans>
Any input would be much appreciated.
Thanks, Kevin.
You can do this using the snappily-named PropertyPlaceHolderConfigurer. See here for the example in the spring docs. You don't define the property values themselves in the spring beans file, you externalise them in a .properties file.
You could, I suspect, use PropertyPlaceHolderConfigurer or one of its siblings to inject a Properties object defined inside your Spring file, but that would be a rather more verbose solution.
If you only need to define them in your xml file and never change them (like you would change a configuration option, say a port or an ip), you can create a bean of class String with the given property and then write . Creating a bean of class String isn't the most straight forward thing, but it's doable.
If you want something more complicated (say you want a configuration file residing outside the jar where your xml with the bean definition is), you can look at the PropertyPlaceholderConfigurer class.
Just found a way to do this all in the spring XML file with no external properties file needed (though you can supplement with one if desired)
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties">
<util:properties>
<prop key="foo1">bar1</prop>
<prop key="foo2">bar2</prop>
</util:properties>
</property>
</bean>
then you reference them just like normal as ${foo1} where needed

Resources