grails application shuts down as a result of caught exception in init - spring

I have an grails 2.4.5 application that runs the init closure from bootstrap.groovy upon startup. It may throw an exception or it may not, but in either case, the application must continue running. What actually happens is during init() the app can't communicate with the server, a SocketTimeoutException is thrown by the underlying library and is then caught by my code. The caught exception unexpectedly causes the application to invoke destroy() and the application shuts down which I don't want. I expected that since the exception is caught, nothing wrong should be reported, and the application should remain running.
Here's the code using the RestTemplate library that causes the application to be destroyed. Without this code it remains running. The exception is caught and printed and then the app shuts down.
init = { servletContext ->
try {
// Send a post request
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", auth);
headers.add(HttpHeaders.CONTENT_TYPE, "application/json");
HttpEntity entity = new HttpEntity(headers);
HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
httpRequestFactory.setConnectTimeout(10000);
httpRequestFactory.setReadTimeout(10000);
RestTemplate rt = new RestTemplate(httpRequestFactory);
ResponseEntity<String> rtString = rt.exchange(ME_URL + '/apps/register', HttpMethod.POST, entity, String.class);
} catch (Exception e) {
// don't rethrow
println e.getMessage(); // gets here, app is shutdown
}
}
I assumed this has something to do with the library I used (RestTemplate) so I also tried using the Unirest library. This also catches the exception but shuts down the application instead of continuing.
init = {
try {
res = Unirest
.post(ME_URL + '/apps/register')
.header('Authorization', auth)
.header('Content-Type', 'application/json')
.body([clientId: CLIENT_ID])
.asObject(Map.class);
} catch (Exception e) {
// don't rethrow
println e.getMessage(); // gets here, app is shutdown
}
}
Now I began to think that maybe exceptions are leaking from init(), so I tried to manually throw an exception on purpose to test this theory. I tried just a base Exception as well as the SocketTimeoutException that the previous two libraries threw. This is interesting because I've proven that this exception doesn't somehow "leak" out of grails init closure. This code below does NOT cause the application to shutdown and it remains running, which is what I want.
init = {
try {
throw new java.net.SocketTimeoutException("fake exception");
} catch (Exception e) {
// don't rethrow
println e.getMessage(); // gets here, but app continues
}
}
Generally the only thing printed out in the error log is a stacktrace for a java.net.SocketTimeoutException, and my own instrumentation code to show that destroy() is indeed being called.
2017-01-09 12:01:51,825 [localhost-startStop-1] ERROR StackTrace -
Full Stack Trace: XXX.YYY.ZZZ.AAA.GatewayException:
java.net.SocketTimeoutException: Read timed out at
XXX.YYY.ZZZ.registerApp(GatewayService.groovy:83) at
XXX.YYY.ZZZ.initApp(GatewayService.groovy:50) at
XXX.YYY.ZZZ.init(GatewayService.groovy:39) at
XXX.YYY.ZZZ.slientInit(GatewayService.groovy:30) at
XXX.YYY.ZZZ.BBB(Initializer.groovy:88) at
BootStrap$_closure1.doCall(BootStrap.groovy:17) at
grails.util.Environment.evaluateEnvironmentSpecificBlock(Environment.java:327)
at
grails.util.Environment.executeForEnvironment(Environment.java:320)
at
grails.util.Environment.executeForCurrentEnvironment(Environment.java:296)
at java.util.concurrent.FutureTask.run(FutureTask.java:266) at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745) Caused by:
com.mashape.unirest.http.exceptions.UnirestException:
java.net.SocketTimeoutException: Read timed out at
com.mashape.unirest.http.HttpClientHelper.request(HttpClientHelper.java:143)
at
com.mashape.unirest.request.BaseRequest.asObject(BaseRequest.java:80)
at XXX.YYY.ZZZ.registerApp(GatewayService.groovy:71) ... 12 more
What I am looking for is:
Why this is happening
What I can do to fix this problem
If there's nothing I can do to fix my existing code, what is a workaround? Workarounds that I've tried: using PostContext annotation and also afterPropertiesSet method after extending InitializingBean. These two solutions do not work, as the init code I use will eventually use GORM, so GORM must be running at the time the code runs. During these two hooks, GORM is not ready (Or so that error messages say).
I am using grails 2.4.5.
One more thing: I have eliminated all my code in init except for the code you see above, so I am fairly sure that these REST calls getting exceptions is directly responsible for the application shutting down.
Any help greatly appreciated. Thanks!

Related

How to simply requeue a RabbitMQ message in case of code exception with SpringBoot

