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

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

commit 83804338aa0ba1ff67f2dc8d88b25b8349e76b3d
Author: Maksim Meliashchuk <[email protected]>
AuthorDate: Sun Sep 10 23:59:15 2023 +0300

    JAMES-2156 Add configuration option to enable attachment storage for JPA
---
 mailbox/jpa/pom.xml                                |  23 +++
 .../jpa/JPAMailboxSessionMapperFactory.java        |  10 +-
 .../james/mailbox/jpa/mail/JPAMessageMapper.java   |  27 +++-
 .../model/openjpa/AbstractJPAMailboxMessage.java   |  46 +++---
 .../JPAMailboxMessageWithAttachmentStorage.java    | 155 +++++++++++++++++++++
 .../james/mailbox/jpa/JPAMailboxFixture.java       |   4 +-
 .../mailbox/jpa/JPASubscriptionManagerTest.java    |  10 +-
 .../mailbox/jpa/JpaMailboxManagerProvider.java     |  10 +-
 .../mailbox/jpa/mail/JPAAttachmentMapperTest.java  |   5 +-
 .../james/mailbox/jpa/mail/JPAMapperProvider.java  |  10 +-
 .../mail/JPAMessageWithAttachmentMapperTest.java   |   2 +-
 .../task/JPARecomputeCurrentQuotasServiceTest.java |  10 +-
 .../james-database-mariadb.properties              |   5 +
 .../sample-configuration/james-database.properties |   5 +
 .../james-database-mariadb.properties              |   5 +
 .../sample-configuration/james-database.properties |   5 +
 .../src/main/resources/james-database.properties   |   6 +
 .../james/modules/data/JPAConfiguration.java       |  30 +++-
 .../james/modules/data/JPAEntityManagerModule.java |  14 +-
 .../james/modules/data/JPAConfigurationTest.java   |   4 +
 src/site/xdoc/server/config-system.xml             |   7 +
 21 files changed, 346 insertions(+), 47 deletions(-)

