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:18 UTC

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

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