Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}