You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2020/05/04 03:21:21 UTC

[james-project] 02/12: JAMES-2997 Strong type for ContentType Field

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 9d6aca49311252e6014e226fe7f9543996415147
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Apr 27 18:01:05 2020 +0700

    JAMES-2997 Strong type for ContentType Field
---
 .../apache/james/mailbox/AttachmentManager.java    |  3 +-
 .../james/mailbox/model/AttachmentMetadata.java    | 17 +++---
 .../java/org/apache/james/mailbox/model/Blob.java  | 13 +++--
 .../apache/james/mailbox/model/ContentType.java    | 60 ++++++++++++++++++++++
 .../james/mailbox/model/ParsedAttachment.java      | 12 +++--
 .../mailbox/model/AttachmentMetadataTest.java      |  2 +-
 .../org/apache/james/mailbox/model/BlobTest.java   |  2 +-
 .../cassandra/mail/CassandraAttachmentDAOV2.java   | 11 ++--
 .../cassandra/mail/CassandraAttachmentMapper.java  |  3 +-
 .../inmemory/mail/InMemoryAttachmentMapper.java    |  3 +-
 .../mailbox/store/StoreAttachmentManager.java      |  3 +-
 .../james/mailbox/store/StoreBlobManager.java      |  3 +-
 .../james/mailbox/store/mail/AttachmentMapper.java |  3 +-
 .../store/mail/model/impl/MessageParser.java       |  8 +--
 .../mailbox/store/search/MessageSearches.java      |  3 +-
 .../james/mailbox/store/StoreBlobManagerTest.java  |  3 +-
 .../store/mail/model/AttachmentMapperTest.java     | 19 +++----
 .../store/mail/model/impl/MessageParserTest.java   | 24 +++++----
 .../methods/integration/SetMessagesMethodTest.java | 42 +++++++--------
 .../james/jmap/draft/json/ObjectMapperFactory.java | 21 ++++++++
 .../jmap/draft/methods/MIMEMessageConverter.java   |  3 +-
 .../apache/james/jmap/draft/model/Attachment.java  | 18 ++++---
 .../org/apache/james/jmap/http/DownloadRoutes.java |  5 +-
 .../org/apache/james/jmap/http/UploadRoutes.java   | 11 ++--
 .../james/jmap/draft/model/AttachmentTest.java     |  7 +--
 25 files changed, 210 insertions(+), 89 deletions(-)

diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/AttachmentManager.java b/mailbox/api/src/main/java/org/apache/james/mailbox/AttachmentManager.java
index 468e037..3a7424b 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/AttachmentManager.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/AttachmentManager.java
@@ -27,6 +27,7 @@ import org.apache.james.mailbox.exception.AttachmentNotFoundException;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.AttachmentId;
 import org.apache.james.mailbox.model.AttachmentMetadata;
