8

In spring context file, I use org.springframework.beans.factory.config.PropertyPlaceholderConfigurer to load several configuration files:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <list>
            <value>a.properties</value>
            <value>b.properties</value>
            <value>c.properties</value>
        </list>
    </property>
</bean>

The a.properties, b.properties, c.propertes may have some hibernate configurations which have prefix of abc.:

abc.hibernate.show_sql=true
abc.hibernate.default_schema=myschema
abc.hibernate.xxx=xxx
abc.hibernate.xxx=xxx
abc.hibernate.xxx=xxx
abc.hibernate.xxx=xxx
abc.hibernate.xxx=xxx
abc.hibernate.xxx=xxx

Now I want to define a hibernate session factory:

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="hibernateProperties">
        <util:properties>
            <prop key="hibernate.show_sql">${abc.hibernate.show_sql}</prop>
            <prop key="hibernate.xxx">${abc.hibernate.xxx}</prop>
            <prop key="hibernate.xxx">${abc.hibernate.xxx}</prop>
            <prop key="hibernate.xxx">${abc.hibernate.xxx}</prop>
            <prop key="hibernate.xxx">${abc.hibernate.xxx}</prop>
            <prop key="hibernate.xxx">${abc.hibernate.xxx}</prop>
        </util:properties>
    </property>
</bean>

You can see I have to write the property in bean declaration again and again:

<prop key="hibernate.xxx">${abc.hibernate.xxx}</prop>

Is there any way to just tell spring to get all properties which have prefix abc.?

So I can write:

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="hibernateProperties">
        <something prefix="abc" /> <!-- TODO -->
    </property>
</bean>

Is it possible or is there any other simple solutions for it?

Freewind
  • 193,756
  • 157
  • 432
  • 708
  • Take a look at Spel http://docs.spring.io/spring/docs/3.0.x/reference/expressions.html you can try some regex on the properies. – Evgeni Dimitrov Dec 27 '13 at 08:15

3 Answers3

4

You can use something like the following class to extend java.util.Properties:

import java.util.Enumeration;
import java.util.Properties;

public class PrefixedProperties extends Properties {
    public PrefixedProperties(Properties props, String prefix){
        if(props == null){
            return;
        }

        Enumeration<String> en = (Enumeration<String>) props.propertyNames();
        while(en.hasMoreElements()){
            String propName = en.nextElement();
            String propValue = props.getProperty(propName);

            if(propName.startsWith(prefix)){
                String key = propName.substring(prefix.length());
                setProperty(key, propValue);
            }
        }
    }    
}

Then you can define sessionFactory as following:

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="hibernateProperties">
        <bean id="sessionFactoryProperties" class="PrefixedProperties">
            <constructor-arg ref="props"/> <!-- reference to all properties -->
            <constructor-arg value="abc.hibernate."/> <!-- prefix -->
        </bean>
    </property>
</bean>

I don't see any other possibility to filter properties.

Freewind
  • 193,756
  • 157
  • 432
  • 708
Vlad
  • 471
  • 5
  • 7
  • Where is the `props` in `` comming from? – Freewind Dec 30 '13 at 03:08
  • The solution is to subclass PropertyPlaceHolderConfigurer and make the Properties available to the context manually. Then you can define a bean "props" with all resolved properties. – Vlad Dec 30 '13 at 09:04
  • You can also use either the spring utils, or load properties via the PropertiesFactoryBean. `` or: ` ` – Vlad Dec 30 '13 at 09:06
3

As Sean said, PropertyPlaceHolderConfigurer don't expose his properties, but you could use reflection to filter them.

public static Properties filterProperties(String prefix, PropertyPlaceholderConfigurer ppc) {
        Properties props = new Properties();
        Method method = ReflectionUtils.findMethod(PropertiesLoaderSupport.class, "mergeProperties");
        ReflectionUtils.makeAccessible(method);
        Properties all = (Properties) ReflectionUtils.invokeMethod(method, ppc);

        for (String key : all.stringPropertyNames()) {
            if (key.startsWith(prefix))
                props.setProperty(key, all.getProperty(key));
        }

        return props;
    }

And inject with spel

<bean id="ppc" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
...
</bean>

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="hibernateProperties"  value="#{T(SomeClass).filterProperties('abc.', @ppc)}" />
</bean>
Jose Luis Martin
  • 10,459
  • 1
  • 37
  • 38
2

The PropertyPlaceHolderConfigurer is a special construct that is meant for bean creation only. It does not expose the underlying properties in a way that you can access.

A possible workaround is to subclass PropertyPlaceHolderConfigurer, as I have outlined in a previous answer of mine.

Another way is to have a custom PropertiesFactoryBean, like this one:

public class PrefixedPropertyFactoryBean extends AbstractFactoryBean<Properties> {

    private List<Resource> locations;
    public void setLocations(List<Resource> locations) { this.locations = locations; }

    private String prefix;
    public void setPrefix(String prefix) { this.prefix = prefix; }

    @Override public Class<?> getObjectType() { return Properties.class; }

    @Override
    protected Properties createInstance() throws Exception {
        Properties properties = new Properties();
        for (Resource resource : locations) {
           properties.load( resource.getInputStream());
        }
        final Iterator<Object> keyIterator = properties.keySet().iterator();
        while(keyIterator.hasNext()) {
            if(!keyIterator.next().toString().startsWith(prefix))
            keyIterator.remove();
        }
        return properties;
    }
}

You can use this in your XML as follows:

<bean id="hibernateProps" class="some.path.PrefixedPropertyFactoryBean">
    <property name="locations">
        <list>
            <value>a.properties</value>
            <value>b.properties</value>
            <value>c.properties</value>
        </list>
    </property>
    <property name="prefix" value="abc.hibernate" />
</bean>

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="hibernateProperties" ref="hibernateProps" />
</bean>

This is of course redundant, as you'd probably still have to wire the PropertyPlaceHolderConfigurer to set up your other beans.

Community
  • 1
  • 1
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588