How to get header value in bean method - spring

I have custom bean which i use it in spring integration flow. It has method which takes hashmap as argument and its values are set dynamically. I am trying to set the value from the payload header field(iploc) but i am not able to achieve so. I tried some combination of spel but it not work. Any pointers?
<int:transformer id="ws.transformer"
input-channel="ws.transformer.in" output-channel="ws.transformer.out">
<bean class="com.my.Mybean">
<property name="map">
<map>
<entry key="user">
<value>"admin"</value>
</entry>
<entry key="location">
<value>"headers['iploc']"</value>
</entry>
</map>
</property>
</bean>
</int:transformer>
I can alternatively set the value in Service activator, but i am trying if i achieve this in the SI config itself.

Spring Integration Transformer can consume entire Message (payload and headers) so there is no point in passing header value by property since transformer already has access to all message headers.
Your transformer bean definition should contain only these properties that do not come from the Message being transformed:
<int:transformer id="ws.transformer" input-channel="ws.transformer.in" output-channel="ws.transformer.out">
<bean class="com.my.MyTransformer">
<property name="user" value="admin"/>
</bean>
</int:transformer>
And your transformer method:
#Transformer
OutgoingPayload transform(IncomingPayload payload, #Header("iploc") String iplocHeader) {
return doTransform(...);
}
or just consume entire message:
Message<OutgoingPayload> transform(Message<IncomingPayload> message) {
final String ipLocHeaderValue = message.getHeaders.get("iploc", String.class);
return doTransform(...);
}

The bean defined within a transformer like that will be instantiated at startup time, not each time a message is received. The normal way to handle a requirement like this is to have a stateless bean with a method that accepts the header value upon each invocation.

Related

Spring - how to reference another property within the same bean?

I have the following definition in my configuration:
<bean class="com.project.TimerBean">
<property name="delay" value="30000" />
<property name="interval" value="60000" />
<property name="invokeThis" value="com.project.TargetClass" />
<property name="receiver" value="XYZ" />
<property name="args" value="#{interval}" />
</bean>
I would like to set the value of args to the same value as interval (in this case, 60000) without having to hard-code the value. However, the above snippet doesn't seem to work. How should I change this?
# syntax (Spel Expressions) are supposed to work the way you wrote it. You need to replace
#{interval} to #{beanId.interval}.
For example, if the id of the bean you are creating is timerBean, #{timerBean.interval} is supposed to work. You cannot refer to a property directly even if it is a part of the bean definition.
It only works if the property you are referring to is a part of another bean.
<bean id="beanA" class="org.BeanA">
<property name="prop1" value="1000" />
</bean>
<bean id="beanB" class="org.BeanB">
<property name="prop2" value = "#{beanA.prop1}" />
<property name="prop3" value = "#{beanB.prop2}" />
</bean>
In the above example, prop2 gets initialised from prop1 correctly. But prop3 gets initialised to null.
If you look at AbstractAutowireCapableBeanFactory class and method,
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs)
you can see that all the property values in a single bean definition are looped over and values are parsed. After all the values have been successfully parsed, only then they are set on the bean instance. In the above beanA and beanB example when prop3's value is getting parsed, spring looks for the value of prop2 on beanB which is not yet set and hence returns null.
AFAIK, there is no way around this except the way suggested by #Alex
PS: I am using spring version 4.1.6.RELEASE
Move interval value "60000" to the property file
yourVariableName = 60000
and change to:
<property name="interval" value="${yourVariableName}" />
<property name="args" value="${yourVariableName}" />

How to set default method for Delegate method of MultiActionController ?

I'm using Spring MVC for a web app project and I'm trying to avoid using annotations.
I came across as far as getting MultiActionController and delegate working.
The question is, how do I set the default method in the delegate of a MultiActionController ?
By MultiActionController, I mean something like this
public class TestController1 extends MultiActionController{
public TestController1(){
System.out.println("TestController1 initialising...");
}
}
My xml settings are...
<bean id="multiactionController1" class="test.TestController1">
<property name="delegate" ref="testDelegater1"/>
<property name="methodNameResolver" ref="paramResolver"/>
</bean>
<!-- Delegaters -->
<bean id="testDelegater1" class="test.TestController1Delegator"/>
<!-- param method name resolver -->
<bean id="paramResolver" class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver">
<property name="paramName" value="action"/>
</bean>
<!-- Simple Url Handler Mapping -->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<map>
<entry key="/multiaction1/**" value-ref="multiactionController1"/>
<entry key="/item/**" value-ref="itemController"/>
</map>
</property>
</bean>
So when I send a request like '*/item' , notice it doesn't have an action parameter, instead of giving me an error I would like to have a default method.
Use following implementation of MethodNameResolver, it has defaultMethodName property.
org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver
I was able to solve this by following instructions in this page.
http://www.cwinters.com/blog/2004/02/18/spring_setting_a_default_action_for_multiactioncontroller.html
As far as I've figured, you need to implement your own MethodNameResolver that returns default method name if no method name has been specified.
I Hope this helps : )

Camel: Aggregator doesn't persist Exchange properties

