Facing issues while implementing graceful shutdown feature - spring-boot

I am working on standalone spring boot application and used spring integration channel architecture to transfer files from source to destination.
We have provided multi-instance feature as well, to achieve this we will be locking(append .lock for the files) files for particular instance to restrict access from other instance.
Currently, I have one requirement for graceful shutdown . I implemented but end-up with the following error. Can anyone help me on this ?
Control bus configuration :
<integration:channel id="controlChannel" />
<integration:control-bus input-channel="controlChannel" />
Adapter configuration :
<file:inbound-channel-adapter id="filesInChannel"
directory="path" auto-startup="false" scanner="recursiveScanner"
auto-create-directory="true">
<integration:poller id="poller"
max-messages-per-poll="5" fixed-rate="1000"
task-executor="pollingExecutor">
<integration:transactional
transaction-manager="transactionManager" />
</integration:poller>
</file:inbound-channel-adapter>
Method added for graceful shutdown component
#PreDestroy
public void onDestroy() throws InterruptedException {
try {
inboundFileAdapterChannel.send(new GenericMessage<String>("#'filesInChannel.adapter'.stop()"));
} catch (Exception e) {
e.printStackTrace();
}
// wait till current processing of files is over
pollingExecutor.shutdown();
pollingExecutor.setWaitForTasksToCompleteOnShutdown(Boolean.TRUE);
System.out.println("Application shutdown succesfully");
}
Error Trace :
org.springframework.messaging.MessageDeliveryException: Dispatcher has no subscribers for channel 'application.controlChannel'.; nested exception is org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers, failedMessage=GenericMessage [payload=#'filesInChannel.adapter'.stop(), headers={id=5f0282c5-5a02-3f57-3056-948dd74a8d72, timestamp=1559894703464}], failedMessage=GenericMessage [payload=#'filesInChannel.adapter'.stop(), headers={id=5f0282c5-5a02-3f57-3056-948dd74a8d72, timestamp=1559894703464}]
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:445)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:394)
at com.openbank.interfaces.file.handler.GracefulShutdownHook.onDestroy(GracefulShutdownHook.java:63)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:365)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeDestroyMethods(InitDestroyAnnotationBeanPostProcessor.java:323)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeDestruction(InitDestroyAnnotationBeanPostProcessor.java:155)
at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:240)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:577)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:549)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:957)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:510)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:964)
at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1041)
at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1017)
at org.springframework.context.support.AbstractApplicationContext$1.run(AbstractApplicationContext.java:937)
Caused by: org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers, failedMessage=GenericMessage [payload=#'filesInChannel.adapter'.stop(), headers={id=5f0282c5-5a02-3f57-3056-948dd74a8d72, timestamp=1559894703464}]
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:138)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:105)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:73)
... 19 more

