This is an automated email from the ASF dual-hosted git repository. matthieu 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 5e69e0d [Refactoring] use a strong type for IMAP MessageSequenceNumber 5e69e0d is described below commit 5e69e0dbddbf788f9aee136596aecdcc024c9454 Author: Matthieu Baechler <matth...@apache.org> AuthorDate: Mon Apr 20 17:23:26 2020 +0200 [Refactoring] use a strong type for IMAP MessageSequenceNumber --- .../james/imap/api/display/HumanReadableText.java | 5 +- .../imap/api/message/response/StatusResponse.java | 5 +- .../james/imap/api/process/SelectedMailbox.java | 9 +- .../james/imap/encode/ExpungeResponseEncoder.java | 3 +- .../james/imap/encode/FetchResponseEncoder.java | 5 +- .../imap/message/response/ExpungeResponse.java | 7 +- .../james/imap/message/response/FetchResponse.java | 7 +- .../imap/processor/AbstractMailboxProcessor.java | 56 ++++---- .../imap/processor/AbstractSelectionProcessor.java | 12 +- .../james/imap/processor/SearchProcessor.java | 7 +- .../james/imap/processor/StoreProcessor.java | 82 +++++------ .../imap/processor/base/SelectedMailboxImpl.java | 9 +- .../james/imap/processor/base/UidMsnConverter.java | 7 +- .../imap/processor/fetch/FetchResponseBuilder.java | 150 ++++++++++----------- .../MessageSequenceNumber.java} | 43 ++++-- .../mailbox/NullableMessageSequenceNumber.java | 90 +++++++++++++ .../encode/FetchResponseEncoderEnvelopeTest.java | 3 +- .../FetchResponseEncoderNoExtensionsTest.java | 10 +- .../imap/encode/FetchResponseEncoderTest.java | 8 +- .../imap/processor/base/UidMsnConverterTest.java | 8 +- .../james/mailbox/MessageSequenceNumberTest.java} | 40 ++++-- .../mailbox/NullableMessageSequenceNumberTest.java | 94 +++++++++++++ 22 files changed, 455 insertions(+), 205 deletions(-) diff --git a/protocols/imap/src/main/java/org/apache/james/imap/api/display/HumanReadableText.java b/protocols/imap/src/main/java/org/apache/james/imap/api/display/HumanReadableText.java index e231dd6..ac52c7d 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/api/display/HumanReadableText.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/api/display/HumanReadableText.java @@ -24,6 +24,7 @@ import java.util.Arrays; import javax.mail.Flags; import org.apache.james.imap.api.ImapConstants; +import org.apache.james.mailbox.MessageSequenceNumber; import com.google.common.base.Joiner; @@ -36,8 +37,8 @@ public class HumanReadableText { public static final HumanReadableText SELECT = new HumanReadableText("org.apache.james.imap.SELECT", "completed."); - public static HumanReadableText unseen(long numberUnseen) { - return new HumanReadableText("org.apache.james.imap.UNSEEN", "MailboxMessage " + numberUnseen + " is first unseen"); + public static HumanReadableText unseen(MessageSequenceNumber numberUnseen) { + return new HumanReadableText("org.apache.james.imap.UNSEEN", "MailboxMessage " + numberUnseen.asInt() + " is first unseen"); } public static final HumanReadableText UIDNEXT = new HumanReadableText("org.apache.james.imap.UIDNEXT", "Predicted next UID"); diff --git a/protocols/imap/src/main/java/org/apache/james/imap/api/message/response/StatusResponse.java b/protocols/imap/src/main/java/org/apache/james/imap/api/message/response/StatusResponse.java index 39ecc29..b2307cd 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/api/message/response/StatusResponse.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/api/message/response/StatusResponse.java @@ -34,6 +34,7 @@ import org.apache.james.imap.api.display.HumanReadableText; import org.apache.james.imap.api.message.IdRange; import org.apache.james.imap.api.message.MessageFlags; import org.apache.james.imap.api.message.UidRange; +import org.apache.james.mailbox.MessageSequenceNumber; import org.apache.james.mailbox.MessageUid; import org.apache.james.mailbox.ModSeq; import org.apache.james.mailbox.model.UidValidity; @@ -299,8 +300,8 @@ public interface StatusResponse extends ImapResponseMessage { * positive non-zero integer * @return <code>ResponseCode</code>, not null */ - public static ResponseCode unseen(int numberUnseen) { - return new ResponseCode("UNSEEN", numberUnseen); + public static ResponseCode unseen(MessageSequenceNumber numberUnseen) { + return new ResponseCode("UNSEEN", numberUnseen.asInt()); } /** diff --git a/protocols/imap/src/main/java/org/apache/james/imap/api/process/SelectedMailbox.java b/protocols/imap/src/main/java/org/apache/james/imap/api/process/SelectedMailbox.java index c16bdbc..8516109 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/api/process/SelectedMailbox.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/api/process/SelectedMailbox.java @@ -25,6 +25,7 @@ import java.util.Optional; import javax.mail.Flags; import org.apache.james.mailbox.MessageUid; +import org.apache.james.mailbox.NullableMessageSequenceNumber; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.model.MailboxId; import org.apache.james.mailbox.model.MailboxPath; @@ -42,10 +43,10 @@ public interface SelectedMailbox { void deselect(); /** - * Return the msg index of the given uid or {@link #NO_SUCH_MESSAGE} if no + * Return the msg index of the given uid or {@link NullableMessageSequenceNumber#noMessage()} instance if no * message with the given uid was found */ - int msn(MessageUid uid); + NullableMessageSequenceNumber msn(MessageUid uid); /** * Return the uid of the message for the given index or empty if no message with the given index was found @@ -144,10 +145,10 @@ public interface SelectedMailbox { * @param uid * not null * @return the message sequence number that the UID held before or - * {@link #NO_SUCH_MESSAGE} if no message with the given uid was + * {@link NullableMessageSequenceNumber#noMessage()} instance if no message with the given uid was * found being expunged */ - int remove(MessageUid uid); + NullableMessageSequenceNumber remove(MessageUid uid); /** * Return a Collection which holds all uids reflecting the Messages which diff --git a/protocols/imap/src/main/java/org/apache/james/imap/encode/ExpungeResponseEncoder.java b/protocols/imap/src/main/java/org/apache/james/imap/encode/ExpungeResponseEncoder.java index 992ee56..49a7c7e 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/encode/ExpungeResponseEncoder.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/encode/ExpungeResponseEncoder.java @@ -25,6 +25,7 @@ import org.apache.james.imap.message.response.ExpungeResponse; public class ExpungeResponseEncoder implements ImapResponseEncoder<ExpungeResponse> { public static final String EXPUNGE = "EXPUNGE"; + private static final int NO_MESSAGE = -1; @Override public Class<ExpungeResponse> acceptableMessages() { @@ -33,7 +34,7 @@ public class ExpungeResponseEncoder implements ImapResponseEncoder<ExpungeRespon @Override public void encode(ExpungeResponse expungeResponse, ImapResponseComposer composer) throws IOException { - int messageSequenceNumber = expungeResponse.getMessageSequenceNumber(); + int messageSequenceNumber = expungeResponse.getMessageSequenceNumber().asInt().orElse(NO_MESSAGE); composer.untagged().message(messageSequenceNumber).message(EXPUNGE).end(); } } diff --git a/protocols/imap/src/main/java/org/apache/james/imap/encode/FetchResponseEncoder.java b/protocols/imap/src/main/java/org/apache/james/imap/encode/FetchResponseEncoder.java index 2849cda..9776f16 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/encode/FetchResponseEncoder.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/encode/FetchResponseEncoder.java @@ -33,6 +33,7 @@ import javax.mail.Flags; import org.apache.james.imap.api.ImapConstants; import org.apache.james.imap.message.response.FetchResponse; import org.apache.james.imap.message.response.FetchResponse.Structure; +import org.apache.james.mailbox.MessageSequenceNumber; import org.apache.james.mailbox.MessageUid; import org.apache.james.mailbox.ModSeq; import org.slf4j.Logger; @@ -63,9 +64,9 @@ public class FetchResponseEncoder implements ImapResponseEncoder<FetchResponse> @Override public void encode(FetchResponse fetchResponse, ImapResponseComposer composer) throws IOException { - long messageNumber = fetchResponse.getMessageNumber(); + MessageSequenceNumber messageNumber = fetchResponse.getMessageNumber(); - composer.untagged().message(messageNumber).message(ImapConstants.FETCH_COMMAND.getName()).openParen(); + composer.untagged().message(messageNumber.asInt()).message(ImapConstants.FETCH_COMMAND.getName()).openParen(); encodeModSeq(composer, fetchResponse); encodeFlags(composer, fetchResponse); diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/response/ExpungeResponse.java b/protocols/imap/src/main/java/org/apache/james/imap/message/response/ExpungeResponse.java index 2d13203..2c46fce 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/message/response/ExpungeResponse.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/message/response/ExpungeResponse.java @@ -20,16 +20,17 @@ package org.apache.james.imap.message.response; import org.apache.james.imap.api.message.response.ImapResponseMessage; +import org.apache.james.mailbox.NullableMessageSequenceNumber; public final class ExpungeResponse implements ImapResponseMessage { - private final int messageSequenceNumber; + private final NullableMessageSequenceNumber messageSequenceNumber; - public ExpungeResponse(int messageSequenceNumber) { + public ExpungeResponse(NullableMessageSequenceNumber messageSequenceNumber) { this.messageSequenceNumber = messageSequenceNumber; } - public int getMessageSequenceNumber() { + public NullableMessageSequenceNumber getMessageSequenceNumber() { return messageSequenceNumber; } diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/response/FetchResponse.java b/protocols/imap/src/main/java/org/apache/james/imap/message/response/FetchResponse.java index 0d64091..1b5133f 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/message/response/FetchResponse.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/message/response/FetchResponse.java @@ -26,11 +26,12 @@ import java.util.Map; import javax.mail.Flags; import org.apache.james.imap.api.message.response.ImapResponseMessage; +import org.apache.james.mailbox.MessageSequenceNumber; import org.apache.james.mailbox.MessageUid; import org.apache.james.mailbox.ModSeq; public final class FetchResponse implements ImapResponseMessage { - private final int messageNumber; + private final MessageSequenceNumber messageNumber; private final Flags flags; private final MessageUid uid; private final Date internalDate; @@ -41,7 +42,7 @@ public final class FetchResponse implements ImapResponseMessage { private final Structure bodystructure; private final ModSeq modSeq; - public FetchResponse(int messageNumber, Flags flags, MessageUid uid, ModSeq modSeq, Date internalDate, Long size, Envelope envelope, Structure body, Structure bodystructure, List<BodyElement> elements) { + public FetchResponse(MessageSequenceNumber messageNumber, Flags flags, MessageUid uid, ModSeq modSeq, Date internalDate, Long size, Envelope envelope, Structure body, Structure bodystructure, List<BodyElement> elements) { this.messageNumber = messageNumber; this.flags = flags; this.uid = uid; @@ -79,7 +80,7 @@ public final class FetchResponse implements ImapResponseMessage { * * @return message number */ - public int getMessageNumber() { + public MessageSequenceNumber getMessageNumber() { return messageNumber; } diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java index f094609..8377e05 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java @@ -57,6 +57,7 @@ import org.apache.james.mailbox.MessageManager; import org.apache.james.mailbox.MessageManager.MetaData; import org.apache.james.mailbox.MessageUid; import org.apache.james.mailbox.ModSeq; +import org.apache.james.mailbox.NullableMessageSequenceNumber; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.exception.MessageRangeException; import org.apache.james.mailbox.model.FetchGroup; @@ -178,7 +179,7 @@ public abstract class AbstractMailboxProcessor<R extends ImapRequest> extends Ab // we need to remove the message in the loop to the sequence numbers // are updated correctly. // See 7.4.1. EXPUNGE Response - final int msn = selected.remove(uid); + final NullableMessageSequenceNumber msn = selected.remove(uid); ExpungeResponse response = new ExpungeResponse(msn); responder.respond(response); } @@ -245,37 +246,38 @@ public abstract class AbstractMailboxProcessor<R extends ImapRequest> extends Ab while (it.hasNext()) { MessageResult mr = it.next(); final MessageUid uid = mr.getUid(); - int msn = selected.msn(uid); - if (msn == SelectedMailbox.NO_SUCH_MESSAGE) { + 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.getMailboxId().serialize()); // skip this as it was not found in the mapping // // See IMAP-346 - continue; - } + return null; + }, msn -> { - final Flags flags = mr.getFlags(); - final MessageUid uidOut; - if (useUid || qresyncEnabled) { - uidOut = uid; - } else { - uidOut = null; - } - if (selected.isRecent(uid)) { - flags.add(Flags.Flag.RECENT); - } else { - flags.remove(Flags.Flag.RECENT); - } - final FetchResponse response; - - // Check if we also need to return the MODSEQ in the response. This is true if CONDSTORE or - // if QRESYNC was enabled, and the mailbox supports the permant storage of mod-sequences - if ((condstoreEnabled || qresyncEnabled) && isModSeqPermanent) { - response = new FetchResponse(msn, flags, uidOut, mr.getModSeq(), null, null, null, null, null, null); - } else { - response = new FetchResponse(msn, flags, uidOut, null, null, null, null, null, null, null); - } - responder.respond(response); + final Flags flags = mr.getFlags(); + final MessageUid uidOut; + if (useUid || qresyncEnabled) { + uidOut = uid; + } else { + uidOut = null; + } + if (selected.isRecent(uid)) { + flags.add(Flags.Flag.RECENT); + } else { + flags.remove(Flags.Flag.RECENT); + } + final FetchResponse response; + + // Check if we also need to return the MODSEQ in the response. This is true if CONDSTORE or + // if QRESYNC was enabled, and the mailbox supports the permant storage of mod-sequences + if ((condstoreEnabled || qresyncEnabled) && isModSeqPermanent) { + response = new FetchResponse(msn, flags, uidOut, mr.getModSeq(), null, null, null, null, null, null); + } else { + response = new FetchResponse(msn, flags, uidOut, null, null, null, null, null, null, null); + } + responder.respond(response); + return null; + }); } } diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java index df3f61a..97d9055 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java @@ -349,15 +349,15 @@ abstract class AbstractSelectionProcessor<R extends AbstractMailboxSelectionRequ private boolean unseen(Responder responder, MessageUid firstUnseen, SelectedMailbox selected) throws MailboxException { if (firstUnseen != null) { final MessageUid unseenUid = firstUnseen; - int msn = selected.msn(unseenUid); - if (msn == SelectedMailbox.NO_SUCH_MESSAGE) { + return selected.msn(unseenUid).fold(() -> { LOGGER.debug("No message found with uid {} in mailbox {}", unseenUid, selected.getMailboxId().serialize()); return false; - } - - final StatusResponse untaggedOk = statusResponseFactory.untaggedOk(HumanReadableText.unseen(msn), ResponseCode.unseen(msn)); - responder.respond(untaggedOk); + }, msn -> { + final StatusResponse untaggedOk = statusResponseFactory.untaggedOk(HumanReadableText.unseen(msn), ResponseCode.unseen(msn)); + responder.respond(untaggedOk); + return true; + }); } return true; diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/SearchProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/SearchProcessor.java index 80b16ad..91d4ee4 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/SearchProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/SearchProcessor.java @@ -67,6 +67,7 @@ import org.apache.james.util.MDCBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.github.fge.lambdas.Throwing; import com.github.steveash.guavate.Guavate; import com.google.common.collect.ImmutableList; @@ -212,8 +213,10 @@ public class SearchProcessor extends AbstractMailboxProcessor<SearchRequest> imp } else { return uids.stream() .map(uid -> session.getSelected().msn(uid)) - .map(Integer::longValue) - .filter(msn -> msn != SelectedMailbox.NO_SUCH_MESSAGE) + .flatMap(Throwing.function(nullableMsn -> + nullableMsn.fold( + Stream::empty, + msn -> Stream.of(Integer.valueOf(msn.asInt()).longValue())))) .collect(Guavate.toImmutableList()); } } 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 79fe6fa..8c34775 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 @@ -49,6 +49,7 @@ import org.apache.james.mailbox.MessageManager; import org.apache.james.mailbox.MessageManager.MetaData; import org.apache.james.mailbox.MessageUid; import org.apache.james.mailbox.ModSeq; +import org.apache.james.mailbox.NullableMessageSequenceNumber; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.exception.MessageRangeException; import org.apache.james.mailbox.model.FetchGroup; @@ -111,7 +112,7 @@ public class StoreProcessor extends AbstractMailboxProcessor<StoreRequest> { } final List<MessageUid> failed = new ArrayList<>(); - List<Long> failedMsns = new ArrayList<>(); + List<NullableMessageSequenceNumber> failedMsns = new ArrayList<>(); final List<String> userFlags = Arrays.asList(flags.getUserFlags()); for (IdRange range : idSet) { final SelectedMailbox selected = session.getSelected(); @@ -157,7 +158,7 @@ public class StoreProcessor extends AbstractMailboxProcessor<StoreRequest> { if (useUids) { failed.add(uid); } else { - failedMsns.add((long)selected.msn(uid)); + failedMsns.add(selected.msn(uid)); } } } @@ -198,8 +199,8 @@ public class StoreProcessor extends AbstractMailboxProcessor<StoreRequest> { responder.respond(response); } else { List<IdRange> ranges = new ArrayList<>(); - for (long msn: failedMsns) { - ranges.add(new IdRange(msn)); + 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 @@ -264,50 +265,51 @@ public class StoreProcessor extends AbstractMailboxProcessor<StoreRequest> { for (Map.Entry<MessageUid, Flags> entry : flagsByUid.entrySet()) { final MessageUid uid = entry.getKey(); - final int msn = selected.msn(uid); - if (msn == SelectedMailbox.NO_SUCH_MESSAGE) { + 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 - continue; - } + 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; - } + final Flags resultFlags = entry.getValue(); + final MessageUid resultUid; - 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)) { + // Check if we need to include the uid. T // - // 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); + // 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; + }); } if (unchangedSince != -1) { diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/base/SelectedMailboxImpl.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/base/SelectedMailboxImpl.java index a8ca86f..1aef2bf 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/base/SelectedMailboxImpl.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/base/SelectedMailboxImpl.java @@ -38,6 +38,7 @@ import org.apache.james.mailbox.MailboxManager; import org.apache.james.mailbox.MailboxSession; import org.apache.james.mailbox.MessageManager; import org.apache.james.mailbox.MessageUid; +import org.apache.james.mailbox.NullableMessageSequenceNumber; import org.apache.james.mailbox.events.Event; import org.apache.james.mailbox.events.EventBus; import org.apache.james.mailbox.events.MailboxIdRegistrationKey; @@ -183,8 +184,8 @@ public class SelectedMailboxImpl implements SelectedMailbox, MailboxListener { } @Override - public synchronized int remove(MessageUid uid) { - final int result = msn(uid); + public synchronized NullableMessageSequenceNumber remove(MessageUid uid) { + NullableMessageSequenceNumber result = msn(uid); uidMsnConverter.remove(uid); return result; } @@ -391,8 +392,8 @@ public class SelectedMailboxImpl implements SelectedMailbox, MailboxListener { } @Override - public synchronized int msn(MessageUid uid) { - return uidMsnConverter.getMsn(uid).orElse(NO_SUCH_MESSAGE); + public synchronized NullableMessageSequenceNumber msn(MessageUid uid) { + return uidMsnConverter.getMsn(uid); } @Override diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/base/UidMsnConverter.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/base/UidMsnConverter.java index ff56229..fc7b114 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/base/UidMsnConverter.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/base/UidMsnConverter.java @@ -26,6 +26,7 @@ import java.util.Optional; import java.util.TreeSet; import org.apache.james.mailbox.MessageUid; +import org.apache.james.mailbox.NullableMessageSequenceNumber; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; @@ -48,12 +49,12 @@ public class UidMsnConverter { uids.addAll(tmp); } - public synchronized Optional<Integer> getMsn(MessageUid uid) { + public synchronized NullableMessageSequenceNumber getMsn(MessageUid uid) { int position = Collections.binarySearch(uids, uid); if (position < 0) { - return Optional.empty(); + return NullableMessageSequenceNumber.noMessage(); } - return Optional.of(position + 1); + return NullableMessageSequenceNumber.of(position + 1); } public synchronized Optional<MessageUid> getUid(int msn) { diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/fetch/FetchResponseBuilder.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/fetch/FetchResponseBuilder.java index 40cd7f6..4229b5e 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/fetch/FetchResponseBuilder.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/fetch/FetchResponseBuilder.java @@ -41,6 +41,7 @@ import org.apache.james.imap.api.process.SelectedMailbox; import org.apache.james.imap.message.response.FetchResponse; import org.apache.james.mailbox.MailboxSession; import org.apache.james.mailbox.MessageManager; +import org.apache.james.mailbox.MessageSequenceNumber; import org.apache.james.mailbox.MessageUid; import org.apache.james.mailbox.ModSeq; import org.apache.james.mailbox.exception.MailboxException; @@ -55,7 +56,7 @@ public final class FetchResponseBuilder { private final EnvelopeBuilder envelopeBuilder; - private int msn; + private MessageSequenceNumber msn; private MessageUid uid; @@ -80,7 +81,7 @@ public final class FetchResponseBuilder { this.envelopeBuilder = envelopeBuilder; } - public void reset(int msn) { + public void reset(MessageSequenceNumber msn) { this.msn = msn; uid = null; flags = null; @@ -112,95 +113,94 @@ public final class FetchResponseBuilder { public FetchResponse build(FetchData fetch, MessageResult result, MessageManager mailbox, ImapSession session, boolean useUids) throws MessageRangeException, MailboxException { final SelectedMailbox selected = session.getSelected(); final MessageUid resultUid = result.getUid(); - final int resultMsn = selected.msn(resultUid); - - if (resultMsn == SelectedMailbox.NO_SUCH_MESSAGE) { + return selected.msn(resultUid).fold(() -> { throw new MessageRangeException("No such message found with uid " + resultUid); - } + }, msn -> { + + reset(msn); + // setMsn(resultMsn); + + // Check if this fetch will cause the "SEEN" flag to be set on this + // message. If so, update the flags, and ensure that a flags response is + // included in the response. + final MailboxSession mailboxSession = session.getMailboxSession(); + boolean ensureFlagsResponse = false; + final Flags resultFlags = result.getFlags(); + if (fetch.isSetSeen() && !resultFlags.contains(Flags.Flag.SEEN)) { + mailbox.setFlags(new Flags(Flags.Flag.SEEN), MessageManager.FlagsUpdateMode.ADD, MessageRange.one(resultUid), mailboxSession); + resultFlags.add(Flags.Flag.SEEN); + ensureFlagsResponse = true; + } - reset(resultMsn); - // setMsn(resultMsn); - - // Check if this fetch will cause the "SEEN" flag to be set on this - // message. If so, update the flags, and ensure that a flags response is - // included in the response. - final MailboxSession mailboxSession = session.getMailboxSession(); - boolean ensureFlagsResponse = false; - final Flags resultFlags = result.getFlags(); - if (fetch.isSetSeen() && !resultFlags.contains(Flags.Flag.SEEN)) { - mailbox.setFlags(new Flags(Flags.Flag.SEEN), MessageManager.FlagsUpdateMode.ADD, MessageRange.one(resultUid), mailboxSession); - resultFlags.add(Flags.Flag.SEEN); - ensureFlagsResponse = true; - } + // FLAGS response + if (fetch.contains(Item.FLAGS) || ensureFlagsResponse) { + if (selected.isRecent(resultUid)) { + resultFlags.add(Flags.Flag.RECENT); + } + setFlags(resultFlags); + } - // FLAGS response - if (fetch.contains(Item.FLAGS) || ensureFlagsResponse) { - if (selected.isRecent(resultUid)) { - resultFlags.add(Flags.Flag.RECENT); + // INTERNALDATE response + if (fetch.contains(Item.INTERNAL_DATE)) { + setInternalDate(result.getInternalDate()); } - setFlags(resultFlags); - } - // INTERNALDATE response - if (fetch.contains(Item.INTERNAL_DATE)) { - setInternalDate(result.getInternalDate()); - } + // RFC822.SIZE response + if (fetch.contains(Item.SIZE)) { + setSize(result.getSize()); + } - // RFC822.SIZE response - if (fetch.contains(Item.SIZE)) { - setSize(result.getSize()); - } + if (fetch.contains(Item.ENVELOPE)) { + this.envelope = buildEnvelope(result); + } - if (fetch.contains(Item.ENVELOPE)) { - this.envelope = buildEnvelope(result); - } + // BODY part responses. + Collection<BodyFetchElement> elements = fetch.getBodyElements(); + this.elements = new ArrayList<>(); + for (BodyFetchElement fetchElement : elements) { + final FetchResponse.BodyElement element = bodyFetch(result, fetchElement); + if (element != null) { + this.elements.add(element); + } + } + + // Only create when needed + if (fetch.contains(Item.BODY) || fetch.contains(Item.BODY_STRUCTURE)) { + // BODY response + // + // the STRUCTURE is only needed when no specific element is requested otherwise we don't need + // to access it and may be able to not parse the message + // + // See IMAP-333 + if (fetch.contains(Item.BODY) && this.elements.isEmpty()) { + body = new MimeDescriptorStructure(false, result.getMimeDescriptor(), envelopeBuilder); + } - // BODY part responses. - Collection<BodyFetchElement> elements = fetch.getBodyElements(); - this.elements = new ArrayList<>(); - for (BodyFetchElement fetchElement : elements) { - final FetchResponse.BodyElement element = bodyFetch(result, fetchElement); - if (element != null) { - this.elements.add(element); + // BODYSTRUCTURE response + if (fetch.contains(Item.BODY_STRUCTURE)) { + bodystructure = new MimeDescriptorStructure(true, result.getMimeDescriptor(), envelopeBuilder); + } } - } - - // Only create when needed - if (fetch.contains(Item.BODY) || fetch.contains(Item.BODY_STRUCTURE)) { - // BODY response - // - // the STRUCTURE is only needed when no specific element is requested otherwise we don't need - // to access it and may be able to not parse the message - // - // See IMAP-333 - if (fetch.contains(Item.BODY) && this.elements.isEmpty()) { - body = new MimeDescriptorStructure(false, result.getMimeDescriptor(), envelopeBuilder); + // UID response + if (fetch.contains(Item.UID)) { + setUid(resultUid); } - // BODYSTRUCTURE response - if (fetch.contains(Item.BODY_STRUCTURE)) { - bodystructure = new MimeDescriptorStructure(true, result.getMimeDescriptor(), envelopeBuilder); - } - } - // UID response - if (fetch.contains(Item.UID)) { - setUid(resultUid); - } - - if (fetch.contains(Item.MODSEQ)) { - long changedSince = fetch.getChangedSince(); - if (changedSince != -1) { - // check if the modsequence if higher then the one specified by the CHANGEDSINCE option - if (changedSince < result.getModSeq().asLong()) { + if (fetch.contains(Item.MODSEQ)) { + long changedSince = fetch.getChangedSince(); + if (changedSince != -1) { + // check if the modsequence if higher then the one specified by the CHANGEDSINCE option + if (changedSince < result.getModSeq().asLong()) { + setModSeq(result.getModSeq()); + } + } else { setModSeq(result.getModSeq()); } - } else { - setModSeq(result.getModSeq()); } - } - return build(); + return build(); + }); } private FetchResponse.Envelope buildEnvelope(MessageResult result) throws MailboxException { diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/response/ExpungeResponse.java b/protocols/imap/src/main/java/org/apache/james/mailbox/MessageSequenceNumber.java similarity index 56% copy from protocols/imap/src/main/java/org/apache/james/imap/message/response/ExpungeResponse.java copy to protocols/imap/src/main/java/org/apache/james/mailbox/MessageSequenceNumber.java index 2d13203..df1c829 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/message/response/ExpungeResponse.java +++ b/protocols/imap/src/main/java/org/apache/james/mailbox/MessageSequenceNumber.java @@ -17,23 +17,48 @@ * under the License. * ****************************************************************/ -package org.apache.james.imap.message.response; +package org.apache.james.mailbox; -import org.apache.james.imap.api.message.response.ImapResponseMessage; +import java.util.Objects; -public final class ExpungeResponse implements ImapResponseMessage { +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; - private final int messageSequenceNumber; +public final class MessageSequenceNumber { - public ExpungeResponse(int messageSequenceNumber) { - this.messageSequenceNumber = messageSequenceNumber; + public static MessageSequenceNumber of(int msn) { + Preconditions.checkArgument(msn >= 0); + return new MessageSequenceNumber(msn); } - public int getMessageSequenceNumber() { - return messageSequenceNumber; + private final int msn; + + private MessageSequenceNumber(int msn) { + this.msn = msn; + } + + public int asInt() { + return msn; + } + + @Override + public boolean equals(Object o) { + if (o instanceof MessageSequenceNumber) { + MessageSequenceNumber that = (MessageSequenceNumber) o; + return Objects.equals(msn, that.msn); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(msn); } + @Override public String toString() { - return "EXPUNGE " + messageSequenceNumber; + return MoreObjects.toStringHelper(this) + .add("msn", msn) + .toString(); } } diff --git a/protocols/imap/src/main/java/org/apache/james/mailbox/NullableMessageSequenceNumber.java b/protocols/imap/src/main/java/org/apache/james/mailbox/NullableMessageSequenceNumber.java new file mode 100644 index 0000000..75d51bd --- /dev/null +++ b/protocols/imap/src/main/java/org/apache/james/mailbox/NullableMessageSequenceNumber.java @@ -0,0 +1,90 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.mailbox; + +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; + +import org.apache.james.mailbox.exception.MailboxException; + +import com.google.common.base.MoreObjects; + +public final class NullableMessageSequenceNumber { + + public interface HandleNoMessage<T> { + T handle() throws MailboxException; + } + + public interface HandleMessage<T> { + T handle(MessageSequenceNumber sequenceNumber) throws MailboxException; + } + + public static NullableMessageSequenceNumber noMessage() { + return new NullableMessageSequenceNumber(Optional.empty()); + } + + public static NullableMessageSequenceNumber of(int msn) { + return new NullableMessageSequenceNumber(Optional.of(MessageSequenceNumber.of(msn))); + } + + private final Optional<MessageSequenceNumber> msn; + + private NullableMessageSequenceNumber(Optional<MessageSequenceNumber> msn) { + this.msn = msn; + } + + public void ifPresent(Consumer<MessageSequenceNumber> consumer) { + msn.ifPresent(consumer); + } + + public <T> T fold(HandleNoMessage<T> handleNoMessage, HandleMessage<T> handleMessage) throws MailboxException { + if (msn.isPresent()) { + return handleMessage.handle(msn.get()); + } else { + return handleNoMessage.handle(); + } + } + + public Optional<Integer> asInt() { + return msn.map(MessageSequenceNumber::asInt); + } + + @Override + public boolean equals(Object o) { + if (o instanceof NullableMessageSequenceNumber) { + NullableMessageSequenceNumber that = (NullableMessageSequenceNumber) o; + return Objects.equals(msn, that.msn); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(msn); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("msn", msn) + .toString(); + } +} diff --git a/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderEnvelopeTest.java b/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderEnvelopeTest.java index 340b8ce..281afc6 100644 --- a/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderEnvelopeTest.java +++ b/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderEnvelopeTest.java @@ -27,6 +27,7 @@ import org.apache.james.imap.encode.base.ByteImapResponseWriter; import org.apache.james.imap.encode.base.ImapResponseComposerImpl; import org.apache.james.imap.message.response.FetchResponse; import org.apache.james.imap.message.response.FetchResponse.Envelope.Address; +import org.apache.james.mailbox.MessageSequenceNumber; import org.junit.Before; import org.junit.Test; @@ -49,7 +50,7 @@ public class FetchResponseEncoderEnvelopeTest { private static final String ADDRESS_TWO_NAME = "2NAME"; - private static final int MSN = 100; + private static final MessageSequenceNumber MSN = MessageSequenceNumber.of(100); private FetchResponseEncoder encoder; diff --git a/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderNoExtensionsTest.java b/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderNoExtensionsTest.java index a805a9c..40face7 100644 --- a/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderNoExtensionsTest.java +++ b/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderNoExtensionsTest.java @@ -33,11 +33,13 @@ import javax.mail.Flags; import org.apache.james.imap.encode.base.ByteImapResponseWriter; import org.apache.james.imap.encode.base.ImapResponseComposerImpl; import org.apache.james.imap.message.response.FetchResponse; +import org.apache.james.mailbox.MessageSequenceNumber; import org.apache.james.mailbox.MessageUid; import org.junit.Before; import org.junit.Test; public class FetchResponseEncoderNoExtensionsTest { + private static final MessageSequenceNumber MSN = MessageSequenceNumber.of(100); private ByteImapResponseWriter writer = new ByteImapResponseWriter(); private ImapResponseComposer composer = new ImapResponseComposerImpl(writer); private Flags flags; @@ -55,7 +57,7 @@ public class FetchResponseEncoderNoExtensionsTest { @Test public void testShouldEncodeFlagsResponse() throws Exception { - FetchResponse message = new FetchResponse(100, flags, null, null, null, null, + FetchResponse message = new FetchResponse(MSN, flags, null, null, null, null, null, null, null, null); encoder.encode(message, composer); assertThat(writer.getString()).isEqualTo("* 100 FETCH (FLAGS (\\Deleted))\r\n"); @@ -63,7 +65,7 @@ public class FetchResponseEncoderNoExtensionsTest { @Test public void testShouldEncodeUidResponse() throws Exception { - FetchResponse message = new FetchResponse(100, null, MessageUid.of(72), null, + FetchResponse message = new FetchResponse(MSN, null, MessageUid.of(72), null, null, null, null, null, null, null); encoder.encode(message, composer); assertThat(writer.getString()).isEqualTo("* 100 FETCH (UID 72)\r\n"); @@ -72,7 +74,7 @@ public class FetchResponseEncoderNoExtensionsTest { @Test public void testShouldEncodeAllResponse() throws Exception { - FetchResponse message = new FetchResponse(100, flags, MessageUid.of(72), null, + FetchResponse message = new FetchResponse(MSN, flags, MessageUid.of(72), null, null, null, null, null, null, null); encoder.encode(message, composer); assertThat(writer.getString()).isEqualTo("* 100 FETCH (FLAGS (\\Deleted) UID 72)\r\n"); @@ -81,7 +83,7 @@ public class FetchResponseEncoderNoExtensionsTest { @Test public void testShouldNotAddExtensionsWithEncodingBodyStructure() throws Exception { - FetchResponse message = new FetchResponse(100, flags, MessageUid.of(72), null, + FetchResponse message = new FetchResponse(MSN, flags, MessageUid.of(72), null, null, null, null, null, stubStructure, null); final Map<String, String> parameters = new HashMap<>(); parameters.put("CHARSET", "US-ASCII"); diff --git a/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderTest.java b/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderTest.java index 3a56fb6..5447cd0 100644 --- a/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderTest.java +++ b/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderTest.java @@ -26,11 +26,13 @@ import javax.mail.Flags; import org.apache.james.imap.encode.base.ByteImapResponseWriter; import org.apache.james.imap.encode.base.ImapResponseComposerImpl; import org.apache.james.imap.message.response.FetchResponse; +import org.apache.james.mailbox.MessageSequenceNumber; import org.apache.james.mailbox.MessageUid; import org.junit.Before; import org.junit.Test; public class FetchResponseEncoderTest { + private static final MessageSequenceNumber MSN = MessageSequenceNumber.of(100); private ByteImapResponseWriter writer = new ByteImapResponseWriter(); private ImapResponseComposer composer = new ImapResponseComposerImpl(writer); private Flags flags; @@ -49,7 +51,7 @@ public class FetchResponseEncoderTest { @Test public void testShouldEncodeFlagsResponse() throws Exception { - FetchResponse message = new FetchResponse(100, flags, null, null, null, null, + FetchResponse message = new FetchResponse(MSN, flags, null, null, null, null, null, null, null, null); encoder.encode(message, composer); assertThat(writer.getString()).isEqualTo("* 100 FETCH (FLAGS (\\Deleted))\r\n"); @@ -59,7 +61,7 @@ public class FetchResponseEncoderTest { @Test public void testShouldEncodeUidResponse() throws Exception { - FetchResponse message = new FetchResponse(100, null, MessageUid.of(72), null, + FetchResponse message = new FetchResponse(MSN, null, MessageUid.of(72), null, null, null, null, null, null, null); encoder.encode(message, composer); assertThat(writer.getString()).isEqualTo("* 100 FETCH (UID 72)\r\n"); @@ -69,7 +71,7 @@ public class FetchResponseEncoderTest { @Test public void testShouldEncodeAllResponse() throws Exception { - FetchResponse message = new FetchResponse(100, flags, MessageUid.of(72), null, + FetchResponse message = new FetchResponse(MSN, flags, MessageUid.of(72), null, null, null, null, null, null, null); encoder.encode(message, composer); assertThat(writer.getString()).isEqualTo("* 100 FETCH (FLAGS (\\Deleted) UID 72)\r\n"); diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/base/UidMsnConverterTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/base/UidMsnConverterTest.java index 3c7c66c..7976841 100644 --- a/protocols/imap/src/test/java/org/apache/james/imap/processor/base/UidMsnConverterTest.java +++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/base/UidMsnConverterTest.java @@ -24,6 +24,8 @@ import static org.assertj.core.api.Assertions.assertThat; import java.time.Duration; import java.util.Map; +import org.apache.james.mailbox.MessageSequenceNumber; +import org.apache.james.mailbox.NullableMessageSequenceNumber; import org.apache.james.mailbox.MessageUid; import org.apache.james.util.concurrency.ConcurrentTestRunner; import org.junit.Before; @@ -98,7 +100,7 @@ public class UidMsnConverterTest { public void getMsnShouldReturnAbsentIfNoCorrespondingMessage() { testee.addUid(messageUid1); - assertThat(testee.getMsn(messageUid2)).isEmpty(); + assertThat(testee.getMsn(messageUid2)).isEqualTo(NullableMessageSequenceNumber.noMessage()); } @Test @@ -107,7 +109,7 @@ public class UidMsnConverterTest { testee.addUid(messageUid2); assertThat(testee.getMsn(messageUid2)) - .contains(2); + .isEqualTo(NullableMessageSequenceNumber.of(2)); } @Test @@ -173,7 +175,7 @@ public class UidMsnConverterTest { testee.addUid(messageUid2); assertThat(testee.getMsn(messageUid2)) - .contains(2); + .isEqualTo(NullableMessageSequenceNumber.of(2)); } @Test diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/response/ExpungeResponse.java b/protocols/imap/src/test/java/org/apache/james/mailbox/MessageSequenceNumberTest.java similarity index 54% copy from protocols/imap/src/main/java/org/apache/james/imap/message/response/ExpungeResponse.java copy to protocols/imap/src/test/java/org/apache/james/mailbox/MessageSequenceNumberTest.java index 2d13203..16369cf 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/message/response/ExpungeResponse.java +++ b/protocols/imap/src/test/java/org/apache/james/mailbox/MessageSequenceNumberTest.java @@ -17,23 +17,41 @@ * under the License. * ****************************************************************/ -package org.apache.james.imap.message.response; +package org.apache.james.mailbox; -import org.apache.james.imap.api.message.response.ImapResponseMessage; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; -public final class ExpungeResponse implements ImapResponseMessage { +import org.junit.jupiter.api.Test; - private final int messageSequenceNumber; +import nl.jqno.equalsverifier.EqualsVerifier; - public ExpungeResponse(int messageSequenceNumber) { - this.messageSequenceNumber = messageSequenceNumber; +class MessageSequenceNumberTest { + + @Test + void ofShouldThrowOnNegative() { + assertThatThrownBy(() -> MessageSequenceNumber.of(-1)).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void ofShouldNotThrowOnZero() { + assertThatCode(() -> MessageSequenceNumber.of(0)).doesNotThrowAnyException(); } - public int getMessageSequenceNumber() { - return messageSequenceNumber; + @Test + void ofShouldNotThrowOnPositiveValue() { + assertThatCode(() -> MessageSequenceNumber.of(12)).doesNotThrowAnyException(); } - public String toString() { - return "EXPUNGE " + messageSequenceNumber; + @Test + void asIntShouldReturnValue() { + assertThat(MessageSequenceNumber.of(12).asInt()).isEqualTo(12); } -} + + @Test + void shouldRespectBeanContract() { + EqualsVerifier.forClass(MessageSequenceNumber.class).verify(); + } + +} \ No newline at end of file diff --git a/protocols/imap/src/test/java/org/apache/james/mailbox/NullableMessageSequenceNumberTest.java b/protocols/imap/src/test/java/org/apache/james/mailbox/NullableMessageSequenceNumberTest.java new file mode 100644 index 0000000..9cb57b5 --- /dev/null +++ b/protocols/imap/src/test/java/org/apache/james/mailbox/NullableMessageSequenceNumberTest.java @@ -0,0 +1,94 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.mailbox; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import nl.jqno.equalsverifier.EqualsVerifier; + +class NullableMessageSequenceNumberTest { + + @Test + void ofShouldThrowOnNegative() { + assertThatThrownBy(() -> NullableMessageSequenceNumber.of(-1)).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void ofShouldNotThrowOnZero() { + assertThatCode(() -> NullableMessageSequenceNumber.of(0)).doesNotThrowAnyException(); + } + + @Test + void ofShouldNotThrowOnPositiveValue() { + assertThatCode(() -> NullableMessageSequenceNumber.of(12)).doesNotThrowAnyException(); + } + + @Test + void asIntShouldReturnIntWhenMessage() { + assertThat(NullableMessageSequenceNumber.of(12).asInt()).contains(12); + } + + @Test + void asIntShouldReturnEmptyWhenNoMessage() { + assertThat(NullableMessageSequenceNumber.noMessage().asInt()).isEmpty(); + } + + @Test + void foldShouldCallOnlyFirstMethodOnNoMessage() { + assertThatCode( + () -> NullableMessageSequenceNumber.noMessage().fold(() -> 12, msn -> fail())) + .doesNotThrowAnyException(); + } + + @Test + void foldShouldCallOnlySecondMethodOnMessage() { + assertThatCode( + () -> NullableMessageSequenceNumber.of(24).fold(Assertions::fail, msn -> 12)) + .doesNotThrowAnyException(); + } + + @Test + void ifPresentShouldNotCallMethodOnNoMessage() { + assertThatCode( + () -> NullableMessageSequenceNumber.noMessage().ifPresent(ignored -> Assertions.fail())) + .doesNotThrowAnyException(); + } + + @Test + void ifPresentShouldCallMethodOnWithMessage() { + AtomicReference<MessageSequenceNumber> ref = new AtomicReference<>(); + NullableMessageSequenceNumber.of(24).ifPresent(ref::set); + assertThat(ref.get()).isNotNull(); + } + + @Test + void shouldRespectBeanContract() { + EqualsVerifier.forClass(NullableMessageSequenceNumber.class).verify(); + } + +} \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org