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 2021/02/23 03:45:13 UTC

[james-project] branch master updated (8788c9b -> 864e1c3)

This is an automated email from the ASF dual-hosted git repository.

btellier pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git.


    from 8788c9b  Avoid github notification on the mailing lists
     new 983278b  JAMES-3431 Allow the use of zoneDateTime in mail attributes
     new 75f4b0b  JAMES-3431 DSNBounce should position Arrival-Date header
     new 828e887  JAMES-3431 Fix RFC-822 date formatting for mailets
     new 814f841  JAMES-3431 Better format DSN when no error message
     new c4e6350  JAMES-3477 Drop MimeMessageCopyOnWriteProxy
     new 1c60fd4  JAMES-3477 Make clear that MimeMessageWrapper is not thread safe
     new 041d49a  JAMES-3477 Clearly state that MimeMessageInputStreamSource is not thread safe
     new e56d236  JAMES-3477 MimeMessageInputStreamSource do not need ordering for opened streams
     new 97ad533  JAMES-3477 JDBCMailRepository should always update email content
     new ca1c0a2  JAMES-3477 Reactivate disabled tests
     new e8ca920  JAMES-3477 Do not call SPamAssassin
     new 3839603  JAMES-3477 Avoid unneeded copies of MimeMessages
     new 1c241aa  JAMES-3501 Starting 2 Cassandra james server concurrently fails
     new 864e1c3  JAMES-2514 Upgrade Cassandra 3.11.3 -> 3.11.10

The 14 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 JenkinsfileStressTests.groovy                      |   6 +-
 README.adoc                                        |   4 +-
 .../james/backends/cassandra/DockerCassandra.java  |   4 +-
 .../init/CassandraTableManagerConcurrentTest.java  |  65 +++
 dockerfiles/run/docker-compose-branch-master.yml   |   2 +-
 dockerfiles/run/docker-compose.yml                 |   2 +-
 .../servers/pages/distributed/operate/guide.adoc   |   6 +-
 .../servers/pages/distributed/operate/logging.adoc |   2 +-
 .../servers/pages/distributed/run-docker.adoc      |   2 +-
 docs/modules/servers/pages/distributed/run.adoc    |   6 +-
 .../java/org/apache/mailet/AttributeValue.java     |   9 +
 .../main/java/org/apache/mailet/Serializer.java    |  34 ++
 .../java/org/apache/mailet/AttributeValueTest.java |  41 ++
 .../java/org/apache/mailet/base/DateFormats.java   |   2 +-
 server/blob/mail-store/pom.xml                     |   4 +
 .../apache/james/blob/mail/MimeMessageStore.java   |  47 +-
 .../org/apache/james/server/core/MailImpl.java     |  21 +-
 .../server/core/MimeMessageCopyOnWriteProxy.java   | 556 ---------------------
 .../james/server/core/MimeMessageInputStream.java  |   6 -
 .../server/core/MimeMessageInputStreamSource.java  |  10 +-
 .../apache/james/server/core/MimeMessageUtil.java  |  12 +-
 .../james/server/core/MimeMessageWrapper.java      |  30 +-
 .../core/MimeMessageCopyOnWriteProxyTest.java      | 290 -----------
 .../apache/james/server/core/MimeMessageTest.java  |  72 ---
 .../james/WithCassandraBlobStoreImmutableTest.java |   2 -
 .../apache/james/WithCassandraBlobStoreTest.java   |  13 -
 .../src/test/resources/listeners.xml               |   3 -
 .../src/test/resources/mailetcontainer.xml         |   4 -
 .../apache/james/MailsShouldBeWellReceived.java    |   6 -
 .../mailrepository/file/FileMailRepository.java    |  10 +-
 .../mailrepository/jdbc/JDBCMailRepository.java    |  34 +-
 .../mailrepository/jdbc/MessageInputStream.java    |  15 +-
 .../impl/JamesMailetContextTest.java               |  18 +-
 .../lib/AbstractStateMailetProcessorTest.java      |   6 +-
 .../apache/james/transport/mailets/DSNBounce.java  |  22 +-
 .../james/transport/mailets/DSNBounceTest.java     | 159 +++++-
 .../RecipientRewriteTableProcessorTest.java        |   8 +-
 .../remote/delivery/RemoteDeliveryTest.java        |  22 +-
 .../james/jmap/draft/methods/MessageSender.java    |  22 +-
 .../james/jmap/draft/send/MailSpoolTest.java       |   6 +
 .../jmap/method/EmailSubmissionSetMethod.scala     |  24 +-
 .../DataLineJamesMessageHookHandler.java           |   6 +-
 .../james/smtpserver/dsn/DSNMessageHook.java       |   5 +
 .../james/webadmin/routes/MailQueueRoutesTest.java |  25 +-
 .../routes/MailRepositoriesRoutesTest.java         |  39 ++
 .../webadmin/service/ReprocessingServiceTest.java  |   6 +
 .../queue/activemq/ActiveMQCacheableMailQueue.java |   4 +-
 .../james/queue/file/FileCacheableMailQueue.java   |   4 +-
 .../james/queue/jms/JMSCacheableMailQueue.java     |   4 +-
 .../apache/james/queue/rabbitmq/MailLoader.java    |  11 +-
 .../install/guice-cassandra-rabbitmq-swift.md      |   6 +-
 .../markdown/server/install/guice-cassandra.md     |   6 +-
 .../server/manage-guice-distributed-james.md       |   6 +-
 src/site/xdoc/server/quick-start-cassandra.xml     |   2 +-
 54 files changed, 584 insertions(+), 1147 deletions(-)
 create mode 100644 backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/CassandraTableManagerConcurrentTest.java
 delete mode 100644 server/container/core/src/main/java/org/apache/james/server/core/MimeMessageCopyOnWriteProxy.java
 delete mode 100644 server/container/core/src/test/java/org/apache/james/server/core/MimeMessageCopyOnWriteProxyTest.java


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


[james-project] 04/14: JAMES-3431 Better format DSN when no error message

Posted by bt...@apache.org.
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 814f841ea16ecdf609afa0c3844ef0d9fa12a51e
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Feb 17 12:18:01 2021 +0700

    JAMES-3431 Better format DSN when no error message
---
 .../apache/james/transport/mailets/DSNBounce.java  | 11 ++-
 .../james/transport/mailets/DSNBounceTest.java     | 98 ++++++++++++++++++++++
 2 files changed, 106 insertions(+), 3 deletions(-)

diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/DSNBounce.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/DSNBounce.java
index b37e206..3dcfe7b 100755
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/DSNBounce.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/DSNBounce.java
@@ -434,9 +434,14 @@ public class DSNBounce extends GenericMailet implements RedirectNotify {
                 .collect(Collectors.joining(", ")));
         builder.append(LINE_BREAK).append(LINE_BREAK);
         if (action.shouldIncludeDiagnostic()) {
-            builder.append("Error message:").append(LINE_BREAK);
-            builder.append(AttributeUtils.getValueAndCastFromMail(originalMail, DELIVERY_ERROR, String.class).orElse("")).append(LINE_BREAK);
-            builder.append(LINE_BREAK);
+            Optional<String> deliveryError = AttributeUtils.getValueAndCastFromMail(originalMail, DELIVERY_ERROR, String.class);
+
+            deliveryError.or(() -> Optional.ofNullable(originalMail.getErrorMessage()))
+                .ifPresent(message -> {
+                    builder.append("Error message:").append(LINE_BREAK);
+                    builder.append(message).append(LINE_BREAK);
+                    builder.append(LINE_BREAK);
+                });
         }
 
         MimeBodyPart bodyPart = new MimeBodyPart();
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/DSNBounceTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/DSNBounceTest.java
index 153c1d1..84b8248 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/DSNBounceTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/DSNBounceTest.java
@@ -1364,4 +1364,102 @@ public class DSNBounceTest {
         SharedByteArrayInputStream actualContent = (SharedByteArrayInputStream) content.getBodyPart(1).getContent();
         assertThat(IOUtils.toString(actualContent, StandardCharsets.UTF_8)).isEqualTo(expectedContent);
     }
+
+    @Test
+    void errorMessagePartShouldNotBeAddedWhenNoError() throws Exception {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .mailetName(MAILET_NAME)
+            .mailetContext(fakeMailContext)
+            .setProperty("defaultStatus", "4.0.0")
+            .build();
+        dsnBounce.init(mailetConfig);
+
+        MailAddress senderMailAddress = new MailAddress("sender@domain.com");
+        FakeMail mail = FakeMail.builder()
+            .name(MAILET_NAME)
+            .sender(senderMailAddress)
+            .mimeMessage(MimeMessageBuilder.mimeMessageBuilder()
+                .setText("My content"))
+            .recipient("recipient@domain.com")
+            .lastUpdated(Date.from(Instant.parse("2016-09-08T14:25:52.000Z")))
+            .remoteAddr("remoteHost")
+            .build();
+        mail.setDsnParameters(DsnParameters.builder().envId(DsnParameters.EnvId.of("xyz")).build().get());
+
+        dsnBounce.service(mail);
+
+        List<SentMail> sentMails = fakeMailContext.getSentMails();
+        assertThat(sentMails).hasSize(1);
+        SentMail sentMail = sentMails.get(0);
+        MimeMessage sentMessage = sentMail.getMsg();
+
+        assertThat(MimeMessageUtil.asString(sentMessage))
+            .doesNotContain("Error message:");
+    }
+
+    @Test
+    void errorShouldBeAdded() throws Exception {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .mailetName(MAILET_NAME)
+            .mailetContext(fakeMailContext)
+            .setProperty("defaultStatus", "4.0.0")
+            .build();
+        dsnBounce.init(mailetConfig);
+
+        MailAddress senderMailAddress = new MailAddress("sender@domain.com");
+        FakeMail mail = FakeMail.builder()
+            .name(MAILET_NAME)
+            .sender(senderMailAddress)
+            .mimeMessage(MimeMessageBuilder.mimeMessageBuilder()
+                .setText("My content"))
+            .recipient("recipient@domain.com")
+            .lastUpdated(Date.from(Instant.parse("2016-09-08T14:25:52.000Z")))
+            .remoteAddr("remoteHost")
+            .errorMessage("This is what happen...")
+            .build();
+        mail.setDsnParameters(DsnParameters.builder().envId(DsnParameters.EnvId.of("xyz")).build().get());
+
+        dsnBounce.service(mail);
+
+        List<SentMail> sentMails = fakeMailContext.getSentMails();
+        assertThat(sentMails).hasSize(1);
+        SentMail sentMail = sentMails.get(0);
+        MimeMessage sentMessage = sentMail.getMsg();
+
+        assertThat(MimeMessageUtil.asString(sentMessage))
+            .contains("Error message:\nThis is what happen...");
+    }
+
+    @Test
+    void deliveryErrorShouldBeAdded() throws Exception {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .mailetName(MAILET_NAME)
+            .mailetContext(fakeMailContext)
+            .setProperty("defaultStatus", "4.0.0")
+            .build();
+        dsnBounce.init(mailetConfig);
+
+        MailAddress senderMailAddress = new MailAddress("sender@domain.com");
+        FakeMail mail = FakeMail.builder()
+            .name(MAILET_NAME)
+            .sender(senderMailAddress)
+            .mimeMessage(MimeMessageBuilder.mimeMessageBuilder()
+                .setText("My content"))
+            .recipient("recipient@domain.com")
+            .lastUpdated(Date.from(Instant.parse("2016-09-08T14:25:52.000Z")))
+            .remoteAddr("remoteHost")
+            .attribute(new Attribute(AttributeName.of("delivery-error"), AttributeValue.of("This is what happen...")))
+            .build();
+        mail.setDsnParameters(DsnParameters.builder().envId(DsnParameters.EnvId.of("xyz")).build().get());
+
+        dsnBounce.service(mail);
+
+        List<SentMail> sentMails = fakeMailContext.getSentMails();
+        assertThat(sentMails).hasSize(1);
+        SentMail sentMail = sentMails.get(0);
+        MimeMessage sentMessage = sentMail.getMsg();
+
+        assertThat(MimeMessageUtil.asString(sentMessage))
+            .contains("Error message:\nThis is what happen...");
+    }
 }
\ No newline at end of file


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


[james-project] 09/14: JAMES-3477 JDBCMailRepository should always update email content

Posted by bt...@apache.org.
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 97ad5331f0b1531b8daa8d3bdab8d0f8fb17755e
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Dec 31 10:45:29 2020 +0700

    JAMES-3477 JDBCMailRepository should always update email content
---
 .../mailrepository/jdbc/JDBCMailRepository.java    | 26 ++--------------------
 1 file changed, 2 insertions(+), 24 deletions(-)

diff --git a/server/data/data-jdbc/src/main/java/org/apache/james/mailrepository/jdbc/JDBCMailRepository.java b/server/data/data-jdbc/src/main/java/org/apache/james/mailrepository/jdbc/JDBCMailRepository.java
index 78e66f0..2fd79f9 100644
--- a/server/data/data-jdbc/src/main/java/org/apache/james/mailrepository/jdbc/JDBCMailRepository.java
+++ b/server/data/data-jdbc/src/main/java/org/apache/james/mailrepository/jdbc/JDBCMailRepository.java
@@ -45,7 +45,6 @@ import java.util.StringTokenizer;
 import javax.annotation.PostConstruct;
 import javax.inject.Inject;
 import javax.mail.MessagingException;
-import javax.mail.internet.MimeMessage;
 import javax.sql.DataSource;
 
 import org.apache.commons.configuration2.BaseHierarchicalConfiguration;
@@ -383,10 +382,7 @@ public class JDBCMailRepository implements MailRepository, Configurable, Initial
             // Need to determine whether need to insert this record, or update
             // it.
 
-            // Determine whether the message body has changed, and possibly
-            // avoid
-            // updating the database.
-            boolean saveBody = saveBodyRequired(mc);
+            mc.getMessage().saveChanges();
             MessageInputStream is = new MessageInputStream(mc, sr, inMemorySizeLimit, true);
 
             // Begin a transaction
@@ -405,9 +401,7 @@ public class JDBCMailRepository implements MailRepository, Configurable, Initial
                     updateMailAttributes(mc, conn);
                 }
 
-                if (saveBody) {
-                    updateMessageBody(mc, conn, is);
-                }
+                updateMessageBody(mc, conn, is);
 
             } else {
                 // Insert the record into the database
@@ -551,22 +545,6 @@ public class JDBCMailRepository implements MailRepository, Configurable, Initial
         }
     }
 
