Announcement Announcement Module
Collapse
No announcement yet.
Properties files chaining? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Properties files chaining?

    Using a recent version of Spring 3.1

    I'm loading a properties file from the classpath like this at the top of my applicationContext.xml:

    Code:
        <context:property-placeholder  location="classpath:/config.properties" order="1" />
    config.properties contains a couple of properties that I'd like to use to build a path to another properties file on the file system, relative to my application server. So I try to load this second properties file like this:

    Code:
        <context:property-placeholder
            location="file:///${app.server.base}/${env.config.dir}/${env.config.file}" order="2"/>
    I pass in app.server.base as a jvm property. I have verified that it is getting passed in correctly by setting it later in a bean and printing out the value. env.config.dir and env.config.file are both defined in my first properties file, config.property.

    But alas, it is not working. When I try to use a property from the second property file further down in my applicationContext.xml, it can't find it:

    Code:
    Apr 19, 2012 3:33:01 PM org.apache.catalina.core.StandardContext listenerStart
    SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
    org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'dsPostgresUser' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Could not resolve placeholder 'db.driver.class.postgresql'
    	at org.springframework.beans.factory.config.PlaceholderConfigurerSupport.doProcessProperties(PlaceholderConfigurerSupport.java:209)
    	at org.springframework.beans.factory.config.PropertyPlaceholderConfigurer.processProperties(PropertyPlaceholderConfigurer.java:220)
    	at org.springframework.beans.factory.config.PropertyResourceConfigurer.postProcessBeanFactory(PropertyResourceConfigurer.java:84)
    The thing I'm trying to accomplish is to have just a few hardcoded properties in a properties file loaded from the classpath, then load environment specific properties from another location on the file system relative to the application server. Any suggestions on a good way to do this, or suggestions on why my attempt above isn't working?

  • #2
    A little more info about my problem. If I comment out the second property-placeholder element, then the properties in my first file seem to get loaded ok. I can use them later in applicationContext.xml and see the correct values in the log. For example, printing out the following looks good:

    Code:
            <property name="dummyProp" value="${app.server.base}/${env.config.dir}/${env.config.files}"/>
            results in valid path:  /home/jdoe/apps/tomcat/conf/myEnvFile.properties
    But, as soon as I enable the second property-placeholder element, then it looks like it clears the property values from the first. It doesn't substitute for the properties and generates an error on startup:

    Code:
       Caused by: java.io.FileNotFoundException: /home/jdoe/apps/tomcat/${env.config.dir}/${env.config.files} (No such file or directory)
    Is there an easy way to tell it to append rather than reset? That's my best guess at the problem at the moment...

    Comment


    • #3
      After some more googling, I came at this from a different angle. I got rid of my 2 'old style' property-placeholder tags. Instead I defined a Context Initializer Class as recommended for 3.1. Here are the relevant tags at the top of my web.xml:

      Code:
          <context-param>
              <param-name>contextInitializerClasses</param-name>
              <param-value>my.package.ContextPropsLoader</param-value>
          </context-param>    
      
          <context-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>/WEB-INF/applicationContext.xml</param-value>
          </context-param>
      In my.package.ContextPropsLoader I load all the props I want and then do this:

      Code:
          public void initialize(ConfigurableWebApplicationContext ctx )
          {
              ConfigurableEnvironment env = ctx.getEnvironment();
              Properties props = ...
              PropertySource ps = new PropertiesPropertySource( "MyCustomProperties", props );
              env.getPropertySources().addFirst(ps);
          }
      I was optimistic this would work, but my hopes were crushed. It seems the bean definitions using these properties are still initialized before this code executes. Here's an example in my applicationContext.xml that tries to use one of these properties and throws an error:

      Code:
         <bean id="dsPostgresUser" class="my.package.SecureDataSource">
              <property name="driverClassName" value="${db.driver.class.postgresql}" />
      Code:
      SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
      org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dsPostgresUser' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Error setting property values; nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
      PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'driverClassName' threw exception; nested exception is java.lang.IllegalStateException: Could not load JDBC driver class [${db.driver.class.postgresql}]
      	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1396)
      	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1118)
      The code is executing but it's too late. I ran it through my debugger--it throws the exceptions first then executes the ApplicationContextInitializer code. So my questions are:

      1) Is there a way to get my ApplicationContextInitializer code to execute earlier before any beans are initialized so those properties will be available for substitution?

      or

      2) Is there a way to get my beans to initialize later after the ApplicationContextInitializer executes?

      3) We're using tomcat-7.0.25, maybe it initializes the context in the wrong order?

      Thanks for reading
      Last edited by medloh; Apr 20th, 2012, 01:18 PM.

      Comment

      Working...
      X