#PreDestroy is too late to perform such operations.
By that time all Lifecyle beans will have been stop()ped (which unsubscribes from the channel).
You need to perform that work using some other signal.
You could implement SmartLifecyle and put your bean in a late phase (so it is stop()ped early.

Related

Spring Integration JMS with JTA rollback although exception is handled in ErrorHandler

I am using Spring Integration with JTA support through Atomikos and JMS bound to different Webshpere MQ and a Oracle Database.
Obviosly for others it seems to be the same thread as in Spring Integration JMS with JTA rollback when message goes to errorChannel but it isn't not at all.
The flow is the following:
message-driven-channel-adapter receive the message within transaction
some transformations
ServiceActivator with deeper business logic
DataBase Update
Everything works really fine expect for one szenario:
If an unchecked exception occurs (maybe due to inkonsistent data which shouldn't be) within the ServiceActivator the message is rethrown and handled within an ErrorHandler (via ErrorChannel).
There - in some cases - the orgininal incoming message should be send to a DeadLetter Queue (Webshere MQ). This is done with outbound-channel-adapter.
See enclosed configuration:
<jms:message-driven-channel-adapter id="midxMessageDrivenAdapter"
channel="midxReplyChannel"
acknowledge="auto"
transaction-manager="transactionManager"
connection-factory="connectionFactory"
concurrent-consumers="1"
error-channel="errorChannel"
max-concurrent-consumers="${cmab.integration.midx.max.consumer}"
idle-task-execution-limit="${cmab.integration.midx.idleTaskExecutionLimit}"
receive-timeout="${cmab.integration.midx.receiveTimeout}"
destination="midxReplyQueue"/>
................
<int:service-activator input-channel="midxReplyProcessChannel" ref="processMidxReplyDbWriter" />
<int:service-activator input-channel="errorChannel" ref="inputErrorHandler" />
<jms:outbound-channel-adapter id="deadLetterOutboundChannelAdapter"
channel="errorDlChannel" destination="deadLetterQueue"
delivery-persistent="true" connection-factory="nonXAConnectionFactory"/>
Some important hints:
message-driven-channel-adapter:
connectionFactory is a MQXAQueueConnectionFactory wihtin an AtomikosConnectionFactoryBean
transaction-manager is Spring JtaTransactionManager
outbound-channel-adapter:
connection-factory is a nonXAConnectionFactory.
CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
MQQueueConnectionFactory mqQueueConnectionFactory = new MQQueueConnectionFactory();
.....
cachingConnectionFactory.setTargetConnectionFactory(mqQueueConnectionFactory);
return cachingConnectionFactory;
And now the error which I observed:
Although I handled the unchecked exception in my ErrorHandler (ref="inputErrorHandler"; send the message to DeadLetter-Queue) an rollback is initiated and the message-driven-channel-adapter receives the message again and again.
Curious is the fact that indeed the message is delivered to the DeadLetterQueue via the outbound-channel-adapter. The destination (deadLetterQueue) contains the failed messages.
Question: What I'm doing wrong? The failed original incoming message is rolled back although I handled the exception in my ErrorHandler.
Any help really appreciate. Many thanks in advance.
concerning to my comment see enclose code of my InputErrorHandler:
if (throwable instanceof MessageHandlingException) {
MessageHandlingException messageHandlingException = (MessageHandlingException) throwable;
if (messageHandlingException.getCause() != null
&& (messageHandlingException.getCause() instanceof CmabProcessDbException
|| messageHandlingException.getCause() instanceof CmabReplyFormatException)) {
String appMessage = ((CmabException) messageHandlingException.getCause()).getAppMessagesAsString();
LOGGER.error(String.format("cmab rollback exception occured: %s", appMessage));
LOGGER.error("******** initiate rollback *********");
throw throwable.getCause();
} else {
ErrorDto payload = fillMessagePayload(messageHandlingException, throwableClassName);
sendMessageToTargetQueue(payload);
}
As I mentioned the "business" ServiceActivator throws an unchecked exception so in this case the ELSE-Statements are calling.
Within that I build up a Message with MessagBuilder and sends it ti the errorDlChannel (s. above the outbound-channel-adapter!).
private void sendMessageToDeadLetterQueue(Message<?> message, String description, String cause) {
.......
Message<?> mb =
MessageBuilder.withPayload(message.getPayload())
.copyHeaders(message.getHeaders())
.setHeaderIfAbsent("senderObject", this.getClass().getName())
.setHeaderIfAbsent("errorText", description)
.setHeaderIfAbsent("errorCause", errorCause).build();
errorDlChannel.send(mb);
}
That's all. This is for this case the last statement. There is nothing anything else in the main method of my ErrorHandler. No rethrow or other stuff.
So this is the reason of my confusion. For me the exception is handled by sending it to the errorDlChannel (outbound-channel-adapter -> DeadLetterQueue).
I saw the message on the DeadLetter Queue but nevertheless a jta rollback occurs...and IMO this shouldn't bee.

Spring JMS transaction rollback - message dequeued off ActiveMQ

I have a simple Spring Boot application(Spring Boot Version 1.5.3.RELEASE) for consuming JMS Messages off an ActiveMQ(version 5.14.5) Queue.
I want the messages to be consumed in a JMS transaction. If there is an exception during message consumption, I expect transaction to be rolled back and message not to be dequeued(taken off message queue). I can see transaction being rolled back in Spring logs, however the message is still dequeued from ActiveMQ queue(after six re delivery attempts).
Any pointers will be appreciated.
Here is the application code:
#SpringBootApplication
public class SpringJmsDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringJmsDemoApplication.class, args);
}
#Bean
public JmsListenerContainerFactory<?> myFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory defaultJmsListenerContainerFactory = new DefaultJmsListenerContainerFactory();
defaultJmsListenerContainerFactory.setTransactionManager(jmsTransactionManager(connectionFactory));
defaultJmsListenerContainerFactory.setSessionTransacted(true);
defaultJmsListenerContainerFactory.setSessionAcknowledgeMode(Session.SESSION_TRANSACTED);
configurer.configure(defaultJmsListenerContainerFactory, connectionFactory);
return defaultJmsListenerContainerFactory;
}
#Bean
public PlatformTransactionManager jmsTransactionManager(ConnectionFactory connectionFactory) {
return new JmsTransactionManager(connectionFactory);
}
}
#Component
public class Receiver {
#JmsListener(destination = "mailbox", containerFactory = "myFactory")
#Transactional
public void receiveMessage(String email) {
System.out.println("Received <" + email + ">");
throw new RuntimeException("nooo");
}
}
Here is the log:
2017-05-24 09:51:59.865 DEBUG 8972 --- [DefaultMessageListenerContainer-1] o.s.j.connection.JmsTransactionManager : Created JMS transaction on Session [ActiveMQSession {id=ID:D6C0B8467A518-58248-1495590693980-1:32:1,started=false} java.lang.Object#65d647cd] from Connection [ActiveMQConnection
2017-05-24 09:51:59.867 DEBUG 8972 --- [DefaultMessageListenerContainer-1] o.s.j.l.DefaultMessageListenerContainer : Received message of type [class org.apache.activemq.command.ActiveMQTextMessage] from consumer [ActiveMQMessageConsumer { value=ID:D6C0B8467A518-58248-1495590693980-1:32:1:1, started=true }] of transactional session [ActiveMQSession {id=ID:D6C0B8467A518-58248-1495590693980-1:32:1,started=true} java.lang.Object#65d647cd]
2017-05-24 09:51:59.867 DEBUG 8972 --- [DefaultMessageListenerContainer-1] o.s.j.l.DefaultMessageListenerContainer : Rolling back transaction because of listener exception thrown: org.springframework.jms.listener.adapter.ListenerExecutionFailedException: Listener method 'public void com.anz.markets.springjmsdemo.Receiver.receiveMessage(java.lang.String)' threw exception; nested exception is java.lang.RuntimeException: nooo
2017-05-24 09:51:59.867 WARN 8972 --- [DefaultMessageListenerContainer-1] o.s.j.l.DefaultMessageListenerContainer : Execution of JMS message listener failed, and no ErrorHandler has been set.
org.springframework.jms.listener.adapter.ListenerExecutionFailedException: Listener method 'public void com.anz.markets.springjmsdemo.Receiver.receiveMessage(java.lang.String)' threw exception; nested exception is java.lang.RuntimeException: nooo
at org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:112) ~[spring-jms-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:69) ~[spring-jms-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.jms.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:721) ~[spring-jms-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.jms.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:681) ~[spring-jms-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.jms.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:651) ~[spring-jms-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.doReceiveAndExecute(AbstractPollingMessageListenerContainer.java:317) [spring-jms-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:235) [spring-jms-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1166) [spring-jms-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1158) [spring-jms-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:1055) [spring-jms-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_72]
Caused by: java.lang.RuntimeException: nooo
at com.anz.markets.springjmsdemo.Receiver.receiveMessage(Receiver.java:12) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_72]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_72]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_72]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_72]
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:180) ~[spring-messaging-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:112) ~[spring-messaging-4.3.8.RELEASE.jar:4.3.8.RELEASE]
at org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:104) ~[spring-jms-4.3.8.RELEASE.jar:4.3.8.RELEASE]
... 10 common frames omitted
2017-05-24 09:51:59.868 DEBUG 8972 --- [DefaultMessageListenerContainer-1] o.s.j.connection.JmsTransactionManager : Transactional code has requested rollback
2017-05-24 09:51:59.868 DEBUG 8972 --- [DefaultMessageListenerContainer-1] o.s.j.connection.JmsTransactionManager : Initiating transaction rollback
2017-05-24 09:51:59.868 DEBUG 8972 --- [DefaultMessageListenerContainer-1] o.s.j.connection.JmsTransactionManager : Rolling back JMS transaction on Session [ActiveMQSession {id=ID:D6C0B8467A518-58248-1495590693980-1:32:1,started=true} java.lang.Object#65d647cd]
According to ActiveMQ message re-delivery documentation, the messages, which failed to be delivered, will go to the dead letter queue (http://activemq.apache.org/message-redelivery-and-dlq-handling.html):
"The default Dead Letter Queue in ActiveMQ is called ActiveMQ.DLQ; all un-deliverable messages will get sent to this queue and this can be difficult to manage. So, you can set an individualDeadLetterStrategy in the destination policy map of the activemq.xml configuration file, which allows you to specify a specific dead letter queue prefix for a given queue or topic. You can apply this strategy using wild card if you like so that all queues get their own dead-letter queue, as is shown in the example below"
Please, extend you activemq.xml with individualDeadLetterStrategy to the queue.

How add a circuit breaker for <int:gateway> in spring integration

Here is my code i had a gateway defined in my spring-config.xml like below
<int:gateway id="myGateway"
service-interface="com.sample.si.MyService"
error-channel="myError-channel"
default-request-channel="sendServiceChannel"
>
<int:method name="sendService" request-channel="sendServiceChannel" /
When i debug my test it is directly going to RequesthandlerCircuitBreakerAdvice.class and never going to my error channel, below is my error channel
<int:channel id="myError-channel">
</int:channel>
<int:chain input-channel="myError-channel">
<int:service-activator ref="myErrorHandler" method="resolveError" />
</int:chain>
I want to redirect to error channel when i had an exception from gateway and want to redirect every message to error channel with the circuitbreaker halfopen timelimit. Any help is appreciated
Edit Showing Outbound gateway
<int-ws:outbound-gateway id="sendMyService"
uri="${my.gateway.send.uri}"
mapped-request-headers="*"
marshaller="SOAPMarshaller"
unmarshaller="SOAPMarshaller"
message-sender="soapMessageSender"
request-channel="sendServiceChannel"
fault-message-resolver="faultResolver"
reply-channel="sendServiceResponseChannel">
<int:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.RequestHandlerCircuitBreakerAdvice">
<property name="threshold" value="1" />
<property name="halfOpenAfter" value="10000" />
</bean>
</int:request-handler-advice-chain>
I want the circuit breaker between the and so that i dont want to hit the outbound gateway when the provided uri in the outbound gateway is not available, thanks for looking in to it Gary Russell
Here is my testcase
public class EmailGatewayProducerConsumerTest {
#Inject
#Qualifier("myGateway")
private Myservice myService;
#Test
public void senderTest(){
MyRequest myRequest = new MyRequest(12,"rajendra","rajednra.ch#hotmail.com");
try {
myService.sendService(myRequest);
}catch (Exception e){
//System.out.println("Caught Exception " +e);
}
}
}
The error handler is a sending the exception message to queue and reading it back and sending again to gateway here is my error handler code
public class ErrorHandler{#Inject private JmsTemplate jmsTemplate;
public void resolveError( Message<MessagingException> message) {
try{
MyRequest myRequest=(MessageRequest)message.getPayload().getFailedMessage().getPayload()
jmsTemplate.convertAndSend(DATA_QUEUE, myRequest);
} catch(Exception e){
//log error
}
}
}
Here is my consumer which consumes it and sends back to the gateway
#JmsListener(destination = DATA_QUEUE)public void consume(MyRequest myRequest) throws InterruptedException {
log.info("Receiving event: {}", myRequest);
try {
myService.sendService(myRequest)
}catch (Exception e){
log.error(e.toString());
} }
Exception:
Caused by: org.springframework.ws.client.WebServiceIOException: I/O error: Connect to localhost:10002 timed out; nested exception is org.apache.http.conn.ConnectTimeoutException: Connect to localhost:10002 timed out
at org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:561)
at org.springframework.integration.ws.MarshallingWebServiceOutboundGateway.doHandle(MarshallingWebServiceOutboundGateway.java:81)
at org.springframework.integration.ws.AbstractWebServiceOutboundGateway.handleRequestMessage(AbstractWebServiceOutboundGateway.java:188)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler$AdvisedRequestHandler.handleRequestMessage(AbstractReplyProducingMessageHandler.java:144)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice$1.execute(AbstractRequestHandlerAdvice.java:74)
at org.springframework.integration.handler.advice.RequestHandlerCircuitBreakerAdvice.doInvoke(RequestHandlerCircuitBreakerAdvice.java:61)
at org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice.invoke(AbstractRequestHandlerAdvice.java:69)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy102.handleRequestMessage(Unknown Source)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.doInvokeAdvisedRequestHandler(AbstractReplyProducingMessageHandler.java:117)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:102)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:78)
... 90 more
The gateway (and hence error channel) is downstream of your circuit breaker - you need to show what's upstream of (before) the service-activator - that's where you need to configure the error-channel to handle the circuit-breaker result.
That said, you have a circular dependency here - the gateway is sending to the service-activator, which is invoking the gateway again.
You need to explain further what you are trying to do - show more config - upstream and downstream.
EDIT
Bear in mind that will cause an infinite loop because the failed message will be retried indefinitely.
Sending to JMS should not make any different compared to this test case, which works exactly as I would expect (including the infinite loop)...
public interface GW {
void send(String foo);
}
<int:gateway id="gw" service-interface="com.example.GW" default-request-channel="foo"
error-channel="errorChannel"/>
<int:service-activator input-channel="foo" expression = "1 / 0"> <!-- always fail -->
<int:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.RequestHandlerCircuitBreakerAdvice">
<property name="threshold" value="1" />
<property name="halfOpenAfter" value="10000" />
</bean>
</int:request-handler-advice-chain>
</int:service-activator>
<int:bridge input-channel="errorChannel" output-channel="retryChannel" />
<int:channel id="retryChannel">
<int:queue />
</int:channel>
<int:chain input-channel="retryChannel">
<int:transformer expression="payload.failedMessage.payload" />
<int:service-activator ref="gw" />
<int:poller fixed-delay="1000" />
</int:chain>
Result:
org.springframework.messaging.MessageHandlingException: Expression evaluation failed: 1 / 0;
...
Caused by: java.lang.ArithmeticException: / by zero
CircuitBreakerOpenException: Circuit Breaker is Open for ServiceActivator for [ExpressionEvaluatingMessageProcessor for: [1 / 0]]
CircuitBreakerOpenException: Circuit Breaker is Open for ServiceActivator for [ExpressionEvaluatingMessageProcessor for: [1 / 0]]
CircuitBreakerOpenException: Circuit Breaker is Open for ServiceActivator for [ExpressionEvaluatingMessageProcessor for: [1 / 0]]
Above is my edited configuration that worked for me to implement the circuit breaker, after all the changes I still had this issue with halfOpenAfter time, which I increased to 60000 and it worked the above solution in the question should work for circuit breaker, follow the comment of me and Gary Russell.

