Skip to content

Commit 20bffc6

Browse files
committed
WIP: buffer: introduce buffer to send API
- still lot to do and improve
1 parent 4c06841 commit 20bffc6

File tree

40 files changed

+382
-122
lines changed

40 files changed

+382
-122
lines changed

jooby/src/main/java/io/jooby/Context.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030

3131
import edu.umd.cs.findbugs.annotations.NonNull;
3232
import edu.umd.cs.findbugs.annotations.Nullable;
33+
import io.jooby.buffer.DataBuffer;
34+
import io.jooby.buffer.DataBufferFactory;
3335
import io.jooby.exception.TypeMismatchException;
3436
import io.jooby.internal.LocaleUtils;
3537
import io.jooby.internal.ParamLookupImpl;
@@ -101,6 +103,8 @@ public interface Context extends Registry {
101103
*/
102104
@NonNull Router getRouter();
103105

106+
@NonNull DataBufferFactory getBufferFactory();
107+
104108
/**
105109
* Forward executing to another route. We use the given path to find a matching route.
106110
*
@@ -1280,6 +1284,14 @@ default ParamLookup lookup() {
12801284
*/
12811285
@NonNull Context send(@NonNull ByteBuffer data);
12821286

1287+
/**
1288+
* Send response data.
1289+
*
1290+
* @param data Response.
1291+
* @return This context.
1292+
*/
1293+
@NonNull Context send(@NonNull DataBuffer data);
1294+
12831295
/**
12841296
* Send response data.
12851297
*

jooby/src/main/java/io/jooby/DefaultContext.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
import edu.umd.cs.findbugs.annotations.NonNull;
3333
import edu.umd.cs.findbugs.annotations.Nullable;
34+
import io.jooby.buffer.DataBufferFactory;
3435
import io.jooby.exception.RegistryException;
3536
import io.jooby.internal.HashValue;
3637
import io.jooby.internal.MissingValue;
@@ -635,4 +636,8 @@ default boolean isSecure() {
635636
}
636637
return this;
637638
}
639+
640+
@NonNull default DataBufferFactory getBufferFactory() {
641+
return getRouter().getBufferFactory();
642+
}
638643
}

jooby/src/main/java/io/jooby/ForwardingContext.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
import edu.umd.cs.findbugs.annotations.NonNull;
2525
import edu.umd.cs.findbugs.annotations.Nullable;
26+
import io.jooby.buffer.DataBuffer;
27+
import io.jooby.buffer.DataBufferFactory;
2628
import io.jooby.exception.RegistryException;
2729

2830
/**
@@ -94,6 +96,11 @@ public Context setAttribute(@NonNull String key, Object value) {
9496
return ctx.getRouter();
9597
}
9698

99+
@NonNull @Override
100+
public DataBufferFactory getBufferFactory() {
101+
return ctx.getBufferFactory();
102+
}
103+
97104
@NonNull @Override
98105
public FlashMap flash() {
99106
return ctx.flash();
@@ -647,6 +654,12 @@ public Context send(@NonNull String data) {
647654
return this;
648655
}
649656

657+
@NonNull @Override
658+
public Context send(@NonNull DataBuffer data) {
659+
ctx.send(data);
660+
return this;
661+
}
662+
650663
@NonNull @Override
651664
public Context send(@NonNull byte[]... data) {
652665
ctx.send(data);

jooby/src/main/java/io/jooby/Jooby.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -930,8 +930,8 @@ private Server loadServer() {
930930
spliteratorUnknownSize(
931931
ServiceLoader.load(Server.class).iterator(), Spliterator.ORDERED),
932932
false)
933-
.collect(Collectors.toList());
934-
if (servers.size() == 0) {
933+
.toList();
934+
if (servers.isEmpty()) {
935935
throw new StartupException("Server not found.");
936936
}
937937
if (servers.size() > 1) {

jooby/src/main/java/io/jooby/MessageEncoder.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
*/
66
package io.jooby;
77

8-
import java.nio.ByteBuffer;
98
import java.nio.charset.StandardCharsets;
109

1110
import edu.umd.cs.findbugs.annotations.NonNull;
1211
import edu.umd.cs.findbugs.annotations.Nullable;
12+
import io.jooby.buffer.DataBuffer;
1313
import io.jooby.exception.NotAcceptableException;
1414

1515
/**
@@ -24,7 +24,7 @@ public interface MessageEncoder {
2424
MessageEncoder TO_STRING =
2525
(ctx, value) -> {
2626
if (ctx.accept(ctx.getResponseType())) {
27-
return ByteBuffer.wrap(value.toString().getBytes(StandardCharsets.UTF_8));
27+
return ctx.getBufferFactory().wrap(value.toString().getBytes(StandardCharsets.UTF_8));
2828
}
2929
throw new NotAcceptableException(ctx.header("Accept").valueOrNull());
3030
};
@@ -38,5 +38,5 @@ public interface MessageEncoder {
3838
* @return Value as byte array or <code>null</code> if given object isn't supported it.
3939
* @throws Exception If something goes wrong.
4040
*/
41-
@Nullable ByteBuffer encode(@NonNull Context ctx, @NonNull Object value) throws Exception;
41+
@Nullable DataBuffer encode(@NonNull Context ctx, @NonNull Object value) throws Exception;
4242
}

