amqp:outbound-gateway throwing ReplyRequiredException - spring-integration

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>.

Related

mapping jms property to RabbitMQ

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.

How to involve a web service method with 4 String params by Spring integration outbound gateway?

I have setup the gateway and service, but the web method is not a wrapped object but just 4 Strings.
JAXB raised the error
"javax.xml.bind.MarshalException
- with linked exception:
[com.sun.istack.internal.SAXException2: unable to marshal type "java.lang.String" as an element because it is missing an #XmlRootElement annotation]"
public interface WebMethodGateway {
#Gateway
#Payload("#args[0] + #args[1] + #args[2] + #args[3]")
public Response invoke(String arg1, String arg2, String arg3, String arg4);
}
integration.xml
<int-ws:outbound-gateway id="outboundGateway"
request-channel="requestChannel"
reply-channel="responseChannel"
uri="http://localhost:8080/Service?wsdl"
message-sender="messageSender"
marshaller="marshaller"
unmarshaller="marshaller"/>
<bean id="messageSender" class="org.springframework.ws.transport.http.HttpComponentsMessageSender">
<property name="connectionTimeout" value="5000"/>
<property name="readTimeout" value="10000"/>
</bean>
<int:channel id="requestChannel"/>
<int:channel id="responseChannel"/>
<oxm:jaxb2-marshaller id="marshaller" context-path="myProject.ws.bean" />
<int:gateway id="webMethodGateway"
default-request-channel="requestChannel"
default-reply-channel="responseChannel"
service-interface="myProject.ws.gateway.WebMethodGateway" />
First of all it isn't 4 Strings, it is single concatenated String :
#Payload("#args[0] + #args[1] + #args[2] + #args[3]")
Another concern.
<int-ws:outbound-gateway> is for Web Services, SOAP. Therefore XML.
Right JaxB produces an XML for you, but it does that exactly for domain entities, not simple strings.
You can bypass JaxB with String payload, but that really must be an XML representing <soap:body> content.

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.

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.

Using GridGain as a local cache

Has anyone tried to use GridGain as a local cache replacement? With built in eviction and expiration policies, its very attractive.
What is the right way to configure GridGain as a local cache?
EDIT
This is a sample configuration I used to run a simple micro benchmark on the GridGain local cache.
final GridCacheConfiguration cfg = new GridCacheConfiguration();
cfg.setCacheMode(GridCacheMode.LOCAL);
cfg.setSwapEnabled(false);
cfg.setAtomicityMode(GridCacheAtomicityMode.ATOMIC);
cfg.setQueryIndexEnabled(false);
cfg.setBackups(0);
cfg.setStartSize(1000000);
cfg.setName("test");
final GridConfiguration gridConfiguration = new GridConfiguration();
gridConfiguration.setRestEnabled(false);
gridConfiguration.setMarshaller(new GridOptimizedMarshaller());
gridConfiguration.setCacheConfiguration(cfg);
try (final Grid grid = GridGain.start(gridConfiguration)){
final GridCache<String, String> test = grid.cache("test");
final String keyPrefix = "key";
final String valuePrefix = "value";
final LoggingStopWatch stopWatch = new LoggingStopWatch("cacheWrite - GRIDGAIN");
for (int i = 0; i < 1000000; i++) {
test.put(keyPrefix + i, valuePrefix + i);
}
stopWatch.stop();
} catch (GridException e) {
e.printStackTrace();
}
It took around 16 seconds to do 1M synchronous puts (on my Core i7-2640M 2.8GHz laptop). I agree this is too simple a test, but still this is not the performance I was expecting. I was expecting around 1-2 seconds. Do I need to tweak the config to get some more juice out of the cache?
You can definitely configure GridGain as a local cache and take advantage of local transactions, evictions, and expiration policies.
Here is sample spring-based configuration that would do this:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="grid.cfg" class="org.gridgain.grid.GridConfiguration" scope="singleton">
<property name="cacheConfiguration">
<list>
<bean class="org.gridgain.grid.cache.GridCacheConfiguration">
<property name="name" value="myCache"/>
<property name="cacheMode" value="LOCAL"/>
<!-- Eviction policy. -->
<property name="evictionPolicy">
<bean class="org.gridgain.grid.cache.eviction.lru.GridCacheLruEvictionPolicy">
<property name="maxSize" value="10000"/>
</bean>
</property>
</bean>
</list>
</property>
</bean>
</beans>
You can start the above configuration as following:
$GRIDGAIN_HOME/bin/ggstart.bat path/to/config/file
As far as performance, the issue was fixed in GridGain 6.0.3. The code above executes in less than 1 second for me.

Resources