Spring Framework
  1. Spring Framework
  2. SPR-6428

Placeholders not resolved when using multiple PropertyPlaceHolderConfigurers

    Details

    • Type: Bug Bug
    • Status: Resolved
    • Priority: Major Major
    • Resolution: Works as Designed
    • Affects Version/s: 2.5.6
    • Fix Version/s: None
    • Component/s: Core
    • Labels:
      None
    • Last commented by a User:
      false

      Description

      When using 2 PropertyPlaceHolderConfigurers, placeholders declared (and given a value) in the first PPC are not resolved in the second PPC.

      <bean id="configurer1" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
      <property name="properties">
      <bean class="java.util.Properties">
      <constructor-arg>
      <map>
      <entry key="resourceDirPlaceHolder">
      <value>myResourceDir</value>
      </entry>
      </map>
      </constructor-arg>
      </bean>
      </property>
      <property name="order" value="1"/>
      <property name="ignoreUnresolvablePlaceholders" value="true"/>
      </bean>

      <bean id="configurer2" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
      <property name="locations">
      <list>
      <value>classpath:$

      {resourceDirPlaceHolder}/props.properties</value>
      </list>
      </property>
      </bean>

      this configuration results in:

      java.io.FileNotFoundException: class path resource [${resourceDirPlaceHolder}

      /props.properties] cannot be opened because it does not exist

        Issue Links

          Activity

          Hide
          Gisbert van Rossum added a comment -

          Zip file contains a maven project illustrating the issue

          Show
          Gisbert van Rossum added a comment - Zip file contains a maven project illustrating the issue
          Hide
          Gisbert van Rossum added a comment -

          Issue might be the same as SPR-5719

          Show
          Gisbert van Rossum added a comment - Issue might be the same as SPR-5719
          Hide
          Spring R added a comment -

          I searched for this issue and I found people are using ignore-unresolvable property as true so that the first PropertyPlaceholderConfigurer does not throw an exception

          Show
          Spring R added a comment - I searched for this issue and I found people are using ignore-unresolvable property as true so that the first PropertyPlaceholderConfigurer does not throw an exception
          Hide
          Spring R added a comment -

          Trace after the change:

          INFO main 2010-04-14 20:10:14,241 Loading properties file from class path resource [jdbc.properties]

          { org.springframework.core.io.support.PropertiesLoaderSupport.loadProperties(PropertiesLoaderSupport.java:177)}

          DEBUG main 2010-04-14 20:10:14,241 Resolved placeholder 'driverClassName'

          { org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:143)}

          DEBUG main 2010-04-14 20:10:14,241 Resolved placeholder 'url'

          { org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:143)}

          DEBUG main 2010-04-14 20:10:14,241 Resolved placeholder 'reuters.username'

          { org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:143)}

          DEBUG main 2010-04-14 20:10:14,241 Resolved placeholder 'reuters.password'

          { org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:143)}

          INFO main 2010-04-14 20:10:14,241 Loading properties file from class path resource [jdbc.properties]

          { org.springframework.core.io.support.PropertiesLoaderSupport.loadProperties(PropertiesLoaderSupport.java:177)}

          INFO main 2010-04-14 20:10:14,241 Loading properties file from class path resource [msgProperties.xml]

          { org.springframework.core.io.support.PropertiesLoaderSupport.loadProperties(PropertiesLoaderSupport.java:177)}

          DEBUG main 2010-04-14 20:10:14,241 Resolved placeholder 'brokerUrl'

          { org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:143)}

          INFO main 2010-04-14 20:10:14,241 Loading properties file from class path resource [msgProperties.xml]

          { org.springframework.core.io.support.PropertiesLoaderSupport.loadProperties(PropertiesLoaderSupport.java:177)}
          Show
          Spring R added a comment - Trace after the change: INFO main 2010-04-14 20:10:14,241 Loading properties file from class path resource [jdbc.properties] { org.springframework.core.io.support.PropertiesLoaderSupport.loadProperties(PropertiesLoaderSupport.java:177)} DEBUG main 2010-04-14 20:10:14,241 Resolved placeholder 'driverClassName' { org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:143)} DEBUG main 2010-04-14 20:10:14,241 Resolved placeholder 'url' { org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:143)} DEBUG main 2010-04-14 20:10:14,241 Resolved placeholder 'reuters.username' { org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:143)} DEBUG main 2010-04-14 20:10:14,241 Resolved placeholder 'reuters.password' { org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:143)} INFO main 2010-04-14 20:10:14,241 Loading properties file from class path resource [jdbc.properties] { org.springframework.core.io.support.PropertiesLoaderSupport.loadProperties(PropertiesLoaderSupport.java:177)} INFO main 2010-04-14 20:10:14,241 Loading properties file from class path resource [msgProperties.xml] { org.springframework.core.io.support.PropertiesLoaderSupport.loadProperties(PropertiesLoaderSupport.java:177)} DEBUG main 2010-04-14 20:10:14,241 Resolved placeholder 'brokerUrl' { org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:143)} INFO main 2010-04-14 20:10:14,241 Loading properties file from class path resource [msgProperties.xml] { org.springframework.core.io.support.PropertiesLoaderSupport.loadProperties(PropertiesLoaderSupport.java:177)}
          Hide
          Ryan Hoegg added a comment -

          I tried changing the order of my PropertyPlaceHolderConfigurers, and confirmed that it is always the one with the highest "order" value that is not found. A little debugging told me that they are both registered as BeanFactoryPostProcessors, but the exception is thrown while trying to resolve a property from the second one. This causes the BeanDefinitionStoreException, which aborts initialization before the second one gets to try to resolve the property.

          I was able to get this working by adding ignoreUnresolvablePlaceholders="true", but I had to add it to ALL the PropertyPlaceHolderConfigurers in my application. If even one of them does not ignoreUnresolvablePlaceholders, you will get the BeanDefinitionStoreException.

          Show
          Ryan Hoegg added a comment - I tried changing the order of my PropertyPlaceHolderConfigurers, and confirmed that it is always the one with the highest "order" value that is not found. A little debugging told me that they are both registered as BeanFactoryPostProcessors, but the exception is thrown while trying to resolve a property from the second one. This causes the BeanDefinitionStoreException, which aborts initialization before the second one gets to try to resolve the property. I was able to get this working by adding ignoreUnresolvablePlaceholders="true", but I had to add it to ALL the PropertyPlaceHolderConfigurers in my application. If even one of them does not ignoreUnresolvablePlaceholders, you will get the BeanDefinitionStoreException.
          Hide
          Chris Beams added a comment - - edited

          Gisbert, all,

          Indeed the configuration described here does cause the reported exception, however I'm resolving this as "Works as Designed" for reasons explained below. Please note that if you read all the way, you'll find my recommendations as to how to eliminate this problem using Spring 3.1.

          To recap the issue, consider two PropertyPlaceholderConfigurer beans P and Q configured in the same context where "Q" has a $

          {...} placeholder that depends on "P" for replacement. In such a case, the placeholder replacement for "Q" will fail every time.

          The reason for this can be understood by studying the way AbstractApplicationContext collects, sorts and executes BeanFactoryPostProcessor instances. It begins with a call to beanFactory.getBeansOfType(). This call requires that all such beans (of type BFPP) be fully instantiated and returned. This means that all the property values will be evaluated and injected into each new BFPP instance, and immediately so. This is done before any one BFPP has a chance to visit other beans. This means that the "Q" bean instance will end up retaining its original, un-replaced ${...}

          placeholder – there was never a chance for "P" to replace it.

          Even though "P" does eventually visit the BeanDefinition of "Q", this does not matter because the "Q" bean instance has already been created. Changing the BeanDefinition metadata is moot at that point.

          The question then arises, how shall we fix this problem? It is certainly possible, given enough careful effort and testing to refactor BFPP handling such that earlier BFPPs are capable of modifying subsequent BFPPs at the level of bean definition metadata.

          However, given the critical and non-trivial nature of the code in question, it is not likely worth the risk to "fix" this issue. Fix is quoted here, because while the behavior described by this issue is unituitive, it is not a bug per se. It might better be termed an 'undocumented limitation': BFPPs cannot modify other BFPPs bean definition metadata.

          With all that said, the good news is that for Spring 3.1 users there is an alternative approach that might even be considered a superior solution.

          The alternative is to (a) use the new PropertySourcesPlaceholderConfigurer instead of the traditional PropertyPlaceholderConfigurer; (b) eliminate the first PPC; (c) register a PropertySource with the ApplicationContext's Environment that contains the properties for the placeholders that need replacement in the PropertySourcesPlaceholderConfigurer, such as $

          {resourceDirPlaceHolder}

          from your original example.

          To demonstrate this, I've added a new "SPR-6428" directory to the spring-framework-issues repository at Github.

          You're welcome to check this repository out and build the SPR-6428 project with Maven / import it into your IDE to see a working example of the workaround to this issue.

          See https://github.com/SpringSource/spring-framework-issues#readme for instructions.

          Thanks,

          Chris

          Show
          Chris Beams added a comment - - edited Gisbert, all, Indeed the configuration described here does cause the reported exception, however I'm resolving this as "Works as Designed" for reasons explained below. Please note that if you read all the way, you'll find my recommendations as to how to eliminate this problem using Spring 3.1. To recap the issue, consider two PropertyPlaceholderConfigurer beans P and Q configured in the same context where "Q" has a $ {...} placeholder that depends on "P" for replacement. In such a case, the placeholder replacement for "Q" will fail every time. The reason for this can be understood by studying the way AbstractApplicationContext collects, sorts and executes BeanFactoryPostProcessor instances. It begins with a call to beanFactory.getBeansOfType() . This call requires that all such beans (of type BFPP ) be fully instantiated and returned. This means that all the property values will be evaluated and injected into each new BFPP instance, and immediately so. This is done before any one BFPP has a chance to visit other beans. This means that the "Q" bean instance will end up retaining its original, un-replaced ${...} placeholder – there was never a chance for "P" to replace it. Even though "P" does eventually visit the BeanDefinition of "Q", this does not matter because the "Q" bean instance has already been created. Changing the BeanDefinition metadata is moot at that point. The question then arises, how shall we fix this problem? It is certainly possible, given enough careful effort and testing to refactor BFPP handling such that earlier BFPPs are capable of modifying subsequent BFPPs at the level of bean definition metadata. However, given the critical and non-trivial nature of the code in question, it is not likely worth the risk to "fix" this issue. Fix is quoted here, because while the behavior described by this issue is unituitive, it is not a bug per se. It might better be termed an 'undocumented limitation': BFPPs cannot modify other BFPPs bean definition metadata. With all that said, the good news is that for Spring 3.1 users there is an alternative approach that might even be considered a superior solution. The alternative is to (a) use the new PropertySourcesPlaceholderConfigurer instead of the traditional PropertyPlaceholderConfigurer ; (b) eliminate the first PPC; (c) register a PropertySource with the ApplicationContext 's Environment that contains the properties for the placeholders that need replacement in the PropertySourcesPlaceholderConfigurer , such as $ {resourceDirPlaceHolder} from your original example. To demonstrate this, I've added a new " SPR-6428 " directory to the spring-framework-issues repository at Github. You're welcome to check this repository out and build the SPR-6428 project with Maven / import it into your IDE to see a working example of the workaround to this issue. See https://github.com/SpringSource/spring-framework-issues#readme for instructions. Thanks, Chris

            People

            • Assignee:
              Chris Beams
              Reporter:
              Gisbert van Rossum
              Last updater:
              Trevor Marshall
            • Votes:
              2 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:
                Days since last comment:
                2 years, 42 weeks, 5 days ago

                Time Tracking

                Estimated:
                Original Estimate - 0.5d
                0.5d
                Remaining:
                Remaining Estimate - 0.5d
                0.5d
                Logged:
                Time Spent - Not Specified
                Not Specified