Skip to content

Commit

Permalink
WIP: buffer: introduce buffer to send API
Browse files Browse the repository at this point in the history
- still lot to do and improve
  • Loading branch information
jknack committed Apr 8, 2024
1 parent 72fadf6 commit d3773e7
Show file tree
Hide file tree
Showing 40 changed files with 382 additions and 122 deletions.
12 changes: 12 additions & 0 deletions jooby/src/main/java/io/jooby/Context.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import io.jooby.buffer.DataBuffer;
import io.jooby.buffer.DataBufferFactory;
import io.jooby.exception.TypeMismatchException;
import io.jooby.internal.LocaleUtils;
import io.jooby.internal.ParamLookupImpl;
Expand Down Expand Up @@ -101,6 +103,8 @@ public interface Context extends Registry {
*/
@NonNull Router getRouter();

@NonNull DataBufferFactory getBufferFactory();

/**
* Forward executing to another route. We use the given path to find a matching route.
*
Expand Down Expand Up @@ -1280,6 +1284,14 @@ default ParamLookup lookup() {
*/
@NonNull Context send(@NonNull ByteBuffer data);

/**
* Send response data.
*
* @param data Response.
* @return This context.
*/
@NonNull Context send(@NonNull DataBuffer data);

/**
* Send response data.
*
Expand Down
5 changes: 5 additions & 0 deletions jooby/src/main/java/io/jooby/DefaultContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import io.jooby.buffer.DataBufferFactory;
import io.jooby.exception.RegistryException;
import io.jooby.internal.HashValue;
import io.jooby.internal.MissingValue;
Expand Down Expand Up @@ -635,4 +636,8 @@ default boolean isSecure() {
}
return this;
}

@NonNull default DataBufferFactory getBufferFactory() {
return getRouter().getBufferFactory();
}
}
13 changes: 13 additions & 0 deletions jooby/src/main/java/io/jooby/ForwardingContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import io.jooby.buffer.DataBuffer;
import io.jooby.buffer.DataBufferFactory;
import io.jooby.exception.RegistryException;

/**
Expand Down Expand Up @@ -94,6 +96,11 @@ public Context setAttribute(@NonNull String key, Object value) {
return ctx.getRouter();
}

@NonNull @Override
public DataBufferFactory getBufferFactory() {
return ctx.getBufferFactory();
}

@NonNull @Override
public FlashMap flash() {
return ctx.flash();
Expand Down Expand Up @@ -647,6 +654,12 @@ public Context send(@NonNull String data) {
return this;
}

@NonNull @Override
public Context send(@NonNull DataBuffer data) {
ctx.send(data);
return this;
}

@NonNull @Override
public Context send(@NonNull byte[]... data) {
ctx.send(data);
Expand Down
4 changes: 2 additions & 2 deletions jooby/src/main/java/io/jooby/Jooby.java
Original file line number Diff line number Diff line change
Expand Up @@ -930,8 +930,8 @@ private Server loadServer() {
spliteratorUnknownSize(
ServiceLoader.load(Server.class).iterator(), Spliterator.ORDERED),
false)
.collect(Collectors.toList());
if (servers.size() == 0) {
.toList();
if (servers.isEmpty()) {
throw new StartupException("Server not found.");
}
if (servers.size() > 1) {
Expand Down
6 changes: 3 additions & 3 deletions jooby/src/main/java/io/jooby/MessageEncoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
*/
package io.jooby;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import io.jooby.buffer.DataBuffer;
import io.jooby.exception.NotAcceptableException;

/**
Expand All @@ -24,7 +24,7 @@ public interface MessageEncoder {
MessageEncoder TO_STRING =
(ctx, value) -> {
if (ctx.accept(ctx.getResponseType())) {
return ByteBuffer.wrap(value.toString().getBytes(StandardCharsets.UTF_8));
return ctx.getBufferFactory().wrap(value.toString().getBytes(StandardCharsets.UTF_8));
}
throw new NotAcceptableException(ctx.header("Accept").valueOrNull());
};
Expand All @@ -38,5 +38,5 @@ public interface MessageEncoder {
* @return Value as byte array or <code>null</code> if given object isn't supported it.
* @throws Exception If something goes wrong.
*/
@Nullable ByteBuffer encode(@NonNull Context ctx, @NonNull Object value) throws Exception;
@Nullable DataBuffer encode(@NonNull Context ctx, @NonNull Object value) throws Exception;
}
3 changes: 3 additions & 0 deletions jooby/src/main/java/io/jooby/Sender.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import io.jooby.buffer.DataBuffer;