-    private boolean saveBodyRequired(Mail mc) throws MessagingException {
-        boolean saveBody;
-        MimeMessage messageBody = mc.getMessage();
-
-        if (messageBody instanceof MimeMessageWrapper) {
-            MimeMessageWrapper message = (MimeMessageWrapper) messageBody;
-            saveBody = message.isModified();
-            if (saveBody) {
-                message.loadMessage();
-            }
-        } else {
-            saveBody = true;
-        }
-        return saveBody;
-    }
-
     @Override
     @SuppressWarnings("unchecked")
     public Mail retrieve(MailKey key) throws MessagingException {


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


[james-project] 10/14: JAMES-3477 Reactivate disabled tests

Posted by bt...@apache.org.
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 ca1c0a20f6db457075e8e52112733488145421ec
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Feb 15 09:50:22 2021 +0700

    JAMES-3477 Reactivate disabled tests
---
 .../java/org/apache/james/WithCassandraBlobStoreTest.java   | 13 -------------
 .../java/org/apache/james/MailsShouldBeWellReceived.java    |  6 ------
 2 files changed, 19 deletions(-)

diff --git a/server/container/guice/cassandra-guice/src/test/java/org/apache/james/WithCassandraBlobStoreTest.java b/server/container/guice/cassandra-guice/src/test/java/org/apache/james/WithCassandraBlobStoreTest.java
index 785dc20..2bf74d8 100644
--- a/server/container/guice/cassandra-guice/src/test/java/org/apache/james/WithCassandraBlobStoreTest.java
+++ b/server/container/guice/cassandra-guice/src/test/java/org/apache/james/WithCassandraBlobStoreTest.java
@@ -19,8 +19,6 @@
 
 package org.apache.james;
 
-import org.apache.james.junit.categories.Unstable;
-import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 class WithCassandraBlobStoreTest implements MailsShouldBeWellReceived {
@@ -28,15 +26,4 @@ class WithCassandraBlobStoreTest implements MailsShouldBeWellReceived {
     static JamesServerExtension jamesServerExtension = WithCassandraBlobStoreImmutableTest.baseExtensionBuilder()
         .lifeCycle(JamesServerExtension.Lifecycle.PER_TEST)
         .build();
-
-    @Override
-    @Tag(Unstable.TAG)
-    public void mailsShouldBeWellReceived(GuiceJamesServer server) throws Exception {
-        MailsShouldBeWellReceived.super.mailsShouldBeWellReceived(server);
-    }
-    @Override
-    @Tag(Unstable.TAG)
-    public void mailsShouldBeWellReceivedByBothRecipient(GuiceJamesServer server) throws Exception {
-        MailsShouldBeWellReceived.super.mailsShouldBeWellReceivedByBothRecipient(server);
-    }
 }
diff --git a/server/container/guice/guice-common/src/test/java/org/apache/james/MailsShouldBeWellReceived.java b/server/container/guice/guice-common/src/test/java/org/apache/james/MailsShouldBeWellReceived.java
index 4834cc9..98c910a 100644
--- a/server/container/guice/guice-common/src/test/java/org/apache/james/MailsShouldBeWellReceived.java
+++ b/server/container/guice/guice-common/src/test/java/org/apache/james/MailsShouldBeWellReceived.java
@@ -36,7 +36,6 @@ import javax.mail.internet.MimeMessage;
 import javax.mail.search.FlagTerm;
 
 import org.apache.james.core.Domain;
-import org.apache.james.junit.categories.Unstable;
 import org.apache.james.mailbox.DefaultMailboxes;
 import org.apache.james.modules.MailboxProbeImpl;
 import org.apache.james.modules.protocols.ImapGuiceProbe;
@@ -50,7 +49,6 @@ import org.apache.james.utils.TestIMAPClient;
 import org.apache.mailet.base.test.FakeMail;
 import org.awaitility.Awaitility;
 import org.awaitility.core.ConditionFactory;
-import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.Test;
 
 import com.github.fge.lambdas.Throwing;
@@ -84,10 +82,6 @@ interface MailsShouldBeWellReceived {
     }
 
     @Test
-    @Tag(Unstable.TAG)
-    /*
-    Condition with lambda expression in org.apache.james.MailsShouldBeWellReceived that uses org.apache.james.GuiceJamesServer was not fulfilled within 10 seconds.
-     */
     default void mailsContentWithUnicodeCharactersShouldBeKeptUnChanged(GuiceJamesServer server) throws Exception {
         server.getProbe(DataProbeImpl.class).fluent()
             .addDomain(DOMAIN)


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


[james-project] 03/14: JAMES-3431 Fix RFC-822 date formatting for mailets

Posted by bt...@apache.org.
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 828e887fcd80018676beb7691d5d1654f78b32c0
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Feb 17 12:02:23 2021 +0700

    JAMES-3431 Fix RFC-822 date formatting for mailets
    
    See http://www.java2s.com/Book/Java/Examples/Format_a_date_using_Rfc822_Date_Format.htm
---
 .../main/java/org/apache/mailet/base/DateFormats.java  |  2 +-
 .../apache/james/transport/mailets/DSNBounceTest.java  | 18 +++++++++---------
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/mailet/base/src/main/java/org/apache/mailet/base/DateFormats.java b/mailet/base/src/main/java/org/apache/mailet/base/DateFormats.java
index 53d99d0..3d52d3d 100644
--- a/mailet/base/src/main/java/org/apache/mailet/base/DateFormats.java
+++ b/mailet/base/src/main/java/org/apache/mailet/base/DateFormats.java
@@ -26,6 +26,6 @@ import java.util.Locale;
 public interface DateFormats {
 
     DateTimeFormatter RFC822_DATE_FORMAT =
-        DateTimeFormatter.ofPattern("EEE, d MMM yyyy HH:mm:ss 'XXXXX' (z)", Locale.US);
+        DateTimeFormatter.ofPattern("EEE, d MMM yyyy HH:mm:ss Z", Locale.US);
 }
 
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/DSNBounceTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/DSNBounceTest.java
index a2936bf..153c1d1 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/DSNBounceTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/DSNBounceTest.java
@@ -761,7 +761,7 @@ public class DSNBounceTest {
                 "Action: failed\n" +
                 "Status: Delivery error\n" +
                 "Diagnostic-Code: X-James; Delivery error\n" +
-                "Last-Attempt-Date: Thu, 8 Sep 2016 14:25:52 XXXXX (UTC)\n";
+                "Last-Attempt-Date: Thu, 8 Sep 2016 14:25:52 +0000\n";
 
             List<SentMail> sentMails = fakeMailContext.getSentMails();
             assertThat(sentMails).hasSize(1);
@@ -876,7 +876,7 @@ public class DSNBounceTest {
                 "Final-Recipient: rfc822; recipient@domain.com\n" +
                 "Action: delivered\n" +
                 "Status: unknown\n" +
-                "Last-Attempt-Date: Thu, 8 Sep 2016 14:25:52 XXXXX (UTC)\n";
+                "Last-Attempt-Date: Thu, 8 Sep 2016 14:25:52 +0000\n";
 
             List<SentMail> sentMails = fakeMailContext.getSentMails();
             assertThat(sentMails).hasSize(1);
@@ -995,7 +995,7 @@ public class DSNBounceTest {
                 "Action: delayed\n" +
                 "Status: Delivery error\n" +
                 "Diagnostic-Code: X-James; Delivery error\n" +
-                "Last-Attempt-Date: Thu, 8 Sep 2016 14:25:52 XXXXX (UTC)\n";
+                "Last-Attempt-Date: Thu, 8 Sep 2016 14:25:52 +0000\n";
 
             List<SentMail> sentMails = fakeMailContext.getSentMails();
             assertThat(sentMails).hasSize(1);
@@ -1110,7 +1110,7 @@ public class DSNBounceTest {
                 "Final-Recipient: rfc822; recipient@domain.com\n" +
                 "Action: relayed\n" +
                 "Status: unknown\n" +
-                "Last-Attempt-Date: Thu, 8 Sep 2016 14:25:52 XXXXX (UTC)\n";
+                "Last-Attempt-Date: Thu, 8 Sep 2016 14:25:52 +0000\n";
 
             List<SentMail> sentMails = fakeMailContext.getSentMails();
             assertThat(sentMails).hasSize(1);
@@ -1225,7 +1225,7 @@ public class DSNBounceTest {
                 "Final-Recipient: rfc822; recipient@domain.com\n" +
                 "Action: expanded\n" +
                 "Status: unknown\n" +
-                "Last-Attempt-Date: Thu, 8 Sep 2016 14:25:52 XXXXX (UTC)\n";
+                "Last-Attempt-Date: Thu, 8 Sep 2016 14:25:52 +0000\n";
 
             List<SentMail> sentMails = fakeMailContext.getSentMails();
             assertThat(sentMails).hasSize(1);
@@ -1268,7 +1268,7 @@ public class DSNBounceTest {
             "Action: failed\n" +
             "Status: Delivery error\n" +
             "Diagnostic-Code: X-James; Delivery error\n" +
-            "Last-Attempt-Date: Thu, 8 Sep 2016 14:25:52 XXXXX (UTC)\n";
+            "Last-Attempt-Date: Thu, 8 Sep 2016 14:25:52 +0000\n";
 
         List<SentMail> sentMails = fakeMailContext.getSentMails();
         assertThat(sentMails).hasSize(1);
@@ -1306,13 +1306,13 @@ public class DSNBounceTest {
         String expectedContent = "Reporting-MTA: dns; myhost\n" +
             "Received-From-MTA: dns; 111.222.333.444\n" +
             "Original-Envelope-Id: xyz\n" +
-            "Arrival-Date: Fri, 30 Oct 2015 16:12:00 XXXXX (UTC)\n" +
+            "Arrival-Date: Fri, 30 Oct 2015 16:12:00 +0000\n" +
             "\n" +
             "Final-Recipient: rfc822; recipient@domain.com\n" +
             "Action: failed\n" +
             "Status: Delivery error\n" +
             "Diagnostic-Code: X-James; Delivery error\n" +
-            "Last-Attempt-Date: Thu, 8 Sep 2016 14:25:52 XXXXX (UTC)\n";
+            "Last-Attempt-Date: Thu, 8 Sep 2016 14:25:52 +0000\n";
 
         List<SentMail> sentMails = fakeMailContext.getSentMails();
         assertThat(sentMails).hasSize(1);
@@ -1354,7 +1354,7 @@ public class DSNBounceTest {
             "Action: failed\n" +
             "Status: 4.0.0\n" +
             "Diagnostic-Code: X-James; 4.0.0\n" +
-            "Last-Attempt-Date: Thu, 8 Sep 2016 14:25:52 XXXXX (UTC)\n";
+            "Last-Attempt-Date: Thu, 8 Sep 2016 14:25:52 +0000\n";
 
         List<SentMail> sentMails = fakeMailContext.getSentMails();
         assertThat(sentMails).hasSize(1);


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


[james-project] 12/14: JAMES-3477 Avoid unneeded copies of MimeMessages

Posted by bt...@apache.org.
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 38396032fe8aa6002dd2420bcf2b69ddf0f5e816
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Sat Jan 2 11:05:02 2021 +0700

    JAMES-3477 Avoid unneeded copies of MimeMessages
    
    We needlessly copy mime messages in a few occasions:
     - After receiving an email via SMTP and enqueuing it
     - When instantiating a Mail via JMAP
     - When dequeuing a mail
    
     In all these cases there is no need to proceed with the defensive copy
     in MailImpl::setMail as we just instanciated it and won't keep a reference.
    
     The COW approach did hide this pitfall.
---
 server/blob/mail-store/pom.xml                     |  4 ++
 .../apache/james/blob/mail/MimeMessageStore.java   | 47 +++++++++++++++++-----
 .../org/apache/james/server/core/MailImpl.java     | 10 ++++-
 .../james/jmap/draft/methods/MessageSender.java    | 16 ++------
 .../jmap/method/EmailSubmissionSetMethod.scala     | 20 +++++----
 .../DataLineJamesMessageHookHandler.java           |  6 +--
 .../apache/james/queue/rabbitmq/MailLoader.java    | 11 ++++-
 7 files changed, 75 insertions(+), 39 deletions(-)

diff --git a/server/blob/mail-store/pom.xml b/server/blob/mail-store/pom.xml
index 32f8268..dbb7261 100644
--- a/server/blob/mail-store/pom.xml
+++ b/server/blob/mail-store/pom.xml
@@ -52,6 +52,10 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>james-server-util</artifactId>
         </dependency>
         <dependency>
diff --git a/server/blob/mail-store/src/main/java/org/apache/james/blob/mail/MimeMessageStore.java b/server/blob/mail-store/src/main/java/org/apache/james/blob/mail/MimeMessageStore.java
index 7614928..f2eb24b 100644
--- a/server/blob/mail-store/src/main/java/org/apache/james/blob/mail/MimeMessageStore.java
+++ b/server/blob/mail-store/src/main/java/org/apache/james/blob/mail/MimeMessageStore.java
@@ -32,12 +32,11 @@ import java.io.InputStream;
 import java.io.SequenceInputStream;
 import java.nio.ByteBuffer;
 import java.util.Map;
-import java.util.Properties;
+import java.util.UUID;
 import java.util.stream.Stream;
 
 import javax.inject.Inject;
 import javax.mail.MessagingException;
-import javax.mail.Session;
 import javax.mail.internet.MimeMessage;
 
 import org.apache.commons.io.IOUtils;
@@ -45,6 +44,8 @@ import org.apache.commons.lang3.tuple.Pair;
 import org.apache.james.blob.api.BlobStore;
 import org.apache.james.blob.api.BlobType;
 import org.apache.james.blob.api.Store;
+import org.apache.james.server.core.MimeMessageSource;
+import org.apache.james.server.core.MimeMessageWrapper;
 import org.apache.james.util.io.BodyOffsetInputStream;
 
 import com.google.common.base.Preconditions;
@@ -137,21 +138,45 @@ public class MimeMessageStore {
             Preconditions.checkArgument(pairs.containsKey(BODY_BLOB_TYPE));
 
             return toMimeMessage(
-                new SequenceInputStream(
-                    new ByteArrayInputStream(pairs.get(HEADER_BLOB_TYPE)),
-                    new ByteArrayInputStream(pairs.get(BODY_BLOB_TYPE))));
+                    pairs.get(HEADER_BLOB_TYPE),
+                    pairs.get(BODY_BLOB_TYPE));
         }
 
-        private MimeMessage toMimeMessage(InputStream inputStream) {
-            try {
-                return new MimeMessage(Session.getInstance(new Properties()), inputStream);
-            } catch (MessagingException e) {
-                throw new RuntimeException(e);
-            }
+        private MimeMessage toMimeMessage(byte[] headers, byte[] body) {
+            return new MimeMessageWrapper(new MimeMessageBytesSource(headers, body));
         }
     }
 
     public static Factory factory(BlobStore blobStore) {
         return new Factory(blobStore);
     }
+
+    private static class MimeMessageBytesSource extends MimeMessageSource {
+        private final byte[] headers;
+        private final byte[] body;
+        private final String sourceId;
+
+        private MimeMessageBytesSource(byte[] headers, byte[] body) {
+            this.headers = headers;
+            this.body = body;
+            this.sourceId = UUID.randomUUID().toString();
+        }
+
+        @Override
+        public String getSourceId() {
+            return sourceId;
+        }
+
+        @Override
+        public InputStream getInputStream() {
+            return new SequenceInputStream(
+                new ByteArrayInputStream(headers),
+                new ByteArrayInputStream(body));
+        }
+
+        @Override
+        public long getMessageSize() {
+            return headers.length + body.length;
+        }
+    }
 }
diff --git a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
index 4a231af..8903a58 100644
--- a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
+++ b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
@@ -495,6 +495,10 @@ public class MailImpl implements Disposable, Mail {
      */
     @Override
     public void setMessage(MimeMessage message) throws MessagingException {
+        setMessageNoCopy(new MimeMessageWrapper(message));
+    }
+
+    public void setMessageNoCopy(MimeMessageWrapper message) throws MessagingException {
         if (this.message != message) {
             // If a setMessage is called on a Mail that already have a message
             // (discouraged) we have to make sure that the message we remove is
@@ -502,10 +506,14 @@ public class MailImpl implements Disposable, Mail {
             if (this.message != null) {
                 LifecycleUtil.dispose(this.message);
             }
-            this.message = new MimeMessageWrapper(message);
+            this.message = message;
         }
     }
 
+    public void setMessageContent(MimeMessageSource message) throws MessagingException {
+           setMessageNoCopy(new MimeMessageWrapper(message));
+    }
+
     @Override
     public void setRecipients(Collection<MailAddress> recipients) {
         this.recipients = ImmutableList.copyOf(recipients);
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MessageSender.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MessageSender.java
index 5ca5d32..dae8b34 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MessageSender.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MessageSender.java
@@ -19,11 +19,8 @@
 
 package org.apache.james.jmap.draft.methods;
 
-import java.io.InputStream;
-
 import javax.inject.Inject;
 import javax.mail.MessagingException;
-import javax.mail.internet.MimeMessage;
 
 import org.apache.james.jmap.draft.model.message.view.MessageFullViewFactory.MetaDataWithContent;
 import org.apache.james.jmap.draft.send.MailMetadata;
@@ -34,8 +31,6 @@ import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.server.core.Envelope;
 import org.apache.james.server.core.MailImpl;
 import org.apache.james.server.core.MimeMessageInputStreamSource;
-import org.apache.james.server.core.MimeMessageSource;
-import org.apache.james.server.core.MimeMessageWrapper;
 import org.apache.mailet.Mail;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -62,18 +57,13 @@ public class MessageSender {
     @VisibleForTesting
     static Mail buildMail(MetaDataWithContent message, Envelope envelope) throws MessagingException {
         String name = message.getMessageId().serialize();
-        return MailImpl.builder()
+        MailImpl mail = MailImpl.builder()
             .name(name)
             .sender(envelope.getFrom().asOptional().orElseThrow(() -> new RuntimeException("Sender is mandatory")))
             .addRecipients(envelope.getRecipients())
-            .mimeMessage(toMimeMessage(name, message.getContent()))
             .build();
-    }
-
-    private static MimeMessage toMimeMessage(String name, InputStream inputStream) throws MessagingException {
-        MimeMessageSource source = new MimeMessageInputStreamSource(name, inputStream);
-
-        return new MimeMessageWrapper(source);
+        mail.setMessageContent(new MimeMessageInputStreamSource(name, message.getContent()));
+        return mail;
     }
 
     public void sendMessage(MessageId messageId,
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala
index ac097e5..9529f14 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala
@@ -214,13 +214,17 @@ class EmailSubmissionSetMethod @Inject()(serializer: EmailSubmissionSetSerialize
         message <- toMimeMessage(submissionId.value, m.getFullContent.getInputStream)
         envelope <- resolveEnvelope(message, request.envelope)
         validation <- validate(mailboxSession)(message, envelope)
-        enqueue <- Try(queue.enQueue(MailImpl.builder()
-          .name(submissionId.value)
-          .addRecipients(envelope.rcptTo.map(_.email).asJava)
-          .sender(envelope.mailFrom.email)
-          .mimeMessage(message)
-          .addAttribute(new Attribute(MAIL_METADATA_USERNAME_ATTRIBUTE, AttributeValue.of(mailboxSession.getUser.asString())))
-          .build()))
+        mail <- Try({
+          val mailImpl = MailImpl.builder()
+            .name(submissionId.value)
+            .addRecipients(envelope.rcptTo.map(_.email).asJava)
+            .sender(envelope.mailFrom.email)
+            .addAttribute(new Attribute(MAIL_METADATA_USERNAME_ATTRIBUTE, AttributeValue.of(mailboxSession.getUser.asString())))
+            .build()
+          mailImpl.setMessageNoCopy(message)
+          mailImpl
+        })
+        enqueue <- Try(queue.enQueue(mail))
       } yield {
         EmailSubmissionCreationResponse(submissionId)
       }
@@ -228,7 +232,7 @@ class EmailSubmissionSetMethod @Inject()(serializer: EmailSubmissionSetSerialize
     })
   }
 
-  private def toMimeMessage(name: String, inputStream: InputStream): Try[MimeMessage] = {
+  private def toMimeMessage(name: String, inputStream: InputStream): Try[MimeMessageWrapper] = {
     val source = new MimeMessageInputStreamSource(name, inputStream)
     // if MimeMessageCopyOnWriteProxy throws an error in the constructor we
     // have to manually care disposing our source.
diff --git a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/DataLineJamesMessageHookHandler.java b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/DataLineJamesMessageHookHandler.java
index e9afa81..0de3915 100644
--- a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/DataLineJamesMessageHookHandler.java
+++ b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/DataLineJamesMessageHookHandler.java
@@ -51,7 +51,6 @@ import org.apache.james.protocols.smtp.hook.MessageHook;
 import org.apache.james.server.core.MailImpl;
 import org.apache.james.server.core.MimeMessageInputStream;
 import org.apache.james.server.core.MimeMessageInputStreamSource;
-import org.apache.james.server.core.MimeMessageWrapper;
 import org.apache.mailet.Mail;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -100,10 +99,8 @@ public class DataLineJamesMessageHookHandler implements DataLineFilter, Extensib
                 // store mail in the session so we can be sure it get disposed later
                 session.setAttachment(SMTPConstants.MAIL, mail, State.Transaction);
 
-                MimeMessageWrapper mimeMessageWrapper = null;
                 try {
-                    mimeMessageWrapper = new MimeMessageWrapper(mmiss);
-                    mail.setMessage(mimeMessageWrapper);
+                    mail.setMessageContent(mmiss);
 
                     Response response = processExtensions(session, mail);
 
@@ -115,7 +112,6 @@ public class DataLineJamesMessageHookHandler implements DataLineFilter, Extensib
                     LOGGER.info("Unexpected error handling DATA stream", e);
                     return new SMTPResponse(SMTPRetCode.LOCAL_ERROR, "Unexpected error handling DATA stream.");
                 } finally {
-                    LifecycleUtil.dispose(mimeMessageWrapper);
                     LifecycleUtil.dispose(mmiss);
                     LifecycleUtil.dispose(mail);
                 }
diff --git a/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/MailLoader.java b/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/MailLoader.java
index ffe765b..1548081 100644
--- a/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/MailLoader.java
+++ b/server/queue/queue-rabbitmq/src/main/java/org/apache/james/queue/rabbitmq/MailLoader.java
@@ -29,6 +29,8 @@ import org.apache.james.blob.api.BlobId;
 import org.apache.james.blob.api.Store;
 import org.apache.james.blob.mail.MimeMessagePartsId;
 import org.apache.james.queue.api.MailQueue;
+import org.apache.james.server.core.MailImpl;
+import org.apache.james.server.core.MimeMessageWrapper;
 import org.apache.mailet.Mail;
 
 import com.github.fge.lambdas.Throwing;
@@ -60,7 +62,14 @@ class MailLoader {
 
     private Mono<Mail> buildMailWithMessageReference(MailReference mailReference, MimeMessage mimeMessage) {
         Function<Mail, Mono<Object>> setMessage = mail ->
-            Mono.fromRunnable(Throwing.runnable(() -> mail.setMessage(mimeMessage)).sneakyThrow())
+            Mono.fromRunnable(Throwing.runnable(() -> {
+                if (mimeMessage instanceof MimeMessageWrapper && mail instanceof MailImpl) {
+                    MailImpl mailImpl = (MailImpl) mail;
+                    mailImpl.setMessageNoCopy((MimeMessageWrapper) mimeMessage);
+                } else {
+                    mail.setMessage(mimeMessage);
+                }
+            }).sneakyThrow())
                 .onErrorResume(AddressException.class, e -> Mono.error(new MailQueue.MailQueueException("Failed to parse mail address", e)))
                 .onErrorResume(MessagingException.class, e -> Mono.error(new MailQueue.MailQueueException("Failed to generate mime message", e)));
 


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


[james-project] 05/14: JAMES-3477 Drop MimeMessageCopyOnWriteProxy

Posted by bt...@apache.org.
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 c4e6350e4b5ebac83dd033580f71a29f241797a3
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Sat Dec 26 11:56:45 2020 +0700

    JAMES-3477 Drop MimeMessageCopyOnWriteProxy
    
    Previous implementation was not thread safe as demonstrated
    by @jeantil and @mbaechler. Upon concurrent writes and disposes
    it leads to corruption of the underlying message, throwing
    NullPointerExceptions and leads to data loss. [1]
    
    Attempts to make it thread safe like [2] were complex, error
    prone (double read-write lock involved!), and ultimately did
    not manage to protect 'optimized' raw access to the underlying
    data thus not fully addressing the concurrency issues.
    
    Benchmarks were conducted in order to compare the Copy On
    Write implementation with its always-copy equivalent. We
    highlighted minor benefits of the copy-on-write version (
    up to a few percent) both on compute time and memory allocation.
    Only for bigger messages (10MB) did we reach higher differences,
    of around 10%.
    
    Note that the actual copy uses DeferredFileOutputStream
    (commons.io) thus limits memory usage to 100KB upon copy.
    
    [1] https://github.com/apache/james-project/pull/280/commits/09b5554bbcbbb98757910d59bac54f97ee1f8b4f
    [2] https://github.com/apache/james-project/pull/280
    [3] https://github.com/apache/james-project/pull/280#issuecomment-745211736
---
 .../org/apache/james/server/core/MailImpl.java     |  15 +-
 .../server/core/MimeMessageCopyOnWriteProxy.java   | 556 ---------------------
 .../james/server/core/MimeMessageInputStream.java  |   6 -
 .../apache/james/server/core/MimeMessageUtil.java  |  12 +-
 .../core/MimeMessageCopyOnWriteProxyTest.java      | 290 -----------
 .../apache/james/server/core/MimeMessageTest.java  |  72 ---
 .../mailrepository/file/FileMailRepository.java    |  10 +-
 .../mailrepository/jdbc/JDBCMailRepository.java    |  10 +-
 .../mailrepository/jdbc/MessageInputStream.java    |  15 +-
 .../impl/JamesMailetContextTest.java               |  18 +-
 .../lib/AbstractStateMailetProcessorTest.java      |   6 +-
 .../RecipientRewriteTableProcessorTest.java        |   8 +-
 .../remote/delivery/RemoteDeliveryTest.java        |  22 +-
 .../james/jmap/draft/methods/MessageSender.java    |  12 +-
 .../jmap/method/EmailSubmissionSetMethod.scala     |   4 +-
 .../DataLineJamesMessageHookHandler.java           |  10 +-
 .../james/webadmin/routes/MailQueueRoutesTest.java |  25 +-
 .../routes/MailRepositoriesRoutesTest.java         |  39 ++
 .../webadmin/service/ReprocessingServiceTest.java  |   6 +
 .../queue/activemq/ActiveMQCacheableMailQueue.java |   4 +-
 .../james/queue/file/FileCacheableMailQueue.java   |   4 +-
 .../james/queue/jms/JMSCacheableMailQueue.java     |   4 +-
 22 files changed, 137 insertions(+), 1011 deletions(-)

diff --git a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
index 172d4e0..4a231af 100644
--- a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
+++ b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
@@ -99,7 +99,7 @@ public class MailImpl implements Disposable, Mail {
             .name(deriveNewName(mail.getName()))
             .sender(mail.getMaybeSender())
             .addRecipients(mail.getRecipients())
-            .mimeMessage(new MimeMessageCopyOnWriteProxy(mail.getMessage()))
+            .mimeMessage(new MimeMessageWrapper(mail.getMessage()))
             .remoteHost(mail.getRemoteHost())
             .remoteAddr(mail.getRemoteAddr())
             .lastUpdated(mail.getLastUpdated())
@@ -365,7 +365,7 @@ public class MailImpl implements Disposable, Mail {
     /**
      * The MimeMessage that holds the mail data.
      */
-    private MimeMessageCopyOnWriteProxy message;
+    private MimeMessageWrapper message;
     /**
      * The sender of this mail.
      */
@@ -495,11 +495,6 @@ public class MailImpl implements Disposable, Mail {
      */
     @Override
     public void setMessage(MimeMessage message) throws MessagingException {
-
-        // TODO: We should use the MimeMessageCopyOnWriteProxy
-        // everytime we set the MimeMessage. We should
-        // investigate if we should wrap it here
-
         if (this.message != message) {
             // If a setMessage is called on a Mail that already have a message
             // (discouraged) we have to make sure that the message we remove is
@@ -507,11 +502,7 @@ public class MailImpl implements Disposable, Mail {
             if (this.message != null) {
                 LifecycleUtil.dispose(this.message);
             }
-            if (message instanceof MimeMessageCopyOnWriteProxy) {
-                this.message = (MimeMessageCopyOnWriteProxy) message;
-            } else {
-                this.message = new MimeMessageCopyOnWriteProxy(message);
-            }
+            this.message = new MimeMessageWrapper(message);
         }
     }
 
diff --git a/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageCopyOnWriteProxy.java b/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageCopyOnWriteProxy.java
deleted file mode 100644
index cc375e9..0000000
--- a/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageCopyOnWriteProxy.java
+++ /dev/null
@@ -1,556 +0,0 @@
-/****************************************************************
- * 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.server.core;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Date;
-import java.util.Enumeration;
-
-import javax.activation.DataHandler;
-import javax.mail.Address;
-import javax.mail.Flags;
-import javax.mail.Flags.Flag;
-import javax.mail.Folder;
-import javax.mail.Header;
-import javax.mail.Message;
-import javax.mail.MessagingException;
-import javax.mail.Multipart;
-import javax.mail.Session;
-import javax.mail.internet.MimeMessage;
-import javax.mail.search.SearchTerm;
-
-import org.apache.james.lifecycle.api.Disposable;
-import org.apache.james.lifecycle.api.LifecycleUtil;
-
-/**
- * This object wraps a "possibly shared" MimeMessage tracking copies and
- * automatically cloning it (if shared) when a write operation is invoked.
- */
-public class MimeMessageCopyOnWriteProxy extends MimeMessage implements Disposable {
-
-    /**
-     * Used internally to track the reference count It is important that this is
-     * static otherwise it will keep a reference to the parent object.
-     */
-    protected static class MessageReferenceTracker {
-
-        /**
-         * reference counter
-         */
-        private int referenceCount = 1;
-
-        /**
-         * The mime message in memory
-         */
-        private MimeMessage wrapped = null;
-
-        public MessageReferenceTracker(MimeMessage ref) {
-            wrapped = ref;
-        }
-
-        protected synchronized void incrementReferenceCount() {
-            referenceCount++;
-        }
-
-        protected synchronized void decrementReferenceCount() {
-            referenceCount--;
-            if (referenceCount <= 0) {
-                LifecycleUtil.dispose(wrapped);
-                wrapped = null;
-            }
-        }
-
-        protected synchronized int getReferenceCount() {
-            return referenceCount;
-        }
-
-        public synchronized MimeMessage getWrapped() {
-            return wrapped;
-        }
-
-    }
-
-    protected MessageReferenceTracker refCount;
-
-    public MimeMessageCopyOnWriteProxy(MimeMessage original) throws MessagingException {
-        this(original, false);
-    }
-
-    public MimeMessageCopyOnWriteProxy(MimeMessageSource original) throws MessagingException {
-        this(new MimeMessageWrapper(original), true);
-    }
-
-    /**
-     * Private constructor providing an external reference counter.
-     */
-    private MimeMessageCopyOnWriteProxy(MimeMessage original, boolean writeable) {
-        super(Session.getDefaultInstance(System.getProperties(), null));
-
-        if (original instanceof MimeMessageCopyOnWriteProxy) {
-            refCount = ((MimeMessageCopyOnWriteProxy) original).refCount;
-        } else {
-            refCount = new MessageReferenceTracker(original);
-        }
-
-        if (!writeable) {
-            refCount.incrementReferenceCount();
-        }
-    }
-
-    /**
-     * Check the number of references over the MimeMessage and clone it if
-     * needed before returning the reference
-     * 
-     * @throws MessagingException
-     *             exception
-     */
-    protected synchronized MimeMessage getWrappedMessageForWriting() throws MessagingException {
-        if (refCount.getReferenceCount() > 1) {
-            refCount.decrementReferenceCount();
-            refCount = new MessageReferenceTracker(new MimeMessageWrapper(refCount.getWrapped()));
-        }
-        return refCount.getWrapped();
-    }
-
-    /**
-     * Return wrapped mimeMessage
-     * 
-     * @return wrapped return the wrapped mimeMessage
-     */
-    public synchronized MimeMessage getWrappedMessage() {
-        return refCount.getWrapped();
-    }
-
-    @Override
-    public synchronized void dispose() {
-        if (refCount != null) {
-            refCount.decrementReferenceCount();
-            refCount = null;
-        }
-    }
-
-    @Override
-    public void writeTo(OutputStream os) throws IOException, MessagingException {
-        getWrappedMessage().writeTo(os);
-    }
-
-    /**
-     * Rewritten for optimization purposes
-     */
-    @Override
-    public void writeTo(OutputStream os, String[] ignoreList) throws IOException, MessagingException {
-        getWrappedMessage().writeTo(os, ignoreList);
-    }
-
-    /*
-     * Various reader methods
-     */
-
-    @Override
-    public Address[] getFrom() throws MessagingException {
-        return getWrappedMessage().getFrom();
-    }
-
-    @Override
-    public Address[] getRecipients(Message.RecipientType type) throws MessagingException {
-        return getWrappedMessage().getRecipients(type);
-    }
-
-    @Override
-    public Address[] getAllRecipients() throws MessagingException {
-        return getWrappedMessage().getAllRecipients();
-    }
-
-    @Override
-    public Address[] getReplyTo() throws MessagingException {
-        return getWrappedMessage().getReplyTo();
-    }
-
-    @Override
-    public String getSubject() throws MessagingException {
-        return getWrappedMessage().getSubject();
-    }
-
-    @Override
-    public Date getSentDate() throws MessagingException {
-        return getWrappedMessage().getSentDate();
-    }
-
-    @Override
-    public Date getReceivedDate() throws MessagingException {
-        return getWrappedMessage().getReceivedDate();
-    }
-
-    @Override
-    public int getSize() throws MessagingException {
-        return getWrappedMessage().getSize();
-    }
-
-    @Override
-    public int getLineCount() throws MessagingException {
-        return getWrappedMessage().getLineCount();
-    }
-
-    @Override
-    public String getContentType() throws MessagingException {
-        return getWrappedMessage().getContentType();
-    }
-
-    @Override
-    public boolean isMimeType(String mimeType) throws MessagingException {
-        return getWrappedMessage().isMimeType(mimeType);
-    }
-
-    @Override
-    public String getDisposition() throws MessagingException {
-        return getWrappedMessage().getDisposition();
-    }
-
-    @Override
-    public String getEncoding() throws MessagingException {
-        return getWrappedMessage().getEncoding();
-    }
-
-    @Override
-    public String getContentID() throws MessagingException {
-        return getWrappedMessage().getContentID();
-    }
-
-    @Override
-    public String getContentMD5() throws MessagingException {
-        return getWrappedMessage().getContentMD5();
-    }
-
-    @Override
-    public String getDescription() throws MessagingException {
-        return getWrappedMessage().getDescription();
-    }
-
-    @Override
-    public String[] getContentLanguage() throws MessagingException {
-        return getWrappedMessage().getContentLanguage();
-    }
-
-    @Override
-    public String getMessageID() throws MessagingException {
-        return getWrappedMessage().getMessageID();
-    }
-
-    @Override
-    public String getFileName() throws MessagingException {
-        return getWrappedMessage().getFileName();
-    }
-
-    @Override
-    public InputStream getInputStream() throws IOException, MessagingException {
-        return getWrappedMessage().getInputStream();
-    }
-
-    @Override
-    public DataHandler getDataHandler() throws MessagingException {
-        return getWrappedMessage().getDataHandler();
-    }
-
-    @Override
-    public Object getContent() throws IOException, MessagingException {
-        return getWrappedMessage().getContent();
-    }
-
-    @Override
-    public String[] getHeader(String name) throws MessagingException {
-        return getWrappedMessage().getHeader(name);
-    }
-
-    @Override
-    public String getHeader(String name, String delimiter) throws MessagingException {
-        return getWrappedMessage().getHeader(name, delimiter);
-    }
-
-    @Override
-    public Enumeration<Header> getAllHeaders() throws MessagingException {
-        return getWrappedMessage().getAllHeaders();
-    }
-
-    @Override
-    public Enumeration<Header> getMatchingHeaders(String[] names) throws MessagingException {
-        return getWrappedMessage().getMatchingHeaders(names);
-    }
-
-    @Override
-    public Enumeration<Header> getNonMatchingHeaders(String[] names) throws MessagingException {
-        return getWrappedMessage().getNonMatchingHeaders(names);
-    }
-
-    @Override
-    public Enumeration<String> getAllHeaderLines() throws MessagingException {
-        return getWrappedMessage().getAllHeaderLines();
-    }
-
-    @Override
-    public Enumeration<String> getMatchingHeaderLines(String[] names) throws MessagingException {
-        return getWrappedMessage().getMatchingHeaderLines(names);
-    }
-
-    @Override
-    public Enumeration<String> getNonMatchingHeaderLines(String[] names) throws MessagingException {
-        return getWrappedMessage().getNonMatchingHeaderLines(names);
-    }
-
-    @Override
-    public Flags getFlags() throws MessagingException {
-        return getWrappedMessage().getFlags();
-    }
-
-    @Override
-    public boolean isSet(Flags.Flag flag) throws MessagingException {
-        return getWrappedMessage().isSet(flag);
-    }
-
-    @Override
-    public Address getSender() throws MessagingException {
-        return getWrappedMessage().getSender();
-    }
-
-    @Override
-    public boolean match(SearchTerm arg0) throws MessagingException {
-        return getWrappedMessage().match(arg0);
-    }
-
-    @Override
-    public InputStream getRawInputStream() throws MessagingException {
-        return getWrappedMessage().getRawInputStream();
-    }
-
-    @Override
-    public Folder getFolder() {
-        return getWrappedMessage().getFolder();
-    }
-
-    @Override
-    public int getMessageNumber() {
-        return getWrappedMessage().getMessageNumber();
-    }
-
-    @Override
-    public boolean isExpunged() {
-        return getWrappedMessage().isExpunged();
-    }
-
-    @Override
-    public boolean equals(Object arg0) {
-        return getWrappedMessage().equals(arg0);
-    }
-
-    @Override
-    public int hashCode() {
-        return getWrappedMessage().hashCode();
-    }
-
-    @Override
-    public String toString() {
-        return getWrappedMessage().toString();
-    }
-
-    @Override
-    public void setFrom(Address address) throws MessagingException {
-        getWrappedMessageForWriting().setFrom(address);
-    }
-
-    @Override
-    public void setFrom() throws MessagingException {
-        getWrappedMessageForWriting().setFrom();
-    }
-
-    @Override
-    public void addFrom(Address[] addresses) throws MessagingException {
-        getWrappedMessageForWriting().addFrom(addresses);
-    }
-
-    @Override
-    public void setRecipients(Message.RecipientType type, Address[] addresses) throws MessagingException {
-        getWrappedMessageForWriting().setRecipients(type, addresses);
-    }
-
-    @Override
-    public void addRecipients(Message.RecipientType type, Address[] addresses) throws MessagingException {
-        getWrappedMessageForWriting().addRecipients(type, addresses);
-    }
-
-    @Override
-    public void setReplyTo(Address[] addresses) throws MessagingException {
-        getWrappedMessageForWriting().setReplyTo(addresses);
-    }
-
-    @Override
-    public void setSubject(String subject) throws MessagingException {
-        getWrappedMessageForWriting().setSubject(subject);
-    }
-
-    @Override
-    public void setSubject(String subject, String charset) throws MessagingException {
-        getWrappedMessageForWriting().setSubject(subject, charset);
-    }
-
-    @Override
-    public void setSentDate(Date d) throws MessagingException {
-        getWrappedMessageForWriting().setSentDate(d);
-    }
-
-    @Override
-    public void setDisposition(String disposition) throws MessagingException {
-        getWrappedMessageForWriting().setDisposition(disposition);
-    }
-
-    @Override
-    public void setContentID(String cid) throws MessagingException {
-        getWrappedMessageForWriting().setContentID(cid);
-    }
-
-    @Override
-    public void setContentMD5(String md5) throws MessagingException {
-        getWrappedMessageForWriting().setContentMD5(md5);
-    }
-
-    @Override
-    public void setDescription(String description) throws MessagingException {
-        getWrappedMessageForWriting().setDescription(description);
-    }
-
-    @Override
-    public void setDescription(String description, String charset) throws MessagingException {
-        getWrappedMessageForWriting().setDescription(description, charset);
-    }
-
-    @Override
-    public void setContentLanguage(String[] languages) throws MessagingException {
-        getWrappedMessageForWriting().setContentLanguage(languages);
-    }
-
-    @Override
-    public void setFileName(String filename) throws MessagingException {
-        getWrappedMessageForWriting().setFileName(filename);
-    }
-
-    @Override
-    public void setDataHandler(DataHandler dh) throws MessagingException {
-        getWrappedMessageForWriting().setDataHandler(dh);
-    }
-
-    @Override
-    public void setContent(Object o, String type) throws MessagingException {
-        getWrappedMessageForWriting().setContent(o, type);
-    }
-
-    @Override
-    public void setText(String text) throws MessagingException {
-        getWrappedMessageForWriting().setText(text);
-    }
-
-    @Override
-    public void setText(String text, String charset) throws MessagingException {
-        getWrappedMessageForWriting().setText(text, charset);
-    }
-
-    @Override
-    public void setContent(Multipart mp) throws MessagingException {
-        getWrappedMessageForWriting().setContent(mp);
-    }
-
-    /**
-     * This does not need a writable message
-     */
-    @Override
-    public Message reply(boolean replyToAll) throws MessagingException {
-        return getWrappedMessage().reply(replyToAll);
-    }
-
-    @Override
-    public void setHeader(String name, String value) throws MessagingException {
-        getWrappedMessageForWriting().setHeader(name, value);
-    }
-
-    @Override
-    public void addHeader(String name, String value) throws MessagingException {
-        getWrappedMessageForWriting().addHeader(name, value);
-    }
-
-    @Override
-    public void removeHeader(String name) throws MessagingException {
-        getWrappedMessageForWriting().removeHeader(name);
-    }
-
-    @Override
-    public void addHeaderLine(String line) throws MessagingException {
-        getWrappedMessageForWriting().addHeaderLine(line);
-    }
-
-    @Override
-    public void setFlags(Flags flag, boolean set) throws MessagingException {
-        getWrappedMessageForWriting().setFlags(flag, set);
-    }
-
-    @Override
-    public void saveChanges() throws MessagingException {
-        getWrappedMessageForWriting().saveChanges();
-    }
-
-    /*
-     * Since JavaMail 1.2
-     */
-
-    @Override
-    public void addRecipients(Message.RecipientType type, String addresses) throws MessagingException {
-        getWrappedMessageForWriting().addRecipients(type, addresses);
-    }
-
-    @Override
-    public void setRecipients(Message.RecipientType type, String addresses) throws MessagingException {
-        getWrappedMessageForWriting().setRecipients(type, addresses);
-    }
-
-    @Override
-    public void setSender(Address arg0) throws MessagingException {
-        getWrappedMessageForWriting().setSender(arg0);
-    }
-
-    public void addRecipient(RecipientType arg0, Address arg1) throws MessagingException {
-        getWrappedMessageForWriting().addRecipient(arg0, arg1);
-    }
-
-    @Override
-    public void setFlag(Flag arg0, boolean arg1) throws MessagingException {
-        getWrappedMessageForWriting().setFlag(arg0, arg1);
-    }
-
-    public long getMessageSize() throws MessagingException {
-        return MimeMessageUtil.getMessageSize(getWrappedMessage());
-    }
-
-    /**
-     * Since javamail 1.4
-     */
-    @Override
-    public void setText(String text, String charset, String subtype) throws MessagingException {
-        getWrappedMessage().setText(text, charset, subtype);
-    }
-
-}
diff --git a/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageInputStream.java b/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageInputStream.java
index c3e27d4..d3fcdbf 100644
--- a/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageInputStream.java
+++ b/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageInputStream.java
@@ -40,7 +40,6 @@ public class MimeMessageInputStream extends InputStream {
      *            the message to wrap
      * @param tryCast
      *            try to cast the {@link MimeMessage} to
-     *            {@link MimeMessageCopyOnWriteProxy} /
      *            {@link MimeMessageWrapper} to do some optimized processing if
      *            possible
      * @throws MessagingException
@@ -48,11 +47,6 @@ public class MimeMessageInputStream extends InputStream {
     public MimeMessageInputStream(MimeMessage message, boolean tryCast) throws MessagingException {
         MimeMessage m = message;
 
-        // check if we need to use the wrapped message
-        if (tryCast && m instanceof MimeMessageCopyOnWriteProxy) {
-            m = ((MimeMessageCopyOnWriteProxy) m).getWrappedMessage();
-        }
-
         // check if we can use optimized operations
         if (tryCast && m instanceof MimeMessageWrapper) {
             in = ((MimeMessageWrapper) m).getMessageInputStream();
diff --git a/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageUtil.java b/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageUtil.java
index e68f00f..937a033 100644
--- a/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageUtil.java
+++ b/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageUtil.java
@@ -74,13 +74,8 @@ public class MimeMessageUtil {
      *             get thrown if an error detected while reading the message
      */
     public static void writeTo(MimeMessage message, OutputStream headerOs, OutputStream bodyOs, String[] ignoreList) throws IOException, MessagingException {
-        MimeMessage testMessage = message;
-        if (message instanceof MimeMessageCopyOnWriteProxy) {
-            MimeMessageCopyOnWriteProxy wr = (MimeMessageCopyOnWriteProxy) message;
-            testMessage = wr.getWrappedMessage();
-        }
-        if (testMessage instanceof MimeMessageWrapper) {
-            MimeMessageWrapper wrapper = (MimeMessageWrapper) testMessage;
+        if (message instanceof MimeMessageWrapper) {
+            MimeMessageWrapper wrapper = (MimeMessageWrapper) message;
             if (!wrapper.isModified()) {
                 wrapper.writeTo(headerOs, bodyOs, ignoreList);
                 return;
@@ -253,9 +248,6 @@ public class MimeMessageUtil {
         if (message instanceof MimeMessageWrapper) {
             MimeMessageWrapper wrapper = (MimeMessageWrapper) message;
             size = wrapper.getMessageSize();
-        } else if (message instanceof MimeMessageCopyOnWriteProxy) {
-            MimeMessageCopyOnWriteProxy wrapper = (MimeMessageCopyOnWriteProxy) message;
-            size = wrapper.getMessageSize();
         }
 
         if (size == -1) {
diff --git a/server/container/core/src/test/java/org/apache/james/server/core/MimeMessageCopyOnWriteProxyTest.java b/server/container/core/src/test/java/org/apache/james/server/core/MimeMessageCopyOnWriteProxyTest.java
deleted file mode 100644
index 8ac24a0..0000000
--- a/server/container/core/src/test/java/org/apache/james/server/core/MimeMessageCopyOnWriteProxyTest.java
+++ /dev/null
@@ -1,290 +0,0 @@
-/****************************************************************
- * 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.server.core;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Fail.fail;
-
-import javax.mail.MessagingException;
-import javax.mail.internet.MimeMessage;
-import javax.mail.util.SharedByteArrayInputStream;
-
-import org.apache.james.core.builder.MimeMessageBuilder;
-import org.apache.james.lifecycle.api.LifecycleUtil;
-import org.apache.mailet.Mail;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-
-public class MimeMessageCopyOnWriteProxyTest extends MimeMessageFromStreamTest {
-
-    final String content = "Subject: foo\r\nContent-Transfer-Encoding2: plain";
-    final String sep = "\r\n\r\n";
-    final String body = "bar\r\n.\r\n";
-    final String mimeMessageAsString = content + sep + body;
-
-    @BeforeAll
-    static void setUp() {
-        ContentTypeCleaner.initialize();
-    }
-
-    @Override
-    protected MimeMessage getMessageFromSources(String sources) throws Exception {
-        MimeMessageInputStreamSource mmis = new MimeMessageInputStreamSource("test", new SharedByteArrayInputStream(sources.getBytes()));
-        return new MimeMessageCopyOnWriteProxy(mmis);
-    }
-
-    @Test
-    void testMessageCloning1() throws Exception {
-        MimeMessageCopyOnWriteProxy messageFromSources = (MimeMessageCopyOnWriteProxy) getMessageFromSources(
-                mimeMessageAsString);
-        MailImpl mail = MailImpl.builder()
-            .name("test")
-            .sender("test@test.com")
-            .addRecipient("recipient@test.com")
-            .mimeMessage(messageFromSources)
-            .build();
-        MailImpl m2 = MailImpl.duplicate(mail);
-        assertThat(mail).isNotSameAs((m2));
-        assertThat(mail.getMessage()).isNotSameAs(m2.getMessage());
-        // test that the wrapped message is the same
-        assertThat(isSameMimeMessage(m2.getMessage(), mail.getMessage())).isTrue();
-        // test it is the same after read only operations!
-        mail.getMessage().getSubject();
-        assertThat(isSameMimeMessage(m2.getMessage(), mail.getMessage())).isTrue();
-        mail.getMessage().setText("new body");
-        mail.getMessage().saveChanges();
-        // test it is different after a write operation!
-        mail.getMessage().setSubject("new Subject");
-        assertThat(!isSameMimeMessage(m2.getMessage(), mail.getMessage())).isTrue();
-        LifecycleUtil.dispose(mail);
-        LifecycleUtil.dispose(m2);
-        LifecycleUtil.dispose(messageFromSources);
-    }
-
-    @Test
-    void testMessageCloning2() throws Exception {
-        MimeMessageCopyOnWriteProxy messageFromSources = (MimeMessageCopyOnWriteProxy) getMessageFromSources(
-                mimeMessageAsString);
-        MailImpl mail = MailImpl.builder()
-            .name("test")
-            .sender("test@test.com")
-            .addRecipient("recipient@test.com")
-            .mimeMessage(messageFromSources)
-            .build();
-
-        MailImpl m2 = MailImpl.duplicate(mail);
-        assertThat(mail).isNotSameAs((m2));
-        assertThat(mail.getMessage()).isNotSameAs(m2.getMessage());
-        // test that the wrapped message is the same
-        assertThat(isSameMimeMessage(m2.getMessage(), mail.getMessage())).isTrue();
-        // test it is the same after real only operations!
-        m2.getMessage().getSubject();
-        assertThat(isSameMimeMessage(m2.getMessage(), mail.getMessage())).isTrue();
-        m2.getMessage().setText("new body");
-        m2.getMessage().saveChanges();
-        // test it is different after a write operation!
-        m2.getMessage().setSubject("new Subject");
-        assertThat(!isSameMimeMessage(m2.getMessage(), mail.getMessage())).isTrue();
-        // check that the subjects are correct on both mails!
-        assertThat("new Subject").isEqualTo(m2.getMessage().getSubject());
-        assertThat("foo").isEqualTo(mail.getMessage().getSubject());
-        // cloning again the messages
-        Mail m2clone = MailImpl.duplicate(m2);
-        assertThat(isSameMimeMessage(m2clone.getMessage(), m2.getMessage())).isTrue();
-        MimeMessage mm = getWrappedMessage(m2.getMessage());
-        assertThat(m2clone.getMessage()).isNotSameAs(m2.getMessage());
-        // test that m2clone has a valid wrapped message
-        MimeMessage mm3 = getWrappedMessage(m2clone.getMessage());
-        assertThat(mm3).isNotNull();
-        // dispose m2 and check that the clone has still a valid message and it
-        // is the same!
-        LifecycleUtil.dispose(m2);
-        assertThat(getWrappedMessage(m2clone.getMessage())).isEqualTo(mm3);
-        // change the message that should be not referenced by m2 that has
-        // been disposed, so it should not clone it!
-        m2clone.getMessage().setSubject("new Subject 2");
-        m2clone.getMessage().setText("new Body 3");
-        assertThat(isSameMimeMessage(m2clone.getMessage(), mm)).isTrue();
-        LifecycleUtil.dispose(mail);
-        LifecycleUtil.dispose(messageFromSources);
-    }
-
-    /**
-     * If I create a new MimeMessageCopyOnWriteProxy from another
-     * MimeMessageCopyOnWriteProxy, I remove references to the first and I
-     * change the second, then it should not clone
-     */
-    @Test
-    void testMessageAvoidCloning() throws Exception {
-        MimeMessageCopyOnWriteProxy messageFromSources = (MimeMessageCopyOnWriteProxy) getMessageFromSources(
-                mimeMessageAsString);
-        MailImpl mail = MailImpl.builder()
-            .name("test")
-            .sender("test@test.com")
-            .addRecipient("recipient@test.com")
-            .mimeMessage(messageFromSources)
-            .build();
-        // cloning the message
-        Mail mailClone = MailImpl.duplicate(mail);
-        assertThat(isSameMimeMessage(mailClone.getMessage(), mail.getMessage())).isTrue();
-        MimeMessage mm = getWrappedMessage(mail.getMessage());
-        assertThat(mailClone.getMessage()).isNotSameAs(mail.getMessage());
-        // dispose mail and check that the clone has still a valid message and
-        // it is the same!
-        LifecycleUtil.dispose(mail);
-        LifecycleUtil.dispose(messageFromSources);
-        // need to add a gc and a wait, because the original mimemessage should
-        // be finalized before the test.
-        System.gc();
-        Thread.sleep(1000);
-        // dumb test
-        assertThat(isSameMimeMessage(mailClone.getMessage(), mailClone.getMessage())).isTrue();
-        // change the message that should be not referenced by mail that has
-        // been disposed, so it should not clone it!
-        mailClone.getMessage().setSubject("new Subject 2");
-        mailClone.getMessage().setText("new Body 3");
-        assertThat(isSameMimeMessage(mailClone.getMessage(), mm)).isTrue();
-        LifecycleUtil.dispose(mailClone);
-        LifecycleUtil.dispose(mm);
-    }
-
-    /**
-     * If I create a new MimeMessageCopyOnWriteProxy from a MimeMessage and I
-     * change the new message, the original should be unaltered and the proxy
-     * should clone the message.
-     */
-    @Test
-    void testMessageCloning3() throws Exception {
-        MimeMessage mimeMessage = MimeMessageBuilder.mimeMessageBuilder()
-            .setText("CIPS")
-            .build();
-        MailImpl mail = MailImpl.builder()
-            .name("test")
-            .sender("test@test.com")
-            .addRecipient("recipient@test.com")
-            .mimeMessage(mimeMessage)
-            .build();
-
-        assertThat(isSameMimeMessage(mimeMessage, mail.getMessage())).isTrue();
-        // change the message that should be not referenced by mail that has
-        // been disposed, so it should not clone it!
-        System.gc();
-        Thread.sleep(100);
-        mail.getMessage().setSubject("new Subject 2");
-        mail.getMessage().setText("new Body 3");
-        System.gc();
-        Thread.sleep(100);
-        assertThat(isSameMimeMessage(mimeMessage, mail.getMessage())).isFalse();
-        LifecycleUtil.dispose(mail);
-        LifecycleUtil.dispose(mimeMessage);
-    }
-
-    @Test
-    void testMessageDisposing() throws Exception {
-        MimeMessageCopyOnWriteProxy messageFromSources = (MimeMessageCopyOnWriteProxy) getMessageFromSources(
-                mimeMessageAsString);
-        MailImpl mail = MailImpl.builder()
-            .name("test")
-            .sender("test@test.com")
-            .addRecipient("recipient@test.com")
-            .mimeMessage(messageFromSources)
-            .build();
-        // cloning the message
-        MailImpl mailClone = MailImpl.duplicate(mail);
-        LifecycleUtil.dispose(mail);
-
-        assertThat(getWrappedMessage(mailClone.getMessage())).isNotNull();
-        assertThat(mail.getMessage()).isNull();
-
-        LifecycleUtil.dispose(mailClone);
-
-        assertThat(mailClone.getMessage()).isNull();
-        assertThat(mail.getMessage()).isNull();
-        LifecycleUtil.dispose(mail);
-        LifecycleUtil.dispose(messageFromSources);
-    }
-
-    @Test
-    void testNPE1() throws MessagingException, InterruptedException {
-        MimeMessageCopyOnWriteProxy mw = new MimeMessageCopyOnWriteProxy(new MimeMessageInputStreamSource("test",
-                new SharedByteArrayInputStream(("Return-path: return@test.com\r\n" + "Content-Transfer-Encoding: plain\r\n" + "Subject: test\r\n\r\n" + "Body Text testNPE1\r\n")
-                        .getBytes())));
-
-        MimeMessageCopyOnWriteProxy mw2 = new MimeMessageCopyOnWriteProxy(mw);
-        LifecycleUtil.dispose(mw2);
-        mw2 = null;
-        System.gc();
-        Thread.sleep(1000);
-        // the NPE was inside this call
-        mw.getMessageSize();
-        LifecycleUtil.dispose(mw);
-    }
-
-    /**
-     * This test throw a NullPointerException when the original message was
-     * created by a MimeMessageInputStreamSource.
-     */
-    @Test
-    void testMessageCloningViaCoW3() throws Exception {
-        MimeMessage mmorig = getSimpleMessage();
-
-        MimeMessage mm = new MimeMessageCopyOnWriteProxy(mmorig);
-
-        LifecycleUtil.dispose(mmorig);
-        mmorig = null;
-        System.gc();
-        Thread.sleep(200);
-
-        try {
-            mm.writeTo(System.out);
-        } catch (Exception e) {
-            e.printStackTrace();
-            fail("Exception while writing the message to output");
-        }
-
-        LifecycleUtil.dispose(mm);
-    }
-
-    @Test
-    void testMessageWithWrongContentTypeShouldNotThrow() throws Exception {
-        MimeMessageCopyOnWriteProxy messageFromSources = (MimeMessageCopyOnWriteProxy) getMessageFromSources(
-                mimeMessageAsString);
-        MailImpl mail = MailImpl.builder()
-            .name("test")
-            .sender("test@test.com")
-            .addRecipient("recipient@test.com")
-            .mimeMessage(messageFromSources)
-            .build();
-        mail.getMessage().addHeader("Content-Type", "file;name=\"malformed.pdf\"");
-        mail.getMessage().saveChanges();
-        LifecycleUtil.dispose(mail);
-        LifecycleUtil.dispose(messageFromSources);
-    }
-
-    private static MimeMessage getWrappedMessage(MimeMessage m) {
-        while (m instanceof MimeMessageCopyOnWriteProxy) {
-            m = ((MimeMessageCopyOnWriteProxy) m).getWrappedMessage();
-        }
-        return m;
-    }
-
-    private static boolean isSameMimeMessage(MimeMessage first, MimeMessage second) {
-        return getWrappedMessage(first) == getWrappedMessage(second);
-    }
-}
diff --git a/server/container/core/src/test/java/org/apache/james/server/core/MimeMessageTest.java b/server/container/core/src/test/java/org/apache/james/server/core/MimeMessageTest.java
index 3e02407..4c74459 100644
--- a/server/container/core/src/test/java/org/apache/james/server/core/MimeMessageTest.java
+++ b/server/container/core/src/test/java/org/apache/james/server/core/MimeMessageTest.java
@@ -303,78 +303,6 @@ public class MimeMessageTest {
         LifecycleUtil.dispose(mm);
     }
 
-    /**
-     * This test throw a NullPointerException when the original message was
-     * created by a MimeMessageInputStreamSource.
-     */
-    @Test
-    public void testMessageCloningViaCoW() throws Exception {
-        MimeMessage mmorig = getSimpleMessage();
-
-        MimeMessage mm = new MimeMessageCopyOnWriteProxy(mmorig);
-
-        MimeMessage mm2 = new MimeMessageCopyOnWriteProxy(mm);
-
-        mm2.setHeader("Subject", "Modified");
-
-        LifecycleUtil.dispose(mm2);
-        System.gc();
-        Thread.sleep(200);
-        // ((Disposable)mail_dup.getMessage()).dispose();
-
-        mm.setHeader("Subject", "Modified");
-
-        LifecycleUtil.dispose(mm);
-        LifecycleUtil.dispose(mmorig);
-    }
-
-    /**
-     * This test throw a NullPointerException when the original message was
-     * created by a MimeMessageInputStreamSource.
-     */
-    @Test
-    public void testMessageCloningViaCoW2() throws Exception {
-        MimeMessage mmorig = getSimpleMessage();
-
-        MimeMessage mm = new MimeMessageCopyOnWriteProxy(mmorig);
-
-        MimeMessage mm2 = new MimeMessageCopyOnWriteProxy(mm);
-
-        LifecycleUtil.dispose(mm);
-        mm = null;
-        System.gc();
-        Thread.sleep(200);
-
-        try {
-            mm2.writeTo(System.out);
-        } catch (Exception e) {
-            e.printStackTrace();
-            fail("Exception while writing the message to output");
-        }
-
-        LifecycleUtil.dispose(mm2);
-        LifecycleUtil.dispose(mmorig);
-    }
-
-    /**
-     * This test throw a NullPointerException when the original message was
-     * created by a MimeMessageInputStreamSource.
-     */
-    @Test
-    public void testMessageCloningViaCoWSubjectLost() throws Exception {
-        MimeMessage mmorig = getSimpleMessage();
-
-        MimeMessage mm = new MimeMessageCopyOnWriteProxy(mmorig);
-
-        mm.setHeader("X-Test", "foo");
-        mm.saveChanges();
-
-        assertThat(getCleanedMessageSource(mm)).isEqualTo(getSimpleMessageCleanedSourceHeaderExpected());
-
-        LifecycleUtil.dispose(mm);
-        LifecycleUtil.dispose(mmorig);
-    }
-
     @Test
     public void testReturnPath() throws Exception {
         MimeMessage message = getSimpleMessage();
diff --git a/server/data/data-file/src/main/java/org/apache/james/mailrepository/file/FileMailRepository.java b/server/data/data-file/src/main/java/org/apache/james/mailrepository/file/FileMailRepository.java
index c5cba49..e5fee5d 100644
--- a/server/data/data-file/src/main/java/org/apache/james/mailrepository/file/FileMailRepository.java
+++ b/server/data/data-file/src/main/java/org/apache/james/mailrepository/file/FileMailRepository.java
@@ -45,7 +45,6 @@ import org.apache.james.mailrepository.api.MailKey;
 import org.apache.james.mailrepository.api.MailRepository;
 import org.apache.james.repository.file.FilePersistentObjectRepository;
 import org.apache.james.repository.file.FilePersistentStreamRepository;
-import org.apache.james.server.core.MimeMessageCopyOnWriteProxy;
 import org.apache.james.server.core.MimeMessageWrapper;
 import org.apache.mailet.Mail;
 import org.slf4j.Logger;
@@ -222,12 +221,7 @@ public class FileMailRepository implements MailRepository, Configurable, Initial
         boolean saveStream = true;
 
         MimeMessage message = mc.getMessage();
-        // if the message is a Copy on Write proxy we check the wrapped message
-        // to optimize the behaviour in case of MimeMessageWrapper
-        if (message instanceof MimeMessageCopyOnWriteProxy) {
-            MimeMessageCopyOnWriteProxy messageCow = (MimeMessageCopyOnWriteProxy) message;
-            message = messageCow.getWrappedMessage();
-        }
+
         if (message instanceof MimeMessageWrapper) {
             MimeMessageWrapper wrapper = (MimeMessageWrapper) message;
             LOGGER.trace("Retrieving from: {}", wrapper.getSourceId());
@@ -288,7 +282,7 @@ public class FileMailRepository implements MailRepository, Configurable, Initial
                 return null;
             }
             MimeMessageStreamRepositorySource source = new MimeMessageStreamRepositorySource(streamRepository, destination, key.asString());
-            mc.setMessage(new MimeMessageCopyOnWriteProxy(source));
+            mc.setMessage(new MimeMessageWrapper(source));
 
             return mc;
         } catch (Exception me) {
diff --git a/server/data/data-jdbc/src/main/java/org/apache/james/mailrepository/jdbc/JDBCMailRepository.java b/server/data/data-jdbc/src/main/java/org/apache/james/mailrepository/jdbc/JDBCMailRepository.java
index 4b25b4f..78e66f0 100644
--- a/server/data/data-jdbc/src/main/java/org/apache/james/mailrepository/jdbc/JDBCMailRepository.java
+++ b/server/data/data-jdbc/src/main/java/org/apache/james/mailrepository/jdbc/JDBCMailRepository.java
@@ -61,7 +61,6 @@ import org.apache.james.mailrepository.api.MailKey;
 import org.apache.james.mailrepository.api.MailRepository;
 import org.apache.james.repository.file.FilePersistentStreamRepository;
 import org.apache.james.server.core.MailImpl;
-import org.apache.james.server.core.MimeMessageCopyOnWriteProxy;
 import org.apache.james.server.core.MimeMessageWrapper;
 import org.apache.james.util.sql.JDBCUtil;
 import org.apache.james.util.sql.SqlResources;
@@ -555,12 +554,7 @@ public class JDBCMailRepository implements MailRepository, Configurable, Initial
     private boolean saveBodyRequired(Mail mc) throws MessagingException {
         boolean saveBody;
         MimeMessage messageBody = mc.getMessage();
-        // if the message is a CopyOnWrite proxy we check the modified
-        // wrapped object.
-        if (messageBody instanceof MimeMessageCopyOnWriteProxy) {
-            MimeMessageCopyOnWriteProxy messageCow = (MimeMessageCopyOnWriteProxy) messageBody;
-            messageBody = messageCow.getWrappedMessage();
-        }
+
         if (messageBody instanceof MimeMessageWrapper) {
             MimeMessageWrapper message = (MimeMessageWrapper) messageBody;
             saveBody = message.isModified();
@@ -668,7 +662,7 @@ public class JDBCMailRepository implements MailRepository, Configurable, Initial
             mc.lastUpdated(rsMessage.getTimestamp(8));
 
             MimeMessageJDBCSource source = new MimeMessageJDBCSource(this, key.asString(), sr);
-            MimeMessageCopyOnWriteProxy message = new MimeMessageCopyOnWriteProxy(source);
+            MimeMessageWrapper message = new MimeMessageWrapper(source);
             mc.mimeMessage(message);
             return mc.build();
         } catch (SQLException sqle) {
diff --git a/server/data/data-jdbc/src/main/java/org/apache/james/mailrepository/jdbc/MessageInputStream.java b/server/data/data-jdbc/src/main/java/org/apache/james/mailrepository/jdbc/MessageInputStream.java
index 3e5ae2b..2235ec0 100644
--- a/server/data/data-jdbc/src/main/java/org/apache/james/mailrepository/jdbc/MessageInputStream.java
+++ b/server/data/data-jdbc/src/main/java/org/apache/james/mailrepository/jdbc/MessageInputStream.java
@@ -31,7 +31,6 @@ import javax.mail.MessagingException;
 import javax.mail.internet.MimeMessage;
 
 import org.apache.james.repository.api.StreamRepository;
-import org.apache.james.server.core.MimeMessageCopyOnWriteProxy;
 import org.apache.james.server.core.MimeMessageUtil;
 import org.apache.james.server.core.MimeMessageWrapper;
 import org.apache.mailet.Mail;
@@ -148,17 +147,9 @@ final class MessageInputStream extends InputStream {
     private void writeStream(Mail mail, OutputStream out, boolean update) throws IOException, MessagingException {
         MimeMessage msg = mail.getMessage();
 
-        if (update) {
-
-            if (msg instanceof MimeMessageCopyOnWriteProxy) {
-                msg = ((MimeMessageCopyOnWriteProxy) msg).getWrappedMessage();
-            }
-
-            if (msg instanceof MimeMessageWrapper) {
-                MimeMessageWrapper wrapper = (MimeMessageWrapper) msg;
-                wrapper.loadMessage();
-
-            }
+        if (update && msg instanceof MimeMessageWrapper) {
+            MimeMessageWrapper wrapper = (MimeMessageWrapper) msg;
+            wrapper.loadMessage();
         }
 
         OutputStream bodyOut = null;
diff --git a/server/mailet/mailetcontainer-camel/src/test/java/org/apache/james/mailetcontainer/impl/JamesMailetContextTest.java b/server/mailet/mailetcontainer-camel/src/test/java/org/apache/james/mailetcontainer/impl/JamesMailetContextTest.java
index c0a0d1b..de64fc2 100644
--- a/server/mailet/mailetcontainer-camel/src/test/java/org/apache/james/mailetcontainer/impl/JamesMailetContextTest.java
+++ b/server/mailet/mailetcontainer-camel/src/test/java/org/apache/james/mailetcontainer/impl/JamesMailetContextTest.java
@@ -19,6 +19,7 @@
 
 package org.apache.james.mailetcontainer.impl;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.mockito.ArgumentMatchers.any;
@@ -30,6 +31,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
+import java.nio.charset.StandardCharsets;
 import java.util.concurrent.TimeUnit;
 
 import javax.mail.internet.MimeMessage;
@@ -285,7 +287,7 @@ class JamesMailetContextTest {
             .name("mail1")
             .sender(mailAddress)
             .addRecipient(mailAddress)
-            .mimeMessage(MimeMessageUtil.defaultMimeMessage())
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes("header: value\r\n".getBytes(UTF_8)))
             .build();
         testee.bounce(mail, "message");
     }
@@ -296,7 +298,7 @@ class JamesMailetContextTest {
             .name("mail1")
             .sender(MailAddress.nullSender())
             .addRecipient(mailAddress)
-            .mimeMessage(MimeMessageUtil.defaultMimeMessage())
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes("header: value\r\n".getBytes(UTF_8)))
             .build();
 
         testee.bounce(mail, "message");
@@ -309,7 +311,7 @@ class JamesMailetContextTest {
         MailImpl mail = MailImpl.builder()
             .name("mail1")
             .addRecipient(mailAddress)
-            .mimeMessage(MimeMessageUtil.defaultMimeMessage())
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes("header: value\r\n".getBytes(UTF_8)))
             .build();
 
         testee.bounce(mail, "message");
@@ -323,7 +325,7 @@ class JamesMailetContextTest {
             .name("mail1")
             .sender(mailAddress)
             .addRecipient(mailAddress)
-            .mimeMessage(MimeMessageUtil.defaultMimeMessage())
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes("header: value\r\n".getBytes(UTF_8)))
             .build();
 
         testee.bounce(mail, "message");
@@ -341,7 +343,7 @@ class JamesMailetContextTest {
             .name("mail1")
             .sender(mailAddress)
             .addRecipient(mailAddress)
-            .mimeMessage(MimeMessageUtil.defaultMimeMessage())
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes("header: value\r\n".getBytes(UTF_8)))
             .build();
         testee.sendMail(mail);
 
@@ -358,7 +360,7 @@ class JamesMailetContextTest {
             .name("mail1")
             .sender(mailAddress)
             .addRecipient(mailAddress)
-            .mimeMessage(MimeMessageUtil.defaultMimeMessage())
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes("header: value\r\n".getBytes(UTF_8)))
             .build();
         String other = "other";
         testee.sendMail(mail, other);
@@ -376,7 +378,7 @@ class JamesMailetContextTest {
             .name("mail1")
             .sender(mailAddress)
             .addRecipient(mailAddress)
-            .mimeMessage(MimeMessageUtil.defaultMimeMessage())
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes("header: value\r\n".getBytes(UTF_8)))
             .build();
         testee.sendMail(mail, 5, TimeUnit.MINUTES);
 
@@ -399,7 +401,7 @@ class JamesMailetContextTest {
             .name("mail1")
             .sender(mailAddress)
             .addRecipient(mailAddress)
-            .mimeMessage(MimeMessageUtil.defaultMimeMessage())
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes("header: value\r\n".getBytes(UTF_8)))
             .build();
         String other = "other";
         testee.sendMail(mail, other, 5, TimeUnit.MINUTES);
diff --git a/server/mailet/mailetcontainer-camel/src/test/java/org/apache/james/mailetcontainer/lib/AbstractStateMailetProcessorTest.java b/server/mailet/mailetcontainer-camel/src/test/java/org/apache/james/mailetcontainer/lib/AbstractStateMailetProcessorTest.java
index bb5f33a..0e8f4b0 100644
--- a/server/mailet/mailetcontainer-camel/src/test/java/org/apache/james/mailetcontainer/lib/AbstractStateMailetProcessorTest.java
+++ b/server/mailet/mailetcontainer-camel/src/test/java/org/apache/james/mailetcontainer/lib/AbstractStateMailetProcessorTest.java
@@ -18,9 +18,11 @@
  ****************************************************************/
 package org.apache.james.mailetcontainer.lib;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
 import java.util.Collection;
 import java.util.concurrent.CountDownLatch;
 
@@ -38,6 +40,7 @@ import org.apache.james.mailetcontainer.api.mock.MockMatcher;
 import org.apache.james.mailetcontainer.lib.AbstractStateMailetProcessor.MailetProcessorListener;
 import org.apache.james.server.core.MailImpl;
 import org.apache.james.server.core.configuration.FileConfigurationProvider;
+import org.apache.james.util.MimeMessageUtil;
 import org.apache.mailet.Mail;
 import org.apache.mailet.Mailet;
 import org.apache.mailet.Matcher;
@@ -269,12 +272,13 @@ public abstract class AbstractStateMailetProcessorTest {
 
     }
 
-    private MailImpl newMail() throws AddressException {
+    private MailImpl newMail() throws MessagingException {
         return MailImpl.builder()
             .name(MailImpl.getId())
             .sender("test@localhost")
             .addRecipient("test@localhost")
             .addRecipient("test2@localhost")
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes("header: value\r\n".getBytes(UTF_8)))
             .build();
     }
 }
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/RecipientRewriteTableProcessorTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/RecipientRewriteTableProcessorTest.java
index 1fa6964..e3b02d1 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/RecipientRewriteTableProcessorTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/RecipientRewriteTableProcessorTest.java
@@ -19,6 +19,7 @@
 
 package org.apache.james.transport.mailets;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.mockito.ArgumentMatchers.any;
@@ -68,11 +69,14 @@ class RecipientRewriteTableProcessorTest {
         virtualTableStore = mock(org.apache.james.rrt.api.RecipientRewriteTable.class);
         mailetContext = FakeMailContext.defaultContext();
         processor = new RecipientRewriteTableProcessor(virtualTableStore, domainList, mailetContext);
-        mail = FakeMail.builder().name("mail").sender(MailAddressFixture.ANY_AT_JAMES).build();
+        mail = FakeMail.builder().name("mail")
+            .sender(MailAddressFixture.ANY_AT_JAMES)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes("h: v\r\n".getBytes(UTF_8)))
+            .build();
         mappings = MappingsImpl.builder()
                 .add(MailAddressFixture.ANY_AT_JAMES.toString())
                 .build();
-        message = MimeMessageUtil.defaultMimeMessage();
+        message = MimeMessageUtil.mimeMessageFromBytes("h: v\r\n".getBytes(UTF_8));
 
         nonDomainWithDefaultLocal = new MailAddress(NONEDOMAIN + "@" + MailAddressFixture.JAMES_LOCAL);
     }
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remote/delivery/RemoteDeliveryTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remote/delivery/RemoteDeliveryTest.java
index 947e052..1445026 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remote/delivery/RemoteDeliveryTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/remote/delivery/RemoteDeliveryTest.java
@@ -19,6 +19,7 @@
 
 package org.apache.james.transport.mailets.remote.delivery;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.apache.mailet.base.MailAddressFixture.JAMES_APACHE_ORG;
 import static org.apache.mailet.base.MailAddressFixture.JAMES_APACHE_ORG_DOMAIN;
 import static org.assertj.core.api.Assertions.assertThat;
@@ -41,6 +42,7 @@ import org.apache.james.queue.api.ManageableMailQueue;
 import org.apache.james.queue.api.RawMailQueueItemDecoratorFactory;
 import org.apache.james.queue.memory.MemoryMailQueueFactory;
 import org.apache.james.transport.mailets.RemoteDelivery;
+import org.apache.james.util.MimeMessageUtil;
 import org.apache.mailet.Attribute;
 import org.apache.mailet.AttributeName;
 import org.apache.mailet.Mail;
@@ -113,7 +115,11 @@ public class RemoteDeliveryTest {
         remoteDelivery.init(FakeMailetConfig.builder()
             .build());
 
-        Mail mail = FakeMail.builder().name(MAIL_NAME).recipients(MailAddressFixture.ANY_AT_JAMES).build();
+        Mail mail = FakeMail.builder()
+            .name(MAIL_NAME)
+            .recipients(MailAddressFixture.ANY_AT_JAMES)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes("h: v\r\n".getBytes(UTF_8)))
+            .build();
         remoteDelivery.service(mail);
 
 
@@ -135,6 +141,7 @@ public class RemoteDeliveryTest {
         Mail mail = FakeMail.builder()
             .name(MAIL_NAME)
             .recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.ANY_AT_JAMES2, MailAddressFixture.OTHER_AT_JAMES)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes("h: v\r\n".getBytes(UTF_8)))
             .build();
         remoteDelivery.service(mail);
 
@@ -162,6 +169,7 @@ public class RemoteDeliveryTest {
         Mail mail = FakeMail.builder()
             .name(MAIL_NAME)
             .recipients(MailAddressFixture.ANY_AT_JAMES, MailAddressFixture.ANY_AT_JAMES2, MailAddressFixture.OTHER_AT_JAMES)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes("h: v\r\n".getBytes(UTF_8)))
             .build();
         remoteDelivery.service(mail);
 
@@ -181,7 +189,11 @@ public class RemoteDeliveryTest {
         remoteDelivery.init(FakeMailetConfig.builder()
             .build());
 
-        Mail mail = FakeMail.builder().name(MAIL_NAME).recipients(MailAddressFixture.ANY_AT_JAMES).build();
+        Mail mail = FakeMail.builder()
+            .name(MAIL_NAME)
+            .recipients(MailAddressFixture.ANY_AT_JAMES)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes("h: v\r\n".getBytes(UTF_8)))
+            .build();
         remoteDelivery.service(mail);
 
         assertThat(mail.getState()).isEqualTo(Mail.GHOST);
@@ -193,7 +205,11 @@ public class RemoteDeliveryTest {
             .setProperty(RemoteDeliveryConfiguration.USE_PRIORITY, "true")
             .build());
 
-        Mail mail = FakeMail.builder().name(MAIL_NAME).recipients(MailAddressFixture.ANY_AT_JAMES).build();
+        Mail mail = FakeMail.builder()
+            .name(MAIL_NAME)
+            .recipients(MailAddressFixture.ANY_AT_JAMES)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes("h: v\r\n".getBytes(UTF_8)))
+            .build();
         remoteDelivery.service(mail);
 
 
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MessageSender.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MessageSender.java
index 2aced88..5ca5d32 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MessageSender.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MessageSender.java
@@ -33,9 +33,9 @@ import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.server.core.Envelope;
 import org.apache.james.server.core.MailImpl;
-import org.apache.james.server.core.MimeMessageCopyOnWriteProxy;
 import org.apache.james.server.core.MimeMessageInputStreamSource;
 import org.apache.james.server.core.MimeMessageSource;
+import org.apache.james.server.core.MimeMessageWrapper;
 import org.apache.mailet.Mail;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -72,14 +72,8 @@ public class MessageSender {
 
     private static MimeMessage toMimeMessage(String name, InputStream inputStream) throws MessagingException {
         MimeMessageSource source = new MimeMessageInputStreamSource(name, inputStream);
-        // if MimeMessageCopyOnWriteProxy throws an error in the constructor we
-        // have to manually care disposing our source.
-        try {
-            return new MimeMessageCopyOnWriteProxy(source);
-        } catch (MessagingException e) {
-            LifecycleUtil.dispose(source);
-            throw e;
-        }
+
+        return new MimeMessageWrapper(source);
     }
 
     public void sendMessage(MessageId messageId,
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala
index e63e283..ac097e5 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala
@@ -45,7 +45,7 @@ import org.apache.james.metrics.api.MetricFactory
 import org.apache.james.queue.api.MailQueueFactory.SPOOL
 import org.apache.james.queue.api.{MailQueue, MailQueueFactory}
 import org.apache.james.rrt.api.CanSendFrom
-import org.apache.james.server.core.{MailImpl, MimeMessageCopyOnWriteProxy, MimeMessageInputStreamSource}
+import org.apache.james.server.core.{MailImpl, MimeMessageInputStreamSource, MimeMessageWrapper}
 import org.apache.mailet.{Attribute, AttributeName, AttributeValue}
 import org.reactivestreams.Publisher
 import org.slf4j.{Logger, LoggerFactory}
@@ -232,7 +232,7 @@ class EmailSubmissionSetMethod @Inject()(serializer: EmailSubmissionSetSerialize
     val source = new MimeMessageInputStreamSource(name, inputStream)
     // if MimeMessageCopyOnWriteProxy throws an error in the constructor we
     // have to manually care disposing our source.
-    Try(new MimeMessageCopyOnWriteProxy(source))
+    Try(new MimeMessageWrapper(source))
       .recover(e => {
         LifecycleUtil.dispose(source)
         throw e
diff --git a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/DataLineJamesMessageHookHandler.java b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/DataLineJamesMessageHookHandler.java
index 4da0261..e9afa81 100644
--- a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/DataLineJamesMessageHookHandler.java
+++ b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/DataLineJamesMessageHookHandler.java
@@ -49,9 +49,9 @@ import org.apache.james.protocols.smtp.hook.HookResult;
 import org.apache.james.protocols.smtp.hook.HookResultHook;
 import org.apache.james.protocols.smtp.hook.MessageHook;
 import org.apache.james.server.core.MailImpl;
-import org.apache.james.server.core.MimeMessageCopyOnWriteProxy;
 import org.apache.james.server.core.MimeMessageInputStream;
 import org.apache.james.server.core.MimeMessageInputStreamSource;
+import org.apache.james.server.core.MimeMessageWrapper;
 import org.apache.mailet.Mail;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -100,10 +100,10 @@ public class DataLineJamesMessageHookHandler implements DataLineFilter, Extensib
                 // store mail in the session so we can be sure it get disposed later
                 session.setAttachment(SMTPConstants.MAIL, mail, State.Transaction);
 
-                MimeMessageCopyOnWriteProxy mimeMessageCopyOnWriteProxy = null;
+                MimeMessageWrapper mimeMessageWrapper = null;
                 try {
-                    mimeMessageCopyOnWriteProxy = new MimeMessageCopyOnWriteProxy(mmiss);
-                    mail.setMessage(mimeMessageCopyOnWriteProxy);
+                    mimeMessageWrapper = new MimeMessageWrapper(mmiss);
+                    mail.setMessage(mimeMessageWrapper);
 
                     Response response = processExtensions(session, mail);
 
@@ -115,7 +115,7 @@ public class DataLineJamesMessageHookHandler implements DataLineFilter, Extensib
                     LOGGER.info("Unexpected error handling DATA stream", e);
                     return new SMTPResponse(SMTPRetCode.LOCAL_ERROR, "Unexpected error handling DATA stream.");
                 } finally {
-                    LifecycleUtil.dispose(mimeMessageCopyOnWriteProxy);
+                    LifecycleUtil.dispose(mimeMessageWrapper);
                     LifecycleUtil.dispose(mmiss);
                     LifecycleUtil.dispose(mail);
                 }
diff --git a/server/protocols/webadmin/webadmin-mailqueue/src/test/java/org/apache/james/webadmin/routes/MailQueueRoutesTest.java b/server/protocols/webadmin/webadmin-mailqueue/src/test/java/org/apache/james/webadmin/routes/MailQueueRoutesTest.java
index 6212c7d..1fdde7d 100644
--- a/server/protocols/webadmin/webadmin-mailqueue/src/test/java/org/apache/james/webadmin/routes/MailQueueRoutesTest.java
+++ b/server/protocols/webadmin/webadmin-mailqueue/src/test/java/org/apache/james/webadmin/routes/MailQueueRoutesTest.java
@@ -24,6 +24,7 @@ import static io.restassured.RestAssured.when;
 import static io.restassured.RestAssured.with;
 import static io.restassured.config.EncoderConfig.encoderConfig;
 import static io.restassured.config.RestAssuredConfig.newConfig;
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.apache.mailet.base.MailAddressFixture.SENDER;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.hamcrest.Matchers.empty;
@@ -47,6 +48,7 @@ import org.apache.james.queue.memory.MemoryMailQueueFactory;
 import org.apache.james.task.Hostname;
 import org.apache.james.task.MemoryTaskManager;
 import org.apache.james.task.TaskManager;
+import org.apache.james.util.MimeMessageUtil;
 import org.apache.james.webadmin.WebAdminServer;
 import org.apache.james.webadmin.WebAdminUtils;
 import org.apache.james.webadmin.service.ClearMailQueueTask;
@@ -83,6 +85,7 @@ class MailQueueRoutesTest {
     static final String FAKE_MAIL_NAME_1 = "fake mail name 1";
     static final String FAKE_MAIL_NAME_2 = "fake mail name 2";
     static final String FAKE_MAIL_NAME_3 = "fake mail name 3";
+    static final byte[] MESSAGE_BYTES = "header: value \r\n".getBytes(UTF_8);
 
     WebAdminServer webAdminServer;
     MemoryMailQueueFactory mailQueueFactory;
@@ -106,7 +109,7 @@ class MailQueueRoutesTest {
             .setAccept(ContentType.JSON)
             .setBasePath(MailQueueRoutes.BASE_URL)
             .setPort(server.getPort().getValue())
-            .setConfig(newConfig().encoderConfig(encoderConfig().defaultContentCharset(StandardCharsets.UTF_8)))
+            .setConfig(newConfig().encoderConfig(encoderConfig().defaultContentCharset(UTF_8)))
             .build();
     }
 
@@ -671,11 +674,13 @@ class MailQueueRoutesTest {
                 queue.enQueue(FakeMail.builder()
                     .name(FAKE_MAIL_NAME_1)
                     .sender(SENDER_1_JAMES_ORG)
+                    .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
                     .build());
 
                 queue.enQueue(FakeMail.builder()
                     .name(FAKE_MAIL_NAME_2)
                     .sender(SENDER_2_JAMES_ORG)
+                    .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
                     .build());
 
                 String taskId = with()
@@ -707,10 +712,12 @@ class MailQueueRoutesTest {
 
                 queue.enQueue(FakeMail.builder()
                     .name(FAKE_MAIL_NAME_1)
+                    .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
                     .build());
 
                 queue.enQueue(FakeMail.builder()
                     .name(FAKE_MAIL_NAME_2)
+                    .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
                     .build());
 
                 String taskId = with()
@@ -743,16 +750,19 @@ class MailQueueRoutesTest {
                 queue.enQueue(FakeMail.builder()
                     .name(FAKE_MAIL_NAME_1)
                     .recipient(RECIPIENT_JAMES_ORG)
+                    .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
                     .build());
 
                 queue.enQueue(FakeMail.builder()
                     .name(FAKE_MAIL_NAME_2)
                     .recipient(RECIPIENT_JAMES_ORG)
+                    .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
                     .build());
 
                 queue.enQueue(FakeMail.builder()
                     .name(FAKE_MAIL_NAME_2)
                     .recipient(RECIPIENT_1_JAMES_ORG)
+                    .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
                     .build());
 
                 String taskId = with()
@@ -789,11 +799,13 @@ class MailQueueRoutesTest {
                 queue.enQueue(FakeMail.builder()
                     .name(FAKE_MAIL_NAME_1)
                     .sender(SENDER_1_JAMES_ORG)
+                    .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
                     .build());
 
                 queue.enQueue(FakeMail.builder()
                     .name(FAKE_MAIL_NAME_2)
                     .sender(SENDER_2_JAMES_ORG)
+                    .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
                     .build());
 
                 String taskId = with()
@@ -822,10 +834,12 @@ class MailQueueRoutesTest {
 
                 queue.enQueue(FakeMail.builder()
                     .name(FAKE_MAIL_NAME_1)
+                    .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
                     .build());
 
                 queue.enQueue(FakeMail.builder()
                     .name(FAKE_MAIL_NAME_2)
+                    .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
                     .build());
 
                 String taskId = with()
@@ -855,16 +869,19 @@ class MailQueueRoutesTest {
                 queue.enQueue(FakeMail.builder()
                     .name(FAKE_MAIL_NAME_1)
                     .recipient(RECIPIENT_JAMES_ORG)
+                    .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
                     .build());
 
                 queue.enQueue(FakeMail.builder()
                     .name(FAKE_MAIL_NAME_2)
                     .recipient(RECIPIENT_1_JAMES_ORG)
+                    .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
                     .build());
 
                 queue.enQueue(FakeMail.builder()
                     .name(FAKE_MAIL_NAME_3)
                     .recipient(RECIPIENT_2_JAMES_ORG)
+                    .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
                     .build());
 
                 String taskId = with()
@@ -959,14 +976,17 @@ class MailQueueRoutesTest {
 
             queue.enQueue(FakeMail.builder()
                 .name(FAKE_MAIL_NAME_1)
+                .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
                 .build());
 
             queue.enQueue(FakeMail.builder()
                 .name(FAKE_MAIL_NAME_2)
+                .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
                 .build());
 
             queue.enQueue(FakeMail.builder()
                 .name(FAKE_MAIL_NAME_3)
+                .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
                 .build());
 
             String taskId = with()
@@ -996,14 +1016,17 @@ class MailQueueRoutesTest {
 
             queue.enQueue(FakeMail.builder()
                 .name(FAKE_MAIL_NAME_1)
+                .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
                 .build());
 
             queue.enQueue(FakeMail.builder()
                 .name(FAKE_MAIL_NAME_2)
+                .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
                 .build());
 
             queue.enQueue(FakeMail.builder()
                 .name(FAKE_MAIL_NAME_3)
+                .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
                 .build());
 
             String taskId = with()
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java b/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java
index 2009f83..95e8860 100644
--- a/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java
@@ -22,6 +22,7 @@ package org.apache.james.webadmin.routes;
 import static io.restassured.RestAssured.given;
 import static io.restassured.RestAssured.when;
 import static io.restassured.RestAssured.with;
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
 import static net.javacrumbs.jsonunit.core.Option.IGNORING_ARRAY_ORDER;
 import static net.javacrumbs.jsonunit.core.Option.IGNORING_EXTRA_FIELDS;
@@ -68,6 +69,7 @@ import org.apache.james.queue.memory.MemoryMailQueueFactory;
 import org.apache.james.task.Hostname;
 import org.apache.james.task.MemoryTaskManager;
 import org.apache.james.util.ClassLoaderUtils;
+import org.apache.james.util.MimeMessageUtil;
 import org.apache.james.webadmin.Constants;
 import org.apache.james.webadmin.WebAdminServer;
 import org.apache.james.webadmin.WebAdminUtils;
@@ -107,6 +109,7 @@ class MailRepositoriesRoutesTest {
     private static final MailQueueName CUSTOM_QUEUE = MailQueueName.of("customQueue");
     private static final String NAME_1 = "name1";
     private static final String NAME_2 = "name2";
+    private static final byte[] MESSAGE_BYTES = "header: value \r\n".getBytes(UTF_8);
     private WebAdminServer webAdminServer;
     private MemoryMailRepositoryStore mailRepositoryStore;
     private ManageableMailQueue spoolQueue;
@@ -1104,9 +1107,11 @@ class MailRepositoriesRoutesTest {
         MailRepository mailRepository = mailRepositoryStore.create(URL_MY_REPO);
         mailRepository.store(FakeMail.builder()
             .name(NAME_1)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
         mailRepository.store(FakeMail.builder()
             .name(NAME_2)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
 
         String taskId = with()
@@ -1140,9 +1145,11 @@ class MailRepositoriesRoutesTest {
         String name2 = "name2";
         mailRepository.store(FakeMail.builder()
             .name(name1)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
         mailRepository.store(FakeMail.builder()
             .name(name2)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
 
         String transport = "transport";
@@ -1180,9 +1187,11 @@ class MailRepositoriesRoutesTest {
         String name2 = "name2";
         mailRepository.store(FakeMail.builder()
             .name(name1)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
         mailRepository.store(FakeMail.builder()
             .name(name2)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
 
         String transport = "transport";
@@ -1209,9 +1218,11 @@ class MailRepositoriesRoutesTest {
         String name2 = "name2";
         mailRepository.store(FakeMail.builder()
             .name(name1)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
         mailRepository.store(FakeMail.builder()
             .name(name2)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
 
         String transport = "transport";
@@ -1240,9 +1251,11 @@ class MailRepositoriesRoutesTest {
         String name2 = "name2";
         mailRepository1.store(FakeMail.builder()
             .name(name1)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
         mailRepository2.store(FakeMail.builder()
             .name(name2)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
 
         String transport = "transport";
@@ -1269,9 +1282,11 @@ class MailRepositoriesRoutesTest {
         MailRepository mailRepository = mailRepositoryStore.create(URL_MY_REPO);
         mailRepository.store(FakeMail.builder()
             .name(NAME_1)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
         mailRepository.store(FakeMail.builder()
             .name(NAME_2)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
 
         String taskId = with()
@@ -1297,9 +1312,11 @@ class MailRepositoriesRoutesTest {
         MailRepository mailRepository2 = mailRepositoryStore.create(URL_MY_REPO_OTHER);
         mailRepository1.store(FakeMail.builder()
             .name(NAME_1)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
         mailRepository2.store(FakeMail.builder()
             .name(NAME_2)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
 
         String taskId = with()
@@ -1327,10 +1344,12 @@ class MailRepositoriesRoutesTest {
         mailRepository.store(FakeMail.builder()
             .name(NAME_1)
             .state(state1)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
         mailRepository.store(FakeMail.builder()
             .name(NAME_2)
             .state(state2)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
 
         String taskId = with()
@@ -1358,10 +1377,12 @@ class MailRepositoriesRoutesTest {
         mailRepository.store(FakeMail.builder()
             .name(NAME_1)
             .state(state1)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
         mailRepository.store(FakeMail.builder()
             .name(NAME_2)
             .state(state2)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
 
         String transport = "transport";
@@ -1388,9 +1409,11 @@ class MailRepositoriesRoutesTest {
         MailRepository mailRepository = mailRepositoryStore.create(URL_MY_REPO);
         mailRepository.store(FakeMail.builder()
             .name(NAME_1)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
         mailRepository.store(FakeMail.builder()
             .name(NAME_2)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
 
         String taskId = with()
@@ -1471,9 +1494,11 @@ class MailRepositoriesRoutesTest {
         String name2 = "name2";
         mailRepository.store(FakeMail.builder()
             .name(name1)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
         mailRepository.store(FakeMail.builder()
             .name(name2)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
 
         String taskId = with()
@@ -1506,9 +1531,11 @@ class MailRepositoriesRoutesTest {
         String name2 = "name2";
         mailRepository.store(FakeMail.builder()
             .name(name1)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
         mailRepository.store(FakeMail.builder()
             .name(name2)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
 
         String transport = "transport";
@@ -1545,9 +1572,11 @@ class MailRepositoriesRoutesTest {
         String name2 = "name2";
         mailRepository.store(FakeMail.builder()
             .name(name1)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
         mailRepository.store(FakeMail.builder()
             .name(name2)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
 
         String transport = "transport";
@@ -1574,9 +1603,11 @@ class MailRepositoriesRoutesTest {
         String name2 = "name2";
         mailRepository.store(FakeMail.builder()
             .name(name1)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
         mailRepository.store(FakeMail.builder()
             .name(name2)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
 
         String transport = "transport";
@@ -1602,9 +1633,11 @@ class MailRepositoriesRoutesTest {
         MailRepository mailRepository = mailRepositoryStore.create(URL_MY_REPO);
         mailRepository.store(FakeMail.builder()
             .name(NAME_1)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
         mailRepository.store(FakeMail.builder()
             .name(NAME_2)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
 
         String taskId = with()
@@ -1632,10 +1665,12 @@ class MailRepositoriesRoutesTest {
         mailRepository.store(FakeMail.builder()
             .name(NAME_1)
             .state(state1)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
         mailRepository.store(FakeMail.builder()
             .name(NAME_2)
             .state(state2)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
 
         String taskId = with()
@@ -1663,10 +1698,12 @@ class MailRepositoriesRoutesTest {
         mailRepository.store(FakeMail.builder()
             .name(NAME_1)
             .state(state1)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
         mailRepository.store(FakeMail.builder()
             .name(NAME_2)
             .state(state2)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
 
         String transport = "transport";
@@ -1693,9 +1730,11 @@ class MailRepositoriesRoutesTest {
         MailRepository mailRepository = mailRepositoryStore.create(URL_MY_REPO);
         mailRepository.store(FakeMail.builder()
             .name(NAME_1)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
         mailRepository.store(FakeMail.builder()
             .name(NAME_2)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build());
 
         String taskId = with()
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/service/ReprocessingServiceTest.java b/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/service/ReprocessingServiceTest.java
index 105be08..d2ad1c5 100644
--- a/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/service/ReprocessingServiceTest.java
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/service/ReprocessingServiceTest.java
@@ -19,6 +19,7 @@
 
 package org.apache.james.webadmin.service;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import java.util.Optional;
@@ -41,6 +42,7 @@ import org.apache.james.queue.api.MailQueueName;
 import org.apache.james.queue.api.ManageableMailQueue;
 import org.apache.james.queue.api.RawMailQueueItemDecoratorFactory;
 import org.apache.james.queue.memory.MemoryMailQueueFactory;
+import org.apache.james.util.MimeMessageUtil;
 import org.apache.mailet.base.test.FakeMail;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -61,6 +63,7 @@ class ReprocessingServiceTest {
     private static final MailQueueName SPOOL = MailQueueName.of("spool");
     private static final Consumer<MailKey> NOOP_CONSUMER = key -> { };
     private static final Optional<String> NO_TARGET_PROCESSOR = Optional.empty();
+    private static final byte[] MESSAGE_BYTES = "header: value \r\n".getBytes(UTF_8);
 
     private ReprocessingService reprocessingService;
     private MemoryMailRepositoryStore mailRepositoryStore;
@@ -82,14 +85,17 @@ class ReprocessingServiceTest {
 
         mail1 = FakeMail.builder()
             .name(NAME_1)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build();
 
         mail2 = FakeMail.builder()
             .name(NAME_2)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build();
 
         mail3 = FakeMail.builder()
             .name(NAME_3)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes(MESSAGE_BYTES))
             .build();
     }
 
diff --git a/server/queue/queue-activemq/src/main/java/org/apache/james/queue/activemq/ActiveMQCacheableMailQueue.java b/server/queue/queue-activemq/src/main/java/org/apache/james/queue/activemq/ActiveMQCacheableMailQueue.java
index 2a5e58a..01596cb 100644
--- a/server/queue/queue-activemq/src/main/java/org/apache/james/queue/activemq/ActiveMQCacheableMailQueue.java
+++ b/server/queue/queue-activemq/src/main/java/org/apache/james/queue/activemq/ActiveMQCacheableMailQueue.java
@@ -47,9 +47,9 @@ import org.apache.james.queue.api.MailQueueItemDecoratorFactory;
 import org.apache.james.queue.api.MailQueueName;
 import org.apache.james.queue.jms.JMSCacheableMailQueue;
 import org.apache.james.server.core.MailImpl;
-import org.apache.james.server.core.MimeMessageCopyOnWriteProxy;
 import org.apache.james.server.core.MimeMessageInputStream;
 import org.apache.james.server.core.MimeMessageSource;
+import org.apache.james.server.core.MimeMessageWrapper;
 import org.apache.mailet.Attribute;
 import org.apache.mailet.AttributeValue;
 import org.apache.mailet.Mail;
@@ -140,7 +140,7 @@ public class ActiveMQCacheableMailQueue extends JMSCacheableMailQueue implements
             try {
                 BlobMessage blobMessage = (BlobMessage) message;
                 MimeMessageSource source = new MimeMessageBlobMessageSource(blobMessage);
-                return new MimeMessageCopyOnWriteProxy(source);
+                return new MimeMessageWrapper(source);
             
             } catch (JMSException e) {
                 throw new MailQueueException("Unable to populate MimeMessage for mail", e);
diff --git a/server/queue/queue-file/src/main/java/org/apache/james/queue/file/FileCacheableMailQueue.java b/server/queue/queue-file/src/main/java/org/apache/james/queue/file/FileCacheableMailQueue.java
index cb69709..d8b0676 100644
--- a/server/queue/queue-file/src/main/java/org/apache/james/queue/file/FileCacheableMailQueue.java
+++ b/server/queue/queue-file/src/main/java/org/apache/james/queue/file/FileCacheableMailQueue.java
@@ -53,8 +53,8 @@ import org.apache.james.lifecycle.api.LifecycleUtil;
 import org.apache.james.queue.api.MailQueueItemDecoratorFactory;
 import org.apache.james.queue.api.MailQueueName;
 import org.apache.james.queue.api.ManageableMailQueue;
-import org.apache.james.server.core.MimeMessageCopyOnWriteProxy;
 import org.apache.james.server.core.MimeMessageSource;
+import org.apache.james.server.core.MimeMessageWrapper;
 import org.apache.james.util.concurrent.NamedThreadFactory;
 import org.apache.mailet.Attribute;
 import org.apache.mailet.AttributeName;
@@ -278,7 +278,7 @@ public class FileCacheableMailQueue implements ManageableMailQueue {
                 final File msgFile = new File(fitem.getMessageFile());
                 try (ObjectInputStream oin = new ObjectInputStream(new FileInputStream(objectFile))) {
                     final Mail mail = (Mail) oin.readObject();
-                    mail.setMessage(new MimeMessageCopyOnWriteProxy(new FileMimeMessageSource(msgFile)));
+                    mail.setMessage(new MimeMessageWrapper(new FileMimeMessageSource(msgFile)));
                     MailQueueItem fileMailQueueItem = new MailQueueItem() {
 
                         @Override
diff --git a/server/queue/queue-jms/src/main/java/org/apache/james/queue/jms/JMSCacheableMailQueue.java b/server/queue/queue-jms/src/main/java/org/apache/james/queue/jms/JMSCacheableMailQueue.java
index a680e2c..56b374d 100644
--- a/server/queue/queue-jms/src/main/java/org/apache/james/queue/jms/JMSCacheableMailQueue.java
+++ b/server/queue/queue-jms/src/main/java/org/apache/james/queue/jms/JMSCacheableMailQueue.java
@@ -67,7 +67,7 @@ import org.apache.james.queue.api.MailQueueItemDecoratorFactory;
 import org.apache.james.queue.api.MailQueueName;
 import org.apache.james.queue.api.ManageableMailQueue;
 import org.apache.james.server.core.MailImpl;
-import org.apache.james.server.core.MimeMessageCopyOnWriteProxy;
+import org.apache.james.server.core.MimeMessageWrapper;
 import org.apache.mailet.Attribute;
 import org.apache.mailet.AttributeName;
 import org.apache.mailet.AttributeUtils;
@@ -395,7 +395,7 @@ public class JMSCacheableMailQueue implements ManageableMailQueue, JMSSupport, M
      */
     protected MimeMessage mimeMessage(Message message) throws MessagingException, JMSException {
         if (message instanceof ObjectMessage) {
-            return new MimeMessageCopyOnWriteProxy(new MimeMessageObjectMessageSource((ObjectMessage) message));
+            return new MimeMessageWrapper(new MimeMessageObjectMessageSource((ObjectMessage) message));
         } else {
             throw new MailQueueException("Not supported JMS Message received " + message);
         }


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


[james-project] 07/14: JAMES-3477 Clearly state that MimeMessageInputStreamSource is not thread safe

Posted by bt...@apache.org.
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 041d49abf31e5dcef07a1b9c5edeb6b3f4da070b
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Dec 28 17:39:12 2020 +0700

    JAMES-3477 Clearly state that MimeMessageInputStreamSource is not thread safe
---
 .../org/apache/james/server/core/MimeMessageInputStreamSource.java    | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageInputStreamSource.java b/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageInputStreamSource.java
index e6cfe38..db95342 100644
--- a/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageInputStreamSource.java
+++ b/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageInputStreamSource.java
@@ -39,6 +39,8 @@ import org.apache.james.lifecycle.api.Disposable;
  * Takes an input stream and creates a repeatable input stream source for a
  * MimeMessageWrapper. It does this by completely reading the input stream and
  * saving that to data to an {@link DeferredFileOutputStream} with its threshold set to 100kb
+ *
+ * This class is not thread safe!
  */
 public class MimeMessageInputStreamSource extends MimeMessageSource implements Disposable {
 
@@ -128,7 +130,7 @@ public class MimeMessageInputStreamSource extends MimeMessageSource implements D
      * @return a <code>BufferedInputStream</code> containing the data
      */
     @Override
-    public synchronized InputStream getInputStream() throws IOException {
+    public InputStream getInputStream() throws IOException {
         InputStream in;
         if (out.isInMemory()) {
             in = new SharedByteArrayInputStream(out.getData());


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


[james-project] 08/14: JAMES-3477 MimeMessageInputStreamSource do not need ordering for opened streams

Posted by bt...@apache.org.
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 e56d236ddb02818e2831398f1dc3b9c0227e8fca
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Dec 28 17:40:39 2020 +0700

    JAMES-3477 MimeMessageInputStreamSource do not need ordering for opened streams
---
 .../org/apache/james/server/core/MimeMessageInputStreamSource.java  | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageInputStreamSource.java b/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageInputStreamSource.java
index db95342..091c09d 100644
--- a/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageInputStreamSource.java
+++ b/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageInputStreamSource.java
@@ -23,8 +23,8 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
 
 import javax.mail.MessagingException;
 import javax.mail.util.SharedByteArrayInputStream;
@@ -44,7 +44,7 @@ import org.apache.james.lifecycle.api.Disposable;
  */
 public class MimeMessageInputStreamSource extends MimeMessageSource implements Disposable {
 
-    private final List<InputStream> streams = new ArrayList<>();
+    private final Set<InputStream> streams = new HashSet<>();
 
     /**
      * A temporary file used to hold the message stream


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


[james-project] 14/14: JAMES-2514 Upgrade Cassandra 3.11.3 -> 3.11.10

Posted by bt...@apache.org.
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 864e1c371b38f9a49bb8869416dfe32d3dbdf0b4
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Feb 15 09:28:20 2021 +0700

    JAMES-2514 Upgrade Cassandra 3.11.3 -> 3.11.10
---
 JenkinsfileStressTests.groovy                                       | 6 +++---
 README.adoc                                                         | 4 ++--
 .../java/org/apache/james/backends/cassandra/DockerCassandra.java   | 4 ++--
 dockerfiles/run/docker-compose-branch-master.yml                    | 2 +-
 dockerfiles/run/docker-compose.yml                                  | 2 +-
 docs/modules/servers/pages/distributed/operate/guide.adoc           | 6 +++---
 docs/modules/servers/pages/distributed/operate/logging.adoc         | 2 +-
 docs/modules/servers/pages/distributed/run-docker.adoc              | 2 +-
 docs/modules/servers/pages/distributed/run.adoc                     | 6 +++---
 src/site/markdown/server/install/guice-cassandra-rabbitmq-swift.md  | 6 +++---
 src/site/markdown/server/install/guice-cassandra.md                 | 6 +++---
 src/site/markdown/server/manage-guice-distributed-james.md          | 6 +++---
 src/site/xdoc/server/quick-start-cassandra.xml                      | 2 +-
 13 files changed, 27 insertions(+), 27 deletions(-)

diff --git a/JenkinsfileStressTests.groovy b/JenkinsfileStressTests.groovy
index b8aafdb..e1a2322 100644
--- a/JenkinsfileStressTests.groovy
+++ b/JenkinsfileStressTests.groovy
@@ -63,7 +63,7 @@ pipeline {
                             switch (params.PROFILE) {
                                 case "reference":
                                     sh "cd /srv && sudo btrfs subvolume snapshot bench-snapshot-s3 bench-running-docker"
-                                    sh 'docker run -d --name=cassandra -p 9042:9042 -v /srv/bench-running-docker/cassandra:/var/lib/cassandra cassandra:3.11.3'
+                                    sh 'docker run -d --name=cassandra -p 9042:9042 -v /srv/bench-running-docker/cassandra:/var/lib/cassandra cassandra:3.11.10'
 
                                     sh 'sleep 10'
 
@@ -86,7 +86,7 @@ pipeline {
                                     sh 'docker run -d --hostname HOSTNAME -p 25:25 -p 1080:80 -p 8000:8000 -p 110:110 -p 143:143 -p 465:465 -p 587:587 -p 993:993 --link cassandra:cassandra --link rabbitmq:rabbitmq --link elasticsearch:elasticsearch --link tika:tika --link s3:s3.docker.test --name james_run -t james_run'
                                     break
                                 case "s3-local":
-                                    sh 'docker run -d --name=cassandra -p 9042:9042 cassandra:3.11.3'
+                                    sh 'docker run -d --name=cassandra -p 9042:9042 cassandra:3.11.10'
                                     sh 'docker run -d --name=elasticsearch -p 9200:9200 --env "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:6.3.2'
                                     sh 'docker run -d --name=tika apache/tika:1.24'
                                     sh 'docker run -d --env "REMOTE_MANAGEMENT_DISABLE=1" --env "SCALITY_ACCESS_KEY_ID=accessKey1" --env "SCALITY_SECRET_ACCESS_KEY=secretKey1" --name=s3 zenko/cloudserver:8.2.6'
@@ -95,7 +95,7 @@ pipeline {
                                     sh 'docker run -d --hostname HOSTNAME -p 25:25 -p 1080:80 -p 8000:8000 -p 110:110 -p 143:143 -p 465:465 -p 587:587 -p 993:993 --link cassandra:cassandra --link rabbitmq:rabbitmq --link elasticsearch:elasticsearch --link s3:s3.docker.test --link tika:tika --name james_run -t james_run'
                                     break
                                 case "s3":
-                                    sh 'docker run -d --name=cassandra -p 9042:9042 cassandra:3.11.3'
+                                    sh 'docker run -d --name=cassandra -p 9042:9042 cassandra:3.11.10'
                                     sh 'docker run -d --name=elasticsearch -p 9200:9200 --env "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:6.3.2'
                                     sh 'docker run -d --name=tika apache/tika:1.24'
                                     sh 'docker run -d --name=rabbitmq -p 15672:15672 -p 5672:5672 rabbitmq:3.8.1-management'
diff --git a/README.adoc b/README.adoc
index a7c92ae..2e5dfb5 100644
--- a/README.adoc
+++ b/README.adoc
@@ -153,7 +153,7 @@ If you haven't already:
 ==== How to ?
 You need a running *cassandra* in docker. To achieve this run:
 
-    $ docker run -d --name=cassandra cassandra:3.11.3
+    $ docker run -d --name=cassandra cassandra:3.11.10
 
 You need a running *rabbitmq* in docker. To achieve this run:
 
@@ -233,7 +233,7 @@ If you haven't already:
 ==== How to ?
 You need a running *cassandra* in docker. To achieve this run:
 
-    $ docker run -d --name=cassandra cassandra:3.11.3
+    $ docker run -d --name=cassandra cassandra:3.11.10
 
 You need a running *ElasticSearch* in docker. To achieve this run:
 
diff --git a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/DockerCassandra.java b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/DockerCassandra.java
index b5be0eb..6865431 100644
--- a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/DockerCassandra.java
+++ b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/DockerCassandra.java
@@ -139,7 +139,7 @@ public class DockerCassandra {
 
     @SuppressWarnings("resource")
     public DockerCassandra() {
-        this("cassandra_3_11_3-" + buildSpecificImageDiscriminator(), AdditionalDockerFileStep.IDENTITY);
+        this("cassandra_3_11_10-" + buildSpecificImageDiscriminator(), AdditionalDockerFileStep.IDENTITY);
     }
 
     private DockerCassandra(String imageName, AdditionalDockerFileStep additionalSteps) {
@@ -174,7 +174,7 @@ public class DockerCassandra {
             new ImageFromDockerfile(imageName,doNotDeleteImageAfterUsage)
                 .withDockerfileFromBuilder(builder ->
                     additionalSteps.applyStep(builder
-                        .from("cassandra:3.11.3")
+                        .from("cassandra:3.11.10")
                         .env("ENV CASSANDRA_CONFIG", "/etc/cassandra")
                         .run("echo \"-Xms" + CASSANDRA_MEMORY + "M\" >> " + JVM_OPTIONS)
                         .run("echo \"-Xmx" + CASSANDRA_MEMORY + "M\" >> " + JVM_OPTIONS)
diff --git a/dockerfiles/run/docker-compose-branch-master.yml b/dockerfiles/run/docker-compose-branch-master.yml
index 5105e99..bbd4349 100644
--- a/dockerfiles/run/docker-compose-branch-master.yml
+++ b/dockerfiles/run/docker-compose-branch-master.yml
@@ -29,7 +29,7 @@ services:
       - discovery.type=single-node
 
   cassandra:
-    image: cassandra:3.11.3
+    image: cassandra:3.11.10
     ports:
       - "9042:9042"
 
diff --git a/dockerfiles/run/docker-compose.yml b/dockerfiles/run/docker-compose.yml
index 321dca3..f60db6f 100644
--- a/dockerfiles/run/docker-compose.yml
+++ b/dockerfiles/run/docker-compose.yml
@@ -29,7 +29,7 @@ services:
       - discovery.type=single-node
 
   cassandra:
-    image: cassandra:3.11.3
+    image: cassandra:3.11.10
     ports:
       - "9042:9042"
 
diff --git a/docs/modules/servers/pages/distributed/operate/guide.adoc b/docs/modules/servers/pages/distributed/operate/guide.adoc
index 1152755..13f0195 100644
--- a/docs/modules/servers/pages/distributed/operate/guide.adoc
+++ b/docs/modules/servers/pages/distributed/operate/guide.adoc
@@ -357,7 +357,7 @@ echo -e "\nauthorizer: org.apache.cassandra.auth.CassandraAuthorizer" >> /etc/ca
 ==== Create a role
 
 Have a look at
-http://cassandra.apache.org/doc/3.11.3/cql/security.html[cassandra documentation] section `CREATE ROLE` for more information
+http://cassandra.apache.org/doc/3.11.10/cql/security.html[cassandra documentation] section `CREATE ROLE` for more information
 
 E.g.
 
@@ -368,7 +368,7 @@ CREATE ROLE james_one WITH PASSWORD = 'james_one' AND LOGIN = true;
 ==== Create a keyspace
 
 Have a look at
-http://cassandra.apache.org/doc/3.11.3/cql/ddl.html[cassandra documentation] section `CREATE KEYSPACE` for more information
+http://cassandra.apache.org/doc/3.11.10/cql/ddl.html[cassandra documentation] section `CREATE KEYSPACE` for more information
 
 ==== Grant permissions on created keyspace to the role
 
@@ -416,7 +416,7 @@ that topic. Consequently, you have to accept that your data models are
 still being exposed to anyone having credentials to Cassandra.
 
 For more information, have a look at
-http://cassandra.apache.org/doc/3.11.3/cql/security.html[cassandra documentation] section `REVOKE PERMISSION`.
+http://cassandra.apache.org/doc/3.11.10/cql/security.html[cassandra documentation] section `REVOKE PERMISSION`.
 
 Except for the case above, the permissions are not auto available for a
 specific role unless they are granted by `GRANT` command. Therefore, if
diff --git a/docs/modules/servers/pages/distributed/operate/logging.adoc b/docs/modules/servers/pages/distributed/operate/logging.adoc
index 39d0d7c..7825f38 100644
--- a/docs/modules/servers/pages/distributed/operate/logging.adoc
+++ b/docs/modules/servers/pages/distributed/operate/logging.adoc
@@ -200,7 +200,7 @@ services:
       - discovery.type=single-node
 
   cassandra:
-    image: cassandra:3.11.3
+    image: cassandra:3.11.10
     ports:
       - "9042:9042"
 
diff --git a/docs/modules/servers/pages/distributed/run-docker.adoc b/docs/modules/servers/pages/distributed/run-docker.adoc
index 6c1a275..185de97 100644
--- a/docs/modules/servers/pages/distributed/run-docker.adoc
+++ b/docs/modules/servers/pages/distributed/run-docker.adoc
@@ -45,7 +45,7 @@ If you haven't already:
 
 You need a running *cassandra* in docker. To achieve this run:
 
-    $ docker run -d --name=cassandra cassandra:3.11.3
+    $ docker run -d --name=cassandra cassandra:3.11.10
 
 You need a running *rabbitmq* in docker. To achieve this run:
 
diff --git a/docs/modules/servers/pages/distributed/run.adoc b/docs/modules/servers/pages/distributed/run.adoc
index 585dd3d..8a2303e 100644
--- a/docs/modules/servers/pages/distributed/run.adoc
+++ b/docs/modules/servers/pages/distributed/run.adoc
@@ -7,7 +7,7 @@
 
 * Java 11 SDK
 * Docker ∕ ElasticSearch 6.3.2, RabbitMQ Management 3.3.7, S3 compatible
-ObjectStorage and Cassandra 3.11.3
+ObjectStorage and Cassandra 3.11.10
 * Maven 3
 
 === Building the artifacts
@@ -29,7 +29,7 @@ mvn clean install
 
 === Requirements
 
-* Cassandra 3.11.3
+* Cassandra 3.11.10
 * ElasticSearch 6.3.2
 * RabbitMQ-Management 3.8.1
 * Swift ObjectStorage 2.15.1 or Zenko Cloudserver or AWS S3
@@ -58,7 +58,7 @@ running. You can either install the servers or launch them via docker:
 
 [source,bash]
 ----
-$ docker run -d -p 9042:9042 --name=cassandra cassandra:3.11.3
+$ docker run -d -p 9042:9042 --name=cassandra cassandra:3.11.10
 $ docker run -d -p 9200:9200 --name=elasticsearch --env 'discovery.type=single-node' docker.elastic.co/elasticsearch/elasticsearch:6.3.2
 $ docker run -d -p 5672:5672 -p 15672:15672 --name=rabbitmq rabbitmq:3.8.1-management
 $ docker run -d --env 'REMOTE_MANAGEMENT_DISABLE=1' --env 'SCALITY_ACCESS_KEY_ID=accessKey1' --env 'SCALITY_SECRET_ACCESS_KEY=secretKey1' --name=s3 zenko/cloudserver:8.2.6
diff --git a/src/site/markdown/server/install/guice-cassandra-rabbitmq-swift.md b/src/site/markdown/server/install/guice-cassandra-rabbitmq-swift.md
index 99754dd..1a204dc 100644
--- a/src/site/markdown/server/install/guice-cassandra-rabbitmq-swift.md
+++ b/src/site/markdown/server/install/guice-cassandra-rabbitmq-swift.md
@@ -5,7 +5,7 @@
 ### Requirements
 
  - Java 11 SDK
- - Docker ∕ ElasticSearch 6.3.2, RabbitMQ Management 3.8.1, compatible S3 ObjectStorage and Cassandra 3.11.3
+ - Docker ∕ ElasticSearch 6.3.2, RabbitMQ Management 3.8.1, compatible S3 ObjectStorage and Cassandra 3.11.10
  - Maven 3
 
 ### Building the artifacts
@@ -25,7 +25,7 @@ mvn clean install
 
 ### Requirements
 
- * Cassandra 3.11.3
+ * Cassandra 3.11.10
  * ElasticSearch 6.3.2
  * RabbitMQ-Management 3.8.1
  * Swift ObjectStorage 2.15.1 or Zenko Cloudserver or AWS S3
@@ -47,7 +47,7 @@ $ keytool -genkey -alias james -keyalg RSA -keystore conf/keystore
 You need to have a Cassandra, ElasticSearch, S3 and RabbitMQ instance running. You can either install the servers or launch them via docker:
 
 ```bash
-$ docker run -d -p 9042:9042 --name=cassandra cassandra:3.11.3
+$ docker run -d -p 9042:9042 --name=cassandra cassandra:3.11.10
 $ docker run -d -p 9200:9200 --name=elasticsearch --env 'discovery.type=single-node' docker.elastic.co/elasticsearch/elasticsearch:6.3.2
 $ docker run -d -p 5672:5672 -p 15672:15672 --name=rabbitmq rabbitmq:3.8.1-management
 $ docker run -d --env 'REMOTE_MANAGEMENT_DISABLE=1' --env 'SCALITY_ACCESS_KEY_ID=accessKey1' --env 'SCALITY_SECRET_ACCESS_KEY=secretKey1' --name=s3 zenko/cloudserver:8.2.6
diff --git a/src/site/markdown/server/install/guice-cassandra.md b/src/site/markdown/server/install/guice-cassandra.md
index 7cf077c..ad1d55a 100644
--- a/src/site/markdown/server/install/guice-cassandra.md
+++ b/src/site/markdown/server/install/guice-cassandra.md
@@ -5,7 +5,7 @@
 ### Requirements
 
  - Java 11 SDK
- - Docker ∕ ElasticSearch 6.3.2 and Cassandra 3.11.3
+ - Docker ∕ ElasticSearch 6.3.2 and Cassandra 3.11.10
  - Maven 3
 
 ### Building the artifacts
@@ -25,7 +25,7 @@ mvn clean install
 
 ### Requirements
 
- * Cassandra 3.11.3
+ * Cassandra 3.11.10
  * ElasticSearch 6.3.2
 
 ### James Launch
@@ -45,7 +45,7 @@ $ keytool -genkey -alias james -keyalg RSA -keystore conf/keystore
 You need to have a Cassandra and an ElasticSearch instance running. You can either install the servers or launch them via docker:
 
 ```bash
-$ docker run -d -p 9042:9042 --name=cassandra cassandra:3.11.3
+$ docker run -d -p 9042:9042 --name=cassandra cassandra:3.11.10
 $ docker run -d -p 9200:9200 --name=elasticsearch --env 'discovery.type=single-node' docker.elastic.co/elasticsearch/elasticsearch:6.3.2
 ```
 
diff --git a/src/site/markdown/server/manage-guice-distributed-james.md b/src/site/markdown/server/manage-guice-distributed-james.md
index b6013df..fe7dff8 100644
--- a/src/site/markdown/server/manage-guice-distributed-james.md
+++ b/src/site/markdown/server/manage-guice-distributed-james.md
@@ -410,7 +410,7 @@ echo -e "\nauthorizer: org.apache.cassandra.auth.CassandraAuthorizer" >> /etc/ca
 
 #### Create a role
 
-Have a look at [cassandra documentation](http://cassandra.apache.org/doc/3.11.3/cql/security.html) section `CREATE ROLE` for more information
+Have a look at [cassandra documentation](http://cassandra.apache.org/doc/3.11.10/cql/security.html) section `CREATE ROLE` for more information
 
 E.g.
 ```
@@ -418,7 +418,7 @@ CREATE ROLE james_one WITH PASSWORD = 'james_one' AND LOGIN = true;
 ```
 #### Create a keyspace
 
-Have a look at [cassandra documentation](http://cassandra.apache.org/doc/3.11.3/cql/ddl.html) section `CREATE KEYSPACE` for more information
+Have a look at [cassandra documentation](http://cassandra.apache.org/doc/3.11.10/cql/ddl.html) section `CREATE KEYSPACE` for more information
 
 #### Grant permissions on created keyspace to the role
 
@@ -460,7 +460,7 @@ describe any keyspace and table. There's no configuration that can make
 effect on that topic. Consequently, you have to accept that your data models 
 are still being exposed to anyone having credentials to Cassandra. 
 
-For more information, have a look at [cassandra documentation](http://cassandra.apache.org/doc/3.11.3/cql/security.html) section `REVOKE PERMISSION`. 
+For more information, have a look at [cassandra documentation](http://cassandra.apache.org/doc/3.11.10/cql/security.html) section `REVOKE PERMISSION`. 
 
 Except for the case above, the permissions are not auto available for 
 a specific role unless they are granted by `GRANT` command. Therefore, 
diff --git a/src/site/xdoc/server/quick-start-cassandra.xml b/src/site/xdoc/server/quick-start-cassandra.xml
index b84a314..e4b6ad9 100644
--- a/src/site/xdoc/server/quick-start-cassandra.xml
+++ b/src/site/xdoc/server/quick-start-cassandra.xml
@@ -64,7 +64,7 @@ Step 3: Deploy
 3.1. Deploy Cassandra (optional)
 You may skip this part if you already have a running Cassandra on your network.
 
-<i>$ docker run --detach=true --name=cassandra cassandra:3.11.3</i>
+<i>$ docker run --detach=true --name=cassandra cassandra:3.11.10</i>
 
 3.2. Deploy ElasticSearch (optional)
 You may skip this part if you already have a running ElasticSearch on your network.


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


[james-project] 02/14: JAMES-3431 DSNBounce should position Arrival-Date header

Posted by bt...@apache.org.
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 75f4b0b79d8a45e11d75853bed191e50d22c5d2a
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Feb 17 11:53:33 2021 +0700

    JAMES-3431 DSNBounce should position Arrival-Date header
---
 .../apache/james/transport/mailets/DSNBounce.java  | 11 +++++
 .../james/transport/mailets/DSNBounceTest.java     | 47 ++++++++++++++++++++++
 .../james/smtpserver/dsn/DSNMessageHook.java       |  5 +++
 3 files changed, 63 insertions(+)

diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/DSNBounce.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/DSNBounce.java
index 271b0c8..b37e206 100755
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/DSNBounce.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/DSNBounce.java
@@ -64,7 +64,10 @@ import org.apache.james.transport.util.ReplyToUtils;
 import org.apache.james.transport.util.SenderUtils;
 import org.apache.james.transport.util.SpecialAddressesUtils;
 import org.apache.james.transport.util.TosUtils;
+import org.apache.mailet.Attribute;
+import org.apache.mailet.AttributeName;
 import org.apache.mailet.AttributeUtils;
+import org.apache.mailet.AttributeValue;
 import org.apache.mailet.DsnParameters;
 import org.apache.mailet.Mail;
 import org.apache.mailet.base.DateFormats;
@@ -470,6 +473,14 @@ public class DSNBounce extends GenericMailet implements RedirectNotify {
             .ifPresent(envId -> buffer.append("Original-Envelope-Id: ")
                 .append(envId.asString())
                 .append(LINE_BREAK));
+        originalMail.getAttribute(AttributeName.of("dsn-arrival-date"))
+            .map(Attribute::getValue)
+            .map(AttributeValue::value)
+            .filter(ZonedDateTime.class::isInstance)
+            .map(ZonedDateTime.class::cast)
+            .ifPresent(arrivalDate -> buffer.append("Arrival-Date: ")
+                .append(arrivalDate.format(dateFormatter))
+                .append(LINE_BREAK));
 
         for (MailAddress rec : originalMail.getRecipients()) {
             appendRecipient(buffer, rec, getDeliveryError(originalMail), originalMail.getLastUpdated());
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/DSNBounceTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/DSNBounceTest.java
index c06f0ba..a2936bf 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/DSNBounceTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/DSNBounceTest.java
@@ -29,6 +29,7 @@ import java.net.InetAddress;
 import java.nio.charset.StandardCharsets;
 import java.time.Instant;
 import java.time.ZoneId;
+import java.time.ZonedDateTime;
 import java.util.Date;
 import java.util.List;
 
@@ -45,6 +46,8 @@ import org.apache.james.dnsservice.api.DNSService;
 import org.apache.james.transport.mailets.redirect.SpecialAddress;
 import org.apache.james.util.MimeMessageUtil;
 import org.apache.mailet.Attribute;
+import org.apache.mailet.AttributeName;
+import org.apache.mailet.AttributeValue;
 import org.apache.mailet.DsnParameters;
 import org.apache.mailet.Mail;
 import org.apache.mailet.base.DateFormats;
@@ -1277,6 +1280,50 @@ public class DSNBounceTest {
     }
 
     @Test
+    void arrivalDateShouldBePositioned() throws Exception {
+        FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
+            .mailetName(MAILET_NAME)
+            .mailetContext(fakeMailContext)
+            .build();
+        dsnBounce.init(mailetConfig);
+
+        MailAddress senderMailAddress = new MailAddress("sender@domain.com");
+        FakeMail mail = FakeMail.builder()
+            .name(MAILET_NAME)
+            .sender(senderMailAddress)
+            .attribute(DELIVERY_ERROR_ATTRIBUTE)
+            .attribute(new Attribute(AttributeName.of("dsn-arrival-date"), AttributeValue.of(ZonedDateTime.parse("2015-10-30T16:12:00Z"))))
+            .mimeMessage(MimeMessageBuilder.mimeMessageBuilder()
+                .setText("My content"))
+            .recipient("recipient@domain.com")
+            .lastUpdated(Date.from(Instant.parse("2016-09-08T14:25:52.000Z")))
+            .remoteAddr("remoteHost")
+            .build();
+        mail.setDsnParameters(DsnParameters.builder().envId(DsnParameters.EnvId.of("xyz")).build().get());
+
+        dsnBounce.service(mail);
+
+        String expectedContent = "Reporting-MTA: dns; myhost\n" +
+            "Received-From-MTA: dns; 111.222.333.444\n" +
+            "Original-Envelope-Id: xyz\n" +
+            "Arrival-Date: Fri, 30 Oct 2015 16:12:00 XXXXX (UTC)\n" +
+            "\n" +
+            "Final-Recipient: rfc822; recipient@domain.com\n" +
+            "Action: failed\n" +
+            "Status: Delivery error\n" +
+            "Diagnostic-Code: X-James; Delivery error\n" +
+            "Last-Attempt-Date: Thu, 8 Sep 2016 14:25:52 XXXXX (UTC)\n";
+
+        List<SentMail> sentMails = fakeMailContext.getSentMails();
+        assertThat(sentMails).hasSize(1);
+        SentMail sentMail = sentMails.get(0);
+        MimeMessage sentMessage = sentMail.getMsg();
+        MimeMultipart content = (MimeMultipart) sentMessage.getContent();
+        SharedByteArrayInputStream actualContent = (SharedByteArrayInputStream) content.getBodyPart(1).getContent();
+        assertThat(IOUtils.toString(actualContent, StandardCharsets.UTF_8)).isEqualTo(expectedContent);
+    }
+
+    @Test
     void defaultStatusShouldBeUsedWhenNone() throws Exception {
         FakeMailetConfig mailetConfig = FakeMailetConfig.builder()
             .mailetName(MAILET_NAME)
diff --git a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/dsn/DSNMessageHook.java b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/dsn/DSNMessageHook.java
index b9060a3..db3b266 100644
--- a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/dsn/DSNMessageHook.java
+++ b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/dsn/DSNMessageHook.java
@@ -23,6 +23,7 @@ import static org.apache.james.smtpserver.dsn.DSNMailParameterHook.DSN_ENVID;
 import static org.apache.james.smtpserver.dsn.DSNMailParameterHook.DSN_RET;
 import static org.apache.james.smtpserver.dsn.DSNRcptParameterHook.DSN_RCPT_PARAMETERS;
 
+import java.time.ZonedDateTime;
 import java.util.Optional;
 
 import org.apache.james.core.MailAddress;
@@ -30,6 +31,9 @@ import org.apache.james.protocols.api.ProtocolSession;
 import org.apache.james.protocols.smtp.SMTPSession;
 import org.apache.james.protocols.smtp.hook.HookResult;
 import org.apache.james.smtpserver.JamesMessageHook;
+import org.apache.mailet.Attribute;
+import org.apache.mailet.AttributeName;
+import org.apache.mailet.AttributeValue;
 import org.apache.mailet.DsnParameters;
 import org.apache.mailet.Mail;
 
@@ -47,6 +51,7 @@ public class DSNMessageHook implements JamesMessageHook {
 
         DsnParameters.of(envId, ret, rcptParameters)
             .ifPresent(mail::setDsnParameters);
+        mail.setAttribute(new Attribute(AttributeName.of("dsn-arrival-date"), AttributeValue.of(ZonedDateTime.now())));
         return HookResult.DECLINED;
     }
 }


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


[james-project] 01/14: JAMES-3431 Allow the use of zoneDateTime in mail attributes

Posted by bt...@apache.org.
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 983278b1e585eab3ed5fead0677bdeeaee816535
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Feb 17 11:38:09 2021 +0700

    JAMES-3431 Allow the use of zoneDateTime in mail attributes
---
 .../java/org/apache/mailet/AttributeValue.java     |  9 +++++
 .../main/java/org/apache/mailet/Serializer.java    | 34 ++++++++++++++++++
 .../java/org/apache/mailet/AttributeValueTest.java | 41 ++++++++++++++++++++++
 3 files changed, 84 insertions(+)

diff --git a/mailet/api/src/main/java/org/apache/mailet/AttributeValue.java b/mailet/api/src/main/java/org/apache/mailet/AttributeValue.java
index 4b71eb8..cd6b66e 100644
--- a/mailet/api/src/main/java/org/apache/mailet/AttributeValue.java
+++ b/mailet/api/src/main/java/org/apache/mailet/AttributeValue.java
@@ -22,6 +22,7 @@ package org.apache.mailet;
 import java.io.IOException;
 import java.io.Serializable;
 import java.net.URL;
+import java.time.ZonedDateTime;
 import java.util.Collection;
 import java.util.Map;
 import java.util.Objects;
@@ -73,6 +74,11 @@ public class AttributeValue<T> {
         return new AttributeValue<>(value, Serializer.FLOAT_SERIALIZER);
     }
 
+    public static AttributeValue<ZonedDateTime> of(ZonedDateTime value) {
+        Preconditions.checkNotNull(value, "value should not be null");
+        return new AttributeValue<>(value, Serializer.DATE_SERIALIZER);
+    }
+
     public static AttributeValue<Double> of(Double value) {
         Preconditions.checkNotNull(value, "value should not be null");
         return new AttributeValue<>(value, Serializer.DOUBLE_SERIALIZER);
@@ -136,6 +142,9 @@ public class AttributeValue<T> {
         if (value instanceof Double) {
             return of((Double) value);
         }
+        if (value instanceof ZonedDateTime) {
+            return of((ZonedDateTime) value);
+        }
         if (value instanceof Collection<?>) {
             return of(((Collection<AttributeValue<?>>) value));
         }
diff --git a/mailet/api/src/main/java/org/apache/mailet/Serializer.java b/mailet/api/src/main/java/org/apache/mailet/Serializer.java
index 7d8203b..d5757a0 100644
--- a/mailet/api/src/main/java/org/apache/mailet/Serializer.java
+++ b/mailet/api/src/main/java/org/apache/mailet/Serializer.java
@@ -19,11 +19,14 @@
 
 package org.apache.mailet;
 
+import static java.time.format.DateTimeFormatter.ISO_DATE_TIME;
+
 import java.io.IOException;
 import java.io.Serializable;
 import java.io.UncheckedIOException;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.time.ZonedDateTime;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -80,6 +83,7 @@ public interface Serializer<T> {
                     LONG_SERIALIZER,
                     FLOAT_SERIALIZER,
                     DOUBLE_SERIALIZER,
+                    DATE_SERIALIZER,
                     MESSAGE_ID_DTO_SERIALIZER,
                     new Serializer.ArbitrarySerializableSerializer<>(),
                     URL_SERIALIZER,
@@ -267,6 +271,36 @@ public interface Serializer<T> {
 
     Serializer<Double> DOUBLE_SERIALIZER = new DoubleSerializer();
 
+    class DateSerializer implements Serializer<ZonedDateTime> {
+        @Override
+        public JsonNode serialize(ZonedDateTime object) {
+            String serialized = object.format(ISO_DATE_TIME);
+            return TextNode.valueOf(serialized);
+        }
+
+        @Override
+        public Optional<ZonedDateTime> deserialize(JsonNode json) {
+            if (json instanceof TextNode) {
+                String serialized = json.asText();
+                return Optional.of(ZonedDateTime.parse(serialized, ISO_DATE_TIME));
+            } else {
+                return Optional.empty();
+            }
+        }
+
+        @Override
+        public String getName() {
+            return "DateSerializer";
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            return this.getClass() == other.getClass();
+        }
+    }
+
+    Serializer<ZonedDateTime> DATE_SERIALIZER = new DateSerializer();
+
     class MessageIdDtoSerializer implements Serializer<MessageIdDto> {
 
         @Override
diff --git a/mailet/api/src/test/java/org/apache/mailet/AttributeValueTest.java b/mailet/api/src/test/java/org/apache/mailet/AttributeValueTest.java
index 28b627c..7ce8b42 100644
--- a/mailet/api/src/test/java/org/apache/mailet/AttributeValueTest.java
+++ b/mailet/api/src/test/java/org/apache/mailet/AttributeValueTest.java
@@ -25,6 +25,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.time.ZonedDateTime;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -330,6 +331,46 @@ class AttributeValueTest {
     }
 
     @Nested
+    class DateSerialization {
+        @Test
+        void dateShouldBeSerializedAndBack() {
+            AttributeValue<ZonedDateTime> expected = AttributeValue.of(ZonedDateTime.parse("2015-10-30T16:12:00Z"));
+
+            JsonNode json = expected.toJson();
+            AttributeValue<?> actual = AttributeValue.fromJson(json);
+
+            assertThat(actual).isEqualTo(expected);
+        }
+
+        @Test
+        void nullDoubleShouldThrowAnException() {
+            assertThatNullPointerException()
+                .isThrownBy(() -> AttributeValue.of((Double) null));
+        }
+
+        @Test
+        void fromJsonStringShouldReturnDoubleAttributeValueWhenDouble() throws Exception {
+            AttributeValue<ZonedDateTime> expected = AttributeValue.of(ZonedDateTime.parse("2015-10-30T16:12:00Z"));
+
+            AttributeValue<?> actual = AttributeValue.fromJsonString("{\"serializer\":\"DateSerializer\",\"value\":\"2015-10-30T16:12:00Z\"}");
+
+            assertThat(actual).isEqualTo(expected);
+        }
+
+        @Test
+        void fromJsonStringShouldThrowOnMalformedFormattedJson() {
+            assertThatIllegalStateException()
+                .isThrownBy(() -> AttributeValue.fromJsonString("{\"serializer\":\"DateSerializer\",\"value\": []}"));
+        }
+
+        @Test
+        void fromJsonStringShouldThrowOnIntNode() {
+            assertThatIllegalStateException()
+                .isThrownBy(() -> AttributeValue.fromJsonString("{\"serializer\":\"DateSerializer\",\"value\": 1}"));
+        }
+    }
+
+    @Nested
     class QueueSerializableTest {
         @Test
         void queueSerializableShouldBeSerializedAndBack() {


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


[james-project] 11/14: JAMES-3477 Do not call SPamAssassin

Posted by bt...@apache.org.
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 e8ca920448c9f3aa660a206ea4e68bfeb45850aa
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Feb 15 16:14:31 2021 +0700

    JAMES-3477 Do not call SPamAssassin
---
 .../java/org/apache/james/WithCassandraBlobStoreImmutableTest.java    | 2 --
 .../container/guice/cassandra-guice/src/test/resources/listeners.xml  | 3 ---
 .../guice/cassandra-guice/src/test/resources/mailetcontainer.xml      | 4 ----
 3 files changed, 9 deletions(-)

diff --git a/server/container/guice/cassandra-guice/src/test/java/org/apache/james/WithCassandraBlobStoreImmutableTest.java b/server/container/guice/cassandra-guice/src/test/java/org/apache/james/WithCassandraBlobStoreImmutableTest.java
index ce408f8..25c26dd 100644
--- a/server/container/guice/cassandra-guice/src/test/java/org/apache/james/WithCassandraBlobStoreImmutableTest.java
+++ b/server/container/guice/cassandra-guice/src/test/java/org/apache/james/WithCassandraBlobStoreImmutableTest.java
@@ -20,7 +20,6 @@
 package org.apache.james;
 
 import org.apache.james.jmap.draft.JmapJamesServerContract;
-import org.apache.james.jmap.draft.methods.integration.SpamAssassinModuleExtension;
 import org.apache.james.modules.TestJMAPServerModule;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
@@ -34,7 +33,6 @@ class WithCassandraBlobStoreImmutableTest implements JmapJamesServerContract, Ja
         return TestingDistributedJamesServerBuilder.withSearchConfiguration(SearchConfiguration.elasticSearch())
             .extension(new DockerElasticSearchExtension())
             .extension(new CassandraExtension())
-            .extension(new SpamAssassinModuleExtension())
             .server(configuration -> CassandraJamesServerMain.createServer(configuration)
                 .overrideWith(new TestJMAPServerModule()));
     }
diff --git a/server/container/guice/cassandra-guice/src/test/resources/listeners.xml b/server/container/guice/cassandra-guice/src/test/resources/listeners.xml
index eabdb5e..71dccb1 100644
--- a/server/container/guice/cassandra-guice/src/test/resources/listeners.xml
+++ b/server/container/guice/cassandra-guice/src/test/resources/listeners.xml
@@ -20,9 +20,6 @@
 
 <listeners>
   <listener>
-    <class>org.apache.james.mailbox.spamassassin.SpamAssassinListener</class>
-  </listener>
-  <listener>
     <class>org.apache.james.mailbox.cassandra.MailboxOperationLoggingListener</class>
   </listener>
 </listeners>
\ No newline at end of file
diff --git a/server/container/guice/cassandra-guice/src/test/resources/mailetcontainer.xml b/server/container/guice/cassandra-guice/src/test/resources/mailetcontainer.xml
index 7855291..e00aa42 100644
--- a/server/container/guice/cassandra-guice/src/test/resources/mailetcontainer.xml
+++ b/server/container/guice/cassandra-guice/src/test/resources/mailetcontainer.xml
@@ -89,10 +89,6 @@
         </processor>
 
         <processor state="local-delivery" enableJmx="true">
-            <mailet match="All" class="SpamAssassin">
-                <spamdHost>localhost</spamdHost>
-                <spamdPort>783</spamdPort>
-            </mailet>
             <mailet match="IsMarkedAsSpam" class="WithStorageDirective">
                 <targetFolderName>Spam</targetFolderName>
             </mailet>


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


[james-project] 13/14: JAMES-3501 Starting 2 Cassandra james server concurrently fails

Posted by bt...@apache.org.
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 1c241aae3917f75cb8480e80a320a9cc0d511455
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Jan 28 11:51:28 2019 +0700

    JAMES-3501 Starting 2 Cassandra james server concurrently fails
---
 .../init/CassandraTableManagerConcurrentTest.java  | 65 ++++++++++++++++++++++
 1 file changed, 65 insertions(+)

diff --git a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/CassandraTableManagerConcurrentTest.java b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/CassandraTableManagerConcurrentTest.java
new file mode 100644
index 0000000..2d31c77
--- /dev/null
+++ b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/CassandraTableManagerConcurrentTest.java
@@ -0,0 +1,65 @@
+/****************************************************************
+ * 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.backends.cassandra.init;
+
+import java.time.Duration;
+
+import org.apache.james.backends.cassandra.CassandraCluster;
+import org.apache.james.backends.cassandra.DockerCassandraExtension;
+import org.apache.james.backends.cassandra.components.CassandraModule;
+import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionModule;
+import org.apache.james.util.Host;
+import org.apache.james.util.concurrency.ConcurrentTestRunner;
+import org.apache.james.util.concurrency.ConcurrentTestRunner.ConcurrentOperation;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import com.datastax.driver.core.DataType;
+
+@Disabled("JAMES-3501 Concurrent start is not supported. Instead start a single server then scale.")
+class CassandraTableManagerConcurrentTest {
+    private static final String TABLE_NAME = "tablename";
+
+    public static final CassandraModule MODULE = CassandraModule.aggregateModules(
+            CassandraSchemaVersionModule.MODULE,
+            CassandraModule.table(TABLE_NAME)
+                .comment("Testing table")
+                .statement(statement -> statement
+                        .addPartitionKey("id", DataType.timeuuid())
+                        .addClusteringColumn("clustering", DataType.bigint()))
+                .build());
+
+    @RegisterExtension
+    static DockerCassandraExtension cassandraExtension = new DockerCassandraExtension();
+
+    @Test
+    void initializeTableShouldCreateAllTheTables() throws Exception{
+        Host cassandraHost = cassandraExtension.getDockerCassandra().getHost();
+        ConcurrentOperation concurrentOperation = (a, b) -> CassandraCluster.create(MODULE, cassandraHost);
+
+
+        ConcurrentTestRunner.builder()
+            .operation(concurrentOperation)
+            .threadCount(2)
+            .operationCount(10)
+            .runSuccessfullyWithin(Duration.ofMinutes(1));
+    }
+}
\ No newline at end of file


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


[james-project] 06/14: JAMES-3477 Make clear that MimeMessageWrapper is not thread safe

Posted by bt...@apache.org.
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 1c60fd47d2a9460352831fec62efda5389d72b87
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Sat Dec 26 15:15:42 2020 +0700

    JAMES-3477 Make clear that MimeMessageWrapper is not thread safe
    
    & remove 'homeopatic' synchronized instructions
---
 .../james/server/core/MimeMessageWrapper.java      | 30 ++++++++++++----------
 .../james/jmap/draft/send/MailSpoolTest.java       |  6 +++++
 2 files changed, 22 insertions(+), 14 deletions(-)

diff --git a/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageWrapper.java b/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageWrapper.java
index 8af2b86..431575a 100644
--- a/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageWrapper.java
+++ b/server/container/core/src/main/java/org/apache/james/server/core/MimeMessageWrapper.java
@@ -46,6 +46,8 @@ import org.apache.james.lifecycle.api.LifecycleUtil;
  * This object wraps a MimeMessage, only loading the underlying MimeMessage
  * object when needed. Also tracks if changes were made to reduce unnecessary
  * saves.
+ *
+ * This class is not thread safe.
  */
 public class MimeMessageWrapper extends MimeMessage implements Disposable {
 
@@ -178,7 +180,7 @@ public class MimeMessageWrapper extends MimeMessage implements Disposable {
      * 
      * @see MimeMessageSource
      */
-    public synchronized String getSourceId() {
+    public String getSourceId() {
         return source != null ? source.getSourceId() : null;
     }
 
@@ -188,7 +190,7 @@ public class MimeMessageWrapper extends MimeMessage implements Disposable {
      * @throws MessagingException
      *             if an error is encountered while loading the headers
      */
-    protected synchronized void loadHeaders() throws MessagingException {
+    protected void loadHeaders() throws MessagingException {
         if (headers != null) {
             // Another thread has already loaded these headers
         } else if (source != null) {
@@ -208,7 +210,7 @@ public class MimeMessageWrapper extends MimeMessage implements Disposable {
      * @throws MessagingException
      *             if an error is encountered while loading the message
      */
-    public synchronized void loadMessage() throws MessagingException {
+    public void loadMessage() throws MessagingException {
         if (messageParsed) {
             // Another thread has already loaded this message
         } else if (source != null) {
@@ -239,7 +241,7 @@ public class MimeMessageWrapper extends MimeMessage implements Disposable {
      * 
      * @return whether the message has been modified
      */
-    public synchronized boolean isModified() {
+    public boolean isModified() {
         return headersModified || bodyModified || modified;
     }
 
@@ -248,7 +250,7 @@ public class MimeMessageWrapper extends MimeMessage implements Disposable {
      * 
      * @return bodyModified
      */
-    public synchronized boolean isBodyModified() {
+    public boolean isBodyModified() {
         return bodyModified;
     }
 
@@ -257,7 +259,7 @@ public class MimeMessageWrapper extends MimeMessage implements Disposable {
      * 
      * @return headersModified
      */
-    public synchronized boolean isHeaderModified() {
+    public boolean isHeaderModified() {
         return headersModified;
     }
 
@@ -289,7 +291,7 @@ public class MimeMessageWrapper extends MimeMessage implements Disposable {
         writeTo(headerOs, bodyOs, ignoreList, false);
     }
 
-    public synchronized void writeTo(OutputStream headerOs, OutputStream bodyOs, String[] ignoreList, boolean preLoad) throws IOException, MessagingException {
+    public void writeTo(OutputStream headerOs, OutputStream bodyOs, String[] ignoreList, boolean preLoad) throws IOException, MessagingException {
         
         if (!preLoad && source != null && !isBodyModified()) {
             // We do not want to instantiate the message... just read from
@@ -344,7 +346,7 @@ public class MimeMessageWrapper extends MimeMessage implements Disposable {
      * never change on {@link #saveChanges()}
      */
     @Override
-    public synchronized int getSize() throws MessagingException {
+    public int getSize() throws MessagingException {
         if (source != null) {
             try {
                 long fullSize = source.getMessageSize();
@@ -490,7 +492,7 @@ public class MimeMessageWrapper extends MimeMessage implements Disposable {
         return headers.getNonMatchingHeaderLines(names);
     }
 
-    private synchronized void checkModifyHeaders() throws MessagingException {
+    private void checkModifyHeaders() throws MessagingException {
         // Disable only-header loading optimizations for JAMES-559
         /*
          * if (!messageParsed) { loadMessage(); }
@@ -534,7 +536,7 @@ public class MimeMessageWrapper extends MimeMessage implements Disposable {
      * content. Every method that alter the content will fallback to this one.
      */
     @Override
-    public synchronized void setDataHandler(DataHandler arg0) throws MessagingException {
+    public void setDataHandler(DataHandler arg0) throws MessagingException {
         modified = true;
         saved = false;
         bodyModified = true;
@@ -556,7 +558,7 @@ public class MimeMessageWrapper extends MimeMessage implements Disposable {
     }
 
     @Override
-    protected synchronized void parse(InputStream is) throws MessagingException {
+    protected void parse(InputStream is) throws MessagingException {
         // the super implementation calls
         // headers = createInternetHeaders(is);
         super.parse(is);
@@ -568,7 +570,7 @@ public class MimeMessageWrapper extends MimeMessage implements Disposable {
      * Otherwise we parse
      */
     @Override
-    protected synchronized InternetHeaders createInternetHeaders(InputStream is) throws MessagingException {
+    protected InternetHeaders createInternetHeaders(InputStream is) throws MessagingException {
         /*
          * This code is no more needed: see JAMES-570 and new tests
          * 
@@ -616,7 +618,7 @@ public class MimeMessageWrapper extends MimeMessage implements Disposable {
     }
 
     @Override
-    public synchronized InputStream getRawInputStream() throws MessagingException {
+    public InputStream getRawInputStream() throws MessagingException {
         if (!messageParsed && !isModified() && source != null) {
             InputStream is;
             try {
@@ -642,7 +644,7 @@ public class MimeMessageWrapper extends MimeMessage implements Disposable {
      * @throws MessagingException
      */
 
-    public synchronized InputStream getMessageInputStream() throws MessagingException {
+    public InputStream getMessageInputStream() throws MessagingException {
         if (!messageParsed && !isModified() && source != null) {
             try {
                 return source.getInputStream();
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/send/MailSpoolTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/send/MailSpoolTest.java
index 0552c2f..66e6576 100644
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/send/MailSpoolTest.java
+++ b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/send/MailSpoolTest.java
@@ -19,14 +19,18 @@
 
 package org.apache.james.jmap.draft.send;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.assertj.core.api.Assertions.assertThat;
 
+import java.nio.charset.StandardCharsets;
+
 import org.apache.james.mailbox.model.TestMessageId;
 import org.apache.james.queue.api.MailQueue;
 import org.apache.james.queue.api.MailQueue.MailQueueItem;
 import org.apache.james.queue.api.MailQueueFactory;
 import org.apache.james.queue.api.RawMailQueueItemDecoratorFactory;
 import org.apache.james.queue.memory.MemoryMailQueueFactory;
+import org.apache.james.util.MimeMessageUtil;
 import org.apache.mailet.Attribute;
 import org.apache.mailet.AttributeValue;
 import org.apache.mailet.base.test.FakeMail;
@@ -56,6 +60,7 @@ public class MailSpoolTest {
     public void sendShouldEnQueueTheMail() throws Exception {
         FakeMail mail = FakeMail.builder()
             .name(NAME)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes("header: value\r\n".getBytes(UTF_8)))
             .build();
 
         mailSpool.send(mail, new MailMetadata(MESSAGE_ID, USERNAME));
@@ -68,6 +73,7 @@ public class MailSpoolTest {
     public void sendShouldPositionJMAPRelatedMetadata() throws Exception {
         FakeMail mail = FakeMail.builder()
             .name(NAME)
+            .mimeMessage(MimeMessageUtil.mimeMessageFromBytes("header: value\r\n".getBytes(UTF_8)))
             .build();
 
         mailSpool.send(mail, new MailMetadata(MESSAGE_ID, USERNAME));


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