You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2022/04/19 04:04:13 UTC

[james-project] 22/49: JAMES-3737 Small refactorings for IMAP STORE

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

commit c55abbeb729e6cf50687285645f3f8721b85a5cd
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Mar 30 13:42:32 2022 +0700

    JAMES-3737 Small refactorings for IMAP STORE
---
 .../james/imap/processor/StoreProcessor.java       | 418 +++++++++++----------
 1 file changed, 215 insertions(+), 203 deletions(-)

diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/StoreProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/StoreProcessor.java
index dcb185cdf4..a740f0ec16 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/StoreProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/StoreProcessor.java
@@ -30,7 +30,6 @@ import java.util.Set;
 
 import javax.mail.Flags;
 
-import org.apache.james.imap.api.ImapCommand;
 import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.display.HumanReadableText;
 import org.apache.james.imap.api.message.Capability;
@@ -54,7 +53,6 @@ import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.exception.MessageRangeException;
 import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
 import org.apache.james.mailbox.model.MessageRange;
-import org.apache.james.mailbox.model.MessageRange.Type;
 import org.apache.james.metrics.api.MetricFactory;
 import org.apache.james.util.MDCBuilder;
 import org.slf4j.Logger;
@@ -64,11 +62,6 @@ import reactor.core.publisher.Flux;
 
 public class StoreProcessor extends AbstractMailboxProcessor<StoreRequest> {
     private static final Logger LOGGER = LoggerFactory.getLogger(StoreProcessor.class);
-
-    /**
-     * The {@link ImapCommand} which should be used for the response if some CONDSTORE option is used
-     */
-    private static final ImapCommand CONDSTORE_COMMAND = ImapCommand.selectedStateCommand("Conditional STORE");
     
     public StoreProcessor(MailboxManager mailboxManager, StatusResponseFactory factory,
             MetricFactory metricFactory) {
@@ -78,127 +71,33 @@ public class StoreProcessor extends AbstractMailboxProcessor<StoreRequest> {
     @Override
     protected void processRequest(StoreRequest request, ImapSession session, Responder responder) {
         IdRange[] idSet = request.getIdSet();
-        boolean useUids = request.isUseUids();
-        long unchangedSince = request.getUnchangedSince();
+        List<MessageUid> failed = new ArrayList<>();
+        List<NullableMessageSequenceNumber> failedMsns = new ArrayList<>();
+        Flags flags = request.getFlags();
+        List<String> userFlags = Arrays.asList(flags.getUserFlags());
+
+        if (rejectUnchangedSinceZeroWithSystemFlagUpdate(request, responder, idSet, flags)) {
+            return;
+        }
 
         try {
+            SelectedMailbox selected = session.getSelected();
+            MailboxSession mailboxSession = session.getMailboxSession();
+
             MessageManager mailbox = getSelectedMailbox(session)
                 .orElseThrow(() -> new MailboxException("Session not in SELECTED state"));
-            MailboxSession mailboxSession = session.getMailboxSession();
-            Flags flags = request.getFlags();
-
-            if (unchangedSince == 0) {
-                Flags.Flag[] systemFlags = flags.getSystemFlags();
-                if (systemFlags != null && systemFlags.length != 0) {
-                    // we need to return all sequences as failed when using a UNCHANGEDSINCE 0 and the request specify a SYSTEM flags
-                    //
-                    // See RFC4551 3.2. STORE and UID STORE Command;
-                    //
-                    //       Use of UNCHANGEDSINCE with a modification sequence of 0 always
-                    //       fails if the metadata item exists.  A system flag MUST always be
-                    //       considered existent, whether it was set or not.
-                    final StatusResponse response = getStatusResponseFactory().taggedOk(request.getTag(), request.getCommand(), HumanReadableText.FAILED, ResponseCode.condStore(idSet));
-                    responder.respond(response);
-                    return;
-                }
-            } 
-            final List<MessageUid> failed = new ArrayList<>();
-            List<NullableMessageSequenceNumber> failedMsns = new ArrayList<>();
-            final List<String> userFlags = Arrays.asList(flags.getUserFlags());
             for (IdRange range : idSet) {
-                final SelectedMailbox selected = session.getSelected();
-                MessageRange messageSet = messageRange(selected, range, useUids);
-                if (messageSet != null) {
-
-                    if (unchangedSince != -1) {
-                        List<MessageUid> uids = new ArrayList<>();
-
-                        Iterator<ComposedMessageIdWithMetaData> results = Flux.from(
-                            mailbox.listMessagesMetadata(messageSet, mailboxSession))
-                            .toStream()
-                            .iterator();
-                        while (results.hasNext()) {
-                            ComposedMessageIdWithMetaData r = results.next();
-                            MessageUid uid = r.getComposedMessageId().getUid();
-
-                            boolean fail = false;
-
-                            // Check if UNCHANGEDSINCE 0 was used and the Message contains the request flag.
-                            // In such cases we need to fail for this message.
-                            //
-                            // From RFC4551:
-                            //       Use of UNCHANGEDSINCE with a modification sequence of 0 always
-                            //       fails if the metadata item exists.  A system flag MUST always be
-                            //       considered existent, whether it was set or not.
-                            if (unchangedSince == 0) {
-                                String[] uFlags = r.getFlags().getUserFlags();
-                                for (String uFlag : uFlags) {
-                                    if (userFlags.contains(uFlag)) {
-                                        fail = true;
-                                        break;
-                                    }
-                                }
-                            }
-
-                            // Check if the mod-sequence of the message is <= the unchangedsince.
-                            //
-                            // See RFC4551 3.2. STORE and UID STORE Commands
-                            if (!fail && r.getModSeq().asLong() <= unchangedSince) {
-                                uids.add(uid);
-                            } else {
-                                if (useUids) {
-                                    failed.add(uid);
-                                } else {
-                                    failedMsns.add(selected.msn(uid));
-                                }
-                            }
-                        }
-                        List<MessageRange> mRanges = MessageRange.toRanges(uids);
-                        for (MessageRange mRange : mRanges) {
-                            setFlags(request, mailboxSession, mailbox, mRange, session, responder);
-                        }
-                    } else {
-                        setFlags(request, mailboxSession, mailbox, messageSet, session, responder);
-                    }
-
-                }
-
-
+                MessageRange messageSet = messageRange(selected, range, request.isUseUids());
+                handleRange(request, session, responder, selected, mailbox, mailboxSession, failed, failedMsns, userFlags, messageSet);
             }
-            final boolean omitExpunged = (!useUids);
-            unsolicitedResponses(session, responder, omitExpunged, useUids).block();
+            boolean omitExpunged = (!request.isUseUids());
+            unsolicitedResponses(session, responder, omitExpunged, request.isUseUids()).block();
             
             // check if we had some failed uids which didn't pass the UNCHANGEDSINCE filter
             if (failed.isEmpty() && failedMsns.isEmpty()) {
                 okComplete(request, responder);
             } else {
-                if (useUids) {
-                    List<MessageRange> ranges = MessageRange.toRanges(failed);
-                    UidRange[] idRanges = new UidRange[ranges.size()];
-                    for (int i = 0; i < ranges.size(); i++) {
-                        MessageRange r = ranges.get(i);
-                        if (r.getType() == Type.ONE) {
-                            idRanges[i] = new UidRange(r.getUidFrom());
-                        } else {
-                            idRanges[i] = new UidRange(r.getUidFrom(), r.getUidTo());
-                        }
-                    }
-                    // we need to return the failed sequences
-                    //
-                    // See RFC4551 3.2. STORE and UID STORE Commands
-                    final StatusResponse response = getStatusResponseFactory().taggedOk(request.getTag(), request.getCommand(), HumanReadableText.FAILED, ResponseCode.condStore(idRanges));
-                    responder.respond(response);
-                } else {
-                    List<IdRange> ranges = new ArrayList<>();
-                    for (NullableMessageSequenceNumber msn: failedMsns) {
-                        msn.ifPresent(id -> ranges.add(new IdRange(id.asInt())));
-                    }
-                    IdRange[] failedRanges = IdRange.mergeRanges(ranges).toArray(IdRange[]::new);
-                    // See RFC4551 3.2. STORE and UID STORE Commands
-                    final StatusResponse response = getStatusResponseFactory().taggedOk(request.getTag(), request.getCommand(), HumanReadableText.FAILED, ResponseCode.condStore(failedRanges));
-                    responder.respond(response);
-                    
-                }
+                respondFailed(request, responder, failed, failedMsns);
             }
         } catch (MessageRangeException e) {
             LOGGER.debug("Store failed for mailbox {} because of an invalid sequence-set {}", session.getSelected().getMailboxId(), idSet, e);
@@ -208,20 +107,137 @@ public class StoreProcessor extends AbstractMailboxProcessor<StoreRequest> {
             no(request, responder, HumanReadableText.SAVE_FAILED);
         }
     }
-    
+
+    private void handleRange(StoreRequest request, ImapSession session, Responder responder, SelectedMailbox selected, MessageManager mailbox, MailboxSession mailboxSession, List<MessageUid> failed, List<NullableMessageSequenceNumber> failedMsns, List<String> userFlags, MessageRange messageSet) throws MailboxException {
+        if (messageSet != null) {
+
+            if (request.getUnchangedSince() != -1) {
+                List<MessageUid> uids = new ArrayList<>();
+
+                Iterator<ComposedMessageIdWithMetaData> results = Flux.from(
+                    mailbox.listMessagesMetadata(messageSet, mailboxSession))
+                    .toStream()
+                    .iterator();
+                while (results.hasNext()) {
+                    ComposedMessageIdWithMetaData r = results.next();
+                    checkIfFailed(request, selected, failed, failedMsns, userFlags, uids, r);
+                }
+                List<MessageRange> mRanges = MessageRange.toRanges(uids);
+                for (MessageRange mRange : mRanges) {
+                    setFlags(request, mailboxSession, mailbox, mRange, session, responder);
+                }
+            } else {
+                setFlags(request, mailboxSession, mailbox, messageSet, session, responder);
+            }
+        }
+    }
+
+    private void checkIfFailed(StoreRequest request, SelectedMailbox selected, List<MessageUid> failed, List<NullableMessageSequenceNumber> failedMsns, List<String> userFlags, List<MessageUid> uids, ComposedMessageIdWithMetaData r) {
+        MessageUid uid = r.getComposedMessageId().getUid();
+
+        boolean fail = false;
+
+        // Check if UNCHANGEDSINCE 0 was used and the Message contains the request flag.
+        // In such cases we need to fail for this message.
+        //
+        // From RFC4551:
+        //       Use of UNCHANGEDSINCE with a modification sequence of 0 always
+        //       fails if the metadata item exists.  A system flag MUST always be
+        //       considered existent, whether it was set or not.
+        if (request.getUnchangedSince() == 0) {
+            String[] uFlags = r.getFlags().getUserFlags();
+            for (String uFlag : uFlags) {
+                if (userFlags.contains(uFlag)) {
+                    fail = true;
+                    break;
+                }
+            }
+        }
+
+        // Check if the mod-sequence of the message is <= the unchangedsince.
+        //
+        // See RFC4551 3.2. STORE and UID STORE Commands
+        if (!fail && r.getModSeq().asLong() <= request.getUnchangedSince()) {
+            uids.add(uid);
+        } else {
+            if (request.isUseUids()) {
+                failed.add(uid);
+            } else {
+                failedMsns.add(selected.msn(uid));
+            }
+        }
+    }
+
+    private boolean rejectUnchangedSinceZeroWithSystemFlagUpdate(StoreRequest request, Responder responder, IdRange[] idSet, Flags flags) {
+        if (request.getUnchangedSince() == 0) {
+            Flags.Flag[] systemFlags = flags.getSystemFlags();
+            if (systemFlags != null && systemFlags.length != 0) {
+                // we need to return all sequences as failed when using a UNCHANGEDSINCE 0 and the request specify a SYSTEM flags
+                //
+                // See RFC4551 3.2. STORE and UID STORE Command
+                //
+                //       Use of UNCHANGEDSINCE with a modification sequence of 0 always
+                //       fails if the metadata item exists.  A system flag MUST always be
+                //       considered existent, whether it was set or not.
+                StatusResponse response = getStatusResponseFactory().taggedOk(request.getTag(), request.getCommand(), HumanReadableText.FAILED, ResponseCode.condStore(idSet));
+                responder.respond(response);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void respondFailed(StoreRequest request, Responder responder, List<MessageUid> failed, List<NullableMessageSequenceNumber> failedMsns) {
+        if (request.isUseUids()) {
+            UidRange[] idRanges = MessageRange.toRanges(failed)
+                .stream()
+                .map(r -> new UidRange(r.getUidFrom(), r.getUidTo()))
+                .toArray(UidRange[]::new);
+            // we need to return the failed sequences
+            //
+            // See RFC4551 3.2. STORE and UID STORE Commands
+            StatusResponse response = getStatusResponseFactory().taggedOk(request.getTag(), request.getCommand(), HumanReadableText.FAILED, ResponseCode.condStore(idRanges));
+            responder.respond(response);
+        } else {
+            List<IdRange> ranges = new ArrayList<>();
+            for (NullableMessageSequenceNumber msn: failedMsns) {
+                msn.ifPresent(id -> ranges.add(new IdRange(id.asInt())));
+            }
+            IdRange[] failedRanges = IdRange.mergeRanges(ranges).toArray(IdRange[]::new);
+            // See RFC4551 3.2. STORE and UID STORE Commands
+            StatusResponse response = getStatusResponseFactory().taggedOk(request.getTag(), request.getCommand(), HumanReadableText.FAILED, ResponseCode.condStore(failedRanges));
+            responder.respond(response);
+        }
+    }
+
     /**
      * Set the flags for given messages
      */
     private void setFlags(StoreRequest request, MailboxSession mailboxSession, MessageManager mailbox, MessageRange messageSet, ImapSession session, Responder responder) throws MailboxException {
-        
-        final Flags flags = request.getFlags();
-        final boolean useUids = request.isUseUids();
-        final boolean silent = request.isSilent();
-        final long unchangedSince = request.getUnchangedSince();
-        final MessageManager.FlagsUpdateMode mode = request.getFlagsUpdateMode();
+        boolean silent = request.isSilent();
+        long unchangedSince = request.getUnchangedSince();
         
         SelectedMailbox selected = session.getSelected();
-        final Map<MessageUid, Flags> flagsByUid = mailbox.setFlags(flags, mode, messageSet, mailboxSession);
+        Map<MessageUid, Flags> flagsByUid = mailbox.setFlags(request.getFlags(), request.getFlagsUpdateMode(), messageSet, mailboxSession);
+        handlePermanentFlagChanges(mailboxSession, mailbox, responder, selected);
+
+        Set<Capability> enabled = EnableProcessor.getEnabledCapabilities(session);
+        boolean qresyncEnabled = enabled.contains(ImapConstants.SUPPORTS_QRESYNC);
+        boolean condstoreEnabled = enabled.contains(ImapConstants.SUPPORTS_CONDSTORE);
+        
+        if (!silent || unchangedSince != -1 || qresyncEnabled || condstoreEnabled) {
+            Map<MessageUid, ModSeq> modSeqs = computeModSeqs(mailboxSession, mailbox, messageSet, unchangedSince, qresyncEnabled, condstoreEnabled);
+
+            sendFetchResponses(responder, request.isUseUids(), silent, unchangedSince, selected, flagsByUid, qresyncEnabled, condstoreEnabled, modSeqs);
+
+            if (unchangedSince != -1) {
+                // Enable CONDSTORE as this is a CONDSTORE enabling command
+                condstoreEnablingCommand(session, responder,  mailbox.getMetaData(false, mailboxSession, MailboxMetaData.FetchGroup.NO_COUNT), true);
+            }
+        }
+    }
+
+    private void handlePermanentFlagChanges(MailboxSession mailboxSession, MessageManager mailbox, Responder responder, SelectedMailbox selected) throws MailboxException {
         // As the STORE command is allowed to create a new "flag/keyword", we need to send a FLAGS and PERMANENTFLAGS response before the FETCH response
         // if some new flag/keyword was used
         // See IMAP-303
@@ -230,89 +246,85 @@ public class StoreProcessor extends AbstractMailboxProcessor<StoreRequest> {
             permanentFlags(responder, mailbox.getMetaData(false, mailboxSession, MailboxMetaData.FetchGroup.NO_COUNT), selected);
             selected.resetNewApplicableFlags();
         }
-        
-        Set<Capability> enabled = EnableProcessor.getEnabledCapabilities(session);
-        boolean qresyncEnabled = enabled.contains(ImapConstants.SUPPORTS_QRESYNC);
-        boolean condstoreEnabled = enabled.contains(ImapConstants.SUPPORTS_CONDSTORE);
-        
-        if (!silent || unchangedSince != -1 || qresyncEnabled || condstoreEnabled) {
-            final Map<MessageUid, ModSeq> modSeqs = new HashMap<>();
-           
-            // Check if we need to also send the the mod-sequences back to the client
-            //
-            // This is the case if one of these is true:
-            //      - UNCHANGEDSINCE was used
-            //      - CONDSTORE was enabled via ENABLE CONDSTORE
-            //      - QRESYNC was enabled via ENABLE QRESYNC
-            //
-            if (unchangedSince != -1 || qresyncEnabled || condstoreEnabled) {
-                Iterator<ComposedMessageIdWithMetaData> results = Flux.from(
-                    mailbox.listMessagesMetadata(messageSet, mailboxSession))
-                    .toStream()
-                    .iterator();
-                while (results.hasNext()) {
-                    ComposedMessageIdWithMetaData r = results.next();
-                    // Store the modseq for the uid for later usage in the response
-                    modSeqs.put(r.getComposedMessageId().getUid(),r.getModSeq());
+    }
+
+    private void sendFetchResponses(Responder responder, boolean useUids, boolean silent, long unchangedSince, SelectedMailbox selected, Map<MessageUid, Flags> flagsByUid, boolean qresyncEnabled, boolean condstoreEnabled, Map<MessageUid, ModSeq> modSeqs) throws MailboxException {
+        for (Map.Entry<MessageUid, Flags> entry : flagsByUid.entrySet()) {
+            final MessageUid uid = entry.getKey();
+
+            selected.msn(uid).fold(() -> {
+                LOGGER.debug("No message found with uid {} in the uid<->msn mapping for mailbox {}. This may be because it was deleted by a concurrent session. So skip it..", uid, selected.getPath().asString());
+                // skip this as it was not found in the mapping
+                //
+                // See IMAP-346
+                return null;
+            }, msn -> {
+
+                final Flags resultFlags = entry.getValue();
+                final MessageUid resultUid;
+
+                // Check if we need to include the uid. T
+                //
+                // This is the case if one of these is true:
+                //      - FETCH (UID...)  was used
+                //      - QRESYNC was enabled via ENABLE QRESYNC
+                if (useUids || qresyncEnabled) {
+                    resultUid = uid;
+                } else {
+                    resultUid = null;
                 }
+
+                if (selected.isRecent(uid)) {
+                    resultFlags.add(Flags.Flag.RECENT);
+                }
+
+                FetchResponse response = computeFetchResponse(silent, unchangedSince, qresyncEnabled, condstoreEnabled, modSeqs, uid, msn, resultFlags, resultUid);
+                responder.respond(response);
+                return null;
+            });
+        }
+    }
+
+    private Map<MessageUid, ModSeq> computeModSeqs(MailboxSession mailboxSession, MessageManager mailbox, MessageRange messageSet, long unchangedSince, boolean qresyncEnabled, boolean condstoreEnabled) {
+        final Map<MessageUid, ModSeq> modSeqs = new HashMap<>();
+
+        // Check if we need to also send the the mod-sequences back to the client
+        //
+        // This is the case if one of these is true:
+        //      - UNCHANGEDSINCE was used
+        //      - CONDSTORE was enabled via ENABLE CONDSTORE
+        //      - QRESYNC was enabled via ENABLE QRESYNC
+        //
+        if (unchangedSince != -1 || qresyncEnabled || condstoreEnabled) {
+            Iterator<ComposedMessageIdWithMetaData> results = Flux.from(
+                mailbox.listMessagesMetadata(messageSet, mailboxSession))
+                .toStream()
+                .iterator();
+            while (results.hasNext()) {
+                ComposedMessageIdWithMetaData r = results.next();
+                // Store the modseq for the uid for later usage in the response
+                modSeqs.put(r.getComposedMessageId().getUid(),r.getModSeq());
             }
-            
-            for (Map.Entry<MessageUid, Flags> entry : flagsByUid.entrySet()) {
-                final MessageUid uid = entry.getKey();
-
-                selected.msn(uid).fold(() -> {
-                    LOGGER.debug("No message found with uid {} in the uid<->msn mapping for mailbox {}. This may be because it was deleted by a concurrent session. So skip it..", uid, selected.getPath().asString());
-                    // skip this as it was not found in the mapping
-                    // 
-                    // See IMAP-346
-                    return null;
-                }, msn -> {
-
-                    final Flags resultFlags = entry.getValue();
-                    final MessageUid resultUid;
-
-                    // Check if we need to include the uid. T
-                    //
-                    // This is the case if one of these is true:
-                    //      - FETCH (UID...)  was used
-                    //      - QRESYNC was enabled via ENABLE QRESYNC
-                    if (useUids || qresyncEnabled) {
-                        resultUid = uid;
-                    } else {
-                        resultUid = null;
-                    }
-
-                    if (selected.isRecent(uid)) {
-                        resultFlags.add(Flags.Flag.RECENT);
-                    }
-
-                    final FetchResponse response;
-                    // For more information related to the FETCH response see
-                    //
-                    // RFC4551 3.2. STORE and UID STORE Commands
-                    if (silent && (unchangedSince != -1 || qresyncEnabled || condstoreEnabled)) {
-                        // We need to return an FETCH response which contains the mod-sequence of the message even if FLAGS.SILENT was used
-                        response = new FetchResponse(msn, null, resultUid, modSeqs.get(uid), null, null, null, null, null, null);
-                    } else if (!silent && (unchangedSince != -1 || qresyncEnabled || condstoreEnabled)) {
-                        //
-                        // Use a FETCH response which contains the mod-sequence and the flags
-                        response = new FetchResponse(msn, resultFlags, resultUid, modSeqs.get(uid), null, null, null, null, null, null);
-                    } else {
-                        // Use a FETCH response which only contains the flags as no CONDSTORE was used
-                        response = new FetchResponse(msn, resultFlags, resultUid, null, null, null, null, null, null, null);
-                    }
-                    responder.respond(response);
-                    return null;
-                });
-            }
+        }
+        return modSeqs;
+    }
 
-            if (unchangedSince != -1) {
-                // Enable CONDSTORE as this is a CONDSTORE enabling command
-                condstoreEnablingCommand(session, responder,  mailbox.getMetaData(false, mailboxSession, MailboxMetaData.FetchGroup.NO_COUNT), true);
-                                  
+    private FetchResponse computeFetchResponse(boolean silent, long unchangedSince, boolean qresyncEnabled, boolean condstoreEnabled, Map<MessageUid, ModSeq> modSeqs, MessageUid uid, org.apache.james.mailbox.MessageSequenceNumber msn, Flags resultFlags, MessageUid resultUid) {
+        // For more information related to the FETCH response see
+        //
+        // RFC4551 3.2. STORE and UID STORE Commands
+        if (unchangedSince != -1 || qresyncEnabled || condstoreEnabled) {
+            if (silent) {
+                // We need to return an FETCH response which contains the mod-sequence of the message even if FLAGS.SILENT was used
+                return new FetchResponse(msn, null, resultUid, modSeqs.get(uid), null, null, null, null, null, null);
+            } else {
+                // Use a FETCH response which contains the mod-sequence and the flags
+                return new FetchResponse(msn, resultFlags, resultUid, modSeqs.get(uid), null, null, null, null, null, null);
             }
+        } else {
+            // Use a FETCH response which only contains the flags as no CONDSTORE was used
+            return new FetchResponse(msn, resultFlags, resultUid, null, null, null, null, null, null, null);
         }
-        
     }
 
     @Override


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org