This is an automated email from the ASF dual-hosted git repository. btellier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
The following commit(s) were added to refs/heads/master by this push: new c227d787b4 [FIX] Fix possible NPE when audit trail in DeleteProcessor (#2732) c227d787b4 is described below commit c227d787b4673e4ca98accb38d67f5b49fe3825e Author: Trần Hồng Quân <55171818+quantranhong1...@users.noreply.github.com> AuthorDate: Mon Jun 2 21:41:08 2025 +0700 [FIX] Fix possible NPE when audit trail in DeleteProcessor (#2732) {"timestamp":"2025-05-30T07:02:53.647Z","level":"WARN","thread":"lettuce-epollEventLoop-4-4","logger":"org.apache.james.util.AuditTrail","message":"Exception while providing AuditTrail parameters","context":"default","exception":"java.lang.NullPointerException: Cannot invoke \"org.apache.james.imap.api.process.SelectedMailbox.getMailboxId()\" because \"selected\" is null at org.apache.james.imap.processor.DeleteProcessor.lambda$auditTrail$11(DeleteProcessor.java:117) at org.apache.james.util.AuditTrail$Entry.parameters(AuditTrail.java:101) at org.apache.james.imap.processor.DeleteProcessor.auditTrail(DeleteProcessor.java:116) at org.apache.james.imap.processor.DeleteProcessor.lambda$processRequestReactive$1(DeleteProcessor.java:75) at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onComplete(MonoPeekTerminal.java:289) at reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber.onComplete(MonoIgnoreElements.java:89) at reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber.onComplete(MonoIgnoreElements.java:89) at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:210) at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:239) at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:204) at reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber.onComplete(MonoIgnoreElements.java:89) at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:85) at reactor.core.publisher.MonoFlatMap$FlatMapMain.secondComplete(MonoFlatMap.java:246) at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:305) at reactor.core.publisher.MonoFlatMap$FlatMapMain.secondComplete(MonoFlatMap.java:245) at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:305) at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.complete(MonoIgnoreThen.java:294) at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onNext(MonoIgnoreThen.java:188) at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:237) at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:204) at reactor.core.publisher.SerializedSubscriber.onComplete(SerializedSubscriber.java:146) at reactor.core.publisher.FluxRetryWhen$RetryWhenMainSubscriber.onComplete(FluxRetryWhen.java:204) at reactor.core.publisher.MonoFlatMap$FlatMapMain.secondComplete(MonoFlatMap.java:250) at reactor.core.publisher.MonoFlatMap$FlatMapInner.onComplete(MonoFlatMap.java:324) at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:210) at reactor.core.publisher.MonoUsing$MonoUsingSubscriber.onComplete(MonoUsing.java:283) at reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber.onComplete(MonoIgnoreElements.java:89) at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260) at reactor.core.publisher.FluxConcatArray$ConcatArraySubscriber.onComplete(FluxConcatArray.java:209) at reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber.onComplete(MonoIgnoreElements.java:89) at reactor.core.publisher.FluxHide$SuppressFuseableSubscriber.onComplete(FluxHide.java:147) at reactor.core.publisher.MonoZip$ZipCoordinator.signal(MonoZip.java:281) at reactor.core.publisher.MonoZip$ZipInner.onComplete(MonoZip.java:527) at reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber.onComplete(MonoIgnoreElements.java:89) at reactor.core.publisher.FluxFlatMap$FlatMapMain.checkTerminated(FluxFlatMap.java:850) at reactor.core.publisher.FluxFlatMap$FlatMapMain.drainLoop(FluxFlatMap.java:612) at reactor.core.publisher.FluxFlatMap$FlatMapMain.innerComplete(FluxFlatMap.java:898) at reactor.core.publisher.FluxFlatMap$FlatMapInner.onComplete(FluxFlatMap.java:1001) at reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber.onComplete(MonoIgnoreElements.java:89) at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onComplete(Operators.java:2231) at reactor.core.publisher.SerializedSubscriber.onComplete(SerializedSubscriber.java:146) at reactor.core.publisher.FluxTimeout$TimeoutMainSubscriber.onComplete(FluxTimeout.java:235) at reactor.core.publisher.FluxFlatMap$FlatMapMain.checkTerminated(FluxFlatMap.java:850) at reactor.core.publisher.FluxFlatMap$FlatMapMain.drainLoop(FluxFlatMap.java:612) at reactor.core.publisher.FluxFlatMap$FlatMapMain.drain(FluxFlatMap.java:592) at reactor.core.publisher.FluxFlatMap$FlatMapMain.onComplete(FluxFlatMap.java:469) at io.lettuce.core.RedisPublisher$ImmediateSubscriber.onComplete(RedisPublisher.java:900) at io.lettuce.core.RedisPublisher$State.onAllDataRead(RedisPublisher.java:702) at io.lettuce.core.RedisPublisher$State$3.read(RedisPublisher.java:612) at io.lettuce.core.RedisPublisher$State$3.onDataAvailable(RedisPublisher.java:569) at io.lettuce.core.RedisPublisher$RedisSubscription.onDataAvailable(RedisPublisher.java:326) at io.lettuce.core.RedisPublisher$RedisSubscription.onAllDataRead(RedisPublisher.java:341) at io.lettuce.core.RedisPublisher$SubscriptionCommand.doOnComplete(RedisPublisher.java:782) at io.lettuce.core.protocol.CommandWrapper.complete(CommandWrapper.java:65) at io.lettuce.core.protocol.CommandWrapper.complete(CommandWrapper.java:63) at io.lettuce.core.protocol.CommandHandler.complete(CommandHandler.java:745) at io.lettuce.core.protocol.CommandHandler.decode(CommandHandler.java:680) at io.lettuce.core.protocol.CommandHandler.channelRead(CommandHandler.java:597) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1407) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:918) at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:799) at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:501) at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:399) at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:994) at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.base/java.lang.Thread.run(Unknown Source) "} Because SelectedMailbox could be deselected before AuditTrail, therefore null SelectedMailbox could be used. I propose we audit the mailboxPath instead: - mailboxId could be null - The deleted mailbox may not be the same as the selected mailbox (id) - After the mailbox is deleted, logging its mailboxId may not be meaningful - If we insist on logging the mailboxId, it may need extra DB requests --- .../main/java/org/apache/james/imap/processor/DeleteProcessor.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/DeleteProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/DeleteProcessor.java index add768ee41..acfef263e2 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/DeleteProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/DeleteProcessor.java @@ -72,7 +72,7 @@ public class DeleteProcessor extends AbstractMailboxProcessor<DeleteRequest> { .then(unsolicitedResponses(session, responder, false)) .then(Mono.fromRunnable(() -> okComplete(request, responder))) .then() - .doOnSuccess(any -> auditTrail(session, selected)) + .doOnSuccess(any -> auditTrail(session, mailboxPath)) .onErrorResume(MailboxNotFoundException.class, e -> { no(request, responder, HumanReadableText.FAILURE_NO_SUCH_MAILBOX); return ReactorUtils.logAsMono(() -> LOGGER.debug("Delete failed for mailbox {} as it doesn't exist", mailboxPath, e)); @@ -107,14 +107,14 @@ public class DeleteProcessor extends AbstractMailboxProcessor<DeleteRequest> { .addToContext("mailbox", request.getMailboxName()); } - private void auditTrail(ImapSession session, SelectedMailbox selected) { + private void auditTrail(ImapSession session, MailboxPath mailboxPath) { AuditTrail.entry() .username(() -> session.getUserName().asString()) .sessionId(() -> session.sessionId().asString()) .protocol("IMAP") .action("DELETE") .parameters(() -> ImmutableMap.of("loggedInUser", session.getMailboxSession().getLoggedInUser().map(Username::asString).orElse(""), - "mailboxId", selected.getMailboxId().serialize())) + "mailboxPath", mailboxPath.asString())) .log(String.format("IMAP DELETE succeeded.")); } } --------------------------------------------------------------------- To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org For additional commands, e-mail: notifications-h...@james.apache.org