Concatenate strings within a Spring XML configuration file? - java

I have a String value in a Spring configuration file that comes to be as the result of a JNDI lookup -- it happens to be a path name:
<jee:jndi-lookup id="myAppHomeDir" jndi-name="myAppHomeDir" />
Now I need to concatenate on to the end of this path another string and hand it off to another Spring bean as follows (which of course doesn't work):
<bean id="LogPath" class="org.mystuff.initBean">
<property name="logDirectory">
<jee:jndi-lookup id="myAppHomeDir"
jndi-name="myAppHomeDir" /> + "/logs"
</property>
</bean>
Is there a simple way to do this without me having to write a utility class in Java?

Try using Spring EL (expression language). I would try the following (not tested):
<jee:jndi-lookup id="myAppHomeDir" jndi-name="myAppHomeDir" />
<bean id="LogPath" class="org.mystuff.initBean">
<property name="logDirectory" value="#{myAppHomeDir+'/logs'}"/>
</bean>
Not quite sure if it would work. The thing that troubles me is the cast from File (I guess) to String when concatenating. So if the previous one didn't work, I would try:
#{myAppHomeDir.canonicalPath+'/logs'}
Let us know if it works.

Related

Spring 3.5 Setting an xml property, handling the default value using PropertyPlaceholderConfigurer

I'm working with an old project using Spring 3.5 and xml config.
In the application context I am setting a property to an externally configured xml file value so something like this -
<bean id="myService" class="com.mypath.MyService">
<property name="myProperty" value="${myValue}:myDefaultValue" />
</bean>
This all works properly.
In some tests though there is a test-context.xml coming into play which sets the values of the properties like this
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties">
<util:properties>
<prop key="myProperty">myValue</prop>
</util:properties>
</property>
</bean>
This is failing to 'understand' the default value. If I leave the value blank I get :myDefaultValue returned. If I set myValue like my example shows I get myValue:myDefaultValue.
I had a quick peak at the PropertyPlaceholderConfigurer and it looks very basic without many options. I've not used it before. Does anyone know if there is a simple way for me to handle the defaults ? Or maybe I have to use a different method to set the test context values ?
You have your syntax slightly off. The default value for the expression goes inside the braces, like so: ${myValue:defaultValue}.

Spring - PropertyOverrideConfigurer multiple configuration lines for ONE bean property

I have a spring xml file with some beans, two of which I have pasted below:
<bean
class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
<property name="locations">
<value>classpath:projectname-override.properties</value>
</property>
</bean>
<bean id="myBeanName"
class="my.company.department.svc.spring.WmSvcJndiDataSource">
<property name="jndiName">
<value>jdbc/XXXXX</value>
</property>
<property name="jndiTemplate">
<ref bean="myJndiTemplate" />
</property>
</bean>
The properties file referred to in the first bean is very small (currently only one line), and looks like this:
myBeanName.jndiName=java:comp/env/jdbc/XXXXX_YYY_DEV3
It is my understanding that PropertyOverrideConfigurer in the first bean
pushes values from a properties file into bean definitions
So the value of the jndiName property in the myBeanName bean will turn from
jdbc/XXXXX
to
java:comp/env/jdbc/XXXXX_YYY_DEV3.
This works and it partially does what we want. I need to expand the functionality so that I can handle multiple deployment environments. This currently replaces the bean property for one of our development environments (dev3). I think I need spring to dynamically alter "myBeanName" for about 25 different dev environments.
For example, I think I want my properties file to look like this
myBeanName.jndiName=java:comp/env/jdbc/XXXXX_YYY_DEV1
myBeanName.jndiName=java:comp/env/jdbc/XXXXX_YYY_DEV2
myBeanName.jndiName=java:comp/env/jdbc/XXXXX_YYY_DEV3
myBeanName.jndiName=java:comp/env/jdbc/XXXXX_YYY_DEV4
myBeanName.jndiName=java:comp/env/jdbc/XXXXX_YYY_DEV5
...
...
myBeanName.jndiName=java:comp/env/jdbc/XXXXX_YYY_DEV24
myBeanName.jndiName=java:comp/env/jdbc/XXXXX_YYY_DEV25
I have no idea if this is the right approach.
Any ideas?
Thanks in advance.
NOTE
my.company.department.svc.spring.WmSvcJndiDataSource in the second bean is simply a class we extended off of org.springframework.jndi.JndiObjectFactoryBean, FYI. I don't know if that is important to know or not.

What is the most Spring-like way to wire a String property to another bean's method?

I've got two beans, and I'd like to wire the String property of one bean to the String value of the property of another (which is computed from the environment of the program). What is the best way to wire these together?
The best solution I can think of is:
<bean id="thisBean">
<property name="foo">
<bean factory-bean="otherBean" factory-method="getStringForThisBean" />
</property>
</bean>
There are other ways to do it, like using SpringEL or working inside Spring's property system, but this is the easiest. It just doesn't feel right creating a heavy-weight bean to store a String value.
Is there a better way?
I feel Spring-EL would be a much better fit, it would be this way with Spring-EL:
<bean id="thisBean">
<property name="foo" value="#{otherbean.property}"/>
</bean>

Modifying spring framework as per my needs

I need to do develop a wrapper on top of spring framework. Details are as follows:
There will be one file called as template
<beans>
<bean class"com.sample.SampleClass">
<property name="abc" identifier="id100" > defaultValue </property>
<property name="abc" identifier="id101" > </property>
</bean>
</beans>
Now there will be many value files
Contents of Value files will be:
id100={ someValue}
id101={ overidingValue}
Now at run time new bean will be created for each value file. So value files will create one separate bean for each value file by overriding values from value file.
How can i go about developing such framework?
Any pointers?
This is just my very basic idea.
How can i use BeanFactory as mentioned by Alex in this context?
I'd suggest you to use BeanFactory. It can implement any logic your want and get its configuration via PropertyPlaceholderConfigurer

conditional beans using spring

I am trying to write a ValidatorFactory which will give me a validator based on its type
public Validator getNewValidator(ValidatorType type){
switch:
case a : new Validator1();
break;
case b : new Validator2();
break;
}
I want to write using spring xml beans definition
I can use method injection but it will let me create only one object and the method does
not take any arguments.
I don't want to use FactoryBean.. I am just looking whether we can do this using spring xml
bean definition.
you can do conditional bean injection with plain xml. The "ref" attribute can be triggered by property values from a property file and thus create conditional beans depending on property values. This feature is not documented but it works perfect.
<bean id="validatorFactory" class="ValidatorFactory">
<property name="validator" ref="${validatorType}" />
</bean>
<bean id="validatorTypeOne" class="Validator1" lazy-init="true" />
<bean id="validatorTypeTwo" class="Validator2" lazy-init="true" />
And the content of the property file would be:
validatorType=validatorTypeOne
To use the property file in your xml just add this context to the top of your spring config
<context:property-placeholder location="classpath:app.properties" />
For complex cases (more complex than the one exposed), Spring JavaConfig could be your friend.
If you are using annotation (#Autowired, #Qualifier etc) instead of xml, you are not able to make conditional beans work (at least at current version 3). This is due to #Qualifier does not support expression
#Qualifier(value="${validatorType}")
More information is at https://stackoverflow.com/a/7813228/418439
I had an slightly different requirements. In my case I wanted to have encoded password in production but plain text in development. Also, I didn't have access to parent bean parentEncoder. This is how I managed to achieve that:
<bean id="plainTextPassword" class="org.springframework.security.authentication.encoding.PlaintextPasswordEncoder"/>
<bean id="shaPassword" class="org.springframework.security.authentication.encoding.ShaPasswordEncoder">
<constructor-arg type="int" value="256"/>
</bean>
<bean id="parentEncoder" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetSource">
<bean class="org.springframework.aop.target.HotSwappableTargetSource">
<constructor-arg ref="${password.encoding}Password"/>
</bean>
</property>
</bean>
Of course, I defined password.encoding in a property file with possible values as sha or plainText.
You should be able to do this:
<bean id="myValidator" factory-bean="validatorFactory" factory-method="getNewValidator" scope="prototype">
<constructor-arg><ref bean="validatorType"/></constructor-arg>
</bean>
<bean id="validatorType" ... />
Of course, it uses an automatically configured FactoryBean underneath but you avoid any Spring dependency in your code.

Categories

Resources