[ 
https://issues.apache.org/jira/browse/CAMEL-24403?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

mayur mohan updated CAMEL-24403:
--------------------------------
    Component/s: camel-mail
    Description: 
h2. Problem

When an IMAP message is expunged (deleted by another client or server-side 
policy) between the time it is fetched by the MailConsumer and when the 
on-completion commit callback fires, {code}IMAPMessage.setFlag(){code} throws 
{code}jakarta.mail.MessageRemovedException{code}. This exception is constructed 
without a message string so {code}getMessage(){code} returns {code}null{code}, 
which surfaces in the Camel error log as:

{noformat}
Error occurred during committing mail message: 
com.sun.mail.imap.IMAPMessage@542a494d. 
Exchange[B5882D23412732A-0000000000000013]. Caused by: 
[javax.mail.MessageRemovedException - null]
{noformat}

The trailing {code}- null{code} is confusing and unhelpful to operators 
diagnosing the issue.

h2. Stack Trace

{noformat}
javax.mail.MessageRemovedException
  at com.sun.mail.imap.IMAPMessage.checkExpunged(IMAPMessage.java:280)
  at com.sun.mail.imap.IMAPMessage.setFlags(IMAPMessage.java:1110)
  at javax.mail.Message.setFlag(Message.java:596)
  at 
org.apache.camel.component.mail.MailConsumer.processCommit(MailConsumer.java:506)
  at 
org.apache.camel.component.mail.MailConsumer$1.onComplete(MailConsumer.java:237)
  at 
org.apache.camel.support.UnitOfWorkHelper.doneSynchronization(UnitOfWorkHelper.java:104)
  at 
org.apache.camel.support.UnitOfWorkHelper.doneSynchronizations(UnitOfWorkHelper.java:89)
  at 
org.apache.camel.impl.engine.DefaultUnitOfWork.done(DefaultUnitOfWork.java:238)
  at org.apache.camel.support.UnitOfWorkHelper.doneUow(UnitOfWorkHelper.java:61)
  at 
org.apache.camel.impl.engine.CamelInternalProcessor$UnitOfWorkProcessorAdvice.after(CamelInternalProcessor.java:770)
  at 
org.apache.camel.impl.engine.CamelInternalProcessor$UnitOfWorkProcessorAdvice.after(CamelInternalProcessor.java:705)
  at 
org.apache.camel.impl.engine.CamelInternalProcessor$AsyncAfterTask.done(CamelInternalProcessor.java:263)
  at org.apache.camel.AsyncCallback.run(AsyncCallback.java:44)
  at 
org.apache.camel.impl.engine.DefaultReactiveExecutor$Worker.schedule(DefaultReactiveExecutor.java:193)
  at 
org.apache.camel.impl.engine.DefaultReactiveExecutor.scheduleMain(DefaultReactiveExecutor.java:64)
  at org.apache.camel.processor.Pipeline.process(Pipeline.java:185)
  at 
org.apache.camel.impl.engine.CamelInternalProcessor.process(CamelInternalProcessor.java:398)
  at 
org.apache.camel.impl.engine.DefaultAsyncProcessorAwaitManager.process(DefaultAsyncProcessorAwaitManager.java:83)
  at 
org.apache.camel.support.AsyncProcessorSupport.process(AsyncProcessorSupport.java:41)
  at 
org.apache.camel.component.mail.MailConsumer.processExchange(MailConsumer.java:449)
  at 
org.apache.camel.component.mail.MailConsumer.processBatch(MailConsumer.java:258)
  at org.apache.camel.component.mail.MailConsumer.poll(MailConsumer.java:165)
  at 
org.apache.camel.support.ScheduledPollConsumer.doRun(ScheduledPollConsumer.java:202)
  at 
org.apache.camel.support.ScheduledPollConsumer.run(ScheduledPollConsumer.java:116)
  at 
org.apache.camel.pollconsumer.quartz.QuartzScheduledPollConsumerJob.execute(QuartzScheduledPollConsumerJob.java:61)
  at org.quartz.core.JobRunShell.run(JobRunShell.java:202)
  at 
org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)
{noformat}

h2. Root Cause

{code}MessageRemovedException{code} extends {code}MessagingException{code} but 
is constructed without a message string, so {code}e.getMessage(){code} returns 
{code}null{code}. The existing {code}catch (MessagingException e){code} block 
in {code}processCommit(){code} passes it directly to the exception handler, 
which includes the null in the formatted log output.

