I have a http gateway call that's occasionally returning 503 errors. I'd like to configure retry advice around that call, but I don't want to do it for every error, just the 503s.
<int-http:outbound-gateway ... errorHandler="...">
<int-http:request-handler-advice-chain>
<int:retry-advice max-attempts="3" />
</int-http:request-handler-advice-chain>
</int-http:outbound-gateway>
I already have a custom error handler configured that filters statuses (ex: 404) that I don't want to treat as errors, but I don't see an obvious way to control how the advice is applied based on what I can do in the error handler. This question deals with the same issue, but the answer doesn't explain how to control the advice behavior or reissue the request at the error handler level. Is there a specific exception type to throw?
edit: Example based on answer:
<bean id="spelParser" class="org.springframework.expression.spel.standard.SpelExpressionParser" />
<int-http:outbound-gateway ...>
<int-http:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice">
<property name="retryTemplate">
<bean class="org.springframework.retry.support.RetryTemplate">
<property name="retryPolicy">
<bean class="org.springframework.retry.policy.ExpressionRetryPolicy">
<constructor-arg index="0" type="org.springframework.expression.Expression" value="#{spelParser.parseExpression('cause.statusCode.value() == 503')}" />
<property name="maxAttempts" value="3" />
</bean>
</property>
<property name="backOffPolicy">
<bean class="org.springframework.retry.backoff.ExponentialBackOffPolicy">
<property name="initialInterval" value="1000" />
<property name="multiplier" value="2" />
</bean>
</property>
</bean>
</property>
</bean>
</int-http:request-handler-advice-chain>
</int-http:outbound-gateway>
Well, for the case when you have a HttpServerErrorException but would like to distinguish it by the statusCode from others and don't retry, I would suggest to take a look into the:
* Subclass of {#link SimpleRetryPolicy} that delegates to super.canRetry() and,
* if true, further evaluates an expression against the last thrown exception.
*
* #author Gary Russell
* #since 1.2
*
*/
#SuppressWarnings("serial")
public class ExpressionRetryPolicy extends SimpleRetryPolicy implements BeanFactoryAware {
Where your expression can be like:
expression="statusCode.value() == 503"
UPDATE
Ah! I see. Since ExpressionRetryPolicy uses TemplateParserContext your expression definitely must be like #{statusCode.value() == 503}. But at the same time it is going to be evaluate during bean factory initialization. I suggest you to do something like this:
<bean id="spelParser" class="org.springframework.expression.spel.standard.SpelExpressionParser"/>
and in the ExpressionRetryPolicy bean definition do:
<constructor-arg index="0" type="org.springframework.expression.Expression"
value="#{spelParser.parseExpression('statusCode.value() == 503')}" />
To overcome the collision.
Related
I am getting the error `Null correlation not allowed. Maybe the CorrelationStrategy is failing?
This forum thread seemed to resolve it with something like the below, however that approach Is not working for me, http://forum.spring.io/forum/spring-projects/integration/102054-aggregator-correlation-strategy-failing.
My understanding is that the inbound-streaming-channel-adapter puts FILE_NAME as a header value, and I'd like to join on that.
<int-ftp:inbound-streaming-channel-adapter
auto-startup="true" id="ftpListener" channel="ftpChannel"
session-factory="ftpSessionFactory" remote-directory="/export/home/udyj"
filename-pattern="test1.txt">
<integration:poller fixed-rate="5000"
max-messages-per-poll="-1" />
</int-ftp:inbound-streaming-channel-adapter>
<int-ftp:inbound-streaming-channel-adapter
auto-startup="true" id="ftpListener2" channel="ftpChannel"
session-factory="ftpSessionFactory" remote-directory="/export/home/udyj"
filename-pattern="test2.txt">
<integration:poller fixed-rate="5000"
max-messages-per-poll="-1" />
</int-ftp:inbound-streaming-channel-adapter>
<bean id="correlationStrategy"
class="org.springframework.integration.aggregator.HeaderAttributeCorrelationStrategy">
<constructor-arg value="FILE_NAME.substring(0,3)" />
</bean>
<integration:aggregator id="nuggetAggregator"
input-channel="ftpChannel" output-channel="sendMQDistributionChannel"
correlation-strategy="correlationStrategy">
</integration:aggregator>
Look, you say HeaderAttributeCorrelationStrategy, what definitely expect a header name to get. but at the same you specify the header name as a FILE_NAME.substring(0,3) which looks more like an expression. And you get null because there is really no such a header.
If you want to evaluate an expression, consider to use ExpressionEvaluatingCorrelationStrategy instead:
<bean id="correlationStrategy"
class="org.springframework.integration.aggregator.ExpressionEvaluatingCorrelationStrategy">
<constructor-arg value="headers.file_name.substring(0,3)" />
</bean>
I have two different ways of declaring a spring integration bean. They both seem to work. I'm using the Spring STS Eclipse based IDE.
This way:
<bean id="int-ftp:request-handler-advice-chain"
class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="trapException" value="true"></property>
<property name="onFailureExpression" value="#root"></property>
<property name="failureChannel" ref="errorChannel"></property>
</bean>
or this way:
<int-ftp:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="trapException" value="true" />
<property name="onFailureExpression" value="#root" />
<property name="failureChannel" ref="errorChannel" />
</bean>
</int-ftp:request-handler-advice-chain>
Which way is better?
For the target <int-ftp:outbound-gateway> it doesn't matter. Works well, as you noticed already.
Only the difference that the second declaration is nested and the final bean is visible only from the <int-ftp:request-handler-advice-chain> context.
The first definition is top-level global bean, which is visible everywhere and can be reused from other bean as well.
You can find more info about Inner Beans in the Spring Framework.
Below i have the program to send a message and consume a message from queue , rite now i have commented out the sending part and only want to consume the messages from queue , the message can be type of string or object message for which i have configure router.
Now there are text message in the queue which my below program is not consuming please advise how to overcome from this below is my configuration , as you can see i have commented out the sender part so the only reading part from the queue is the active one
and also when rite now i have observe that messages are getting consumed but files are not being generated so this means that there is some error after payload router i have configured
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/jms
http://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/integration/file
http://www.springframework.org/schema/integration/file/spring-integration-file.xsd
http://www.springframework.org/schema/context/spring-context.xsd">
<int:poller id="poller" default="true">
<int:interval-trigger interval="200" />
</int:poller>
<int:channel id="input">
<int:queue capacity="10" />
</int:channel>
<bean id="tibcoEMSJndiTemplate" class="org.springframework.jndi.JndiTemplate">
<property name="environment">
<props>
<prop key="java.naming.factory.initial">com.tibco.tibjms.naming.TibjmsInitialContextFactory
</prop>
<prop key="java.naming.provider.url">tcp://lsdrtems2.fm.crdgrp.net:7333</prop>
<prop key="java.naming.security.principal">acfgtir</prop>
<prop key="java.naming.security.credentials">acfgtir</prop>
</props>
</property>
</bean>
<bean id="tibcoEMSConnFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiTemplate">
<ref bean="tibcoEMSJndiTemplate" />
</property>
<property name="jndiName">
<value>GenericConnectionFactory</value>
</property>
</bean>
<bean id="tibcosendJMSTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory">
<ref bean="tibcoEMSConnFactory" />
</property>
<property name="defaultDestinationName">
<value>acfgtirrtyation.ioa.swretift_publish_poc1</value>
</property>
<property name="pubSubDomain">
<value>false</value>
</property>
<property name="receiveTimeout">
<value>120000</value>
</property>
</bean>
<!-- <jms:outbound-channel-adapter channel="input"
destination-name="acfgtirrtyation.ioa.swretift_publish_poc1" connection-factory="tibcoEMSConnFactory" /> -->
<int:channel id="objetChannel"></int:channel>
<int:channel id="StringChannel"></int:channel>
<int:channel id="jmsInChannel" />
<jms:message-driven-channel-adapter id="jmsIn" concurrent-consumers="10"
destination-name="acfgtirrtyation.ioa.swretift_publish_poc1" connection-factory="tibcoEMSConnFactory" extract-payload="false"
channel="jmsInChannel" />
<int:payload-type-router input-channel="jmsInChannel">
<int:mapping type="javax.jms.ObjectMessage" channel="objetChannel" />
<int:mapping type="javax.jms.TextMessage" channel="StringChannel" />
</int:payload-type-router>
<file:outbound-channel-adapter id="filesoutOject" channel="objetChannel" directory="C:\\abcsaral"
filename-generator="generatorr" />
<file:outbound-channel-adapter id="filesoutString" channel="StringChannel" directory="C:\\abcsaral"
filename-generator="generatorr" />
<bean id="generatorr" class="com.abs.tibco.TimestampTextGenerator">
</bean>
</beans>
Folks please advise for this any early help would be much appreciated
below is the timestamp generator class as shown below
public class TimestampTextGenerator implements FileNameGenerator {
#Override
public String generateFileName(Message<?> arg0) {
return new java.text.SimpleDateFormat("yyyyMMdd-HHmmss.SSS")
.format(new java.util.Date()) + ".txt";
}
}
Folks please advise as I am completely stuck up on this..!
Remove this attribute extract-payload="false" in your message-driven-channel-adapter. It changes expected payload type and your payload-type-router doesn't work.
Also look this 20.1. Inbound Channel Adapter manual chapter.
The best way to understand what's wrong with your application is to take a look into logs. I should admit that sometimes Java is more smarter than me :-).
See Gary Russel's answer for you on the matter: Enabling logging in spring integration utiliy
Please, try to follow with all our answers to you around this topic.
I suggested you to use extract-payload="false" to get the whole javax.jms.ObjectMessage or javax.jms.TextMessage as a payload independently of the conversion result, when the javax.jms.ObjectMessage maybe converted to the java.lang.String as well. Although, right, in most case the routing based just only on the java.lang.String and all others should work, too.
Now about the <file:outbound-channel-adapter> problem. If you take a into logs you should see something like:
else {
throw new IllegalArgumentException(
"unsupported Message payload type [" + payload.getClass().getName() + "]");
}
M-m-m, yeah, I mean the result of this Exception in logs. Just because your payload is javax.jms.Message, but to write into file you should supply File, InputStream, byte[] or String.
So, as I said in comments to my answer (consuming all the object type messages from a queue) to your similar question, you should convert the message after routing into some appropriate type for <file:outbound-channel-adapter>.
I am trying to use the parameter-source to provide a parameter to my query in the inbound-channel-adapter but it doesn't seem to work. I have looked through the Parser code and I don't think it is processing the parameter-source.
<int-jpa:inbound-channel-adapter delete-after-poll="true"
delete-in-batch="true" auto-startup="true" flush-after-delete="true"
max-results="2000"
entity-manager-factory="entityManagerFactory"
entity-class="AuditMessage"
channel="processMessage"
jpa-query="select a from AuditMessage a where a.updateTs < :purgeDate"
parameter-source="parameterSource">
<int:poller cron="*/5 * * * * *" >
<int:transactional transaction-manager="transactionManager"/>
</int:poller>
</int-jpa:inbound-channel-adapter>
<bean id="parameterSource" factory-bean="spelSource"
factory-method="createParameterSourceNoCache">
<constructor-arg value="" />
</bean>
<bean id="spelSource"
class="org.springframework.integration.jdbc.ExpressionEvaluatingSqlParameterSourceFactory">
<property name="parameterExpressions">
<map>
<entry key="purgeDate"
value="new java.sql.Timestamp(T(System).currentTimeMillis()-(T(java.util.concurrent.TimeUnit).MILLISECONDS.convert(${eam.integration.arcgis.purge.daysToKeep}, T(java.util.concurrent.TimeUnit).DAYS)))" />
</map>
</property>
</bean>
I get the following exception:
ERROR [task-scheduler-9] [java.lang.IllegalArgumentException: Query has parameters but no parameter source provided
at org.springframework.integration.jpa.core.DefaultJpaOperations.setParametersIfRequired(DefaultJpaOperations.java:330)
at org.springframework.integration.jpa.core.DefaultJpaOperations.getQuery(DefaultJpaOperations.java:114)
at org.springframework.integration.jpa.core.DefaultJpaOperations.getResultListForQuery(DefaultJpaOperations.java:186)
at org.springframework.integration.jpa.core.JpaExecutor.doPoll(JpaExecutor.java:396)...
I am on spring version 4.0.3. Am I doing something wrong? Or is there another way to provide query parameters?
I think you've just found a bug!
The JpaInboundChannelAdapterParser just doesn't parse the parameter-source attrbiute. Feel free to raise a JIRA bug.
As a workaround you must configure JpaPollingChannelAdapter as a raw <bean> with injection of JpaExecutor and use it as a ref from generic <int:inbound-channel-adapter>.
The JIRA ticket for those who would like to track.
I have an scenario where I must send messages in order to a rest service and I plan to use a resequencer. The behaviour of this resequencer must be:
Order messages by time in day (hh:mm:ss): data on the message
Release messages only after they stay a period of time in the bus (p.e. 2 minutes)
As the default Resequencer didn't serve for this purpose I decided to develop a custom one changing the ResequencerMessageGroupProcessor for a CustomResequencerMessageGroupProcessor.
I succeded using a service activator but I had to explictly define the output-channel as a property. Isn't there a way to use the output-channel attribute on the xml declaration?
When I use the output-channel attribute the following error occurs:
Caused by: java.lang.IllegalArgumentException: no outputChannel or replyChannel header available
at org.springframework.util.Assert.notNull(Assert.java:112)
at org.springframework.integration.aggregator.AbstractCorrelatingMessageHandler.sendReplies(AbstractCorrelatingMessageHandler.java:616)
at org.springframework.integration.aggregator.AbstractCorrelatingMessageHandler.completeGroup(AbstractCorrelatingMessageHandler.java:597)
at org.springframework.integration.aggregator.AbstractCorrelatingMessageHandler.handleMessageInternal(AbstractCorrelatingMessageHandler.java:405)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:78)
... 46 more
here's my example:
<int:channel id="resequencerChannel"/>
<int:service-activator id="customResequencer" ref="resequencingMessageHandler"
input-channel="resequencerChannel" />
<int:channel id="aggregatedMessageChannel" />
<bean id="resequencingMessageHandler" class="org.springframework.integration.aggregator.ResequencingMessageHandler">
<constructor-arg name="releaseStrategy" ref="timeoutReleaseStrategy"/>
<constructor-arg name="processor" ref="customResequencerMessageGroupProcessor"/>
<constructor-arg name="store" ref="redisMessageStore"/>
<constructor-arg name="correlationStrategy" ref="customCorrelationStrategy"/>
<property name="outputChannel" ref="aggregatedMessageChannel"/>
<property name="sendPartialResultOnExpiry" value="true"></property>
</bean>
<bean id="customResequencerMessageGroupProcessor" class="test.resequencer.CustomResequencerMessageGroupProcessor">
<constructor-arg name="timeout" value="10000"/>
</bean>
<bean id="timeoutReleaseStrategy" class="org.springframework.integration.aggregator.TimeoutCountSequenceSizeReleaseStrategy" >
<constructor-arg name="threshold" value="100000"></constructor-arg>
<constructor-arg name="timeout" value="10000"/>
</bean>
<bean id="customCorrelationStrategy" class="org.springframework.integration.aggregator.HeaderAttributeCorrelationStrategy" >
<constructor-arg name="attributeName" value="correlationId"/>
Also, if you think there is a better way to do this, please, I would apreciatte telling so
Thanks in advance!
Regards
Guzman
When referencing (ref) a MessageHandler from a <service-activator/> the XML output-channel is only applied if the referenced handler is an AbstractReplyProducingMessageHandler (ARPMH).
Components such as routers, aggregators, resequencers, are not considered to be ARPMHs because they sometimes produce a reply, sometimes don't and, in the case of a router, might produce multiple "replies" which doesn't fit the service activator model.
We could probably refactor the aggregator/resequencer to be ARPMHs because they only produce 0 or 1 "reply". We could also add some smarts to the ServiceActivatorFactoryBean to inject the output channel if the reference is an AbstractCorrelatingMessageHandler. Feel free to open an Improvement JIRA Issue.
In the meantime, your solution is the correct work-around.