Spring putting dynamically generated values into placeholders - java

I am new to Spring. I now understand how to use placeholders to read values from a properties file:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:properties.txt"/>
</bean>
<int-mqtt:outbound-channel-adapter id="mqtt-publish"
client-id="${clientID}"
client-factory="clientFactory"
auto-startup="true"
url="${url}"
default-qos="${qos}"
default-retained="${retain}"
default-topic="${topic}" />
Everything works fine with the code above... But... Is it possible for instance to replace the clientID by something generated at runtime (or from user input) instead of statically reading it from a properties file?

By runtime, do you mean dynamically for each message?
In that case, no, because the clientId is used while establishing the connection, which is done once (or when the connection to the server is lost).
If you mean to provide a dynamic value programmatically when the application context initializes, then, yes, the Spring Expression Language is the solution.
For example, #{myBean.myProperty} will call the getMyProperty() method on a bean myBean and #{myBean.someMethod()} will invoke someMethod().
Also see the dynamic-ftp sample, which uses placeholders at runtime by creating a new outbound adapter on demand using property placeholders, in a child application context.

Related

How Spring loads properties file in memory?

Am really new to spring, so please don't get angry and bear with me for a while.
I'm trying to understand how application.properties or any other external config file values gets associated with the places where config file's keys (value="${log4j.configuration}") are mentioned to consume their values from file itself.
For example below is spring bean xml file:
<bean id = "propertiesToBeTaken"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath*:application.properties</value>
<value>classpath*:*keys.properties</value>
</list>
</property>
</bean>
<bean id="log4jLoader" class="my.loader.Log4jLoader">
<property name="log4jCongif" value="${log4j.configuration}" />
</bean>
As you can see, propertiesToBeTaken is an instance of class org.springframework.beans.factory.config.PropertyPlaceholderConfigurer.
And in second case ,log4jLoader is an instance of class my.loader.Log4jLoader, where log4j.configuration key's value is assigned to Log4jLoader class's instance variable log4jCongif.
My questions are below:
There is no locations variable in class PropertyPlaceholderConfigurer, then what is locations here and from where it came ? How can I relate locations to class's instance propertiesToBeTaken?
If lets say, application.properties key value pairs gets loaded into memory, then who or I mean which method loads that ? And even if it gets loaded then how those key value pairs of application.properties file are accessible to Log4jLoader's instance variables...?
Last but I think it could very stupid question, In class log4jLoader class, there is a setLog4jConfig(String log4jpropertiesLocation), but I really cant see who is calling this method. Just correct me here, Its not true, that In spring what ever the beans of object we have created, calls automatically class's instance methods. RIGHT ? I need to search more in code. CORRECT ?
Please put some light on my doubts here. Read about this alot on spring docs and online as well, but could not understand. Really want to understand how it's happening.
Thanks
Answers to your questions:
locations in bean config XML is referring to the setLocations method of PropertiesLoaderSupport class which Set locations of properties files to be loaded. PropertiesLoaderSupport is parent class of PropertyPlaceholderConfigurer in multi-level inheritance. See Official Document
When PropertyPlaceholderConfigurer gets instantiated by Spring IoC, these properties are set in the bean, now any other bean in the config XML asks for property values using ${key.name}, spring injects the value from the bean having value for this key. In your case Log4jLoader requires ${log4j.configuration} so it's value will be injected by PropertyPlaceholderConfigurer or it's any parent class.
setLog4jConfig spring calls this method when it has to give value to one of the property of the class Log4jLoader. In your case because of <property name="log4jCongif" value="${log4j.configuration}" /> configuration spring will call setter method of the property log4jCongif to inject the value. Yes spring can call instance methods (specially setter) as per the configuration.

Syntax for specifying spring xml files in ClassPathXmlApplicationContext

I am trying to find out the syntax for specifying the spring's XML file in the constructor of ClassPathXmlApplicationContext. By syntax I don't mean the method signature but the actual string
For example the following three work.
ApplicationContext context = new ClassPathXmlApplicationContext("com/anshbansal/alarm/alarm.xml");
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:alarm.xml");
ApplicationContext context = new ClassPathXmlApplicationContext("alarm.xml");
I have googled and tried to go through the Spring 3.1.0 source code also. But I got stuck after doResolvePlaceholders method in org.springframework.core.env.AbstractPropertyResolver class of Spring. Specificaly I was not able to understand how placeholders are resolving to the path.
Can anyone share what is the syntax for the string to specify the xml file?
EDIT
I mean the syntax to specify path to spring xml file like in the constructor. I do not mean the syntax of the xml file itself.
Ok, I understand the question now :-). PropertyResolver is used only to put environment values (or property file values) into spring XML file, i.e.:
<context:property-placeholder location="file:///some/path/file.properties"/>
and then resolving them inside this spring xml, i.e.:
<bean id="mailInviteMessage" class="org.springframework.mail.SimpleMailMessage">
<property name="from" value="${mail.from}"/>
<property name="subject" value="${mail.subject}"/>
</bean>
Putting spring XML in the classpath and naming in standard convention (i.e. beans.xml) is preferable. However, you may configure it putting a parameter to java invocation, i.e.
java -Dmy.parameter=/path/to/beans.xml ...
and then loading it manually:
Context context = new FileSystemXmlApplicationContext(System.getProperty("my.parameter"));