Session destroy event with JTA Transaction

I am working on JBOSS application and I am facing issue for loggin sessionTimeout event
I want to make JTA transaction once the session is expire.
My code is as follow
spring.xml
...
<bean
class="org.springframework.orm.hibernate3.HibernateTransactionManager"
id="transactionManager">
<property name="sessionFactory" >
<ref local="sessionFactory" />
</property>
</bean>
<bean id="msg" class="com.Message">
....
</bean>
...
web.xml
...
<listener>
<listener-class>
com.demo.MySessionListener
</listener-class>
</listener>
...
Class File
public class MySessionListener implements HttpSessionListener{
public void sessionCreated(HttpSessionEvent event) {
}
public void sessionDestroyed(HttpSessionEvent event) {
HttpSession session = event.getSession();
ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(session.getServletContext());
Message msg = (Message)ctx.getBean("msg");
msg.save(); // throws error org.springframework.transaction.CannotCreateTransactionException
}
}
I found that All the JTA operation must be done in session, But in this case how can I achieve this ? Because I want to perform JTA transaction because of the session expiration event.
Let me know if anyone have solve this problem even before.
Thanks
StackTrace
at org.jboss.web.tomcat.service.session.ClusteredSession.removeAttributeInternal(ClusteredSession.java:1292)
at org.jboss.web.tomcat.service.session.ClusteredSession.expire(ClusteredSession.java:844)
at org.jboss.web.tomcat.service.session.ClusteredSession.expire(ClusteredSession.java:740)
at org.jboss.web.tomcat.service.session.ClusteredSession.isValid(ClusteredSession.java:720)
at org.jboss.web.tomcat.service.session.ClusteredSession.isValid(ClusteredSession.java:685)
at org.jboss.web.tomcat.service.session.JBossCacheManager.processExpires(JBossCacheManager.java:1236)
at org.jboss.web.tomcat.service.session.JBossManager.backgroundProcess(JBossManager.java:817)
at org.jboss.web.tomcat.service.session.JBossCacheManager.backgroundProcess(JBossCacheManager.java:1185)
at org.apache.catalina.core.ContainerBase.backgroundProcess(ContainerBase.java:1270)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1555)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1564)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1564)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1544)
at java.lang.Thread.run(Thread.java:619)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'workContext': Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request? If you are actually operating within a web request and still receive this message,your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:304)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:160)
at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:33)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.getTarget(Cglib2AopProxy.java:661)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:611)
at com.ideas.carparkpro.commons.bean.CproWorkContext$$EnhancerByCGLIB$$ef8de176.getDataSource()
at com.ideas.carparkpro.core.util.UserRoutingDataSource.determineCurrentLookupKey(UserRoutingDataSource.java:103)
... 30 more
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request? If you are actually operating within a web request and still receive this message,your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:102)
at org.springframework.web.context.request.SessionScope.get(SessionScope.java:88)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:285)
... 36 more

