From 272fcd044d1b15b95c9fa94caf7b91b3d18c40e5 Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Tue, 2 Jul 2024 15:38:07 -0400 Subject: [PATCH] GH-9297: Improve `ImapIdleChannelAdapter.callIdle()` for cause Fixes: #9297 For a normal `jakarta.mail.StoreClosedException: * BYE Jakarta Mail Exception: java.net.SocketException: Connection reset` the `ImapIdleChannelAdapter.callIdle()` results in a resubmission (and reconnection) with logging 'Failed to execute IDLE task. Will attempt to resubmit in 10000 milliseconds.'` However, when `selectorExpression` is in used and that one is based on mail `Message` object, we may fail with `FolderClosedException` which is wrapped to the `SpelEvaluationException` and some other stack traces. So, `jakarta.mail.MessagingException` might be deep in the cause chain. * Fix `ImapIdleChannelAdapter` via introducing `getJakartaMailMessagingExceptionFromCause()` utility method which searches for the `jakarta.mail.MessagingException` in cause chain. * Rework `ImapIdleChannelAdapter.callIdle()` logic to rely on this new method **Auto-cherry-pick to `6.3.x` & `6.2.x`** --- .../mail/ImapIdleChannelAdapter.java | 48 +++++++++++++------ 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/spring-integration-mail/src/main/java/org/springframework/integration/mail/ImapIdleChannelAdapter.java b/spring-integration-mail/src/main/java/org/springframework/integration/mail/ImapIdleChannelAdapter.java index 8782442c06..943a854a6d 100755 --- a/spring-integration-mail/src/main/java/org/springframework/integration/mail/ImapIdleChannelAdapter.java +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/ImapIdleChannelAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,6 +36,7 @@ import org.springframework.integration.mail.event.MailIntegrationEvent; import org.springframework.integration.transaction.IntegrationResourceHolder; import org.springframework.integration.transaction.IntegrationResourceHolderSynchronization; import org.springframework.integration.transaction.TransactionSynchronizationFactory; +import org.springframework.lang.Nullable; import org.springframework.messaging.MessagingException; import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; @@ -200,22 +201,24 @@ public class ImapIdleChannelAdapter extends MessageProducerSupport implements Be } catch (Exception ex) { publishException(ex); - if (this.shouldReconnectAutomatically - && ex.getCause() instanceof jakarta.mail.MessagingException messagingException) { + if (this.shouldReconnectAutomatically) { + jakarta.mail.MessagingException messagingException = + getJakartaMailMessagingExceptionFromCause(ex.getCause()); - //run again after a delay - logger.info(messagingException, - () -> "Failed to execute IDLE task. Will attempt to resubmit in " - + this.reconnectDelay + " milliseconds."); - delayNextIdleCall(); - } - else { - logger.warn(ex, - "Failed to execute IDLE task. " + - "Won't resubmit since not a 'shouldReconnectAutomatically' " + - "or not a 'jakarta.mail.MessagingException'"); - break; + if (messagingException != null) { + //run again after a delay + logger.info(messagingException, + () -> "Failed to execute IDLE task. Will attempt to resubmit in " + + this.reconnectDelay + " milliseconds."); + delayNextIdleCall(); + continue; + } } + logger.warn(ex, + "Failed to execute IDLE task. " + + "Won't resubmit since not a 'shouldReconnectAutomatically' " + + "or not a 'jakarta.mail.MessagingException'"); + break; } } } @@ -256,6 +259,21 @@ public class ImapIdleChannelAdapter extends MessageProducerSupport implements Be } } + @Nullable + private static jakarta.mail.MessagingException getJakartaMailMessagingExceptionFromCause(Throwable cause) { + if (cause == null) { + return null; + } + if (cause instanceof jakarta.mail.MessagingException messagingException) { + return messagingException; + } + Throwable nextCause = cause.getCause(); + if (cause == nextCause) { + return null; + } + return getJakartaMailMessagingExceptionFromCause(nextCause); + } + private class MessageSender implements Consumer { MessageSender() {