Spring context:property-placeholder for a boolean value

I am working on an application where I have two classes both implementing a common interface. So in time of bean declaration, I am going to mark one of them primary in my app-context.xml file. I can achieve this by simply declaring the primary bean like this:
<bean id="oracleImpl" class="com.me.dao.OracleImpl" primary="true">
</bean>
Now I don't want to hard code which of the beans is going to be the primary bean, rather want to read the true/false value from a properties file. So I went like this:
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="oracleImpl" class="com.me.dao.OracleImpl" primary="${oracle.primary}">
</bean>
<bean id="pgsqlImpl" class="com.me.dao.PgsqlImpl" primary="${pgsql.primary}">
</bean>
The values of oracle.primary and pgsql.primary are defined in the file jdbc.properties along with other jdbc (non-boolean) properties.
But it doesn't work and says, "'${oracle.primary}' is not a valid value for 'boolean'"
I have a feeling it is something to do with the xsd validators. Browsing through this site and google gave me this much idea, but got no real solution. Can any body help?
This will not work.
As of 3.2.5.RELEASE only the following bean definition elemets support property placeholder:
parent name
bean class name
factory bean name
factory method name
scope
property values
indexed constructor arguments
generic constructor arguments
See the BeanDefinitionVisitor's visitBeanDefinition method for the details. This method is used by the PlaceholderConfigurerSupport.
I would recommend you to create a feature request in the spring issue management system.
PS: if you create an issue please add a comment to the issues url.

Setting Values of enum via Spring Integration

I have a system where I have an enum of Shops for example. These shows each have their own ShopCommand property (some of which share the same type of command class). from a method in the command class I then want to call send on a Spring Integration gateway. Where I'm confused is how to actually insantiate this gateway in spring. Ideally what I want is to construct the enum via XML configuration with command property being created also in spring, which has the property outGateway set via Spring. I'm not sure if I've made myself very clear with this descrition, if clarification is needed then just ask!
I think this is what you are asking for:
Say I have an enum for ShopType
public enum ShopType {
GROCERY, DEPARTMENT, MALL;
}
Then I have some Store bean that I want to setup via spring configuration. You can instantiate and use the enum like this:
<bean id="DEPTARTMENT_STORE" class="my.package.ShopType" factory-method="valueOf">
<constructor-arg value="DEPARTMENT"/>
</bean>
<bean id="searsStore" class="my.package.Store">
<property name="shopType" ref="DEPTARTMENT_STORE"/>
</bean>
The factory-method points to a static method that is used to create the object. So you can use the enum's method "valueOf" as a factory method.

In Spring can you alter property placeholder values using a BeanDefinitionDecorator?

I have a BeanDefinitionDecorator that makes modifications to properties that a user would set on a bean. It works fine; except if the bean is using placeholders. I am trying to find a strategy to modify those values while still have access to the original value at runtime. An example of what this would look like in XML:
<bean id="bean">
<property name="jdbcUrl" value="${jdbc.url}" />
<d:spyDecorator />
</bean>
I know that user would be writing the jdbcUrl property as "jdbc:myDatabase". What I want to do is change their property to "jdbc:spy:myDatabase". This is easy if they are just using string literals for the property value, but if they are using property placeholders I am not sure how to change the value -- because I need the original value in order to supply the new value. They key is to keep the property rewriting transparent to the user.
Are there any possible solutions for this?
I think your namespace handler can register a BeanFactoryPostProcessor (implementing Orderer with order = Integer.MAX_VALUE to be the last post processor applied). Then your BeanDefinitionDecorator will register the beans being decorated for processing with that post processor (implement it in the post processor somehow), and post processor will apply the actual property modification to that beans.
You can use PropertyPlaceholderConfigurer to substitute property values for placeholders in bean properties, aliases, and other places. Note that the replacements happen AFTER the bean definitions have been loaded, so this mechanism does not apply to <import> elements
For example:
...
<bean id="ppc"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:build.properties</value>
<value>classpath:default-emmet-substitution.properties</value>
<value>classpath:default-danno-substitution.properties</value>
<value>classpath:default-dannotate-substitution.properties</value>
<value>classpath:substitution.properties</value>
</list>
</property>
</bean>
...
For more information refer to this section of the Spring Framework docs.
EDIT - I guess from your comment you are already familiar with how placeholder replacement works, and are using PropertyPlaceholderConfigurer to do the replacements. So now you need to choose between these strategies, or some combination:
Do the placeholder replacements yourself in your custom BeanDefinitionDecorator. That would work, though you'd be duplicating a lot of code.
Have the custom BeanDefinitionDecorator modify the placeholder names to different ones that will pull in different values; e.g. "${jdbc.url}" becomes "${spy.jdbc.url}".
Extend the PropertyPlaceholderConfigurer class to modify the substituted property values; i.e. override convertProperty or convertProperties. That has the potential problem that all placeholders will get the modified values ... not just the ones in beans that you have decorated.
Create a new PropertyResourceConfigurer class to substitute different property values depending on the context. Essentially, the processProperties needs to work like the method does in a PropertyPlaceholderConfigurer, but do something different if it sees bean properties or whatever that tell it to do the "spy" substitution.
A combination of 2) and 3) looks the most promising.

Categories