diff --git a/jooby/src/main/java/io/jooby/BeanConverter.java b/jooby/src/main/java/io/jooby/BeanConverter.java
deleted file mode 100644
index e1e9865f61..0000000000
--- a/jooby/src/main/java/io/jooby/BeanConverter.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Jooby https://jooby.io
- * Apache License Version 2.0 https://jooby.io/LICENSE.txt
- * Copyright 2014 Edgar Espina
- */
-package io.jooby;
-
-import edu.umd.cs.findbugs.annotations.NonNull;
-
-/**
- * Value converter for complex values that come from query, path, form, etc... parameters into more
- * specific type.
- *
- * It is an extension point for {@link ValueNode#to(Class)} calls.
- */
-public interface BeanConverter extends ValueConverter {
-
- /**
- * True if the converter applies for the given type.
- *
- * @param type Conversion type.
- * @return True if the converter applies for the given type.
- */
- boolean supports(@NonNull Class> type);
-
- /**
- * Convert a node value into more specific type.
- *
- * @param node Value value.
- * @param type Requested type.
- * @return Converted value.
- */
- Object convert(@NonNull ValueNode node, @NonNull Class> type);
-}
diff --git a/jooby/src/main/java/io/jooby/Body.java b/jooby/src/main/java/io/jooby/Body.java
index ffc0fb0f53..48128b4415 100644
--- a/jooby/src/main/java/io/jooby/Body.java
+++ b/jooby/src/main/java/io/jooby/Body.java
@@ -10,7 +10,7 @@
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.Charset;
import java.nio.file.Path;
-import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
import java.util.Set;
@@ -20,6 +20,8 @@
import io.jooby.internal.ByteArrayBody;
import io.jooby.internal.FileBody;
import io.jooby.internal.InputStreamBody;
+import io.jooby.internal.MissingValue;
+import io.jooby.value.Value;
/**
* HTTP body value. Allows to access HTTP body as string, byte[], stream, etc..
@@ -30,7 +32,7 @@
* @author edgar
* @since 2.0.0
*/
-public interface Body extends ValueNode {
+public interface Body extends Value {
/**
* HTTP body as string.
@@ -38,7 +40,7 @@ public interface Body extends ValueNode {
* @param charset Charset.
* @return Body as string.
*/
- default @NonNull String value(@NonNull Charset charset) {
+ default String value(@NonNull Charset charset) {
byte[] bytes = bytes();
if (bytes.length == 0) {
throw new MissingValueException("body");
@@ -46,6 +48,26 @@ public interface Body extends ValueNode {
return new String(bytes, charset);
}
+ @Override
+ default int size() {
+ return 1;
+ }
+
+ @Override
+ default Value get(int index) {
+ return get(Integer.toString(index));
+ }
+
+ @Override
+ default Value get(@NonNull String name) {
+ return new MissingValue(name);
+ }
+
+ @Override
+ default Iterator iterator() {
+ return List.of((Value) this).iterator();
+ }
+
/**
* HTTP body as byte array.
*
@@ -69,34 +91,34 @@ public interface Body extends ValueNode {
long getSize();
/**
- * Body as readable channel.
+ * Body as a readable channel.
*
- * @return Body as readable channel.
+ * @return Body as a readable channel.
*/
- @NonNull ReadableByteChannel channel();
+ ReadableByteChannel channel();
/**
* Body as input stream.
*
* @return Body as input stream.
*/
- @NonNull InputStream stream();
+ InputStream stream();
- @NonNull @Override
+ @Override
default List toList(@NonNull Class type) {
return to(Reified.list(type).getType());
}
- default @NonNull @Override List toList() {
- return Collections.singletonList(value());
+ default @Override List toList() {
+ return List.of(value());
}
- default @NonNull @Override Set toSet() {
- return Collections.singleton(value());
+ default @Override Set toSet() {
+ return Set.of(value());
}
@Override
- default @NonNull T to(@NonNull Class type) {
+ default T to(@NonNull Class type) {
return to((Type) type);
}
@@ -111,7 +133,7 @@ default List toList(@NonNull Class type) {
* @param Generic type.
* @return Converted value.
*/
- @NonNull T to(@NonNull Type type);
+ T to(@NonNull Type type);
/**
* Convert this body into the given type.
@@ -133,7 +155,7 @@ default List toList(@NonNull Class type) {
* @param ctx Current context.
* @return Empty body.
*/
- static @NonNull Body empty(@NonNull Context ctx) {
+ static Body empty(@NonNull Context ctx) {
return ByteArrayBody.empty(ctx);
}
@@ -145,7 +167,7 @@ default List toList(@NonNull Class type) {
* @param size Size in bytes or -1.
* @return A new body.
*/
- static @NonNull Body of(@NonNull Context ctx, @NonNull InputStream stream, long size) {
+ static Body of(@NonNull Context ctx, @NonNull InputStream stream, long size) {
return new InputStreamBody(ctx, stream, size);
}
@@ -156,7 +178,7 @@ default List toList(@NonNull Class type) {
* @param bytes byte array.
* @return A new body.
*/
- static @NonNull Body of(@NonNull Context ctx, @NonNull byte[] bytes) {
+ static Body of(@NonNull Context ctx, @NonNull byte[] bytes) {
return new ByteArrayBody(ctx, bytes);
}
@@ -167,7 +189,7 @@ default List toList(@NonNull Class type) {
* @param file File.
* @return A new body.
*/
- static @NonNull Body of(@NonNull Context ctx, @NonNull Path file) {
+ static Body of(@NonNull Context ctx, @NonNull Path file) {
return new FileBody(ctx, file);
}
}
diff --git a/jooby/src/main/java/io/jooby/Context.java b/jooby/src/main/java/io/jooby/Context.java
index 690e2a58ab..10d4e79d56 100644
--- a/jooby/src/main/java/io/jooby/Context.java
+++ b/jooby/src/main/java/io/jooby/Context.java
@@ -18,7 +18,6 @@
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
-import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
@@ -30,13 +29,13 @@
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;
import io.jooby.internal.ReadOnlyContext;
import io.jooby.internal.WebSocketSender;
+import io.jooby.output.Output;
+import io.jooby.output.OutputFactory;
+import io.jooby.value.Value;
+import io.jooby.value.ValueFactory;
/**
* HTTP context allows you to interact with the HTTP Request and manipulate the HTTP Response.
@@ -116,7 +115,7 @@ private static Selector single() {
*
* @return Mutable Context attributes.
*/
- @NonNull Map getAttributes();
+ Map getAttributes();
/**
* Get an attribute by his key. This is just an utility method around {@link #getAttributes()}.
@@ -135,16 +134,16 @@ private static Selector single() {
* @param value Attribute value.
* @return This router.
*/
- @NonNull Context setAttribute(@NonNull String key, Object value);
+ Context setAttribute(@NonNull String key, Object value);
/**
* Get the HTTP router (usually this represents an instance of {@link Jooby}.
*
* @return HTTP router (usually this represents an instance of {@link Jooby}.
*/
- @NonNull Router getRouter();
+ Router getRouter();
- @NonNull DataBufferFactory getBufferFactory();
+ OutputFactory getOutputFactory();
/**
* Forward executing to another route. We use the given path to find a matching route.
@@ -154,33 +153,7 @@ private static Selector single() {
* @param path Path to forward the request.
* @return Forward result.
*/
- @NonNull Object forward(@NonNull String path);
-
- /**
- * Converts a value (single or hash) into the given type.
- *
- * @param value Value to convert.
- * @param type Expected type.
- * @param Generic type.
- * @return Converted value.
- */
- default @NonNull T convert(@NonNull ValueNode value, @NonNull Class type) {
- T result = convertOrNull(value, type);
- if (result == null) {
- throw new TypeMismatchException(value.name(), type);
- }
- return result;
- }
-
- /**
- * Converts a value (single or hash) into the given type.
- *
- * @param value Value to convert.
- * @param type Expected type.
- * @param Generic type.
- * @return Converted value or null.
- */
- @Nullable T convertOrNull(@NonNull ValueNode value, @NonNull Class type);
+ Object forward(@NonNull String path);
/*
* **********************************************************************************************
@@ -193,7 +166,7 @@ private static Selector single() {
*
* @return Flash map.
*/
- @NonNull FlashMap flash();
+ FlashMap flash();
/**
* Flash map or null when no flash cookie exists.
@@ -202,20 +175,22 @@ private static Selector single() {
*/
@Nullable FlashMap flashOrNull();
+ ValueFactory getValueFactory();
+
/**
* Get a flash attribute.
*
* @param name Attribute's name.
* @return Flash attribute.
*/
- @NonNull Value flash(@NonNull String name);
+ Value flash(@NonNull String name);
/**
* Find a session or creates a new session.
*
* @return Session.
*/
- @NonNull Session session();
+ Session session();
/**
* Find a session attribute using the given name. If there is no session or attribute under that
@@ -224,7 +199,7 @@ private static Selector single() {
* @param name Attribute's name.
* @return Session's attribute or missing.
*/
- @NonNull Value session(@NonNull String name);
+ Value session(@NonNull String name);
/**
* Find an existing session.
@@ -239,21 +214,21 @@ private static Selector single() {
* @param name Cookie's name.
* @return Cookie value.
*/
- @NonNull Value cookie(@NonNull String name);
+ Value cookie(@NonNull String name);
/**
* Request cookies.
*
* @return Request cookies.
*/
- @NonNull Map cookieMap();
+ Map cookieMap();
/**
* HTTP method in upper-case form.
*
* @return HTTP method in upper-case form.
*/
- @NonNull String getMethod();
+ String getMethod();
/**
* Set HTTP method in upper-case form.
@@ -261,14 +236,14 @@ private static Selector single() {
* @param method HTTP method in upper-case form.
* @return This context.
*/
- @NonNull Context setMethod(@NonNull String method);
+ Context setMethod(@NonNull String method);
/**
* Matching route.
*
* @return Matching route.
*/
- @NonNull Route getRoute();
+ Route getRoute();
/**
* Check if the request path matches the given pattern.
@@ -284,14 +259,14 @@ private static Selector single() {
* @param route Matching route.
* @return This context.
*/
- @NonNull Context setRoute(@NonNull Route route);
+ Context setRoute(@NonNull Route route);
/**
* Get application context path (a.k.a as base path).
*
* @return Application context path (a.k.a as base path).
*/
- default @NonNull String getContextPath() {
+ default String getContextPath() {
return getRouter().getContextPath();
}
@@ -300,7 +275,7 @@ private static Selector single() {
*
* @return Request path without decoding (a.k.a raw Path) without query string.
*/
- @NonNull String getRequestPath();
+ String getRequestPath();
/**
* Set request path. This is usually done by Web Server or framework, but by user.
@@ -308,7 +283,7 @@ private static Selector single() {
* @param path Request path.
* @return This context.
*/
- @NonNull Context setRequestPath(@NonNull String path);
+ Context setRequestPath(@NonNull String path);
/**
* Path variable. Value is decoded.
@@ -316,7 +291,7 @@ private static Selector single() {
* @param name Path key.
* @return Associated value or a missing value, but never a null reference.
*/
- @NonNull Value path(@NonNull String name);
+ Value path(@NonNull String name);
/**
* Convert the {@link #pathMap()} to the given type.
@@ -325,14 +300,14 @@ private static Selector single() {
* @param Target type.
* @return Instance of target type.
*/
- @NonNull T path(@NonNull Class type);
+ T path(@NonNull Class type);
/**
- * Convert {@link #pathMap()} to a {@link ValueNode} object.
+ * Convert {@link #pathMap()} to a {@link Value} object.
*
* @return A value object.
*/
- @NonNull ValueNode path();
+ Value path();
/**
* Path map represent all the path keys with their values.
@@ -351,7 +326,7 @@ private static Selector single() {
*
* @return Path map from path pattern.
*/
- @NonNull Map pathMap();
+ Map pathMap();
/**
* Set path map. This method is part of public API but shouldn't be use it by application code.
@@ -359,7 +334,7 @@ private static Selector single() {
* @param pathMap Path map.
* @return This context.
*/
- @NonNull Context setPathMap(@NonNull Map pathMap);
+ Context setPathMap(@NonNull Map pathMap);
/* **********************************************************************************************
* Query String API
@@ -367,11 +342,11 @@ private static Selector single() {
*/
/**
- * Query string as {@link ValueNode} object.
+ * Query string as {@link Value} object.
*
- * @return Query string as {@link ValueNode} object.
+ * @return Query string as {@link Value} object.
*/
- @NonNull QueryString query();
+ QueryString query();
/**
* Get a query parameter that matches the given name.
@@ -389,7 +364,7 @@ private static Selector single() {
* @param name Parameter name.
* @return A query value.
*/
- @NonNull ValueNode query(@NonNull String name);
+ Value query(@NonNull String name);
/**
* Query string with the leading ? or empty string. This is the raw query string,
@@ -398,7 +373,7 @@ private static Selector single() {
* @return Query string with the leading ? or empty string. This is the raw query
* string, without decoding it.
*/
- @NonNull String queryString();
+ String queryString();
/**
* Convert the queryString to the given type.
@@ -407,12 +382,12 @@ private static Selector single() {
* @param Target type.
* @return Query string converted to target type.
*/
- @NonNull T query(@NonNull Class type);
+ T query(@NonNull Class type);
/**
* Query string as simple map.
*
- * {@code/search?q=jooby&sort=name}
+ * {@code /search?q=jooby&sort=name}
*
* Produces
*
@@ -420,7 +395,7 @@ private static Selector single() {
*
* @return Query string as map.
*/
- @NonNull Map queryMap();
+ Map queryMap();
/* **********************************************************************************************
* Header API
@@ -428,11 +403,11 @@ private static Selector single() {
*/
/**
- * Request headers as {@link ValueNode}.
+ * Request headers as {@link Value}.
*
- * @return Request headers as {@link ValueNode}.
+ * @return Request headers as {@link Value}.
*/
- @NonNull ValueNode header();
+ Value header();
/**
* Get a header that matches the given name.
@@ -440,14 +415,14 @@ private static Selector single() {
* @param name Header name. Case insensitive.
* @return A header value or missing value, never a null reference.
*/
- @NonNull Value header(@NonNull String name);
+ Value header(@NonNull String name);
/**
* Header as single-value map.
*
* @return Header as single-value map, with case insensitive keys.
*/
- @NonNull Map headerMap();
+ Map headerMap();
/**
* True if the given type matches the `Accept` header. This method returns true if
@@ -490,7 +465,7 @@ default boolean isPreflight() {
* @param defaults Default content type to use when the header is missing.
* @return Request Content-Type header or null when missing.
*/
- @NonNull MediaType getRequestType(MediaType defaults);
+ MediaType getRequestType(MediaType defaults);
/**
* Request Content-Length header or -1 when missing.
@@ -517,7 +492,7 @@ default boolean isPreflight() {
* @param filter A locale filter.
* @return A list of matching locales.
*/
- @NonNull default List locales(
+ default List locales(
BiFunction, List, List> filter) {
return filter.apply(
header("Accept-Language")
@@ -533,7 +508,7 @@ default boolean isPreflight() {
* @return A list of matching locales or empty list.
* @see #locales(BiFunction)
*/
- @NonNull default List locales() {
+ default List locales() {
return locales(Locale::filter);
}
@@ -555,7 +530,7 @@ default boolean isPreflight() {
* @param filter A locale filter.
* @return A matching locale.
*/
- @NonNull default Locale locale(BiFunction, List, Locale> filter) {
+ default Locale locale(BiFunction, List, Locale> filter) {
return filter.apply(
header("Accept-Language")
.toOptional()
@@ -570,7 +545,7 @@ default boolean isPreflight() {
*
* @return A matching locale.
*/
- @NonNull default Locale locale() {
+ default Locale locale() {
return locale(
(priorityList, locales) ->
Optional.ofNullable(Locale.lookup(priorityList, locales)).orElse(locales.get(0)));
@@ -590,7 +565,7 @@ default boolean isPreflight() {
* @param user Current user.
* @return This context.
*/
- @NonNull Context setUser(@Nullable Object user);
+ Context setUser(@Nullable Object user);
/**
* Recreates full/entire url of the current request using the Host header.
@@ -600,7 +575,7 @@ default boolean isPreflight() {
*
* @return Full/entire request url using the Host header.
*/
- @NonNull String getRequestURL();
+ String getRequestURL();
/**
* Recreates full/entire request url using the Host header with a custom path/suffix.
@@ -611,7 +586,7 @@ default boolean isPreflight() {
* @param path Path or suffix to use, can also include query string parameters.
* @return Full/entire request url using the Host header.
*/
- @NonNull String getRequestURL(@NonNull String path);
+ String getRequestURL(@NonNull String path);
/**
* The IP address of the client or last proxy that sent the request.
@@ -622,7 +597,7 @@ default boolean isPreflight() {
* @return The IP address of the client or last proxy that sent the request or empty string
* for interrupted requests.
*/
- @NonNull String getRemoteAddress();
+ String getRemoteAddress();
/**
* Set IP address of client or last proxy that sent the request.
@@ -630,7 +605,7 @@ default boolean isPreflight() {
* @param remoteAddress Remote Address.
* @return This context.
*/
- @NonNull Context setRemoteAddress(@NonNull String remoteAddress);
+ Context setRemoteAddress(@NonNull String remoteAddress);
/**
* Return the host that this request was sent to, in general this will be the value of the Host
@@ -643,7 +618,7 @@ default boolean isPreflight() {
* @return Return the host that this request was sent to, in general this will be the value of the
* Host header, minus the port specifier.
*/
- @NonNull String getHost();
+ String getHost();
/**
* Set the host (without the port value).
@@ -653,7 +628,7 @@ default boolean isPreflight() {
* @param host Host value.
* @return This context.
*/
- @NonNull Context setHost(@NonNull String host);
+ Context setHost(@NonNull String host);
/**
* Return the host and port that this request was sent to, in general this will be the value of
@@ -665,7 +640,7 @@ default boolean isPreflight() {
* @return Return the host that this request was sent to, in general this will be the value of the
* Host header.
*/
- @NonNull String getHostAndPort();
+ String getHostAndPort();
/**
* Return the port that this request was sent to. In general this will be the value of the Host
@@ -684,14 +659,14 @@ default boolean isPreflight() {
* @param port Port.
* @return This context.
*/
- @NonNull Context setPort(int port);
+ Context setPort(int port);
/**
* The name of the protocol the request. Always in lower-case.
*
* @return The name of the protocol the request. Always in lower-case.
*/
- @NonNull String getProtocol();
+ String getProtocol();
/**
* The certificates presented by the client for mutual TLS. Empty if ssl is not enabled, or client
@@ -700,7 +675,7 @@ default boolean isPreflight() {
* @return The certificates presented by the client for mutual TLS. Empty if ssl is not enabled,
* or client authentication is not required.
*/
- @NonNull List getClientCertificates();
+ List getClientCertificates();
/**
* Server port for current request.
@@ -714,7 +689,7 @@ default boolean isPreflight() {
*
* @return Server host.
*/
- @NonNull String getServerHost();
+ String getServerHost();
/**
* Returns a boolean indicating whether this request was made using a secure channel, such as
@@ -729,7 +704,7 @@ default boolean isPreflight() {
*
* @return HTTP scheme in lower case.
*/
- @NonNull String getScheme();
+ String getScheme();
/**
* Set HTTP scheme in lower case.
@@ -737,7 +712,7 @@ default boolean isPreflight() {
* @param scheme HTTP scheme in lower case.
* @return This context.
*/
- @NonNull Context setScheme(@NonNull String scheme);
+ Context setScheme(@NonNull String scheme);
/* **********************************************************************************************
* Form/Multipart API
@@ -752,7 +727,7 @@ default boolean isPreflight() {
*
* @return Multipart value.
*/
- @NonNull Formdata form();
+ Formdata form();
/**
* Get a form field that matches the given name.
@@ -764,7 +739,7 @@ default boolean isPreflight() {
* @param name Field name.
* @return Multipart value.
*/
- @NonNull ValueNode form(@NonNull String name);
+ Value form(@NonNull String name);
/**
* Convert form data to the given type.
@@ -776,7 +751,7 @@ default boolean isPreflight() {
* @param Target type.
* @return Target value.
*/
- @NonNull T form(@NonNull Class type);
+ T form(@NonNull Class type);
/**
* Form data as single-value map.
@@ -786,14 +761,14 @@ default boolean isPreflight() {
*
* @return Single-value map.
*/
- @NonNull Map formMap();
+ Map formMap();
/**
* All file uploads. Only for multipart/form-data request.
*
* @return All file uploads.
*/
- @NonNull List files();
+ List files();
/**
* All file uploads that matches the given field name.
@@ -803,7 +778,7 @@ default boolean isPreflight() {
* @param name Field name. Please note this is the form field name, not the actual file name.
* @return All file uploads.
*/
- @NonNull List files(@NonNull String name);
+ List files(@NonNull String name);
/**
* A file upload that matches the given field name.
@@ -813,7 +788,7 @@ default boolean isPreflight() {
* @param name Field name. Please note this is the form field name, not the actual file name.
* @return A file upload.
*/
- @NonNull FileUpload file(@NonNull String name);
+ FileUpload file(@NonNull String name);
/* **********************************************************************************************
* Parameter Lookup
@@ -845,17 +820,7 @@ default Value lookup(String name) {
* none found.
* @throws IllegalArgumentException If no {@link ParamSource}s are specified.
*/
- default Value lookup(@NonNull String name, ParamSource... sources) {
- if (sources.length == 0) {
- throw new IllegalArgumentException("No parameter sources were specified.");
- }
-
- return Arrays.stream(sources)
- .map(source -> source.provider.apply(this, name))
- .filter(value -> !value.isMissing())
- .findFirst()
- .orElseGet(() -> Value.missing(name));
- }
+ Value lookup(@NonNull String name, ParamSource... sources);
/**
* Returns a {@link ParamLookup} instance which is a fluent interface covering the functionality
@@ -872,9 +837,7 @@ default Value lookup(@NonNull String name, ParamSource... sources) {
* @see ParamLookup
* @see #lookup(String, ParamSource...)
*/
- default ParamLookup lookup() {
- return new ParamLookupImpl(this);
- }
+ ParamLookup lookup();
/* **********************************************************************************************
* Request Body
@@ -886,7 +849,7 @@ default ParamLookup lookup() {
*
* @return HTTP body which provides access to body content.
*/
- @NonNull Body body();
+ Body body();
/**
* Convert the HTTP body to the given type.
@@ -895,7 +858,7 @@ default ParamLookup lookup() {
* @param Conversion type.
* @return Instance of conversion type.
*/
- @NonNull T body(@NonNull Class type);
+ T body(@NonNull Class type);
/**
* Convert the HTTP body to the given type.
@@ -904,7 +867,7 @@ default ParamLookup lookup() {
* @param Conversion type.
* @return Instance of conversion type.
*/
- @NonNull T body(@NonNull Type type);
+ T body(@NonNull Type type);
/**
* Convert the HTTP body to the given type.
@@ -914,7 +877,7 @@ default ParamLookup lookup() {
* @param Conversion type.
* @return Instance of conversion type.
*/
- @NonNull T decode(@NonNull Type type, @NonNull MediaType contentType);
+ T decode(@NonNull Type type, @NonNull MediaType contentType);
/* **********************************************************************************************
* Body MessageDecoder
@@ -927,7 +890,7 @@ default ParamLookup lookup() {
* @param contentType Content type.
* @return MessageDecoder.
*/
- @NonNull MessageDecoder decoder(@NonNull MediaType contentType);
+ MessageDecoder decoder(@NonNull MediaType contentType);
/* **********************************************************************************************
* Dispatch methods
@@ -962,7 +925,7 @@ default ParamLookup lookup() {
* @param action Application code.
* @return This context.
*/
- @NonNull Context dispatch(@NonNull Runnable action);
+ Context dispatch(@NonNull Runnable action);
/**
* Dispatch context to the given executor.
@@ -985,7 +948,7 @@ default ParamLookup lookup() {
* @param action Application code.
* @return This context.
*/
- @NonNull Context dispatch(@NonNull Executor executor, @NonNull Runnable action);
+ Context dispatch(@NonNull Executor executor, @NonNull Runnable action);
/**
* Tells context that response will be generated form a different thread. This operation is
@@ -997,7 +960,7 @@ default ParamLookup lookup() {
* @return This context.
* @throws Exception When detach operation fails.
*/
- @NonNull Context detach(@NonNull Route.Handler next) throws Exception;
+ Context detach(@NonNull Route.Handler next) throws Exception;
/**
* Perform a websocket handsahke and upgrade a HTTP GET into a websocket protocol.
@@ -1007,7 +970,7 @@ default ParamLookup lookup() {
* @param handler Web socket initializer.
* @return This context.
*/
- @NonNull Context upgrade(@NonNull WebSocket.Initializer handler);
+ Context upgrade(@NonNull WebSocket.Initializer handler);
/**
* Perform a server-sent event handshake and upgrade HTTP GET into a Server-Sent protocol.
@@ -1017,7 +980,7 @@ default ParamLookup lookup() {
* @param handler Server-Sent event handler.
* @return This context.
*/
- @NonNull Context upgrade(@NonNull ServerSentEmitter.Handler handler);
+ Context upgrade(@NonNull ServerSentEmitter.Handler handler);
/*
* **********************************************************************************************
@@ -1032,7 +995,7 @@ default ParamLookup lookup() {
* @param value Header value.
* @return This context.
*/
- @NonNull Context setResponseHeader(@NonNull String name, @NonNull Date value);
+ Context setResponseHeader(@NonNull String name, @NonNull Date value);
/**
* Set response header.
@@ -1041,7 +1004,7 @@ default ParamLookup lookup() {
* @param value Header value.
* @return This context.
*/
- @NonNull Context setResponseHeader(@NonNull String name, @NonNull Instant value);
+ Context setResponseHeader(@NonNull String name, @NonNull Instant value);
/**
* Set response header.
@@ -1050,7 +1013,7 @@ default ParamLookup lookup() {
* @param value Header value.
* @return This context.
*/
- @NonNull Context setResponseHeader(@NonNull String name, @NonNull Object value);
+ Context setResponseHeader(@NonNull String name, @NonNull Object value);
/**
* Set response header.
@@ -1059,7 +1022,7 @@ default ParamLookup lookup() {
* @param value Header value.
* @return This context.
*/
- @NonNull Context setResponseHeader(@NonNull String name, @NonNull String value);
+ Context setResponseHeader(@NonNull String name, @NonNull String value);
/**
* Remove a response header.
@@ -1067,14 +1030,14 @@ default ParamLookup lookup() {
* @param name Header's name.
* @return This context.
*/
- @NonNull Context removeResponseHeader(@NonNull String name);
+ Context removeResponseHeader(@NonNull String name);
/**
* Clear/reset all the headers, including cookies.
*
* @return This context.
*/
- @NonNull Context removeResponseHeaders();
+ Context removeResponseHeaders();
/**
* Set response content length header.
@@ -1082,7 +1045,7 @@ default ParamLookup lookup() {
* @param length Response length.
* @return This context.
*/
- @NonNull Context setResponseLength(long length);
+ Context setResponseLength(long length);
/**
* Get response header.
@@ -1105,7 +1068,7 @@ default ParamLookup lookup() {
* @param cookie Cookie to add.
* @return This context.
*/
- @NonNull Context setResponseCookie(@NonNull Cookie cookie);
+ Context setResponseCookie(@NonNull Cookie cookie);
/**
* Set response content type header.
@@ -1113,7 +1076,7 @@ default ParamLookup lookup() {
* @param contentType Content type.
* @return This context.
*/
- @NonNull Context setResponseType(@NonNull String contentType);
+ Context setResponseType(@NonNull String contentType);
/**
* Set response content type header.
@@ -1121,7 +1084,7 @@ default ParamLookup lookup() {
* @param contentType Content type.
* @return This context.
*/
- @NonNull Context setResponseType(@NonNull MediaType contentType);
+ Context setResponseType(@NonNull MediaType contentType);
/**
* Set response content type header.
@@ -1130,7 +1093,7 @@ default ParamLookup lookup() {
* @param charset Charset.
* @return This context.
*/
- @NonNull Context setResponseType(@NonNull MediaType contentType, @Nullable Charset charset);
+ Context setResponseType(@NonNull MediaType contentType, @Nullable Charset charset);
/**
* Set the default response content type header. It is used if the response content type header
@@ -1139,14 +1102,14 @@ default ParamLookup lookup() {
* @param contentType Content type.
* @return This context.
*/
- @NonNull Context setDefaultResponseType(@NonNull MediaType contentType);
+ Context setDefaultResponseType(@NonNull MediaType contentType);
/**
* Get response content type.
*
* @return Response content type.
*/
- @NonNull MediaType getResponseType();
+ MediaType getResponseType();
/**
* Set response status code.
@@ -1154,7 +1117,7 @@ default ParamLookup lookup() {
* @param statusCode Status code.
* @return This context.
*/
- @NonNull Context setResponseCode(@NonNull StatusCode statusCode);
+ Context setResponseCode(@NonNull StatusCode statusCode);
/**
* Set response status code.
@@ -1162,14 +1125,14 @@ default ParamLookup lookup() {
* @param statusCode Status code.
* @return This context.
*/
- @NonNull Context setResponseCode(int statusCode);
+ Context setResponseCode(int statusCode);
/**
* Get response status code.
*
* @return Response status code.
*/
- @NonNull StatusCode getResponseCode();
+ StatusCode getResponseCode();
/**
* Render a value and send the response to client.
@@ -1177,14 +1140,14 @@ default ParamLookup lookup() {
* @param value Object value.
* @return This context.
*/
- @NonNull Context render(@NonNull Object value);
+ Context render(@NonNull Object value);
/**
* HTTP response channel as output stream. Usually for chunked responses.
*
* @return HTTP channel as output stream. Usually for chunked responses.
*/
- @NonNull OutputStream responseStream();
+ OutputStream responseStream();
/**
* HTTP response channel as output stream. Usually for chunked responses.
@@ -1192,7 +1155,7 @@ default ParamLookup lookup() {
* @param contentType Media type.
* @return HTTP channel as output stream. Usually for chunked responses.
*/
- @NonNull OutputStream responseStream(@NonNull MediaType contentType);
+ OutputStream responseStream(@NonNull MediaType contentType);
/**
* HTTP response channel as output stream. Usually for chunked responses.
@@ -1202,8 +1165,8 @@ default ParamLookup lookup() {
* @return HTTP channel as output stream. Usually for chunked responses.
* @throws Exception Is something goes wrong.
*/
- @NonNull Context responseStream(
- @NonNull MediaType contentType, @NonNull SneakyThrows.Consumer consumer)
+ Context responseStream(
+ MediaType contentType, @NonNull SneakyThrows.Consumer consumer)
throws Exception;
/**
@@ -1213,21 +1176,21 @@ default ParamLookup lookup() {
* @return HTTP channel as output stream. Usually for chunked responses.
* @throws Exception Is something goes wrong.
*/
- @NonNull Context responseStream(@NonNull SneakyThrows.Consumer consumer) throws Exception;
+ Context responseStream(@NonNull SneakyThrows.Consumer consumer) throws Exception;
/**
* HTTP response channel as chunker.
*
* @return HTTP channel as chunker. Usually for chunked response.
*/
- @NonNull Sender responseSender();
+ Sender responseSender();
/**
* HTTP response channel as response writer.
*
* @return HTTP channel as response writer. Usually for chunked response.
*/
- @NonNull PrintWriter responseWriter();
+ PrintWriter responseWriter();
/**
* HTTP response channel as response writer.
@@ -1235,7 +1198,7 @@ default ParamLookup lookup() {
* @param contentType Content type.
* @return HTTP channel as response writer. Usually for chunked response.
*/
- @NonNull PrintWriter responseWriter(@NonNull MediaType contentType);
+ PrintWriter responseWriter(@NonNull MediaType contentType);
/**
* HTTP response channel as response writer.
@@ -1244,7 +1207,7 @@ default ParamLookup lookup() {
* @param charset Charset.
* @return HTTP channel as response writer. Usually for chunked response.
*/
- @NonNull PrintWriter responseWriter(@NonNull MediaType contentType, @Nullable Charset charset);
+ PrintWriter responseWriter(@NonNull MediaType contentType, @Nullable Charset charset);
/**
* HTTP response channel as response writer.
@@ -1253,7 +1216,7 @@ default ParamLookup lookup() {
* @return This context.
* @throws Exception Is something goes wrong.
*/
- @NonNull Context responseWriter(@NonNull SneakyThrows.Consumer consumer) throws Exception;
+ Context responseWriter(@NonNull SneakyThrows.Consumer consumer) throws Exception;
/**
* HTTP response channel as response writer.
@@ -1263,9 +1226,8 @@ default ParamLookup lookup() {
* @return This context.
* @throws Exception Is something goes wrong.
*/
- @NonNull Context responseWriter(
- @NonNull MediaType contentType, @NonNull SneakyThrows.Consumer consumer)
- throws Exception;
+ Context responseWriter(
+ MediaType contentType, @NonNull SneakyThrows.Consumer consumer) throws Exception;
/**
* HTTP response channel as response writer.
@@ -1276,10 +1238,8 @@ default ParamLookup lookup() {
* @return This context.
* @throws Exception Is something goes wrong.
*/
- @NonNull Context responseWriter(
- @NonNull MediaType contentType,
- @Nullable Charset charset,
- @NonNull SneakyThrows.Consumer consumer)
+ Context responseWriter(
+ MediaType contentType, @Nullable Charset charset, SneakyThrows.Consumer consumer)
throws Exception;
/**
@@ -1288,7 +1248,7 @@ default ParamLookup lookup() {
* @param location Location.
* @return This context.
*/
- @NonNull Context sendRedirect(@NonNull String location);
+ Context sendRedirect(@NonNull String location);
/**
* Send a redirect response.
@@ -1297,7 +1257,7 @@ default ParamLookup lookup() {
* @param location Location.
* @return This context.
*/
- @NonNull Context sendRedirect(@NonNull StatusCode redirect, @NonNull String location);
+ Context sendRedirect(@NonNull StatusCode redirect, @NonNull String location);
/**
* Send response data.
@@ -1305,7 +1265,7 @@ default ParamLookup lookup() {
* @param data Response. Use UTF-8 charset.
* @return This context.
*/
- @NonNull Context send(@NonNull String data);
+ Context send(@NonNull String data);
/**
* Send response data.
@@ -1314,7 +1274,7 @@ default ParamLookup lookup() {
* @param charset Charset.
* @return This context.
*/
- @NonNull Context send(@NonNull String data, @NonNull Charset charset);
+ Context send(@NonNull String data, @NonNull Charset charset);
/**
* Send response data.
@@ -1322,7 +1282,7 @@ default ParamLookup lookup() {
* @param data Response.
* @return This context.
*/
- @NonNull Context send(@NonNull byte[] data);
+ Context send(@NonNull byte[] data);
/**
* Send response data.
@@ -1330,15 +1290,15 @@ default ParamLookup lookup() {
* @param data Response.
* @return This context.
*/
- @NonNull Context send(@NonNull ByteBuffer data);
+ Context send(@NonNull ByteBuffer data);
/**
* Send response data.
*
- * @param data Response.
+ * @param output Output.
* @return This context.
*/
- @NonNull Context send(@NonNull DataBuffer data);
+ Context send(@NonNull Output output);
/**
* Send response data.
@@ -1346,7 +1306,7 @@ default ParamLookup lookup() {
* @param data Response.
* @return This context.
*/
- @NonNull Context send(@NonNull byte[]... data);
+ Context send(@NonNull byte[]... data);
/**
* Send response data.
@@ -1354,7 +1314,7 @@ default ParamLookup lookup() {
* @param data Response.
* @return This context.
*/
- @NonNull Context send(@NonNull ByteBuffer[] data);
+ Context send(@NonNull ByteBuffer[] data);
/**
* Send response data.
@@ -1362,7 +1322,7 @@ default ParamLookup lookup() {
* @param channel Response input.
* @return This context.
*/
- @NonNull Context send(@NonNull ReadableByteChannel channel);
+ Context send(@NonNull ReadableByteChannel channel);
/**
* Send response data.
@@ -1370,7 +1330,7 @@ default ParamLookup lookup() {
* @param input Response.
* @return This context.
*/
- @NonNull Context send(@NonNull InputStream input);
+ Context send(@NonNull InputStream input);
/**
* Send a file download response.
@@ -1378,7 +1338,7 @@ default ParamLookup lookup() {
* @param file File download.
* @return This context.
*/
- @NonNull Context send(@NonNull FileDownload file);
+ Context send(@NonNull FileDownload file);
/**
* Send a file response.
@@ -1386,7 +1346,7 @@ default ParamLookup lookup() {
* @param file File response.
* @return This context.
*/
- @NonNull Context send(@NonNull Path file);
+ Context send(@NonNull Path file);
/**
* Send a file response.
@@ -1394,7 +1354,7 @@ default ParamLookup lookup() {
* @param file File response.
* @return This context.
*/
- @NonNull Context send(@NonNull FileChannel file);
+ Context send(@NonNull FileChannel file);
/**
* Send an empty response with the given status code.
@@ -1402,7 +1362,7 @@ default ParamLookup lookup() {
* @param statusCode Status code.
* @return This context.
*/
- @NonNull Context send(@NonNull StatusCode statusCode);
+ Context send(@NonNull StatusCode statusCode);
/**
* Send an error response. Status code is computed via {@link Router#errorCode(Throwable)}.
@@ -1410,7 +1370,7 @@ default ParamLookup lookup() {
* @param cause Error. If this is a fatal error it is going to be rethrow it.
* @return This context.
*/
- @NonNull Context sendError(@NonNull Throwable cause);
+ Context sendError(@NonNull Throwable cause);
/**
* Send an error response.
@@ -1419,7 +1379,7 @@ default ParamLookup lookup() {
* @param statusCode Status code.
* @return This context.
*/
- @NonNull Context sendError(@NonNull Throwable cause, @NonNull StatusCode statusCode);
+ Context sendError(@NonNull Throwable cause, @NonNull StatusCode statusCode);
/**
* True if response already started.
@@ -1430,10 +1390,10 @@ default ParamLookup lookup() {
/**
* True if response headers are cleared on application error. If none set it uses the
- * default/global value specified by {@link RouterOption#RESET_HEADERS_ON_ERROR}.
+ * default/global value specified by {@link RouterOptions#RESET_HEADERS_ON_ERROR}.
*
* @return True if response headers are cleared on application error. If none set it uses the
- * default/global value specified by {@link RouterOption#RESET_HEADERS_ON_ERROR}.
+ * default/global value specified by {@link RouterOptions#RESET_HEADERS_ON_ERROR}.
*/
boolean getResetHeadersOnError();
@@ -1443,7 +1403,7 @@ default ParamLookup lookup() {
* @param value True for reset/clear headers.
* @return This context.
*/
- @NonNull Context setResetHeadersOnError(boolean value);
+ Context setResetHeadersOnError(boolean value);
/**
* Add a complete listener.
@@ -1451,7 +1411,7 @@ default ParamLookup lookup() {
* @param task Task to execute.
* @return This context.
*/
- @NonNull Context onComplete(@NonNull Route.Complete task);
+ Context onComplete(@NonNull Route.Complete task);
/* **********************************************************************************************
* Factory methods
@@ -1465,7 +1425,7 @@ default ParamLookup lookup() {
* @param ctx Originating context.
* @return Read only context.
*/
- static @NonNull Context readOnly(@NonNull Context ctx) {
+ static Context readOnly(@NonNull Context ctx) {
return new ReadOnlyContext(ctx);
}
@@ -1484,11 +1444,8 @@ default ParamLookup lookup() {
* @param binary True for sending binary message.
* @return Read only context.
*/
- static @NonNull Context websocket(
- @NonNull Context ctx,
- @NonNull WebSocket ws,
- boolean binary,
- WebSocket.WriteCallback callback) {
+ static Context websocket(
+ Context ctx, WebSocket ws, boolean binary, WebSocket.WriteCallback callback) {
return new WebSocketSender(ctx, ws, binary, callback);
}
}
diff --git a/jooby/src/main/java/io/jooby/Cookie.java b/jooby/src/main/java/io/jooby/Cookie.java
index 1d9909dd16..8d8f1aa558 100644
--- a/jooby/src/main/java/io/jooby/Cookie.java
+++ b/jooby/src/main/java/io/jooby/Cookie.java
@@ -588,6 +588,17 @@ public String toString() {
return Optional.empty();
}
+ /**
+ * Creates a session cookie which never expires (maxAge: -1), is http only and path is /
+ * .
+ *
+ * @param sid Session ID.
+ * @return Session's cookie.
+ */
+ public static Cookie session(@NonNull String sid) {
+ return new Cookie(sid).setMaxAge(-1).setHttpOnly(true).setPath("/");
+ }
+
private static void value(
Config conf, String name, BiFunction mapper, Consumer consumer) {
if (conf.hasPath(name)) {
diff --git a/jooby/src/main/java/io/jooby/DefaultContext.java b/jooby/src/main/java/io/jooby/DefaultContext.java
index 7cea85c99a..34ac7811fd 100644
--- a/jooby/src/main/java/io/jooby/DefaultContext.java
+++ b/jooby/src/main/java/io/jooby/DefaultContext.java
@@ -20,24 +20,17 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.time.Instant;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.stream.Collectors;
+import java.util.*;
import org.slf4j.Logger;
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;
-import io.jooby.internal.SingleValue;
-import io.jooby.internal.UrlParser;
-import io.jooby.internal.ValueConverters;
+import io.jooby.internal.*;
+import io.jooby.output.OutputFactory;
+import io.jooby.value.Value;
+import io.jooby.value.ValueFactory;
/***
* Like {@link Context} but with couple of default methods.
@@ -47,44 +40,45 @@
*/
public interface DefaultContext extends Context {
- @NonNull @Override
+ @Override
default T require(@NonNull Class type, @NonNull String name) throws RegistryException {
return getRouter().require(type, name);
}
- @NonNull @Override
+ @Override
default T require(@NonNull Class type) throws RegistryException {
return getRouter().require(type);
}
- @NonNull @Override
+ @Override
default T require(@NonNull Reified type) throws RegistryException {
return getRouter().require(type);
}
- @NonNull @Override
+ @Override
default T require(@NonNull Reified type, @NonNull String name) throws RegistryException {
return getRouter().require(type, name);
}
- @NonNull @Override
+ @Override
default T require(@NonNull ServiceKey key) throws RegistryException {
return getRouter().require(key);
}
+ @SuppressWarnings("unchecked")
@Nullable @Override
default T getUser() {
return (T) getAttributes().get("user");
}
- @NonNull @Override
+ @Override
default Context setUser(@Nullable Object user) {
getAttributes().put("user", user);
return this;
}
@Override
- default boolean matches(String pattern) {
+ default boolean matches(@NonNull String pattern) {
return getRouter().match(pattern, getRequestPath());
}
@@ -107,13 +101,13 @@ default boolean matches(String pattern) {
}
@Override
- @NonNull default Context setAttribute(@NonNull String key, Object value) {
+ default Context setAttribute(@NonNull String key, Object value) {
getAttributes().put(key, value);
return this;
}
@Override
- default @NonNull FlashMap flash() {
+ default FlashMap flash() {
return (FlashMap)
getAttributes()
.computeIfAbsent(
@@ -133,12 +127,12 @@ default FlashMap flashOrNull() {
* @return Flash attribute.
*/
@Override
- default @NonNull Value flash(@NonNull String name) {
- return Value.create(this, name, flash().get(name));
+ default Value flash(@NonNull String name) {
+ return Value.create(getValueFactory(), name, flash().get(name));
}
@Override
- default @NonNull Value session(@NonNull String name) {
+ default Value session(@NonNull String name) {
Session session = sessionOrNull();
if (session != null) {
return session.get(name);
@@ -147,7 +141,7 @@ default FlashMap flashOrNull() {
}
@Override
- default @NonNull Session session() {
+ default Session session() {
Session session = sessionOrNull();
if (session == null) {
SessionStore store = getRouter().getSessionStore();
@@ -172,7 +166,7 @@ default FlashMap flashOrNull() {
}
@Override
- default @NonNull Object forward(@NonNull String path) {
+ default Object forward(@NonNull String path) {
try {
setRequestPath(path);
Router.Match match = getRouter().match(this);
@@ -183,27 +177,70 @@ default FlashMap flashOrNull() {
}
@Override
- default @NonNull Value cookie(@NonNull String name) {
+ default Value cookie(@NonNull String name) {
String value = cookieMap().get(name);
- return value == null ? Value.missing(name) : Value.value(this, name, value);
+ return value == null ? Value.missing(name) : Value.value(getValueFactory(), name, value);
+ }
+
+ /**
+ * Returns a {@link ParamLookup} instance which is a fluent interface covering the functionality
+ * of the {@link #lookup(String, ParamSource...)} method.
+ *
+ * {@code
+ * Value foo = ctx.lookup()
+ * .inQuery()
+ * .inPath()
+ * .get("foo");
+ * }
+ *
+ * @return A {@link ParamLookup} instance.
+ * @see ParamLookup
+ * @see #lookup(String, ParamSource...)
+ */
+ default ParamLookup lookup() {
+ return new ParamLookupImpl(this);
+ }
+
+ /**
+ * Searches for a parameter in the specified sources, in the specified order, returning the first
+ * non-missing {@link Value}, or a 'missing' {@link Value} if none found.
+ *
+ * At least one {@link ParamSource} must be specified.
+ *
+ * @param name The name of the parameter.
+ * @param sources Sources to search in.
+ * @return The first non-missing {@link Value} or a {@link Value} representing a missing value if
+ * none found.
+ * @throws IllegalArgumentException If no {@link ParamSource}s are specified.
+ */
+ default Value lookup(@NonNull String name, ParamSource... sources) {
+ if (sources.length == 0) {
+ throw new IllegalArgumentException("No parameter sources were specified.");
+ }
+
+ return Arrays.stream(sources)
+ .map(source -> source.provider.apply(this, name))
+ .filter(value -> !value.isMissing())
+ .findFirst()
+ .orElseGet(() -> Value.missing(name));
}
@Override
- @NonNull default Value path(@NonNull String name) {
+ default Value path(@NonNull String name) {
String value = pathMap().get(name);
return value == null
? new MissingValue(name)
- : new SingleValue(this, name, UrlParser.decodePathSegment(value));
+ : new SingleValue(getValueFactory(), name, UrlParser.decodePathSegment(value));
}
@Override
- @NonNull default T path(@NonNull Class type) {
+ default T path(@NonNull Class type) {
return path().to(type);
}
@Override
- @NonNull default ValueNode path() {
- HashValue path = new HashValue(this, null);
+ default Value path() {
+ var path = new HashValue(getValueFactory(), null);
for (Map.Entry entry : pathMap().entrySet()) {
path.put(entry.getKey(), entry.getValue());
}
@@ -211,32 +248,32 @@ default FlashMap flashOrNull() {
}
@Override
- @NonNull default ValueNode query(@NonNull String name) {
+ default Value query(@NonNull String name) {
return query().get(name);
}
@Override
- @NonNull default String queryString() {
+ default String queryString() {
return query().queryString();
}
@Override
- @NonNull default T query(@NonNull Class type) {
- return query().to(type);
+ default T query(@NonNull Class type) {
+ return query().toEmpty(type);
}
@Override
- @NonNull default Map queryMap() {
+ default Map queryMap() {
return query().toMap();
}
@Override
- @NonNull default Value header(@NonNull String name) {
+ default Value header(@NonNull String name) {
return header().get(name);
}
@Override
- @NonNull default Map headerMap() {
+ default Map headerMap() {
return header().toMap();
}
@@ -246,25 +283,25 @@ default boolean accept(@NonNull MediaType contentType) {
}
@Override
- default MediaType accept(@NonNull List produceTypes) {
- Value accept = header(ACCEPT);
+ default @Nullable MediaType accept(@NonNull List produceTypes) {
+ var accept = header(ACCEPT);
if (accept.isMissing()) {
// NO header? Pick first, which is the default.
return produceTypes.isEmpty() ? null : produceTypes.get(0);
}
// Sort accept by most relevant/specific first:
- List acceptTypes =
+ var acceptTypes =
accept.toList().stream()
.flatMap(value -> MediaType.parse(value).stream())
.distinct()
.sorted()
- .collect(Collectors.toList());
+ .toList();
// Find most appropriated type:
- int idx = Integer.MAX_VALUE;
+ var idx = Integer.MAX_VALUE;
MediaType result = null;
- for (MediaType produceType : produceTypes) {
+ for (var produceType : produceTypes) {
for (int i = 0; i < acceptTypes.size(); i++) {
MediaType acceptType = acceptTypes.get(i);
if (produceType.matches(acceptType)) {
@@ -280,21 +317,21 @@ default MediaType accept(@NonNull List produceTypes) {
}
@Override
- default @NonNull String getRequestURL() {
+ default String getRequestURL() {
return getRequestURL(getRequestPath() + queryString());
}
@Override
- default @NonNull String getRequestURL(@NonNull String path) {
- String scheme = getScheme();
- String host = getHost();
+ default String getRequestURL(@NonNull String path) {
+ var scheme = getScheme();
+ var host = getHost();
int port = getPort();
- StringBuilder url = new StringBuilder();
+ var url = new StringBuilder();
url.append(scheme).append("://").append(host);
if (port > 0 && port != PORT && port != SECURE_PORT) {
url.append(":").append(port);
}
- String contextPath = getContextPath();
+ var contextPath = getContextPath();
if (!contextPath.equals("/") && !path.startsWith(contextPath)) {
url.append(contextPath);
}
@@ -310,7 +347,7 @@ default MediaType accept(@NonNull List produceTypes) {
}
@Override
- @NonNull default MediaType getRequestType(MediaType defaults) {
+ default MediaType getRequestType(MediaType defaults) {
Value contentType = header("Content-Type");
return contentType.isMissing() ? defaults : MediaType.valueOf(contentType.value());
}
@@ -322,10 +359,12 @@ default long getRequestLength() {
}
@Override
- default @Nullable String getHostAndPort() {
+ default String getHostAndPort() {
Optional header =
- getRouter().isTrustProxy() ? header("X-Forwarded-Host").toOptional() : Optional.empty();
- String value =
+ getRouter().getRouterOptions().isTrustProxy()
+ ? header("X-Forwarded-Host").toOptional()
+ : Optional.empty();
+ var value =
header.orElseGet(
() ->
ofNullable(header("Host").valueOrNull())
@@ -339,14 +378,14 @@ default long getRequestLength() {
}
@Override
- default @NonNull String getServerHost() {
- String host = getRouter().getServerOptions().getHost();
+ default String getServerHost() {
+ var host = require(ServerOptions.class).getHost();
return host.equals("0.0.0.0") ? "localhost" : host;
}
@Override
default int getServerPort() {
- ServerOptions options = getRouter().getServerOptions();
+ var options = require(ServerOptions.class);
return isSecure()
// Buggy proxy where it report a https scheme but there is no HTTPS configured option
? ofNullable(options.getSecurePort()).orElse(options.getPort())
@@ -355,7 +394,7 @@ default int getServerPort() {
@Override
default int getPort() {
- String hostAndPort = getHostAndPort();
+ var hostAndPort = getHostAndPort();
if (hostAndPort != null) {
int index = hostAndPort.indexOf(':');
if (index > 0) {
@@ -367,7 +406,7 @@ default int getPort() {
}
@Override
- default @NonNull String getHost() {
+ default String getHost() {
String hostAndPort = getHostAndPort();
if (hostAndPort != null) {
int index = hostAndPort.indexOf(':');
@@ -382,57 +421,56 @@ default boolean isSecure() {
}
@Override
- @NonNull default ValueNode form(@NonNull String name) {
+ default Value form(@NonNull String name) {
return form().get(name);
}
@Override
- @NonNull default T form(@NonNull Class type) {
+ default T form(@NonNull Class type) {
return form().to(type);
}
@Override
- @NonNull default Map formMap() {
+ default Map formMap() {
return form().toMap();
}
@Override
- @NonNull default List files() {
+ default List files() {
return form().files();
}
@Override
- @NonNull default List files(@NonNull String name) {
+ default List files(@NonNull String name) {
return form().files(name);
}
@Override
- @NonNull default FileUpload file(@NonNull String name) {
+ default FileUpload file(@NonNull String name) {
return form().file(name);
}
@Override
- default @NonNull T body(@NonNull Class type) {
+ default T body(@NonNull Class type) {
return body().to(type);
}
@Override
- default @NonNull T body(@NonNull Type type) {
+ default T body(@NonNull Type type) {
return body().to(type);
}
- @Override
- default @NonNull T convertOrNull(@NonNull ValueNode value, @NonNull Class type) {
- return ValueConverters.convert(value, type, getRouter());
+ default ValueFactory getValueFactory() {
+ return getRouter().getValueFactory();
}
@Override
- default @NonNull T decode(@NonNull Type type, @NonNull MediaType contentType) {
+ default T decode(@NonNull Type type, @NonNull MediaType contentType) {
try {
if (MediaType.text.equals(contentType)) {
- T result = ValueConverters.convert(body(), type, getRouter());
- return result;
+ return getValueFactory().convert(type, body());
}
+ //noinspection unchecked
return (T) decoder(contentType).decode(this, type);
} catch (Exception x) {
throw SneakyThrows.propagate(x);
@@ -440,22 +478,22 @@ default boolean isSecure() {
}
@Override
- default @NonNull MessageDecoder decoder(@NonNull MediaType contentType) {
+ default MessageDecoder decoder(@NonNull MediaType contentType) {
return getRoute().decoder(contentType);
}
@Override
- @NonNull default Context setResponseHeader(@NonNull String name, @NonNull Date value) {
+ default Context setResponseHeader(@NonNull String name, @NonNull Date value) {
return setResponseHeader(name, RFC1123.format(Instant.ofEpochMilli(value.getTime())));
}
@Override
- @NonNull default Context setResponseHeader(@NonNull String name, @NonNull Instant value) {
+ default Context setResponseHeader(@NonNull String name, @NonNull Instant value) {
return setResponseHeader(name, RFC1123.format(value));
}
@Override
- @NonNull default Context setResponseHeader(@NonNull String name, @NonNull Object value) {
+ default Context setResponseHeader(@NonNull String name, @NonNull Object value) {
if (value instanceof Date) {
return setResponseHeader(name, (Date) value);
}
@@ -466,20 +504,20 @@ default boolean isSecure() {
}
@Override
- @NonNull default Context setResponseType(@NonNull MediaType contentType) {
+ default Context setResponseType(@NonNull MediaType contentType) {
return setResponseType(contentType, contentType.getCharset());
}
@Override
- @NonNull default Context setResponseCode(@NonNull StatusCode statusCode) {
+ default Context setResponseCode(@NonNull StatusCode statusCode) {
return setResponseCode(statusCode.value());
}
@Override
- default @NonNull Context render(@NonNull Object value) {
+ default Context render(@NonNull Object value) {
try {
- Route route = getRoute();
- MessageEncoder encoder = route.getEncoder();
+ var route = getRoute();
+ var encoder = route.getEncoder();
var bytes = encoder.encode(this, value);
if (bytes == null) {
if (!isResponseStarted()) {
@@ -495,13 +533,13 @@ default boolean isSecure() {
}
@Override
- default @NonNull OutputStream responseStream(@NonNull MediaType contentType) {
+ default OutputStream responseStream(@NonNull MediaType contentType) {
setResponseType(contentType);
return responseStream();
}
@Override
- default @NonNull Context responseStream(
+ default Context responseStream(
@NonNull MediaType contentType, @NonNull SneakyThrows.Consumer consumer)
throws Exception {
setResponseType(contentType);
@@ -509,7 +547,7 @@ default boolean isSecure() {
}
@Override
- default @NonNull Context responseStream(@NonNull SneakyThrows.Consumer consumer)
+ default Context responseStream(@NonNull SneakyThrows.Consumer consumer)
throws Exception {
try (OutputStream out = responseStream()) {
consumer.accept(out);
@@ -518,30 +556,30 @@ default boolean isSecure() {
}
@Override
- default @NonNull PrintWriter responseWriter() {
+ default PrintWriter responseWriter() {
return responseWriter(MediaType.text);
}
@Override
- default @NonNull PrintWriter responseWriter(@NonNull MediaType contentType) {
+ default PrintWriter responseWriter(@NonNull MediaType contentType) {
return responseWriter(contentType, contentType.getCharset());
}
@Override
- default @NonNull Context responseWriter(@NonNull SneakyThrows.Consumer consumer)
+ default Context responseWriter(@NonNull SneakyThrows.Consumer consumer)
throws Exception {
return responseWriter(MediaType.text, consumer);
}
@Override
- default @NonNull Context responseWriter(
+ default Context responseWriter(
@NonNull MediaType contentType, @NonNull SneakyThrows.Consumer consumer)
throws Exception {
return responseWriter(contentType, contentType.getCharset(), consumer);
}
@Override
- default @NonNull Context responseWriter(
+ default Context responseWriter(
@NonNull MediaType contentType,
@Nullable Charset charset,
@NonNull SneakyThrows.Consumer consumer)
@@ -553,18 +591,18 @@ default boolean isSecure() {
}
@Override
- default @NonNull Context sendRedirect(@NonNull String location) {
+ default Context sendRedirect(@NonNull String location) {
return sendRedirect(StatusCode.FOUND, location);
}
@Override
- default @NonNull Context sendRedirect(@NonNull StatusCode redirect, @NonNull String location) {
+ default Context sendRedirect(@NonNull StatusCode redirect, @NonNull String location) {
setResponseHeader("location", location);
return send(redirect);
}
@Override
- default @NonNull Context send(@NonNull byte[]... data) {
+ default Context send(@NonNull byte[]... data) {
ByteBuffer[] buffer = new ByteBuffer[data.length];
for (int i = 0; i < data.length; i++) {
buffer[i] = ByteBuffer.wrap(data[i]);
@@ -573,12 +611,12 @@ default boolean isSecure() {
}
@Override
- default @NonNull Context send(@NonNull String data) {
+ default Context send(@NonNull String data) {
return send(data, StandardCharsets.UTF_8);
}
@Override
- default @NonNull Context send(@NonNull FileDownload file) {
+ default Context send(@NonNull FileDownload file) {
setResponseHeader("Content-Disposition", file.getContentDisposition());
InputStream content = file.stream();
long length = file.getFileSize();
@@ -595,7 +633,7 @@ default boolean isSecure() {
}
@Override
- default @NonNull Context send(@NonNull Path file) {
+ default Context send(@NonNull Path file) {
try {
setDefaultResponseType(MediaType.byFile(file));
return send(FileChannel.open(file));
@@ -605,7 +643,7 @@ default boolean isSecure() {
}
@Override
- @NonNull default Context sendError(@NonNull Throwable cause) {
+ default Context sendError(@NonNull Throwable cause) {
sendError(cause, getRouter().errorCode(cause));
return this;
}
@@ -618,7 +656,7 @@ default boolean isSecure() {
* @return This context.
*/
@Override
- @NonNull default Context sendError(@NonNull Throwable cause, @NonNull StatusCode code) {
+ default Context sendError(@NonNull Throwable cause, @NonNull StatusCode code) {
Router router = getRouter();
Logger log = router.getLog();
if (isResponseStarted()) {
@@ -646,14 +684,15 @@ default boolean isSecure() {
}
}
}
- /** rethrow fatal exceptions: */
+ /* rethrow fatal exceptions: */
if (SneakyThrows.isFatal(cause)) {
throw SneakyThrows.propagate(cause);
}
return this;
}
- @NonNull default DataBufferFactory getBufferFactory() {
- return getRouter().getBufferFactory();
+ @Override
+ default OutputFactory getOutputFactory() {
+ return getRouter().getOutputFactory().getContextFactory();
}
}
diff --git a/jooby/src/main/java/io/jooby/Formdata.java b/jooby/src/main/java/io/jooby/Formdata.java
index 8c8dfdb9eb..c2bed23c49 100644
--- a/jooby/src/main/java/io/jooby/Formdata.java
+++ b/jooby/src/main/java/io/jooby/Formdata.java
@@ -10,6 +10,8 @@
import edu.umd.cs.findbugs.annotations.NonNull;
import io.jooby.internal.MultipartNode;
+import io.jooby.value.Value;
+import io.jooby.value.ValueFactory;
/**
* Form class for direct MVC parameter provisioning.
@@ -20,7 +22,7 @@
* @author edgar
* @since 2.0.0
*/
-public interface Formdata extends ValueNode {
+public interface Formdata extends Value {
/**
* Add a form field.
@@ -29,7 +31,7 @@ public interface Formdata extends ValueNode {
* @param value Form value.
*/
@NonNull
- void put(@NonNull String path, @NonNull ValueNode value);
+ void put(@NonNull String path, @NonNull Value value);
/**
* Add a form field.
@@ -87,10 +89,10 @@ public interface Formdata extends ValueNode {
/**
* Creates a new multipart object.
*
- * @param ctx Current context.
+ * @param valueFactory Current context.
* @return Multipart instance.
*/
- static @NonNull Formdata create(@NonNull Context ctx) {
- return new MultipartNode(ctx);
+ static @NonNull Formdata create(@NonNull ValueFactory valueFactory) {
+ return new MultipartNode(valueFactory);
}
}
diff --git a/jooby/src/main/java/io/jooby/ForwardingContext.java b/jooby/src/main/java/io/jooby/ForwardingContext.java
index f8c68b7189..98f4cb92a9 100644
--- a/jooby/src/main/java/io/jooby/ForwardingContext.java
+++ b/jooby/src/main/java/io/jooby/ForwardingContext.java
@@ -23,9 +23,11 @@
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;
+import io.jooby.output.Output;
+import io.jooby.output.OutputFactory;
+import io.jooby.value.Value;
+import io.jooby.value.ValueFactory;
/**
* Utility class that helps to wrap and delegate to another context.
@@ -43,12 +45,12 @@ public ForwardingBody(Body body) {
}
@Override
- @NonNull public String value(@NonNull Charset charset) {
+ public String value(@NonNull Charset charset) {
return delegate.value(charset);
}
@Override
- @NonNull public byte[] bytes() {
+ public byte[] bytes() {
return delegate.bytes();
}
@@ -63,32 +65,32 @@ public long getSize() {
}
@Override
- @NonNull public ReadableByteChannel channel() {
+ public ReadableByteChannel channel() {
return delegate.channel();
}
@Override
- @NonNull public InputStream stream() {
+ public InputStream stream() {
return delegate.stream();
}
@Override
- @NonNull public List toList(@NonNull Class type) {
+ public List toList(@NonNull Class type) {
return delegate.toList(type);
}
@Override
- @NonNull public List toList() {
+ public List toList() {
return delegate.toList();
}
@Override
- @NonNull public Set toSet() {
+ public Set toSet() {
return delegate.toSet();
}
@Override
- @NonNull public T to(@NonNull Class type) {
+ public T to(@NonNull Class type) {
return delegate.to(type);
}
@@ -98,7 +100,7 @@ public long getSize() {
}
@Override
- @NonNull public T to(@NonNull Type type) {
+ public T to(@NonNull Type type) {
return delegate.to(type);
}
@@ -108,12 +110,12 @@ public long getSize() {
}
@Override
- @NonNull public ValueNode get(int index) {
+ public Value get(int index) {
return delegate.get(index);
}
@Override
- @NonNull public ValueNode get(@NonNull String name) {
+ public Value get(@NonNull String name) {
return delegate.get(name);
}
@@ -123,28 +125,28 @@ public int size() {
}
@Override
- @NonNull public Iterator iterator() {
+ public Iterator iterator() {
return delegate.iterator();
}
@Override
- @NonNull public String resolve(@NonNull String expression) {
+ public String resolve(@NonNull String expression) {
return delegate.resolve(expression);
}
@Override
- @NonNull public String resolve(@NonNull String expression, boolean ignoreMissing) {
+ public String resolve(@NonNull String expression, boolean ignoreMissing) {
return delegate.resolve(expression, ignoreMissing);
}
@Override
- @NonNull public String resolve(
+ public String resolve(
@NonNull String expression, @NonNull String startDelim, @NonNull String endDelim) {
return delegate.resolve(expression, startDelim, endDelim);
}
@Override
- @NonNull public String resolve(
+ public String resolve(
@NonNull String expression,
boolean ignoreMissing,
@NonNull String startDelim,
@@ -153,12 +155,12 @@ public int size() {
}
@Override
- public void forEach(Consumer super ValueNode> action) {
+ public void forEach(Consumer super Value> action) {
delegate.forEach(action);
}
@Override
- public Spliterator spliterator() {
+ public Spliterator spliterator() {
return delegate.spliterator();
}
@@ -223,7 +225,7 @@ public boolean booleanValue(boolean defaultValue) {
}
@Override
- @NonNull public String value(@NonNull String defaultValue) {
+ public String value(@NonNull String defaultValue) {
return delegate.value(defaultValue);
}
@@ -233,29 +235,29 @@ public boolean booleanValue(boolean defaultValue) {
}
@Override
- @NonNull public T value(@NonNull SneakyThrows.Function fn) {
+ public T value(@NonNull SneakyThrows.Function fn) {
return delegate.value(fn);
}
@Override
- @NonNull public String value() {
+ public String value() {
return delegate.value();
}
@Override
- @NonNull public > T toEnum(@NonNull SneakyThrows.Function fn) {
+ public > T toEnum(@NonNull SneakyThrows.Function fn) {
return delegate.toEnum(fn);
}
@Override
- @NonNull public > T toEnum(
+ public > T toEnum(
@NonNull SneakyThrows.Function fn,
@NonNull Function nameProvider) {
return delegate.toEnum(fn, nameProvider);
}
@Override
- @NonNull public Optional toOptional() {
+ public Optional toOptional() {
return delegate.toOptional();
}
@@ -290,40 +292,40 @@ public boolean isObject() {
}
@Override
- @NonNull public Optional toOptional(@NonNull Class type) {
+ public Optional toOptional(@NonNull Class type) {
return delegate.toOptional(type);
}
@Override
- @NonNull public Set toSet(@NonNull Class type) {
+ public Set toSet(@NonNull Class type) {
return delegate.toSet(type);
}
@Override
- @NonNull public Map> toMultimap() {
+ public Map> toMultimap() {
return delegate.toMultimap();
}
@Override
- @NonNull public Map toMap() {
+ public Map toMap() {
return delegate.toMap();
}
}
- public static class ForwardingValueNode implements ValueNode {
- protected final ValueNode delegate;
+ public static class ForwardingValue implements Value {
+ protected final Value delegate;
- public ForwardingValueNode(ValueNode delegate) {
+ public ForwardingValue(Value delegate) {
this.delegate = delegate;
}
@Override
- @NonNull public ValueNode get(@NonNull int index) {
+ public Value get(@NonNull int index) {
return delegate.get(index);
}
@Override
- @NonNull public ValueNode get(@NonNull String name) {
+ public Value get(@NonNull String name) {
return delegate.get(name);
}
@@ -333,28 +335,28 @@ public int size() {
}
@Override
- @NonNull public Iterator iterator() {
+ public Iterator iterator() {
return delegate.iterator();
}
@Override
- @NonNull public String resolve(@NonNull String expression) {
+ public String resolve(@NonNull String expression) {
return delegate.resolve(expression);
}
@Override
- @NonNull public String resolve(@NonNull String expression, boolean ignoreMissing) {
+ public String resolve(@NonNull String expression, boolean ignoreMissing) {
return delegate.resolve(expression, ignoreMissing);
}
@Override
- @NonNull public String resolve(
+ public String resolve(
@NonNull String expression, @NonNull String startDelim, @NonNull String endDelim) {
return delegate.resolve(expression, startDelim, endDelim);
}
@Override
- @NonNull public String resolve(
+ public String resolve(
@NonNull String expression,
boolean ignoreMissing,
@NonNull String startDelim,
@@ -363,12 +365,12 @@ public int size() {
}
@Override
- public void forEach(Consumer super ValueNode> action) {
+ public void forEach(Consumer super Value> action) {
delegate.forEach(action);
}
@Override
- public Spliterator spliterator() {
+ public Spliterator spliterator() {
return delegate.spliterator();
}
@@ -433,7 +435,7 @@ public boolean booleanValue(boolean defaultValue) {
}
@Override
- @NonNull public String value(@NonNull String defaultValue) {
+ public String value(@NonNull String defaultValue) {
return delegate.value(defaultValue);
}
@@ -443,39 +445,39 @@ public boolean booleanValue(boolean defaultValue) {
}
@Override
- @NonNull public T value(@NonNull SneakyThrows.Function fn) {
+ public T value(@NonNull SneakyThrows.Function fn) {
return delegate.value(fn);
}
@Override
- @NonNull public String value() {
+ public String value() {
return delegate.value();
}
@Override
- @NonNull public List toList() {
+ public List toList() {
return delegate.toList();
}
@Override
- @NonNull public Set toSet() {
+ public Set toSet() {
return delegate.toSet();
}
@Override
- @NonNull public > T toEnum(@NonNull SneakyThrows.Function fn) {
+ public > T toEnum(@NonNull SneakyThrows.Function fn) {
return delegate.toEnum(fn);
}
@Override
- @NonNull public > T toEnum(
+ public > T toEnum(
@NonNull SneakyThrows.Function fn,
@NonNull Function nameProvider) {
return delegate.toEnum(fn, nameProvider);
}
@Override
- @NonNull public Optional toOptional() {
+ public Optional toOptional() {
return delegate.toOptional();
}
@@ -510,22 +512,22 @@ public boolean isObject() {
}
@Override
- @NonNull public Optional toOptional(@NonNull Class type) {
+ public Optional toOptional(@NonNull Class type) {
return delegate.toOptional(type);
}
@Override
- @NonNull public List toList(@NonNull Class type) {
+ public List toList(@NonNull Class type) {
return delegate.toList(type);
}
@Override
- @NonNull public Set toSet(@NonNull Class type) {
+ public