diff --git a/mailbox/jpa/pom.xml b/mailbox/jpa/pom.xml
index b3020ba999..558c5ca266 100644
--- a/mailbox/jpa/pom.xml
+++ b/mailbox/jpa/pom.xml
@@ -31,6 +31,18 @@
     <packaging>jar</packaging>
     <name>Apache James :: Mailbox :: JPA</name>
 
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>${james.groupId}</groupId>
+                <artifactId>james-server-guice</artifactId>
+                <version>${project.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
     <dependencies>
         <dependency>
             <groupId>${james.groupId}</groupId>
@@ -89,6 +101,17 @@
             <artifactId>james-server-data-jpa</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-jpa-common-guice</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-jpa-common-guice</artifactId>
+            <scope>compile</scope>
+        </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
             <artifactId>james-server-testing</artifactId>
diff --git 
a/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/JPAMailboxSessionMapperFactory.java
 
b/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/JPAMailboxSessionMapperFactory.java
index 935623ef6d..96612081f1 100644
--- 
a/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/JPAMailboxSessionMapperFactory.java
+++ 
b/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/JPAMailboxSessionMapperFactory.java
@@ -42,6 +42,7 @@ import org.apache.james.mailbox.store.mail.MessageMapper;
 import org.apache.james.mailbox.store.mail.ModSeqProvider;
 import org.apache.james.mailbox.store.mail.UidProvider;
 import org.apache.james.mailbox.store.user.SubscriptionMapper;
+import org.apache.james.modules.data.JPAConfiguration;
 
 /**
  * JPA implementation of {@link MailboxSessionMapperFactory}
@@ -53,16 +54,19 @@ public class JPAMailboxSessionMapperFactory extends 
MailboxSessionMapperFactory
     private final JPAUidProvider uidProvider;
     private final JPAModSeqProvider modSeqProvider;
     private final AttachmentMapper attachmentMapper;
+    private final JPAConfiguration jpaConfiguration;
 
     @Inject
-    public JPAMailboxSessionMapperFactory(EntityManagerFactory 
entityManagerFactory, JPAUidProvider uidProvider, JPAModSeqProvider 
modSeqProvider) {
+    public JPAMailboxSessionMapperFactory(EntityManagerFactory 
entityManagerFactory, JPAUidProvider uidProvider,
+                                          JPAModSeqProvider modSeqProvider, 
JPAConfiguration jpaConfiguration) {
         this.entityManagerFactory = entityManagerFactory;
         this.uidProvider = uidProvider;
         this.modSeqProvider = modSeqProvider;
         EntityManagerUtils.safelyClose(createEntityManager());
         this.attachmentMapper = new JPAAttachmentMapper(entityManagerFactory);
+        this.jpaConfiguration = jpaConfiguration;
     }
-    
+
     @Override
     public MailboxMapper createMailboxMapper(MailboxSession session) {
         return new JPAMailboxMapper(entityManagerFactory);
@@ -70,7 +74,7 @@ public class JPAMailboxSessionMapperFactory extends 
MailboxSessionMapperFactory
 
     @Override
     public MessageMapper createMessageMapper(MailboxSession session) {
-        return new JPAMessageMapper(uidProvider, modSeqProvider, 
entityManagerFactory);
+        return new JPAMessageMapper(uidProvider, modSeqProvider, 
entityManagerFactory, jpaConfiguration);
     }
 
     @Override
diff --git 
a/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/JPAMessageMapper.java
 
b/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/JPAMessageMapper.java
index 28cc9f207e..a0c0ef3a4b 100644
--- 
a/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/JPAMessageMapper.java
+++ 
b/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/JPAMessageMapper.java
@@ -22,6 +22,7 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.stream.Collectors;
 
@@ -42,6 +43,7 @@ import org.apache.james.mailbox.jpa.mail.model.JPAMailbox;
 import 
org.apache.james.mailbox.jpa.mail.model.openjpa.AbstractJPAMailboxMessage;
 import 
org.apache.james.mailbox.jpa.mail.model.openjpa.JPAEncryptedMailboxMessage;
 import org.apache.james.mailbox.jpa.mail.model.openjpa.JPAMailboxMessage;
+import 
org.apache.james.mailbox.jpa.mail.model.openjpa.JPAMailboxMessageWithAttachmentStorage;
 import 
org.apache.james.mailbox.jpa.mail.model.openjpa.JPAStreamingMailboxMessage;
 import org.apache.james.mailbox.model.Mailbox;
 import org.apache.james.mailbox.model.MailboxCounters;
@@ -54,6 +56,7 @@ import org.apache.james.mailbox.model.UpdatedFlags;
 import org.apache.james.mailbox.store.FlagsUpdateCalculator;
 import org.apache.james.mailbox.store.mail.MessageMapper;
 import org.apache.james.mailbox.store.mail.model.MailboxMessage;
+import org.apache.james.modules.data.JPAConfiguration;
 import org.apache.openjpa.persistence.ArgumentException;
 
 import com.github.fge.lambdas.Throwing;
@@ -73,12 +76,15 @@ public class JPAMessageMapper extends 
JPATransactionalMapper implements MessageM
     private final MessageUtils messageMetadataMapper;
     private final JPAUidProvider uidProvider;
     private final JPAModSeqProvider modSeqProvider;
+    private final JPAConfiguration jpaConfiguration;
 
-    public JPAMessageMapper(JPAUidProvider uidProvider, JPAModSeqProvider 
modSeqProvider, EntityManagerFactory entityManagerFactory) {
+    public JPAMessageMapper(JPAUidProvider uidProvider, JPAModSeqProvider 
modSeqProvider, EntityManagerFactory entityManagerFactory,
+                            JPAConfiguration jpaConfiguration) {
         super(entityManagerFactory);
         this.messageMetadataMapper = new MessageUtils(uidProvider, 
modSeqProvider);
         this.uidProvider = uidProvider;
         this.modSeqProvider = modSeqProvider;
+        this.jpaConfiguration = jpaConfiguration;
     }
 
     @Override
@@ -361,6 +367,8 @@ public class JPAMessageMapper extends 
JPATransactionalMapper implements MessageM
             copy = new JPAStreamingMailboxMessage(currentMailbox, uid, modSeq, 
original);
         } else if (original instanceof JPAEncryptedMailboxMessage) {
             copy = new JPAEncryptedMailboxMessage(currentMailbox, uid, modSeq, 
original);
+        } else if (original instanceof JPAMailboxMessageWithAttachmentStorage) 
{
+            copy = new JPAMailboxMessageWithAttachmentStorage(currentMailbox, 
uid, modSeq, original);
         } else {
             copy = new JPAMailboxMessage(currentMailbox, uid, modSeq, 
original);
         }
@@ -375,13 +383,19 @@ public class JPAMessageMapper extends 
JPATransactionalMapper implements MessageM
             // org.apache.openjpa.persistence.ArgumentException.
             JPAId mailboxId = (JPAId) mailbox.getMailboxId();
             JPAMailbox currentMailbox = 
getEntityManager().find(JPAMailbox.class, mailboxId.getRawId());
+
+            boolean isAttachmentStorage = false;
+            if (Objects.nonNull(jpaConfiguration)) {
+                isAttachmentStorage = 
jpaConfiguration.isAttachmentStorageEnabled().orElse(false);
+            }
+
             if (message instanceof AbstractJPAMailboxMessage) {
                 ((AbstractJPAMailboxMessage) 
message).setMailbox(currentMailbox);
 
                 getEntityManager().persist(message);
                 return message.metaData();
-            } else {
-                JPAMailboxMessage persistData = new 
JPAMailboxMessage(currentMailbox, message.getUid(), message.getModSeq(), 
message);
+            } else if (isAttachmentStorage) {
+                JPAMailboxMessageWithAttachmentStorage persistData = new 
JPAMailboxMessageWithAttachmentStorage(currentMailbox, message.getUid(), 
message.getModSeq(), message);
                 persistData.setFlags(message.createFlags());
 
                 if (message.getAttachments().isEmpty()) {
@@ -399,6 +413,11 @@ public class JPAMessageMapper extends 
JPATransactionalMapper implements MessageM
                     }
                 }
                 return persistData.metaData();
+            } else {
+                JPAMailboxMessage persistData = new 
JPAMailboxMessage(currentMailbox, message.getUid(), message.getModSeq(), 
message);
+                persistData.setFlags(message.createFlags());
+                getEntityManager().persist(persistData);
+                return persistData.metaData();
             }
 
         } catch (PersistenceException | ArgumentException e) {
@@ -468,7 +487,7 @@ public class JPAMessageMapper extends 
JPATransactionalMapper implements MessageM
 
     private List<MessageUid> getUidList(List<MailboxMessage> messages) {
         return messages.stream()
-            .map(message -> message.getUid())
+            .map(MailboxMessage::getUid)
             .collect(ImmutableList.toImmutableList());
     }
 
diff --git 
a/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/openjpa/AbstractJPAMailboxMessage.java
 
b/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/openjpa/AbstractJPAMailboxMessage.java
index 73fa322478..8b9f1fe0a9 100644
--- 
a/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/openjpa/AbstractJPAMailboxMessage.java
+++ 
b/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/openjpa/AbstractJPAMailboxMessage.java
@@ -26,7 +26,7 @@ import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 import java.util.Optional;
-import java.util.stream.Collectors;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.mail.Flags;
 import javax.persistence.Basic;
@@ -46,26 +46,29 @@ import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.ModSeq;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.jpa.JPAId;
-import org.apache.james.mailbox.jpa.mail.model.JPAAttachment;
 import org.apache.james.mailbox.jpa.mail.model.JPAMailbox;
 import org.apache.james.mailbox.jpa.mail.model.JPAProperty;
 import org.apache.james.mailbox.jpa.mail.model.JPAUserFlag;
+import org.apache.james.mailbox.model.AttachmentId;
 import org.apache.james.mailbox.model.ComposedMessageId;
 import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
 import org.apache.james.mailbox.model.MessageAttachmentMetadata;
 import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.model.ParsedAttachment;
 import org.apache.james.mailbox.model.ThreadId;
 import org.apache.james.mailbox.store.mail.model.DefaultMessageId;
 import org.apache.james.mailbox.store.mail.model.DelegatingMailboxMessage;
 import org.apache.james.mailbox.store.mail.model.FlagsFactory;
 import org.apache.james.mailbox.store.mail.model.MailboxMessage;
 import org.apache.james.mailbox.store.mail.model.Property;
+import org.apache.james.mailbox.store.mail.model.impl.MessageParser;
 import org.apache.james.mailbox.store.mail.model.impl.Properties;
 import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
 import org.apache.openjpa.persistence.jdbc.ElementJoinColumn;
 import org.apache.openjpa.persistence.jdbc.ElementJoinColumns;
 import org.apache.openjpa.persistence.jdbc.Index;
 
+import com.github.fge.lambdas.Throwing;
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableList;
 
@@ -274,14 +277,6 @@ public abstract class AbstractJPAMailboxMessage implements 
MailboxMessage {
         @ElementJoinColumn(name = "MAIL_UID", referencedColumnName = 
"MAIL_UID")})
     private List<JPAUserFlag> userFlags;
 
-    /**
-     * Metadata for attachments
-     */
-    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
-    @OrderBy("attachmentId")
-    @ElementJoinColumns({@ElementJoinColumn(name = "MAILBOX_ID", 
referencedColumnName = "MAILBOX_ID"),
-        @ElementJoinColumn(name = "MAIL_UID", referencedColumnName = 
"MAIL_UID")})
-    private List<JPAAttachment> attachments;
 
     protected AbstractJPAMailboxMessage() {
     }
@@ -291,7 +286,6 @@ public abstract class AbstractJPAMailboxMessage implements 
MailboxMessage {
         this.mailbox = mailbox;
         this.internalDate = internalDate;
         userFlags = new ArrayList<>();
-        attachments = new ArrayList<>();
 
         setFlags(flags);
         this.contentOctets = contentOctets;
@@ -344,7 +338,6 @@ public abstract class AbstractJPAMailboxMessage implements 
MailboxMessage {
         for (Property property : properties) {
             this.properties.add(new JPAProperty(property, order++));
         }
-        this.attachments = new ArrayList<>();
     }
 
     @Override
@@ -561,17 +554,26 @@ public abstract class AbstractJPAMailboxMessage 
implements MailboxMessage {
             + " )";
     }
 
-    /**
-     * Utility attachments' setter.
-     */
-    public void setAttachments(List<JPAAttachment> attachments) {
-        this.attachments = attachments;
-    }
-
     @Override
     public List<MessageAttachmentMetadata> getAttachments() {
-        return this.attachments.stream()
-            .map(JPAAttachment::toMessageAttachmentMetadata)
-            .collect(Collectors.toList());
+        try {
+            AtomicInteger counter = new AtomicInteger(0);
+            MessageParser.ParsingResult parsingResult = new 
MessageParser().retrieveAttachments(getFullContent());
+            ImmutableList<MessageAttachmentMetadata> result = parsingResult
+                .getAttachments()
+                .stream()
+                .map(Throwing.<ParsedAttachment, 
MessageAttachmentMetadata>function(
+                        attachmentMetadata -> 
attachmentMetadata.asMessageAttachment(generateFixedAttachmentId(counter.incrementAndGet()),
 getMessageId()))
+                    .sneakyThrow())
+                .collect(ImmutableList.toImmutableList());
+            parsingResult.dispose();
+            return result;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private AttachmentId generateFixedAttachmentId(int position) {
+        return AttachmentId.from(getMailboxId().serialize() + "-" + 
getUid().asLong() + "-" + position);
     }
 }
diff --git 
a/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/openjpa/JPAMailboxMessageWithAttachmentStorage.java
 
b/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/openjpa/JPAMailboxMessageWithAttachmentStorage.java
new file mode 100644
index 0000000000..2e4e1a969e
--- /dev/null
+++ 
b/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/openjpa/JPAMailboxMessageWithAttachmentStorage.java
@@ -0,0 +1,155 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+package org.apache.james.mailbox.jpa.mail.model.openjpa;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.mail.Flags;
+import javax.persistence.Basic;
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Lob;
+import javax.persistence.OneToMany;
+import javax.persistence.OrderBy;
+import javax.persistence.Table;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.input.BoundedInputStream;
+import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.ModSeq;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.jpa.mail.model.JPAAttachment;
+import org.apache.james.mailbox.jpa.mail.model.JPAMailbox;
+import org.apache.james.mailbox.model.Content;
+import org.apache.james.mailbox.model.Mailbox;
+import org.apache.james.mailbox.model.MessageAttachmentMetadata;
+import org.apache.james.mailbox.store.mail.model.MailboxMessage;
+import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
+import org.apache.openjpa.persistence.jdbc.ElementJoinColumn;
+import org.apache.openjpa.persistence.jdbc.ElementJoinColumns;
+
+@Entity(name = "MailboxMessage")
+@Table(name = "JAMES_MAIL")
+public class JPAMailboxMessageWithAttachmentStorage extends 
AbstractJPAMailboxMessage {
+
+    private static final byte[] EMPTY_ARRAY = new byte[] {};
+
+    /** The value for the body field. Lazy loaded */
+    /** We use a max length to represent 1gb data. Thats prolly overkill, but 
who knows */
+    @Basic(optional = false, fetch = FetchType.LAZY)
+    @Column(name = "MAIL_BYTES", length = 1048576000, nullable = false)
+    @Lob
+    private byte[] body;
+
+    /** The value for the header field. Lazy loaded */
+    /** We use a max length to represent 10mb data. Thats prolly overkill, but 
who knows */
+    @Basic(optional = false, fetch = FetchType.LAZY)
+    @Column(name = "HEADER_BYTES", length = 10485760, nullable = false)
+    @Lob private byte[] header;
+
+    /**
+     * Metadata for attachments
+     */
+    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
+    @OrderBy("attachmentId")
+    @ElementJoinColumns({@ElementJoinColumn(name = "MAILBOX_ID", 
referencedColumnName = "MAILBOX_ID"),
+        @ElementJoinColumn(name = "MAIL_UID", referencedColumnName = 
"MAIL_UID")})
+    private List<JPAAttachment> attachments;
+
+
+    public JPAMailboxMessageWithAttachmentStorage() {
+
+    }
+
+    public JPAMailboxMessageWithAttachmentStorage(JPAMailbox mailbox, Date 
internalDate, int size, Flags flags, Content content, int bodyStartOctet, 
PropertyBuilder propertyBuilder) throws MailboxException {
+        super(mailbox, internalDate, flags, size, bodyStartOctet, 
propertyBuilder);
+        try {
+            int headerEnd = bodyStartOctet;
+            if (headerEnd < 0) {
+                headerEnd = 0;
+            }
+            InputStream stream = content.getInputStream();
+            this.header = IOUtils.toByteArray(new BoundedInputStream(stream, 
headerEnd));
+            this.body = IOUtils.toByteArray(stream);
+
+        } catch (IOException e) {
+            throw new MailboxException("Unable to parse message",e);
+        }
+        attachments = new ArrayList<>();
+    }
+
+    /**
+     * Create a copy of the given message
+     */
+    public JPAMailboxMessageWithAttachmentStorage(JPAMailbox mailbox, 
MessageUid uid, ModSeq modSeq, MailboxMessage message) throws MailboxException {
+        super(mailbox, uid, modSeq, message);
+        try {
+            this.body = IOUtils.toByteArray(message.getBodyContent());
+            this.header = IOUtils.toByteArray(message.getHeaderContent());
+        } catch (IOException e) {
+            throw new MailboxException("Unable to parse message",e);
+        }
+        attachments = new ArrayList<>();
+
+    }
+
+    @Override
+    public InputStream getBodyContent() throws IOException {
+        if (body == null) {
+            return new ByteArrayInputStream(EMPTY_ARRAY);
+        }
+        return new ByteArrayInputStream(body);
+    }
+
+    @Override
+    public InputStream getHeaderContent() throws IOException {
+        if (header == null) {
+            return new ByteArrayInputStream(EMPTY_ARRAY);
+        }
+        return new ByteArrayInputStream(header);
+    }
+
+    @Override
+    public MailboxMessage copy(Mailbox mailbox) throws MailboxException {
+        return new JPAMailboxMessage(JPAMailbox.from(mailbox), getUid(), 
getModSeq(), this);
+    }
+
+    /**
+     * Utility attachments' setter.
+     */
+    public void setAttachments(List<JPAAttachment> attachments) {
+        this.attachments = attachments;
+    }
+
+    @Override
+    public List<MessageAttachmentMetadata> getAttachments() {
+
+        return this.attachments.stream()
+            .map(JPAAttachment::toMessageAttachmentMetadata)
+            .collect(Collectors.toList());
+    }
+}
diff --git 
a/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/JPAMailboxFixture.java 
b/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/JPAMailboxFixture.java
index 6dc6d62d5b..25a96d93ca 100644
--- 
a/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/JPAMailboxFixture.java
+++ 
b/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/JPAMailboxFixture.java
@@ -28,6 +28,7 @@ import org.apache.james.mailbox.jpa.mail.model.JPAProperty;
 import org.apache.james.mailbox.jpa.mail.model.JPAUserFlag;
 import 
org.apache.james.mailbox.jpa.mail.model.openjpa.AbstractJPAMailboxMessage;
 import org.apache.james.mailbox.jpa.mail.model.openjpa.JPAMailboxMessage;
+import 
org.apache.james.mailbox.jpa.mail.model.openjpa.JPAMailboxMessageWithAttachmentStorage;
 import org.apache.james.mailbox.jpa.quota.model.JpaCurrentQuota;
 import org.apache.james.mailbox.jpa.quota.model.MaxDomainMessageCount;
 import org.apache.james.mailbox.jpa.quota.model.MaxDomainStorage;
@@ -49,7 +50,8 @@ public interface JPAMailboxFixture {
         JPAUserFlag.class,
         JPAMailboxAnnotation.class,
         JPASubscription.class,
-        JPAAttachment.class
+        JPAAttachment.class,
+        JPAMailboxMessageWithAttachmentStorage.class
     );
 
     List<Class<?>> QUOTA_PERSISTANCE_CLASSES = ImmutableList.of(
diff --git 
a/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/JPASubscriptionManagerTest.java
 
b/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/JPASubscriptionManagerTest.java
index de8112a3e4..6f4510e852 100644
--- 
a/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/JPASubscriptionManagerTest.java
+++ 
b/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/JPASubscriptionManagerTest.java
@@ -31,6 +31,8 @@ import org.apache.james.mailbox.jpa.mail.JPAModSeqProvider;
 import org.apache.james.mailbox.jpa.mail.JPAUidProvider;
 import org.apache.james.mailbox.store.StoreSubscriptionManager;
 import org.apache.james.metrics.tests.RecordingMetricFactory;
+import org.apache.james.modules.data.JPAConfiguration;
+
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 
@@ -49,9 +51,15 @@ class JPASubscriptionManagerTest implements 
SubscriptionManagerContract {
     void setUp() {
         EntityManagerFactory entityManagerFactory = 
JPA_TEST_CLUSTER.getEntityManagerFactory();
 
+        JPAConfiguration jpaConfiguration = JPAConfiguration.builder()
+            .driverName("driverName")
+            .driverURL("driverUrl")
+            .build();
+
         JPAMailboxSessionMapperFactory mapperFactory = new 
JPAMailboxSessionMapperFactory(entityManagerFactory,
             new JPAUidProvider(entityManagerFactory),
-            new JPAModSeqProvider(entityManagerFactory));
+            new JPAModSeqProvider(entityManagerFactory),
+            jpaConfiguration);
         InVMEventBus eventBus = new InVMEventBus(new InVmEventDelivery(new 
RecordingMetricFactory()), EventBusTestFixture.RETRY_BACKOFF_CONFIGURATION, new 
MemoryEventDeadLetters());
         subscriptionManager = new StoreSubscriptionManager(mapperFactory, 
mapperFactory, eventBus);
     }
diff --git 
a/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/JpaMailboxManagerProvider.java
 
b/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/JpaMailboxManagerProvider.java
index b951986ab2..afe26b1119 100644
--- 
a/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/JpaMailboxManagerProvider.java
+++ 
b/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/JpaMailboxManagerProvider.java
@@ -46,6 +46,7 @@ import org.apache.james.mailbox.store.quota.QuotaComponents;
 import org.apache.james.mailbox.store.search.MessageSearchIndex;
 import org.apache.james.mailbox.store.search.SimpleMessageSearchIndex;
 import org.apache.james.metrics.tests.RecordingMetricFactory;
+import org.apache.james.modules.data.JPAConfiguration;
 import org.apache.james.utils.UpdatableTickingClock;
 
 public class JpaMailboxManagerProvider {
@@ -55,7 +56,14 @@ public class JpaMailboxManagerProvider {
 
     public static OpenJPAMailboxManager provideMailboxManager(JpaTestCluster 
jpaTestCluster) {
         EntityManagerFactory entityManagerFactory = 
jpaTestCluster.getEntityManagerFactory();
-        JPAMailboxSessionMapperFactory mf = new 
JPAMailboxSessionMapperFactory(entityManagerFactory, new 
JPAUidProvider(entityManagerFactory), new 
JPAModSeqProvider(entityManagerFactory));
+
+        JPAConfiguration jpaConfiguration = JPAConfiguration.builder()
+            .driverName("driverName")
+            .driverURL("driverUrl")
+            .attachmentStorage(true)
+            .build();
+
+        JPAMailboxSessionMapperFactory mf = new 
JPAMailboxSessionMapperFactory(entityManagerFactory, new 
JPAUidProvider(entityManagerFactory), new 
JPAModSeqProvider(entityManagerFactory), jpaConfiguration);
 
         MailboxACLResolver aclResolver = new UnionMailboxACLResolver();
         MessageParser messageParser = new MessageParser();
diff --git 
a/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/JPAAttachmentMapperTest.java
 
b/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/JPAAttachmentMapperTest.java
index 552d50a9e9..d4dc4282a5 100644
--- 
a/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/JPAAttachmentMapperTest.java
+++ 
b/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/JPAAttachmentMapperTest.java
@@ -1,4 +1,4 @@
-/***************************************************************
+/**************************************************************
  * 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        *
@@ -15,7 +15,8 @@
  * KIND, either express or implied.  See the License for the    *
  * specific language governing permissions and limitations      *
  * under the License.                                           *
- ***************************************************************/
+ **************************************************************/
+
 
 
 package org.apache.james.mailbox.jpa.mail;
diff --git 
a/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/JPAMapperProvider.java
 
b/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/JPAMapperProvider.java
index 58e735840f..9388bd60c1 100644
--- 
a/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/JPAMapperProvider.java
+++ 
b/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/JPAMapperProvider.java
@@ -39,6 +39,7 @@ import org.apache.james.mailbox.store.mail.MessageIdMapper;
 import org.apache.james.mailbox.store.mail.MessageMapper;
 import org.apache.james.mailbox.store.mail.model.DefaultMessageId;
 import org.apache.james.mailbox.store.mail.model.MapperProvider;
+import org.apache.james.modules.data.JPAConfiguration;
 
 import com.google.common.collect.ImmutableList;
 
@@ -59,9 +60,16 @@ public class JPAMapperProvider implements MapperProvider {
     public MessageMapper createMessageMapper() {
         EntityManagerFactory entityManagerFactory = 
jpaTestCluster.getEntityManagerFactory();
 
+        JPAConfiguration jpaConfiguration = JPAConfiguration.builder()
+            .driverName("driverName")
+            .driverURL("driverUrl")
+            .attachmentStorage(true)
+            .build();
+
         JPAMessageMapper messageMapper = new JPAMessageMapper(new 
JPAUidProvider(entityManagerFactory),
             new JPAModSeqProvider(entityManagerFactory),
-            entityManagerFactory);
+            entityManagerFactory,
+            jpaConfiguration);
 
         return new TransactionalMessageMapper(messageMapper);
     }
diff --git 
a/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/JPAMessageWithAttachmentMapperTest.java
 
b/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/JPAMessageWithAttachmentMapperTest.java
index a744d806bd..7383b55b71 100644
--- 
a/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/JPAMessageWithAttachmentMapperTest.java
+++ 
b/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/JPAMessageWithAttachmentMapperTest.java
@@ -1,4 +1,4 @@
-/***************************************************************
+/**************************************************************
  * 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        *
diff --git 
a/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/task/JPARecomputeCurrentQuotasServiceTest.java
 
b/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/task/JPARecomputeCurrentQuotasServiceTest.java
index 8fcb1300bc..f044028ac3 100644
--- 
a/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/task/JPARecomputeCurrentQuotasServiceTest.java
+++ 
b/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/task/JPARecomputeCurrentQuotasServiceTest.java
@@ -41,6 +41,7 @@ import 
org.apache.james.mailbox.quota.task.RecomputeMailboxCurrentQuotasService;
 import org.apache.james.mailbox.store.StoreMailboxManager;
 import org.apache.james.mailbox.store.quota.CurrentQuotaCalculator;
 import org.apache.james.mailbox.store.quota.DefaultUserQuotaRootResolver;
+import org.apache.james.modules.data.JPAConfiguration;
 import org.apache.james.user.api.UsersRepository;
 import org.apache.james.user.jpa.JPAUsersRepository;
 import org.apache.james.user.jpa.model.JPAUser;
@@ -71,9 +72,16 @@ class JPARecomputeCurrentQuotasServiceTest implements 
RecomputeCurrentQuotasServ
     @BeforeEach
     void setUp() throws Exception {
         EntityManagerFactory entityManagerFactory = 
JPA_TEST_CLUSTER.getEntityManagerFactory();
+
+        JPAConfiguration jpaConfiguration = JPAConfiguration.builder()
+            .driverName("driverName")
+            .driverURL("driverUrl")
+            .build();
+
         JPAMailboxSessionMapperFactory mapperFactory = new 
JPAMailboxSessionMapperFactory(entityManagerFactory,
             new JPAUidProvider(entityManagerFactory),
-            new JPAModSeqProvider(entityManagerFactory));
+            new JPAModSeqProvider(entityManagerFactory),
+            jpaConfiguration);
 
         usersRepository = new JPAUsersRepository(NO_DOMAIN_LIST);
         
usersRepository.setEntityManagerFactory(JPA_TEST_CLUSTER.getEntityManagerFactory());
diff --git 
a/server/apps/jpa-app/sample-configuration/james-database-mariadb.properties 
b/server/apps/jpa-app/sample-configuration/james-database-mariadb.properties
index cf091319a4..52ca423635 100644
--- a/server/apps/jpa-app/sample-configuration/james-database-mariadb.properties
+++ b/server/apps/jpa-app/sample-configuration/james-database-mariadb.properties
@@ -42,3 +42,8 @@ openjpa.streaming=false
 # datasource.validationQuery=select 1
 # The maximum number of active connections that can be allocated from this 
pool at the same time, or negative for no limit.
 # datasource.maxTotal=8
+
+# Attachment storage
+# *WARNING*: Is not made to store large binary content
+# Optional, Allowed values are: true, false, defaults to false
+# attachmentStorage.enabled=false
diff --git a/server/apps/jpa-app/sample-configuration/james-database.properties 
b/server/apps/jpa-app/sample-configuration/james-database.properties
index d569d6d5d6..1336fe5a1c 100644
--- a/server/apps/jpa-app/sample-configuration/james-database.properties
+++ b/server/apps/jpa-app/sample-configuration/james-database.properties
@@ -46,3 +46,8 @@ openjpa.streaming=false
 # datasource.validationQuery=select 1
 # The maximum number of active connections that can be allocated from this 
pool at the same time, or negative for no limit.
 # datasource.maxTotal=8
+
+# Attachment storage
+# *WARNING*: Is not made to store large binary content
+# Optional, Allowed values are: true, false, defaults to false
+# attachmentStorage.enabled=false
diff --git 
a/server/apps/jpa-smtp-app/sample-configuration/james-database-mariadb.properties
 
b/server/apps/jpa-smtp-app/sample-configuration/james-database-mariadb.properties
index cf091319a4..52ca423635 100644
--- 
a/server/apps/jpa-smtp-app/sample-configuration/james-database-mariadb.properties
+++ 
b/server/apps/jpa-smtp-app/sample-configuration/james-database-mariadb.properties
@@ -42,3 +42,8 @@ openjpa.streaming=false
 # datasource.validationQuery=select 1
 # The maximum number of active connections that can be allocated from this 
pool at the same time, or negative for no limit.
 # datasource.maxTotal=8
+
+# Attachment storage
+# *WARNING*: Is not made to store large binary content
+# Optional, Allowed values are: true, false, defaults to false
+# attachmentStorage.enabled=false
diff --git 
a/server/apps/jpa-smtp-app/sample-configuration/james-database.properties 
b/server/apps/jpa-smtp-app/sample-configuration/james-database.properties
index d569d6d5d6..1336fe5a1c 100644
--- a/server/apps/jpa-smtp-app/sample-configuration/james-database.properties
+++ b/server/apps/jpa-smtp-app/sample-configuration/james-database.properties
@@ -46,3 +46,8 @@ openjpa.streaming=false
 # datasource.validationQuery=select 1
 # The maximum number of active connections that can be allocated from this 
pool at the same time, or negative for no limit.
 # datasource.maxTotal=8
+
+# Attachment storage
+# *WARNING*: Is not made to store large binary content
+# Optional, Allowed values are: true, false, defaults to false
+# attachmentStorage.enabled=false
diff --git 
a/server/apps/spring-app/src/main/resources/james-database.properties 
b/server/apps/spring-app/src/main/resources/james-database.properties
index 22770d00d2..5dc74806b1 100644
--- a/server/apps/spring-app/src/main/resources/james-database.properties
+++ b/server/apps/spring-app/src/main/resources/james-database.properties
@@ -44,3 +44,9 @@ openjpa.streaming=false
 # datasource.validationQueryTimeoutSec=2
 # This is different per database. See 
https://stackoverflow.com/questions/10684244/dbcp-validationquery-for-different-databases#10684260
 # datasource.validationQuery=select 1
+
+# Attachment storage
+# *WARNING*: Is not made to store large binary content
+# Optional, Allowed values are: true, false, defaults to false
+# attachmentStorage.enabled=false
+
diff --git 
a/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPAConfiguration.java
 
b/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPAConfiguration.java
index 62419c1bb2..07733020b1 100644
--- 
a/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPAConfiguration.java
+++ 
b/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPAConfiguration.java
@@ -21,6 +21,7 @@ package org.apache.james.modules.data;
 import static 
org.apache.james.modules.data.JPAConfiguration.Credential.NO_CREDENTIAL;
 import static 
org.apache.james.modules.data.JPAConfiguration.ReadyToBuild.CUSTOM_DATASOURCE_PROPERTIES;
 import static 
org.apache.james.modules.data.JPAConfiguration.ReadyToBuild.CUSTOM_OPENJPA_PROPERTIES;
+import static 
org.apache.james.modules.data.JPAConfiguration.ReadyToBuild.NO_ATTACHMENT_STORAGE;
 import static 
org.apache.james.modules.data.JPAConfiguration.ReadyToBuild.NO_MAX_CONNECTIONS;
 import static 
org.apache.james.modules.data.JPAConfiguration.ReadyToBuild.NO_MULTITHREADED;
 import static 
org.apache.james.modules.data.JPAConfiguration.ReadyToBuild.NO_TEST_ON_BORROW;
@@ -54,6 +55,8 @@ public class JPAConfiguration {
     static final String DATASOURCE_MAX_TOTAL = "datasource.maxTotal";
     static List<String> DEFAULT_DATASOURCE_PROPERTIES = 
List.of(DATASOURCE_TEST_ON_BORROW, DATASOURCE_VALIDATION_QUERY_TIMEOUT_SEC, 
DATASOURCE_VALIDATION_QUERY, DATASOURCE_MAX_TOTAL);
 
+    static final String ATTACHMENT_STORAGE = "attachmentStorage.enabled";
+
     static {
     }
 
@@ -107,6 +110,7 @@ public class JPAConfiguration {
         static final Optional<Integer> NO_MAX_CONNECTIONS = Optional.empty();
         static final Map<String,String> CUSTOM_OPENJPA_PROPERTIES = Map.of();
         static final Map<String,String> CUSTOM_DATASOURCE_PROPERTIES = 
Map.of();
+        static final Optional<Boolean> NO_ATTACHMENT_STORAGE = 
Optional.empty();
 
         private final String driverName;
         private final String driverURL;
@@ -119,12 +123,14 @@ public class JPAConfiguration {
         private Optional<Integer> maxConnections;
         private Map<String,String> customDatasourceProperties;
         private Map<String,String> customOpenjpaProperties;
+        private Optional<Boolean> attachmentStorage;
 
 
         private ReadyToBuild(String driverName, String driverURL, 
Optional<Credential> credential,
                             Optional<Boolean> testOnBorrow, Optional<Boolean> 
multithreaded, Optional<Integer> validationQueryTimeoutSec,
                             Optional<String> validationQuery,Optional<Integer> 
maxConnections,
-                            Map<String,String> customDatasourceProperties, 
Map<String,String> customOpenjpaProperties
+                            Map<String,String> customDatasourceProperties, 
Map<String,String> customOpenjpaProperties,
+                            Optional<Boolean> attachmentStorage
         ) {
             this.driverName = driverName;
             this.driverURL = driverURL;
@@ -136,15 +142,16 @@ public class JPAConfiguration {
             this.maxConnections = maxConnections;
             this.customDatasourceProperties = customDatasourceProperties;
             this.customOpenjpaProperties = customOpenjpaProperties;
+            this.attachmentStorage = attachmentStorage;
         }
 
         public JPAConfiguration build() {
-            return new JPAConfiguration(driverName, driverURL, credential, 
testOnBorrow, multithreaded, validationQueryTimeoutSec, validationQuery, 
maxConnections, customDatasourceProperties, customOpenjpaProperties);
+            return new JPAConfiguration(driverName, driverURL, credential, 
testOnBorrow, multithreaded, validationQueryTimeoutSec, validationQuery, 
maxConnections, customDatasourceProperties, customOpenjpaProperties, 
attachmentStorage);
         }
 
         public RequirePassword username(String username) {
             return password -> new ReadyToBuild(driverName, driverURL, 
Credential.of(username, password),
-                testOnBorrow, multithreaded, validationQueryTimeoutSec, 
validationQuery, maxConnections, customDatasourceProperties, 
customOpenjpaProperties);
+                testOnBorrow, multithreaded, validationQueryTimeoutSec, 
validationQuery, maxConnections, customDatasourceProperties, 
customOpenjpaProperties, attachmentStorage);
         }
 
         public ReadyToBuild testOnBorrow(Boolean testOnBorrow) {
@@ -172,6 +179,11 @@ public class JPAConfiguration {
             return this;
         }
 
+        public ReadyToBuild attachmentStorage(Boolean attachmentStorage) {
+            this.attachmentStorage = Optional.ofNullable(attachmentStorage);
+            return this;
+        }
+
         public ReadyToBuild setCustomDatasourceProperties(Map<String, String> 
customDatasourceProperties) {
             this.customDatasourceProperties = new 
HashMap<>(customDatasourceProperties);
             
DEFAULT_DATASOURCE_PROPERTIES.forEach(this.customDatasourceProperties::remove);
@@ -193,7 +205,7 @@ public class JPAConfiguration {
 
     public static RequireDriverName builder() {
         return driverName -> driverURL -> new ReadyToBuild(driverName, 
driverURL, NO_CREDENTIAL, NO_TEST_ON_BORROW, NO_MULTITHREADED,
-            NO_VALIDATION_QUERY_TIMEOUT_SEC, NO_VALIDATION_QUERY, 
NO_MAX_CONNECTIONS, CUSTOM_DATASOURCE_PROPERTIES, CUSTOM_OPENJPA_PROPERTIES);
+            NO_VALIDATION_QUERY_TIMEOUT_SEC, NO_VALIDATION_QUERY, 
NO_MAX_CONNECTIONS, CUSTOM_DATASOURCE_PROPERTIES, CUSTOM_OPENJPA_PROPERTIES, 
NO_ATTACHMENT_STORAGE);
     }
 
     private final String driverName;
@@ -206,11 +218,13 @@ public class JPAConfiguration {
     private final Optional<Integer> maxConnections;
     private Map<String,String> customDatasourceProperties;
     private Map<String,String> customOpenjpaProperties;
+    private final Optional<Boolean> attachmentStorage;
 
 
     @VisibleForTesting
     JPAConfiguration(String driverName, String driverURL, Optional<Credential> 
credential, Optional<Boolean> testOnBorrow, Optional<Boolean> multithreaded,
-                     Optional<Integer> validationQueryTimeoutSec, 
Optional<String> validationQuery, Optional<Integer> maxConnections, 
Map<String,String> customDatasourceProperties, Map<String,String> 
customOpenjpaProperties) {
+                     Optional<Integer> validationQueryTimeoutSec, 
Optional<String> validationQuery, Optional<Integer> maxConnections, 
Map<String,String> customDatasourceProperties, Map<String,String> 
customOpenjpaProperties,
+                     Optional<Boolean> attachmentStorage) {
         Preconditions.checkNotNull(driverName, "driverName cannot be null");
         Preconditions.checkNotNull(driverURL, "driverURL cannot be null");
         validationQueryTimeoutSec.ifPresent(timeoutInSec ->
@@ -228,6 +242,7 @@ public class JPAConfiguration {
         this.maxConnections = maxConnections;
         this.customDatasourceProperties = customDatasourceProperties;
         this.customOpenjpaProperties = customOpenjpaProperties;
+        this.attachmentStorage = attachmentStorage;
 
     }
 
@@ -267,8 +282,11 @@ public class JPAConfiguration {
         return customDatasourceProperties;
     }
 
-
     public Optional<Integer> getMaxConnections() {
         return maxConnections;
     }
+
+    public Optional<Boolean> isAttachmentStorageEnabled() {
+        return attachmentStorage;
+    }
 }
diff --git 
a/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPAEntityManagerModule.java
 
b/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPAEntityManagerModule.java
index efcc300c11..758e3aa87e 100644
--- 
a/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPAEntityManagerModule.java
+++ 
b/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPAEntityManagerModule.java
@@ -65,9 +65,15 @@ public class JPAEntityManagerModule extends AbstractModule {
         properties.put(JPAConfiguration.JPA_CONNECTION_PROPERTIES, 
Joiner.on(",").join(connectionProperties));
         properties.putAll(jpaConfiguration.getCustomOpenjpaProperties());
 
-        if (jpaConfiguration.isMultithreaded().isPresent()) {
-            properties.put(JPAConfiguration.JPA_MULTITHREADED, 
jpaConfiguration.isMultithreaded().get().toString());
-        }
+        jpaConfiguration.isMultithreaded()
+            .ifPresent(isMultiThread ->
+                properties.put(JPAConfiguration.JPA_MULTITHREADED, 
jpaConfiguration.isMultithreaded().toString())
+            );
+
+        jpaConfiguration.isAttachmentStorageEnabled()
+            .ifPresent(isMultiThread ->
+                properties.put(JPAConfiguration.ATTACHMENT_STORAGE, 
jpaConfiguration.isAttachmentStorageEnabled().toString())
+            );
 
         return Persistence.createEntityManagerFactory("Global", properties);
     }
@@ -80,7 +86,6 @@ public class JPAEntityManagerModule extends AbstractModule {
         Map<String, String> openjpaProperties = getKeysForPrefix(dataSource, 
"openjpa", false);
         Map<String, String> datasourceProperties = 
getKeysForPrefix(dataSource, "datasource", true);
 
-
         return JPAConfiguration.builder()
                 .driverName(dataSource.getString("database.driverClassName"))
                 .driverURL(dataSource.getString("database.url"))
@@ -93,6 +98,7 @@ public class JPAEntityManagerModule extends AbstractModule {
                 .password(dataSource.getString("database.password"))
                 .setCustomOpenjpaProperties(openjpaProperties)
                 .setCustomDatasourceProperties(datasourceProperties)
+                
.attachmentStorage(dataSource.getBoolean(JPAConfiguration.ATTACHMENT_STORAGE, 
false))
                 .build();
     }
 
diff --git 
a/server/container/guice/jpa-common/src/test/java/org/apache/james/modules/data/JPAConfigurationTest.java
 
b/server/container/guice/jpa-common/src/test/java/org/apache/james/modules/data/JPAConfigurationTest.java
index b81fd7fb52..6758163475 100644
--- 
a/server/container/guice/jpa-common/src/test/java/org/apache/james/modules/data/JPAConfigurationTest.java
+++ 
b/server/container/guice/jpa-common/src/test/java/org/apache/james/modules/data/JPAConfigurationTest.java
@@ -36,6 +36,7 @@ class JPAConfigurationTest {
     private static final String USER_NAME = "username";
     private static final String PASSWORD = "password";
     private static final String EMPTY_STRING = "";
+    private static final boolean ATTACHMENT_STORAGE = true;
 
     @Test
     void buildShouldReturnCorrespondingProperties() {
@@ -48,6 +49,7 @@ class JPAConfigurationTest {
             .username(USER_NAME)
             .password(PASSWORD)
             .maxConnections(MAX_CONNECTIONS)
+            .attachmentStorage(ATTACHMENT_STORAGE)
             .build();
 
         SoftAssertions.assertSoftly(softly -> {
@@ -61,6 +63,7 @@ class JPAConfigurationTest {
                 
softly.assertThat(credential.getUsername()).isEqualTo(USER_NAME);
             });
             
softly.assertThat(configuration.getMaxConnections()).contains(MAX_CONNECTIONS);
+            
softly.assertThat(configuration.isAttachmentStorageEnabled()).contains(ATTACHMENT_STORAGE);
         });
     }
 
@@ -80,6 +83,7 @@ class JPAConfigurationTest {
             
softly.assertThat(configuration.getValidationQueryTimeoutSec()).isEmpty();
             softly.assertThat(configuration.getCredential()).isEmpty();
             softly.assertThat(configuration.getMaxConnections()).isEmpty();
+            
softly.assertThat(configuration.isAttachmentStorageEnabled()).isEmpty();
         });
     }
 
diff --git a/src/site/xdoc/server/config-system.xml 
b/src/site/xdoc/server/config-system.xml
index 89a45c50c4..3e33f17c0f 100644
--- a/src/site/xdoc/server/config-system.xml
+++ b/src/site/xdoc/server/config-system.xml
@@ -107,6 +107,13 @@
                     t0.mailbox_uid_validity, t0.user_name FROM 
public.james_mailbox t0 WHERE (t0.mailbox_name LIKE ?
                         ESCAPE '\\' AND t0.user_name = ? AND 
t0.mailbox_namespace = ?) [params=?, ?, ?]} [code=0, state=22025]"</code></p>
 
+                <p>Attachment storage configuration</p>
+                <dl>
+                    <dt><strong>attachmentStorage.enabled</strong></dt>
+                    <dd>(Guice only). The attachment storage configuration for 
JPA is used to enable the implementation of the attachment storage mechanism.
+                        *WARNING*: this configuration is not designed to store 
large binary content, as it is not optimized for this purpose.
+                        Optional, Allowed values are: `true` or `false`, 
defaults to `false`</dd>
+                </dl>
             </subsection>
 
             <subsection name="META-INF/persistence.xml">


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to