Announcement Announcement Module
Collapse
No announcement yet.
PropertyPlaceholderConfigurer problems Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • PropertyPlaceholderConfigurer problems

    Hi all!

    I have a problem with PropertyPlaceholderConfigurer usage.
    I need to load 2 properties set....the first from properties file and the second from db.
    The problem is: to load the properties from db I would use a properties set from property file but I'm receiving an exception.

    My config files:

    - web.xml
    Code:
    <!-- SPRING APPLICATION CONTEXT PARAM -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
        	/WEB-INF/spring-properties-from-file-context.xml,
        	/WEB-INF/spring-properties-from-db-context.xml,
        	/WEB-INF/spring-all-properties-context.xml,
        	/WEB-INF/spring-service-context.xml
        </param-value>
    </context-param>
    - spring-properties-from-file-context.xml
    Code:
    <!-- PROPERTIES BEAN FROM FILE -->
    <bean id="rfxPropertiesFromFile" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
        <property name="locations">
    	<value>file:${RFX_HOME}/RFX.properties</value>
        </property>
    </bean>
    - /WEB-INF/spring-properties-from-db-context.xml
    Code:
    <!-- PROPERTIES BEAN FROM DB -->
    <bean id="rfxPropertiesFromDB" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
        <property name="properties" ref="rfxCommonsConfigurationFactoryBean"/>
    </bean>
      	
    <bean name="rfxCommonsConfigurationFactoryBean" class="org.springmodules.commons.configuration.CommonsConfigurationFactoryBean">
           <constructor-arg ref="rfxDatabaseConfiguration"/>
    </bean>
    	
    <bean name="rfxDatabaseConfiguration" class="org.apache.commons.configuration.DatabaseConfiguration">
            <constructor-arg>
       	<bean id="ds1" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
         	    <property name="driverClassName" >
         	        <value>${database.driver}</value>
    	    </property>
         	    <property name="url">
         	        <value>${database.url}</value>
         	    </property>
         	    <property name="username">
         	        <value>${database.username}</value>
         	    </property>
         	    <property name="password">
         	        <value>${database.password}</value>
         	    </property>     			
                 </bean>
             </constructor-arg>
             <constructor-arg index="1" value="UTENTERFX.RFX_PROPERTIES_TABLE"/>
             <constructor-arg index="2" value="PROPERTY_KEY"/>
             <constructor-arg index="3" value="PROPERTY_VALUE"/>
    </bean>
    - spring-all-properties-context.xml
    Code:
    <!-- PROPERTIES BEAN FROM ALL -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="propertiesArray">
            <list>
    	<ref bean="rfxPropertiesFromFile"/>
    	<ref bean="rfxPropertiesFromDB"/>
            </list>
        </property>
    </bean>

    In the rfxDatabaseConfiguration bean, If I use the correct value for driver, url, etc. it will work fine...but if I use the variables I will receive this exception:

    Code:
    2010-05-20 10:30:05,609 WARN  [org.apache.commons.configuration.DatabaseConfiguration] Internal error
    org.apache.commons.dbcp.SQLNestedException: Cannot load JDBC driver class '${database.driver}'
    	at org.apache.commons.dbcp.BasicDataSource.createConnectionFactory(BasicDataSource.java:1429)
    	at org.apache.commons.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1371)
    	at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)
    	at org.apache.commons.configuration.DatabaseConfiguration.getConnection(DatabaseConfiguration.java:568)
    	at org.apache.commons.configuration.DatabaseConfiguration.getKeys(DatabaseConfiguration.java:515)
    	at org.apache.commons.configuration.CompositeConfiguration.getKeys(CompositeConfiguration.java:214)
    	at org.apache.commons.configuration.ConfigurationConverter.getProperties(ConfigurationConverter.java:112)
    	at org.springmodules.commons.configuration.CommonsConfigurationFactoryBean.getObject(CommonsConfigurationFactoryBean.java:57)
    	at org.springframework.beans.factory.support.FactoryBeanRegistrySupport$1.run(FactoryBeanRegistrySupport.java:121)
    	at java.security.AccessController.doPrivileged(Native Method)
    	at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:116)
    	at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:91)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1285)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:275)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
    	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:269)
    	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:104)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1244)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1008)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:470)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)
    	at java.security.AccessController.doPrivileged(Native Method)
    
    
    ................. MORE ..........................
    Thanks in advance for any suggestions.

    Cheers, Valentina

  • #2
    I'm not sure if this would work. The PropertyPlaceHolderConfigurer is required to replace the property place holders, but these can't be replaced until you have loaded the properties from the database, which you can't do until you have your PropertyPlaceHolderConfigurer. Its a classic Catch-22.

    This may be possible if you were to set these variables as environment variables. That way these should be set before the PropertyPlaceHolderConfigurer is initialised.

    http://forum.springsource.org/showpo...55&postcount=5

    If you don't want to have to -D... these no the environment variables, you can create a wrapper class for this, such as:

    Code:
    <bean id="systemPropertiesLoader" class="SystemPropertiesLoader" >
        	<constructor-arg ref="rfxPropertiesFromFile"/>
    </bean>
    with this loading the relevant properties and setting them as environment variables, such as:

    Code:
    public SystemPropertiesLoader(final AbstractConfiguration configuration) {
            System.setProperty("database.url", configuration.getString("database.url"));
            ...
            ...
        }
    Then, if you place a dependency between rfxPropertiesFromDB and systemPropertiesLoader, you might have some success.

    Comment


    • #3
      Thanks a lot for your help!

      I try to place a dependency between rfxPropertiesFromDB and systemPropertiesLoader.

      I've implemented a SystemPropertiesLoader

      Code:
      public class SystemPropertiesLoader implements Serializable{
      
      	private static final long serialVersionUID = 1L;
      	final Properties configuration;
      	
      	public SystemPropertiesLoader(final Properties configuration){
      		
      		this.configuration = configuration;
      		System.setProperty("${database.driver}", configuration.getProperty("database.driver"));
      		System.setProperty("${database.url}", configuration.getProperty("database.url"));
      		System.setProperty("${database.user}", configuration.getProperty("database.user"));
      		System.setProperty("${database.password}", configuration.getProperty("database.password"));
      		
      	}
      
      }
      I've created a bean like this:

      Code:
      public class RfxPropertiesFactoryBean extends PropertiesFactoryBean{
      
      	SystemPropertiesLoader systemPropertyLoader;
      	
      	public RfxPropertiesFactoryBean(SystemPropertiesLoader systemPropertyLoader){
      		super();
      		this.systemPropertyLoader = systemPropertyLoader;
      	}
      }
      I've configured this bean:

      Code:
      <bean name="systemPropertiesLoader" class="it.niuma.rfx.systemPropertiesLoader.SystemPropertiesLoader" >
          <constructor-arg ref="rfxPropertiesFromFile"/>
      </bean>
      I've replaced the rfxPropertiesFromDB definition with:
      Code:
      <bean id="rfxPropertiesFromDB" class="it.niuma.rfx.rfxPropertiesFactoryBean.RfxPropertiesFactoryBean">
          <constructor-arg ref="systemPropertiesLoader"/>
          <property name="properties" ref="rfxCommonsConfigurationFactoryBean"/>    	
      </bean>
      but I'm receiving the same exception:

      Code:
      2010-05-20 16:35:10,890 WARN  [org.apache.commons.configuration.DatabaseConfiguration] Internal error
      org.apache.commons.dbcp.SQLNestedException: Cannot load JDBC driver class '${database.driver}'
      	at org.apache.commons.dbcp.BasicDataSource.createConnectionFactory(BasicDataSource.java:1429)
      	at org.apache.commons.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1371)
      	at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)
      	at org.apache.commons.configuration.DatabaseConfiguration.getConnection(DatabaseConfiguration.java:568)
      	at org.apache.commons.configuration.DatabaseConfiguration.getKeys(DatabaseConfiguration.java:515)
      	at org.apache.commons.configuration.CompositeConfiguration.getKeys(CompositeConfiguration.java:214)
      	at org.apache.commons.configuration.ConfigurationConverter.getProperties(ConfigurationConverter.java:112)
      	at org.springmodules.commons.configuration.CommonsConfigurationFactoryBean.getObject(CommonsConfigurationFactoryBean.java:57)
      	at org.springframework.beans.factory.support.FactoryBeanRegistrySupport$1.run(FactoryBeanRegistrySupport.java:121)
      	at java.security.AccessController.doPrivileged(Native Method)
      
      ................. MORE .................

      Comment


      • #4
        System.setProperty("${database.driver}", configuration.getProperty("database.driver"));
        System.setProperty("${database.url}", configuration.getProperty("database.url"));
        System.setProperty("${database.user}", configuration.getProperty("database.user"));
        System.setProperty("${database.password}", configuration.getProperty("database.password"));
        This should just have the strings "database.url" etc.

        Comment


        • #5
          Sorry...but...I don't understand you!

          Comment


          • #6
            I think what richard is saying is , instead of using

            Code:
            System.setProperty("${database.driver}", configuration.getProperty("database.driver"));
            use
            Code:
              System.setProperty("database.driver", configuration.getProperty("database.driver"));

            Comment


            • #7
              Ok!
              I've tried.....the problem is the same!
              I'm receiving the same exception!

              Comment


              • #8
                Maheshguruswamy is right, that is what I was after. I've used something similar before and thought that would work.

                Comment


                • #9
                  What version of Spring are you using?

                  Comment


                  • #10
                    I was going to wait to see what version of Spring you were using prior to responding because the solution I have only works for Spring 2.5+. I decided to reply anyways because I thought it may be useful to others too. As a heads up, the explanation of what is happening is a bit confusing, but the solution is pretty straightforward.

                    What is happening

                    As you suspected this is happening because the PropertyPlaceholderConfigurer will not post process the datasource until it has obtained all of the properties objects it needs to do the replacement. This means the datasource does not have its values replaced on it yet. A solution is to use two property PropertyPlaceholderConfigurer instances. This way the property file instance can obtain all the properties and do replacement on the datasource prior to the database PropertyPlaceholderConfigurer trying to do replacement.

                    The tricky part is that even if you order the beans it is still going to try to resolve all the properties of all the PropertyPlaceholderConfigurer instances prior to doing replacement (this is so that Spring can order the post processors). This means any properties (or properties of properties...like your datasource) will not have the values replaced and it will still break. A way around this (for Spring 2.5+) is to create an instance of an Ordered, BeanFactoryPostProcessor that does the same thing as the PropertyPlaceholderConfigurer with the database properties. This is because all PriorityOrdered BeanFactoryPostProcessor's (PropertyPlaceholderConfigurer) will get completely processed prior to Ordered BeanFactoryPostProcessor's.

                    As I mentioned, the explanation is a bit confusing, but the solution is fairly straight forward. Below is an outline of what I would do.

                    Solution

                    Create a PropertyPlaceholderConfigurer for the properties file replacement. You must ignoreUnresolvablePlaceholders since you are only replacing a subset of the properties (you will perform the rest of the replacement and validation later).

                    Code:
                    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
                      <property name="ignoreUnresolvablePlaceholders" value="true"/>
                      <property name="location" value="file:${RFX_HOME}/RFX.properties"/>
                    </bean>
                    Create a bean that implements Ordered, BeanFactoryPostProcessor that does the same thing as the PropertyPlaceholderConfigurer with the database properties. Ordered gets initialized after the PriorityOrdered implementations (i.e. PropertyPlaceholderConfigurer). This ensures that the properties that this class depends on have already been replaced.

                    Code:
                    public class OrderedPropertyPlaceholderConfigurer
                        implements Ordered, BeanFactoryPostProcessor {
                    
                      private Properties properties;
                    
                      private int order = 0;
                    
                      public OrderedPropertyPlaceholderConfigurer(
                          Properties properties) {
                        this.properties = properties;
                      }
                    
                      public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
                          throws BeansException {
                        PropertyPlaceholderConfigurer bfPostProcessor = new PropertyPlaceholderConfigurer();
                        bfPostProcessor.setProperties(properties);
                        bfPostProcessor.postProcessBeanFactory(beanFactory);
                      }
                    
                      public int getOrder() {
                        return order;
                      }
                    
                      public void setOrder(int order) {
                        this.order = order;
                      }
                    }
                    Create the db properties configuration:

                    Code:
                    <bean class="OrderedPropertyPlaceholderConfigurer">
                        <constructor-arg ref="rfxPropertiesFromDB" />
                    </bean>
                    
                    <bean id="rfxPropertiesFromDB"
                        class="org.springframework.beans.factory.config.PropertiesFactoryBean">
                        <property name="properties" ref="rfxCommonsConfigurationFactoryBean" />
                    </bean>
                    
                    <bean name="rfxCommonsConfigurationFactoryBean"
                        class="org.springmodules.commons.configuration.CommonsConfigurationFactoryBean">
                        <constructor-arg ref="rfxDatabaseConfiguration" />
                    </bean>
                    
                    <bean name="rfxDatabaseConfiguration"
                        class="org.apache.commons.configuration.DatabaseConfiguration">
                        <constructor-arg>
                            <bean id="ds1" class="org.apache.commons.dbcp.BasicDataSource"
                                destroy-method="close">
                                <property name="driverClassName">
                                    <value>${database.driver}</value>
                                </property>
                                <property name="url">
                                    <value>${database.url}</value>
                                </property>
                                <property name="username">
                                    <value>${database.username}</value>
                                </property>
                                <property name="password">
                                    <value>${database.password}</value>
                                </property>
                            </bean>
                        </constructor-arg>
                        <constructor-arg index="1" value="properties" />
                        <constructor-arg index="2" value="PROPERTY_KEY" />
                        <constructor-arg index="3" value="PROPERTY_VALUE" />
                    </bean>
                    HTH,
                    Rob Winch

                    Comment


                    • #11
                      Hi Rob!

                      Thank you very much for your precius suggestions!
                      I've implemented the OrderedPropertyPlaceholderConfigurer and it works!
                      Your solution is wonderful!

                      Thanks for the explanation!

                      Cheers, Valentina

                      PS: I'm using Spring 2.5.x

                      Comment


                      • #12
                        Sorry Rob....

                        When db properties will change at run time....can I reload this properties?

                        Cheers, Vale

                        Comment


                        • #13
                          Sorry didn't see your question until just now. I think your best is to invoke ConfigurableApplicationContext.refresh() after your database properties change.

                          HTH,
                          Rob Winch

                          Comment

                          Working...
                          X