diff --git a/avaje-jex/src/main/java/io/avaje/jex/Context.java b/avaje-jex/src/main/java/io/avaje/jex/Context.java index 00d8cec9..399448c4 100644 --- a/avaje-jex/src/main/java/io/avaje/jex/Context.java +++ b/avaje-jex/src/main/java/io/avaje/jex/Context.java @@ -14,9 +14,9 @@ import com.sun.net.httpserver.HttpExchange; +import io.avaje.jex.core.HeaderKeys; import io.avaje.jex.security.BasicAuthCredentials; import io.avaje.jex.security.Role; -import io.avaje.jex.spi.HeaderKeys; /** * Provides access to functions for handling the request and response. diff --git a/avaje-jex/src/main/java/io/avaje/jex/core/CoreServiceManager.java b/avaje-jex/src/main/java/io/avaje/jex/core/CoreServiceManager.java index 74a3082b..503ef50b 100644 --- a/avaje-jex/src/main/java/io/avaje/jex/core/CoreServiceManager.java +++ b/avaje-jex/src/main/java/io/avaje/jex/core/CoreServiceManager.java @@ -1,5 +1,7 @@ package io.avaje.jex.core; +import java.io.InputStream; +import java.io.OutputStream; import java.io.UncheckedIOException; import java.io.UnsupportedEncodingException; import java.lang.System.Logger.Level; @@ -18,7 +20,6 @@ import io.avaje.jex.Routing; import io.avaje.jex.core.json.JacksonJsonService; import io.avaje.jex.core.json.JsonbJsonService; -import io.avaje.jex.spi.HeaderKeys; import io.avaje.jex.spi.JsonService; import io.avaje.jex.spi.SpiContext; import io.avaje.jex.spi.TemplateRender; @@ -47,26 +48,26 @@ public static SpiServiceManager create(Jex jex) { } @Override - public T jsonRead(Class clazz, SpiContext ctx) { - return jsonService.jsonRead(clazz, ctx); + public T jsonRead(Class clazz, InputStream is) { + return jsonService.jsonRead(clazz, is); } @Override - public void jsonWrite(Object bean, SpiContext ctx) { - jsonService.jsonWrite(bean, ctx); + public void jsonWrite(Object bean, OutputStream os) { + jsonService.jsonWrite(bean, os); } @Override - public void jsonWriteStream(Stream stream, SpiContext ctx) { + public void jsonWriteStream(Stream stream, OutputStream os) { try (stream) { - jsonService.jsonWriteStream(stream.iterator(), ctx); + jsonService.jsonWriteStream(stream.iterator(), os); } } @Override - public void jsonWriteStream(Iterator iterator, SpiContext ctx) { + public void jsonWriteStream(Iterator iterator, OutputStream os) { try { - jsonService.jsonWriteStream(iterator, ctx); + jsonService.jsonWriteStream(iterator, os); } finally { maybeClose(iterator); } diff --git a/avaje-jex/src/main/java/io/avaje/jex/core/ExceptionManager.java b/avaje-jex/src/main/java/io/avaje/jex/core/ExceptionManager.java index ddafb842..f70f0b57 100644 --- a/avaje-jex/src/main/java/io/avaje/jex/core/ExceptionManager.java +++ b/avaje-jex/src/main/java/io/avaje/jex/core/ExceptionManager.java @@ -9,7 +9,6 @@ import io.avaje.jex.http.ErrorCode; import io.avaje.jex.http.HttpResponseException; import io.avaje.jex.http.InternalServerErrorException; -import io.avaje.jex.spi.HeaderKeys; import io.avaje.jex.spi.SpiContext; public final class ExceptionManager { diff --git a/avaje-jex/src/main/java/io/avaje/jex/spi/HeaderKeys.java b/avaje-jex/src/main/java/io/avaje/jex/core/HeaderKeys.java similarity index 96% rename from avaje-jex/src/main/java/io/avaje/jex/spi/HeaderKeys.java rename to avaje-jex/src/main/java/io/avaje/jex/core/HeaderKeys.java index c7eaaf71..c44b8c0e 100644 --- a/avaje-jex/src/main/java/io/avaje/jex/spi/HeaderKeys.java +++ b/avaje-jex/src/main/java/io/avaje/jex/core/HeaderKeys.java @@ -1,4 +1,4 @@ -package io.avaje.jex.spi; +package io.avaje.jex.core; public class HeaderKeys { diff --git a/avaje-jex/src/main/java/io/avaje/jex/core/SpiServiceManager.java b/avaje-jex/src/main/java/io/avaje/jex/core/SpiServiceManager.java index b6b4822c..3586ce15 100644 --- a/avaje-jex/src/main/java/io/avaje/jex/core/SpiServiceManager.java +++ b/avaje-jex/src/main/java/io/avaje/jex/core/SpiServiceManager.java @@ -5,6 +5,8 @@ import io.avaje.jex.jdk.CtxServiceManager; import io.avaje.jex.spi.SpiContext; +import java.io.InputStream; +import java.io.OutputStream; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -18,22 +20,22 @@ public sealed interface SpiServiceManager permits CoreServiceManager, CtxService /** * Read and return the type from json request content. */ - T jsonRead(Class clazz, SpiContext ctx); + T jsonRead(Class clazz, InputStream ctx); /** * Write as json to response content. */ - void jsonWrite(Object bean, SpiContext ctx); + void jsonWrite(Object bean, OutputStream ctx); /** * Write as json stream to response content. */ - void jsonWriteStream(Stream stream, SpiContext ctx); + void jsonWriteStream(Stream stream, OutputStream ctx); /** * Write as json stream to response content. */ - void jsonWriteStream(Iterator iterator, SpiContext ctx); + void jsonWriteStream(Iterator iterator, OutputStream ctx); /** * Maybe close if iterator is a AutoClosable. diff --git a/avaje-jex/src/main/java/io/avaje/jex/core/json/JacksonJsonService.java b/avaje-jex/src/main/java/io/avaje/jex/core/json/JacksonJsonService.java index 5b3f8d20..dfb05387 100644 --- a/avaje-jex/src/main/java/io/avaje/jex/core/json/JacksonJsonService.java +++ b/avaje-jex/src/main/java/io/avaje/jex/core/json/JacksonJsonService.java @@ -1,16 +1,17 @@ package io.avaje.jex.core.json; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.avaje.jex.spi.JsonService; -import io.avaje.jex.spi.SpiContext; - import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.io.UncheckedIOException; import java.util.Iterator; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.avaje.jex.spi.JsonService; + public class JacksonJsonService implements JsonService { private final ObjectMapper mapper; @@ -25,22 +26,18 @@ public JacksonJsonService(ObjectMapper mapper) { } @Override - public T jsonRead(Class clazz, SpiContext ctx) { + public T jsonRead(Class clazz, InputStream is) { try { - // TODO: Handle gzipped content // read direct - return mapper.readValue(ctx.inputStream(), clazz); - //return mapper.readValue(ctx.bodyAsBytes(), clazz); + return mapper.readValue(is, clazz); } catch (IOException e) { throw new UncheckedIOException(e); } } @Override - public void jsonWrite(Object bean, SpiContext ctx) { + public void jsonWrite(Object bean, OutputStream os) { try { - // gzip compression etc ? - OutputStream os = ctx.outputStream(); try (JsonGenerator generator = mapper.createGenerator(os)) { // only flush to underlying OutputStream on success generator.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); @@ -57,19 +54,14 @@ public void jsonWrite(Object bean, SpiContext ctx) { } @Override - public void jsonWriteStream(Iterator iterator, SpiContext ctx) { + public void jsonWriteStream(Iterator iterator, OutputStream os) { final JsonGenerator generator; try { - generator = mapper.createGenerator(ctx.outputStream()); + generator = mapper.createGenerator(os); generator.setPrettyPrinter(null); try { while (iterator.hasNext()) { - try { - mapper.writeValue(generator, iterator.next()); - generator.writeRaw('\n'); - } catch (IOException e) { - throw new UncheckedIOException(e); - } + write(iterator, generator); } } finally { generator.flush(); @@ -79,4 +71,13 @@ public void jsonWriteStream(Iterator iterator, SpiContext ctx) { throw new UncheckedIOException(e); } } + + private void write(Iterator iterator, final JsonGenerator generator) { + try { + mapper.writeValue(generator, iterator.next()); + generator.writeRaw('\n'); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } } diff --git a/avaje-jex/src/main/java/io/avaje/jex/core/json/JsonbJsonService.java b/avaje-jex/src/main/java/io/avaje/jex/core/json/JsonbJsonService.java index 77eaca95..cf09975d 100644 --- a/avaje-jex/src/main/java/io/avaje/jex/core/json/JsonbJsonService.java +++ b/avaje-jex/src/main/java/io/avaje/jex/core/json/JsonbJsonService.java @@ -6,6 +6,8 @@ import io.avaje.jsonb.JsonWriter; import io.avaje.jsonb.Jsonb; +import java.io.InputStream; +import java.io.OutputStream; import java.util.Iterator; /** @@ -30,20 +32,20 @@ public JsonbJsonService(Jsonb jsonb) { } @Override - public T jsonRead(Class clazz, SpiContext ctx) { - // TODO: Handle gzipped content - return jsonb.type(clazz).fromJson(ctx.inputStream()); + public T jsonRead(Class clazz, InputStream is) { + + return jsonb.type(clazz).fromJson(is); } @Override - public void jsonWrite(Object bean, SpiContext ctx) { - // gzip compression etc ? - jsonb.toJson(bean, ctx.outputStream()); + public void jsonWrite(Object bean, OutputStream os) { + + jsonb.toJson(bean, os); } @Override - public void jsonWriteStream(Iterator iterator, SpiContext ctx) { - try (JsonWriter writer = jsonb.writer(ctx.outputStream())) { + public void jsonWriteStream(Iterator iterator, OutputStream os) { + try (JsonWriter writer = jsonb.writer(os)) { writer.pretty(false); if (iterator.hasNext()) { T first = iterator.next(); diff --git a/avaje-jex/src/main/java/io/avaje/jex/jdk/CtxServiceManager.java b/avaje-jex/src/main/java/io/avaje/jex/jdk/CtxServiceManager.java index 93e93c33..522578e1 100644 --- a/avaje-jex/src/main/java/io/avaje/jex/jdk/CtxServiceManager.java +++ b/avaje-jex/src/main/java/io/avaje/jex/jdk/CtxServiceManager.java @@ -5,6 +5,7 @@ import io.avaje.jex.core.SpiServiceManager; import io.avaje.jex.spi.SpiContext; +import java.io.InputStream; import java.io.OutputStream; import java.util.Iterator; import java.util.List; @@ -41,23 +42,23 @@ public String contextPath() { } @Override - public T jsonRead(Class clazz, SpiContext ctx) { - return delegate.jsonRead(clazz, ctx); + public T jsonRead(Class clazz, InputStream is) { + return delegate.jsonRead(clazz, is); } @Override - public void jsonWrite(Object bean, SpiContext ctx) { - delegate.jsonWrite(bean, ctx); + public void jsonWrite(Object bean, OutputStream os) { + delegate.jsonWrite(bean, os); } @Override - public void jsonWriteStream(Stream stream, SpiContext ctx) { - delegate.jsonWriteStream(stream, ctx); + public void jsonWriteStream(Stream stream, OutputStream os) { + delegate.jsonWriteStream(stream, os); } @Override - public void jsonWriteStream(Iterator iterator, SpiContext ctx) { - delegate.jsonWriteStream(iterator, ctx); + public void jsonWriteStream(Iterator iterator, OutputStream os) { + delegate.jsonWriteStream(iterator, os); } @Override diff --git a/avaje-jex/src/main/java/io/avaje/jex/jdk/JdkContext.java b/avaje-jex/src/main/java/io/avaje/jex/jdk/JdkContext.java index 2192ecb7..05597c68 100644 --- a/avaje-jex/src/main/java/io/avaje/jex/jdk/JdkContext.java +++ b/avaje-jex/src/main/java/io/avaje/jex/jdk/JdkContext.java @@ -25,12 +25,11 @@ import io.avaje.jex.Context; import io.avaje.jex.Routing; +import io.avaje.jex.core.HeaderKeys; import io.avaje.jex.http.ErrorCode; -import io.avaje.jex.http.HttpResponseException; import io.avaje.jex.http.RedirectException; import io.avaje.jex.security.BasicAuthCredentials; import io.avaje.jex.security.Role; -import io.avaje.jex.spi.HeaderKeys; import io.avaje.jex.spi.SpiContext; class JdkContext implements Context, SpiContext { @@ -169,7 +168,7 @@ public void performRedirect() { @Override public T bodyAsClass(Class beanType) { - return mgr.jsonRead(beanType, this); + return mgr.jsonRead(beanType, inputStream()); } @Override @@ -316,21 +315,21 @@ public int status() { @Override public Context json(Object bean) { contentType(APPLICATION_JSON); - mgr.jsonWrite(bean, this); + mgr.jsonWrite(bean, outputStream()); return this; } @Override public Context jsonStream(Stream stream) { contentType(APPLICATION_X_JSON_STREAM); - mgr.jsonWriteStream(stream, this); + mgr.jsonWriteStream(stream, outputStream()); return this; } @Override public Context jsonStream(Iterator iterator) { contentType(APPLICATION_X_JSON_STREAM); - mgr.jsonWriteStream(iterator, this); + mgr.jsonWriteStream(iterator, outputStream()); return this; } diff --git a/avaje-jex/src/main/java/io/avaje/jex/spi/JexExtension.java b/avaje-jex/src/main/java/io/avaje/jex/spi/JexExtension.java index a0eb3947..bc179508 100644 --- a/avaje-jex/src/main/java/io/avaje/jex/spi/JexExtension.java +++ b/avaje-jex/src/main/java/io/avaje/jex/spi/JexExtension.java @@ -2,5 +2,11 @@ import io.avaje.spi.Service; +/** + * Extension point for all Jex SPI interfaces + * + *

All types that implement this interface must be registered as an entry in {@code + * META-INF/services/io.avaje.jex.spi.JexExtension } for it to be loaded by Jex + */ @Service public sealed interface JexExtension permits JsonService, TemplateRender {} diff --git a/avaje-jex/src/main/java/io/avaje/jex/spi/JsonService.java b/avaje-jex/src/main/java/io/avaje/jex/spi/JsonService.java index 91834abb..d068622d 100644 --- a/avaje-jex/src/main/java/io/avaje/jex/spi/JsonService.java +++ b/avaje-jex/src/main/java/io/avaje/jex/spi/JsonService.java @@ -1,25 +1,48 @@ package io.avaje.jex.spi; +import java.io.InputStream; +import java.io.OutputStream; import java.util.Iterator; /** - * HttpService used to convert request/response bodies to beans. + * **JsonService** + * + *

A service responsible for handling JSON-based request and response bodies. + * + * @see {@link JexExtension} for SPI registration details. */ public non-sealed interface JsonService extends JexExtension { /** - * Read the request body as a bean and return the bean. + * **Reads JSON from an InputStream** + * + *

Reads a JSON-formatted input stream and deserializes it into a Java object of the specified + * type. + * + * @param type the Class object of the desired type + * @param is the input stream containing the JSON data + * @return the deserialized object */ - T jsonRead(Class type, SpiContext ctx); + T jsonRead(Class type, InputStream is); /** - * Write the bean as JSON response content. + * **Writes a Java Object as JSON to an OutputStream** + * + *

Serializes a Java object into JSON format and writes the resulting JSON to the specified + * output stream. + * + * @param bean the Java object to be serialized + * @param os the output stream to write the JSON data to */ - void jsonWrite(Object bean, SpiContext ctx); + void jsonWrite(Object bean, OutputStream os); /** - * Write the beans as {@literal x-json-stream } JSON with new line delimiter. + * Serializes a stream of Java objects into a JSON-Stream format, using the {@code x-json-stream} + * media type. Each object in the stream is serialized as a separate JSON object, and the objects + * are separated by newlines. + * + * @param iterator the stream of objects to be serialized + * @param os the output stream to write the JSON-Stream data to */ - void jsonWriteStream(Iterator stream, SpiContext ctx); - + void jsonWriteStream(Iterator iterator, OutputStream os); }