Access application properties within SpEL in Spring xml configuration - java

I'm trying to configure a spring bean based on an application property, my end goal is described in the following pseudo code:
if ${my.config}
<bean id="myBean" class="path.to.MyBeanImplOne" />
else
<bean id="myBean" class="path.to.MyBeanImplTwo" />
end
where my.config is a boolean property.
According to this SpEL guide, #{${my.config} ? 'path.to.MyBeanImplOne' : 'path.to.MyBeanImplTwo'} is a valid expression, so I tried the following configuration:
<bean id="myBean" class="#{${my.config} ? 'path.to.MyBeanImplOne' : 'path.to.MyBeanImplTwo'}" />
but got the following exception:
Expression parsing failed; nested exception is org.springframework.expression.spel.SpelParseException: EL1041E: After parsing a valid expression, there is still more data in the expression: 'lcurly({)'
I can't find documentation for accessing properties in SpEL expressions for xml configuration. Is this supported only in Java configuration?
I've seen a number of proposed solutions to my problem (some of which are in this question). I'd like to not use systemProperties since I feel this sort of configuration should not be specified a run arguments, and I feel the use of profiles is overkill for this particular use case.
Has someone been able to do what I've attempted successfully? Or can someone confirm whether or not the syntax I've tried to use is indeed not supported in xml configuration.

Try
class="#{'${my.config}'.equals('true') ? 'path.to.MyBeanImplOne' : 'path.to.MyBeanImplTwo'}"
EDIT
This works for me...
<bean id="foo" class="#{'${my.config}'.equals('true') ? 'java.lang.Integer' : 'java.lang.String'}">
<constructor-arg value="1" />
</bean>

You cannot use SpEL in application.properties file unfortunately.
Documentation
SpEL expressions from application property files are not processed at time of parsing these files and populating the environment. However, it is possible to write a SpEL expression in #Value. If the value of a property from an application property file is a SpEL expression, it will be evaluated when consumed through #Value.

Related

Spring Bean Constructor load From Other bean using List from Constants

I have a class bean with a constructor like this
public Test(final Integer c01,final List<Students>c02){this.c01 = c01;this.c02 = c02;return;}
The students comes from another bean which has a method which takes care of retrieving from Database i have a bean declaration like this
<constructor-arg index="0" type="java.lang.Integer" value='13'/>
<constructor-arg index="1" type="java.util.List" value="#{myDAO.loadStudents(#{T(java.util.Arrays).asList(T(Constants).HOLIDAY,T(Constants).OUT_OF_OFFICE,T(Constants).SICK_LEAVE)})}"/>
The method has a signature like this
public List<Student>loadStudents(final List<String>filters){}
I will try to explain i need to populate the 2 index of the constructor with a List which i need to retrieve from DB from another bean which is pass a List which i have a Constants in a static manner of course this could be easy solved using this signature only Java
new Test(13,Arrays.asList(Constants.HOLIDAY,Constants.OUT_OF_OFFICE,Constant.SICK_LEAVE));
But when i have my XML Spring configuration file a error is thrown something like
Unexpected token. Expected 'identifier' but was 'lcurly({)'
What's is wrong with this code i am not very good using spring
<constructor-arg index="1" type="java.util.List" value="#{myDAO.loadStudents(#{T(java.util.Arrays).asList(T(Constants).HOLIDAY,T(Constants).OUT_OF_OFFICE,T(Constants).SICK_LEAVE)})}"/>
Your SpEL expression is too complex :-)
I would suggest you use Spring Java config and write out what you are trying to do in plain Java code.
As an aside, I have (strong) architectural doubts about reading a database from your Spring configuration. But that's not what the question is about, so I'll try to ignore that :-)

java spring context:property-placeholder - set properties paths from placeholder

<context:property-placeholder
location="a.properties,b.properties"
ignore-unresolvable="true"/>
result: both properties file are loaded
<context:property-placeholder
location="${properties_location}"
ignore-unresolvable="true"/>
where properties_location is "a.properties,b.properties"
result: Exception in thread "main" org.springframework.beans.factory.BeanInitializationException: Could not load properties; nested exception is java.io.FileNotFoundException: class path resource [a.properties,b.properties] cannot be opened because it does not exist
edit: ${properties_location} is set the following way:
System.getProperties().setProperty("properties_location", "a.properties,b.properties");
ApplicationContext ctx = new GenericXmlApplicationContext("context.xml");
...
How can I initialize my application the 2nd way? to have all the properties file's path defined in a placeholder.
You have to change this to:
<context:property-placeholder
location="classpath:a.properties,
classpath:b.properties"
ignore-unresolvable="true"/>
From the source of the parser for the property-placeholder element.
String location = element.getAttribute("location");
if (StringUtils.hasLength(location)) {
String[] locations = StringUtils.commaDelimitedListToStringArray(location);
builder.addPropertyValue("locations", locations);
}
First the location is retrieved, if that has a value it is converted to a String[]. Springs conversion service takes care of replacing any placeholders in the String[]. But at that moment the properties_location placeholder is just a single element in the array and that gets resolved to a.properties,b.properties without further processing.
So at the moment this isn't possible with placeholders I'm afraid.
One thing that might work is using SpEL if it is always going to be a system property you can use #{systemProperties['properties_location']} to resolve the value. That should be resolved before anything else.
You cant use a property placeholder as a value in a placeholder placeholder resolver. Its like saying, "hey, resolve the placeholder for the location of the all the properties, and then you can start resolving properties!".
Logically it just dosent make sense. I was experimenting with spring property placeholder resolution recently, and stumbled upon this same question. I attempted to use two property placeholder configurers, one to resolve the location of the properties for the second, and the second to resolve the rest of the properties. Of course this dosent work due to the way in which spring initialises its beans.
Initialise bean post processors
Construct them
Construct all other beans
Since the property placeholder configurer is a bean post processor, if you have more than one of them, they get initialised and constructed at the same time, so know nothing of each others properties at construction
Edit
Given that the property location is a system property you could have:
System.getProperties().setProperty("properties_location_a", "classpath:/a.properties");
System.getProperties().setProperty("properties_location_b", "classpath:/b.properties");
And then in your spring content.xml:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="locations">
<list>
<value>${properties_location_a}</value>
<value>${properties_location_b}</value>
</list>
</property>
</bean>

