<bean id="Mybean" class="Bean">
<property name="config" ref="dev"/>
</bean>
<bean id="dev" class="Dev">
<property name="x" ref="Dev1">
<property name="y" ref="Dev2">
<property name="z" ref="Dev3">
</bean>
<bean id="stag" class="Dev">
<property name="x" ref="Stag1">
<property name="y" ref="Stag2">
<property name="z" ref="Stag3">
</bean>
In the above scenario, the config property in the bean MyBean change from environment to environment. At the time of dev, reference of config change to dev. And in staging, the reference change to stag. The problem comes at the time of checked in the spring file. We have to analyze everytime the reference of config before checked in. If the reference of config with the value of dev checked in, we might have to explain a lot of questions.
Is there any solution to solve to make it automate?
Note: Spring version is 2.0.1
Use the PropertyPlaceholderConfigurer from Spring, and remove an unused bean :
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>env.properties</value>
</property>
</bean>
<bean id="Mybean" class="Bean">
<property name="config" ref="config"/>
</bean>
<bean id="config" class="Config">
<property name="x" ref="${x}">
<property name="y" ref="${y}">
<property name="z" ref="${z}">
</bean>
and the env.properties file contains the following properties :
x=Dev1
y=Dev2
z=Dev3
or
x=Stag1
y=Stag2
z=Stag3
setup up the placeholder bean by specfiy, let spring know you want the placeholder
set up the config for the "my bean" by using the "${env}"
for example:
<beans>
<bean id="configBean" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location"><value>env.properties</value></property>
</bean>
<bean id="Mybean" class="Bean">
<property name="config" ref="${env}"/>
</bean>
</beans>
and you need the add the env = dev key-value to the env.properties file
Assuming you meant Spring 3.1, rather than Spring 2.1 (which doesn't exist), then you can use the new "Environment Profiles" feature that was introduced in 3.1. This allows you to define a set of beans for each of your environments, and then select the "active" one at runtime.
See this SpringSource Blog Entry for examples.
You can do it using PropertyPlaceholderConfigurer or using #Profile
Also See
is-there-any-way-to-enable-or-disable-the-spring-bean-definition-in-applicationc
PropertyPlaceholderConfigurer example
PropertyPlaceholderConfigurer is the answer, yet I would imagine that you would like this to happen without the need to stay updating your properties file for each environment.
My suggestion would therefore be as follows
Use PropertyPlaceholderConfigurer, but do not create a properties file
By default, PropertyPlaceholderConfigurer first tries to find a value in a properties file, but if that fails, it will look for one in system properties
So all you need to do is to define both beans the same way that you are doing it, i.e. dev and stag.. which is a fine approach since you're clearly showing the different configurations... it would help if you also added some alias to show clearly the setting you want to use.
Next, pass in a system property defining what mode you are in... and ideally explicitly set PropertyPlaceholderConfigurer to use System properties.
So.. your config would look something like this
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
systemPropertiesMode="2"/>
<bean id="Mybean" class="Bean">
<property name="config" ref="${launch.mode}"/>
</bean>
<bean id="dev" name="dev_mode" class="Dev">
<property name="x" ref="Dev1">
<property name="y" ref="Dev2">
<property name="z" ref="Dev3">
</bean>
<bean id="stag" name="staging_mode" class="Dev">
<property name="x" ref="Stag1">
<property name="y" ref="Stag2">
<property name="z" ref="Stag3">
</bean>
You can then pass in the property upon startup in the following fashion
-D<property-name>=<value>
So in this case you'd use
-Dlaunch.mode=dev_mode
Or
-Dlaunch.mode=staging_mode
And you won't need to touch any of the configuration files.
Just a further note on systemPropertiesMode, accepted values are the following:
0 - never look in system properties
1 - use system properties as a fallback (i.e. if not found in properties files)
2 - system properties always override (the mode i'm suggesting)
Hope it helps :)
Note: This recommendation is only applicable to Spring < 3.1, since from 3.1 onward, the recommended approach is to use #Profile
Spring provides a mechanism called property placeholders. This way you can set certain properties in a database/properties file and spring will fill them in on startup.
The class to use for this is located here.
Related
I am moving our config properties from a properties file to a database using apache commons DatabaseConfiguration. Works fine. However, it obviously needs database definition, and I want to load that with a jndi datasource name that is itself a property. So I want to keep a property file with that property and one or two others, that would load first and get used by the database configuration.
I have tried about a million permutations and I can't seem to get it to work. If I hardcode the jndi name in the spring config, it works, but whether I include both in the same property configurator or separate ones, whether I use the order property or not...nothing seems to work.
Here's one of my iterations that didn't work:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="/WEB-INF/esp.properties" />
<property name="order" value="10" />
</bean>
<bean id="propertyConfigurer2" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="order" value="20" />
<property name="properties" ref="dbConfigFactory" />
</bean>
dbConfigFactory there is the factory that includes the databaseconfiguration bean. Does anyone know how to do this?
Here is the situation:
The xml configuration looks as follows:
<bean id="ppConfig1"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>file:c:\test\env.properties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="true" />
</bean>
<bean id="string" class="java.lang.String" depends-on="ppConfig1">
<constructor-arg value="${env.app.type}"/>
</bean>
<bean id="ppConfig2"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" depends-on="string">
<property name="locations">
<list>
<value>file:c:\test\#{string}.properties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="false" />
</bean>
As is evident, I want load a specific property file (e.g. app1.properties) based on the value of key env.app.type in C:\test\env.properties.
The code to load/test the above config looks as follows:
ApplicationContext context = new ClassPathXmlApplicationContext(
"SpringBeans.xml");
String ss = (String)context.getBean("string");
System.out.println(ss);
This seems not working. It failing with following error:
Exception in thread "main"
org.springframework.beans.factory.BeanInitializationException: Could
not load properties; nested exception is
java.io.FileNotFoundException: C:\test\${env.app.type}.properties (The
system cannot find the file specified) at
org.springframework.beans.factory.config.PropertyResourceConfigurer.postProcessBeanFactory(PropertyResourceConfigurer.java:87)
The file c:\test\env.properties looks as follows
env.app.type=app1
One interesting observation is that when I comment out the ppConfig2, everything works fine and correct value of env.app.type is printed. I am open to any other suggestion to get around this. My basic requirement is to select the property file at run time based on the property specified in env.properties. (I am using spring 3.1.0)
You need something like this:
<context:component-scan base-package="com.foo.bar" />
<context:property-placeholder location="file:c:/test/${env.app.type}.properties" />
<bean id="service" class="com.foo.bar.ExampleService">
<property name="foo" value="${foo}" />
</bean>
package com.foo.bar;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
#Configuration
#PropertySource("file:c:/test/env.properties")
public class SpringConfig{
}
And "foo" value comes from app1.properties file. Basically, one file is being loaded with #PropertySource, the other with the regular property placeholder.
I think you are trying to do things that Spring does not like. The normal order for bean initialization is :
fully construct bean post processors
construct other beans
init other beans
As long as those 3 passes are cleanly separated, all will be ok, but you have a post processor bean (ppConfig2) depending on a simple bean (string).
IMHO, you should avoid such construct if possible. Even if it works now, you are under the risk of problem arising at any modification of you application context because you are allready on the edge.
Could it be acceptable to use environment variables or system properties to avoid that a bean post-processor depends on another bean post-processor ? If yes, you would'nt be bothered by ppConfig1 and simply have :
<bean id="ppConfig2"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" depends-on="string">
<property name="locations">
<list>
<value>file:c:\test\${configured_env}.properties</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="false" />
</bean>
If environment variables or system properties are not an option, you could use programmatic configuration as suggested by Andrei Stefan.
You can do this with util:properties:
<util:properties id="envProperties" location="env.properties"/>
<util:properties id="properties" location="#{envProperties.getProperty('env.app.type')}.properties"/>
<context:property-placeholder properties-ref="envProperties" ignore-unresolvable="true" ignore-resource-not-found="true"/>
<context:property-placeholder properties-ref="properties" ignore-unresolvable="true"/>
BTW there's a bug in Spring that prevents Spring EL from being evaluated inside location attribute in context:property-placeholder.
I need to set the properties on a bean with common fields from different "places" so something along the lines of:
<bean id="parent1" abstract="true">
<property name="commonField" value="parent1_val"></property>
</bean>
<bean id="parent2" abstract="true">
<property name="commonField2" value="parent2_val"></property>
</bean>
<bean id="injectDemo1" class="spring.testClasses.InjectDemo1" parent="parent1" parent="parent2>
<property name="val1" value="val1"/>
</bean>
The above example does not work because only one parent attribute is allowed. But this is what I need to do; is there a mechanism in Spring that will allow me to set properties from multiple sources.
I looked at property files to do the same thing but properties files cannot hold Sets, Lists or Maps which I need or can they?
Something like:
<bean id="injectDemo1" class="spring.testClasses.InjectDemo1">
<property name="val1" value="val1"/>
<property name="commonField1" value="${prop1}"/>
<property name="commonField2" value="${prop2}"/>
</bean>
I hope I've explained it okay?
I dont think Spring beans support multiple inheritance, for the same reason the makers of Java decided to not support muliple inheritance.
I want to know if beans in my application context are binded dynamically. Specifically, if I have
<bean id="mySpringRemoteService"
class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
<property name="serviceInterface"
value="foo.bar.services.mySpringRemoteService" />
<property name="serviceUrl" value="${spring.remote.service.url}"/>
</bean>
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:some.properties"/>
</bean>
if i change the property for the service url in my properties file, will retrieving the bean later reflect this change?
Well, I think it is easy for you to have a try yourself.
The answer is No (Unless behavior of Spring changed in 3.x )
I believe you have to implement runtime update behavior for config files. Take a look in Apache Commons Configurations.
Configuration:
Spring 2.5, Junit 4, Log4j
The log4j file location is specified from a system property
${log.location}
At runtime, system property set with -D java option. All is well.
Problem / What I Need:
At unit test time, system property not set, and file location not resolved.
App uses Spring, would like to simply configure Spring to set the system property.
More Info:
Requirement is for configuration only. Can't introduce new Java code, or entries into IDE. Ideally, one of Spring's property configuration implementations could handle this--I just haven't been able to find the right combination.
This idea is close, but needs to add Java code:
Spring SystemPropertyInitializingBean
Any help out there? Any ideas are appreciated.
There was a request in the comments for a Spring 3 example on how to do this.
<bean id="systemPrereqs"
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" value="#{#systemProperties}" />
<property name="targetMethod" value="putAll" />
<property name="arguments">
<!-- The new Properties -->
<util:properties>
<prop key="java.security.auth.login.config">/super/secret/jaas.conf</prop>
</util:properties>
</property>
</bean>
You can achieve that with the combination of two MethodInvokingFactoryBeans
Create an inner bean that accesses System.getProperties and an outer bean that invokes putAll on the properties acquired by the inner bean:
<bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property
name="targetObject">
<!-- System.getProperties() -->
<bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="java.lang.System" />
<property name="targetMethod" value="getProperties" />
</bean>
</property>
<property
name="targetMethod"
value="putAll" />
<property
name="arguments">
<!-- The new Properties -->
<util:properties>
<prop
key="my.key">myvalue</prop>
<prop
key="my.key2">myvalue2</prop>
<prop
key="my.key3">myvalue3</prop>
</util:properties>
</property>
</bean>
(You could of course use just one bean and target System.setProperties(), but then you'd be replacing existing properties which is not a good idea.
Anyway, here's my little test method:
public static void main(final String[] args) {
new ClassPathXmlApplicationContext("classpath:beans.xml");
System.out.println("my.key: "+System.getProperty("my.key"));
System.out.println("my.key2: "+System.getProperty("my.key2"));
System.out.println("my.key3: "+System.getProperty("my.key3"));
// to test that we're not overwriting existing properties
System.out.println("java.io.tmpdir: "+System.getProperty("java.io.tmpdir"));
}
And here's the output:
my.key: myvalue
my.key2: myvalue2
my.key3: myvalue3
java.io.tmpdir: C:\DOKUME~1\SEANFL~1\LOKALE~1\Temp\
Spring Batch has a SystemPropertyInitializer class which can be used to set a system property slightly more concisely, e.g. to force JBoss logging to use slf4j (with Spring JPA):
<bean id="setupJBossLoggingProperty"
class="org.springframework.batch.support.SystemPropertyInitializer"
p:keyName="org.jboss.logging.provider" p:defaultValue="slf4j"/>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
depends-on="setupJBossLoggingProperty"
Remember to add the "depends-on" attribute to force the system property to be set first.
For a more terse approach try:
<beans ... xmlns:p="http://www.springframework.org/schema/p" ...
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
p:targetObject="#{#systemProperties}" p:targetMethod="setProperty"
p:arguments="#{{'org.jboss.logging.provider','slf4j'}}"/>