/**
* Non-blocking sender. Reactive responses uses this class to send partial data in non-blocking
Expand Down Expand Up @@ -93,6 +94,8 @@ interface Callback {
*/
@NonNull Sender write(@NonNull byte[] data, @NonNull Callback callback);

@NonNull Sender write(@NonNull DataBuffer data, @NonNull Callback callback);

/** Close the sender. */
void close();
}
41 changes: 17 additions & 24 deletions jooby/src/main/java/io/jooby/ServerSentMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@

import static java.nio.charset.StandardCharsets.UTF_8;

import java.io.ByteArrayOutputStream;
import java.util.Arrays;
import java.util.function.IntPredicate;

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import io.jooby.buffer.DataBuffer;

/**
* Server-Sent message.
Expand Down Expand Up @@ -129,14 +129,14 @@ public ServerSentMessage(@NonNull Object data) {
* @param ctx Web context. To encode complex objects.
* @return Encoded data.
*/
public @NonNull byte[] toByteArray(@NonNull Context ctx) {
public @NonNull DataBuffer toByteArray(@NonNull Context ctx) {
try {
Route route = ctx.getRoute();
MessageEncoder encoder = route.getEncoder();
// TODO: ByteBuffer fix me this need to be better once we add buffer API
var bytes = encoder.encode(ctx, data).array();
var bufferFactory = ctx.getBufferFactory();
var buffer = bufferFactory.allocateBuffer();
var message = encoder.encode(ctx, data);

ByteArrayOutputStream buffer = new ByteArrayOutputStream(bytes.length);
if (id != null) {
buffer.write(ID);
buffer.write(id.toString().getBytes(UTF_8));
Expand All @@ -152,29 +152,22 @@ public ServerSentMessage(@NonNull Object data) {
buffer.write(retry.toString().getBytes(UTF_8));
buffer.write(SEPARATOR);
}
/** do multi-line processing: */
/* do multi-line processing: */
buffer.write(DATA);
int offset = 0;
for (int i = 0; i < bytes.length; i++) {
byte ch = bytes[i];
if (ch == '\n') {
buffer.write(Arrays.copyOfRange(bytes, offset, offset + i));
buffer.write(SEPARATOR);
if (i + 1 < bytes.length) {
buffer.write(DATA);
}
offset = i + 1;
IntPredicate nl = ch -> ch == '\n';
var i = message.indexOf(nl, 0);
while (i > 0) {
buffer.write(message.split(i + 1));
if (message.readableByteCount() > 0) {
buffer.write(DATA);
}
i = message.indexOf(nl, 1);
}
if (offset == 0) {
buffer.write(bytes);
} else if (offset < bytes.length) {
buffer.write(Arrays.copyOfRange(bytes, offset, bytes.length));
}
// write any pending bytes
buffer.write(message);
buffer.write(SEPARATOR);
buffer.write(SEPARATOR);

return buffer.toByteArray();
return buffer;
} catch (Exception x) {
throw SneakyThrows.propagate(x);
}
Expand Down
6 changes: 3 additions & 3 deletions jooby/src/main/java/io/jooby/TemplateEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
*/
package io.jooby;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;

import edu.umd.cs.findbugs.annotations.NonNull;
import io.jooby.buffer.DataBuffer;

/**
* Template engine renderer. This class renderer instances of {@link ModelAndView} objects. Template
Expand Down Expand Up @@ -38,14 +38,14 @@ public interface TemplateEngine extends MessageEncoder {
String render(Context ctx, ModelAndView modelAndView) throws Exception;

@Override
default ByteBuffer encode(@NonNull Context ctx, @NonNull Object value) throws Exception {
default DataBuffer encode(@NonNull Context ctx, @NonNull Object value) throws Exception {
// initialize flash and session attributes (if any)
ctx.flash();
ctx.sessionOrNull();

ctx.setDefaultResponseType(MediaType.html);
String output = render(ctx, (ModelAndView) value);
return ByteBuffer.wrap(output.getBytes(StandardCharsets.UTF_8));
return ctx.getBufferFactory().wrap(output.getBytes(StandardCharsets.UTF_8));
}

/**
Expand Down
14 changes: 14 additions & 0 deletions jooby/src/main/java/io/jooby/internal/HeadContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import edu.umd.cs.findbugs.annotations.NonNull;
import io.jooby.*;
import io.jooby.buffer.DataBuffer;

public class HeadContext extends ForwardingContext {
/**
Expand Down Expand Up @@ -64,6 +65,14 @@ public Context send(@NonNull ByteBuffer data) {
return this;
}

@NonNull @Override
public Context send(@NonNull DataBuffer data) {
ctx.setResponseLength(data.readableByteCount());
checkSizeHeaders();
ctx.send(StatusCode.OK);
return this;
}

@NonNull @Override
public Context send(@NonNull FileChannel file) {
try {
Expand Down Expand Up @@ -176,6 +185,11 @@ public Sender write(@NonNull byte[] data, @NonNull Callback callback) {
return this;
}

@NonNull @Override
public Sender write(@NonNull DataBuffer data, @NonNull Callback callback) {
return this;
}

@Override
public void close() {}
}
Expand Down
10 changes: 6 additions & 4 deletions jooby/src/main/java/io/jooby/internal/HttpMessageEncoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import io.jooby.ModelAndView;
import io.jooby.StatusCode;
import io.jooby.TemplateEngine;
import io.jooby.buffer.DataBuffer;

public class HttpMessageEncoder implements MessageEncoder {

Expand All @@ -45,7 +46,7 @@ public HttpMessageEncoder add(MediaType type, MessageEncoder encoder) {
}

@Override
public ByteBuffer encode(@NonNull Context ctx, @NonNull Object value) throws Exception {
public DataBuffer encode(@NonNull Context ctx, @NonNull Object value) throws Exception {
if (value instanceof ModelAndView) {
ModelAndView modelAndView = (ModelAndView) value;
for (TemplateEngine engine : templateEngineList) {
Expand Down Expand Up @@ -83,16 +84,17 @@ public ByteBuffer encode(@NonNull Context ctx, @NonNull Object value) throws Exc
ctx.send((FileDownload) value);
return null;
}
var bufferFactory = ctx.getBufferFactory();
/** Strings: */
if (value instanceof CharSequence) {
return ByteBuffer.wrap(value.toString().getBytes(StandardCharsets.UTF_8));
return bufferFactory.wrap(value.toString().getBytes(StandardCharsets.UTF_8));
}
if (value instanceof Number) {
return ByteBuffer.wrap(value.toString().getBytes(StandardCharsets.UTF_8));
return bufferFactory.wrap(value.toString().getBytes(StandardCharsets.UTF_8));
}
/** RawByte: */
if (value instanceof byte[]) {
return ByteBuffer.wrap((byte[]) value);
return bufferFactory.wrap((byte[]) value);
}
if (value instanceof ByteBuffer) {
ctx.send((ByteBuffer) value);
Expand Down
6 changes: 6 additions & 0 deletions jooby/src/main/java/io/jooby/internal/RouterImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,12 @@ public Executor getWorker() {

@NonNull @Override
public DataBufferFactory getBufferFactory() {
if (bufferFactory == null) {
bufferFactory =
ServiceLoader.load(DataBufferFactory.class)
.findFirst()
.orElseGet(DefaultDataBufferFactory::new);
}
return bufferFactory;
}

Expand Down
10 changes: 10 additions & 0 deletions jooby/src/main/java/io/jooby/internal/WebSocketSender.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import io.jooby.MediaType;
import io.jooby.StatusCode;
import io.jooby.WebSocket;
import io.jooby.buffer.DataBuffer;

public class WebSocketSender extends ForwardingContext implements DefaultContext {

Expand Down Expand Up @@ -67,6 +68,15 @@ public Context send(@NonNull ByteBuffer data) {
return this;
}

@NonNull @Override
public Context send(@NonNull DataBuffer data) {
// TODO; FIX Add ws.
try (var it = data.readableByteBuffers()) {
this.send(it.next());
}
return this;
}

@NonNull @Override
public Context render(@NonNull Object value) {
DefaultContext.super.render(value);
Expand Down
Loading

0 comments on commit d3773e7

Please sign in to comment.