Scheduler stopped when deadlock happened in Spring Transaction

I've developed a monitoring application that uses Spring-3.0 Hibernate-3.6.5 with SpringHibernateTemplate as DAO. Database SQL Server 2008. This app has long running transaction every day.
#Repository("areaDaoHibernate")
public class AreaDAO implements IArea {
protected HibernateTemplate template = null;
#Autowired #Required
public void setSessionFactory(SessionFactory sessionFactory) {
template = new HibernateTemplate(sessionFactory);
}
// basic dao method
}
This app has some scheduling (implements Runable) via springbeans.
#Component("dailyTask")
#Scope("prototype")
public class DailyTask implements Runnable {
#Override
public void run() {
// running task
}
}
This app uses Apache DBCP for pooling.
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource">
<ref bean="CustodyDataSource" />
</property>
…
</bean>
<bean id = "CustodyDataSource"
class = "org.apache.commons.dbcp.BasicDataSource"
p:driverClassName = "${jdbc1.driverClassName}"
p:url = "${jdbc1.url}"
p:username = "${jdbc1.username}"
p:password = "${jdbc1.password}"
p:maxIdle = "${jdbc1.maxIdle}"
p:maxActive = "${jdbc1.maxActive}"
p:validationQuery = "${jdbc1.validationQuery}"
p:testWhileIdle = "${jdbc1.testWhileIdle}" />
<bean id = "transactionManager"
class = "org.springframework.orm.hibernate3.HibernateTransactionManager"
p:sessionFactory-ref = "sessionFactory" />
I have problem when this app deadlock, the scheduling stop to run.
The error shows:
2012-12-27 12:57:20,861 WARN (JDBCExceptionReporter:233) - SQL Error: 1205, SQLState: 40001
2012-12-27 12:57:20,862 ERROR (JDBCExceptionReporter:234) - Transaction (Process ID 55) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
2012-12-27 12:57:20,866 ERROR (DailyTask:238) - org.springframework.dao.CannotAcquireLockException: could not update: [com.btpn.custody.entity.MasterData#00176ed4-b7a2-4da5-a266-ffaab3654050]; SQL [update T_CIF set __UPDATED_DATE=?, STATUS_MD=? where __SID=?]; nested exception is org.hibernate.exception.LockAcquisitionException: could not update: [com.btpn.custody.entity.MasterData#00176ed4-b7a2-4da5-a266-ffaab3654050]
at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:633)
at org.springframework.orm.hibernate3.HibernateTransactionManager.convertHibernateAccessException(HibernateTransactionManager.java:793)
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:664)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at $Proxy33.relateWithBranch(Unknown Source)
at com.btpn.custody.bean.DailyTask.relateMasterDataWithBranch(DailyTask.java:1166)
at com.btpn.custody.bean.DailyTask.manualInitial(DailyTask.java:213)
at com.btpn.custody.bean.DailyTask.run(DailyTask.java:197)
at java.lang.Thread.run(Unknown Source)
Caused by: org.hibernate.exception.LockAcquisitionException: could not update: [com.btpn.custody.entity.MasterData#00176ed4-b7a2-4da5-a266-ffaab3654050]
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:107)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2612)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2494)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2821)
at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:113)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:273)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:265)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:185)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:383)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:133)
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:656)
... 11 more
Caused by: java.sql.SQLException: Transaction (Process ID 55) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
at net.sourceforge.jtds.jdbc.SQLDiagnostic.addDiagnostic(SQLDiagnostic.java:368)
at net.sourceforge.jtds.jdbc.TdsCore.tdsErrorToken(TdsCore.java:2820)
at net.sourceforge.jtds.jdbc.TdsCore.nextToken(TdsCore.java:2258)
at net.sourceforge.jtds.jdbc.TdsCore.getMoreResults(TdsCore.java:632)
at net.sourceforge.jtds.jdbc.JtdsStatement.processResults(JtdsStatement.java:584)
at net.sourceforge.jtds.jdbc.JtdsStatement.executeSQL(JtdsStatement.java:546)
at net.sourceforge.jtds.jdbc.JtdsPreparedStatement.executeUpdate(JtdsPreparedStatement.java:504)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:102)
at org.hibernate.jdbc.NonBatchingBatcher.addToBatch(NonBatchingBatcher.java:46)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2590)
... 23 more
So my questions:
Should I change connection pool to avoid deadlock problem?
Why my scheduling bean stop run after deadlock happen?
The deadlock occurred in SQL Server. This is neither Spring, Hibernate, the connection pool nor SQL Servers fault. The problem is that your application is locking rows that either your application, or other applications are also trying to lock (in a deadlock manner).
The easiest fix is probably to have a retry-strategy for your job (just retry the whole transaction if it fails). You could also try to figure out exactly which applications / users were involved in the deadlock by looking at the SQL Server logs.

Resources