Skip to content

Commit

Permalink
Simplify JsonService interface (#79)
Browse files Browse the repository at this point in the history
* simplify JsonService interface

* Update JsonService.java
  • Loading branch information
SentryMan authored Nov 26, 2024
1 parent 8975701 commit 0736537
Show file tree
Hide file tree
Showing 11 changed files with 101 additions and 67 deletions.
2 changes: 1 addition & 1 deletion avaje-jex/src/main/java/io/avaje/jex/Context.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
19 changes: 10 additions & 9 deletions avaje-jex/src/main/java/io/avaje/jex/core/CoreServiceManager.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -47,26 +48,26 @@ public static SpiServiceManager create(Jex jex) {
}

@Override
public <T> T jsonRead(Class<T> clazz, SpiContext ctx) {
return jsonService.jsonRead(clazz, ctx);
public <T> T jsonRead(Class<T> 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 <E> void jsonWriteStream(Stream<E> stream, SpiContext ctx) {
public <E> void jsonWriteStream(Stream<E> stream, OutputStream os) {
try (stream) {
jsonService.jsonWriteStream(stream.iterator(), ctx);
jsonService.jsonWriteStream(stream.iterator(), os);
}
}

@Override
public <E> void jsonWriteStream(Iterator<E> iterator, SpiContext ctx) {
public <E> void jsonWriteStream(Iterator<E> iterator, OutputStream os) {
try {
jsonService.jsonWriteStream(iterator, ctx);
jsonService.jsonWriteStream(iterator, os);
} finally {
maybeClose(iterator);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.avaje.jex.spi;
package io.avaje.jex.core;

public class HeaderKeys {

Expand Down
10 changes: 6 additions & 4 deletions avaje-jex/src/main/java/io/avaje/jex/core/SpiServiceManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -18,22 +20,22 @@ public sealed interface SpiServiceManager permits CoreServiceManager, CtxService
/**
* Read and return the type from json request content.
*/
<T> T jsonRead(Class<T> clazz, SpiContext ctx);
<T> T jsonRead(Class<T> 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.
*/
<E> void jsonWriteStream(Stream<E> stream, SpiContext ctx);
<E> void jsonWriteStream(Stream<E> stream, OutputStream ctx);

/**
* Write as json stream to response content.
*/
<E> void jsonWriteStream(Iterator<E> iterator, SpiContext ctx);
<E> void jsonWriteStream(Iterator<E> iterator, OutputStream ctx);

/**
* Maybe close if iterator is a AutoClosable.
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -25,22 +26,18 @@ public JacksonJsonService(ObjectMapper mapper) {
}

@Override
public <T> T jsonRead(Class<T> clazz, SpiContext ctx) {
public <T> T jsonRead(Class<T> 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);
Expand All @@ -57,19 +54,14 @@ public void jsonWrite(Object bean, SpiContext ctx) {
}

@Override
public <T> void jsonWriteStream(Iterator<T> iterator, SpiContext ctx) {
public <T> void jsonWriteStream(Iterator<T> 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();
Expand All @@ -79,4 +71,13 @@ public <T> void jsonWriteStream(Iterator<T> iterator, SpiContext ctx) {
throw new UncheckedIOException(e);
}
}

private <T> void write(Iterator<T> iterator, final JsonGenerator generator) {
try {
mapper.writeValue(generator, iterator.next());
generator.writeRaw('\n');
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -30,20 +32,20 @@ public JsonbJsonService(Jsonb jsonb) {
}

@Override
public <T> T jsonRead(Class<T> clazz, SpiContext ctx) {
// TODO: Handle gzipped content
return jsonb.type(clazz).fromJson(ctx.inputStream());
public <T> T jsonRead(Class<T> 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 <T> void jsonWriteStream(Iterator<T> iterator, SpiContext ctx) {
try (JsonWriter writer = jsonb.writer(ctx.outputStream())) {
public <T> void jsonWriteStream(Iterator<T> iterator, OutputStream os) {
try (JsonWriter writer = jsonb.writer(os)) {
writer.pretty(false);
if (iterator.hasNext()) {
T first = iterator.next();
Expand Down
17 changes: 9 additions & 8 deletions avaje-jex/src/main/java/io/avaje/jex/jdk/CtxServiceManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -41,23 +42,23 @@ public String contextPath() {
}

@Override
public <T> T jsonRead(Class<T> clazz, SpiContext ctx) {
return delegate.jsonRead(clazz, ctx);
public <T> T jsonRead(Class<T> 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 <E> void jsonWriteStream(Stream<E> stream, SpiContext ctx) {
delegate.jsonWriteStream(stream, ctx);
public <E> void jsonWriteStream(Stream<E> stream, OutputStream os) {
delegate.jsonWriteStream(stream, os);
}

@Override
public <E> void jsonWriteStream(Iterator<E> iterator, SpiContext ctx) {
delegate.jsonWriteStream(iterator, ctx);
public <E> void jsonWriteStream(Iterator<E> iterator, OutputStream os) {
delegate.jsonWriteStream(iterator, os);
}

@Override
Expand Down
11 changes: 5 additions & 6 deletions avaje-jex/src/main/java/io/avaje/jex/jdk/JdkContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -169,7 +168,7 @@ public void performRedirect() {

@Override
public <T> T bodyAsClass(Class<T> beanType) {
return mgr.jsonRead(beanType, this);
return mgr.jsonRead(beanType, inputStream());
}

@Override
Expand Down Expand Up @@ -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 <E> Context jsonStream(Stream<E> stream) {
contentType(APPLICATION_X_JSON_STREAM);
mgr.jsonWriteStream(stream, this);
mgr.jsonWriteStream(stream, outputStream());
return this;
}

@Override
public <E> Context jsonStream(Iterator<E> iterator) {
contentType(APPLICATION_X_JSON_STREAM);
mgr.jsonWriteStream(iterator, this);
mgr.jsonWriteStream(iterator, outputStream());
return this;
}

Expand Down
6 changes: 6 additions & 0 deletions avaje-jex/src/main/java/io/avaje/jex/spi/JexExtension.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,11 @@

import io.avaje.spi.Service;

/**
* Extension point for all Jex SPI interfaces
*
* <p>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 {}
39 changes: 31 additions & 8 deletions avaje-jex/src/main/java/io/avaje/jex/spi/JsonService.java
Original file line number Diff line number Diff line change
@@ -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**
*
* <p>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**
*
* <p>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> T jsonRead(Class<T> type, SpiContext ctx);
<T> T jsonRead(Class<T> type, InputStream is);

/**
* Write the bean as JSON response content.
* **Writes a Java Object as JSON to an OutputStream**
*
* <p>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
*/
<E> void jsonWriteStream(Iterator<E> stream, SpiContext ctx);

<E> void jsonWriteStream(Iterator<E> iterator, OutputStream os);
}

0 comments on commit 0736537

Please sign in to comment.