Santosh
Santosh

Reputation: 17893

Spring: Loading PropertyPlaceholderConfigurer

Here is the situation:

The xml configuration looks as follows:

<bean id="ppConfig1"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>file:c:\test\env.properties</value>      
            </list>
        </property>
        <property name="ignoreResourceNotFound" value="true" />
    </bean>
    <bean id="string" class="java.lang.String" depends-on="ppConfig1">
        <constructor-arg value="${env.app.type}"/>
    </bean>

     <bean id="ppConfig2"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" depends-on="string">
        <property name="locations">
            <list>
                <value>file:c:\test\#{string}.properties</value>                
            </list>
        </property>
        <property name="ignoreResourceNotFound" value="false" />
    </bean>

As is evident, I want load a specific property file (e.g. app1.properties) based on the value of key env.app.type in C:\test\env.properties.

The code to load/test the above config looks as follows:

ApplicationContext context = new ClassPathXmlApplicationContext(
                "SpringBeans.xml"); 
        String ss = (String)context.getBean("string");      
        System.out.println(ss);

This seems not working. It failing with following error:

Exception in thread "main" org.springframework.beans.factory.BeanInitializationException: Could not load properties; nested exception is java.io.FileNotFoundException: C:\test\${env.app.type}.properties (The system cannot find the file specified) at org.springframework.beans.factory.config.PropertyResourceConfigurer.postProcessBeanFactory(PropertyResourceConfigurer.java:87)

The file c:\test\env.properties looks as follows

env.app.type=app1

One interesting observation is that when I comment out the ppConfig2, everything works fine and correct value of env.app.type is printed. I am open to any other suggestion to get around this. My basic requirement is to select the property file at run time based on the property specified in env.properties. (I am using spring 3.1.0)

Upvotes: 1

Views: 2243

Answers (3)

pingw33n
pingw33n

Reputation: 12510

You can do this with util:properties:

<util:properties id="envProperties" location="env.properties"/>
<util:properties id="properties" location="#{envProperties.getProperty('env.app.type')}.properties"/>

<context:property-placeholder properties-ref="envProperties" ignore-unresolvable="true" ignore-resource-not-found="true"/>
<context:property-placeholder properties-ref="properties" ignore-unresolvable="true"/>

BTW there's a bug in Spring that prevents Spring EL from being evaluated inside location attribute in context:property-placeholder.

Upvotes: 0

Serge Ballesta
Serge Ballesta

Reputation: 148870

I think you are trying to do things that Spring does not like. The normal order for bean initialization is :

  • fully construct bean post processors
  • construct other beans
  • init other beans

As long as those 3 passes are cleanly separated, all will be ok, but you have a post processor bean (ppConfig2) depending on a simple bean (string).

IMHO, you should avoid such construct if possible. Even if it works now, you are under the risk of problem arising at any modification of you application context because you are allready on the edge.

Could it be acceptable to use environment variables or system properties to avoid that a bean post-processor depends on another bean post-processor ? If yes, you would'nt be bothered by ppConfig1 and simply have :

 <bean id="ppConfig2"
    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" depends-on="string">
    <property name="locations">
        <list>
            <value>file:c:\test\${configured_env}.properties</value>                
        </list>
    </property>
    <property name="ignoreResourceNotFound" value="false" />
</bean>

If environment variables or system properties are not an option, you could use programmatic configuration as suggested by Andrei Stefan.

Upvotes: 0

Andrei Stefan
Andrei Stefan

Reputation: 52368

You need something like this:

    <context:component-scan base-package="com.foo.bar" />

    <context:property-placeholder location="file:c:/test/${env.app.type}.properties" />

    <bean id="service" class="com.foo.bar.ExampleService">
        <property name="foo" value="${foo}" />
    </bean>


package com.foo.bar;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@PropertySource("file:c:/test/env.properties")
public class SpringConfig{

}

And "foo" value comes from app1.properties file. Basically, one file is being loaded with @PropertySource, the other with the regular property placeholder.

Upvotes: 1

Related Questions