Spring loading properties from a bean - spring

I am currently usinmg PropertyPlaceholderConfigurer to load properties into my spring context, however I want to have a custom hierarchy/overriding of property files that I am handling in a Java class. Can I have my class, which itself is a bean in the context, inject the properties into the context?
e.g.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:/config/conf.properties" />
</bean>
<bean id="config" class="com.main.Config" />
My Config class has a Properties getProperties() method -- can I wire that into spring somehow? Obviously the properties would not be loaded until the config bean is created or inited.
Thanks!
Update: As suggested by the answer I change my Config to Config extends PropertySource<Properties> and had to write a small wrapper around MutablePropertySources that takes a single PropertySource and then my xml looks like this:
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="propertySources">
<bean class="com.main.ConfigMutablePropertySources">
<constructor-arg name="propertySource" ref="config" />
</bean>
</property>
</bean>
Where:
public class ConfigMutablePropertySources extends MutablePropertySources {
public ConfigMutablePropertySources(PropertySource<?> propertySource) {
super(new MutablePropertySources());
addLast(propertySource);
}
}

Looking at the methods available (from JavaDoc for PropertyPlaceholderConfigurer) suggests to me no easy way to do such a thing.
The general comment at the top suggests that, as of Spring 3.1, one should prefer to use PropertySourcesPlaceholderConfigurer instead. Using this solution, it appears that you could register your own set of PropertySource objects via the setPropertySources(...) method. Of course your Config class would have to be fit into the interface (abstract class actually) of PropertySource.
It is worth noting that using the setPropertySources(...) method above will assume nothing else about your property sources and will therefore not add the default property sources (Environment and Local properties).

Related

Prevent Spring from meddling with CDI annotations even for factory created instances

I have a legacy product's JAR that contain Spring (4.3.8) managed classes. I need to integrate it with CDI (JavaEE 7).
I have an interface from the legacy JAR, that is implemented by a CDI bean. The CDI bean is requested from the CDI BeanManager and returned from a factory method. The factory method is registered inside Spring XML and works as expected.
The problem occurs, when a Spring bean of the legacy JAR depends on the implemented interface. Spring than injects the CDI implementation instance and scans the class it for known annotations, namingly #Inject. It then tries to resolve the dependency, which doesn't work since the dependency is not available to Spring.
I already tweaked context:property-placeholder excludes, but that changes nothing.
So how can I tell Spring to stop trying to inject something in my factory produced bean instance?
I finally was able to solve (work around) the problem. I had to remove all CDI-Annotations in the legacy JAR (by replacining them with their Spring counterparts), so spring would any longer work.
Then I added the following XML block to the applicationContext.xml of my CDI WAR:
<context:component-scan annotation-config="false" base-package="com.example">
</context:component-scan>
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor">
<property name="autowiredAnnotationTypes">
<set>
<value>org.springframework.beans.factory.annotation.Autowired</value>
<value>org.springframework.beans.factory.annotation.Value</value>
</set>
</property>
</bean>
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor" />
<bean class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
<property name="customQualifierTypes">
<set>
<value>org.springframework.beans.factory.annotation.Qualifier</value>
</set>
</property>
</bean>
Basically that drops the support for #Inject, etc. from Spring and leaves it where it belongs: CDI.
It's a bit easier.
AutowiredAnnotationBeanPostProcessor is already a bean, so you can configure it before Spring starts to scan with a ServletContextListener to exclude #Inject annotations. At least from Spring 4.1+, AutowiredAnnotationBeanPostProcessor has a method setAutowiredAnnotationTypes, e.g.:
#WebListener
public class ApplicationConfigListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent event) {
ApplicationContext appCtx = WebApplicationContextUtils.getWebApplicationContext(event.getServletContext());
Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<Class<? extends Annotation>>();
AutowiredAnnotationBeanPostProcessor bean = appCtx.getBean(AutowiredAnnotationBeanPostProcessor.class);
autowiredAnnotationTypes.add(Autowired.class);
autowiredAnnotationTypes.add(Value.class);
bean.setAutowiredAnnotationTypes(autowiredAnnotationTypes);
}
}
You could use a SpringBeanAutowiringInterceptor too.
This is explained here.

