Add properties to property placeholder

30,611

Solution 1

Not sure if this will help, but what I do in a similar situation is have 2 app.properties files with the same name, one in sec/test/resources and the other in src/main/resources. Now during testing the first is loaded because the test classes are first on the classpath, but when I deploy only the main one is there and so it is loaded.

Solution 2

Does it work if you add the same location attribute to the context:property-placeholder defined in testContext.xml that is in the one defined in applicationContex.xml? You would also need to add the attribute local-override="true" to have the properties-ref override those from under META-INF.

Edit:

Given your most recent comment, I think that you will need to forgo using the context namespace and directly use the Spring objects that is uses behind the scenes. Perhaps something like this:

In applicationContext.xml:

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="classpath*:META-INF/spring/*.properties" />
</bean>

In testContext.xml:

<bean id="propertyConfigurerTest" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" parent="propertyConfigurer">
    <property name="properties" ref="simulatedTomcatProperties" />
    <property name="localOverride" value="true" />
</bean>
<util:properties id="simulatedTomcatProperties">
   <prop key="cfmt.applicationBaseUrl">localhost:8080/cfmt</prop>
</util:properties>

I'm guessing you want the local properties to override the properties defined from the classpath resources so I defined localOverride as true.

Solution 3

If, in your main applicationContext.xml, you specify several property lookups as listed below using a PropertiesFactoryBean, any missed properties files are not loaded, and the last successfully loaded properties file is used. In your case, default.properties (e.g. your test properties file) would be loaded, and because the second file:${catalina}... wouldn't be loaded, your @Value fields would be injected with values specified in default.properties.

Answer taken from here:

<bean id="myProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
   <property name="ignoreResourceNotFound" value="true" />
   <property name="locations">
      <list>
        <value>classpath:default.properties</value>
        <value>file:${catalina.home}/webapps/myProperties.properties</value>
      </list>
   </property>
</bean> 

Solution 4

I solved the problem by splitting up the applicationContext.xml in two files: - applicationContext.xml -- contains the "normal" bean, but NO include of applicationContext-properties.xml - applicationContext-properties.xml -- contains the property placeholder config

applicationContext-properties.xml:

 <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
      <property name="locations" value="classpath*:META-INF/spring/*.properties" />
      <property name="ignoreUnresolvablePlaceholders" value="false" />
 </bean>

The web application loads both files on startup:

web.xml:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:META-INF/spring/applicationContext*.xml</param-value>
</context-param>

For my tests I have added a propoperties file: simulatedTomcat.test-properties that contains all the tomcat properties. note: that this file does not match the pattern used by the properties placeholder configurer of applicationContext-properties.xml

Then I have an properties placeholder configurer for my test that load both kind op properties files (*.properties and *.test-properties)

test-context.xml

<import resource="classpath:/META-INF/spring/applicationContext.xml" />
<bean class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer">
   <property name="locations">
      <list>
         <value>classpath*:META-INF/spring/*.properties</value>
         <value>classpath*:META-INF/spring/*.test-properties</value>
      </list>
   </property>
</bean>
Share:
30,611
Ralph
Author by

Ralph

Updated on July 09, 2022

Comments

  • Ralph
    Ralph almost 2 years

    I have an application where a property placeholder is used to read properties, configured in applicationContext.xml:

    ...
    <context:property-placeholder
         location="classpath*:META-INF/spring/*.properties"/> 
    ...
    

    The application runs in an Tomcat and uses the parameter defined in context.xml. The application access this parameter like normal properties (@Value(${cfma.applicationUrl})). This works

    In my test cases I do not have this tomcat properties, so I want to add them "manually" to the application context. But also load the normal applicationContext.xml

    testContext.xml:

    <import resource="classpath:/META-INF/spring/applicationContext.xml" />
    <context:property-placeholder properties-ref="simulatedTomcatProperties"/>
    <util:properties id="simulatedTomcatProperties">
       <prop key="cfmt.applicationBaseUrl">localhost:8080/cfmt</prop>
    </util:properties>
    

    Now I have two context:property-placeholder and this does not work (of course) – So my question is, who can I extend the properties in the “normal” property-placeholder in my test?


    More Explanation of what I need:

    • The productive environment (as well as the development environment) defines some properties via Tomcat parameter. Therefore they are not included in any properties file, but nerveless they can be accessed like normal properties (@Value(${cfma.applicationUrl})). Moreover there must not be any Fallback, if the properties are not defined in the Tomcat, the application must not start!
    • In the test cases (that use the spring context) I must some how insert the property (cfma.applicationUrl) so that it can be injected in the annotated variables. But if I add an second context:property-placeholder they are not merged:

    @See Comments on https://jira.springsource.org/browse/SPR-4881 -- they explain that behaviour.


    When I talk about Tomcat parameter I am talking about somethink like this:

    context.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <Context>
        <WatchedResource>WEB-INF/web.xml</WatchedResource>
        <Parameter name="cfmt.applicationBaseUrl"
              value="http://localhost/demoApp" override="false"/>
    </Context>
    
  • Ralph
    Ralph almost 13 years
    no unfortunately this does not help. I fare as I understood, the problem is that if I have two context:property-placeholder definitons I also have to property place holder, and there are not merged.
  • laz
    laz almost 13 years
    I would expect that the one in testContext.xml would override the one in applicationContext.xml if it is loaded 2nd. What is the incorrect behavior that you are seeing?
  • Ralph
    Ralph almost 13 years
    Is is not incorrect, it is expected (that it does not work with two context:property-placeholder). The behavior is, that the field annotated @Value(${cfma.applicationUrl}) is not populated in the tests. -- I am searching for a way to "extend" the property-placeholder from the applicationContex.xml in my testContext.xml.
  • Ralph
    Ralph almost 13 years
    Because my problem belongs to additional properties my workaround is to have an additional properties file for my tests in test/resources (Maven bases project).
  • Ralph
    Ralph almost 13 years
    So the difference of your suggestion and <context:property-placeholder location="classpath*:META-INF/spring/*.properties"/> is not so much. - At least you suggest to have an additional properties file for the tests - right?
  • Fil
    Fil almost 13 years
    Exactly. And you could also extend the PropertiesFactoryBean to more closely match your needs; I use it to help me load different config files depending on what I'm doing (e.g. using JNDI for lookups, or using a local H2 database, seeding with DBUnit, etc...)
  • Duncan McGregor
    Duncan McGregor almost 13 years
    Yes, I do end up duplicating properties in the two files!