jooby/src/main/java/io/jooby/Sender.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import edu.umd.cs.findbugs.annotations.NonNull;
1212
import edu.umd.cs.findbugs.annotations.Nullable;
13+
import io.jooby.buffer.DataBuffer;
1314

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

97+
@NonNull Sender write(@NonNull DataBuffer data, @NonNull Callback callback);
98+
9699
/** Close the sender. */
97100
void close();
98101
}

jooby/src/main/java/io/jooby/ServerSentMessage.java

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77

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

10-
import java.io.ByteArrayOutputStream;
11-
import java.util.Arrays;
10+
import java.util.function.IntPredicate;
1211

1312
import edu.umd.cs.findbugs.annotations.NonNull;
1413
import edu.umd.cs.findbugs.annotations.Nullable;
14+
import io.jooby.buffer.DataBuffer;
1515

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

139-
ByteArrayOutputStream buffer = new ByteArrayOutputStream(bytes.length);
140140
if (id != null) {
141141
buffer.write(ID);
142142
buffer.write(id.toString().getBytes(UTF_8));
@@ -152,29 +152,22 @@ public ServerSentMessage(@NonNull Object data) {
152152
buffer.write(retry.toString().getBytes(UTF_8));
153153
buffer.write(SEPARATOR);
154154
}
155-
/** do multi-line processing: */
155+
/* do multi-line processing: */
156156
buffer.write(DATA);
157-
int offset = 0;
158-
for (int i = 0; i < bytes.length; i++) {
159-
byte ch = bytes[i];
160-
if (ch == '\n') {
161-
buffer.write(Arrays.copyOfRange(bytes, offset, offset + i));
162-
buffer.write(SEPARATOR);
163-
if (i + 1 < bytes.length) {
164-
buffer.write(DATA);
165-
}
166-
offset = i + 1;
157+
IntPredicate nl = ch -> ch == '\n';
158+
var i = message.indexOf(nl, 0);
159+
while (i > 0) {
160+
buffer.write(message.split(i + 1));
161+
if (message.readableByteCount() > 0) {
162+
buffer.write(DATA);
167163
}
164+
i = message.indexOf(nl, 1);
168165
}
169-
if (offset == 0) {
170-
buffer.write(bytes);
171-
} else if (offset < bytes.length) {
172-
buffer.write(Arrays.copyOfRange(bytes, offset, bytes.length));
173-
}
166+
// write any pending bytes
167+
buffer.write(message);
174168
buffer.write(SEPARATOR);
175169
buffer.write(SEPARATOR);
176-
177-
return buffer.toByteArray();
170+
return buffer;
178171
} catch (Exception x) {
179172
throw SneakyThrows.propagate(x);
180173
}

jooby/src/main/java/io/jooby/TemplateEngine.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
*/
66
package io.jooby;
77

8-
import java.nio.ByteBuffer;
98
import java.nio.charset.StandardCharsets;
109
import java.util.Collections;
1110
import java.util.List;
1211

1312
import edu.umd.cs.findbugs.annotations.NonNull;
13+
import io.jooby.buffer.DataBuffer;
1414

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

4040
@Override
41-
default ByteBuffer encode(@NonNull Context ctx, @NonNull Object value) throws Exception {
41+
default DataBuffer encode(@NonNull Context ctx, @NonNull Object value) throws Exception {
4242
// initialize flash and session attributes (if any)
4343
ctx.flash();
4444
ctx.sessionOrNull();
4545

4646
ctx.setDefaultResponseType(MediaType.html);
4747
String output = render(ctx, (ModelAndView) value);
48-
return ByteBuffer.wrap(output.getBytes(StandardCharsets.UTF_8));
48+
return ctx.getBufferFactory().wrap(output.getBytes(StandardCharsets.UTF_8));
4949
}
5050

5151
/**

jooby/src/main/java/io/jooby/internal/HeadContext.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import edu.umd.cs.findbugs.annotations.NonNull;
2121
import io.jooby.*;
22+
import io.jooby.buffer.DataBuffer;
2223

2324
public class HeadContext extends ForwardingContext {
2425
/**
@@ -64,6 +65,14 @@ public Context send(@NonNull ByteBuffer data) {
6465
return this;
6566
}
6667

68+
@NonNull @Override
69+
public Context send(@NonNull DataBuffer data) {
70+
ctx.setResponseLength(data.readableByteCount());
71+
checkSizeHeaders();
72+
ctx.send(StatusCode.OK);
73+
return this;
74+
}
75+
6776
@NonNull @Override
6877
public Context send(@NonNull FileChannel file) {
6978
try {
@@ -176,6 +185,11 @@ public Sender write(@NonNull byte[] data, @NonNull Callback callback) {
176185
return this;
177186
}
178187

188+
@NonNull @Override
189+
public Sender write(@NonNull DataBuffer data, @NonNull Callback callback) {
190+
return this;
191+
}
192+
179193
@Override
180194
public void close() {}
181195
}

jooby/src/main/java/io/jooby/internal/HttpMessageEncoder.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import io.jooby.ModelAndView;
2525
import io.jooby.StatusCode;
2626
import io.jooby.TemplateEngine;
27+
import io.jooby.buffer.DataBuffer;
2728

2829
public class HttpMessageEncoder implements MessageEncoder {
2930

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

4748
@Override
48-
public ByteBuffer encode(@NonNull Context ctx, @NonNull Object value) throws Exception {
49+
public DataBuffer encode(@NonNull Context ctx, @NonNull Object value) throws Exception {
4950
if (value instanceof ModelAndView) {
5051
ModelAndView modelAndView = (ModelAndView) value;
5152
for (TemplateEngine engine : templateEngineList) {
@@ -83,16 +84,17 @@ public ByteBuffer encode(@NonNull Context ctx, @NonNull Object value) throws Exc
8384
ctx.send((FileDownload) value);
8485
return null;
8586
}
87+
var bufferFactory = ctx.getBufferFactory();
8688
/** Strings: */
8789
if (value instanceof CharSequence) {
88-
return ByteBuffer.wrap(value.toString().getBytes(StandardCharsets.UTF_8));
90+
return bufferFactory.wrap(value.toString().getBytes(StandardCharsets.UTF_8));
8991
}
9092
if (value instanceof Number) {
91-
return ByteBuffer.wrap(value.toString().getBytes(StandardCharsets.UTF_8));
93+
return bufferFactory.wrap(value.toString().getBytes(StandardCharsets.UTF_8));
9294
}
9395
/** RawByte: */
9496
if (value instanceof byte[]) {
95-
return ByteBuffer.wrap((byte[]) value);
97+
return bufferFactory.wrap((byte[]) value);
9698
}
9799
if (value instanceof ByteBuffer) {
98100
ctx.send((ByteBuffer) value);

0 commit comments

Comments
 (0)