Correctly using Spring environment profiles in order to manage PropertySourcesPlaceholderConfigurer and sets of properties files

I am working on a Spring application and I am realizing that I have an issue with the way I manage my properties. I use Spring environment profiles in order to load my properties and I've recently added more profiles which has made my properties files unmanagable.
The properties files are located in different folders within src/main/resources/META-INF/props/, with eah folder matching a different Spring environment profile.
I have at least 5 profiles now which means I have 5 sub-folders each containing the properties files with the same names but with different values for only some keys.
Here is how it looks:
Here is how I've configured my PropertyPlaceholders:
#Configuration
public class PropertyPlaceholderConfiguration {
#Profile(Profiles.CLOUD)
static class cloudConfiguration {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/cloud/*.properties"));
return propertySourcesPlaceholderConfigurer;
}
}
#Profile(Profiles.DEFAULT)
static class defaultConfiguration {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/default/*.properties"));
return propertySourcesPlaceholderConfigurer;
}
}
#Profile(Profiles.TEST)
static class testConfiguration {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/test/*.properties"));
return propertySourcesPlaceholderConfigurer;
}
}
#Profile(Profiles.DEV)
static class devConfiguration {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/dev/*.properties"));
return propertySourcesPlaceholderConfigurer;
}
...
}
To sum up, my problem is as follows:
key/value pairs are duplicated all over the 5 different folders because only a few values are different.
I am therefore looking for a new strategy to manage the differences between the different environments.
Can anyone please help?
There are many ways to do this but I think you are in the right path.
Overriding of properties files gets done through BeanFactoryPostProcessors, and there's two implementations that can help you in this case so you don't have to do it from scratch:
PropertySourcesPlaceholderConfigurer & PropertyOverrideConfigurer.
This is an example using PropertySourcesPlaceholderConfigurer:
<bean id="someProperties" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer" abstract="true" >
<property name="locations">
<list>
<value>classpath:database.properties</value>
<value>classpath:email.properties</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="false"/>
</bean>
<bean id="devProperties" parent="someProperties" >
<property name="properties" >
<props >
<prop key="database.username">Database Username used for Development Environment </prop>
</props>
</property>
<property name="localOverride" value="true" />
</bean>
<bean id="testProperties" parent="someProperties" >
<property name="properties" >
<props >
<prop key="database.username">Database Username used for Testing Environment </prop>
</props>
</property>
<property name="localOverride" value="true" />
</bean>
In that example you load the default properties into a bean that will be used as a template for other beans, and in the specific bean, say TestEnvironmentProperties Bean or DevEnvironmentProperties Bean you override only the specific properties you want to override from the default properties files. The example only shows how to override specific properties without the need to create another properties file, from there you can decide how to choose which bean to create with a factory, a simple facade class or a profiles system, anything that you like and matches your architecture.
Also if you think this option is too verbose you can use the property-placeholder element.
I recommend you this book:
Getting started with Spring Framework, Second Edition
it has just the examples you need in its 5th chapter. I didn't write it or anything, I just bought it some time ago and I loved it after going through so many bad spring books.
Pull the common properties into a separate file and specify that plus the profile specific properties as inputs for each profile. Haven't used the Java based Spring config but here's how I do it in XML. Assume you can do the same in code:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<beans profile="default">
<bean id="applicationPropertiesPlaceholder"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:profiles/common.profile.properties</value>
<value>classpath:profiles/local.profile.properties</value>
</list>
</property>
</bean>
</beans>
<beans profile="local">
<bean id="applicationPropertiesPlaceholder"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:profiles/common.profile.properties</value>
<value>classpath:profiles/local.profile.properties</value>
</list>
</property>
</bean>
</beans>
<beans profile="trial">
<bean id="applicationPropertiesPlaceholder"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:profiles/common.profile.properties</value>
<value>classpath:profiles/trial.profile.properties</value>
</list>
</property>
</bean>
</beans>
<beans profile="live">
<bean id="applicationPropertiesPlaceholder"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:profiles/common.profile.properties</value>
<value>classpath:profiles/live.profile.properties</value>
</list>
</property>
</bean>
</beans>
</beans>
I think I stumbled upon the beginning of a solution with this interesting blog post.
To quote from the article:
Beware of redundancy of environment-specific properties. For example,
if the solution is to have one property file for each environment
(e.g. “db-test.properties”, “db-dev.properties”, etc.), then
maintaining these properties can be a bit of a nightmare – if a
property “foo” is added, then it would have to be added to the
property file for each environment (e.g. DEV, TEST, PROD, etc.). The
PropertyOverrideConfigurer is appropriate to eliminate this
redundancy, setting the default value in the application context
itself, but then the overriding value in a separate file. It’s
important, however, to document this well, since it can look a bit
“magical” to an unsuspecting maintenance developer who sees one value
specified in the context file, but another used at runtime.
The idea is to rely on PropertyOverrideConfigurer and factor out common properties.
The better practice is to put all of the properties files outside of the WAR packaging. You can use a JNDI variable to point Spring to the physical path where the external properties files can be read. Example:
<jee:jndi-lookup id="externalFileArea" jndi-name="java:comp/env/mgo/externalFileArea"
default-value="/opt/external/props" lookup-on-startup="true"/>
<util:properties id="myConf" location="file:#{externalFileArea}/my-conf.properties"/>
<!-- And now, to use an entry from this properties import -->
<bean id="foo" class="foo.bar.com">
<property name="configParam1" value="#{myConf['fooConfig.param1']}"
</bean>
If on Windows, the the JNDI entry might be specified as /C/Users/someone.
Finally, add a file named /opt/external/props/my-conf.properties, and in there place an entry like: fooConfig.param1=true
Wash, rinse, repeat. Far less work, much more secure and much more easy to maintain.
I would suggest that "common" properties do not need to be in a common file, and instead can be default values of property placeholder in-line in your code. This allows them to be overridden via JVM args (or local env) while not needing to be "managed" in a file. Your environment-specific properties, in your environment-specific files, then indicate just those properties that MUST be provided in each environment for the app to start up. As such they would NOT have default values in placeholders.

How to Get the #Configuration annotation capability in Spring2.5.1

I need to load property file when context is loading.(constants.properties As per below example) one of the keys in this property file pointing to another property file (for example privatedata.properties) which is not in the class path. I need to read properties of privatedata.properties file and inject to my bean. I can do this as below with the help of #Configuration annotation in spring 3.x. How would I get the same capability in Spring2.5.1 ?
With Spring 3.x
package com.snippets.enterprise;
**#Configuration**
#PropertySource("classpath:/constants.properties")
public class SpringConfig { }
<context:component-scan base-package="com.snippets.enterprise"/>
//constants.properties has a key called private.supportiveFile and that points to the
file in **non-class path**
<context:property-placeholder location="file:${private.supportiveFile}" />
<bean id="helloWorldBean"
class="com.snippets.enterprise.services.HelloWorld">
<property name="key" value="${keypath}" />
</bean>
How can I get the same capability in Spring2.5.X.
org.springframework.context.annotation.Configuration is present from spring3.x onwards.

Define a custom template loader for FreeMarker with Spring

I'm trying to use FreeMarker to create html for sending an email using Spring. I do not want to access the templates from files, but rather get it from db (Mongo, but any db would be the same).
My current configuration is as follows:
<!-- freemarker config -->
<bean id="freemarkerConfiguration" class="org.springframework.ui.freemarker.FreeMarkerConfigurationFactoryBean">
<property name="preTemplateLoaders">
<list>
<ref bean="databaseTemplateLoader"/>
</list>
</property>
</bean>
<bean name="databaseTemplateLoader" class="com.myapp.service.MongoDBToFreeMarkerTemplateLoader"/>
When I autowire the Configuration object I get the following exception:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [freemarker.template.Configuration] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency.
I use the following code to Autowire:
#Autowired
Configuration freeMarkerTemplateEngine;
Without dependency injection (i.e. when using "new Configuration()" and manually setting the custom loader), it works fine, but obviously I want to stick to DI here.
Is there anything else I need to define in order to do this? I've followed this blog and it didn't say anything else is needed.
Well, I figured it out.
As it turns out, there were two problems here:
I put the factory bean configuration in spring-servlet.xml, next to
the freemarkerViewResolver which probably made it available to the
view resolver, but invisible to the rest of the application. Moving
this config to applicationcontext.xml was step one in solving this
problem.
I had a Maven misconfiguration. When I added FreeMarker to
my POM.XML file, I did not set the scope. The default scope is
Compilation, which means that Configuration class was not available
at runtime. Adding runtime to the freemarker maven
include fixed that one.
It now works!
To be able to use Spring's tag lib with a custom template loader
<#import "spring.ftl" as spring />
you need to do the following (using Java configuration):
public FreeMarkerConfigurer getFreemarkerConfig(DBTemplateLoader dbTemplateLoader) throws IOException, TemplateException {
FreeMarkerConfigurationFactoryBean freeMarkerConfigurationFactoryBean = new FreeMarkerConfigurationFactoryBean();
freeMarkerConfigurationFactoryBean.setPreTemplateLoaders(new ClassTemplateLoader(FreeMarkerConfigurer.class, ""), dbTemplateLoader);
FreeMarkerConfigurer result = new FreeMarkerConfigurer();
result.setConfiguration(freeMarkerConfigurationFactoryBean.createConfiguration());
return result;
}
I believe this is because Spring cannot find the matching bean defined in your applicationContext.
Since you define freemarker configuration bean like this:
<!-- freemarker config -->
<bean id="freemarkerConfiguration" class="org.springframework.ui.freemarker.FreeMarkerConfigurationFactoryBean">
<property name="preTemplateLoaders">
<list>
<ref bean="databaseTemplateLoader"/>
</list>
</property>
</bean>
<bean name="databaseTemplateLoader" class="com.myapp.service.MongoDBToFreeMarkerTemplateLoader"/>
Then in your code, you should inject bean with the name freemarkerConfiguration as below:
#Autowired
FreeMarkerConfigurationFactoryBean freemarkerConfiguration;

Setting property reference for a bean programmatically

Right now i am using the following bean entry
<bean id="Service" >
<property name="target">
<bean class="someClass" lazy-init="false">
<property name="SessionFactory1"><ref bean="SessionFactory1"/></property>
<property name="SessionFactory2"><ref bean="SessionFactory2"/></property>
<property name="SessionFactory3"><ref bean="SessionFactory3"/></property>
</bean>
</property>
</bean>
Now the requirement is to first check which all session factories have an active datasource and include those only in the above bean definition. So that the application does not break if we try to initialize a session factory with inactive datasource.
sessionfactory initialization will be take care by using seperate config xml for session factories and loading only the ones with active datasources.
Please help on how can this be achieved.
You can use Spring InitializingBean interface, which makes you implement an afterPropertiesSet() method. This method will be executed after Spring instantiates your class, and you could check if your session factories are available or not.
InitializingBean: Interface to be implemented by beans that need to react once all their properties have been set by a BeanFactory: for example, to perform custom initialization, or merely to check that all mandatory properties have been set.
link: Spring InitializingBean

Resources