mapping jms property to RabbitMQ - spring-integration

We use spring integration to communicate components using MQSeries and RabbitMQ.
With MQseries there are a lot of JMS properties that are used by customers.
We would like to add an intermediate function between the input and the output which makes a mapping of these properties (getting the jms_priority from mqseries and put in the output message the property "priority" for RabbitMQ).
Without the specific properties JMS it works very well.
Below the code we use:
<int:channel id="input" ></int:channel>
<int-jms:message-driven-channel-adapter id="jmsIn" connection-factory="connectionFactoryCaching" channel="input" ...>
<int:service-activator id="sa1" input-channel="input" ref="serviceBean" output-channel="output"/>
<bean id="serviceBean" class="com.poc.ServiceActivator"> </bean>
<int:channel id="output" ></int:channel>
<int-amqp:outbound-channel-adapter channel="output" .../>
import org.springframework.amqp.core.MessageProperties;
The ServiceActivator code:
public class ServiceActivator {
public org.springframework.amqp.core.Message convertMessageMQSeriesToRabbit (Message obj){
MessageProperties messageProperties = new MessageProperties();
try {
messageProperties.setCorrelationId(obj.getJMSCorrelationID());
System.out.println("getJMSReplyTo "+obj.getJMSReplyTo());
System.out.println("getJMSPriority "+obj.getJMSPriority());
messageProperties.setPriority(obj.getJMSPriority());
System.out.println("getJMSType "+obj.getJMSType());
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Is this the right way to do it? In the convertMessageMQSeriesToRabbit method, what type of return object should be passed to the outbound? org.springframework.amqp.core.Message ?
In this method we want to fill all RabbitMQmessage properties (content_type, content_encoding, priority, correlation_id, reply_to, expiration, message_id, timestamp, type, user_id, app_id, cluster_id) with corresponding JMS value.
We also need to do it the other way (inboud rabbitMQ => outbound mqseries)

No, Spring Integration uses its own abstraction org.springframework.messaging.Message<?>. You don't need to interact with it directly, simply use a header-enricher.
See JmsHeaders and AmqpHeaders for the constants.
<header-enricher id="headerEnricherWithShouldSkipNullsFalse" input-channel="fromJms" output-channel="toRabbit">
<header name="amqp_correlationId" expression="headers.jms_correlationId"/>
...
</header-enricher>
For AMQP priority, the standard IntegrationMessageHeaderAccessor.PRIORITY value is used.
Also see the DefaultAmqpHeaderMapper and DefaultJmsHeaderMapper to see how the adapters map from Message<?> to/from RabbitMQ and JMS messageds.

Related

how to pass parameter #Payload

i have code
<int:channel id="partnerConfigChannel" />
<int:gateway id="partnerService" service-interface="org.service.PartnerService"
default-request-timeout="5000" default-reply-timeout="5000">
<int:method name="findConfig" request-channel="partnerConfigChannel" />
</int:gateway>
<int-jpa:retrieving-outbound-gateway entity-manager="entityManager"
request-channel="partnerConfigChannel"
jpa-query="select q from QueueConfiguration q where q.partnerId = :partnerId">
<int-jpa:parameter name="partnerId" expression="payload['partnerId']" />
</int-jpa:retrieving-outbound-gateway>
and java interface
public interface PartnerService {
#Payload("partnerId")
List<QueueConfiguration> findConfig();
}
i am calling it
List<QueueConfiguration> qc= partnerService.findConfig();
but i am getting exception
EL1007E:(pos 0): Property or field 'partnerId' cannot be found on null
please tell me how can i pass payload . i tried by passing Message object with a map , string but same error .
please tell me how can i pass payload in such case.
#Payload("partnerId")
At this point, there is no object for the SpEL expression to be evaluated against.
It either needs to be a literal
#Payload("'partnerId'")
Or refer to some other bean.
Further, on your adapter, you are expecting the payload to be a map with key partnerId.
expression="payload['partnerId']"
So this won't work.
If you want to pass a variable, you should do something like this...
public interface PartnerService {
List<QueueConfiguration> findConfig(MyClass param);
Where MyClass has some property 'partnerId'.
or
List<QueueConfiguration> findConfig(String partnerId);
and
expression="payload"
I suggest you do some more reading.
i modified my code
public interface PartnerService {
List<QueueConfiguration> findConfig(#Payload Message msg);
}
and the call it like
Map msgMap=new HashMap();
msgMap.put("partnerId", partnerId);
Message msg=MessageBuilder.withPayload(msgMap).build();
List<QueueConfiguration> qc= partnerService.findConfig(msg);
and it is working fine.

amqp:outbound-gateway throwing ReplyRequiredException

I am using int-amqp:outbound-gateway to create a message in Rabbit. The message is published but my flow stops with ReplyRequiredException
setPublisherReturns and setPublisherConfirms are set on the CachingConnectionFactory
when I set requires-reply to 'false' the thread is frozen and doesn't continue
Code:
<rabbit:template id="amqpTemplateCore" connection-factory="connectionFactoryCore" />
<bean id="connectionFactoryCore" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject"><ref bean="rabbitConfiguration" /></property>
<property name="targetMethod"><value>connectionFactory</value></property>
</bean>
<int-amqp:outbound-gateway
request-channel="requestIn"
reply-channel="requestOut"
amqp-template="amqpTemplateCore"
exchange-name="CDS"
routing-key="keyA">
</int-amqp:outbound-gateway>
bean:
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory =
new CachingConnectionFactory(host);
connectionFactory.setVirtualHost(virtualhost);
connectionFactory.setPort(port);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setPublisherReturns(true);
connectionFactory.setPublisherConfirms(true);
return (ConnectionFactory)connectionFactory;
}
An outbound-gateway is for request/reply scenario. When you send something to an external system and wait for reply from there.
If your logic is one-way, just to send and forget, consider to use <int-amqp:outbound-channel-adapter>.

File Poller FileListFilter cast exception

Doing exploratory work before I drink the kool-aid.
I am trying to create a simple inbound channel adapter to monitor a directory for new ZIP files.
In order to deal with the ever-present "is it complete?" question, I am trying to adapt the example posted here to incorporate a FileListFilter which checks the modified time of the file.
However, I am getting the following exception:
a boolean result is requiredclass java.util.ArrayList is not assignable to class java.lang.Boolean
at org.springframework.util.Assert.isAssignable(Assert.java:368)
at org.springframework.integration.filter.AbstractMessageProcessingSelector.accept(AbstractMessageProcessingSelector.java:61)
at org.springframework.integration.filter.MessageFilter.handleRequestMessage(MessageFilter.java:103)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:134)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:73)
I had this working well with a simple router based on the file extension, but when I replaced it with this filter it fell apart. Seems the actual list of files is what the Assert is trying to cast to Boolean.
Is it not possible to have a filter wired between an inbound and an outbound adapter? Or must I do the file move to the destination myself in the filter? (The way it is done in the linked example)
Here is the config:
<int-file:inbound-channel-adapter id="filePoller" directory="file:input" channel="filesChannel" filename-pattern="*.zip">
<int:poller fixed-rate="2000" max-messages-per-poll="10" />
</int-file:inbound-channel-adapter>
<int:filter input-channel="filesChannel" ref="lastModifiedFileFilter" output-channel="zipFilesOut"/>
<bean id="lastModifiedFileFilter" class="FileFilterOnLastModifiedTime">
<property name="timeDifference" value="10000"/>
</bean>
<int-file:outbound-channel-adapter id="zipFilesOut" directory="file:target/output/zip" delete-source-files="true" />
Here is the filter:
import java.io.File;
import org.springframework.integration.file.filters.AbstractFileListFilter;
public class FileFilterOnLastModifiedTime extends AbstractFileListFilter<File> {
Long timeDifference = 1000L;
#Override
protected boolean accept(File file) {
long lastModified = file.lastModified();
long currentTime = System.currentTimeMillis();
return (currentTime - lastModified) > timeDifference ;
}
public void setTimeDifference(Long timeDifference) {
this.timeDifference = timeDifference;
}
}
Your FileFilterOnLastModifiedTime bean should be provided to the inbound adapter using the filter attribute.
<int-file:inbound-channel-adapter id="filePoller" directory="file:input" channel="zipFilesOut" filename-pattern="*.zip"
filter="lastModifiedFileFilter">
<int:poller fixed-rate="2000" max-messages-per-poll="10" />
</int-file:inbound-channel-adapter>
An inline <filter/> element is a simple POJO that takes some argument and returns a boolean.
Since you are providing an AbstractFileListFilter, the framework is trying to invoke filterFiles which takes an array and returns a List, not a boolean.

Why is ExecutorChannel.onInit() resetting the dispatcher?

I have a simple, working Spring Integration application the moves messages from an inbound RabbitMQ gateway, through a handler chain and into a MongoDB database. When I changed from a direct channel to an executor channel, I started getting subscriber errors. Watching things in the debugger I saw that after I set up the ExecutorChannel bean, the onInit() method gets triggered and resets everything to default values. I cannot figure out why the code would be structured to do this? I looked at DirectChannel.onInit() and it only modifies things if values have not previously been set. Any ideas? I am using Spring Integration 4.1.2.
// from
#Bean
DirectChannel uploadChannel( MessageHandlerChain uploadMessageHandlerChain ) {
def bean = new DirectChannel()
bean.subscribe( uploadMessageHandlerChain )
bean
}
// to
#Bean
ExecutorChannel uploadChannel( MessageHandlerChain uploadMessageHandlerChain ) {
def bean = new ExecutorChannel( Executors.newCachedThreadPool() )
bean.subscribe( uploadMessageHandlerChain )
bean
}
org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers
When you create channels using XML application context, you first define the channel and after you use it in an EIP pattern:
<int:channel id="directChannel"/>
<int:service-activator input-channel="directChannel"/>
This allows in my opinion a better separation of concern. Same pattern must be used with java configuration, first declare your channel then declare MessageHandlerChain and subscribe to the channel
#Bean
ExecutorChannel uploadChannel() {
def bean = new ExecutorChannel( Executors.newCachedThreadPool() )
bean
}
#Bean
MessageHandlerChain uploadMessageHandlerChain(){
def uploadMessageHandlerChain = new MessageHandlerChain()
uploadChannel().subscribe(uploadMessageHandlerChain)
uploadMessageHandlerChain
}

Passing 2/n-number of (multiple) parameters to 'int-jdbc:stored-proc-outbound-gateway'

Hi guys i need help here :)
Previously when I tried to pass only one variable, it was working fine. Now as soon as I am trying to pass 2/n-number of parameters by using Spring Integration, getting following 'payload' exceptions, which I am not clear about.
Exception I am getting is as follows:
[2014-10-31 12:12:43,943][WARN ]GatewayProxyFactoryBean$MethodInvocationGateway.doSendAndReceive: failure occurred in gateway sendAndReceive
org.springframework.messaging.converter.MessageConversionException: failed to convert object to Message
at org.springframework.integration.support.converter.SimpleMessageConverter.toMessage(SimpleMessageConverter.java:85)
at org.springframework.messaging.core.AbstractMessagingTemplate.convertSendAndReceive(AbstractMessagingTemplate.java:112)
at org.springframework.messaging.core.AbstractMessagingTemplate.convertSendAndReceive(AbstractMessagingTemplate.java:103)
...
Caused by: org.springframework.messaging.MessagingException: At most one parameter (or expression via method-level #Payload) may be mapped to the payload or Message. Found more than one on method [public abstract java.util.List com.dao.PersonalinfoDao.queryExecute(java.lang.String,java.lang.String)]
at org.springframework.integration.gateway.GatewayMethodInboundMessageMapper.throwExceptionForMultipleMessageOrPayloadParameters(GatewayMethodInboundMessageMapper.java:235)
at org.springframework.integration.gateway.GatewayMethodInboundMessageMapper.access$400(GatewayMethodInboundMessageMapper.java:77)
at org.springframework.integration.gateway.GatewayMethodInboundMessageMapper$DefaultMethodArgsMessageMapper.toMessage(GatewayMethodInboundMessageMapper.java:337)
...
Bellow showing what I am doing for passing 2 parameters:
In my PersonalinfoDao.java file I have this:
public interface PersonalinfoDao {
/** Method to call a SQL query using spring integration mechanism */
public List<PersonalInfo> queryExecute(String firstname, String lastname);
}
In my PersonalinfoService.java file I have this:
public class PersonalinfoService {
#Autowired
private PersonalinfoDao personalinfoDao;
public List<PersonalInfo> dbConnect(String firstname, String lastname) {
List<PersonalInfo> personalinfoList = personalinfoDao.queryExecute(firstname, lastname);
return personalinfoList;
}
}
In Gateway definition file I have the following:
<!-- Mapper Declarations -->
<bean id="personalinfoMapper" class="com.support.PersonalinfoMapper"/>
<!-- Service Inheritance -->
<bean id="personalinfoService" class="com.service.PersonalinfoService"/>
<!-- Channels = For calling DAO interface methods in Spring Integration Mechanism one has to create request & response channels -->
<int:channel id="procedureRequestChannel"/>
<!-- Gateway = DAO Interface Method Mapped to Request & Response Channels -->
<int:gateway id="gateway_personalinfo" default-request-timeout="5000"
default-reply-timeout="5000"
service-interface="com.dao.PersonalinfoDao">
<int:method name="queryExecute" request-channel="procedureRequestChannel" />
</int:gateway>
<!-- Stored Procedure Outbound-Gateway = To call a database stored procedure -->
<int-jdbc:stored-proc-outbound-gateway id="outbound-gateway-storedproc-personalinfo"
request-channel="procedureRequestChannel"
data-source="dataSource"
stored-procedure-name="pkg_personalinfo_spring.proc_personalinfo_spring"
expect-single-result="true"
ignore-column-meta-data="true">
<!-- Parameter Definitions -->
<int-jdbc:sql-parameter-definition name="firstname" direction="IN"/>
<int-jdbc:sql-parameter-definition name="lastname" direction="IN"/>
<int-jdbc:sql-parameter-definition name="get_ResultSet" type="#{T(oracle.jdbc.OracleTypes).CURSOR}" direction="OUT"/>
<!-- Parameter Mappings Before Passing & Receiving -->
<int-jdbc:parameter name="firstname" expression="payload"/>
<int-jdbc:parameter name="lastname" expression="payload"/>
<int-jdbc:returning-resultset name="get_ResultSet" row-mapper="com.support.PersonalinfoMapper"/>
</int-jdbc:stored-proc-outbound-gateway>
I know that i am doing something wrong in this above gateway definition specially when using expression="payload"...because for any given Getway i can use only one payload. But as I am not clear how to do that by using Map/Array/List, can any one plz help me to resolve this?
Thank u so much :)
Probably the simplest way is to use a #Payload annotation:
public interface PersonalinfoDao {
/** Method to call a SQL query using spring integration mechanism */
#Payload("#args")
public List<PersonalInfo> queryExecute(String firstname, String lastname);
}
Or use a payload-expression="#args" in the XML declaration of the <gateway/>'s method.
The framework will then make the payload an Object[] and you can use payload[0], payload[1] etc in your expressions.

Resources