h2. Fix

Catch {code}MessageRemovedException{code} before the generic 
{code}MessagingException{code} handler in {code}processCommit(){code} and wrap 
it with an explicit description:

{code:java}
} catch (MessageRemovedException e) {
    MessagingException wrapped = new MessagingException(
            "Message already removed/expunged on server (no flag update 
possible)", e);
    getExceptionHandler().handleException(
            "Error occurred during committing mail message: " + mail, exchange, 
wrapped);
} catch (MessagingException e) {
    getExceptionHandler().handleException(
            "Error occurred during committing mail message: " + mail, exchange, 
e);
}
{code}

Expected log output after fix:

{noformat}
Caused by: [javax.mail.MessagingException - Message already removed/expunged on 
server (no flag update possible)]
{noformat}

h2. Files Changed

* 
{code}components/camel-mail/src/main/java/org/apache/camel/component/mail/MailConsumer.java{code}
** Added {code}jakarta.mail.MessageRemovedException{code} import
** Added dedicated {code}catch (MessageRemovedException e){code} block before 
the generic {code}MessagingException{code} handler in 
{code}processCommit(){code}

* 
{code}components/camel-mail/src/test/java/org/apache/camel/component/mail/MailConsumerCommitExpungedMessageTest.java{code}
 (new file)
** {code}testCommitWithExpungedMessageProducesNonNullCause{code}: verifies 
{code}MessageRemovedException{code} is wrapped in a 
{code}MessagingException{code} with a non-null message and root cause preserved
** {code}testCommitWithOtherMessagingExceptionPassedThroughAsIs{code}: verifies 
other {code}MessagingException{code} types pass through unchanged

h2. Pull Request

https://github.com/apache/camel/pull/25553

h2. Test Results

{noformat}
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0  [JDK 21 / Maven 3.9]
BUILD SUCCESS
{noformat}

  was:test update