I would like to learn an easy way to requeue a RabbitMQ if an exception is thrown in an SpringBoot application.
#RabbitListener(queues = TRANSACTION_171_REQUEST_QUEUE, errorHandler = "receiverExceptionHandler" )
public void listen171RequestsQueue(Transaction171Request request) {
try {
Transaction171Response response = null;
send171Response("OK", request.getNumeroFormularioRenach());
} catch (Exception e){
//Requeue message
}
}
My code behaviour is to consume a message and take it out of the queue independing of what it happens. I would like to requeue message in RabbitMQ if an exception is thrown.
Could you help me?
I am working in a SpringBoot application with Java 11.
RabbitMQ's default behavior is to requeue when there is an unhandled exception. In your case, you could remove the try..catch block or in the catch block just re-throw the exception.

RabbitMQ infinite loop issue

I have an issue using rabbitMQ to send a message from a service A to service B which also sends a notification to service C , the problem is, i had to put the #RabbitListener and the Rabbittemplate in the same method like so :
#Autowired private RabbitTemplate template;
#RabbitListener(queues=RabbitConfig.QUEUETD)
public ResponseEntity<String> AddSas_Campaign(SasCampaign sasCampaign){
if
//...
template.convertAndSend(RabbitConfig.EXCHANGE,RabbitConfig.ROUTING_KEY,sasCampaign);
return new ResponseEntity<String>( "New line inserted ", HttpStatus.OK);}
//...
}
else return new ResponseEntity<String>("Campaign Code exists",HttpStatus.OK);
}
and it is creating and infinite loop of (+2000/min) messages and exceptions non stop.
2021-06-29 14:34:16,560 WARN [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#1-1] org.springframework.amqp.rabbit.listener.ConditionalRejectingErrorHandler: Execution of Rabbit message listener failed.
org.springframework.amqp.rabbit.support.ListenerExecutionFailedException: Listener threw exception
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.wrapToListenerExecutionFailedExceptionIfNeeded(AbstractMessageListenerContainer.java:1746)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1636)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1551)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1539)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:1530)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1474)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:967)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:913)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1600(SimpleMessageListenerContainer.java:83)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.mainLoop(SimpleMessageListenerContainer.java:1288)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1194)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.amqp.rabbit.listener.adapter.ReplyFailureException: Failed to send reply with payload 'InvocationResult [returnValue=<200 OK OK,Test SasCamapign inserted ,[]>, returnType=org.springframework.http.ResponseEntity<java.lang.String>, bean=tn.itserv.services.Sas_CampaignService#4232ecc, method=public org.springframework.http.ResponseEntity tn.itserv.services.Sas_CampaignService.AddSas_Campaign(tn.itserv.entities.SasCampaign)]'
at org.springframework.amqp.rabbit.listener.adapter.AbstractAdaptableMessageListener.doHandleResult(AbstractAdaptableMessageListener.java:476)
at org.springframework.amqp.rabbit.listener.adapter.AbstractAdaptableMessageListener.handleResult(AbstractAdaptableMessageListener.java:400)
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandlerAndProcessResult(MessagingMessageListenerAdapter.java:152)
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:135)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1632)
... 10 common frames omitted
Caused by: org.springframework.amqp.AmqpException: Cannot determine ReplyTo message property value: Request message does not contain reply-to property, and no default response Exchange was set.
at org.springframework.amqp.rabbit.listener.adapter.AbstractAdaptableMessageListener.getReplyToAddress(AbstractAdaptableMessageListener.java:576)
at org.springframework.amqp.rabbit.listener.adapter.AbstractAdaptableMessageListener.doHandleResult(AbstractAdaptableMessageListener.java:472)
... 14 common frames omitted
To be honest I haven't understood 100% how it works ,should I create different Queue to every exchange ? because it works partially (From Service A to B or From B to C ) But when i use both it creates these exceptions.
I've experienced a similar problem in production that rears its head a couple times a year. The concept of 'requeue on failure' seems like a bad idea, but it's what was implemented by the previous developer.
The cause is that the message is put back in the queue on exception. To fix it, you would not throw an exception from your RabbitListener. Try surrounding with a try catch and return a response entity with a server error.
#Autowired private RabbitTemplate template;
#RabbitListener(queues=RabbitConfig.QUEUETD)
public ResponseEntity<String> AddSas_Campaign(SasCampaign sasCampaign){
try {
if
//...
template.convertAndSend(RabbitConfig.EXCHANGE,RabbitConfig.ROUTING_KEY,sasCampaign);
return new ResponseEntity<String>( "New line inserted ", HttpStatus.OK);}
//...
}
else return new ResponseEntity<String>("Campaign Code exists",HttpStatus.OK);
} catch(Exception e) {
return new ResponseEntity<String>("Error on Message Processing", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
So i realized that a #RabbitListener function should have no return value which means void instead of my ResponseEntity so my idea was like to create a new method that calls my method and has no return value:
#RabbitListener(queues=RabbitConfig.QUEUETD)
public void receiveFromService(SasCampaign sasCampaign){
AddSas_Campaign(sasCampaign);
}

Catching exception in Kafkalistener Integration test

So I need to create an integration test for my kafkalistener method, where the test expects ListenerExecutionFailedException is actually thrown because the message was failed during consumption due to another service being inactive.
Below is the test code, where I use embeddedkafkabroker for producer and consumer:
#Test(expected = ListenerExecutionFailedException.class)
public void shouldThrowException() {
RecordHeaders recordHeaders = new RecordHeaders();
recordHeaders.add(new RecordHeader("messageType", "bootstrap".getBytes()));
recordHeaders.add(new RecordHeader("userId", "123".getBytes()));
recordHeaders.add(new RecordHeader("applicationId", "1234".getBytes()));
recordHeaders.add(new RecordHeader("correlationId", UUID.randomUUID().toString().getBytes()));
ProducerRecord<String, String> producerRecord = new ProducerRecord<>(
"TEST_TOPIC",
1,
null,
"message",
"",
recordHeaders);
producer.send(producerRecord);
consumer.subscribe(Collections.singleton("TEST_TOPIC"));
consumer.poll(Duration.ofSeconds(2));
}
What I'm wondering is the exception was considered as not thrown and the test fails even though I know the message is indeed received by the listener and the exception was thrown since I saw them on the log.
And even though I changed the expected into Throwable no exception seems to be detected.
What should I do to make the exception to be detected by Junit?
Also, another interesting thing is that I tried to mock the service class which was called in the listener and return some dummy value but the service is not called when I used Mockito.verify
You seem to have some misunderstanding.
producer.send
consumer.poll
You are calling the kafka-clients directly and are not using Spring at all in this test.
ListenerExecutionFailedException is an exception that Spring's listener container wraps user exceptions thrown by message listeners.

if the exceptions have been swallowed, how does the spring aop log the exceptions' messages?

Here is my example bellow
public void test() throws Exception {
try {
int i = 1/0;
System.out.println(i);
} catch (Exception e) {
//the exception have been swallowed.
}
}
and the problem is spring aop's AfterThrowing can't work for this. if i remove the try-catch block.it works well then. how can i solve this problem. thanks for any suggestions.
Although it's unclear from question what you intend to achieve but this is the intended behavior of #AfterThrowing advice.
From docs -
After throwing advice runs when a matched method execution exits by throwing an exception.
which means that the advice will be executed only if an exception is thrown from method. In your sample the the exception is consumed and method exits normally thus no advice execution occurs.

Handling code/configuration error from entry action classes in spring state machine

I am using a state machine builder to build state machine in my app.
Also the application has Action classes which implements org.springframework.statemachine.action.Action.
These Action classes are for executing entry actions for each stages.
If any exception is thrown from these Action classes, ie from execute(StateContext paramStateContext) method, I wanted to catch that exception and send an event(Terminated) and drive the state machine to End state, after updating the db with error details.
I tried to use state machine listener by overriding stateMachineError(StateMachine stateMachine, Exception e) method. But unfortunately this is not working.
Any other spring state machine component to catch exceptions, before I go to wrap the entire code in Action classes with try catch, and inside catch block sending the Terminated event so that state machine would navigate End state.
Here is the builder Iam using.
Builder<String, String> builder = StateMachineBuilder
.<String, String> builder();
builder.configureConfiguration()
.withConfiguration()
.autoStartup(false)
.listener(listener())
.beanFactory(
this.applicationContext.getAutowireCapableBeanFactory());
private StateMachineListener<String, String> listener() {
return new StateMachineListenerAdapter<String, String>() {
#Override
public void stateChanged(
org.springframework.statemachine.state.State<String, String> from,
org.springframework.statemachine.state.State<String, String> to) {
LOGGER.debug("State change to " + to.getId());
}
#Override
public void stateMachineError(
StateMachine<String, String> stateMachine, Exception e) {
e.printStackTrace();
LOGGER.debug("Ah... I am not getting executed when exception occurs from entry actions");
LOGGER.debug("Error occured from " + stateMachine.getState()
+ "and the error is" + e.toString());
}
};
}
Iam using 1.1.0.RELEASE version of spring-statemachine-core
You're correct, neither stateMachineError or a method annotated with #onStateMachineError are executed on an error. This is addressed in version 1.2, which is at milestone 1 right now. They introduced errorAction which is executed with the state machine context:
User can always catch exceptions manually but with actions defined for transitions it is possible to define error action which is called if exception is raised. Exception is then available from a StateContext passed to that action.
All that's needed is to specify an error action along with the desired action when defining a transition in the state machine configuration class. From the example in the documentation:
public void configure(StateMachineTransitionConfigurer<States, Events> transitions)
throws Exception {
transitions
.withExternal()
.source(States.S1)
.target(States.S2)
.event(Events.E1)
.action(action(), errorAction());
}
A detailed discussion of this can be found in Spring Statemachine issue #240.

Resources