Setting a Spring bean class name using a SpEL expression and PropertyPlaceHolder

UPDATED: Resolution summary as of 12/9/2016
According to #altazar's answer below, this is now possible as of Spring 4.2!
Old resolution summary as of 3/29/2012
As of this date, Spring SpEL could not execute inside a class attribute of a <bean>.
Original question:
I'm trying to implement a dynamic class attribute for a Spring bean, ultimately set using a combination of a PropertyPlaceHolder property and a SpEL expression. The purpose is to choose either a production or debug version of a class to instantiate. It is not working and I'm wondering if it is possible to achieve.
So far, I have the following:
Flat properties file:
is.debug.mode=false
Spring XML config:
<bean id="example"
class="#{ ${is.debug.mode} ?
com.springtest.ExampleDebug :
com.springtest.ExampleProd}"
/>
Spring bootstrap Java code:
// Get basic ApplicationContext - DO NOT REFRESH
FileSystemXmlApplicationContext applicationContext = new
FileSystemXmlApplicationContext
(new String[] {pathSpringConfig}, false);
// Load properties
ResourceLoader resourceLoader = new DefaultResourceLoader ();
Resource resource = resourceLoader.getResource("file:" + pathProperties);
Properties properties = new Properties();
properties.load(resource.getInputStream());
// Link to ApplicationContext
PropertyPlaceholderConfigurer propertyConfigurer =
new PropertyPlaceholderConfigurer() ;
propertyConfigurer.setProperties(properties) ;
applicationContext.addBeanFactoryPostProcessor(propertyConfigurer);
// Refresh - load beans
applicationContext.refresh();
// Done
Example example = (Example) applicationContext.getBean("example");
Error message (with a lot of whitespace removed for clarity):
Caused by: java.lang.ClassNotFoundException:
#{ true ? com.springtest.ExampleDebug : com.springtest.ExampleProd}
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
. . .
As you can see by the "true" in the message, the is.debug.mode property is successfully loaded and substituted. But something else is going wrong. Is it my bootstrap sequence in the Java? Or the SPeL syntax in the XML? Or a different issue?
BTW I am aware of the new 3.1 profiles feature, but I would like to do this via SPeL for a variety of reasons. Also I realize I'm using a filesystem-based context and paths - I have reasons for that too.
You could accomplish what you intend with a factoryBean:
<bean id="example" class="MyFactoryBean">
<property name="class" value="#{ ${is.debug.mode} ? com.springtest.ExampleDebug : com.springtest.ExampleProd}"/>
</bean>
where MyFactoryBean is a trivial FactoryBean implementation returning instance of indicated class.
You can do this.
debug.class=com.springtest.ExampleDebug
#debug.class=com.springtest.ExampleProd
and then
<bean id="example" class="${debug.class}"/>
From Spring 4.2 it is possible to use SpEl in class attribute so following works well:
<bean id="example"
class="#{T(java.lang.Boolean).parseBoolean('${is.debug.mode:false}') ?
'com.springtest.ExampleDebug' :
'com.springtest.ExampleProd'}"/>
In my case placeholder ${is.debug.mode:false} was not work, so I parse it explicitly.

Making a spring bean dynamically refer to two classes based on a system property in Spring 3.0

I am trying to achieve the below functionality using Spring but have not succeeded till now.
Create a spring bean "testBean" in XML file and dynamically point it to class "A" or "B" depending on whether some system property "C" has been set or not. I want to handle this at the XML configuration file level itself. The rest of the application should be able to use the bean "testBean" seamlessly.
Please let me know how can this be done using Spring? Let me know if any other details are required.
Thanks in advance.
Use bean profiles to achieve this
Also See
is-there-any-way-to-enable-or-disable-the-spring-bean-definition-in-applicationcation
You could use the expression language to configure your testBean like this (not tested):
<bean id="testBean" class="com.test.TestBean">
<property name="pointer" value="#{ systemProperties['C'] != null ? 'com.test.A' : 'com.test.B' }"/>
</bean>
See the documentation for further reference.
Relevant parts:
6.4.1 XML based configuration
6.5.13 Ternary Operator (If-Then-Else)

Setting sub-property of a Spring bean

Is it possible to set a Spring bean sub-property using dot notation? For instance:
<bean name="rememberMe" class="com.mydomain.security.RememberMeManager">
<property name="cookie.domain" value=".${webRoot}"/>
</bean>
Or do I need to also create an intermediary bean for the Cookie object stored in RememberMeManager.getCookie()?
My objective is to set cookies set by my site to ".mydomain.com" instead of "mydomain.com". I have a properties file with webRoot=mydomain.com in it.
Spring's PropertyPlaceholder will have no problem with replacing placeholders that are substrings of the property/value, such as ".${webRoot}", and according to the documentation, it will also fallback to the system properties if no property in the properties file is found.
Did you try this? Does it work or not?

Categories

Resources