diff --git a/docs/asciidoc/modules/avaje-jsonb.adoc b/docs/asciidoc/modules/avaje-jsonb.adoc index 345c1f45ab..2898470994 100644 --- a/docs/asciidoc/modules/avaje-jsonb.adoc +++ b/docs/asciidoc/modules/avaje-jsonb.adoc @@ -81,7 +81,7 @@ import io.jooby.avaje.jsonb.AvajeJsonbModule import io.avaje.jsonb.Jsonb { - install(JacksonModule()) + install(AvajeJsonbModule()) val jsonb = require() } diff --git a/docs/asciidoc/modules/graphql.adoc b/docs/asciidoc/modules/graphql.adoc index 3d0fcc8f2b..a9dc781166 100644 --- a/docs/asciidoc/modules/graphql.adoc +++ b/docs/asciidoc/modules/graphql.adoc @@ -36,11 +36,11 @@ type Author { .Java [source, java, role="primary"] ---- -import io.jooby.json.JacksonModule; +import io.jooby.json.Jackson2Module; import io.jooby.graphql.GraphQLModule; { - install(new JacksonModule()); <1> + install(new Jackson2Module()); <1> install(new GraphQLModule( RuntimeWiring.newRuntimeWiring() <2> @@ -56,11 +56,11 @@ import io.jooby.graphql.GraphQLModule; .Kotlin [source, kt, role="secondary"] ---- -import io.jooby.json.JacksonModule +import io.jooby.json.Jackson2Module import io.jooby.graphql.GraphQLModule { - install(JacksonModule()) <1> + install(Jackson2Module()) <1> install(GraphQLModule( RuntimeWiring.newRuntimeWiring() <2> @@ -88,7 +88,7 @@ the async mode: .Async Mode [source, java, role="primary"] ---- -import io.jooby.json.JacksonModule; +import io.jooby.json.Jackson2Module; import io.jooby.graphql.GraphQLModule; { @@ -101,7 +101,7 @@ import io.jooby.graphql.GraphQLModule; .Kotlin [source, kt, role="secondary"] ---- -import io.jooby.json.JacksonModule +import io.jooby.json.Jackson2Module import io.jooby.graphql.GraphQLModule { @@ -122,7 +122,7 @@ By default the module only install a HTTP POST route. If you want a HTTP GET: .HTTP GET [source, java, role="primary"] ---- -import io.jooby.json.JacksonModule; +import io.jooby.json.Jackson2Module; import io.jooby.graphql.GraphQLModule; { @@ -135,7 +135,7 @@ import io.jooby.graphql.GraphQLModule; .Kotlin [source, kt, role="secondary"] ---- -import io.jooby.json.JacksonModule +import io.jooby.json.Jackson2Module import io.jooby.graphql.GraphQLModule { @@ -170,12 +170,12 @@ They are provided as optional dependencies. .Java [source, java, role="primary"] ---- -import io.jooby.json.JacksonModule; +import io.jooby.json.Jackson2Module; import io.jooby.graphql.GraphQLModule; import io.jooby.graphql.GraphiQLModule; { - install(new JacksonModule()); <1> + install(new Jackson2Module()); <1> install(new GraphQLModule(...)); <2> @@ -186,12 +186,12 @@ import io.jooby.graphql.GraphiQLModule; .Kotlin [source, kt, role="secondary"] ---- -import io.jooby.json.JacksonModule +import io.jooby.json.Jackson2Module import io.jooby.graphql.GraphQLModule import io.jooby.graphql.GraphiQLModule { - install(JacksonModule()) <1> + install(Jackson2Module()) <1> install(GraphQLModule(...)) <2> @@ -217,12 +217,12 @@ https://github.com/graphql/graphiql[GraphiQL] should be up and running at `/grap .Java [source, java, role="primary"] ---- -import io.jooby.json.JacksonModule; +import io.jooby.json.Jackson2Module; import io.jooby.graphql.GraphQLModule; import io.jooby.graphql.GraphQLPlaygroundModule; { - install(new JacksonModule()); <1> + install(new Jackson2Module()); <1> install(new GraphQLModule(...)); <2> @@ -233,12 +233,12 @@ import io.jooby.graphql.GraphQLPlaygroundModule; .Kotlin [source, kt, role="secondary"] ---- -import io.jooby.json.JacksonModule +import io.jooby.json.Jackson2Module import io.jooby.graphql.GraphQLModule import io.jooby.graphql.GraphQLPlaygroundModule { - install(JacksonModule()) <1> + install(Jackson2Module()) <1> install(GraphQLModule(...)) <2> diff --git a/docs/asciidoc/modules/hibernate-validator.adoc b/docs/asciidoc/modules/hibernate-validator.adoc index c4cb738e17..4c1e6839d2 100644 --- a/docs/asciidoc/modules/hibernate-validator.adoc +++ b/docs/asciidoc/modules/hibernate-validator.adoc @@ -191,7 +191,7 @@ It is possible to override the `title` and `status` code of the response above: ---- { - install(new JacksonModule()); + install(new Jackson2Module()); install(new HibernateValidatorModule() .statusCode(StatusCode.BAD_REQUEST) .validationTitle("Incorrect input data") @@ -205,7 +205,7 @@ If the default error handler doesn't fully meet your needs, you can always disab ---- { - install(new JacksonModule()); + install(new Jackson2Module()); install(new HibernateValidatorModule().disableViolationHandler()); error(ConstraintViolationException.class, new MyConstraintViolationHandler()); diff --git a/docs/asciidoc/modules/jackson2.adoc b/docs/asciidoc/modules/jackson2.adoc index efcfb70c77..0d6fbf44ab 100644 --- a/docs/asciidoc/modules/jackson2.adoc +++ b/docs/asciidoc/modules/jackson2.adoc @@ -14,10 +14,10 @@ JSON support using https://github.com/FasterXML/jackson[Jackson 2] library. .Java [source, java, role="primary"] ---- -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; { - install(new JacksonModule()); <1> + install(new Jackson2Module()); <1> get("/", ctx -> { MyObject myObject = ...; @@ -34,10 +34,10 @@ import io.jooby.jackson.JacksonModule; .Kotlin [source, kt, role="secondary"] ---- -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; { - install(JacksonModule()) <1> + install(Jackson2Module()) <1> get("/") { val myObject = ...; @@ -62,10 +62,10 @@ Access to default object mapper is available via require call: .Default object mapper [source, java, role="primary"] ---- -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; { - install(new JacksonModule()); + install(new Jackson2Module()); ObjectMapper mapper = require(ObjectMapper.class); @@ -76,10 +76,10 @@ import io.jooby.jackson.JacksonModule; .Kotlin [source, kt, role="secondary"] ---- -import io.jooby.jackson.JacksonModule +import io.jooby.jackson.Jackson2Module { - install(JacksonModule()) + install(Jackson2Module()) val mapper = require() } @@ -90,46 +90,46 @@ You can provide your own `ObjectMapper`: .Custom ObjectMapper [source, java, role="primary"] ---- -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; { ObjectMapper mapper = new ObjectMapper(); - install(new JacksonModule(mapper)); + install(new Jackson2Module(mapper)); } ---- .Kotlin [source, kt, role="secondary"] ---- -import io.jooby.jackson.JacksonModule +import io.jooby.jackson.Jackson2Module { val mapper = ObjectMapper() - install(JacksonModule(mapper)) + install(Jackson2Module(mapper)) } ---- -This allows to configure JacksonModule for doing `xml` processing: +This allows to configure Jackson2Module for doing `xml` processing: .XmlMapper [source, java, role="primary"] ---- -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; { - install(new JacksonModule(new XmlMapper())); + install(new Jackson2Module(new XmlMapper())); } ---- .Kotlin [source, kt, role="secondary"] ---- -import io.jooby.jackson.JacksonModule +import io.jooby.jackson.Jackson2Module { - install(JacksonModule(XmlMapper())) + install(Jackson2Module(XmlMapper())) } ---- @@ -138,22 +138,22 @@ If you want `json` and `xml` processing then install twice: .XmlMapper [source, java, role="primary"] ---- -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; { - install(new JacksonModule(new ObjectMapper())); - install(new JacksonModule(new XmlMapper())); + install(new Jackson2Module(new ObjectMapper())); + install(new Jackson2Module(new XmlMapper())); } ---- .Kotlin [source, kt, role="secondary"] ---- -import io.jooby.jackson.JacksonModule +import io.jooby.jackson.Jackson2Module { - install(JacksonModule(ObjectMapper())) - install(JacksonModule(XmlMapper())) + install(Jackson2Module(ObjectMapper())) + install(Jackson2Module(XmlMapper())) } ---- @@ -164,20 +164,20 @@ Jackson module can be provided by a link:{uiVersion}/#ecosystem-services-and-the .Provisioning Modules [source, java, role="primary"] ---- -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; { - install(new JacksonModule().module(MyModule.class); + install(new Jackson2Module().module(MyModule.class); } ---- .Kotlin [source, kt, role="secondary"] ---- -import io.jooby.jackson.JacksonModule +import io.jooby.jackson.Jackson2Module { - install(JacksonModule().module(MyModule::class.java) + install(Jackson2Module().module(MyModule::class.java) } ---- diff --git a/docs/asciidoc/modules/json-rpc.adoc b/docs/asciidoc/modules/json-rpc.adoc index 47d5fc0703..0dcf695c8a 100644 --- a/docs/asciidoc/modules/json-rpc.adoc +++ b/docs/asciidoc/modules/json-rpc.adoc @@ -99,7 +99,7 @@ The JSON-RPC extension delegates payload parsing and serialization to Jooby's st Supported engines include: -* **Jackson 2**: `JacksonModule`, `JsonRpcJackson2Module` +* **Jackson 2**: `Jackson2Module`, `JsonRpcJackson2Module` * **Jackson 3**: `Jackson3Module`, `JsonRpcJackson3Module` * **Avaje JSON-B**: `AvajeJsonbModule`, `JsonRpcAvajeJsonbModule` diff --git a/docs/asciidoc/modules/mcp.adoc b/docs/asciidoc/modules/mcp.adoc index a193f1705c..d63589193a 100644 --- a/docs/asciidoc/modules/mcp.adoc +++ b/docs/asciidoc/modules/mcp.adoc @@ -80,7 +80,7 @@ import io.jooby.mcp.McpModule } ---- -<1> Install JSON support (Jackson is required for MCP JSON-RPC serialization). For Jackson 2, use `JacksonModule()` instead. +<1> Install JSON support (Jackson is required for MCP JSON-RPC serialization). For Jackson 2, use `Jackson2Module()` instead. <2> Install MCP JSON support. For Jackson 2, use `McpJackson2Module()` instead. <3> Install the MCP module with the APT-generated `McpService` dispatcher. The generated class always ends with the `Mcp_` suffix. diff --git a/docs/asciidoc/modules/tRPC.adoc b/docs/asciidoc/modules/tRPC.adoc index 14524896fd..9581300779 100644 --- a/docs/asciidoc/modules/tRPC.adoc +++ b/docs/asciidoc/modules/tRPC.adoc @@ -17,7 +17,7 @@ Installing tRPC in your Jooby app is a 3-step process. Because tRPC relies heavi [source,java,role="primary"] ---- import io.jooby.Jooby; -import io.jooby.json.JacksonModule; +import io.jooby.json.Jackson2Module; import io.jooby.trpc.TrpcModule; import io.jooby.trpc.TrpcJackson2Module; @@ -34,7 +34,7 @@ import io.jooby.trpc.TrpcJackson2Module; [source,kotlin,role="secondary"] ---- import io.jooby.kt.Kooby -import io.jooby.json.JacksonModule +import io.jooby.json.Jackson2Module import io.jooby.trpc.TrpcModule import io.jooby.trpc.TrpcJackson2Module @@ -47,7 +47,7 @@ import io.jooby.trpc.TrpcJackson2Module } ---- -1. Install a supported JSON engine (`Jackson3Module`, `JacksonModule` for Jackson 2, or `AvajeJsonbModule`). +1. Install a supported JSON engine (`Jackson3Module`, `Jackson2Module` for Jackson 2, or `AvajeJsonbModule`). 2. Install the corresponding bridge module (`TrcJackson3Module`, `TrpcJackson2Module`, or `TrpcAvajeJsonbModule`). 3. Install the core tRPC extension (`TrpcModule`). 4. Register your `@Trpc` annotated controllers (using the APT generated route). diff --git a/docs/asciidoc/websocket.adoc b/docs/asciidoc/websocket.adoc index af885b71c0..2ad9483d31 100644 --- a/docs/asciidoc/websocket.adoc +++ b/docs/asciidoc/websocket.adoc @@ -89,15 +89,15 @@ You are free to access the HTTP context from the WebSocket configurer or callbac Structured data (like JSON) is supported using the Value API and the javadoc:WebSocket[render, java.lang.Object] method. -To use structured messages, you need a registered javadoc:MessageDecoder[] and javadoc:MessageEncoder[]. In the following example, both are provided by the `JacksonModule`. +To use structured messages, you need a registered javadoc:MessageDecoder[] and javadoc:MessageEncoder[]. In the following example, both are provided by the `Jackson2Module`. .JSON Example [source,java,role="primary"] ---- -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; { - install(new JacksonModule()); // <1> + install(new Jackson2Module()); // <1> ws("/ws", (ctx, configurer) -> { configurer.onMessage((ws, message) -> { @@ -111,10 +111,10 @@ import io.jooby.jackson.JacksonModule; .Kotlin [source,kotlin,role="secondary"] ---- -import io.jooby.jackson.JacksonModule +import io.jooby.jackson.Jackson2Module { - install(JacksonModule()) // <1> + install(Jackson2Module()) // <1> ws("/ws") { ctx, configurer -> configurer.onMessage { ws, message -> @@ -134,10 +134,10 @@ Alternatively, you can explicitly tell the WebSocket which decoder/encoder to us .Explicit Content Types [source,java,role="primary"] ---- -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; { - install(new JacksonModule()); // <1> + install(new Jackson2Module()); // <1> ws("/ws", (ctx, configurer) -> { configurer.onMessage((ws, message) -> { @@ -153,10 +153,10 @@ import io.jooby.jackson.JacksonModule; .Kotlin [source,kotlin,role="secondary"] ---- -import io.jooby.jackson.JacksonModule +import io.jooby.jackson.Jackson2Module { - install(JacksonModule()) // <1> + install(Jackson2Module()) // <1> ws("/ws") { ctx, configurer -> configurer.onMessage { ws, message -> diff --git a/modules/jooby-jackson/src/main/java/io/jooby/internal/jackson/JacksonProjectedSerializer.java b/modules/jooby-jackson/src/main/java/io/jooby/internal/jackson/JacksonProjectedSerializer.java index ba3158877c..87d300457a 100644 --- a/modules/jooby-jackson/src/main/java/io/jooby/internal/jackson/JacksonProjectedSerializer.java +++ b/modules/jooby-jackson/src/main/java/io/jooby/internal/jackson/JacksonProjectedSerializer.java @@ -17,7 +17,7 @@ import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; import io.jooby.Projected; import io.jooby.Projection; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; public class JacksonProjectedSerializer extends JsonSerializer { private final Map, ObjectWriter> writerCache = new ConcurrentHashMap<>(); @@ -39,7 +39,7 @@ public void serialize( p -> { var filters = new SimpleFilterProvider() - .addFilter(JacksonModule.FILTER_ID, new JacksonProjectionFilter(p)); + .addFilter(Jackson2Module.FILTER_ID, new JacksonProjectionFilter(p)); return mapper.writer(filters); }); diff --git a/modules/jooby-jackson/src/main/java/io/jooby/jackson/Jackson2Module.java b/modules/jooby-jackson/src/main/java/io/jooby/jackson/Jackson2Module.java new file mode 100644 index 0000000000..0a53e685ff --- /dev/null +++ b/modules/jooby-jackson/src/main/java/io/jooby/jackson/Jackson2Module.java @@ -0,0 +1,231 @@ +/* + * Jooby https://jooby.io + * Apache License Version 2.0 https://jooby.io/LICENSE.txt + * Copyright 2014 Edgar Espina + */ +package io.jooby.jackson; + +import java.io.InputStream; +import java.lang.reflect.Type; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Stream; + +import com.fasterxml.jackson.annotation.JsonFilter; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.exc.MismatchedInputException; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; +import edu.umd.cs.findbugs.annotations.NonNull; +import io.jooby.*; +import io.jooby.internal.jackson.*; +import io.jooby.output.Output; + +/** + * JSON module using Jackson: https://jooby.io/modules/jackson2. + * + *

Usage: + * + *

{@code
+ * {
+ *
+ *   install(new Jackson2Module());
+ *
+ *   get("/", ctx -> {
+ *     MyObject myObject = ...;
+ *     // send json
+ *     return myObject;
+ *   });
+ *
+ *   post("/", ctx -> {
+ *     // read json
+ *     MyObject myObject = ctx.body(MyObject.class);
+ *     // send json
+ *     return myObject;
+ *   });
+ * }
+ * }
+ * + * For body decoding the client must specify the Content-Type header set to + * application/json. + * + *

You can retrieve the {@link ObjectMapper} via require call: + * + *

{@code
+ * {
+ *
+ *   ObjectMapper mapper = require(ObjectMapper.class);
+ *
+ * }
+ * }
+ * + * Complete documentation is available at: https://jooby.io/modules/jackson2. + * + * @author edgar + * @since 2.0.0 + */ +public class Jackson2Module implements Extension, MessageDecoder, MessageEncoder { + public static final String FILTER_ID = "jooby.projection"; + + // Cache for ObjectWriters tied to specific projection strings + private final Map writerCache = new ConcurrentHashMap<>(); + + @JsonFilter(FILTER_ID) + private interface ProjectionMixIn {} + + private final MediaType mediaType; + + private final ObjectMapper mapper; + + private final TypeFactory typeFactory; + + private final Set> modules = new HashSet<>(); + + private static final Map defaultTypes = new HashMap<>(); + + static { + defaultTypes.put("XmlMapper", MediaType.xml); + } + + /** + * Creates a Jackson module. + * + * @param mapper Object mapper to use. + * @param contentType Content type. + */ + public Jackson2Module(@NonNull ObjectMapper mapper, @NonNull MediaType contentType) { + this.mapper = mapper; + this.typeFactory = mapper.getTypeFactory(); + this.mediaType = contentType; + } + + /** + * Creates a Jackson module. + * + * @param mapper Object mapper to use. + */ + public Jackson2Module(@NonNull ObjectMapper mapper) { + this(mapper, defaultTypes.getOrDefault(mapper.getClass().getSimpleName(), MediaType.json)); + } + + /** Creates a Jackson module using the default object mapper from {@link #create(Module...)}. */ + public Jackson2Module() { + this(create()); + } + + /** + * Add a Jackson module to the object mapper. This method require a dependency injection framework + * which is responsible for provisioning a module instance. + * + * @param module Module type. + * @return This module. + */ + public Jackson2Module module(Class module) { + modules.add(module); + return this; + } + + @Override + public void install(@NonNull Jooby application) { + application.decoder(mediaType, this); + application.encoder(mediaType, this); + + ServiceRegistry services = application.getServices(); + Class mapperType = mapper.getClass(); + services.put(mapperType, mapper); + services.put(ObjectMapper.class, mapper); + + // Parsing exception as 400 + application.errorCode(JsonParseException.class, StatusCode.BAD_REQUEST); + application.errorCode(MismatchedInputException.class, StatusCode.BAD_REQUEST); + + // Filter + var defaultProvider = new SimpleFilterProvider().setFailOnUnknownId(false); + mapper.addMixIn(Object.class, ProjectionMixIn.class); + mapper.setFilterProvider(defaultProvider); + var projectionModule = new SimpleModule(); + projectionModule.addSerializer(Projected.class, new JacksonProjectedSerializer(mapper)); + mapper.registerModule(projectionModule); + + application.onStarting( + () -> { + for (Class type : modules) { + Module module = application.require(type); + mapper.registerModule(module); + } + List moreModules = + application.getServices().getOrNull(Reified.list(Module.class)); + if (moreModules != null) { + for (Module module : moreModules) { + mapper.registerModule(module); + } + } + }); + } + + @Override + public Output encode(@NonNull Context ctx, @NonNull Object value) throws Exception { + var factory = ctx.getOutputFactory(); + ctx.setDefaultResponseType(mediaType); + if (value instanceof Projected projected) { + var p = projected.getProjection(); + var writer = + writerCache.computeIfAbsent( + p.getType().getName() + p.toView(), + k -> { + // Use a specialized ObjectWriter with our custom path filter + var filters = + new SimpleFilterProvider().addFilter(FILTER_ID, new JacksonProjectionFilter(p)); + return mapper.writer(filters); + }); + return factory.wrap(writer.writeValueAsBytes(projected.getValue())); + } + // let jackson uses his own cache, so wrap the bytes + return factory.wrap(mapper.writeValueAsBytes(value)); + } + + @Override + public Object decode(Context ctx, Type type) throws Exception { + Body body = ctx.body(); + if (body.isInMemory()) { + if (type == JsonNode.class) { + return mapper.readTree(body.bytes()); + } + return mapper.readValue(body.bytes(), typeFactory.constructType(type)); + } else { + try (InputStream stream = body.stream()) { + if (type == JsonNode.class) { + return mapper.readTree(stream); + } + return mapper.readValue(stream, typeFactory.constructType(type)); + } + } + } + + /** + * Default object mapper. Install {@link Jdk8Module}, {@link JavaTimeModule}, {@link + * ParameterNamesModule}. + * + * @param modules Extra/additional modules to install. + * @return Object mapper instance. + */ + public static ObjectMapper create(Module... modules) { + JsonMapper.Builder builder = + JsonMapper.builder() + .addModule(new ParameterNamesModule()) + .addModule(new Jdk8Module()) + .addModule(new JavaTimeModule()) + .findAndAddModules(); + + Stream.of(modules).forEach(builder::addModule); + + return builder.build(); + } +} diff --git a/modules/jooby-jackson/src/main/java/io/jooby/jackson/JacksonModule.java b/modules/jooby-jackson/src/main/java/io/jooby/jackson/JacksonModule.java index 6b85922326..0e21925126 100644 --- a/modules/jooby-jackson/src/main/java/io/jooby/jackson/JacksonModule.java +++ b/modules/jooby-jackson/src/main/java/io/jooby/jackson/JacksonModule.java @@ -5,28 +5,13 @@ */ package io.jooby.jackson; -import java.io.InputStream; -import java.lang.reflect.Type; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Stream; - -import com.fasterxml.jackson.annotation.JsonFilter; -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.Module; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; -import com.fasterxml.jackson.databind.json.JsonMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; -import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; import edu.umd.cs.findbugs.annotations.NonNull; -import io.jooby.*; -import io.jooby.internal.jackson.*; -import io.jooby.output.Output; +import io.jooby.MediaType; /** * JSON module using Jackson: https://jooby.io/modules/jackson2. @@ -70,40 +55,13 @@ * * @author edgar * @since 2.0.0 + * @deprecated Use {@link io.jooby.jackson.JacksonModule} instead. */ -public class JacksonModule implements Extension, MessageDecoder, MessageEncoder { - public static final String FILTER_ID = "jooby.projection"; - - // Cache for ObjectWriters tied to specific projection strings - private final Map writerCache = new ConcurrentHashMap<>(); - - @JsonFilter(FILTER_ID) - private interface ProjectionMixIn {} - - private final MediaType mediaType; - - private final ObjectMapper mapper; - - private final TypeFactory typeFactory; - - private final Set> modules = new HashSet<>(); - - private static final Map defaultTypes = new HashMap<>(); +@Deprecated(since = "4.3.0", forRemoval = true) +public class JacksonModule extends Jackson2Module { - static { - defaultTypes.put("XmlMapper", MediaType.xml); - } - - /** - * Creates a Jackson module. - * - * @param mapper Object mapper to use. - * @param contentType Content type. - */ public JacksonModule(@NonNull ObjectMapper mapper, @NonNull MediaType contentType) { - this.mapper = mapper; - this.typeFactory = mapper.getTypeFactory(); - this.mediaType = contentType; + super(mapper, contentType); } /** @@ -112,101 +70,12 @@ public JacksonModule(@NonNull ObjectMapper mapper, @NonNull MediaType contentTyp * @param mapper Object mapper to use. */ public JacksonModule(@NonNull ObjectMapper mapper) { - this(mapper, defaultTypes.getOrDefault(mapper.getClass().getSimpleName(), MediaType.json)); + super(mapper); } /** Creates a Jackson module using the default object mapper from {@link #create(Module...)}. */ public JacksonModule() { - this(create()); - } - - /** - * Add a Jackson module to the object mapper. This method require a dependency injection framework - * which is responsible for provisioning a module instance. - * - * @param module Module type. - * @return This module. - */ - public JacksonModule module(Class module) { - modules.add(module); - return this; - } - - @Override - public void install(@NonNull Jooby application) { - application.decoder(mediaType, this); - application.encoder(mediaType, this); - - ServiceRegistry services = application.getServices(); - Class mapperType = mapper.getClass(); - services.put(mapperType, mapper); - services.put(ObjectMapper.class, mapper); - - // Parsing exception as 400 - application.errorCode(JsonParseException.class, StatusCode.BAD_REQUEST); - application.errorCode(MismatchedInputException.class, StatusCode.BAD_REQUEST); - - // Filter - var defaultProvider = new SimpleFilterProvider().setFailOnUnknownId(false); - mapper.addMixIn(Object.class, ProjectionMixIn.class); - mapper.setFilterProvider(defaultProvider); - var projectionModule = new SimpleModule(); - projectionModule.addSerializer(Projected.class, new JacksonProjectedSerializer(mapper)); - mapper.registerModule(projectionModule); - - application.onStarting( - () -> { - for (Class type : modules) { - Module module = application.require(type); - mapper.registerModule(module); - } - List moreModules = - application.getServices().getOrNull(Reified.list(Module.class)); - if (moreModules != null) { - for (Module module : moreModules) { - mapper.registerModule(module); - } - } - }); - } - - @Override - public Output encode(@NonNull Context ctx, @NonNull Object value) throws Exception { - var factory = ctx.getOutputFactory(); - ctx.setDefaultResponseType(mediaType); - if (value instanceof Projected projected) { - var p = projected.getProjection(); - var writer = - writerCache.computeIfAbsent( - p.getType().getName() + p.toView(), - k -> { - // Use a specialized ObjectWriter with our custom path filter - var filters = - new SimpleFilterProvider().addFilter(FILTER_ID, new JacksonProjectionFilter(p)); - return mapper.writer(filters); - }); - return factory.wrap(writer.writeValueAsBytes(projected.getValue())); - } - // let jackson uses his own cache, so wrap the bytes - return factory.wrap(mapper.writeValueAsBytes(value)); - } - - @Override - public Object decode(Context ctx, Type type) throws Exception { - Body body = ctx.body(); - if (body.isInMemory()) { - if (type == JsonNode.class) { - return mapper.readTree(body.bytes()); - } - return mapper.readValue(body.bytes(), typeFactory.constructType(type)); - } else { - try (InputStream stream = body.stream()) { - if (type == JsonNode.class) { - return mapper.readTree(stream); - } - return mapper.readValue(stream, typeFactory.constructType(type)); - } - } + super(create()); } /** @@ -217,15 +86,6 @@ public Object decode(Context ctx, Type type) throws Exception { * @return Object mapper instance. */ public static ObjectMapper create(Module... modules) { - JsonMapper.Builder builder = - JsonMapper.builder() - .addModule(new ParameterNamesModule()) - .addModule(new Jdk8Module()) - .addModule(new JavaTimeModule()) - .findAndAddModules(); - - Stream.of(modules).forEach(builder::addModule); - - return builder.build(); + return Jackson2Module.create(modules); } } diff --git a/modules/jooby-jackson/src/main/java/io/jooby/jackson/package-info.java b/modules/jooby-jackson/src/main/java/io/jooby/jackson/package-info.java index 12468e3123..50c4576c91 100644 --- a/modules/jooby-jackson/src/main/java/io/jooby/jackson/package-info.java +++ b/modules/jooby-jackson/src/main/java/io/jooby/jackson/package-info.java @@ -1,2 +1,45 @@ +/** + * JSON module using Jackson: https://jooby.io/modules/jackson2. + * + *

Usage: + * + *

{@code
+ * {
+ *
+ *   install(new Jackson2Module());
+ *
+ *   get("/", ctx -> {
+ *     MyObject myObject = ...;
+ *     // send json
+ *     return myObject;
+ *   });
+ *
+ *   post("/", ctx -> {
+ *     // read json
+ *     MyObject myObject = ctx.body(MyObject.class);
+ *     // send json
+ *     return myObject;
+ *   });
+ * }
+ * }
+ * + * For body decoding the client must specify the Content-Type header set to + * application/json. + * + *

You can retrieve the {@link com.fasterxml.jackson.databind.ObjectMapper} via require call: + * + *

{@code
+ * {
+ *
+ *   ObjectMapper mapper = require(ObjectMapper.class);
+ *
+ * }
+ * }
+ * + * Complete documentation is available at: https://jooby.io/modules/jackson2. + * + * @author edgar + * @since 2.0.0 + */ @edu.umd.cs.findbugs.annotations.ReturnValuesAreNonnullByDefault package io.jooby.jackson; diff --git a/modules/jooby-jackson/src/main/java/module-info.java b/modules/jooby-jackson/src/main/java/module-info.java index f66c8fa145..018fb55d15 100644 --- a/modules/jooby-jackson/src/main/java/module-info.java +++ b/modules/jooby-jackson/src/main/java/module-info.java @@ -3,7 +3,52 @@ * Apache License Version 2.0 https://jooby.io/LICENSE.txt * Copyright 2014 Edgar Espina */ -/** Jackson module. */ + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * JSON module using Jackson: https://jooby.io/modules/jackson2. + * + *

Usage: + * + *

{@code
+ * {
+ *
+ *   install(new Jackson2Module());
+ *
+ *   get("/", ctx -> {
+ *     MyObject myObject = ...;
+ *     // send json
+ *     return myObject;
+ *   });
+ *
+ *   post("/", ctx -> {
+ *     // read json
+ *     MyObject myObject = ctx.body(MyObject.class);
+ *     // send json
+ *     return myObject;
+ *   });
+ * }
+ * }
+ * + * For body decoding the client must specify the Content-Type header set to + * application/json. + * + *

You can retrieve the {@link ObjectMapper} via require call: + * + *

{@code
+ * {
+ *
+ *   ObjectMapper mapper = require(ObjectMapper.class);
+ *
+ * }
+ * }
+ * + * Complete documentation is available at: https://jooby.io/modules/jackson2. + * + * @author edgar + * @since 2.0.0 + */ module io.jooby.jackson { exports io.jooby.jackson; diff --git a/modules/jooby-jackson/src/test/java/io/jooby/jackson/JacksonJsonModuleTest.java b/modules/jooby-jackson/src/test/java/io/jooby/jackson/JacksonJsonModuleTest.java index e4bf60dac4..39a397d8ec 100644 --- a/modules/jooby-jackson/src/test/java/io/jooby/jackson/JacksonJsonModuleTest.java +++ b/modules/jooby-jackson/src/test/java/io/jooby/jackson/JacksonJsonModuleTest.java @@ -31,7 +31,7 @@ public void renderJson() throws Exception { Context ctx = mock(Context.class); when(ctx.getOutputFactory()).thenReturn(OutputFactory.create(OutputOptions.small())); - JacksonModule jackson = new JacksonModule(new ObjectMapper()); + Jackson2Module jackson = new Jackson2Module(new ObjectMapper()); var buffer = jackson.encode(ctx, mapOf("k", "v")); assertEquals("{\"k\":\"v\"}", StandardCharsets.UTF_8.decode(buffer.asByteBuffer()).toString()); @@ -49,7 +49,7 @@ public void parseJson() throws Exception { Context ctx = mock(Context.class); when(ctx.body()).thenReturn(body); - JacksonModule jackson = new JacksonModule(new ObjectMapper()); + Jackson2Module jackson = new Jackson2Module(new ObjectMapper()); Map result = (Map) jackson.decode(ctx, Map.class); assertEquals(mapOf("k", "v"), result); @@ -60,7 +60,7 @@ public void renderXml() throws Exception { Context ctx = mock(Context.class); when(ctx.getOutputFactory()).thenReturn(OutputFactory.create(OutputOptions.small())); - JacksonModule jackson = new JacksonModule(new XmlMapper()); + Jackson2Module jackson = new Jackson2Module(new XmlMapper()); var buffer = jackson.encode(ctx, mapOf("k", "v")); assertEquals( @@ -80,7 +80,7 @@ public void parseXml() throws Exception { Context ctx = mock(Context.class); when(ctx.body()).thenReturn(body); - JacksonModule jackson = new JacksonModule(new XmlMapper()); + Jackson2Module jackson = new Jackson2Module(new XmlMapper()); Map result = (Map) jackson.decode(ctx, Map.class); assertEquals(mapOf("k", "v"), result); diff --git a/tests/src/test/java/examples/vertx/VertxPoolApp.java b/tests/src/test/java/examples/vertx/VertxPoolApp.java index 1cf340cac0..82a0fcf402 100644 --- a/tests/src/test/java/examples/vertx/VertxPoolApp.java +++ b/tests/src/test/java/examples/vertx/VertxPoolApp.java @@ -12,7 +12,7 @@ import com.typesafe.config.ConfigValueFactory; import io.jooby.ExecutionMode; import io.jooby.Jooby; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.vertx.VertxHandler; import io.jooby.vertx.VertxModule; import io.jooby.vertx.VertxServer; @@ -38,7 +38,7 @@ public class VertxPoolApp extends Jooby { .withValue( "db.password", ConfigValueFactory.fromAnyRef("benchmarkdbpass")))); - install(new JacksonModule()); + install(new Jackson2Module()); install(new VertxModule()); install(new VertxPgModule(PgBuilder::pool)); diff --git a/tests/src/test/java/io/jooby/i2413/Issue2413.java b/tests/src/test/java/io/jooby/i2413/Issue2413.java index 456966fea7..8ce1ac2c48 100644 --- a/tests/src/test/java/io/jooby/i2413/Issue2413.java +++ b/tests/src/test/java/io/jooby/i2413/Issue2413.java @@ -10,7 +10,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.xml.XmlMapper; import io.jooby.MediaType; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; import io.jooby.test.WebClient; @@ -21,9 +21,9 @@ public void shouldGenerateJsonAndXmlDefaultsJson(ServerTestRunner runner) { runner .define( app -> { - app.install(new JacksonModule(new ObjectMapper())); + app.install(new Jackson2Module(new ObjectMapper())); - app.install(new JacksonModule(new XmlMapper())); + app.install(new Jackson2Module(new XmlMapper())); app.get("/2413", ctx -> new B2413("someId", "someName")); }) @@ -92,9 +92,9 @@ public void shouldGenerateJsonAndXmlDefaultsXml(ServerTestRunner runner) { runner .define( app -> { - app.install(new JacksonModule(new XmlMapper(), MediaType.xml)); + app.install(new Jackson2Module(new XmlMapper(), MediaType.xml)); - app.install(new JacksonModule(new ObjectMapper())); + app.install(new Jackson2Module(new ObjectMapper())); app.get("/2413", ctx -> new B2413("someId", "someName")); }) diff --git a/tests/src/test/java/io/jooby/i2462/Issue2462.java b/tests/src/test/java/io/jooby/i2462/Issue2462.java index b36dd27bd4..abbe59728a 100644 --- a/tests/src/test/java/io/jooby/i2462/Issue2462.java +++ b/tests/src/test/java/io/jooby/i2462/Issue2462.java @@ -10,7 +10,7 @@ import java.util.Arrays; import io.jooby.ServerOptions; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; import io.jooby.test.WebClient; @@ -25,7 +25,7 @@ public void shouldSendMultipleMessageWithoutError(ServerTestRunner runner) { runner .define( app -> { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.sse( "/2462", diff --git a/tests/src/test/java/io/jooby/i2525/Issue2525.java b/tests/src/test/java/io/jooby/i2525/Issue2525.java index 243a6a1a0c..7d12e2b885 100644 --- a/tests/src/test/java/io/jooby/i2525/Issue2525.java +++ b/tests/src/test/java/io/jooby/i2525/Issue2525.java @@ -7,7 +7,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; @@ -17,7 +17,7 @@ public void shouldHandleMultipleAcceptHeaders(ServerTestRunner runner) { runner .define( app -> { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.get( "/2525", ctx -> { diff --git a/tests/src/test/java/io/jooby/i2557/Issue2557.java b/tests/src/test/java/io/jooby/i2557/Issue2557.java index 703c1b33bf..2789dd1334 100644 --- a/tests/src/test/java/io/jooby/i2557/Issue2557.java +++ b/tests/src/test/java/io/jooby/i2557/Issue2557.java @@ -10,7 +10,7 @@ import java.util.UUID; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; import io.jooby.value.ConversionHint; @@ -28,7 +28,7 @@ public void shouldWorkWithEmptyUUID(ServerTestRunner runner) { runner .define( app -> { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.post( "/2557/strict", diff --git a/tests/src/test/java/io/jooby/i2572/Issue2572.java b/tests/src/test/java/io/jooby/i2572/Issue2572.java index 3af9326b40..440dc98db0 100644 --- a/tests/src/test/java/io/jooby/i2572/Issue2572.java +++ b/tests/src/test/java/io/jooby/i2572/Issue2572.java @@ -13,7 +13,7 @@ import org.json.JSONObject; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; @@ -27,7 +27,7 @@ public void onCompleteShouldRunOnCallerThread(ServerTestRunner runner) { Map state = new ConcurrentHashMap<>(); CountDownLatch latch = new CountDownLatch(1); - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.get( "/2572/state", diff --git a/tests/src/test/java/io/jooby/i2613/Issue2613.java b/tests/src/test/java/io/jooby/i2613/Issue2613.java index ddd458e005..a3bb38b072 100644 --- a/tests/src/test/java/io/jooby/i2613/Issue2613.java +++ b/tests/src/test/java/io/jooby/i2613/Issue2613.java @@ -14,7 +14,7 @@ import io.jooby.Context; import io.jooby.MediaType; import io.jooby.MessageEncoder; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; import io.jooby.output.Output; @@ -46,7 +46,7 @@ public void shouldConsiderRouteProduces(ServerTestRunner runner) { .define( app -> { app.encoder(MediaType.html, new ThemeResultEncoder()); - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.get("/2613/json", ctx -> ImmutableMap.of("foo", "bar")).produces(MediaType.json); }) diff --git a/tests/src/test/java/io/jooby/i2806/Issue2806.java b/tests/src/test/java/io/jooby/i2806/Issue2806.java index dc60183d31..7aeab8cdd5 100644 --- a/tests/src/test/java/io/jooby/i2806/Issue2806.java +++ b/tests/src/test/java/io/jooby/i2806/Issue2806.java @@ -12,7 +12,7 @@ import com.google.common.collect.ImmutableMap; import io.jooby.ServerOptions; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; import io.jooby.output.OutputOptions; @@ -30,7 +30,7 @@ public void renderShouldWorkFromErrorHandlerWhenLargeRequestAreSent(ServerTestRu .setMaxRequestSize(ServerOptions._16KB)) .define( app -> { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.error( (ctx, cause, code) -> { diff --git a/tests/src/test/java/io/jooby/i2818/Issue2818.java b/tests/src/test/java/io/jooby/i2818/Issue2818.java index 0ed49989ea..7eeb3abc37 100644 --- a/tests/src/test/java/io/jooby/i2818/Issue2818.java +++ b/tests/src/test/java/io/jooby/i2818/Issue2818.java @@ -10,7 +10,7 @@ import java.nio.charset.StandardCharsets; import java.util.Map; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; @@ -117,7 +117,7 @@ public void shouldRenderBinary(ServerTestRunner runner) { runner .define( app -> { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.ws( "/ws/render-bin", (ctx, initializer) -> { @@ -139,7 +139,7 @@ public void shouldBroadcastRenderBinary(ServerTestRunner runner) { runner .define( app -> { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.ws( "/ws/render-bin", (ctx, initializer) -> { diff --git a/tests/src/test/java/io/jooby/i2923/Issue2923.java b/tests/src/test/java/io/jooby/i2923/Issue2923.java index e725c5e867..93d126e036 100644 --- a/tests/src/test/java/io/jooby/i2923/Issue2923.java +++ b/tests/src/test/java/io/jooby/i2923/Issue2923.java @@ -7,7 +7,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; @@ -18,7 +18,7 @@ public void shouldParseJavaRecord(ServerTestRunner runner) { runner .define( app -> { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.get( "/2923", ctx -> { diff --git a/tests/src/test/java/io/jooby/i3400/Issue3400.java b/tests/src/test/java/io/jooby/i3400/Issue3400.java index 7cd0e8baf7..c5c1cca805 100644 --- a/tests/src/test/java/io/jooby/i3400/Issue3400.java +++ b/tests/src/test/java/io/jooby/i3400/Issue3400.java @@ -8,7 +8,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import io.jooby.Jooby; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; import okhttp3.MediaType; @@ -27,7 +27,7 @@ public void shouldShareDecodersOnMountedResources(ServerTestRunner runner) { runner .define( app -> { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.mount(new AppA()); }) .ready( @@ -48,7 +48,7 @@ public void shouldShareDecodersOnInstalledResources(ServerTestRunner runner) { runner .define( app -> { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.install(AppA::new); }) .ready( diff --git a/tests/src/test/java/io/jooby/i3479/Issue3479.java b/tests/src/test/java/io/jooby/i3479/Issue3479.java index dc3f77f327..0392497531 100644 --- a/tests/src/test/java/io/jooby/i3479/Issue3479.java +++ b/tests/src/test/java/io/jooby/i3479/Issue3479.java @@ -7,7 +7,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; @@ -21,7 +21,7 @@ public void shouldRespectLeadingWhiteSpace(ServerTestRunner runner) { runner .define( app -> { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.sse( "/3479", diff --git a/tests/src/test/java/io/jooby/i3508/App3508.java b/tests/src/test/java/io/jooby/i3508/App3508.java index 0e2cf3cf69..0504225d13 100644 --- a/tests/src/test/java/io/jooby/i3508/App3508.java +++ b/tests/src/test/java/io/jooby/i3508/App3508.java @@ -11,7 +11,7 @@ import com.typesafe.config.ConfigFactory; import io.jooby.Extension; import io.jooby.Jooby; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; public class App3508 extends Jooby { public App3508(Extension validator, boolean withProblemDetails) { @@ -24,7 +24,7 @@ public App3508(Extension validator, boolean withProblemDetails) { getEnvironment().setConfig(problemDetailsConfig.withFallback(getConfig())); } - install(new JacksonModule()); + install(new Jackson2Module()); install(validator); mvc(new Controller3508_()); diff --git a/tests/src/test/java/io/jooby/i3508/Issue3508.java b/tests/src/test/java/io/jooby/i3508/Issue3508.java index a4c628e7c9..e0cf198613 100644 --- a/tests/src/test/java/io/jooby/i3508/Issue3508.java +++ b/tests/src/test/java/io/jooby/i3508/Issue3508.java @@ -24,7 +24,7 @@ import io.jooby.i3508.data.HbvNewAccountRequest; import io.jooby.i3508.data.NewAccountRequest; import io.jooby.i3508.data.Person; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; import io.jooby.validation.BeanValidator; @@ -41,7 +41,7 @@ public void shouldValidateUsingProxy(ServerTestRunner runner) { runner .define( app -> { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.install(new HibernateValidatorModule()); app.get("/3508/query", validate(ctx -> ctx.query().to(Bean3508.class))); diff --git a/tests/src/test/java/io/jooby/i3530/Issue3530.java b/tests/src/test/java/io/jooby/i3530/Issue3530.java index 3c223f52cb..69287228c2 100644 --- a/tests/src/test/java/io/jooby/i3530/Issue3530.java +++ b/tests/src/test/java/io/jooby/i3530/Issue3530.java @@ -9,7 +9,7 @@ import io.jooby.StatusCode; import io.jooby.hibernate.validator.HibernateValidatorModule; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; import io.jooby.validation.BeanValidator; @@ -24,7 +24,7 @@ public void shouldRunValidationOnce(ServerTestRunner runner) { runner .define( app -> { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.install(new HibernateValidatorModule()); app.use(BeanValidator.validate()); diff --git a/tests/src/test/java/io/jooby/i3551/Issue3551.java b/tests/src/test/java/io/jooby/i3551/Issue3551.java index b5b3e102c3..fe1f4e3f4f 100644 --- a/tests/src/test/java/io/jooby/i3551/Issue3551.java +++ b/tests/src/test/java/io/jooby/i3551/Issue3551.java @@ -11,7 +11,7 @@ import io.jooby.ServiceKey; import io.jooby.StatusCode; import io.jooby.guice.GuiceModule; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; @@ -25,7 +25,7 @@ public void shouldEnsureDIOnStartCallbacks(ServerTestRunner runner) { .put( ServiceKey.key(Service3551.class, "service"), new Service3551(app.getEnvironment())); - app.install(new JacksonModule().module(JacksonModule3551.class)); + app.install(new Jackson2Module().module(JacksonModule3551.class)); app.install(new GuiceModule()); diff --git a/tests/src/test/java/io/jooby/i3751/Issue3751.java b/tests/src/test/java/io/jooby/i3751/Issue3751.java index 75f0d95fd8..066238b81c 100644 --- a/tests/src/test/java/io/jooby/i3751/Issue3751.java +++ b/tests/src/test/java/io/jooby/i3751/Issue3751.java @@ -10,7 +10,7 @@ import java.util.List; import io.jooby.hibernate.validator.HibernateValidatorModule; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; import io.jooby.validation.BeanValidator; @@ -25,7 +25,7 @@ public void shouldValidateDataBody(ServerTestRunner runner) { .define( app -> { app.install(new HibernateValidatorModule()); - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.post( "/3751-direct", (ctx -> { diff --git a/tests/src/test/java/io/jooby/i3778/Issue3778.java b/tests/src/test/java/io/jooby/i3778/Issue3778.java index 38f7b3200f..82809e06a8 100644 --- a/tests/src/test/java/io/jooby/i3778/Issue3778.java +++ b/tests/src/test/java/io/jooby/i3778/Issue3778.java @@ -16,7 +16,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.jooby.ExecutionMode; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; import io.jooby.vertx.VertxModule; @@ -48,7 +48,7 @@ private void shouldHandleVertxReactiveResponse(ServerTestRunner runner, boolean if (!onVertx) { app.install(new VertxModule(new VertxOptions().setEventLoopPoolSize(4))); } - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.use(vertx()); diff --git a/tests/src/test/java/io/jooby/i3830/UserToolsTest.java b/tests/src/test/java/io/jooby/i3830/UserToolsTest.java index 9d06b2bf95..08afc2d823 100644 --- a/tests/src/test/java/io/jooby/i3830/UserToolsTest.java +++ b/tests/src/test/java/io/jooby/i3830/UserToolsTest.java @@ -12,7 +12,7 @@ import io.jooby.Extension; import io.jooby.Jooby; import io.jooby.SneakyThrows; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.jackson3.Jackson3Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; @@ -36,7 +36,7 @@ private void setupMcpApp(Jooby app, Extension... extensions) { @ServerTest public void shouldReturnStructuredJsonObjectOnJackson2(ServerTestRunner runner) { runner - .define(app -> setupMcpApp(app, new JacksonModule(), new McpJackson2Module())) + .define(app -> setupMcpApp(app, new Jackson2Module(), new McpJackson2Module())) .ready(assertStructuredJson()); } diff --git a/tests/src/test/java/io/jooby/i3853/Issue3853.java b/tests/src/test/java/io/jooby/i3853/Issue3853.java index 62e663d8a0..061dc717d5 100644 --- a/tests/src/test/java/io/jooby/i3853/Issue3853.java +++ b/tests/src/test/java/io/jooby/i3853/Issue3853.java @@ -14,7 +14,7 @@ import io.jooby.Projected; import io.jooby.Projection; import io.jooby.avaje.jsonb.AvajeJsonbModule; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.jackson3.Jackson3Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; @@ -25,7 +25,7 @@ public class Issue3853 { @ServerTest public void shouldProjectJackson2Data(ServerTestRunner runner) { - shouldProjectData(runner, new JacksonModule()); + shouldProjectData(runner, new Jackson2Module()); } @ServerTest @@ -205,7 +205,7 @@ public void shouldProjectData(ServerTestRunner runner, Extension extension) { @ServerTest public void jackson2ShouldNotThrowInvalidDefinitionException(ServerTestRunner runner) { - jacksonShouldNotThrowInvalidDefinitionException(runner, new JacksonModule()); + jacksonShouldNotThrowInvalidDefinitionException(runner, new Jackson2Module()); } @ServerTest diff --git a/tests/src/test/java/io/jooby/i3853/Issue3853Mvc.java b/tests/src/test/java/io/jooby/i3853/Issue3853Mvc.java index b563a088dd..c3bb4b1cf5 100644 --- a/tests/src/test/java/io/jooby/i3853/Issue3853Mvc.java +++ b/tests/src/test/java/io/jooby/i3853/Issue3853Mvc.java @@ -9,7 +9,7 @@ import io.jooby.Extension; import io.jooby.avaje.jsonb.AvajeJsonbModule; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.jackson3.Jackson3Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; @@ -18,7 +18,7 @@ public class Issue3853Mvc { @ServerTest public void shouldProjectJackson2Data(ServerTestRunner runner) { - shouldProjectData(runner, new JacksonModule()); + shouldProjectData(runner, new Jackson2Module()); } @ServerTest diff --git a/tests/src/test/java/io/jooby/i3863/Jackson2TrpcProtocolTest.java b/tests/src/test/java/io/jooby/i3863/Jackson2TrpcProtocolTest.java index 280f710416..b4596c2695 100644 --- a/tests/src/test/java/io/jooby/i3863/Jackson2TrpcProtocolTest.java +++ b/tests/src/test/java/io/jooby/i3863/Jackson2TrpcProtocolTest.java @@ -6,13 +6,13 @@ package io.jooby.i3863; import io.jooby.Jooby; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.trpc.jackson2.TrpcJackson2Module; public class Jackson2TrpcProtocolTest extends AbstractTrpcProtocolTest { @Override protected void installJsonEngine(Jooby app) { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.install(new TrpcJackson2Module()); } } diff --git a/tests/src/test/java/io/jooby/i3868/Jackson2JsonRpcProtocolTest.java b/tests/src/test/java/io/jooby/i3868/Jackson2JsonRpcProtocolTest.java index 9970dbf280..1060b4013a 100644 --- a/tests/src/test/java/io/jooby/i3868/Jackson2JsonRpcProtocolTest.java +++ b/tests/src/test/java/io/jooby/i3868/Jackson2JsonRpcProtocolTest.java @@ -6,13 +6,13 @@ package io.jooby.i3868; import io.jooby.Jooby; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.jsonrpc.jackson2.JsonRpcJackson2Module; public class Jackson2JsonRpcProtocolTest extends AbstractJsonRpcProtocolTest { @Override protected void installJsonEngine(Jooby app) { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.install(new JsonRpcJackson2Module()); } } diff --git a/tests/src/test/java/io/jooby/problem/data/App.java b/tests/src/test/java/io/jooby/problem/data/App.java index 44833ad3ae..62521fb54e 100644 --- a/tests/src/test/java/io/jooby/problem/data/App.java +++ b/tests/src/test/java/io/jooby/problem/data/App.java @@ -17,7 +17,7 @@ import io.jooby.StatusCode; import io.jooby.exception.InvalidCsrfToken; import io.jooby.exception.StatusCodeException; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.problem.HttpProblem; public class App extends Jooby { @@ -36,7 +36,7 @@ public class App extends Jooby { List.of("io.jooby.exception.UnauthorizedException"))); getEnvironment().setConfig(problemDetailsConfig.withFallback(getConfig())); - install(new JacksonModule()); + install(new Jackson2Module()); get( "/throw-simple-http-problem", diff --git a/tests/src/test/java/io/jooby/test/FeaturedTest.java b/tests/src/test/java/io/jooby/test/FeaturedTest.java index 84352b972f..ab5ed8eaba 100644 --- a/tests/src/test/java/io/jooby/test/FeaturedTest.java +++ b/tests/src/test/java/io/jooby/test/FeaturedTest.java @@ -66,7 +66,7 @@ import io.jooby.handler.HeadHandler; import io.jooby.handler.TraceHandler; import io.jooby.handler.WebVariables; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.jackson3.Jackson3Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; @@ -3044,7 +3044,7 @@ public void render(ServerTestRunner runner) { runner .define( app -> { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.get("/int", ctx -> ctx.render(1)); app.get("/bytes", ctx -> ctx.render("bytes".getBytes(StandardCharsets.UTF_8))); app.get( @@ -3674,7 +3674,7 @@ public void head(ServerTestRunner runner) { runner .define( app -> { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.use(new HeadHandler()); app.get("/fn", ctx -> "string"); @@ -3984,7 +3984,7 @@ public void errorOnParsingFormUrlEncoded(ServerTestRunner runner) { runner .define( app -> { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.post( "/", diff --git a/tests/src/test/java/io/jooby/test/Http2Test.java b/tests/src/test/java/io/jooby/test/Http2Test.java index 89796b2e35..54b37e82df 100644 --- a/tests/src/test/java/io/jooby/test/Http2Test.java +++ b/tests/src/test/java/io/jooby/test/Http2Test.java @@ -31,7 +31,7 @@ import com.google.common.collect.ImmutableMap; import io.jooby.*; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; import okhttp3.*; @@ -45,7 +45,7 @@ public void h2body(ServerTestRunner runner) { .options(new ServerOptions().setHttp2(true)) .define( app -> { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.post( "/h2/multipart", ctx -> { diff --git a/tests/src/test/java/io/jooby/test/Issue1391.java b/tests/src/test/java/io/jooby/test/Issue1391.java index 3cb6d1bd7b..11316cfd0f 100644 --- a/tests/src/test/java/io/jooby/test/Issue1391.java +++ b/tests/src/test/java/io/jooby/test/Issue1391.java @@ -7,7 +7,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; import okhttp3.MediaType; @@ -20,7 +20,7 @@ public void issue1391(ServerTestRunner runner) { runner .define( app -> { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.mvc(new Controller1391_()); }) .ready( diff --git a/tests/src/test/java/io/jooby/test/MvcTest.java b/tests/src/test/java/io/jooby/test/MvcTest.java index 8b4d418a10..a58c39eefc 100644 --- a/tests/src/test/java/io/jooby/test/MvcTest.java +++ b/tests/src/test/java/io/jooby/test/MvcTest.java @@ -19,7 +19,7 @@ import io.jooby.ExecutionMode; import io.jooby.Jooby; import io.jooby.MessageDecoder; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; import okhttp3.FormBody; @@ -470,7 +470,7 @@ public void mvcBody(ServerTestRunner runner) { runner .define( app -> { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.mvc(new MvcBody_()); diff --git a/tests/src/test/java/io/jooby/test/ServerSentEventTest.java b/tests/src/test/java/io/jooby/test/ServerSentEventTest.java index 64301d00a1..a9aeaf4316 100644 --- a/tests/src/test/java/io/jooby/test/ServerSentEventTest.java +++ b/tests/src/test/java/io/jooby/test/ServerSentEventTest.java @@ -22,7 +22,7 @@ import io.jooby.ExecutionMode; import io.jooby.ServerSentMessage; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; @@ -32,7 +32,7 @@ public void shouldSupportsSse(ServerTestRunner runner) { runner .define( app -> { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.sse( "/", @@ -85,7 +85,7 @@ public void shouldUseEncoder(ServerTestRunner runner) { runner .define( app -> { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.sse( "/json", @@ -113,7 +113,7 @@ public void shouldWorkWithEventType(ServerTestRunner runner) { runner .define( app -> { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.sse( "/event-type", @@ -170,7 +170,7 @@ public void shouldWorkWithMultilineMessages(ServerTestRunner runner) { runner .define( app -> { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.sse( "/multi-line", diff --git a/tests/src/test/java/io/jooby/test/WebSocketTest.java b/tests/src/test/java/io/jooby/test/WebSocketTest.java index 2e64bcbdab..9dc18951fd 100644 --- a/tests/src/test/java/io/jooby/test/WebSocketTest.java +++ b/tests/src/test/java/io/jooby/test/WebSocketTest.java @@ -10,7 +10,7 @@ import java.nio.charset.StandardCharsets; import com.fasterxml.jackson.databind.JsonNode; -import io.jooby.jackson.JacksonModule; +import io.jooby.jackson.Jackson2Module; import io.jooby.junit.ServerTest; import io.jooby.junit.ServerTestRunner; @@ -120,7 +120,7 @@ public void webSocketJson(ServerTestRunner runner) { runner .define( app -> { - app.install(new JacksonModule()); + app.install(new Jackson2Module()); app.ws( "/wsjson", diff --git a/tests/src/test/kotlin/i3476/Issue3476.kt b/tests/src/test/kotlin/i3476/Issue3476.kt index f0487cc0c7..2779711e9b 100644 --- a/tests/src/test/kotlin/i3476/Issue3476.kt +++ b/tests/src/test/kotlin/i3476/Issue3476.kt @@ -5,7 +5,7 @@ */ package i3476 -import io.jooby.jackson.JacksonModule +import io.jooby.jackson.Jackson2Module import io.jooby.junit.ServerTest import io.jooby.junit.ServerTestRunner import io.jooby.kt.Kooby @@ -17,7 +17,7 @@ class Issue3476 { runner .use { Kooby { - install(JacksonModule()) + install(Jackson2Module()) mvc(C3476_()) } } diff --git a/tests/src/test/kotlin/i3477/Issue3477.kt b/tests/src/test/kotlin/i3477/Issue3477.kt index de41772092..44c2f597a9 100644 --- a/tests/src/test/kotlin/i3477/Issue3477.kt +++ b/tests/src/test/kotlin/i3477/Issue3477.kt @@ -5,7 +5,7 @@ */ package i3477 -import io.jooby.jackson.JacksonModule +import io.jooby.jackson.Jackson2Module import io.jooby.junit.ServerTest import io.jooby.junit.ServerTestRunner import io.jooby.kt.Kooby @@ -17,7 +17,7 @@ class Issue3477 { runner .use { Kooby { - install(JacksonModule()) + install(Jackson2Module()) mvc(C3477_()) } } diff --git a/tests/src/test/kotlin/io/jooby/FeaturedKotlinTest.kt b/tests/src/test/kotlin/io/jooby/FeaturedKotlinTest.kt index a9faf595f3..b064ec3c1f 100644 --- a/tests/src/test/kotlin/io/jooby/FeaturedKotlinTest.kt +++ b/tests/src/test/kotlin/io/jooby/FeaturedKotlinTest.kt @@ -6,7 +6,7 @@ package io.jooby import io.jooby.internal.mvc.KotlinMvc_ -import io.jooby.jackson.JacksonModule +import io.jooby.jackson.Jackson2Module import io.jooby.junit.ServerTest import io.jooby.junit.ServerTestRunner import io.jooby.kt.Kooby @@ -150,7 +150,7 @@ class FeaturedKotlinTest { runner .use { -> Kooby { - install(JacksonModule()) + install(Jackson2Module()) use { RequestScope.bind("key", UUID.randomUUID().toString())