> camel-mail: MessageRemovedException in processCommit causes null in error log
> -----------------------------------------------------------------------------
>
>                 Key: CAMEL-24403
>                 URL: https://issues.apache.org/jira/browse/CAMEL-24403
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-mail
>            Reporter: mayur mohan
>            Priority: Major
>
> h2. Problem
> When an IMAP message is expunged (deleted by another client or server-side 
> policy) between the time it is fetched by the MailConsumer and when the 
> on-completion commit callback fires, {code}IMAPMessage.setFlag(){code} throws 
> {code}jakarta.mail.MessageRemovedException{code}. This exception is 
> constructed without a message string so {code}getMessage(){code} returns 
> {code}null{code}, which surfaces in the Camel error log as:
> {noformat}
> Error occurred during committing mail message: 
> com.sun.mail.imap.IMAPMessage@542a494d. 
> Exchange[B5882D23412732A-0000000000000013]. Caused by: 
> [javax.mail.MessageRemovedException - null]
> {noformat}
> The trailing {code}- null{code} is confusing and unhelpful to operators 
> diagnosing the issue.
> h2. Stack Trace
> {noformat}
> javax.mail.MessageRemovedException
>   at com.sun.mail.imap.IMAPMessage.checkExpunged(IMAPMessage.java:280)
>   at com.sun.mail.imap.IMAPMessage.setFlags(IMAPMessage.java:1110)
>   at javax.mail.Message.setFlag(Message.java:596)
>   at 
> org.apache.camel.component.mail.MailConsumer.processCommit(MailConsumer.java:506)
>   at 
> org.apache.camel.component.mail.MailConsumer$1.onComplete(MailConsumer.java:237)
>   at 
> org.apache.camel.support.UnitOfWorkHelper.doneSynchronization(UnitOfWorkHelper.java:104)
>   at 
> org.apache.camel.support.UnitOfWorkHelper.doneSynchronizations(UnitOfWorkHelper.java:89)
>   at 
> org.apache.camel.impl.engine.DefaultUnitOfWork.done(DefaultUnitOfWork.java:238)
>   at 
> org.apache.camel.support.UnitOfWorkHelper.doneUow(UnitOfWorkHelper.java:61)
>   at 
> org.apache.camel.impl.engine.CamelInternalProcessor$UnitOfWorkProcessorAdvice.after(CamelInternalProcessor.java:770)
>   at 
> org.apache.camel.impl.engine.CamelInternalProcessor$UnitOfWorkProcessorAdvice.after(CamelInternalProcessor.java:705)
>   at 
> org.apache.camel.impl.engine.CamelInternalProcessor$AsyncAfterTask.done(CamelInternalProcessor.java:263)
>   at org.apache.camel.AsyncCallback.run(AsyncCallback.java:44)
>   at 
> org.apache.camel.impl.engine.DefaultReactiveExecutor$Worker.schedule(DefaultReactiveExecutor.java:193)
>   at 
> org.apache.camel.impl.engine.DefaultReactiveExecutor.scheduleMain(DefaultReactiveExecutor.java:64)
>   at org.apache.camel.processor.Pipeline.process(Pipeline.java:185)
>   at 
> org.apache.camel.impl.engine.CamelInternalProcessor.process(CamelInternalProcessor.java:398)
>   at 
> org.apache.camel.impl.engine.DefaultAsyncProcessorAwaitManager.process(DefaultAsyncProcessorAwaitManager.java:83)
>   at 
> org.apache.camel.support.AsyncProcessorSupport.process(AsyncProcessorSupport.java:41)
>   at 
> org.apache.camel.component.mail.MailConsumer.processExchange(MailConsumer.java:449)
>   at 
> org.apache.camel.component.mail.MailConsumer.processBatch(MailConsumer.java:258)
>   at org.apache.camel.component.mail.MailConsumer.poll(MailConsumer.java:165)
>   at 
> org.apache.camel.support.ScheduledPollConsumer.doRun(ScheduledPollConsumer.java:202)
>   at 
> org.apache.camel.support.ScheduledPollConsumer.run(ScheduledPollConsumer.java:116)
>   at 
> org.apache.camel.pollconsumer.quartz.QuartzScheduledPollConsumerJob.execute(QuartzScheduledPollConsumerJob.java:61)
>   at org.quartz.core.JobRunShell.run(JobRunShell.java:202)
>   at 
> org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)
> {noformat}
> h2. Root Cause
> {code}MessageRemovedException{code} extends {code}MessagingException{code} 
> but is constructed without a message string, so {code}e.getMessage(){code} 
> returns {code}null{code}. The existing {code}catch (MessagingException 
> e){code} block in {code}processCommit(){code} passes it directly to the 
> exception handler, which includes the null in the formatted log output.
> h2. Fix
> Catch {code}MessageRemovedException{code} before the generic 
> {code}MessagingException{code} handler in {code}processCommit(){code} and 
> wrap it with an explicit description:
> {code:java}
> } catch (MessageRemovedException e) {
>     MessagingException wrapped = new MessagingException(
>             "Message already removed/expunged on server (no flag update 
> possible)", e);
>     getExceptionHandler().handleException(
>             "Error occurred during committing mail message: " + mail, 
> exchange, wrapped);
> } catch (MessagingException e) {
>     getExceptionHandler().handleException(
>             "Error occurred during committing mail message: " + mail, 
> exchange, e);
> }
> {code}
> Expected log output after fix:
> {noformat}
> Caused by: [javax.mail.MessagingException - Message already removed/expunged 
> on server (no flag update possible)]
> {noformat}
> h2. Files Changed
> * 
> {code}components/camel-mail/src/main/java/org/apache/camel/component/mail/MailConsumer.java{code}
> ** Added {code}jakarta.mail.MessageRemovedException{code} import
> ** Added dedicated {code}catch (MessageRemovedException e){code} block before 
> the generic {code}MessagingException{code} handler in 
> {code}processCommit(){code}
> * 
> {code}components/camel-mail/src/test/java/org/apache/camel/component/mail/MailConsumerCommitExpungedMessageTest.java{code}
>  (new file)
> ** {code}testCommitWithExpungedMessageProducesNonNullCause{code}: verifies 
> {code}MessageRemovedException{code} is wrapped in a 
> {code}MessagingException{code} with a non-null message and root cause 
> preserved
> ** {code}testCommitWithOtherMessagingExceptionPassedThroughAsIs{code}: 
> verifies other {code}MessagingException{code} types pass through unchanged
> h2. Pull Request
> https://github.com/apache/camel/pull/25553
> h2. Test Results
> {noformat}
> Tests run: 2, Failures: 0, Errors: 0, Skipped: 0  [JDK 21 / Maven 3.9]
> BUILD SUCCESS
> {noformat}



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to