blob: a5b43696c029d5cbc78d9e54d5a20fb0cd6d0be4 [file] [log] [blame]
/****************************************************************
* 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.jmap.json;
import java.io.IOException;
import java.util.Arrays;
import java.util.Set;
import jakarta.inject.Inject;
import org.apache.james.core.Username;
import org.apache.james.jmap.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;
import org.apache.james.mdn.sending.mode.DispositionSendingMode;
import org.apache.james.mdn.type.DispositionType;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.guava.GuavaModule;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
public class ObjectMapperFactory {
private static final ImmutableSet.Builder<Module> JACKSON_BASE_MODULES = ImmutableSet.<Module>builder().add(
new Jdk8Module(),
new JavaTimeModule(),
new GuavaModule());
private final Set<Module> jacksonModules;
@Inject
public ObjectMapperFactory(MailboxId.Factory mailboxIdFactory, MessageId.Factory messageIdFactory) {
SimpleModule mailboxIdModule = new SimpleModule();
mailboxIdModule.addDeserializer(MailboxId.class, new MailboxIdDeserializer(mailboxIdFactory));
mailboxIdModule.addSerializer(MailboxId.class, new MailboxIdSerializer());
mailboxIdModule.addKeyDeserializer(MailboxId.class, new MailboxIdKeyDeserializer(mailboxIdFactory));
mailboxIdModule.addKeySerializer(MailboxId.class, new MailboxIdKeySerializer());
mailboxIdModule.addDeserializer(MessageId.class, new MessageIdDeserializer(messageIdFactory));
mailboxIdModule.addSerializer(MessageId.class, new MessageIdSerializer());
mailboxIdModule.addKeyDeserializer(MessageId.class, new MessageIdKeyDeserializer(messageIdFactory));
mailboxIdModule.addKeySerializer(MessageId.class, new MessageIdKeySerializer());
mailboxIdModule.addSerializer(Username.class, new UsernameSerializer());
mailboxIdModule.addDeserializer(Username.class, new UsernameDeserializer());
mailboxIdModule.addKeyDeserializer(Username.class, new UsernameKeyDeserializer());
mailboxIdModule.addKeySerializer(Username.class, new UsernameKeySerializer());
mailboxIdModule.addDeserializer(Rights.Right.class, new RightDeserializer());
SimpleModule mdnModule = new SimpleModule();
mailboxIdModule.addDeserializer(DispositionActionMode.class, new MDNActionModeDeserializer());
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();
}
public ObjectMapper forParsing() {
return new ObjectMapper()
.registerModules(jacksonModules)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
public ObjectMapper forWriting() {
return new ObjectMapper()
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.registerModules(jacksonModules);
}
public static class MDNActionModeDeserializer extends JsonDeserializer<DispositionActionMode> {
private static final ImmutableList<String> ALLOWED_VALUES = Arrays.stream(DispositionActionMode.values())
.map(DispositionActionMode::getValue)
.collect(ImmutableList.toImmutableList());
@Override
public DispositionActionMode deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
String value = jsonParser.getValueAsString();
return DispositionActionMode.fromString(value)
.orElseThrow(() -> new IllegalStateException(
String.format("Unrecognized MDN Disposition action mode %s. Should be one of %s", value, ALLOWED_VALUES)));
}
}
public static class MDNSendingModeDeserializer extends JsonDeserializer<DispositionSendingMode> {
private static final ImmutableList<String> ALLOWED_VALUES = Arrays.stream(DispositionSendingMode.values())
.map(DispositionSendingMode::getValue)
.collect(ImmutableList.toImmutableList());
@Override
public DispositionSendingMode deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
String value = jsonParser.getValueAsString();
return DispositionSendingMode.fromString(value)
.orElseThrow(() -> new IllegalStateException(
String.format("Unrecognized MDN Disposition sending mode %s. Should be one of %s", value, ALLOWED_VALUES)));
}
}
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)
.collect(ImmutableList.toImmutableList());
@Override
public DispositionType deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
String value = jsonParser.getValueAsString();
return DispositionType.fromString(value)
.orElseThrow(() -> new IllegalStateException(
String.format("Unrecognized MDN Disposition type %s. Should be one of %s", value, ALLOWED_VALUES)));
}
}
public static class MailboxIdDeserializer extends JsonDeserializer<MailboxId> {
private MailboxId.Factory factory;
public MailboxIdDeserializer(MailboxId.Factory factory) {
this.factory = factory;
}
@Override
public MailboxId deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
return factory.fromString(p.getValueAsString());
}
}
public static class MailboxIdSerializer extends JsonSerializer<MailboxId> {
@Override
public void serialize(MailboxId value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
gen.writeString(value.serialize());
}
}
public static class UsernameSerializer extends JsonSerializer<Username> {
@Override
public void serialize(Username value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
gen.writeString(value.asString());
}
}
public static class UsernameKeySerializer extends JsonSerializer<Username> {
@Override
public void serialize(Username value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
gen.writeFieldName(value.asString());
}
}
public static class UsernameDeserializer extends JsonDeserializer<Username> {
@Override
public Username deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
return Username.of(p.getValueAsString());
}
}
public static class UsernameKeyDeserializer extends KeyDeserializer {
@Override
public Object deserializeKey(String key, DeserializationContext ctxt) {
return Username.of(key);
}
}
public static class RightDeserializer extends JsonDeserializer<Rights.Right> {
@Override
public Rights.Right deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
String nodeValue = p.getValueAsString();
Preconditions.checkArgument(nodeValue.length() == 1, "Rights should be represented as single value characters");
return Rights.Right.forChar(nodeValue.charAt(0));
}
}
public static class MailboxIdKeyDeserializer extends KeyDeserializer {
private MailboxId.Factory factory;
public MailboxIdKeyDeserializer(MailboxId.Factory factory) {
this.factory = factory;
}
@Override
public Object deserializeKey(String key, DeserializationContext ctxt) {
return factory.fromString(key);
}
}
public static class MailboxIdKeySerializer extends JsonSerializer<MailboxId> {
@Override
public void serialize(MailboxId value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
gen.writeFieldName(value.serialize());
}
}
public static class MessageIdDeserializer extends JsonDeserializer<MessageId> {
private MessageId.Factory factory;
public MessageIdDeserializer(MessageId.Factory factory) {
this.factory = factory;
}
@Override
public MessageId deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
return factory.fromString(p.getValueAsString());
}
}
public static class MessageIdKeyDeserializer extends KeyDeserializer {
private MessageId.Factory factory;
public MessageIdKeyDeserializer(MessageId.Factory factory) {
this.factory = factory;
}
@Override
public Object deserializeKey(String key, DeserializationContext ctxt) {
return factory.fromString(key);
}
}
public static class MessageIdKeySerializer extends JsonSerializer<MessageId> {
@Override
public void serialize(MessageId value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
gen.writeFieldName(value.serialize());
}
}
public static class MessageIdSerializer extends JsonSerializer<MessageId> {
@Override
public void serialize(MessageId value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
gen.writeString(value.serialize());
}
}
}