I'm using camel:aggregate backed by jdbc and it seems it doesn't save Exchange properties. For instance, if I configure the following route and the execution is stopped once aggregation has been completed and just before execute camel:to(log) forcing the aggregation to retrieve data from database when restarted, then camel:to(log) won't print the property myProperty
<camel:route id="myRoute">
<camel:from uri="direct:in"/>
<camel:setProperty propertyName="myProperty">
<camel:constant>myPropertyValue</camel:constant>
</camel:setProperty>
<camel:aggregate strategyRef="myStrategy" aggregationRepositoryRef="myAggregationRepo" discardOnCompletionTimeout="true" completionTimeout="86400000" >
<camel:correlationExpression>
<camel:simple>${property.partlastcorrelationkey}</camel:simple>
</camel:correlationExpression>
<camel:completionPredicate>
<camel:simple>${property.partlastcorrelationwaitmore} == false</camel:simple>
</camel:completionPredicate>
<camel:to uri="log:com.test?showAll=true"/>
</camel:aggregate>
</camel:route>
My aggregation repository is configured this way:
<bean id="myAggregationRepo" class="org.apache.camel.processor.aggregate.jdbc.JdbcAggregationRepository" init-method="start" destroy-method="stop">
<property name="transactionManager" ref="transactionManager"/>
<property name="repositoryName" value="PROC_AGG"/>
<property name="dataSource" ref="oracle-ds"/>
<property name="lobHandler">
<bean class="org.springframework.jdbc.support.lob.OracleLobHandler">
<property name="nativeJdbcExtractor">
<bean class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor"/>
</property>
</bean>
</property>
</bean>
How can I save properties when using the Aggregator?
I'll reply myself. As seen on the code JdbcCamelCodec doesn't allow to save properties when backing the Aggregator with a database:
public final class JdbcCamelCodec {
public byte[] marshallExchange(CamelContext camelContext, Exchange exchange) throws IOException {
// use DefaultExchangeHolder to marshal to a serialized object
DefaultExchangeHolder pe = DefaultExchangeHolder.marshal(exchange, false);
// add the aggregated size property as the only property we want to retain
DefaultExchangeHolder.addProperty(pe, Exchange.AGGREGATED_SIZE, exchange.getProperty(Exchange.AGGREGATED_SIZE, Integer.class));
// add the aggregated completed by property to retain
DefaultExchangeHolder.addProperty(pe, Exchange.AGGREGATED_COMPLETED_BY, exchange.getProperty(Exchange.AGGREGATED_COMPLETED_BY, String.class));
// add the aggregated correlation key property to retain
DefaultExchangeHolder.addProperty(pe, Exchange.AGGREGATED_CORRELATION_KEY, exchange.getProperty(Exchange.AGGREGATED_CORRELATION_KEY, String.class));
// persist the from endpoint as well
if (exchange.getFromEndpoint() != null) {
DefaultExchangeHolder.addProperty(pe, "CamelAggregatedFromEndpoint", exchange.getFromEndpoint().getEndpointUri());
}
return encode(pe);
}
Basically, the problem lies on this line where false means: don't save properties.
DefaultExchangeHolder pe = DefaultExchangeHolder.marshal(exchange, false);
The headers and the body are the only ones stored on database.

constructor injection with value provided by another bean

I want to pass value to constructor(type string) with value provided by another bean.
Class BeanOne () {
BeanOne (String message) {
...
}
}
Below declaration will work
<bean id="beanOne"
class="com.abc.BeanOne">
<constructor-arg index="0" type="java.lang.String"
value="Hi There" /> // instead of value="Hi There", i want to use bean property (value="someBean.message")
</bean>
However I want another bean (say BeanTwo) to set value for message property for BeanOne. I tried nested property as given below but it does not work. Also message property is not visible directly in the class & is referred internally by another constructor & then by the method so i cannot use property injection& have to use only constructor injection
You can use the MethodInvokingFactoryBean to get your string value and then inject that into your bean.
<bean id="message" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject"><ref local="someBean"></property>
<property name="targetMethod"><value>getMessage</value></property>
</bean>
<bean id="beanOne" class="com.abc.BeanOne">
<constructor-arg index="0" type="java.lang.String" ref="message"/>
</bean>

BeanInstantiationException:Cannot convert type from java.lang.String to required type --- idref tag

I am new to spring and trying to understand the functionality of the idref tag
My config file is pasted here:
<bean id="AudioSystembean" class="com.springexample.AudioSystem">
<property name="price" value="200"/>
</bean>
<bean id="Vehicle1" class="com.SpringExample.Vehicle1">
<property name="vehicleType" value="CAR" />
<property name="audioSystem" >
<idref bean = "AudioSystembean" />
</property>
</bean>
When I am executing the code I am getting the error as : "BeanInstantiationException:Cannot convert type from java.lang.String to required type :com.SpringExampl.AudioSystem" [I dont remember the exact exception - I have the code at home]
If I use ref instead of idref it is working fine.
I tried google to understand about idref but could not get much information..
What am I doing wrong here?
In a Spring context, <idref> is used to pass the name of the referenced bean, rather than the bean itself (which is what <ref> does). So in your example:
<property name="audioSystem" >
<idref bean = "AudioSystembean" />
</property>
The audioSystem system property will be injected with the name of the AudioSystembean, i.e. the String "AudioSystembean".
This is not what you need here, though, you need the <ref> element, which passes a reference to the bean itself.

Resources