2

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.

f.khantsis
  • 3,256
  • 5
  • 50
  • 67

3 Answers3

3

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>
Community
  • 1
  • 1
Ralph
  • 118,862
  • 56
  • 287
  • 383
0

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.

Martin Frey
  • 10,025
  • 4
  • 25
  • 30
  • profiles can be switched without changing the war either – f.khantsis Jul 21 '14 at 10:04
  • @doom777. Yes, but why limit yourself to predefined values? I'm just speaking properties here, not beans. Think of the situation where you have a production profile with the datasource defined. For whatever reason the database moves and the connectionstring changes. You will have to update your profile and recreate the war. – Martin Frey Jul 21 '14 at 12:39
0

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".
f.khantsis
  • 3,256
  • 5
  • 50
  • 67
Ran Adler
  • 3,587
  • 30
  • 27