Announcement Announcement Module
Collapse
No announcement yet.
Java Config for Autowiring a DataSource with PropertyPlaceholderConfigurer Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Java Config for Autowiring a DataSource with PropertyPlaceholderConfigurer

    I want to use Java Config to declare my PropertyPlaceholderConfigurer bean. The bean loads its properties from a database table using CommonsConfigurationFactoryBean with a DatabaseConfiguration bean. A different DataSource bean profile is loaded depending upon the environment in which the application is deployed. The problem I am facing appears to be in the order in which my beans are loaded. The PropertyPlaceholderConfigurer bean seems to be loaded before my DataSource bean, which causes problems because I need a reference to the DataSource bean in order to load my properties. In the following code example, dataSource is null when PropertyPlaceholderConfigurer initializes. I am using Spring 3.1.1.RELEASE

    Code:
    @Configuration
    public class DatabaseConfig {
        @Autowired
        private DataSource dataSource;
    
        @Bean
        public PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() throws Exception {
            PropertyPlaceholderConfigurer p = new PropertyPlaceholderConfigurer();
            p.setProperties((Properties) commonsConfigurationFactoryBean().getObject());
            return p;
        }
    
        @Bean
        public CommonsConfigurationFactoryBean commonsConfigurationFactoryBean() {
            return new CommonsConfigurationFactoryBean(databaseConfiguration());
        }
    
        @Bean
        public DatabaseConfiguration databaseConfiguration() {
            return new DatabaseConfiguration(dataSource, "TABLE", "NAME_COLUMN", "KEY_COLUMN", "VALUE_COLUMN", "NAME");
        }
    
        @Profile({ "dev" })
        static class Dev {
            @Bean
            public DataSource dataSource() {
                return new SimpleDriverDataSource(new OracleDriver(), "jdbc:oracle:thin:@dev-host:port:db", "username",
                        "password");
            }
        }
    
        @Profile({ "prod" })
        static class Prod {
            @Bean
            public DataSource dataSource() {
                return new SimpleDriverDataSource(new OracleDriver(), "jdbc:oracle:thin:@prod-host:port:db", "username",
                        "password");
            }
        }
    }

  • #2
    First of all your PropertyPlaceholderConfigurer bean needs to be static. Please see http://static.springsource.org/sprin...tion/Bean.html the section entitled ''A note on BeanFactoryPostProcessor-returning @Bean methods".

    That said the static is not required for your profile classes although it certainly does not hurt anything and is actually more performant.


    You could try to do it during initialization (untested but worth a shot)

    create a class something like this

    Code:
    public class MyWebContextInitializer
        implements ApplicationContextInitializer<ConfigurableWebApplicationContext>
    {
    
        @Override
        public void initialize(ConfigurableWebApplicationContext applicationContext)
        {
            DataSource dataSource = (DataSource) applicationContext.getBean("dataSource");
            DatabaseConfiguration databaseConfiguration = new DatabaseConfiguration(dataSource,
                "TABLE", "NAME_COLUMN", "KEY_COLUMN", "VALUE_COLUMN", "NAME");
            CommonsConfigurationFactoryBean commonsConfigurationFactoryBean = new CommonsConfigurationFactoryBean(
                databaseConfiguration);
             PropertySource<Map<String, Object>> propertySource = new PropertiesPropertySource("nameForMyPropertiesSource",
                (Properties) commonsConfigurationFactoryBean.getObject());
            applicationContext.getEnvironment().getPropertySources().addFirst(propertySource);
        }
    }
    set it up in your web.xml

    Code:
    	<context-param>
    		<param-name>contextInitializerClasses</param-name>
    		<param-value>com.xxx.xxx.MyWebContextInitializer</param-value>
    	</context-param>
    Last edited by wgorder; Apr 27th, 2012, 02:19 PM.

    Comment


    • #3
      Some other things I should have mentioned... This is a stand-alone application. The application context initialization is handled by Mule ESB... all I am telling Mule to do is import my applicationContext.xml, which has a spring-configured and component scanner. I also have other bean definitions that are dependent upon the values retrieved from the database by the property placeholder configurer, so performing a reload of application context in a PostConstruct is not an option. I have a working example using XML config which I will post shortly. Ultimately, I would like to get an equivalent solution in Java Config.

      Comment


      • #4
        Here is my working solution with a combination of XML and Java Config. Again, I would like to see a solution with all Java Config.

        Current Java Config
        Code:
        @Configuration
        public class DatabaseConfig {
            @Profile({ "dev" })
            static class Dev {
                @Bean
                public DataSource dataSource() {
                    return new SimpleDriverDataSource(new OracleDriver(), "jdbc:oracle:thin:@dev-host:port:db", "username",
                            "password");
                }
            }
        
            @Profile({ "prod" })
            static class Prod {
                @Bean
                public DataSource dataSource() {
                    return new SimpleDriverDataSource(new OracleDriver(), "jdbc:oracle:thin:@prod-host:port:db", "username",
                            "password");
                }
            }
        }
        Current XML Config
        Code:
        <bean name="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        		<property name="location" value="classpath:env.${spring.profiles.active}.properties"/>
        		<property name="properties" ref="commonsConfigurationFactoryBean"/>
        	</bean>
        	
        	<bean name="commonsConfigurationFactoryBean" class="org.springmodules.commons.configuration.CommonsConfigurationFactoryBean">
        		<constructor-arg ref="databaseConfiguration"/>
        	</bean>
        	
        	<bean name="databaseConfiguration" class="org.apache.commons.configuration.DatabaseConfiguration">
        		<constructor-arg name="datasource" ref="dataSource"/>
        		<constructor-arg name="table" value="TABLE"/>
        		<constructor-arg name="nameColumn" value="NAME_COLUMN"/>
        		<constructor-arg name="keyColumn" value="KEY_COLUMN"/>
        		<constructor-arg name="valueColumn" value="VALUE_COLUMN"/>
        		<constructor-arg name="name" value="NAME"/>
        	</bean>
        Last edited by akohring; Apr 27th, 2012, 03:52 PM.

        Comment


        • #5
          Using inheritance should work here, though I have not verified.

          I removed the CommonsConfigurationFactoryBean as it is not supported in Spring 3.x and we can do fine here without it. Because the @Profile extends the abstract Base class the bean context should properly resolve. Hope this works for you!

          Code:
          @Configuration
          public class DatabaseConfig {
              static abstract class Base {
                  public abstract DataSource dataSource();
          
                  @Bean
                  public PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() throws Exception {
                      PropertyPlaceholderConfigurer p = new PropertyPlaceholderConfigurer();
                      p.setProperties(ConfigurationConverter.getProperties(databaseConfiguration()));
                      return p;
                  }
          
                  @Bean
                  public DatabaseConfiguration databaseConfiguration() {
                      return new DatabaseConfiguration(dataSource(), "TABLE", "NAME_COLUMN", "KEY_COLUMN", "VALUE_COLUMN", "NAME");
                  }
              }
          
          
              @Profile({ "dev" })
              static class Dev extends Base {
                  @Bean
                  public DataSource dataSource() {
                      return new SimpleDriverDataSource(new OracleDriver(), "jdbc:oracle:thin:@dev-host:port:db", "username",
                              "password");
                  }
              }
          
              @Profile({ "prod" })
              static class Prod extends Base  {
                  @Bean
                  public DataSource dataSource() {
                      return new SimpleDriverDataSource(new OracleDriver(), "jdbc:oracle:thin:@prod-host:port:db", "username",
                              "password");
                  }
              }
          }
          Last edited by dkrieg; May 10th, 2012, 01:08 AM.

          Comment

          Working...
          X