From 8311b827ada9a6351cb546ae9e52933598860c73 Mon Sep 17 00:00:00 2001 From: DManstrator Date: Wed, 14 Jun 2023 11:35:35 +0200 Subject: [PATCH 1/7] Add initial dependencies using latest Spring Boot 2.7 --- pom.xml | 137 ++++++++++++++++++ .../sb3_serialization_test/Application.java | 13 ++ .../entity/CustomObject.java | 6 + .../exporter/EntityExporter.java | 22 +++ .../serialization/SerializationHandler.java | 22 +++ .../SerializationHandlerFactory.java | 20 +++ .../AbstractJsonSerializationHandler.java | 101 +++++++++++++ .../impl/JsonSerializationHandler.java | 42 ++++++ .../exporter/EntityExporterTest.java | 23 +++ 9 files changed, 386 insertions(+) create mode 100644 src/main/java/tk/dmanstrator/sb3_serialization_test/Application.java create mode 100644 src/main/java/tk/dmanstrator/sb3_serialization_test/entity/CustomObject.java create mode 100644 src/main/java/tk/dmanstrator/sb3_serialization_test/exporter/EntityExporter.java create mode 100644 src/main/java/tk/dmanstrator/sb3_serialization_test/serialization/SerializationHandler.java create mode 100644 src/main/java/tk/dmanstrator/sb3_serialization_test/serialization/SerializationHandlerFactory.java create mode 100644 src/main/java/tk/dmanstrator/sb3_serialization_test/serialization/impl/AbstractJsonSerializationHandler.java create mode 100644 src/main/java/tk/dmanstrator/sb3_serialization_test/serialization/impl/JsonSerializationHandler.java create mode 100644 src/test/java/tk/dmanstrator/sb3_serialization_test/exporter/EntityExporterTest.java diff --git a/pom.xml b/pom.xml index 52ee17e..1387646 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,123 @@ ${java.version} + + + + + org.springframework.boot + spring-boot-dependencies + pom + import + 2.7.12 + + + + + org.slf4j + slf4j-api + 1.7.36 + + + ch.qos.logback + logback-classic + 1.2.11 + + + ch.qos.logback.contrib + logback-jackson + 0.1.5 + + + ch.qos.logback.contrib + logback-json-classic + 0.1.5 + + + + + javax.validation + validation-api + 2.0.1.Final + + + org.openapitools + jackson-databind-nullable + 0.2.6 + + + + + org.assertj + assertj-core + 3.24.2 + test + + + + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-oauth2-resource-server + + + org.springframework.boot + spring-boot-starter-oauth2-client + + + + + org.slf4j + slf4j-api + + + ch.qos.logback + logback-classic + + + ch.qos.logback.contrib + logback-jackson + + + ch.qos.logback.contrib + logback-json-classic + + + + + javax.validation + validation-api + + + org.openapitools + jackson-databind-nullable + + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.assertj + assertj-core + test + + + @@ -36,6 +153,26 @@ true + + + org.apache.maven.plugins + maven-surefire-plugin + 3.1.2 + + + org.springframework.boot + spring-boot-maven-plugin + 2.7.12 + + + + build-info + repackage + + + + + diff --git a/src/main/java/tk/dmanstrator/sb3_serialization_test/Application.java b/src/main/java/tk/dmanstrator/sb3_serialization_test/Application.java new file mode 100644 index 0000000..4f30228 --- /dev/null +++ b/src/main/java/tk/dmanstrator/sb3_serialization_test/Application.java @@ -0,0 +1,13 @@ +package tk.dmanstrator.sb3_serialization_test; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(final String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/src/main/java/tk/dmanstrator/sb3_serialization_test/entity/CustomObject.java b/src/main/java/tk/dmanstrator/sb3_serialization_test/entity/CustomObject.java new file mode 100644 index 0000000..ec4d503 --- /dev/null +++ b/src/main/java/tk/dmanstrator/sb3_serialization_test/entity/CustomObject.java @@ -0,0 +1,6 @@ +package tk.dmanstrator.sb3_serialization_test.entity; + +import java.io.Serializable; +import java.util.List; + +public record CustomObject(String hello, List world) implements Serializable {} diff --git a/src/main/java/tk/dmanstrator/sb3_serialization_test/exporter/EntityExporter.java b/src/main/java/tk/dmanstrator/sb3_serialization_test/exporter/EntityExporter.java new file mode 100644 index 0000000..cb9fceb --- /dev/null +++ b/src/main/java/tk/dmanstrator/sb3_serialization_test/exporter/EntityExporter.java @@ -0,0 +1,22 @@ +package tk.dmanstrator.sb3_serialization_test.exporter; + +import tk.dmanstrator.sb3_serialization_test.entity.CustomObject; +import tk.dmanstrator.sb3_serialization_test.serialization.SerializationHandlerFactory; + +import java.io.IOException; + +/** + * Class responsible for exporting entities. + */ +public class EntityExporter { + + private EntityExporter() { + // hide default constructor + } + + public static String exportCustomObject(final CustomObject customObject) throws IOException { + return SerializationHandlerFactory.getDefaultHandlerWithPrettyPrint() + .writeObjectAsString(customObject); + } + +} diff --git a/src/main/java/tk/dmanstrator/sb3_serialization_test/serialization/SerializationHandler.java b/src/main/java/tk/dmanstrator/sb3_serialization_test/serialization/SerializationHandler.java new file mode 100644 index 0000000..de70cf1 --- /dev/null +++ b/src/main/java/tk/dmanstrator/sb3_serialization_test/serialization/SerializationHandler.java @@ -0,0 +1,22 @@ +package tk.dmanstrator.sb3_serialization_test.serialization; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Serializable; + +public interface SerializationHandler { + + boolean isBinary(); + + String getFileNameExtension(); + + void writeObject(final T object, final OutputStream os) throws IOException; + + String writeObjectAsString(final T object) throws IOException; + + T readObject(final Class clazz, final InputStream is) throws IOException; + + T readObjectFromString(final Class clazz, final String input) throws IOException; + +} diff --git a/src/main/java/tk/dmanstrator/sb3_serialization_test/serialization/SerializationHandlerFactory.java b/src/main/java/tk/dmanstrator/sb3_serialization_test/serialization/SerializationHandlerFactory.java new file mode 100644 index 0000000..29d113a --- /dev/null +++ b/src/main/java/tk/dmanstrator/sb3_serialization_test/serialization/SerializationHandlerFactory.java @@ -0,0 +1,20 @@ +package tk.dmanstrator.sb3_serialization_test.serialization; + +import tk.dmanstrator.sb3_serialization_test.serialization.impl.JsonSerializationHandler; + +public final class SerializationHandlerFactory { + + private static final SerializationHandler DEFAULT_HANDLER = new JsonSerializationHandler(false); + private static final SerializationHandler DEFAULT_HANDLER_WITH_PRETTY_PRINT = new JsonSerializationHandler(true); + + private SerializationHandlerFactory() {} + + public static SerializationHandler getDefaultHandler() { + return DEFAULT_HANDLER; + } + + public static SerializationHandler getDefaultHandlerWithPrettyPrint() { + return DEFAULT_HANDLER_WITH_PRETTY_PRINT; + } + +} diff --git a/src/main/java/tk/dmanstrator/sb3_serialization_test/serialization/impl/AbstractJsonSerializationHandler.java b/src/main/java/tk/dmanstrator/sb3_serialization_test/serialization/impl/AbstractJsonSerializationHandler.java new file mode 100644 index 0000000..9f516b9 --- /dev/null +++ b/src/main/java/tk/dmanstrator/sb3_serialization_test/serialization/impl/AbstractJsonSerializationHandler.java @@ -0,0 +1,101 @@ +package tk.dmanstrator.sb3_serialization_test.serialization.impl; + +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator; +import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import tk.dmanstrator.sb3_serialization_test.serialization.SerializationHandler; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public abstract class AbstractJsonSerializationHandler implements SerializationHandler { + + protected static final ThreadLocal OBJECT_MAPPER_THREAD_LOCAL = + ThreadLocal.withInitial(AbstractJsonSerializationHandler::newObjectMapper); + + private final Map, ObjectReader> readers = new ConcurrentHashMap<>(); + private final Map, ObjectWriter> writers = new ConcurrentHashMap<>(); + + private final boolean doPrettyPrint; + + protected AbstractJsonSerializationHandler(final boolean doPrettyPrint) { + this.doPrettyPrint = doPrettyPrint; + } + + @Override + public void writeObject(final T object, final OutputStream os) throws IOException { + ObjectWriter writer = getWriter(object.getClass()); + + if (doPrettyPrint) { + writer = writer.withDefaultPrettyPrinter(); + } + + writer.writeValue(os, object); + } + + @Override + public String writeObjectAsString(final T object) throws IOException { + ObjectWriter writer = getWriter(object.getClass()); + + if (doPrettyPrint) { + writer = writer.withDefaultPrettyPrinter(); + } + + return writer.writeValueAsString(object); + } + + @Override + public T readObject(final Class clazz, final InputStream is) throws IOException { + return getReader(clazz).readValue(is); + } + + @Override + public T readObjectFromString(final Class clazz, final String input) + throws IOException { + return getReader(clazz).readValue(input); + } + + private static ObjectMapper newObjectMapper() { + final PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator + .builder() + .allowIfBaseType(List.class) + .allowIfBaseType(Map.class) + .allowIfBaseType(Set.class) + .allowIfSubType(List.class) + .allowIfSubType(Map.class) + .build(); + return JsonMapper.builder() + .enable(MapperFeature.BLOCK_UNSAFE_POLYMORPHIC_BASE_TYPES) + .activateDefaultTyping(ptv) + .addModule(new JavaTimeModule()) + .addModule(new Jdk8Module()) + .visibility(PropertyAccessor.ALL, Visibility.NONE) + .visibility(PropertyAccessor.FIELD, Visibility.ANY) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .serializationInclusion(Include.NON_NULL) + .build(); + } + + protected abstract ObjectMapper createObjectMapper(); + + private ObjectReader getReader(final Class clazz) { + return readers.computeIfAbsent(clazz, c -> OBJECT_MAPPER_THREAD_LOCAL.get().readerFor(c)); + } + + private ObjectWriter getWriter(final Class clazz) { + return writers.computeIfAbsent(clazz, c -> OBJECT_MAPPER_THREAD_LOCAL.get().writerFor(c)); + } + +} diff --git a/src/main/java/tk/dmanstrator/sb3_serialization_test/serialization/impl/JsonSerializationHandler.java b/src/main/java/tk/dmanstrator/sb3_serialization_test/serialization/impl/JsonSerializationHandler.java new file mode 100644 index 0000000..cd749e2 --- /dev/null +++ b/src/main/java/tk/dmanstrator/sb3_serialization_test/serialization/impl/JsonSerializationHandler.java @@ -0,0 +1,42 @@ +package tk.dmanstrator.sb3_serialization_test.serialization.impl; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JsonSerializationHandler extends AbstractJsonSerializationHandler { + + /** + * Creates a new JSON Serializer. Won't apply pretty-printing when writing objects. + */ + public JsonSerializationHandler() { + this(false); + } + + /** + * Creates a new JSON Serializer. + * + * @param doPrettyPrint Flag to tell whether pretty-printing should be performed when writing objects or not. + */ + public JsonSerializationHandler(final boolean doPrettyPrint) { + super(doPrettyPrint); + } + + @Override + public boolean isBinary() { + return false; + } + + @Override + public String getFileNameExtension() { + return ".json"; + } + + public static ObjectMapper getStandardObjectMapper() { + return OBJECT_MAPPER_THREAD_LOCAL.get(); + } + + @Override + protected ObjectMapper createObjectMapper() { + return new ObjectMapper(); + } + +} diff --git a/src/test/java/tk/dmanstrator/sb3_serialization_test/exporter/EntityExporterTest.java b/src/test/java/tk/dmanstrator/sb3_serialization_test/exporter/EntityExporterTest.java new file mode 100644 index 0000000..fccee60 --- /dev/null +++ b/src/test/java/tk/dmanstrator/sb3_serialization_test/exporter/EntityExporterTest.java @@ -0,0 +1,23 @@ +package tk.dmanstrator.sb3_serialization_test.exporter; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import tk.dmanstrator.sb3_serialization_test.entity.CustomObject; + +import java.io.IOException; +import java.util.Arrays; + +class EntityExporterTest { + + private static final String EMPTY_JSON = "{ }"; + + @Test + void testExportCustomObject() throws IOException { + final CustomObject customObject = new CustomObject("hello", Arrays.asList("wor", "ld")); + final String json = EntityExporter.exportCustomObject(customObject); + Assertions.assertThat(json) + .isNotEmpty() + .isNotEqualTo(EMPTY_JSON); + } + +} \ No newline at end of file From 08ee53c41b973e5f48df70a1dd5a69050526f6e5 Mon Sep 17 00:00:00 2001 From: DManstrator Date: Wed, 14 Jun 2023 11:40:33 +0200 Subject: [PATCH 2/7] Upgrade to Jakarta (Spring Boot 3, SLF4J 2, Logback 1.4) --- pom.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 1387646..26a65b1 100644 --- a/pom.xml +++ b/pom.xml @@ -23,19 +23,19 @@ spring-boot-dependencies pom import - 2.7.12 + 3.1.0 org.slf4j slf4j-api - 1.7.36 + 2.0.7 ch.qos.logback logback-classic - 1.2.11 + 1.4.8 ch.qos.logback.contrib @@ -50,9 +50,9 @@ - javax.validation - validation-api - 2.0.1.Final + jakarta.validation + jakarta.validation-api + 3.0.2 org.openapitools @@ -111,8 +111,8 @@ - javax.validation - validation-api + jakarta.validation + jakarta.validation-api org.openapitools From 31717393a38d7f8a3ec9c9d9df3cef3657e07d75 Mon Sep 17 00:00:00 2001 From: DManstrator Date: Wed, 14 Jun 2023 12:55:17 +0200 Subject: [PATCH 3/7] Convert record to class --- .../entity/CustomObject.java | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/src/main/java/tk/dmanstrator/sb3_serialization_test/entity/CustomObject.java b/src/main/java/tk/dmanstrator/sb3_serialization_test/entity/CustomObject.java index ec4d503..244714a 100644 --- a/src/main/java/tk/dmanstrator/sb3_serialization_test/entity/CustomObject.java +++ b/src/main/java/tk/dmanstrator/sb3_serialization_test/entity/CustomObject.java @@ -1,6 +1,57 @@ package tk.dmanstrator.sb3_serialization_test.entity; +import java.io.Serial; import java.io.Serializable; +import java.util.ArrayList; import java.util.List; +import java.util.Objects; -public record CustomObject(String hello, List world) implements Serializable {} +public class CustomObject implements Serializable { + + @Serial + private static final long serialVersionUID = 0L; + + private final String hello; + private final List world; + + // Default Constructor needed for (de)serialization. + public CustomObject() { + this("", new ArrayList<>()); + } + + public CustomObject(String hello, List world) { + this.hello = hello; + this.world = world; + } + + public String getHello() { + return hello; + } + + public List getWorld() { + return world; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (CustomObject) obj; + return Objects.equals(this.hello, that.hello) && + Objects.equals(this.world, that.world); + } + + @Override + public int hashCode() { + return Objects.hash(hello, world); + } + + @Override + public String toString() { + return "CustomObject{" + + "hello='" + hello + '\'' + + ", world=" + world + + '}'; + } + +} From 18fcd87001edd112218bdcbea3df49926603b1d0 Mon Sep 17 00:00:00 2001 From: DManstrator Date: Wed, 14 Jun 2023 16:55:42 +0200 Subject: [PATCH 4/7] Revert "Convert record to class" This reverts commit 31717393a38d7f8a3ec9c9d9df3cef3657e07d75. --- .../entity/CustomObject.java | 53 +------------------ 1 file changed, 1 insertion(+), 52 deletions(-) diff --git a/src/main/java/tk/dmanstrator/sb3_serialization_test/entity/CustomObject.java b/src/main/java/tk/dmanstrator/sb3_serialization_test/entity/CustomObject.java index 244714a..ec4d503 100644 --- a/src/main/java/tk/dmanstrator/sb3_serialization_test/entity/CustomObject.java +++ b/src/main/java/tk/dmanstrator/sb3_serialization_test/entity/CustomObject.java @@ -1,57 +1,6 @@ package tk.dmanstrator.sb3_serialization_test.entity; -import java.io.Serial; import java.io.Serializable; -import java.util.ArrayList; import java.util.List; -import java.util.Objects; -public class CustomObject implements Serializable { - - @Serial - private static final long serialVersionUID = 0L; - - private final String hello; - private final List world; - - // Default Constructor needed for (de)serialization. - public CustomObject() { - this("", new ArrayList<>()); - } - - public CustomObject(String hello, List world) { - this.hello = hello; - this.world = world; - } - - public String getHello() { - return hello; - } - - public List getWorld() { - return world; - } - - @Override - public boolean equals(Object obj) { - if (obj == this) return true; - if (obj == null || obj.getClass() != this.getClass()) return false; - var that = (CustomObject) obj; - return Objects.equals(this.hello, that.hello) && - Objects.equals(this.world, that.world); - } - - @Override - public int hashCode() { - return Objects.hash(hello, world); - } - - @Override - public String toString() { - return "CustomObject{" + - "hello='" + hello + '\'' + - ", world=" + world + - '}'; - } - -} +public record CustomObject(String hello, List world) implements Serializable {} From b37072328fd1d5615f5f7d3604a70ae4374d77a0 Mon Sep 17 00:00:00 2001 From: DManstrator Date: Wed, 14 Jun 2023 17:00:25 +0200 Subject: [PATCH 5/7] Using the jackson version used by Spring Boot 2.7.12 --- pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pom.xml b/pom.xml index 26a65b1..fb69336 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,13 @@ + + com.fasterxml.jackson + jackson-bom + 2.13.5 + pom + import + org.springframework.boot spring-boot-dependencies From 4e2c46a14d381fa1b4c048cb80babf8833804811 Mon Sep 17 00:00:00 2001 From: DManstrator Date: Wed, 14 Jun 2023 17:02:20 +0200 Subject: [PATCH 6/7] Using the jackson version used by Spring Boot 3.1.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fb69336..af4cd5c 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ com.fasterxml.jackson jackson-bom - 2.13.5 + 2.15.0 pom import From 30eed5bcda489f8b413ffbe2c404108b9730835c Mon Sep 17 00:00:00 2001 From: DManstrator Date: Wed, 14 Jun 2023 17:05:40 +0200 Subject: [PATCH 7/7] Using the latest version of Jackson --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index af4cd5c..839fe0e 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ com.fasterxml.jackson jackson-bom - 2.15.0 + 2.15.2 pom import