I have defined a queue - blah.queue and have defined the dead-letter-routing-key for it. However, when I have a poison message which fails with a Exception, the same message is requeued and reattempted (in an infinite loop).
I would expect that after 3 retries, the message should be put on the exchange with the dead-letter-routing-key. But that doesn't seem to be happening.
I have the following settings:
<rabbit:queue name="blah.queue" auto-delete="false" durable="true">
<rabbit:queue-arguments>
<entry key="x-dead-letter-exchange" value="" />
<entry key="x-dead-letter-routing-key" value="blah.queue.dlq.route" />
<entry key="x-ha-policy" value="all" />
</rabbit:queue-arguments>
</rabbit:queue>
<rabbit:direct-exchange name="${rabbit.idesk.exchange}">
<rabbit:bindings>
<rabbit:binding queue="blah.queue" key="blah.route" />
</rabbit:bindings>
</rabbit:direct-exchange>
<bean id="myConsumer" class="com.ankit.CustomConsumer" />
<bean id="myConsumerMessageListenerAdapter" class="org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter">
<constructor-arg ref="myConsumer" />
<constructor-arg ref="myMessageConverter" />
</bean>
<bean id="myConsumerMessageListenerContainer" class="org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer">
<property name="queueNames" value="blah.queue" />
<property name="connectionFactory" ref="queueConnectionFactory" />
<property name="messageListener" ref="myConsumerMessageListenerAdapter" />
<property name="errorHandler" ref="loggingErrorHandler" />
<property name="adviceChain">
<list>
<ref bean="retryAdvice" />
</list>
</property>
</bean>
<bean id="loggingErrorHandler" class="org.springframework.scheduling.support.TaskUtils.LoggingErrorHandler" />
<bean id="myMessageConverter"
class="org.springframework.amqp.support.converter.JsonMessageConverter">
<property name="classMapper">
<bean class="com.ankit.queue.mapper.NamedClassMapper">
<constructor-arg
value="com.ankit.dto.EventDTO" />
</bean>
</property>
<property name="createMessageIds" value="true" />
</bean>
<bean id="retryAdvice" class="org.springframework.amqp.rabbit.config.StatefulRetryOperationsInterceptorFactoryBean">
<property name="messageRecoverer" ref="rejectAndDontRequeueRecoverer" />
<property name="retryOperations" ref="retryTemplate" />
</bean>
<bean id="retryTemplate" class="org.springframework.retry.support.RetryTemplate">
<property name="retryPolicy" ref="simpleRetryPolicy" />
<property name="backOffPolicy">
<bean class="org.springframework.retry.backoff.FixedBackOffPolicy">
<property name="backOffPeriod" value="5000" />
</bean>
</property>
</bean>
<bean id="simpleRetryPolicy" class="org.springframework.retry.policy.SimpleRetryPolicy">
<property name="maxAttempts" value="3" />
</bean>
Do you not see the WARN message from the recoverer?
#Override
public void recover(Message message, Throwable cause) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Retries exhausted for message " + message, cause);
}
throw new ListenerExecutionFailedException("Retry Policy Exhausted",
new AmqpRejectAndDontRequeueException(cause), message);
}
Turn on DEBUG logging to watch the retry behavior.
Related
My ThreadPoolTaskExecutor's corePoolSize is 5 and in JdbcPaginingItemReader bean config I have set saveState to false (per documentation it should be set to false if used in a multi-threaded env) and my table have a primary key, which I am using in sortKey attribute of queryProvider yet when task-executor spawns all five threads simultaneously they all are trying to read the data, startAfterValues of JdbcPaginingItemReader is getting messed up. Reader call from each thread is reading the duplicate rows due to the fact that startAfterValues is not thread safe.
How do I overcome this?
Here are my config info.
<job id="myJob" xmlns="http://www.springframework.org/schema/batch" incrementer="jobIncrementer">
<step id="step1">
<tasklet task-executor="myTaskExecutor">
<chunk reader="myReader" writer="myWriter" commit-interval="1000" />
<transaction-attributes isolation="READ_COMMITTED" />
</tasklet>
</step>
</job>
<bean id="myReader" class="org.springframework.batch.item.database.JdbcPagingItemReader" scope="step">
<property name="dataSource" ref="myDataSource" />
<property name="queryProvider">
<bean class="org.springframework.batch.item.database.support.SqlPagingQueryProviderFactoryBean">
<property name="dataSource" ref="myDataSource"/>
<property name="selectClause" value="SELECT ID, NAME"/>
<property name="fromClause" value="FROM EMPLOYEE" />
<property name="whereClause" value="where 1=1" />
<property name="sortKey" value="ID"/>
</bean>
</property>
<property name="pageSize" value="1000"/>
<property name="fetchSize" value="1000"/>
<property name="saveState" value="false"/>
<property name="rowMapper">
<bean class="com...MyRowMapper"/>
</property>
</bean>
<bean id="myWriter" class="com...MyItemWriter" scope="step">
<constructor-arg name="jdbcTemplate" ref="jdbcTemplate" />
<constructor-arg name="namedParamJdbcTemplate" ref="namedParamJdbcTemplate" />
<constructor-arg name="endUserID" value="123" />
</bean>
<bean id="myTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5" />
<property name="maxPoolSize" value="10"/>
</bean>
I have a Websphere MQ and a java app receiveng messages from it. I want to make redelivering system if any exceptions is thrown .
Is there a way to add redeliveryDelay in my configuration spring xml?
here's my spring configuration:
<!-- JMS CONNECTION FACTORY -->
<bean id="MQFactory" class="com.ibm.mq.jms.MQConnectionFactory">
<property name="transportType">
<util:constant static-field="com.ibm.msg.client.wmq.WMQConstants.WMQ_CM_CLIENT" />
</property>
<property name="queueManager" value="${queueManager}" />
<property name="hostName" value="${hostName}" />
<property name="port" value="${port}" />
<property name="channel" value="${channel}" />
</bean>
<bean id="JmsConnectionFactory"
class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter">
<property name="targetConnectionFactory" ref="MQFactory" />
<property name="username" value="${username}" />
<property name="password" value="${username}" />
</bean>
<!-- JMS LISTENER -->
<bean id="Listener" class="jms.impl.Listener"></bean>
<!-- JMS CONTAINER -->
<bean id="JmsContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="JmsConnectionFactory" />
<property name="destinationName" value="${destination}" />
<property name="messageListener" ref="Listener" />
<property name="autoStartup" value="false" />
<property name="concurrentConsumers" value="${jms.consumers}" />
<property name="sessionTransacted" value="true" />
</bean>
Here is my job structure.
<bean id="ControlJob" class="com.example.batch.ControlJob">
<property name="jobRepository" ref="jobRepository" />
<property name="name" value="Outter job" />
<property name="moduleId" value="d" />
<property name="jobList">
<list>
<ref bean="a" />
<ref bean="b" />
<ref bean="c" />
<ref bean="d" />
<ref bean="e" />
<ref bean="f" />
<ref bean="g" />
<ref bean="h" />
</list>
</property>
<property name="jobLauncher" ref="jobLauncher" />
</bean>
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="taskExecutor" ref="syncTaskExecutor" />
<property name="jobRepository" ref="jobRepository" />
</bean>
<bean id="syncTaskExecutor" class="org.springframework.core.task.SyncTaskExecutor" />
This is my entrance:
public class SimpleJobLauncher {
public static void main(String[] args) {
String[] path = {"classpath:com/context.xml"};
AbstractApplicationContext context = new FileSystemXmlApplicationContext(path);
context.registerShutdownHook();
IBatchJobControllerBO batch = (IBatchJobControllerBO) context.getBean("batchJobControllerBO");
//IBatchJobControllerBO batch = (IBatchJobControllerBO) context.getBean("synchronizedBatchJobControllerBO");
try {
BatchJobExecution execution = batch.startBatchJob("Outter job");
//BatchJobExecution execution = batch.startBatchJob("Outter job");
String message = execution.getExitMessage();
System.out.println("job completed: " + message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Two different bo to run the Outter job:
<bean id="batchJobControllerBO"
class="com.bo.impl.BatchJobControllerBOImpl">
<property name="jobLauncher" ref="jobLauncher" />
<property name="jobRegistry" ref="jobConfigurationRegistry" />
<property name="batchJobDAO" ref="batchJobDAO" />
<property name="batchJobExecutionDAO" ref="batchJobExecutionDAO" />
</bean>
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
<property name="taskExecutor">
<bean class="org.springframework.core.task.SimpleAsyncTaskExecutor" />
</property>
</bean>
<bean id="synchronizedBatchJobControllerBO" parent="batchJobControllerBO"
class="com.bo.impl.BatchJobControllerBOImpl">
<property name="jobLauncher" ref="synchronizeJobLauncher" />
</bean>
<bean id="synchronizeJobLauncher" parent="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="taskExecutor">
<bean class="org.springframework.core.task.SyncTaskExecutor" />
</property>
</bean>
Each job a ~ h is working perfectly and for the Outter Job if i use jobLauncher : "SimpleAsyncTaskExecutor" to call the Outter Job, it will work fine.
But when I use jobLauncher : "SyncTaskExecutor" to call the Outter Job, some of the job (the error is not stable) didn't commit any data to DB (the table is empty).
The job status is completed without any error.
Can anyone tell me what's wrong in this scenario?
I'm trying to integrate Atomikos transaction manager into a Spring Integration program that forwards JMS from ActiveMQ to a WebMethods ESB.
The spring integration part only retrieves JMs from local ActiveMQ broker and sends them to a distant ESB broker.
When I test the nominal case, JMS is sent well and passes through the ESB and is dispatched to the subscribers then.
When I test the case where ESB sending fails, I have an issue : the JMS is never published back. I suppose it's a transaction issue because the transaction should have been rolled back when program tried to publish on ESB broker but it seems not.
Here's my spring config :
<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="AtomikosTransactionManager" />
<property name="userTransaction" ref="AtomikosUserTransaction" />
</bean>
<!-- Atomikos Transaction Manager Defintion (JTA) -->
<bean id="AtomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
init-method="init" destroy-method="close" depends-on="atomikosConnectionFactorySource,connectionFactoryDestination">
<property name="transactionTimeout" value="300" />
<property name="forceShutdown" value="false" />
</bean>
<bean id="AtomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
<property name="transactionTimeout" value="300" />
</bean>
<bean id="jmsXaConnectionFactory" class="org.apache.activemq.ActiveMQXAConnectionFactory">
<property name="brokerURL" value="${source.java.naming.provider.url}" />
<property name="userName" value="${source.username}" />
<property name="password" value="${source.passwd}" />
</bean>
<bean id="atomikosConnectionFactorySource" class="com.atomikos.jms.AtomikosConnectionFactoryBean"
init-method="init" destroy-method="close">
<property name="poolSize" value="1" />
<property name="uniqueResourceName" value="activemq" />
<property name="xaConnectionFactory" ref="jmsXaConnectionFactory" />
</bean>
<bean id="connectionFactorySource"
class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory" ref="jmsXaConnectionFactory" />
<property name="clientId" value="CustomerOrderForwarderID" />
<property name="reconnectOnException" value="true" />
</bean>
<!-- Destination JNDI Context -->
<bean id="jndiTemplateDestination" class="org.springframework.jndi.JndiTemplate"
lazy-init="true">
<property name="environment">
<props>
<prop key="java.naming.factory.initial">${destination.java.naming.factory.initial}</prop>
<prop key="java.naming.provider.url">${destination.java.naming.provider.url}</prop>
<prop key="java.naming.factory.url.pkgs">${destination.java.naming.factory.url.pkgs}</prop>
</props>
</property>
</bean>
<!-- Destination Connection factory -->
<bean id="customerOrderXAConnectionFactoryDestination" class="org.springframework.jndi.JndiObjectFactoryBean"
lazy-init="true">
<property name="jndiTemplate" ref="jndiTemplateDestination" />
<property name="jndiName"
value="${destination.java.naming.factory.connection}" />
<property name="lookupOnStartup" value="false" />
<property name="proxyInterface" value="javax.jms.XAConnectionFactory" />
</bean>
<bean id="connectionFactoryDestination" class="com.atomikos.jms.AtomikosConnectionFactoryBean"
init-method="init" destroy-method="close">
<property name="poolSize" value="100" />
<property name="uniqueResourceName" value="esb" />
<property name="xaConnectionFactory" ref="customerOrderXAConnectionFactoryDestination" />
<property name="localTransactionMode" value="true" />
</bean>
<bean id="ddr" class="com.adeo.transverse.jms.forwarder.customerorder.DynamicDestinationResolver" />
<bean id="userCredentialsConnectionFactoryDestination"
class="org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter" lazy-init="true">
<property name="targetConnectionFactory">
<ref bean="connectionFactoryDestination" />
</property>
<property name="username" value="${destination.username}" />
<property name="password" value="${destination.passwd}" />
</bean>
Here's the integration part :
<!-- In bridge -->
<jms:message-driven-channel-adapter
id="StoreStockMotionSourceJmsAdapter" channel="bridgeChannelStoreStockMotionEnricher"
container="jmsContainerSourceStoreStockMotion" />
<!-- Channel -->
<si:channel id="bridgeChannelStoreStockMotionEnricher" />
<jms:outbound-channel-adapter id="StoreStockMotionDestinationJmsAdapter"
channel="bridgeChannelStoreStockMotionEnricher" jms-template="jmsTemplateStoreStockMotionDestination" />
<bean id="jmsTemplateStoreStockMotionDestination" class="org.springframework.jms.core.JmsTemplate">
<property name="transactionManager" ref ="transactionManager"/>
<property name="connectionFactory" ref="userCredentialsConnectionFactoryDestination" />
<property name="defaultDestinationName" value="${StoreStockMotion.destination.topic}" />
<property name="defaultDestination" ref="StoreStockMotionDestinationTopic" />
<property name="pubSubDomain" value="true"/>
</bean>
<!-- Topic JMS for published message -->
<bean id="StoreStockMotionDestinationTopic" class="org.springframework.jndi.JndiObjectFactoryBean" lazy-init="true">
<property name="jndiTemplate">
<ref bean="jndiTemplateDestination" />
</property>
<property name="jndiName">
<value>${StoreStockMotion.destination.topic}</value>
</property>
</bean>
<!-- Topic JMS for Subscribing Message -->
<bean id="jmsContainerSourceStoreStockMotion"
class="org.springframework.jms.listener.DefaultMessageListenerContainer"
lazy-init="true">
<property name="connectionFactory" ref="connectionFactorySource" />
<property name="destinationName" value="${StoreStockMotion.source.topic}" />
<property name="subscriptionDurable" value="true" />
<!-- 2 is client acknowledge -->
<property name="sessionAcknowledgeMode" value="2" />
<property name="durableSubscriptionName" value="${StoreStockMotion.source.subname}" />
<property name="sessionTransacted" value="false" />
<property name="pubSubDomain" value="true"/>
</bean>
Source and Destination are both encapsulated in XA connection factories and transactionManager handles the two transactions. Any idea what's missing ?
hi actually springbatch jobrepository uses jdbctemplate for the CRUD operations of springbatch metadata
and my requirement is to replace jdbctemplate with jpa and do the crud operations...
the actual configuration of springbatch job repository is as follows...
can anyone kindly help regarding this issue...i searched alot but got nothing......
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3307/test" />
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
<!-- create job-meta tables automatically -->
<jdbc:initialize-database data-source="jobRepository-dataSource">
<jdbc:script location="classpath:/org/springframework/batch/core/schema-drop-mysql.sql" />
<jdbc:script location="classpath:/org/springframework/batch/core/schema-mysql.sql" />
</jdbc:initialize-database>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
lazy-init="true">
<property name="dataSource" ref="jobRepository-dataSource" />
</bean>
<bean id="jobRepository-transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
lazy-init="true">
<property name="dataSource" ref="jobRepository-dataSource" />
</bean>
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean" >
<property name="dataSource" ref="jobRepository-dataSource" />
<property name="transactionManager" ref="jobRepository-transactionManager" />
<property name="databaseType" value="mysql" />
<property name="isolationLevelForCreate" value="ISOLATION_DEFAULT" />
<property name="tablePrefix" value="batch_" />
</bean>
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<bean id="jobExplorer"
class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean"
p:dataSource-ref="jobRepository-dataSource" p:tablePrefix="batch_" />
<bean id="jobRegistryBeanPostProcessor"
class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
<property name="jobRegistry" ref="jobRegistry" />
</bean>
<bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry" />
<bean id="jobOperator" class="org.springframework.batch.core.launch.support.SimpleJobOperator">
<property name="jobRepository" ref="jobRepository" />
<property name="jobLauncher" ref="jobLauncher" />
<property name="jobRegistry" ref="jobRegistry" />
<property name="jobExplorer" ref="jobExplorer" />
</bean>