Skip to content

Commit 27f2845

Browse files
mbaechlerchibenwa
authored andcommitted
JAMES-2813 Split JsonGenericSerializer between conversion and serialization
1 parent 80bb186 commit 27f2845

File tree

6 files changed

+202
-47
lines changed

6 files changed

+202
-47
lines changed

event-sourcing/event-store-cassandra/src/main/java/org/apache/james/eventsourcing/eventstore/cassandra/JsonEventSerializer.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ public UnknownEventException(JsonGenericSerializer.UnknownTypeException original
5050

5151
@Inject
5252
public JsonEventSerializer(Set<EventDTOModule<?, ?>> modules) {
53-
jsonGenericSerializer = new JsonGenericSerializer(modules);
53+
//FIXME
54+
jsonGenericSerializer = new JsonGenericSerializer(modules, null);
5455
}
5556

5657
public JsonEventSerializer(EventDTOModule<?, ?>... modules) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/****************************************************************
2+
* Licensed to the Apache Software Foundation (ASF) under one *
3+
* or more contributor license agreements. See the NOTICE file *
4+
* distributed with this work for additional information *
5+
* regarding copyright ownership. The ASF licenses this file *
6+
* to you under the Apache License, Version 2.0 (the *
7+
* "License"); you may not use this file except in compliance *
8+
* with the License. You may obtain a copy of the License at *
9+
* *
10+
* http://www.apache.org/licenses/LICENSE-2.0 *
11+
* *
12+
* Unless required by applicable law or agreed to in writing, *
13+
* software distributed under the License is distributed on an *
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
15+
* KIND, either express or implied. See the License for the *
16+
* specific language governing permissions and limitations *
17+
* under the License. *
18+
****************************************************************/
19+
20+
package org.apache.james.json;
21+
22+
import java.util.Map;
23+
import java.util.Optional;
24+
import java.util.Set;
25+
import java.util.function.Function;
26+
27+
import com.github.steveash.guavate.Guavate;
28+
import com.google.common.collect.ImmutableSet;
29+
30+
public class DTOConverter<T, U extends DTO> {
31+
32+
private final Map<String, DTOModule<T, U>> typeToModule;
33+
private final Map<Class<? extends T>, DTOModule<T, U>> domainClassToModule;
34+
35+
@SafeVarargs
36+
public static <T, U extends DTO> DTOConverter<T, U> of(DTOModule<T, U>... modules) {
37+
return new DTOConverter<>(ImmutableSet.copyOf(modules));
38+
}
39+
40+
public DTOConverter(Set<DTOModule<T, U>> modules) {
41+
typeToModule = modules.stream()
42+
.collect(Guavate.toImmutableMap(
43+
DTOModule::getDomainObjectType,
44+
Function.identity()));
45+
46+
domainClassToModule = modules.stream()
47+
.collect(Guavate.toImmutableMap(
48+
DTOModule::getDomainObjectClass,
49+
Function.identity()));
50+
}
51+
52+
public Optional<U> convert(T domainObject) {
53+
return Optional
54+
.ofNullable(domainClassToModule.get(domainObject.getClass()))
55+
.map(module -> module.toDTO(domainObject));
56+
}
57+
58+
public Optional<T> convert(U dto) {
59+
String type = dto.getType();
60+
return Optional
61+
.ofNullable(typeToModule.get(type))
62+
.map(DTOModule::getToDomainObjectConverter)
63+
.map(convert -> convert.convert(dto));
64+
}
65+
}

json/src/main/java/org/apache/james/json/JsonGenericSerializer.java

+16-44
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,7 @@
2020
package org.apache.james.json;
2121

2222
import java.io.IOException;
23-
import java.util.Map;
24-
import java.util.Optional;
2523
import java.util.Set;
26-
import java.util.function.Function;
2724

2825
import com.fasterxml.jackson.annotation.JsonInclude;
2926
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -37,40 +34,10 @@
3734
import com.fasterxml.jackson.datatype.guava.GuavaModule;
3835
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
3936
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
40-
import com.github.steveash.guavate.Guavate;
4137
import com.google.common.collect.ImmutableSet;
4238

4339
public class JsonGenericSerializer<T, U extends DTO> {
4440

45-
private static class DTOConverter<T, U extends DTO> {
46-
47-
private final Map<String, DTOModule<T, U>> typeToModule;
48-
private final Map<Class<? extends T>, DTOModule<T, U>> domainClassToModule;
49-
50-
public DTOConverter(Set<DTOModule<T, U>> modules) {
51-
typeToModule = modules.stream()
52-
.collect(Guavate.toImmutableMap(
53-
DTOModule::getDomainObjectType,
54-
Function.identity()));
55-
56-
domainClassToModule = modules.stream()
57-
.collect(Guavate.toImmutableMap(
58-
DTOModule::getDomainObjectClass,
59-
Function.identity()));
60-
}
61-
62-
public Optional<U> convert(T domainObject) {
63-
return Optional.ofNullable(domainClassToModule.get(domainObject.getClass()))
64-
.map(module -> module.toDTO(domainObject));
65-
}
66-
67-
public Optional<T> convert(U dto) {
68-
String type = dto.getType();
69-
return Optional.ofNullable(typeToModule.get(type))
70-
.map(module -> module.getToDomainObjectConverter().convert(dto));
71-
}
72-
}
73-
7441
public static class InvalidTypeException extends RuntimeException {
7542
public InvalidTypeException(String message) {
7643
super(message);
@@ -92,21 +59,26 @@ public UnknownTypeException(String message) {
9259

9360
@SafeVarargs
9461
public static <T, U extends DTO> JsonGenericSerializer<T, U> of(DTOModule<T, U>... modules) {
95-
return new JsonGenericSerializer<>(ImmutableSet.copyOf(modules));
62+
return new JsonGenericSerializer<>(ImmutableSet.copyOf(modules), DTOConverter.of(modules));
63+
}
64+
65+
public JsonGenericSerializer(Set<DTOModule<T, U>> modules, DTOConverter<T, U> converter) {
66+
this.dtoConverter = converter;
67+
this.objectMapper = buildObjectMapper(modules);
9668
}
9769

98-
public JsonGenericSerializer(Set<DTOModule<T, U>> modules) {
99-
objectMapper = new ObjectMapper();
100-
objectMapper.registerModule(new Jdk8Module());
101-
objectMapper.registerModule(new JavaTimeModule());
102-
objectMapper.registerModule(new GuavaModule());
103-
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_ABSENT);
104-
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
105-
objectMapper.enable(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY);
70+
private ObjectMapper buildObjectMapper(Set<DTOModule<T, U>> modules) {
71+
ObjectMapper objectMapper = new ObjectMapper()
72+
.registerModule(new Jdk8Module())
73+
.registerModule(new JavaTimeModule())
74+
.registerModule(new GuavaModule())
75+
.setSerializationInclusion(JsonInclude.Include.NON_ABSENT)
76+
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
77+
.enable(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY);
10678
modules.stream()
10779
.map(module -> new NamedType(module.getDTOClass(), module.getDomainObjectType()))
10880
.forEach(objectMapper::registerSubtypes);
109-
dtoConverter = new DTOConverter<>(modules);
81+
return objectMapper;
11082
}
11183

11284
public String serialize(T domainObject) throws JsonProcessingException {
@@ -122,7 +94,7 @@ public T deserialize(String value) throws IOException {
12294
.orElseThrow(() -> new UnknownTypeException("unknown type " + dto.getType()));
12395
}
12496

125-
public U jsonToDTO(String value) throws IOException {
97+
private U jsonToDTO(String value) throws IOException {
12698
try {
12799
JsonNode jsonTree = detectDuplicateProperty(value);
128100
return parseAsPolymorphicDTO(jsonTree);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/****************************************************************
2+
* Licensed to the Apache Software Foundation (ASF) under one *
3+
* or more contributor license agreements. See the NOTICE file *
4+
* distributed with this work for additional information *
5+
* regarding copyright ownership. The ASF licenses this file *
6+
* to you under the Apache License, Version 2.0 (the *
7+
* "License"); you may not use this file except in compliance *
8+
* with the License. You may obtain a copy of the License at *
9+
* *
10+
* http://www.apache.org/licenses/LICENSE-2.0 *
11+
* *
12+
* Unless required by applicable law or agreed to in writing, *
13+
* software distributed under the License is distributed on an *
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
15+
* KIND, either express or implied. See the License for the *
16+
* specific language governing permissions and limitations *
17+
* under the License. *
18+
****************************************************************/
19+
20+
package org.apache;
21+
22+
import static org.assertj.core.api.Assertions.assertThat;
23+
24+
import java.time.ZonedDateTime;
25+
import java.util.Optional;
26+
import java.util.UUID;
27+
import java.util.stream.Stream;
28+
29+
import org.apache.dto.BaseType;
30+
import org.apache.dto.FirstDTO;
31+
import org.apache.dto.FirstDomainObject;
32+
import org.apache.dto.SecondDTO;
33+
import org.apache.dto.SecondDomainObject;
34+
import org.apache.dto.TestModules;
35+
import org.apache.james.json.DTO;
36+
import org.apache.james.json.DTOConverter;
37+
import org.junit.jupiter.api.Test;
38+
import org.junit.jupiter.params.ParameterizedTest;
39+
import org.junit.jupiter.params.provider.Arguments;
40+
import org.junit.jupiter.params.provider.MethodSource;
41+
42+
class DTOConverterTest {
43+
private static final FirstDomainObject FIRST = new FirstDomainObject(Optional.of(1L), ZonedDateTime.parse("2016-04-03T02:01+07:00[Asia/Vientiane]"), "first payload");
44+
private static final FirstDTO FIRST_DTO = new FirstDTO("first", Optional.of(1L), "2016-04-03T02:01+07:00[Asia/Vientiane]", "first payload");
45+
private static final SecondDomainObject SECOND = new SecondDomainObject(UUID.fromString("4a2c853f-7ffc-4ce3-9410-a47e85b3b741"), "second payload");
46+
private static final SecondDTO SECOND_DTO = new SecondDTO("second", "4a2c853f-7ffc-4ce3-9410-a47e85b3b741", "second payload");
47+
48+
@SuppressWarnings("unchecked")
49+
@Test
50+
void shouldConvertFromKnownDTO() throws Exception {
51+
assertThat(DTOConverter.of(TestModules.FIRST_TYPE)
52+
.convert(FIRST_DTO))
53+
.contains(FIRST);
54+
}
55+
56+
@Test
57+
void shouldReturnEmptyWhenConvertingFromUnknownDTO() {
58+
assertThat(DTOConverter.of()
59+
.convert(FIRST_DTO))
60+
.isEmpty();
61+
}
62+
63+
@ParameterizedTest
64+
@MethodSource
65+
void convertFromDomainObjectShouldHandleAllKnownTypes(BaseType domainObject, DTO dto) throws Exception {
66+
@SuppressWarnings("unchecked")
67+
DTOConverter<BaseType, DTO> serializer = DTOConverter.of(
68+
TestModules.FIRST_TYPE,
69+
TestModules.SECOND_TYPE);
70+
71+
assertThat(serializer.convert(domainObject))
72+
.hasValueSatisfying(result -> assertThat(result).isInstanceOf(dto.getClass()).isEqualToComparingFieldByField(dto));
73+
}
74+
75+
private static Stream<Arguments> convertFromDomainObjectShouldHandleAllKnownTypes() {
76+
return allKnownTypes();
77+
}
78+
79+
@ParameterizedTest
80+
@MethodSource
81+
void convertFromDTOShouldHandleAllKnownTypes(BaseType domainObject, DTO dto) throws Exception {
82+
@SuppressWarnings("unchecked")
83+
DTOConverter<BaseType, DTO> serializer = DTOConverter.of(
84+
TestModules.FIRST_TYPE,
85+
TestModules.SECOND_TYPE);
86+
87+
assertThat(serializer.convert(dto))
88+
.hasValueSatisfying(result -> assertThat(result).isInstanceOf(domainObject.getClass()).isEqualToComparingFieldByField(domainObject));
89+
}
90+
91+
private static Stream<Arguments> convertFromDTOShouldHandleAllKnownTypes() {
92+
return allKnownTypes();
93+
}
94+
95+
private static Stream<Arguments> allKnownTypes() {
96+
return Stream.of(
97+
Arguments.of(SECOND, SECOND_DTO),
98+
Arguments.of(FIRST, FIRST_DTO)
99+
);
100+
}
101+
102+
@SuppressWarnings("unchecked")
103+
@Test
104+
void shouldConvertFromKnownDomainObject() throws Exception {
105+
assertThat(DTOConverter.of(TestModules.FIRST_TYPE)
106+
.convert(FIRST))
107+
.hasValueSatisfying(result -> assertThat(result).isInstanceOf(FirstDTO.class).isEqualToComparingFieldByField(FIRST_DTO));
108+
}
109+
110+
@Test
111+
void shouldReturnEmptyWhenConvertUnknownDomainObject() {
112+
assertThat(DTOConverter.of().convert(FIRST))
113+
.isEmpty();
114+
}
115+
}

server/task/task-json/src/main/java/org/apache/james/server/task/json/JsonTaskAdditionalInformationSerializer.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ public UnknownAdditionalInformationException(JsonGenericSerializer.UnknownTypeEx
4949

5050
@Inject
5151
public JsonTaskAdditionalInformationSerializer(Set<AdditionalInformationDTOModule<?, ?>> modules) {
52-
jsonGenericSerializer = new JsonGenericSerializer(modules);
52+
//FIXME
53+
jsonGenericSerializer = new JsonGenericSerializer(modules, null);
5354
}
5455

5556
public JsonTaskAdditionalInformationSerializer(@SuppressWarnings("rawtypes") AdditionalInformationDTOModule... modules) {

server/task/task-json/src/main/java/org/apache/james/server/task/json/JsonTaskSerializer.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ public UnknownTaskException(JsonGenericSerializer.UnknownTypeException original)
5050

5151
@Inject
5252
public JsonTaskSerializer(Set<TaskDTOModule<?, ?>> modules) {
53-
jsonGenericSerializer = new JsonGenericSerializer(modules);
53+
//FIXME
54+
jsonGenericSerializer = new JsonGenericSerializer(modules, null);
5455
}
5556

5657
public JsonTaskSerializer(@SuppressWarnings("rawtypes") TaskDTOModule... modules) {

0 commit comments

Comments
 (0)