+import org.apache.james.mailbox.model.ContentType;
 import org.reactivestreams.Publisher;
 
 public interface AttachmentManager extends AttachmentContentLoader {
@@ -37,7 +38,7 @@ public interface AttachmentManager extends AttachmentContentLoader {
 
     List<AttachmentMetadata> getAttachments(List<AttachmentId> attachmentIds, MailboxSession mailboxSession) throws MailboxException;
 
-    Publisher<AttachmentMetadata> storeAttachment(String contentType, InputStream attachmentContent, MailboxSession mailboxSession);
+    Publisher<AttachmentMetadata> storeAttachment(ContentType contentType, InputStream attachmentContent, MailboxSession mailboxSession);
 
     InputStream loadAttachmentContent(AttachmentId attachmentId, MailboxSession mailboxSession) throws AttachmentNotFoundException, IOException;
 
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/AttachmentMetadata.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/AttachmentMetadata.java
index 9676f76..0f4e9f7 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/AttachmentMetadata.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/AttachmentMetadata.java
@@ -22,13 +22,12 @@ package org.apache.james.mailbox.model;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
-import com.google.common.base.Strings;
 
 public class AttachmentMetadata {
     public static class Builder {
         private AttachmentId attachmentId;
         private Long size;
-        private String type;
+        private ContentType type;
 
         public Builder attachmentId(AttachmentId attachmentId) {
             Preconditions.checkArgument(attachmentId != null);
@@ -36,12 +35,16 @@ public class AttachmentMetadata {
             return this;
         }
 
-        public Builder type(String type) {
-            Preconditions.checkArgument(!Strings.isNullOrEmpty(type));
+        public Builder type(ContentType type) {
             this.type = type;
             return this;
         }
 
+        public Builder type(String type) {
+            this.type = ContentType.of(type);
+            return this;
+        }
+
         public Builder size(long size) {
             Preconditions.checkArgument(size >= 0, "'size' must be positive");
             this.size = size;
@@ -62,10 +65,10 @@ public class AttachmentMetadata {
     }
 
     private final AttachmentId attachmentId;
-    private final String type;
+    private final ContentType type;
     private final long size;
 
-    private AttachmentMetadata(AttachmentId attachmentId, String type, long size) {
+    private AttachmentMetadata(AttachmentId attachmentId, ContentType type, long size) {
         this.attachmentId = attachmentId;
         this.type = type;
         this.size = size;
@@ -75,7 +78,7 @@ public class AttachmentMetadata {
         return attachmentId;
     }
 
-    public String getType() {
+    public ContentType getType() {
         return type;
     }
 
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/Blob.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/Blob.java
index 141f3a8..0103085 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/Blob.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/Blob.java
@@ -44,7 +44,7 @@ public class Blob {
     public static class Builder {
         private BlobId blobId;
         private InputStreamSupplier payload;
-        private String contentType;
+        private ContentType contentType;
         private Long size;
 
         private Builder() {
@@ -61,6 +61,11 @@ public class Blob {
         }
 
         public Builder contentType(String contentType) {
+            this.contentType = ContentType.of(contentType);
+            return this;
+        }
+
+        public Builder contentType(ContentType contentType) {
             this.contentType = contentType;
             return this;
         }
@@ -86,11 +91,11 @@ public class Blob {
 
     private final BlobId blobId;
     private final InputStreamSupplier payload;
-    private final String contentType;
+    private final ContentType contentType;
     private final long size;
 
     @VisibleForTesting
-    Blob(BlobId blobId, InputStreamSupplier payload, String contentType, long size) {
+    Blob(BlobId blobId, InputStreamSupplier payload, ContentType contentType, long size) {
         this.blobId = blobId;
         this.payload = payload;
         this.contentType = contentType;
@@ -109,7 +114,7 @@ public class Blob {
         return size;
     }
 
-    public String getContentType() {
+    public ContentType getContentType() {
         return contentType;
     }
 
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/ContentType.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/ContentType.java
new file mode 100644
index 0000000..ed8ecf5
--- /dev/null
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/ContentType.java
@@ -0,0 +1,60 @@
+/****************************************************************
+ * 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.model;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+
+public class ContentType {
+    public static ContentType of(String value) {
+        Preconditions.checkState(!Strings.isNullOrEmpty(value), "'content type' is mandatory");
+        return new ContentType(value);
+    }
+
+    /**
+     * Follows syntax and usage as defined in https://tools.ietf.org/html/rfc2045#section-5
+     * Thus includes media type and parameters, including charset
+     * Example: text/plain; charset=utf-8
+     */
+    private final String value;
+
+    public ContentType(String value) {
+        this.value = value;
+    }
+
+    public String asString() {
+        return value;
+    }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof ContentType) {
+            ContentType that = (ContentType) o;
+
+            return java.util.Objects.equals(this.value, that.value);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return java.util.Objects.hash(value);
+    }
+}
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/ParsedAttachment.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/ParsedAttachment.java
index c615473..429528e 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/ParsedAttachment.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/ParsedAttachment.java
@@ -26,7 +26,11 @@ public class ParsedAttachment {
     interface Builder {
         @FunctionalInterface
         interface RequireContentType {
-            RequireContent contentType(String contentType);
+            RequireContent contentType(ContentType contentType);
+
+            default RequireContent contentType(String contentType) {
+                return contentType(ContentType.of(contentType));
+            }
         }
 
         @FunctionalInterface
@@ -74,13 +78,13 @@ public class ParsedAttachment {
         return contentType -> content -> name -> cid -> isInline -> new ParsedAttachment(contentType, content, name, cid, isInline);
     }
 
-    private final String contentType;
+    private final ContentType contentType;
     private final byte[] content;
     private final Optional<String> name;
     private final Optional<Cid> cid;
     private final boolean isInline;
 
-    private ParsedAttachment(String contentType, byte[] content, Optional<String> name, Optional<Cid> cid, boolean isInline) {
+    private ParsedAttachment(ContentType contentType, byte[] content, Optional<String> name, Optional<Cid> cid, boolean isInline) {
         this.contentType = contentType;
         this.content = content;
         this.name = name;
@@ -88,7 +92,7 @@ public class ParsedAttachment {
         this.isInline = isInline;
     }
 
-    public String getContentType() {
+    public ContentType getContentType() {
         return contentType;
     }
 
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/model/AttachmentMetadataTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/model/AttachmentMetadataTest.java
index 1c0eaba..f75bc1b 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/model/AttachmentMetadataTest.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/model/AttachmentMetadataTest.java
@@ -35,7 +35,7 @@ class AttachmentMetadataTest {
     @Test
     void builderShouldThrowWhenTypeIsNull() {
         assertThatThrownBy(() -> AttachmentMetadata.builder()
-                .type(null))
+                .type((String) null))
             .isInstanceOf(IllegalArgumentException.class);
     }
 
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/model/BlobTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/model/BlobTest.java
index 55c3b3c..931cf72 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/model/BlobTest.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/model/BlobTest.java
@@ -32,7 +32,7 @@ import nl.jqno.equalsverifier.EqualsVerifier;
 
 class BlobTest {
     static final BlobId ID = BlobId.fromString("123");
-    static final String CONTENT_TYPE = "text/plain";
+    static final ContentType CONTENT_TYPE = ContentType.of("text/plain");
     static final InputStreamSupplier PAYLOAD = () -> new ByteArrayInputStream("abc".getBytes(StandardCharsets.UTF_8));
     static final int LENGTH = 3;
 
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentDAOV2.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentDAOV2.java
index 5d1cb80..8627a73 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentDAOV2.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentDAOV2.java
@@ -40,6 +40,7 @@ import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
 import org.apache.james.blob.api.BlobId;
 import org.apache.james.mailbox.model.AttachmentId;
 import org.apache.james.mailbox.model.AttachmentMetadata;
+import org.apache.james.mailbox.model.ContentType;
 
 import com.datastax.driver.core.PreparedStatement;
 import com.datastax.driver.core.Row;
@@ -52,10 +53,10 @@ public class CassandraAttachmentDAOV2 {
     public static class DAOAttachment {
         private final AttachmentId attachmentId;
         private final BlobId blobId;
-        private final String type;
+        private final ContentType type;
         private final long size;
 
-        DAOAttachment(AttachmentId attachmentId, BlobId blobId, String type, long size) {
+        DAOAttachment(AttachmentId attachmentId, BlobId blobId, ContentType type, long size) {
             this.attachmentId = attachmentId;
             this.blobId = blobId;
             this.type = type;
@@ -70,7 +71,7 @@ public class CassandraAttachmentDAOV2 {
             return blobId;
         }
 
-        public String getType() {
+        public ContentType getType() {
             return type;
         }
 
@@ -117,7 +118,7 @@ public class CassandraAttachmentDAOV2 {
         return new DAOAttachment(
             AttachmentId.from(row.getString(ID)),
             blobIfFactory.from(row.getString(BLOB_ID)),
-            row.getString(TYPE),
+            ContentType.of(row.getString(TYPE)),
             row.getLong(SIZE));
     }
 
@@ -166,7 +167,7 @@ public class CassandraAttachmentDAOV2 {
                 .setUUID(ID_AS_UUID, attachment.getAttachmentId().asUUID())
                 .setString(ID, attachment.getAttachmentId().getId())
                 .setLong(SIZE, attachment.getSize())
-                .setString(TYPE, attachment.getType())
+                .setString(TYPE, attachment.getType().asString())
                 .setString(BLOB_ID, attachment.getBlobId().asString()));
     }
 
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentMapper.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentMapper.java
index 8d84078..dd6edec 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentMapper.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentMapper.java
@@ -35,6 +35,7 @@ import org.apache.james.mailbox.exception.AttachmentNotFoundException;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.AttachmentId;
 import org.apache.james.mailbox.model.AttachmentMetadata;
+import org.apache.james.mailbox.model.ContentType;
 import org.apache.james.mailbox.model.MessageAttachmentMetadata;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.ParsedAttachment;
@@ -111,7 +112,7 @@ public class CassandraAttachmentMapper implements AttachmentMapper {
     }
 
     @Override
-    public Mono<AttachmentMetadata> storeAttachmentForOwner(String contentType, InputStream inputStream, Username owner) {
+    public Mono<AttachmentMetadata> storeAttachmentForOwner(ContentType contentType, InputStream inputStream, Username owner) {
         CurrentPositionInputStream currentPositionInputStream = new CurrentPositionInputStream(inputStream);
         AttachmentId attachmentId = AttachmentId.random();
         return ownerDAO.addOwner(attachmentId, owner)
diff --git a/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryAttachmentMapper.java b/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryAttachmentMapper.java
index d35f515..c5237b9 100644
--- a/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryAttachmentMapper.java
+++ b/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryAttachmentMapper.java
@@ -33,6 +33,7 @@ import org.apache.james.mailbox.exception.AttachmentNotFoundException;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.AttachmentId;
 import org.apache.james.mailbox.model.AttachmentMetadata;
+import org.apache.james.mailbox.model.ContentType;
 import org.apache.james.mailbox.model.MessageAttachmentMetadata;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.ParsedAttachment;
@@ -86,7 +87,7 @@ public class InMemoryAttachmentMapper implements AttachmentMapper {
     }
 
     @Override
-    public Mono<AttachmentMetadata> storeAttachmentForOwner(String contentType, InputStream inputStream, Username owner) {
+    public Mono<AttachmentMetadata> storeAttachmentForOwner(ContentType contentType, InputStream inputStream, Username owner) {
         return Mono.fromCallable(() -> {
             byte[] bytes = toByteArray(inputStream);
             AttachmentMetadata attachment = AttachmentMetadata.builder()
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreAttachmentManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreAttachmentManager.java
index 6f230b9..7a4efe2 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreAttachmentManager.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreAttachmentManager.java
@@ -34,6 +34,7 @@ import org.apache.james.mailbox.exception.AttachmentNotFoundException;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.AttachmentId;
 import org.apache.james.mailbox.model.AttachmentMetadata;
+import org.apache.james.mailbox.model.ContentType;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.store.mail.AttachmentMapperFactory;
 import org.reactivestreams.Publisher;
@@ -77,7 +78,7 @@ public class StoreAttachmentManager implements AttachmentManager {
     }
 
     @Override
-    public Publisher<AttachmentMetadata> storeAttachment(String contentType, InputStream attachmentContent, MailboxSession mailboxSession) {
+    public Publisher<AttachmentMetadata> storeAttachment(ContentType contentType, InputStream attachmentContent, MailboxSession mailboxSession) {
         return attachmentMapperFactory.getAttachmentMapper(mailboxSession)
             .storeAttachmentForOwner(contentType, attachmentContent, mailboxSession.getUser());
     }
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreBlobManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreBlobManager.java
index 90674b1..ea7ff91 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreBlobManager.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreBlobManager.java
@@ -35,6 +35,7 @@ import org.apache.james.mailbox.model.AttachmentMetadata;
 import org.apache.james.mailbox.model.Blob;
 import org.apache.james.mailbox.model.BlobId;
 import org.apache.james.mailbox.model.Content;
+import org.apache.james.mailbox.model.ContentType;
 import org.apache.james.mailbox.model.FetchGroup;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MessageResult;
@@ -42,7 +43,7 @@ import org.apache.james.mailbox.model.MessageResult;
 import com.github.fge.lambdas.Throwing;
 
 public class StoreBlobManager implements BlobManager {
-    public static final String MESSAGE_RFC822_CONTENT_TYPE = "message/rfc822";
+    public static final ContentType MESSAGE_RFC822_CONTENT_TYPE = ContentType.of("message/rfc822");
     private final AttachmentManager attachmentManager;
     private final MessageIdManager messageIdManager;
     private final MessageId.Factory messageIdFactory;
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/AttachmentMapper.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/AttachmentMapper.java
index 377651b..8a90153 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/AttachmentMapper.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/AttachmentMapper.java
@@ -28,6 +28,7 @@ import org.apache.james.mailbox.exception.AttachmentNotFoundException;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.AttachmentId;
 import org.apache.james.mailbox.model.AttachmentMetadata;
+import org.apache.james.mailbox.model.ContentType;
 import org.apache.james.mailbox.model.MessageAttachmentMetadata;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.ParsedAttachment;
@@ -42,7 +43,7 @@ public interface AttachmentMapper extends Mapper {
 
     List<AttachmentMetadata> getAttachments(Collection<AttachmentId> attachmentIds);
 
-    Publisher<AttachmentMetadata> storeAttachmentForOwner(String contentType, InputStream attachmentContent, Username owner);
+    Publisher<AttachmentMetadata> storeAttachmentForOwner(ContentType contentType, InputStream attachmentContent, Username owner);
 
     List<MessageAttachmentMetadata> storeAttachmentsForMessage(Collection<ParsedAttachment> attachments, MessageId ownerMessageId) throws MailboxException;
 
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/MessageParser.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/MessageParser.java
index b8fc5ba..328a98d 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/MessageParser.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/MessageParser.java
@@ -28,6 +28,7 @@ import java.util.Optional;
 import java.util.stream.Stream;
 
 import org.apache.james.mailbox.model.Cid;
+import org.apache.james.mailbox.model.ContentType;
 import org.apache.james.mailbox.model.ParsedAttachment;
 import org.apache.james.mime4j.codec.DecodeMonitor;
 import org.apache.james.mime4j.dom.Body;
@@ -55,7 +56,7 @@ public class MessageParser {
     private static final String CONTENT_TYPE = "Content-Type";
     private static final String CONTENT_ID = "Content-ID";
     private static final String CONTENT_DISPOSITION = "Content-Disposition";
-    private static final String DEFAULT_CONTENT_TYPE = "application/octet-stream";
+    private static final ContentType DEFAULT_CONTENT_TYPE = ContentType.of("application/octet-stream");
     private static final List<String> ATTACHMENT_CONTENT_DISPOSITIONS = ImmutableList.of(
             ContentDispositionField.DISPOSITION_TYPE_ATTACHMENT.toLowerCase(Locale.US),
             ContentDispositionField.DISPOSITION_TYPE_INLINE.toLowerCase(Locale.US));
@@ -122,8 +123,9 @@ public class MessageParser {
     private ParsedAttachment retrieveAttachment(Entity entity) throws IOException {
         Optional<ContentTypeField> contentTypeField = getContentTypeField(entity);
         Optional<ContentDispositionField> contentDispositionField = getContentDispositionField(entity);
-        Optional<String> contentType = contentTypeField.map(ContentTypeField::getBody)
-            .filter(string -> !string.isEmpty());
+        Optional<ContentType> contentType = contentTypeField.map(ContentTypeField::getBody)
+            .filter(string -> !string.isEmpty())
+            .map(ContentType::of);
         Optional<String> name = name(contentTypeField, contentDispositionField);
         Optional<Cid> cid = cid(readHeader(entity, CONTENT_ID, ContentIdField.class));
         boolean isInline = isInline(readHeader(entity, CONTENT_DISPOSITION, ContentDispositionField.class)) && cid.isPresent();
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/MessageSearches.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/MessageSearches.java
index 583ec25..690fcbf 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/MessageSearches.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/MessageSearches.java
@@ -267,7 +267,8 @@ public class MessageSearches implements Iterable<SimpleMessageSearchIndex.Search
             return textExtractor
                     .extractContent(
                         rawData,
-                        attachment.getType())
+                        // todo we likely want only the media type here
+                        attachment.getType().asString())
                     .getTextualContent()
                     .stream();
         } catch (Exception e) {
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreBlobManagerTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreBlobManagerTest.java
index 3390947..49f929e 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreBlobManagerTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreBlobManagerTest.java
@@ -43,6 +43,7 @@ import org.apache.james.mailbox.model.AttachmentMetadata;
 import org.apache.james.mailbox.model.Blob;
 import org.apache.james.mailbox.model.BlobId;
 import org.apache.james.mailbox.model.Content;
+import org.apache.james.mailbox.model.ContentType;
 import org.apache.james.mailbox.model.FetchGroup;
 import org.apache.james.mailbox.model.MessageResult;
 import org.apache.james.mailbox.model.TestMessageId;
@@ -57,7 +58,7 @@ import com.google.common.collect.ImmutableList;
 class StoreBlobManagerTest {
     static final String ID = "abc";
     static final AttachmentId ATTACHMENT_ID = AttachmentId.from(ID);
-    static final String CONTENT_TYPE = "text/plain";
+    static final ContentType CONTENT_TYPE = ContentType.of("text/plain");
     static final byte[] BYTES = "abc".getBytes(StandardCharsets.UTF_8);
     static final TestMessageId MESSAGE_ID = TestMessageId.of(125);
     static final BlobId BLOB_ID_ATTACHMENT = BlobId.fromString(ID);
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/AttachmentMapperTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/AttachmentMapperTest.java
index 0bf9af7..89c9c9c 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/AttachmentMapperTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/AttachmentMapperTest.java
@@ -31,6 +31,7 @@ import org.apache.james.core.Username;
 import org.apache.james.mailbox.exception.AttachmentNotFoundException;
 import org.apache.james.mailbox.model.AttachmentId;
 import org.apache.james.mailbox.model.AttachmentMetadata;
+import org.apache.james.mailbox.model.ContentType;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.ParsedAttachment;
 import org.apache.james.mailbox.store.mail.AttachmentMapper;
@@ -72,7 +73,7 @@ public abstract class AttachmentMapperTest {
 
     @Test
     void storeAttachmentForOwnerShouldReturnSuppliedInformation() throws Exception {
-        String content = "content";
+        ContentType content = ContentType.of("content");
         byte[] bytes = "payload".getBytes(StandardCharsets.UTF_8);
 
         AttachmentMetadata stored = Mono.from(attachmentMapper.storeAttachmentForOwner(content, new ByteArrayInputStream(bytes), OWNER)).block();
@@ -85,7 +86,7 @@ public abstract class AttachmentMapperTest {
 
     @Test
     void getAttachmentShouldReturnTheAttachmentWhenReferenced() throws Exception {
-        String content = "content";
+        ContentType content = ContentType.of("content");
         byte[] bytes = "payload".getBytes(StandardCharsets.UTF_8);
 
         AttachmentMetadata stored = Mono.from(attachmentMapper.storeAttachmentForOwner(content, new ByteArrayInputStream(bytes), OWNER)).block();
@@ -101,7 +102,7 @@ public abstract class AttachmentMapperTest {
 
     @Test
     void loadAttachmentContentShouldReturnStoredContent() throws Exception {
-        String content = "content";
+        ContentType content = ContentType.of("content");
         byte[] bytes = "payload".getBytes(StandardCharsets.UTF_8);
 
         AttachmentMetadata stored = Mono.from(attachmentMapper.storeAttachmentForOwner(content, new ByteArrayInputStream(bytes), OWNER)).block();
@@ -126,10 +127,10 @@ public abstract class AttachmentMapperTest {
     @Test
     void getAttachmentsShouldReturnTheAttachmentsWhenSome() {
         //Given
-        String content1 = "content";
+        ContentType content1 = ContentType.of("content");
         byte[] bytes1 = "payload".getBytes(StandardCharsets.UTF_8);
         AttachmentMetadata stored1 = Mono.from(attachmentMapper.storeAttachmentForOwner(content1, new ByteArrayInputStream(bytes1), OWNER)).block();
-        String content2 = "content";
+        ContentType content2 = ContentType.of("content");
         byte[] bytes2 = "payload".getBytes(StandardCharsets.UTF_8);
         AttachmentMetadata stored2 = Mono.from(attachmentMapper.storeAttachmentForOwner(content2, new ByteArrayInputStream(bytes2), OWNER)).block();
 
@@ -146,7 +147,7 @@ public abstract class AttachmentMapperTest {
 
     @Test
     void getOwnerMessageIdsShouldReturnEmptyWhenStoredWithoutMessageId() throws Exception {
-        String content = "content";
+        ContentType content = ContentType.of("content");
         byte[] bytes = "payload".getBytes(StandardCharsets.UTF_8);
         AttachmentMetadata stored = Mono.from(attachmentMapper.storeAttachmentForOwner(content, new ByteArrayInputStream(bytes), OWNER)).block();
 
@@ -190,7 +191,7 @@ public abstract class AttachmentMapperTest {
 
     @Test
     void getOwnersShouldBeRetrievedWhenExplicitlySpecified() throws Exception {
-        String content = "content";
+        ContentType content = ContentType.of("content");
         byte[] bytes = "payload".getBytes(StandardCharsets.UTF_8);
         AttachmentMetadata stored = Mono.from(attachmentMapper.storeAttachmentForOwner(content, new ByteArrayInputStream(bytes), OWNER)).block();
 
@@ -201,10 +202,10 @@ public abstract class AttachmentMapperTest {
 
     @Test
     void getOwnersShouldNotReturnUnrelatedOwners() throws Exception {
-        String content = "content";
+        ContentType content = ContentType.of("content");
         byte[] bytes = "payload".getBytes(StandardCharsets.UTF_8);
         AttachmentMetadata stored = Mono.from(attachmentMapper.storeAttachmentForOwner(content, new ByteArrayInputStream(bytes), OWNER)).block();
-        String content2 = "content";
+        ContentType content2 = ContentType.of("content");
         byte[] bytes2 = "payload".getBytes(StandardCharsets.UTF_8);
         AttachmentMetadata stored2 = Mono.from(attachmentMapper.storeAttachmentForOwner(content2, new ByteArrayInputStream(bytes2), ADDITIONAL_OWNER)).block();
 
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/impl/MessageParserTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/impl/MessageParserTest.java
index 3f29cee..0bf210a 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/impl/MessageParserTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/impl/MessageParserTest.java
@@ -26,6 +26,7 @@ import java.util.List;
 import java.util.Optional;
 
 import org.apache.james.mailbox.model.Cid;
+import org.apache.james.mailbox.model.ContentType;
 import org.apache.james.mailbox.model.ParsedAttachment;
 import org.apache.james.mdn.MDN;
 import org.apache.james.mdn.MDNReport;
@@ -90,7 +91,8 @@ class MessageParserTest {
         List<ParsedAttachment> attachments = testee.retrieveAttachments(ClassLoader.getSystemResourceAsStream("eml/oneAttachmentWithoutContentType.eml"));
 
         assertThat(attachments).hasSize(1);
-        assertThat(attachments.get(0).getContentType()).isEqualTo("application/octet-stream");
+        assertThat(attachments.get(0).getContentType())
+            .isEqualTo(ContentType.of("application/octet-stream"));
     }
 
     @Test
@@ -98,7 +100,8 @@ class MessageParserTest {
         List<ParsedAttachment> attachments = testee.retrieveAttachments(ClassLoader.getSystemResourceAsStream("eml/oneAttachmentWithEmptyContentType.eml"));
 
         assertThat(attachments).hasSize(1);
-        assertThat(attachments.get(0).getContentType()).isEqualTo("application/octet-stream");
+        assertThat(attachments.get(0).getContentType())
+            .isEqualTo(ContentType.of("application/octet-stream"));
     }
 
     @Test
@@ -106,7 +109,8 @@ class MessageParserTest {
         List<ParsedAttachment> attachments = testee.retrieveAttachments(ClassLoader.getSystemResourceAsStream("eml/oneAttachmentAndSomeTextInlined.eml"));
 
         assertThat(attachments).hasSize(1);
-        assertThat(attachments.get(0).getContentType()).isEqualTo("application/octet-stream;\tname=\"exploits_of_a_mom.png\"");
+        assertThat(attachments.get(0).getContentType())
+            .isEqualTo(ContentType.of("application/octet-stream;\tname=\"exploits_of_a_mom.png\""));
     }
 
     @Test
@@ -128,7 +132,8 @@ class MessageParserTest {
         List<ParsedAttachment> attachments = testee.retrieveAttachments(ClassLoader.getSystemResourceAsStream("eml/oneAttachmentWithSimpleContentType.eml"));
 
         assertThat(attachments).hasSize(1);
-        assertThat(attachments.get(0).getContentType()).isEqualTo("application/octet-stream");
+        assertThat(attachments.get(0).getContentType())
+            .isEqualTo(ContentType.of("application/octet-stream"));
     }
 
     @Test
@@ -270,7 +275,8 @@ class MessageParserTest {
 
         assertThat(attachments).hasSize(1)
             .first()
-            .satisfies(attachment -> assertThat(attachment.getContentType()).isEqualTo("text/calendar; charset=\"iso-8859-1\"; method=COUNTER"));
+            .satisfies(attachment -> assertThat(attachment.getContentType())
+                .isEqualTo(ContentType.of("text/calendar; charset=\"iso-8859-1\"; method=COUNTER")));
     }
 
     @Test
@@ -280,8 +286,8 @@ class MessageParserTest {
 
         assertThat(attachments).hasSize(2)
             .extracting(ParsedAttachment::getContentType)
-            .containsOnly("text/calendar; charset=\"iso-8859-1\"; method=COUNTER",
-                "text/calendar; charset=\"iso-4444-5\"; method=COUNTER");
+            .containsOnly(ContentType.of("text/calendar; charset=\"iso-8859-1\"; method=COUNTER"),
+                ContentType.of("text/calendar; charset=\"iso-4444-5\"; method=COUNTER"));
     }
 
     @Test
@@ -292,7 +298,7 @@ class MessageParserTest {
         assertThat(attachments)
             .hasSize(1)
             .extracting(ParsedAttachment::getContentType)
-            .containsExactly("text/calendar; charset=\"utf-8\"; method=COUNTER");
+            .containsExactly(ContentType.of("text/calendar; charset=\"utf-8\"; method=COUNTER"));
     }
 
     @Test
@@ -328,6 +334,6 @@ class MessageParserTest {
 
         List<ParsedAttachment> result = testee.retrieveAttachments(new ByteArrayInputStream(DefaultMessageWriter.asBytes(message)));
         assertThat(result).hasSize(1)
-            .allMatch(attachment -> attachment.getContentType().equals("message/disposition-notification; charset=UTF-8"));
+            .allMatch(attachment -> attachment.getContentType().equals(ContentType.of("message/disposition-notification; charset=UTF-8")));
     }
 }
diff --git a/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/SetMessagesMethodTest.java b/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/SetMessagesMethodTest.java
index f17efc6..b70eab1 100644
--- a/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/SetMessagesMethodTest.java
+++ b/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/SetMessagesMethodTest.java
@@ -2109,7 +2109,7 @@ public abstract class SetMessagesMethodTest {
             "        \"keywords\": {\"$Draft\": true}," +
             "        \"attachments\": [" +
             "                {\"blobId\" : \"" + uploadedAttachment.getAttachmentId().getId() + "\", " +
-            "                 \"type\" : \"" + uploadedAttachment.getType() + "\"," +
+            "                 \"type\" : \"" + uploadedAttachment.getType().asString() + "\"," +
             "                 \"size\" : " + uploadedAttachment.getSize() + "}" +
             "             ]," +
             "        \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
@@ -3982,10 +3982,10 @@ public abstract class SetMessagesMethodTest {
             "        \"mailboxIds\": [\"" + outboxId + "\"], " +
             "        \"attachments\": [" +
             "               {\"blobId\" : \"" + uploadedAttachment1.getAttachmentId().getId() + "\", " +
-            "               \"type\" : \"" + uploadedAttachment1.getType() + "\", " +
+            "               \"type\" : \"" + uploadedAttachment1.getType().asString() + "\", " +
             "               \"size\" : " + uploadedAttachment1.getSize() + "}," +
             "               {\"blobId\" : \"" + uploadedAttachment2.getAttachmentId().getId() + "\", " +
-            "               \"type\" : \"" + uploadedAttachment2.getType() + "\", " +
+            "               \"type\" : \"" + uploadedAttachment2.getType().asString() + "\", " +
             "               \"size\" : " + uploadedAttachment2.getSize() + ", " +
             "               \"cid\" : \"123456789\", " +
             "               \"isInline\" : true }" +
@@ -4051,10 +4051,10 @@ public abstract class SetMessagesMethodTest {
             "        \"mailboxIds\": [\"" + outboxId + "\"], " +
             "        \"attachments\": [" +
             "               {\"blobId\" : \"" + uploadedAttachment1.getAttachmentId().getId() + "\", " +
-            "               \"type\" : \"" + uploadedAttachment1.getType() + "\", " +
+            "               \"type\" : \"" + uploadedAttachment1.getType().asString() + "\", " +
             "               \"size\" : " + uploadedAttachment1.getSize() + "}," +
             "               {\"blobId\" : \"" + uploadedAttachment2.getAttachmentId().getId() + "\", " +
-            "               \"type\" : \"" + uploadedAttachment2.getType() + "\", " +
+            "               \"type\" : \"" + uploadedAttachment2.getType().asString() + "\", " +
             "               \"size\" : " + uploadedAttachment2.getSize() + ", " +
             "               \"cid\" : \"123456789\", " +
             "               \"isInline\" : true }" +
@@ -4127,21 +4127,21 @@ public abstract class SetMessagesMethodTest {
             "          [" +
             "            {" +
             "              \"blobId\" : \"" + uploadedAttachment1.getAttachmentId().getId() + "\", " +
-            "              \"type\" : \"" + uploadedAttachment1.getType() + "\", " +
+            "              \"type\" : \"" + uploadedAttachment1.getType().asString() + "\", " +
             "              \"size\" : " + uploadedAttachment1.getSize() + "," +
             "              \"name\" : \"ديناصور.png\", " +
             "              \"isInline\" : false" +
             "            }," +
             "            {" +
             "              \"blobId\" : \"" + uploadedAttachment2.getAttachmentId().getId() + "\", " +
-            "              \"type\" : \"" + uploadedAttachment2.getType() + "\", " +
+            "              \"type\" : \"" + uploadedAttachment2.getType().asString() + "\", " +
             "              \"size\" : " + uploadedAttachment2.getSize() + "," +
             "              \"name\" : \"эволюционировать.png\", " +
             "              \"isInline\" : false" +
             "            }," +
             "            {" +
             "              \"blobId\" : \"" + uploadedAttachment3.getAttachmentId().getId() + "\", " +
-            "              \"type\" : \"" + uploadedAttachment3.getType() + "\", " +
+            "              \"type\" : \"" + uploadedAttachment3.getType().asString() + "\", " +
             "              \"size\" : " + uploadedAttachment3.getSize() + "," +
             "              \"name\" : \"进化还是不.png\"," +
             "              \"isInline\" : false" +
@@ -4204,21 +4204,21 @@ public abstract class SetMessagesMethodTest {
             "          [" +
             "            {" +
             "              \"blobId\" : \"" + uploadedAttachment1.getAttachmentId().getId() + "\", " +
-            "              \"type\" : \"" + uploadedAttachment1.getType() + "\", " +
+            "              \"type\" : \"" + uploadedAttachment1.getType().asString() + "\", " +
             "              \"size\" : " + uploadedAttachment1.getSize() + "," +
             "              \"name\" : \"ديناصور.png\", " +
             "              \"isInline\" : false" +
             "            }," +
             "            {" +
             "              \"blobId\" : \"" + uploadedAttachment2.getAttachmentId().getId() + "\", " +
-            "              \"type\" : \"" + uploadedAttachment2.getType() + "\", " +
+            "              \"type\" : \"" + uploadedAttachment2.getType().asString() + "\", " +
             "              \"size\" : " + uploadedAttachment2.getSize() + "," +
             "              \"name\" : \"эволюционировать.png\", " +
             "              \"isInline\" : false" +
             "            }," +
             "            {" +
             "              \"blobId\" : \"" + uploadedAttachment3.getAttachmentId().getId() + "\", " +
-            "              \"type\" : \"" + uploadedAttachment3.getType() + "\", " +
+            "              \"type\" : \"" + uploadedAttachment3.getType().asString() + "\", " +
             "              \"size\" : " + uploadedAttachment3.getSize() + "," +
             "              \"name\" : \"进化还是不.png\"," +
             "              \"isInline\" : false" +
@@ -4332,7 +4332,7 @@ public abstract class SetMessagesMethodTest {
             "        \"mailboxIds\": [\"" + outboxId + "\"], " +
             "        \"attachments\": [" +
             "               {\"blobId\" : \"" + uploadedAttachment.getAttachmentId().getId() + "\", " +
-            "               \"type\" : \"" + uploadedAttachment.getType() + "\", " +
+            "               \"type\" : \"" + uploadedAttachment.getType().asString() + "\", " +
             "               \"size\" : " + uploadedAttachment.getSize() + ", " +
             "               \"cid\" : \"123456789\", " +
             "               \"isInline\" : true }" +
@@ -4404,7 +4404,7 @@ public abstract class SetMessagesMethodTest {
             "        \"mailboxIds\": [\"" + outboxId + "\"], " +
             "        \"attachments\": [" +
             "               {\"blobId\" : \"" + uploadedAttachment.getAttachmentId().getId() + "\", " +
-            "               \"type\" : \"" + uploadedAttachment.getType() + "\", " +
+            "               \"type\" : \"" + uploadedAttachment.getType().asString() + "\", " +
             "               \"size\" : " + uploadedAttachment.getSize() + ", " +
             "               \"cid\" : \"123456789\", " +
             "               \"isInline\" : true }" +
@@ -4499,7 +4499,7 @@ public abstract class SetMessagesMethodTest {
             "        \"mailboxIds\": [\"" + outboxId + "\"], " +
             "        \"attachments\": [" +
             "               {\"blobId\" : \"" + uploadedAttachment.getAttachmentId().getId() + "\", " +
-            "               \"type\" : \"" + uploadedAttachment.getType() + "\", " +
+            "               \"type\" : \"" + uploadedAttachment.getType().asString() + "\", " +
             "               \"size\" : " + uploadedAttachment.getSize() + ", " +
             "               \"isInline\" : false }" +
             "           ]" +
@@ -4574,7 +4574,7 @@ public abstract class SetMessagesMethodTest {
             "        \"mailboxIds\": [\"" + outboxId + "\"], " +
             "        \"attachments\": [" +
             "               {\"blobId\" : \"" + uploadedAttachment.getAttachmentId().getId() + "\", " +
-            "               \"type\" : \"" + uploadedAttachment.getType() + "\", " +
+            "               \"type\" : \"" + uploadedAttachment.getType().asString() + "\", " +
             "               \"size\" : " + uploadedAttachment.getSize() + ", " +
             "               \"isInline\" : false }" +
             "           ]" +
@@ -4659,7 +4659,7 @@ public abstract class SetMessagesMethodTest {
             "        \"mailboxIds\": [\"" + outboxId + "\"], " +
             "        \"attachments\": [" +
             "               {\"blobId\" : \"" + uploadedAttachment.getAttachmentId().getId() + "\", " +
-            "               \"type\" : \"" + uploadedAttachment.getType() + "\", " +
+            "               \"type\" : \"" + uploadedAttachment.getType().asString() + "\", " +
             "               \"size\" : " + uploadedAttachment.getSize() + ", " +
             "               \"isInline\" : false }" +
             "           ]" +
@@ -5420,7 +5420,7 @@ public abstract class SetMessagesMethodTest {
                 "        \"mailboxIds\": [\"" + outboxId + "\"], " +
                 "        \"attachments\": [" +
                 "               {\"blobId\" : \"" + uploadedAttachment.getAttachmentId().getId() + "\", " +
-                "               \"type\" : \"" + uploadedAttachment.getType() + "\", " +
+                "               \"type\" : \"" + uploadedAttachment.getType().asString() + "\", " +
                 "               \"name\" : \"nonIndexableAttachment.html\", " +
                 "               \"size\" : " + uploadedAttachment.getSize() + "}" +
                 "           ]" +
@@ -5469,7 +5469,7 @@ public abstract class SetMessagesMethodTest {
                 "        \"mailboxIds\": [\"" + outboxId + "\"], " +
                 "        \"attachments\": [" +
                 "               {\"blobId\" : \"" + uploadedAttachment.getAttachmentId().getId() + "\", " +
-                "               \"type\" : \"" + uploadedAttachment.getType() + "\", " +
+                "               \"type\" : \"" + uploadedAttachment.getType().asString() + "\", " +
                 "               \"name\" : \"nonIndexableAttachment.html\", " +
                 "               \"size\" : " + uploadedAttachment.getSize() + "}" +
                 "           ]" +
@@ -5528,7 +5528,7 @@ public abstract class SetMessagesMethodTest {
                 "        \"mailboxIds\": [\"" + outboxId + "\"], " +
                 "        \"attachments\": [" +
                 "               {\"blobId\" : \"" + uploadedAttachment.getAttachmentId().getId() + "\", " +
-                "               \"type\" : \"" + uploadedAttachment.getType() + "\", " +
+                "               \"type\" : \"" + uploadedAttachment.getType().asString() + "\", " +
                 "               \"name\" : \"nonIndexableAttachment.html\", " +
                 "               \"size\" : " + uploadedAttachment.getSize() + "}" +
                 "           ]" +
@@ -5582,10 +5582,10 @@ public abstract class SetMessagesMethodTest {
             "        \"mailboxIds\": [\"" + outboxId + "\"], " +
             "        \"attachments\": [" +
             "               {\"blobId\" : \"" + uploadedAttachment1.getAttachmentId().getId() + "\", " +
-            "               \"type\" : \"" + uploadedAttachment1.getType() + "\", " +
+            "               \"type\" : \"" + uploadedAttachment1.getType().asString() + "\", " +
             "               \"size\" : " + uploadedAttachment1.getSize() + "}," +
             "               {\"blobId\" : \"" + uploadedAttachment2.getAttachmentId().getId() + "\", " +
-            "               \"type\" : \"" + uploadedAttachment2.getType() + "\", " +
+            "               \"type\" : \"" + uploadedAttachment2.getType().asString() + "\", " +
             "               \"size\" : " + uploadedAttachment2.getSize() + ", " +
             "               \"isInline\" : true }" +
             "           ]" +
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/json/ObjectMapperFactory.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/json/ObjectMapperFactory.java
index 345a80b..cb923f5 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/json/ObjectMapperFactory.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/json/ObjectMapperFactory.java
@@ -28,6 +28,7 @@ import javax.inject.Inject;
 import org.apache.james.core.Username;
 import org.apache.james.jmap.draft.model.mailbox.Rights;
 import org.apache.james.mailbox.Role;
+import org.apache.james.mailbox.model.ContentType;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mdn.action.mode.DispositionActionMode;
@@ -87,10 +88,15 @@ public class ObjectMapperFactory {
         mailboxIdModule.addDeserializer(DispositionSendingMode.class, new MDNSendingModeDeserializer());
         mailboxIdModule.addDeserializer(DispositionType.class, new MDNTypeDeserializer());
 
+        SimpleModule contentTypeModule = new SimpleModule();
+        contentTypeModule.addDeserializer(ContentType.class, new ContentTypeDeserializer());
+        contentTypeModule.addSerializer(ContentType.class, new ContentTypeSerializer());
+
         mailboxIdModule.setMixInAnnotation(Role.class, RoleMixIn.class);
 
         jacksonModules = JACKSON_BASE_MODULES.add(mailboxIdModule)
             .add(mdnModule)
+            .add(contentTypeModule)
             .build();
     }
 
@@ -134,6 +140,21 @@ public class ObjectMapperFactory {
         }
     }
 
+    public static class ContentTypeDeserializer extends JsonDeserializer<ContentType> {
+        @Override
+        public ContentType deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
+            String value = jsonParser.getValueAsString();
+            return ContentType.of(value);
+        }
+    }
+
+    public static class ContentTypeSerializer extends JsonSerializer<ContentType> {
+        @Override
+        public void serialize(ContentType value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
+            gen.writeString(value.asString());
+        }
+    }
+
     public static class MDNTypeDeserializer extends JsonDeserializer<DispositionType> {
         private static final ImmutableList<String> ALLOWED_VALUES = Arrays.stream(DispositionType.values())
             .map(DispositionType::getValue)
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MIMEMessageConverter.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MIMEMessageConverter.java
index 4864f05..a7c9dc7 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MIMEMessageConverter.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/MIMEMessageConverter.java
@@ -328,7 +328,8 @@ public class MIMEMessageConverter {
     }
 
     private ContentTypeField contentTypeField(MessageAttachmentMetadata att) {
-        String type = att.getAttachment().getType();
+        // todo mailbox pojo should be able to expose itself as a mime4j object
+        String type = att.getAttachment().getType().asString();
         ContentTypeField typeAsField = Fields.contentType(type);
         if (att.getName().isPresent()) {
             return Fields.contentType(typeAsField.getMimeType(),
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/Attachment.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/Attachment.java
index 271e442..1ea228f 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/Attachment.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/Attachment.java
@@ -22,12 +22,13 @@ package org.apache.james.jmap.draft.model;
 import java.util.Objects;
 import java.util.Optional;
 
+import org.apache.james.mailbox.model.ContentType;
+
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Preconditions;
-import com.google.common.base.Strings;
 
 @JsonDeserialize(builder = Attachment.Builder.class)
 public class Attachment {
@@ -39,7 +40,7 @@ public class Attachment {
     @JsonPOJOBuilder(withPrefix = "")
     public static class Builder {
         private BlobId blobId;
-        private String type;
+        private ContentType type;
         private String name;
         private Number size;
         private String cid;
@@ -52,7 +53,13 @@ public class Attachment {
             return this;
         }
 
+        @JsonDeserialize
         public Builder type(String type) {
+            this.type = ContentType.of(type);
+            return this;
+        }
+
+        public Builder type(ContentType type) {
             this.type = type;
             return this;
         }
@@ -104,14 +111,13 @@ public class Attachment {
 
         public Attachment build() {
             Preconditions.checkState(blobId != null, "'blobId' is mandatory");
-            Preconditions.checkState(!Strings.isNullOrEmpty(type), "'type' is mandatory");
             Preconditions.checkState(size != null, "'size' is mandatory");
             return new Attachment(blobId, type, Optional.ofNullable(name), size, Optional.ofNullable(cid), isInline, Optional.ofNullable(width), Optional.ofNullable(height));
         }
     }
 
     private final BlobId blobId;
-    private final String type;
+    private final ContentType type;
     private final Optional<String> name;
     private final Number size;
     private final Optional<String> cid;
@@ -119,7 +125,7 @@ public class Attachment {
     private final Optional<Number> width;
     private final Optional<Number> height;
 
-    @VisibleForTesting Attachment(BlobId blobId, String type, Optional<String> name, Number size, Optional<String> cid, boolean isInline, Optional<Number> width, Optional<Number> height) {
+    @VisibleForTesting Attachment(BlobId blobId, ContentType type, Optional<String> name, Number size, Optional<String> cid, boolean isInline, Optional<Number> width, Optional<Number> height) {
         this.blobId = blobId;
         this.type = type;
         this.name = name;
@@ -134,7 +140,7 @@ public class Attachment {
         return blobId;
     }
 
-    public String getType() {
+    public ContentType getType() {
         return type;
     }
 
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DownloadRoutes.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DownloadRoutes.java
index ddabcf7..7eb041a 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DownloadRoutes.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DownloadRoutes.java
@@ -52,6 +52,7 @@ import org.apache.james.mailbox.exception.BlobNotFoundException;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.Blob;
 import org.apache.james.mailbox.model.BlobId;
+import org.apache.james.mailbox.model.ContentType;
 import org.apache.james.metrics.api.MetricFactory;
 import org.apache.james.mime4j.codec.EncoderUtil;
 import org.apache.james.mime4j.codec.EncoderUtil.Usage;
@@ -222,10 +223,10 @@ public class DownloadRoutes implements JMAPRoutes {
         }
     }
 
-    private Mono<Void> downloadBlob(Optional<String> optionalName, HttpServerResponse response, long blobSize, String blobContentType, InputStream stream) {
+    private Mono<Void> downloadBlob(Optional<String> optionalName, HttpServerResponse response, long blobSize, ContentType blobContentType, InputStream stream) {
         return addContentDispositionHeader(optionalName, response)
             .header("Content-Length", String.valueOf(blobSize))
-            .header(CONTENT_TYPE, blobContentType)
+            .header(CONTENT_TYPE, blobContentType.asString())
             .status(OK)
             .send(ReactorUtils.toChunks(stream, BUFFER_SIZE)
                 .map(Unpooled::wrappedBuffer)
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/UploadRoutes.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/UploadRoutes.java
index 95e6d62..cc530b0 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/UploadRoutes.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/UploadRoutes.java
@@ -43,6 +43,7 @@ import org.apache.james.jmap.draft.model.UploadResponse;
 import org.apache.james.jmap.exceptions.UnauthorizedException;
 import org.apache.james.mailbox.AttachmentManager;
 import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.model.ContentType;
 import org.apache.james.metrics.api.MetricFactory;
 import org.apache.james.util.ReactorUtils;
 import org.slf4j.Logger;
@@ -98,7 +99,7 @@ public class UploadRoutes implements JMAPRoutes {
             return response.status(BAD_REQUEST).send();
         } else {
             return authenticator.authenticate(request)
-                .flatMap(session -> post(request, response, contentType, session)
+                .flatMap(session -> post(request, response, ContentType.of(contentType), session)
                     .subscriberContext(jmapAuthContext(session)))
                 .onErrorResume(CancelledUploadException.class, e -> handleCanceledUpload(response, e))
                 .onErrorResume(BadRequestException.class, e -> handleBadRequest(response, e))
@@ -111,13 +112,13 @@ public class UploadRoutes implements JMAPRoutes {
         }
     }
 
-    private Mono<Void> post(HttpServerRequest request, HttpServerResponse response, String contentType, MailboxSession session) {
+    private Mono<Void> post(HttpServerRequest request, HttpServerResponse response, ContentType contentType, MailboxSession session) {
         InputStream content = ReactorUtils.toInputStream(request.receive().asByteBuffer().subscribeOn(Schedulers.elastic()));
         return Mono.from(metricFactory.runPublishingTimerMetric("JMAP-upload-post",
             handle(contentType, content, session, response)));
     }
 
-    private Mono<Void> handle(String contentType, InputStream content, MailboxSession mailboxSession, HttpServerResponse response) {
+    private Mono<Void> handle(ContentType contentType, InputStream content, MailboxSession mailboxSession, HttpServerResponse response) {
         return uploadContent(contentType, content, mailboxSession)
             .flatMap(storedContent -> {
                 try {
@@ -131,11 +132,11 @@ public class UploadRoutes implements JMAPRoutes {
             });
     }
 
-    private Mono<UploadResponse> uploadContent(String contentType, InputStream inputStream, MailboxSession session) {
+    private Mono<UploadResponse> uploadContent(ContentType contentType, InputStream inputStream, MailboxSession session) {
         return Mono.from(attachmentManager.storeAttachment(contentType, inputStream, session))
             .map(attachment -> UploadResponse.builder()
                 .blobId(attachment.getAttachmentId().getId())
-                .type(attachment.getType())
+                .type(attachment.getType().asString())
                 .size(attachment.getSize())
                 .build())
             .onErrorMap(e -> e.getCause() instanceof EOFException, any -> new CancelledUploadException())
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/AttachmentTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/AttachmentTest.java
index 9e7e122..b81c986 100644
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/AttachmentTest.java
+++ b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/AttachmentTest.java
@@ -22,6 +22,7 @@ import static org.assertj.core.api.Assertions.assertThat;
 
 import java.util.Optional;
 
+import org.apache.james.mailbox.model.ContentType;
 import org.junit.Test;
 
 public class AttachmentTest {
@@ -46,7 +47,7 @@ public class AttachmentTest {
         Attachment.builder().blobId(BlobId.of("blobId")).type("type").name("name").build();
     }
     
-    @Test(expected = IllegalStateException.class)
+    @Test(expected = IllegalArgumentException.class)
     public void buildShouldThrowWhenTypeIsEmpty() {
         Attachment.builder().blobId(BlobId.of("blobId")).type("").name("name").size(123).build();
     }
@@ -54,7 +55,7 @@ public class AttachmentTest {
     @Test
     public void buildShouldWorkWhenMandatoryFieldsArePresent() {
         Number attachmentSize = Number.fromLong(123);
-        Attachment expected = new Attachment(BlobId.of("blobId"), "type", Optional.empty(), attachmentSize, Optional.empty(), false, Optional.empty(), Optional.empty());
+        Attachment expected = new Attachment(BlobId.of("blobId"), ContentType.of("type"), Optional.empty(), attachmentSize, Optional.empty(), false, Optional.empty(), Optional.empty());
         Attachment tested = Attachment.builder()
             .blobId(BlobId.of("blobId"))
             .type("type")
@@ -68,7 +69,7 @@ public class AttachmentTest {
         Number attachmentSize = Number.fromLong(123);
         Optional<Number> attachmentWidth = Optional.of(Number.fromLong(456L));
         Optional<Number> attachmentHeight = Optional.of(Number.fromLong(789L));
-        Attachment expected = new Attachment(BlobId.of("blobId"), "type", Optional.of("name"), attachmentSize, Optional.of("cid"), true,
+        Attachment expected = new Attachment(BlobId.of("blobId"), ContentType.of("type"), Optional.of("name"), attachmentSize, Optional.of("cid"), true,
             attachmentWidth, attachmentHeight);
         Attachment tested = Attachment.builder()
             .blobId(BlobId.of("blobId"))


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