JAMES-3977 Add bytes preservation in manager and mapper contracts
This was not preserved for the Cassandra mailbox, leading to
innacurrate backpressure and down the line to OutOfMemoryExceptions
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java
index f70d7f5..e7d875a 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java
@@ -2923,6 +2923,13 @@
MessageResult messageResult = inboxManager.getMessages(MessageRange.one(composeId1.getUid()), FetchGroup.MINIMAL, session).next();
assertThat(messageResult.getSaveDate()).isPresent();
}
+
+ @Test
+ void fullFetchGroupShouldPreserveBytesSequence() throws Exception {
+ ComposedMessageId composeId1 = inboxManager.appendMessage(AppendCommand.builder().build(message), session).getId();
+ MessageResult messageResult = inboxManager.getMessages(MessageRange.one(composeId1.getUid()), FetchGroup.FULL_CONTENT, session).next();
+ assertThat(messageResult.getFullContent().asBytesSequence()).isPresent();
+ }
}
@Nested
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMetadata.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMetadata.java
index 56e1203..c741f7e 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMetadata.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMetadata.java
@@ -269,6 +269,16 @@
}
@Override
+ public Optional<byte[][]> getFullBytes() {
+ return delegate.getFullBytes();
+ }
+
+ @Override
+ public Optional<byte[][]> getHeadersBytes() {
+ return delegate.getHeadersBytes();
+ }
+
+ @Override
public ThreadId getThreadId() {
return delegate.getThreadId();
}
diff --git a/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/InMemoryMapperProvider.java b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/InMemoryMapperProvider.java
index 4667385..286057e 100644
--- a/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/InMemoryMapperProvider.java
+++ b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/InMemoryMapperProvider.java
@@ -113,7 +113,8 @@
Capabilities.ANNOTATION,
Capabilities.MOVE,
Capabilities.ACL_STORAGE,
- Capabilities.UNIQUE_MESSAGE_ID);
+ Capabilities.UNIQUE_MESSAGE_ID,
+ Capabilities.FULL_BYTES);
}
@Override
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/DelegatingMailboxMessage.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/DelegatingMailboxMessage.java
index 29726f4..4a743ec 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/DelegatingMailboxMessage.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/DelegatingMailboxMessage.java
@@ -132,4 +132,9 @@
public Optional<byte[][]> getFullBytes() {
return message.getFullBytes();
}
+
+ @Override
+ public Optional<byte[][]> getHeadersBytes() {
+ return message.getHeadersBytes();
+ }
}
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMessage.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMessage.java
index ffe9b02..1fb0a44 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMessage.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMessage.java
@@ -106,11 +106,23 @@
@Override
public InputStream getHeaderContent() throws IOException {
- long headerEnd = bodyStartOctet;
- if (headerEnd < 0) {
- headerEnd = 0;
+ return new BoundedInputStream(content.getInputStream(), headerSize());
+ }
+
+ private long headerSize() {
+ return Math.max(0, bodyStartOctet);
+ }
+
+ @Override
+ public Optional<byte[][]> getHeadersBytes() {
+ try {
+ if (headerSize() == content.size()) {
+ return content.asBytesSequence();
+ }
+ } catch (MailboxException e) {
+ return Optional.empty();
}
- return new BoundedInputStream(content.getInputStream(), headerEnd);
+ return Optional.empty();
}
@Override
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MapperProvider.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MapperProvider.java
index 2db1faa..b6bd054 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MapperProvider.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MapperProvider.java
@@ -42,7 +42,9 @@
UNIQUE_MESSAGE_ID,
THREAD_SAFE_FLAGS_UPDATE,
INCREMENTAL_APPLICABLE_FLAGS,
- ACL_STORAGE
+ ACL_STORAGE,
+ FULL_BYTES,
+ HEADER_BYTES
}
List<Capabilities> getSupportedCapabilities();
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMapperTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMapperTest.java
index b4514d3..3d16316 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMapperTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMapperTest.java
@@ -73,7 +73,7 @@
private static final char DELIMITER = '.';
private static final int LIMIT = 10;
- private static final int BODY_START = 16;
+ private static final int BODY_START = 19;
private static final UidValidity UID_VALIDITY = UidValidity.of(42);
private static final String CUSTOMS_USER_FLAGS_VALUE = "CustomsFlags";
@@ -115,12 +115,12 @@
benwaInboxMailbox = createMailbox(MailboxPath.forUser(BENWA, "INBOX"));
benwaWorkMailbox = createMailbox(MailboxPath.forUser(BENWA, "INBOX" + DELIMITER + "work"));
- message1 = createMessage(benwaInboxMailbox, mapperProvider.generateMessageId(), "Subject: Test1 \n\nBody1\n.\n", BODY_START, new PropertyBuilder());
- message2 = createMessage(benwaInboxMailbox, mapperProvider.generateMessageId(), "Subject: Test2 \n\nBody2\n.\n", BODY_START, new PropertyBuilder());
- message3 = createMessage(benwaInboxMailbox, mapperProvider.generateMessageId(), "Subject: Test3 \n\nBody3\n.\n", BODY_START, new PropertyBuilder());
- message4 = createMessage(benwaInboxMailbox, mapperProvider.generateMessageId(), "Subject: Test4 \n\nBody4\n.\n", BODY_START, new PropertyBuilder());
- message5 = createMessage(benwaInboxMailbox, mapperProvider.generateMessageId(), "Subject: Test5 \n\nBody5\n.\n", BODY_START, new PropertyBuilder());
- message6 = createMessage(benwaWorkMailbox, mapperProvider.generateMessageId(), "Subject: Test6 \n\nBody6\n.\n", BODY_START, new PropertyBuilder());
+ message1 = createMessage(benwaInboxMailbox, mapperProvider.generateMessageId(), "Subject: Test1 \r\n\r\nBody1\n.\n", BODY_START, new PropertyBuilder());
+ message2 = createMessage(benwaInboxMailbox, mapperProvider.generateMessageId(), "Subject: Test2 \r\n\r\nBody2\n.\n", BODY_START, new PropertyBuilder());
+ message3 = createMessage(benwaInboxMailbox, mapperProvider.generateMessageId(), "Subject: Test3 \r\n\r\nBody3\n.\n", BODY_START, new PropertyBuilder());
+ message4 = createMessage(benwaInboxMailbox, mapperProvider.generateMessageId(), "Subject: Test4 \r\n\r\nBody4\n.\n", BODY_START, new PropertyBuilder());
+ message5 = createMessage(benwaInboxMailbox, mapperProvider.generateMessageId(), "Subject: Test5 \r\n\r\nBody5\n.\n", BODY_START, new PropertyBuilder());
+ message6 = createMessage(benwaWorkMailbox, mapperProvider.generateMessageId(), "Subject: Test6 \r\n\r\nBody6\n.\n", BODY_START, new PropertyBuilder());
}
@Test
@@ -213,6 +213,54 @@
}
@Test
+ void getFullBytesShouldBePresentWhenFullFetchType() throws MailboxException, IOException {
+ Assume.assumeTrue(mapperProvider.getSupportedCapabilities().contains(Capabilities.FULL_BYTES));
+ saveMessages();
+ int limit = 10;
+ MailboxMessage next = messageMapper.findInMailbox(benwaInboxMailbox, MessageRange.one(message1.getUid()), FetchType.FULL, limit).next();
+ assertThat(next.getFullBytes())
+ .isNotEmpty();
+ }
+
+ @Test
+ void getFullBytesShouldBePresentWhenHeadersFetchType() throws Exception {
+ Assume.assumeTrue(mapperProvider.getSupportedCapabilities().contains(Capabilities.FULL_BYTES));
+ saveMessages();
+ int limit = 10;
+ assertThat(messageMapper.findInMailbox(benwaInboxMailbox, MessageRange.one(message1.getUid()), FetchType.HEADERS, limit).next().getFullBytes())
+ .get()
+ .satisfies(b -> assertThat(b.length).isEqualTo(1));
+ }
+
+ @Test
+ void getHeadersBytesShouldBePresentWhenHeadersFetchType() throws Exception {
+ Assume.assumeTrue(mapperProvider.getSupportedCapabilities().contains(Capabilities.HEADER_BYTES));
+ saveMessages();
+ int limit = 10;
+ assertThat(messageMapper.findInMailbox(benwaInboxMailbox, MessageRange.one(message1.getUid()), FetchType.HEADERS, limit).next().getHeadersBytes())
+ .isNotEmpty();
+ }
+
+ @Test
+ void getFullBytesShouldBePresentWhenAttachmentMetadataFetchType() throws Exception {
+ Assume.assumeTrue(mapperProvider.getSupportedCapabilities().contains(Capabilities.FULL_BYTES));
+ saveMessages();
+ int limit = 10;
+ assertThat(messageMapper.findInMailbox(benwaInboxMailbox, MessageRange.one(message1.getUid()), FetchType.ATTACHMENTS_METADATA, limit).next().getFullBytes())
+ .get()
+ .satisfies(b -> assertThat(b.length).isEqualTo(1));
+ }
+
+ @Test
+ void getHeadersBytesShouldBePresentWhenAttachmentMetadataFetchType() throws Exception {
+ Assume.assumeTrue(mapperProvider.getSupportedCapabilities().contains(Capabilities.HEADER_BYTES));
+ saveMessages();
+ int limit = 10;
+ assertThat(messageMapper.findInMailbox(benwaInboxMailbox, MessageRange.one(message1.getUid()), FetchType.ATTACHMENTS_METADATA, limit).next().getHeadersBytes())
+ .isNotEmpty();
+ }
+
+ @Test
void messagesCanBeRetrievedInMailboxWithRangeTypeRange() throws MailboxException, IOException {
saveMessages();
Iterator<MailboxMessage> retrievedMessageIterator = messageMapper