diff --git a/unchained-api/pom.xml b/unchained-api/pom.xml index 9634bd0..5584dc5 100644 --- a/unchained-api/pom.xml +++ b/unchained-api/pom.xml @@ -19,4 +19,20 @@ Unchained: API + + + ${project.groupId} + unchained-commons + ${project.version} + + + org.testng + testng + + + org.hamcrest + hamcrest-all + + + diff --git a/unchained-api/src/main/java/unchained/Application.java b/unchained-api/src/main/java/unchained/Application.java new file mode 100644 index 0000000..b46e259 --- /dev/null +++ b/unchained-api/src/main/java/unchained/Application.java @@ -0,0 +1,23 @@ +package unchained; + +/** + * TODO: doc + * + * @param + * @param + * @param + * @param + * @param + * @param + */ +public interface Application, I extends Input, + O extends Output, S extends Selector, A extends Application> + extends CompositeMiddleware { + + /** + * TODO: doc + * @return + */ + ApplicationContext context(); + +} diff --git a/unchained-api/src/main/java/unchained/ApplicationLifecycle.java b/unchained-api/src/main/java/unchained/ApplicationLifecycle.java index 847102b..da1d2fa 100644 --- a/unchained-api/src/main/java/unchained/ApplicationLifecycle.java +++ b/unchained-api/src/main/java/unchained/ApplicationLifecycle.java @@ -6,7 +6,7 @@ public interface ApplicationLifecycle extends Lifecycle { /** - * TODO + * TODO: doc */ void start(); diff --git a/unchained-api/src/main/java/unchained/ChainContext.java b/unchained-api/src/main/java/unchained/ChainContext.java index d872d51..011950c 100644 --- a/unchained-api/src/main/java/unchained/ChainContext.java +++ b/unchained-api/src/main/java/unchained/ChainContext.java @@ -8,10 +8,7 @@ * @param the lifecycle type. * @param the context type. */ -public interface ChainContext< - C extends ChainContext, - L extends ChainLifecycle - > extends Context { +public interface ChainContext> extends Context { /** * Sets the property to the given value. diff --git a/unchained-api/src/main/java/unchained/CompositeMiddleware.java b/unchained-api/src/main/java/unchained/CompositeMiddleware.java index a624abf..c76c710 100644 --- a/unchained-api/src/main/java/unchained/CompositeMiddleware.java +++ b/unchained-api/src/main/java/unchained/CompositeMiddleware.java @@ -3,35 +3,37 @@ /** * TODO: doc * - * @param * @param * @param * @param * @param - * @param - * @param - * @param + * @param + * @param + * @param */ -public interface CompositeMiddleware< - W extends CompositeMiddleware, - L extends ChainLifecycle, - C extends ChainContext, - I extends Input, - O extends Output, - M extends Matcher, - Q, - H> extends Middleware { +public interface CompositeMiddleware, I extends Input, + O extends Output, S extends Selector, W extends CompositeMiddleware, K> + extends Middleware { + + /** + * TODO: doc + * + * @param first + * @param rest + * @param + * @return + */ + > W use(N first, N... rest); /** * TODO: doc * - * @param matcher + * @param selector * @param first * @param rest * @param * @return */ - @SuppressWarnings("unchecked") - > W use(M matcher, N first, N... rest); + > W use(S selector, N first, N... rest); } diff --git a/unchained-api/src/main/java/unchained/Configuration.java b/unchained-api/src/main/java/unchained/Configuration.java index cea43fb..5a9ad2f 100644 --- a/unchained-api/src/main/java/unchained/Configuration.java +++ b/unchained-api/src/main/java/unchained/Configuration.java @@ -114,9 +114,7 @@ default boolean disabled(Option option) { * @param the type. * @return the value or {@code null} if it was never configured. */ - default E get(String key) { - return get(key, null); - } + E get(String key); /** *

Returns the value of the given option, or the corresponding default value for this option if it was never @@ -143,6 +141,8 @@ default E get(Option option) { * @param the type. * @return the configured value or the provided default. */ - E get(String key, E defaultValue); + default E get(String key, E defaultValue) { + return has(key) ? get(key) : defaultValue; + } } diff --git a/unchained-api/src/main/java/unchained/Input.java b/unchained-api/src/main/java/unchained/Input.java index 062908d..9f6b2eb 100644 --- a/unchained-api/src/main/java/unchained/Input.java +++ b/unchained-api/src/main/java/unchained/Input.java @@ -6,9 +6,7 @@ * @param the lifecycle type. * @param the context type. */ -public interface Input< - L extends ChainLifecycle, - C extends ChainContext> { +public interface Input> { /** * Gives access to the context associated with this input. diff --git a/unchained-api/src/main/java/unchained/Matcher.java b/unchained-api/src/main/java/unchained/Matcher.java deleted file mode 100644 index de6686b..0000000 --- a/unchained-api/src/main/java/unchained/Matcher.java +++ /dev/null @@ -1,57 +0,0 @@ -package unchained; - -/** - * TODO: doc - * - * @param - * @param - * @param - */ -public interface Matcher< - C extends ChainContext, - I extends Input, - E> { - - /** - * TODO: doc - * - * @param - * @param - * @param - * @param - * @param - */ - interface Compiler< - C extends ChainContext, - I extends Input, - M extends Matcher, - S, - T> { - - /** - * TODO: doc - * - * @param expression - * @return - */ - M compile(S expression); - - } - - interface Hit { - - boolean matched(); - - T value(); - - } - - /** - * TODO: doc - * - * @param input - * @return - */ - Hit test(I input); - -} diff --git a/unchained-api/src/main/java/unchained/Middleware.java b/unchained-api/src/main/java/unchained/Middleware.java index b0ddc25..22fdec5 100644 --- a/unchained-api/src/main/java/unchained/Middleware.java +++ b/unchained-api/src/main/java/unchained/Middleware.java @@ -12,14 +12,10 @@ * @param the type of context bound to this middleware's input. * @param the type of input this middleware works with. * @param the type of output this middleware can deal with. - * @param the type of output for this middleware. + * @param the type of outcome for this middleware. */ -public interface Middleware< - L extends ChainLifecycle, - C extends ChainContext, - I extends Input, - O extends Output, - Q> { +public interface Middleware, I extends Input, + O extends Output, K> { /** * Handles the incoming input and works on the input and output. This is a very generic method which will be called @@ -30,6 +26,6 @@ public interface Middleware< * @return a future whose completion represents the completion of the middleware itself. * @throws Exception in case the underlying logic causes an error. */ - CompletableFuture execute(I input, O output) throws Exception; + CompletableFuture execute(I input, O output) throws Exception; } diff --git a/unchained-api/src/main/java/unchained/MutableConfiguration.java b/unchained-api/src/main/java/unchained/MutableConfiguration.java index 5b1dbc1..946ce6d 100644 --- a/unchained-api/src/main/java/unchained/MutableConfiguration.java +++ b/unchained-api/src/main/java/unchained/MutableConfiguration.java @@ -3,9 +3,8 @@ /** * Mutable configuration. * - * @param */ -public interface MutableConfiguration extends Configuration { +public interface MutableConfiguration extends Configuration { /** * Enables the provided key by setting it to {@code true}. @@ -13,7 +12,7 @@ public interface MutableConfiguration extends Co * @param key the key to enable. * @return the configuration itself to use for chaining method calls. */ - default M enable(String key) { + default MutableConfiguration enable(String key) { return set(key, true); } @@ -23,7 +22,7 @@ default M enable(String key) { * @param option the option to enable. * @return the configuration itself to use for chaining method calls. */ - default M enable(Option option) { + default MutableConfiguration enable(Option option) { return set(option, true); } @@ -33,7 +32,7 @@ default M enable(Option option) { * @param key the key to disable. * @return the configuration itself to use for chaining method calls. */ - default M disable(String key) { + default MutableConfiguration disable(String key) { return set(key, false); } @@ -43,7 +42,7 @@ default M disable(String key) { * @param option the option to disable. * @return the configuration itself to use for chaining method calls. */ - default M disable(Option option) { + default MutableConfiguration disable(Option option) { return set(option, false); } @@ -54,7 +53,7 @@ default M disable(Option option) { * @param value the value. * @return the configuration itself to use for chaining method calls. */ - M set(String key, E value); + MutableConfiguration set(String key, E value); /** * Sets the value of the provided option to the given value. @@ -64,7 +63,7 @@ default M disable(Option option) { * @param the type of the value. * @return the configuration itself to use for chaining method calls. */ - default M set(Option option, E value) { + default MutableConfiguration set(Option option, E value) { return set(option.key(), value); } @@ -92,4 +91,12 @@ default E unset(Option option) { return unset(option.key()); } + /** + * Snapshots this configuration object and returns an immutable copy of it. Further changes to this object do not + * reflect in the returned copy. + * + * @return A immutable snapshot of this configuration. + */ + Configuration snapshot(); + } diff --git a/unchained-api/src/main/java/unchained/Output.java b/unchained-api/src/main/java/unchained/Output.java index 6c19339..99338c0 100644 --- a/unchained-api/src/main/java/unchained/Output.java +++ b/unchained-api/src/main/java/unchained/Output.java @@ -7,10 +7,7 @@ * @param the context type. * @param the input type. */ -public interface Output< - L extends ChainLifecycle, - C extends ChainContext, - I extends Input> { +public interface Output, I extends Input> { /** * Gives access to the input associated with this output. diff --git a/unchained-api/src/main/java/unchained/Selector.java b/unchained-api/src/main/java/unchained/Selector.java new file mode 100644 index 0000000..8a923a9 --- /dev/null +++ b/unchained-api/src/main/java/unchained/Selector.java @@ -0,0 +1,54 @@ +package unchained; + +/** + * TODO: doc + * + * @param + * @param + */ +public interface Selector, M> { + + /** + * TODO: doc + * + * @param + */ + interface Compiler, ?>> { + + /** + * TODO: doc + * + * @param expressions + * @return + */ + S compile(String... expressions); + + } + + interface Hit { + + /** + * TODO: doc + * + * @return + */ + boolean matched(); + + /** + * TODO: doc + * + * @return + */ + M value(); + + } + + /** + * TODO: doc + * + * @param input + * @return + */ + Hit test(I input); + +} diff --git a/unchained-api/src/main/java/unchained/Unchained.java b/unchained-api/src/main/java/unchained/Unchained.java new file mode 100644 index 0000000..39ce795 --- /dev/null +++ b/unchained-api/src/main/java/unchained/Unchained.java @@ -0,0 +1,44 @@ +package unchained; + +import unchained.contract.Factory; +import unchained.http.HttpApplication; +import unchained.http.HttpRoute; +import unchained.web.Route; + +public final class Unchained { + + private Unchained() { } + + public static Configuration emptyConfiguration() { + return Factory.forType(Configuration.class).create(); + } + + public static MutableConfiguration newConfiguration() { + return Factory.forType(MutableConfiguration.class).create(); + } + + public static HttpApplication newHttpApplication(Configuration configuration) { + return newApplication(HttpApplication.class, configuration); + } + + public static T newApplication(Class type, Configuration configuration) { + return Factory.forType(type).create(configuration); + } + + public static T newRoute(Class type) { + return Factory.forType(type).create(); + } + + public static HttpRoute newHttpRoute() { + return newRoute(HttpRoute.class); + } + + static { + try { + Class.forName("unchained.Utils"); + } catch (Exception ignored) { + ignored.printStackTrace(); + } + } + +} diff --git a/unchained-api/src/main/java/unchained/error/ContextException.java b/unchained-api/src/main/java/unchained/error/ContextException.java deleted file mode 100644 index 9012757..0000000 --- a/unchained-api/src/main/java/unchained/error/ContextException.java +++ /dev/null @@ -1,13 +0,0 @@ -package unchained.error; - -public abstract class ContextException extends FrameworkException { - - public ContextException(String message) { - super(message); - } - - public ContextException(String message, Throwable cause) { - super(message, cause); - } - -} diff --git a/unchained-api/src/main/java/unchained/http/BodyReader.java b/unchained-api/src/main/java/unchained/http/BodyReader.java deleted file mode 100644 index 45de809..0000000 --- a/unchained-api/src/main/java/unchained/http/BodyReader.java +++ /dev/null @@ -1,21 +0,0 @@ -package unchained.http; - -import java.io.InputStream; -import java.lang.reflect.Type; -import java.nio.charset.Charset; - -public interface BodyReader { - - InputStream asStream(); - - byte[] asBytes(); - - String asString(); - - String asString(Charset charset); - - E as(Class type); - - E as(Type type); - -} diff --git a/unchained-api/src/main/java/unchained/http/BodyWriter.java b/unchained-api/src/main/java/unchained/http/BodyWriter.java deleted file mode 100644 index 85c899d..0000000 --- a/unchained-api/src/main/java/unchained/http/BodyWriter.java +++ /dev/null @@ -1,17 +0,0 @@ -package unchained.http; - -import java.io.OutputStream; - -public interface BodyWriter { - - OutputStream asStream(); - - void set(byte[] body); - - void set(String body); - - void set(E body); - - void done(); - -} diff --git a/unchained-api/src/main/java/unchained/http/HttpApplication.java b/unchained-api/src/main/java/unchained/http/HttpApplication.java new file mode 100644 index 0000000..45d018e --- /dev/null +++ b/unchained-api/src/main/java/unchained/http/HttpApplication.java @@ -0,0 +1,13 @@ +package unchained.http; + +import unchained.Application; +import unchained.web.RequestContext; +import unchained.web.RequestLifecycle; + +/** + * TODO: doc + */ +public interface HttpApplication extends Application, HttpRouteDefiner { + +} diff --git a/unchained-api/src/main/java/unchained/http/HttpBodyReader.java b/unchained-api/src/main/java/unchained/http/HttpBodyReader.java new file mode 100644 index 0000000..09f2e86 --- /dev/null +++ b/unchained-api/src/main/java/unchained/http/HttpBodyReader.java @@ -0,0 +1,11 @@ +package unchained.http; + +import unchained.web.PayloadReader; + +/** + * TODO: doc + * + */ +public interface HttpBodyReader extends PayloadReader { + +} diff --git a/unchained-api/src/main/java/unchained/http/HttpBodyWriter.java b/unchained-api/src/main/java/unchained/http/HttpBodyWriter.java new file mode 100644 index 0000000..85a787b --- /dev/null +++ b/unchained-api/src/main/java/unchained/http/HttpBodyWriter.java @@ -0,0 +1,10 @@ +package unchained.http; + +import unchained.web.PayloadWriter; + +/** + * TODO: doc + */ +public interface HttpBodyWriter extends PayloadWriter { + +} diff --git a/unchained-api/src/main/java/unchained/http/HttpMethod.java b/unchained-api/src/main/java/unchained/http/HttpMethod.java new file mode 100644 index 0000000..067df82 --- /dev/null +++ b/unchained-api/src/main/java/unchained/http/HttpMethod.java @@ -0,0 +1,7 @@ +package unchained.http; + +public interface HttpMethod { + + String GET = "GET"; + +} diff --git a/unchained-api/src/main/java/unchained/http/HttpRequest.java b/unchained-api/src/main/java/unchained/http/HttpRequest.java index 8af28fa..0635184 100644 --- a/unchained-api/src/main/java/unchained/http/HttpRequest.java +++ b/unchained-api/src/main/java/unchained/http/HttpRequest.java @@ -4,13 +4,36 @@ import java.net.InetSocketAddress; -public interface HttpRequest extends Request { +public interface HttpRequest extends Request, HttpBodyReader { /** * @return the HTTP method/verb associated with this request. */ String method(); + /** + * TODO: doc + * + * @param method + * @return + */ + HttpRequest method(String method); + + /** + * TODO: doc + * + * @return + */ + String uri(); + + /** + * TODO: doc + * + * @param uri + * @return + */ + HttpRequest uri(String uri); + /** * @return the HTTP scheme used to run this request (e.g. https). */ @@ -26,4 +49,25 @@ public interface HttpRequest extends Request { */ InetSocketAddress remote(); + /** + * TODO: doc + * + * @return + */ + @Override + default String target() { + return uri(); + } + + /** + * TODO: doc + * + * @param target + * @return + */ + @Override + default HttpRequest target(String target) { + return uri(target); + } + } diff --git a/unchained-api/src/main/java/unchained/http/HttpRequestHandler.java b/unchained-api/src/main/java/unchained/http/HttpRequestHandler.java index 18f4ba4..7faf599 100644 --- a/unchained-api/src/main/java/unchained/http/HttpRequestHandler.java +++ b/unchained-api/src/main/java/unchained/http/HttpRequestHandler.java @@ -4,15 +4,6 @@ import java.util.concurrent.CompletableFuture; -public interface HttpRequestHandler extends RequestHandler { - - void handle(HttpRequest request, HttpResponse response); - - @Override - default CompletableFuture execute(HttpRequest input, HttpResponse output) throws Exception { - return CompletableFuture - .allOf(input.payload(), output.payload()) - .thenRunAsync(() -> handle(input, output)); - } +public interface HttpRequestHandler extends RequestHandler { } diff --git a/unchained-api/src/main/java/unchained/http/HttpRequestLine.java b/unchained-api/src/main/java/unchained/http/HttpRequestLine.java new file mode 100644 index 0000000..696a0c1 --- /dev/null +++ b/unchained-api/src/main/java/unchained/http/HttpRequestLine.java @@ -0,0 +1,29 @@ +package unchained.http; + +/** + * TODO: doc + */ +public interface HttpRequestLine { + + /** + * TODO: doc + * + * @return + */ + String method(); + + /** + * TODO: doc + * + * @return + */ + String target(); + + /** + * TODO: doc + * + * @return + */ + String version(); + +} diff --git a/unchained-api/src/main/java/unchained/http/HttpResponse.java b/unchained-api/src/main/java/unchained/http/HttpResponse.java index 58663d3..3ef6f4a 100644 --- a/unchained-api/src/main/java/unchained/http/HttpResponse.java +++ b/unchained-api/src/main/java/unchained/http/HttpResponse.java @@ -2,12 +2,36 @@ import unchained.web.Response; -public interface HttpResponse extends Response { +/** + * + */ +public interface HttpResponse extends Response, HttpBodyWriter { + /** + * + * @return + */ int status(); + /** + * + * @return + */ String statusText(); + /** + * + * @param status + * @return + */ + HttpResponse status(int status); + + /** + * + * @param status + * @param text + * @return + */ HttpResponse status(int status, String text); } diff --git a/unchained-api/src/main/java/unchained/http/HttpRoute.java b/unchained-api/src/main/java/unchained/http/HttpRoute.java new file mode 100644 index 0000000..c2313cc --- /dev/null +++ b/unchained-api/src/main/java/unchained/http/HttpRoute.java @@ -0,0 +1,8 @@ +package unchained.http; + +import unchained.web.Route; + +public interface HttpRoute extends Route, + HttpRouteDefiner { + +} diff --git a/unchained-api/src/main/java/unchained/http/HttpRouteDefiner.java b/unchained-api/src/main/java/unchained/http/HttpRouteDefiner.java new file mode 100644 index 0000000..a4f16ef --- /dev/null +++ b/unchained-api/src/main/java/unchained/http/HttpRouteDefiner.java @@ -0,0 +1,57 @@ +package unchained.http; + +import unchained.Middleware; +import unchained.web.RequestContext; +import unchained.web.RequestLifecycle; + +public interface HttpRouteDefiner { + + > T use(N first, N... rest); + + default T use(HttpRequestHandler first, HttpRequestHandler... rest) { + return use((Middleware) first, rest); + } + + default T use(ReturningHttpRequestHandler first, ReturningHttpRequestHandler... rest) { + return use((Middleware) first, rest); + } + + > T use(String pattern, N first, N... rest); + + default T use(String pattern, HttpRequestHandler first, HttpRequestHandler... rest) { + return use(pattern, (Middleware) first, rest); + } + + > T all(String pattern, N first, N... rest); + + default T all(String pattern, HttpRequestHandler first, HttpRequestHandler... rest) { + return all(pattern, (Middleware) first, rest); + } + + default T all(String pattern, ReturningHttpRequestHandler first, ReturningHttpRequestHandler... rest) { + return all(pattern, (Middleware) first, rest); + } + + > T custom(String method, String pattern, N first, N... rest); + + default T custom(String method, String pattern, HttpRequestHandler first, HttpRequestHandler... rest) { + return custom(method, pattern, (Middleware) first, rest); + } + + default T custom(String method, String pattern, ReturningHttpRequestHandler first, ReturningHttpRequestHandler... rest) { + return custom(method, pattern, (Middleware) first, rest); + } + + default > T get(String pattern, N first, N... rest) { + return custom(HttpMethod.GET, pattern, first, rest); + } + + default T get(String pattern, HttpRequestHandler first, HttpRequestHandler... rest) { + return get(pattern, (Middleware) first, rest); + } + + default T get(String pattern, ReturningHttpRequestHandler first, ReturningHttpRequestHandler... rest) { + return get(pattern, (Middleware) first, rest); + } + +} diff --git a/unchained-api/src/main/java/unchained/http/HttpSelector.java b/unchained-api/src/main/java/unchained/http/HttpSelector.java new file mode 100644 index 0000000..2538e6a --- /dev/null +++ b/unchained-api/src/main/java/unchained/http/HttpSelector.java @@ -0,0 +1,11 @@ +package unchained.http; + +import unchained.Selector; + +public interface HttpSelector extends Selector { + + interface Compiler extends Selector.Compiler { + + } + +} diff --git a/unchained-api/src/main/java/unchained/http/ReturningHttpRequestHandler.java b/unchained-api/src/main/java/unchained/http/ReturningHttpRequestHandler.java index 5c2540e..8a81855 100644 --- a/unchained-api/src/main/java/unchained/http/ReturningHttpRequestHandler.java +++ b/unchained-api/src/main/java/unchained/http/ReturningHttpRequestHandler.java @@ -2,17 +2,16 @@ import unchained.web.RequestHandler; +import java.io.IOException; import java.util.concurrent.CompletableFuture; -public interface ReturningHttpRequestHandler extends RequestHandler { +public interface ReturningHttpRequestHandler extends RequestHandler { Object handle(HttpRequest request); @Override - default CompletableFuture execute(HttpRequest input, HttpResponse output) throws Exception { - return CompletableFuture - .allOf(input.payload(), output.payload()) - .thenRunAsync(() -> output.payload().join().set(handle(input))); + default void handle(HttpRequest request, HttpResponse response) { + response.write(handle(request)); } } diff --git a/unchained-api/src/main/java/unchained/web/PayloadReader.java b/unchained-api/src/main/java/unchained/web/PayloadReader.java new file mode 100644 index 0000000..5ad0bae --- /dev/null +++ b/unchained-api/src/main/java/unchained/web/PayloadReader.java @@ -0,0 +1,70 @@ +package unchained.web; + +import java.io.InputStream; +import java.io.Reader; +import java.lang.reflect.Type; +import java.nio.charset.Charset; + +/** + * TODO: doc + * + * @param + */ +public interface PayloadReader> { + + /** + * TODO: doc + * + * @return + */ + InputStream asStream(); + + /** + * TODO: doc + * + * @param charset + * @return + */ + Reader asReader(Charset charset); + + /** + * TODO: doc + * + * @return + */ + byte[] asBytes(); + + /** + * TODO: doc + * + * @return + */ + String asString(); + + /** + * TODO: doc + * + * @param charset + * @return + */ + String asString(Charset charset); + + /** + * TODO: doc + * + * @param type + * @param + * @return + */ + E as(Class type); + + /** + * TODO: doc + * + * @param type + * @param + * @return + */ + E as(Type type); + +} diff --git a/unchained-api/src/main/java/unchained/web/PayloadWriter.java b/unchained-api/src/main/java/unchained/web/PayloadWriter.java new file mode 100644 index 0000000..fd47315 --- /dev/null +++ b/unchained-api/src/main/java/unchained/web/PayloadWriter.java @@ -0,0 +1,70 @@ +package unchained.web; + +import java.io.OutputStream; +import java.io.Writer; +import java.nio.charset.Charset; + +/** + * TODO: doc + * + * @param + */ +public interface PayloadWriter> { + + /** + * TODO: doc + * + * @return + */ + OutputStream asStream(); + + /** + * TODO: doc + * + * @param charset + * @return + */ + Writer asWriter(Charset charset); + + /** + * TODO: doc + * + * @param payload + * @return + */ + O write(byte[] payload); + + /** + * TODO: doc + * + * @param payload + * @return + */ + O write(String payload, Charset charset); + + /** + * TODO: doc + * + * @param payload + * @return + */ + @SuppressWarnings("unchecked") + O write(String payload); + + /** + * TODO: doc + * + * @param payload + * @param + * @return + */ + O write(E payload); + + /** + * TODO: doc + * + * @return + */ + O done(); + +} diff --git a/unchained-api/src/main/java/unchained/web/Request.java b/unchained-api/src/main/java/unchained/web/Request.java index 6e1d8a5..82dff6c 100644 --- a/unchained-api/src/main/java/unchained/web/Request.java +++ b/unchained-api/src/main/java/unchained/web/Request.java @@ -4,28 +4,25 @@ import java.util.Map; import java.util.Set; -import java.util.concurrent.CompletableFuture; /** * TODO: doc - * - * @param */ -public interface Request extends Input { +public interface Request> extends Input, PayloadReader { /** * TODO: doc * * @return */ - String uri(); + String target(); /** * TODO: doc * - * @param uri + * @param target */ - void uri(String uri); + I target(String target); /** * TODO: doc @@ -73,20 +70,6 @@ public interface Request extends Input { * @param name * @param value */ - E header(String name, E value); - - /** - * TODO: doc - * - * @return - */ - CompletableFuture payload(); - - /** - * TODO: doc - * - * @param payload - */ - void payload(T payload); + I header(String name, E value); } diff --git a/unchained-api/src/main/java/unchained/web/RequestContext.java b/unchained-api/src/main/java/unchained/web/RequestContext.java index bc680d1..dc3b474 100644 --- a/unchained-api/src/main/java/unchained/web/RequestContext.java +++ b/unchained-api/src/main/java/unchained/web/RequestContext.java @@ -3,19 +3,16 @@ import unchained.ChainContext; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ForkJoinPool; /** * TODO: doc */ -public interface RequestContext extends ChainContext { +public interface RequestContext extends ChainContext { - /** - * Convenient method to get access to the {@link Executor} bound to this context. - * - * @return the {@link Executor} bound to this context. - */ default Executor executor() { - return get(Executor.class); + return has(Executor.class) ? get(Executor.class) : ForkJoinPool.commonPool(); } } diff --git a/unchained-api/src/main/java/unchained/web/RequestHandler.java b/unchained-api/src/main/java/unchained/web/RequestHandler.java index b4357c6..c1c76d6 100644 --- a/unchained-api/src/main/java/unchained/web/RequestHandler.java +++ b/unchained-api/src/main/java/unchained/web/RequestHandler.java @@ -2,17 +2,22 @@ import unchained.Middleware; +import java.util.concurrent.CompletableFuture; + /** * TODO: doc * - * @param - * @param * @param * @param */ -public interface RequestHandler< - S, T, - I extends Request, - O extends Response> extends Middleware { +public interface RequestHandler, O extends Response> + extends Middleware { + + void handle(I input, O output); + @Override + default CompletableFuture execute(I input, O output) throws Exception { + return CompletableFuture + .runAsync(() -> handle(input, output), input.context().executor()); + } } diff --git a/unchained-api/src/main/java/unchained/web/Response.java b/unchained-api/src/main/java/unchained/web/Response.java index c0c53c4..e8633da 100644 --- a/unchained-api/src/main/java/unchained/web/Response.java +++ b/unchained-api/src/main/java/unchained/web/Response.java @@ -4,15 +4,14 @@ import java.util.Map; import java.util.Set; -import java.util.concurrent.CompletableFuture; /** + * TODO: doc * - * @param - * @param * @param */ -public interface Response> extends Output { +public interface Response, O extends Response> extends + Output, PayloadWriter { /** * TODO: doc @@ -60,20 +59,6 @@ public interface Response> extends Output E header(String name, E value); - - /** - * TODO: doc - * - * @return - */ - CompletableFuture payload(); - - /** - * TODO: doc - * - * @param payload - */ - void payload(T payload); + O header(String name, E value); } diff --git a/unchained-api/src/main/java/unchained/web/Route.java b/unchained-api/src/main/java/unchained/web/Route.java new file mode 100644 index 0000000..5ef0e2f --- /dev/null +++ b/unchained-api/src/main/java/unchained/web/Route.java @@ -0,0 +1,25 @@ +package unchained.web; + +import unchained.CompositeMiddleware; +import unchained.Selector; + +/** + * TODO: doc + * + * @param + * @param + * @param + * @param + * @param + */ +public interface Route, O extends Response, S extends Selector, M, + R extends Route> extends CompositeMiddleware { + + /** + * TODO: doc + * + * @return + */ + M domain(); + +} diff --git a/unchained-build/pom.xml b/unchained-build/pom.xml index 3681235..2ab8f23 100644 --- a/unchained-build/pom.xml +++ b/unchained-build/pom.xml @@ -26,6 +26,7 @@ ../unchained-commons ../unchained-api ../unchained-spi + ../unchained-examples diff --git a/unchained-commons/src/main/java/unchained/contract/DefaultRegistry.java b/unchained-commons/src/main/java/unchained/contract/DefaultRegistry.java index ae46f74..b8b5a90 100644 --- a/unchained-commons/src/main/java/unchained/contract/DefaultRegistry.java +++ b/unchained-commons/src/main/java/unchained/contract/DefaultRegistry.java @@ -4,19 +4,19 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; -class DefaultRegistry implements Registry { +class DefaultRegistry> implements Registry { - private final Map entries = new ConcurrentHashMap<>(); + private final Map entries = new ConcurrentHashMap<>(); private DefaultRegistry() {} @Override - public T get(String key) { + public T get(K key) { return entries.get(key); } @Override - public boolean has(String key) { + public boolean has(K key) { return entries.containsKey(key); } @@ -26,7 +26,7 @@ public void register(T value) { } @Override - public Set keys() { + public Set keys() { return Collections.unmodifiableSet(entries.keySet()); } @@ -35,14 +35,14 @@ public Set values() { return Collections.unmodifiableSet(new HashSet<>(entries.values())); } - private static final Map, Registry> ALL = new HashMap<>(); + private static final Map, Registry>> ALL = new HashMap<>(); private static final ReentrantReadWriteLock LOCK = new ReentrantReadWriteLock(); @SuppressWarnings("unchecked") - static Registry lookup(Class type) { + static > Registry lookup(Class type) { LOCK.readLock().lock(); try { - Registry result = (Registry) ALL.get(type); + Registry result = (Registry) ALL.get(type); if (result == null) { LOCK.readLock().unlock(); LOCK.writeLock().lock(); diff --git a/unchained-commons/src/main/java/unchained/contract/Factory.java b/unchained-commons/src/main/java/unchained/contract/Factory.java index 4ee5102..db4e821 100644 --- a/unchained-commons/src/main/java/unchained/contract/Factory.java +++ b/unchained-commons/src/main/java/unchained/contract/Factory.java @@ -2,38 +2,44 @@ import unchained.error.FactoryNotFoundException; -public interface Factory extends Registrable { +public interface Factory extends Registrable> { Class type(); - T create(A configuration); - - interface Arguments {} - - class NoArgument implements Arguments { - - public static final NoArgument INSTANCE = new NoArgument(); + T create(U arguments); + default T create() { + return create(null); } @Override - default String registryKey() { - return type().getCanonicalName(); + default Class registryKey() { + return type(); } - static void register(Factory factory) { + @SuppressWarnings("unchecked") + static void register(Factory factory) { Registry.lookup(Factory.class).register(factory); } @SuppressWarnings("unchecked") - static Factory forType(Class type) { - // WARNING: duplicate logic at `type.getCanonicalName()`. - Factory result = Registry.lookup(Factory.class).get(type.getCanonicalName()); + static Factory forType(Class type) { + /* + + NOTE: + + This is an oversimplified logic to implement factory lookup. + A more advanced implementation must be able to find the best + factory among a set of candidate factories. + + */ + Factory result = (Factory) Registry.lookup(Factory.class).get(type); + if (result == null) { throw new FactoryNotFoundException(type); } try { - return (Factory) result; + return (Factory) result; } catch (ClassCastException e) { throw new FactoryNotFoundException(type, e); } diff --git a/unchained-commons/src/main/java/unchained/contract/Registrable.java b/unchained-commons/src/main/java/unchained/contract/Registrable.java index 54df2f5..624f698 100644 --- a/unchained-commons/src/main/java/unchained/contract/Registrable.java +++ b/unchained-commons/src/main/java/unchained/contract/Registrable.java @@ -1,7 +1,7 @@ package unchained.contract; -public interface Registrable { +public interface Registrable { - String registryKey(); + K registryKey(); } diff --git a/unchained-commons/src/main/java/unchained/contract/Registry.java b/unchained-commons/src/main/java/unchained/contract/Registry.java index b2d0799..abe51c0 100644 --- a/unchained-commons/src/main/java/unchained/contract/Registry.java +++ b/unchained-commons/src/main/java/unchained/contract/Registry.java @@ -5,32 +5,33 @@ import java.util.function.Function; import java.util.stream.Collectors; -public interface Registry { +public interface Registry> { void register(T value); - T get(String key); + T get(K key); - default T get(String key, T defaultValue) { + default T get(K key, T defaultValue) { return get(key, s -> defaultValue); } - default T get(String key, Function computer) { + default T get(K key, Function computer) { final T result = get(key); return result == null ? computer.apply(key) : result; } - boolean has(String key); + boolean has(K key); - Set keys(); + Set keys(); Set values(); - default Map toMap() { + default Map toMap() { return keys().stream().collect(Collectors.toMap(Function.identity(), this::get)); } - static Registry lookup(Class type) { + @SuppressWarnings("unchecked") + static > Registry lookup(Class type) { return DefaultRegistry.lookup(type); } diff --git a/unchained-commons/src/test/java/unchained/contract/FactoryTest.java b/unchained-commons/src/test/java/unchained/contract/FactoryTest.java index ff55094..7ee286a 100644 --- a/unchained-commons/src/test/java/unchained/contract/FactoryTest.java +++ b/unchained-commons/src/test/java/unchained/contract/FactoryTest.java @@ -17,7 +17,7 @@ private static class A { } - private static class ArgumentsA implements Factory.Arguments { + private static class ArgumentsA { private String foo; @@ -51,20 +51,20 @@ public void setUp() { } @Test - public void testForType() { - final Factory factoryA = Factory.forType(A.class); + public void testForExistingType() { + final Factory factoryA = Factory.forType(A.class); assertThat(factoryA, "factory", isNotNull()); assertThat(factoryA, "factory", isA(FactoryA.class)); } @Test(expectedExceptions = FactoryNotFoundException.class) - public void testForTypeNonExisting() { - Factory dummy = Factory.forType(String.class); + public void testForNonExistingType() { + Factory dummy = Factory.forType(String.class); } @Test public void testType() { - final Factory factoryA = Factory.forType(A.class); + final Factory factoryA = Factory.forType(A.class); assertThat(factoryA.type(), "type", is(A.class)); } diff --git a/unchained-commons/src/test/java/unchained/contract/RegistryTest.java b/unchained-commons/src/test/java/unchained/contract/RegistryTest.java index ac263da..04a097a 100644 --- a/unchained-commons/src/test/java/unchained/contract/RegistryTest.java +++ b/unchained-commons/src/test/java/unchained/contract/RegistryTest.java @@ -7,7 +7,7 @@ public class RegistryTest { - private static class TestRegistrable implements Registrable { + private static class TestRegistrable implements Registrable { private String registryKey; @@ -24,14 +24,14 @@ public String registryKey() { @Test public void testLookup() { - final Registry registry = Registry.lookup(TestRegistrable.class); + final Registry registry = Registry.lookup(TestRegistrable.class); assertThat(registry, "registry", isNotNull()); assertThat(registry, "registry", isSameAs(Registry.lookup(TestRegistrable.class))); } @Test public void testRegister() { - final Registry registry = Registry.lookup(TestRegistrable.class); + final Registry registry = Registry.lookup(TestRegistrable.class); final TestRegistrable registrable = new TestRegistrable("registered"); registry.register(registrable); assertThat(registry.get("registered"), "registered", isSameAs(registrable)); @@ -41,7 +41,7 @@ public void testRegister() { @Test public void testRegisterAgain() { - final Registry registry = Registry.lookup(TestRegistrable.class); + final Registry registry = Registry.lookup(TestRegistrable.class); final TestRegistrable registrable = new TestRegistrable("registered"); if (!registry.has("registered")) { registry.register(new TestRegistrable("registered")); @@ -52,7 +52,7 @@ public void testRegisterAgain() { @Test public void testKeysAndValues() { - final Registry registry = Registry.lookup(TestRegistrable.class); + final Registry registry = Registry.lookup(TestRegistrable.class); final TestRegistrable newRegistrable = new TestRegistrable("new-registrable"); registry.register(newRegistrable); assertThat(registry.keys().contains("new-registrable"), "existingKey", is(true)); diff --git a/unchained-examples/pom.xml b/unchained-examples/pom.xml new file mode 100644 index 0000000..8b612ae --- /dev/null +++ b/unchained-examples/pom.xml @@ -0,0 +1,43 @@ + + + + + 4.0.0 + + + unchained + unchained-parent + 1.0.0-SNAPSHOT + ../unchained-parent/pom.xml + + + unchained-examples + jar + + Unchained: Examples + + + + ${project.groupId} + unchained-api + ${project.version} + + + ${project.groupId} + unchained-spi + ${project.version} + + + org.testng + testng + + + org.hamcrest + hamcrest-all + + + + diff --git a/unchained-examples/src/com/example/HelloWorld.java b/unchained-examples/src/com/example/HelloWorld.java new file mode 100644 index 0000000..db5fa78 --- /dev/null +++ b/unchained-examples/src/com/example/HelloWorld.java @@ -0,0 +1,39 @@ +package com.example; + +import unchained.Configuration; +import unchained.http.HttpApplication; +import unchained.http.HttpRoute; + +import static unchained.Unchained.*; + +public final class HelloWorld { + + + + public static void main(String[] args) { + + // Step 1: define + + Configuration cfg = emptyConfiguration(); + HttpApplication app = newHttpApplication(cfg); + + app.get("/hello", (request, response) -> { + // Say hello + }); + app.get("/bye", (input, output) -> null); + + HttpRoute secure = newHttpRoute(); + + secure.use((request, response) -> { + // Check security + }); + secure.get("/hello", (request) -> { + // Say secured hello + return "Hello, World"; + }); + + app.use("/secure", secure); + + } + +} diff --git a/unchained-spi/src/main/java/unchained/AbstractChainContext.java b/unchained-spi/src/main/java/unchained/AbstractChainContext.java deleted file mode 100644 index 0d6a4a3..0000000 --- a/unchained-spi/src/main/java/unchained/AbstractChainContext.java +++ /dev/null @@ -1,57 +0,0 @@ -package unchained; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import static unchained.Utils.forceNotNull; -import static unchained.assertion.Assert.assertThat; -import static unchained.assertion.Assertions.isNotNull; - -/** - * TODO: doc - * - * @param - * @param - */ -public abstract class AbstractChainContext, L extends ChainLifecycle> - extends AbstractNestableContext implements ChainContext { - - private final Map properties; - - protected AbstractChainContext(C parent) { - this(parent, forceNotNull(parent, "parent").lifecycle(), null); - } - - protected AbstractChainContext(ApplicationContext applicationContext, L lifecycle) { - this(forceNotNull(applicationContext, "applicationContext"), forceNotNull(lifecycle, "lifecycle"), - null); - } - - protected AbstractChainContext(Context parent, L lifecycle, Map properties) { - super(forceNotNull(parent, "parent"), forceNotNull(lifecycle, "lifecycle")); - this.properties = properties == null ? new ConcurrentHashMap<>() : properties; - } - - @Override - protected Map properties() { - return properties; - } - - /** - * {@inheritDoc} - */ - @Override - @SuppressWarnings("unchecked") - public C property(String name, Object value) { - assertThat(name, "propertyName", isNotNull()); - if (value == null) { - properties().remove(name); - } else { - properties().put(name, value); - } - return (C) this; - } - - -} - diff --git a/unchained-spi/src/main/java/unchained/AbstractChainLifecycle.java b/unchained-spi/src/main/java/unchained/AbstractChainLifecycle.java deleted file mode 100644 index e63263a..0000000 --- a/unchained-spi/src/main/java/unchained/AbstractChainLifecycle.java +++ /dev/null @@ -1,43 +0,0 @@ -package unchained; - -import java.util.concurrent.atomic.AtomicBoolean; - -import static unchained.Utils.forceNotNull; - -/** - * TODO: doc - */ -public abstract class AbstractChainLifecycle extends AbstractLifecycle implements ChainLifecycle { - - private final AtomicBoolean stopped; - - protected AbstractChainLifecycle() { - this(false); - } - - protected AbstractChainLifecycle(Lifecycle lifecycle) { - this(forceNotNull(lifecycle, "lifecycle").stopped()); - } - - protected AbstractChainLifecycle(boolean stopped) { - super(stopped); - this.stopped = new AtomicBoolean(stopped); - } - - /** - * {@inheritDoc} - */ - @Override - public void stop() { - stopped.set(true); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean stopped() { - return stopped.get(); - } - -} diff --git a/unchained-spi/src/main/java/unchained/AbstractCompositeContext.java b/unchained-spi/src/main/java/unchained/AbstractCompositeContext.java deleted file mode 100644 index 39c6c96..0000000 --- a/unchained-spi/src/main/java/unchained/AbstractCompositeContext.java +++ /dev/null @@ -1,175 +0,0 @@ -package unchained; - -import unchained.error.AmbiguousBeanSelectionException; - -import java.util.*; -import java.util.stream.Collectors; - -import static unchained.assertion.Assert.assertThat; -import static unchained.assertion.Assertions.isNotNull; - -/** - * TODO: doc - */ -public abstract class AbstractCompositeContext, M extends AbstractCompositeContext> - extends AbstractNestableContext implements Context { - - private final List contexts; - - protected AbstractCompositeContext(L lifecycle) { - this(null, lifecycle); - } - - protected AbstractCompositeContext(Context parent, L lifecycle) { - super(parent, lifecycle); - this.contexts = new LinkedList<>(); - } - - @SuppressWarnings("unchecked") - private Set allOf(String name) { - assertThat(name, "beanName", isNotNull()); - return contexts.stream() - .filter(context -> context.has(name)) - .map(context -> (E) context.get(name)) - .collect(Collectors.toSet()); - } - - private Set allOf(Class type) { - assertThat(type, "beanType", isNotNull()); - return contexts.stream() - .filter(context -> context.has(type)) - .map(context -> context.get(type)) - .collect(Collectors.toSet()); - } - - private Set allOf(String name, Class type) { - assertThat(name, "beanName", isNotNull()); - assertThat(type, "beanType", isNotNull()); - return contexts.stream() - .filter(context -> context.has(name, type)) - .map(context -> context.get(name, type)) - .collect(Collectors.toSet()); - } - - private boolean addContext(C context) { - assertThat(context, "applicationContext", isNotNull()); - return !contexts.contains(context) && contexts.add(context); - } - - /** - * {@inheritDoc} - */ - @Override - public E get(String name) { - final Set beans = allOf(name); - if (beans.isEmpty()) { - return super.get(name); - } - if (beans.size() == 1) { - return beans.iterator().next(); - } - throw new AmbiguousBeanSelectionException(name); - } - - /** - * {@inheritDoc} - */ - @Override - public E get(Class type) { - final Set beans = allOf(type); - if (beans.isEmpty()) { - return super.get(type); - } - if (beans.size() == 1) { - return beans.iterator().next(); - } - throw new AmbiguousBeanSelectionException(type); - } - - /** - * {@inheritDoc} - */ - @Override - public E get(String name, Class type) { - final Set beans = allOf(name, type); - if (beans.isEmpty()) { - return super.get(name, type); - } - if (beans.size() == 1) { - return beans.iterator().next(); - } - throw new AmbiguousBeanSelectionException(name, type); - } - - /** - * {@inheritDoc} - */ - @Override - public Collection getAll(Class type) { - assertThat(type, "beanType", isNotNull()); - final Set allBeans = new HashSet<>(); - allBeans.addAll(contexts.stream() - .map(context -> context.getAll(type)) - .flatMap(Collection::stream) - .collect(Collectors.toSet())); - allBeans.addAll(super.getAll(type)); - return allBeans; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean has(String name) { - assertThat(name, "beanName", isNotNull()); - return contexts.stream() - .filter(context -> context.has(name)) - .map(context -> true) - .findFirst() - .orElse(super.has(name)); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean has(Class type) { - assertThat(type, "beanType", isNotNull()); - return contexts.stream() - .filter(context -> context.has(type)) - .map(context -> true) - .findFirst() - .orElse(super.has(type)); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean has(String name, Class type) { - assertThat(name, "beanName", isNotNull()); - assertThat(type, "beanType", isNotNull()); - return contexts.stream() - .filter(context -> context.has(name, type)) - .map(context -> true) - .findFirst() - .orElse(super.has(name, type)); - } - - /** - * TODO: doc - * - * @param first - * @param rest - * @return - */ - @SuppressWarnings("unchecked") - public M add(C first, C... rest) { - addContext(first); - for (C context : rest) { - addContext(context); - } - return (M) this; - } - -} diff --git a/unchained-spi/src/main/java/unchained/AbstractContext.java b/unchained-spi/src/main/java/unchained/AbstractContext.java deleted file mode 100644 index 0948fb4..0000000 --- a/unchained-spi/src/main/java/unchained/AbstractContext.java +++ /dev/null @@ -1,65 +0,0 @@ -package unchained; - -import java.util.Map; -import java.util.Set; - -import static unchained.Utils.forceNotNull; -import static unchained.assertion.Assert.assertThat; -import static unchained.assertion.Assertions.isNotNull; - -/** - * TODO: doc - * - * @param - */ -public abstract class AbstractContext implements Context { - - private final L lifecycle; - - protected AbstractContext(L lifecycle) { - this.lifecycle = forceNotNull(lifecycle, "lifecycle"); - } - - /** - * TODO: doc - * - * @return - */ - protected abstract Map properties(); - - /** - * ${@inheritDoc} - */ - @Override - public L lifecycle() { - return lifecycle; - } - - /** - * ${@inheritDoc} - */ - @Override - @SuppressWarnings("unchecked") - public E property(String name) { - assertThat(name, "propertyName", isNotNull()); - return (E) properties().get(name); - } - - /** - * ${@inheritDoc} - */ - @Override - public boolean hasProperty(String name) { - assertThat(name, "propertyName", isNotNull()); - return properties().containsKey(name); - } - - /** - * ${@inheritDoc} - */ - @Override - public Set propertyNames() { - return properties().keySet(); - } - -} diff --git a/unchained-spi/src/main/java/unchained/AbstractLifecycle.java b/unchained-spi/src/main/java/unchained/AbstractLifecycle.java deleted file mode 100644 index 6c44691..0000000 --- a/unchained-spi/src/main/java/unchained/AbstractLifecycle.java +++ /dev/null @@ -1,29 +0,0 @@ -package unchained; - -import static unchained.Utils.forceNotNull; - -public abstract class AbstractLifecycle implements Lifecycle { - - private final boolean stopped; - - protected AbstractLifecycle() { - this(false); - } - - protected AbstractLifecycle(boolean stopped) { - this.stopped = stopped; - } - - protected AbstractLifecycle(Lifecycle lifecycle) { - this(forceNotNull(lifecycle, "lifecycle").stopped()); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean stopped() { - return stopped; - } - -} diff --git a/unchained-spi/src/main/java/unchained/AbstractNestableContext.java b/unchained-spi/src/main/java/unchained/AbstractNestableContext.java deleted file mode 100644 index 7b21124..0000000 --- a/unchained-spi/src/main/java/unchained/AbstractNestableContext.java +++ /dev/null @@ -1,134 +0,0 @@ -package unchained; - -import unchained.error.NoSuchBeanException; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -/** - * TODO: doc - * - * @param - */ -public abstract class AbstractNestableContext extends AbstractContext { - - private final Context parent; - - protected AbstractNestableContext(L lifecycle) { - this(null, lifecycle); - } - - protected AbstractNestableContext(Context parent, L lifecycle) { - super(lifecycle); - this.parent = parent; - } - - /** - * {@inheritDoc} - */ - @Override - public E get(String name) { - if (parent != null) { - return parent.get(name); - } - throw new NoSuchBeanException(name); - } - - /** - * {@inheritDoc} - */ - @Override - public E get(Class type) { - if (parent != null) { - return parent.get(type); - } - throw new NoSuchBeanException(type); - } - - /** - * {@inheritDoc} - */ - @Override - public E get(String name, Class type) { - if (parent != null) { - return parent.get(name, type); - } - throw new NoSuchBeanException(name, type); - } - - /** - * {@inheritDoc} - */ - @Override - public Collection getAll(Class type) { - if (parent != null) { - return parent.getAll(type); - } - return Collections.emptySet(); - } - - @Override - public boolean has(String name) { - if (parent != null) { - return parent.has(name); - } - return false; - } - - @Override - public boolean has(Class type) { - if (parent != null) { - return parent.has(type); - } - return false; - - } - - @Override - public boolean has(String name, Class type) { - if (parent != null) { - return parent.has(name, type); - } - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public E property(String name) { - final E result = super.property(name); - if (result != null) { - return result; - } - if (parent != null) { - return parent.property(name); - } - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean hasProperty(String name) { - return super.hasProperty(name) || (parent != null && parent.hasProperty(name)); - } - - /** - * {@inheritDoc} - */ - @Override - public Set propertyNames() { - if (parent != null) { - final Set allNames = new HashSet<>(); - allNames.addAll(parent.propertyNames()); - allNames.addAll(super.propertyNames()); - return allNames; - } - return super.propertyNames(); - } - -} diff --git a/unchained-spi/src/main/java/unchained/Boundary.java b/unchained-spi/src/main/java/unchained/Boundary.java deleted file mode 100644 index 3778a1b..0000000 --- a/unchained-spi/src/main/java/unchained/Boundary.java +++ /dev/null @@ -1,4 +0,0 @@ -package unchained; - -public interface Boundary { -} diff --git a/unchained-spi/src/main/java/unchained/CompositeApplicationContext.java b/unchained-spi/src/main/java/unchained/CompositeApplicationContext.java deleted file mode 100644 index 112f454..0000000 --- a/unchained-spi/src/main/java/unchained/CompositeApplicationContext.java +++ /dev/null @@ -1,36 +0,0 @@ -package unchained; - -import java.util.Map; - -import static unchained.Utils.forceNotNull; - -/** - * TODO: doc - */ -public class CompositeApplicationContext extends AbstractCompositeContext< - ApplicationLifecycle, ApplicationContext, CompositeApplicationContext> { - - /** - * TODO: doc - * - * @param lifecycle - */ - public CompositeApplicationContext(ApplicationLifecycle lifecycle) { - super(lifecycle); - } - - /** - * TODO: doc - * - * @param parent - */ - public CompositeApplicationContext(ApplicationContext parent) { - super(forceNotNull(parent, "parent"), parent.lifecycle()); - } - - @Override - protected Map properties() { - return Utils.environment; - } - -} diff --git a/unchained-spi/src/main/java/unchained/SimpleApplicationContext.java b/unchained-spi/src/main/java/unchained/SimpleApplicationContext.java deleted file mode 100644 index 8b6944e..0000000 --- a/unchained-spi/src/main/java/unchained/SimpleApplicationContext.java +++ /dev/null @@ -1,148 +0,0 @@ -package unchained; - -import unchained.utils.MutabilityUtils; -import unchained.error.TooManyBeansException; - -import java.util.*; -import java.util.stream.Collectors; - -import static unchained.Utils.forceNotNull; -import static unchained.assertion.Assert.assertThat; -import static unchained.assertion.Assertions.isNotNull; - -/** - * TODO: doc - */ -public class SimpleApplicationContext extends AbstractNestableContext - implements ApplicationContext { - - private final Map instances; - - /** - * TODO: doc - * - * @param lifecycle - * @param instances - */ - public SimpleApplicationContext(ApplicationLifecycle lifecycle, Map instances) { - this(null, lifecycle, instances); - } - - /** - * TODO: doc - * - * @param parent - * @param instances - */ - public SimpleApplicationContext(ApplicationContext parent, Map instances) { - this(forceNotNull(parent, "parent"), parent.lifecycle(), instances); - } - - /** - * TODO: doc - * - * @param parent - * @param lifecycle - * @param instances - */ - protected SimpleApplicationContext(ApplicationContext parent, ApplicationLifecycle lifecycle, Map instances) { - super(forceNotNull(parent, "parent"), forceNotNull(lifecycle, "lifecycle")); - this.instances = MutabilityUtils.immutableCopy(instances); - } - - private Collection allOf(Class type) { - return instances.values().stream() - .filter(type::isInstance) - .map(type::cast) - .collect(Collectors.toSet()); - } - - @Override - protected Map properties() { - return Utils.environment; - } - - /** - * {@inheritDoc} - */ - @Override - @SuppressWarnings("unchecked") - public E get(String name) { - assertThat(name, "beanName", isNotNull()); - if (has(name)) { - return (E) instances.get(name); - } - return super.get(name); - } - - /** - * {@inheritDoc} - */ - @Override - public E get(Class type) { - assertThat(type, "beanType", isNotNull()); - final Collection beans = allOf(type); - if (beans.isEmpty()) { - return super.get(type); - } - if (beans.size() == 1) { - return beans.iterator().next(); - } - throw new TooManyBeansException(type); - } - - /** - * {@inheritDoc} - */ - @Override - public E get(String name, Class type) { - assertThat(name, "beanName", isNotNull()); - assertThat(type, "beanType", isNotNull()); - final Object bean = get(name); - if (type.isInstance(bean)) { - return type.cast(bean); - } - return super.get(name, type); - } - - /** - * {@inheritDoc} - */ - @Override - public Collection getAll(Class type) { - assertThat(type, "beanType", isNotNull()); - final Set allBeans = new HashSet<>(); - allBeans.addAll(allOf(type)); - allBeans.addAll(super.getAll(type)); - return allBeans; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean has(String name) { - assertThat(name, "beanName", isNotNull()); - return instances.containsKey(name) || super.has(name); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean has(Class type) { - assertThat(type, "beanType", isNotNull()); - return instances.values().stream().anyMatch(type::isInstance) || super.has(type); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean has(String name, Class type) { - assertThat(name, "beanName", isNotNull()); - assertThat(type, "beanType", isNotNull()); - return has(name) && type.isInstance(get(name)) || super.has(name); - } - -} diff --git a/unchained-spi/src/main/java/unchained/SimpleConfiguration.java b/unchained-spi/src/main/java/unchained/SimpleConfiguration.java new file mode 100644 index 0000000..f5181fd --- /dev/null +++ b/unchained-spi/src/main/java/unchained/SimpleConfiguration.java @@ -0,0 +1,35 @@ +package unchained; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class SimpleConfiguration implements Configuration { + + protected final Map container; + + protected SimpleConfiguration(Map container) { + this.container = container; + } + + public SimpleConfiguration() { + this(new HashMap<>()); + } + + @Override + public Set keys() { + return container.keySet(); + } + + @Override + public boolean has(String key) { + return container.containsKey(key); + } + + @Override + @SuppressWarnings("unchecked") + public E get(String key) { + return (E) container.get(key); + } + +} diff --git a/unchained-spi/src/main/java/unchained/SimpleMutableConfiguration.java b/unchained-spi/src/main/java/unchained/SimpleMutableConfiguration.java new file mode 100644 index 0000000..a744d63 --- /dev/null +++ b/unchained-spi/src/main/java/unchained/SimpleMutableConfiguration.java @@ -0,0 +1,24 @@ +package unchained; + +import java.util.HashMap; + +public class SimpleMutableConfiguration extends SimpleConfiguration implements MutableConfiguration { + + @Override + public MutableConfiguration set(String key, E value) { + container.put(key, value); + return this; + } + + @Override + @SuppressWarnings("unchecked") + public E unset(String key) { + return (E) container.remove(key); + } + + @Override + public Configuration snapshot() { + return new SimpleConfiguration(new HashMap<>(this.container)); + } + +} diff --git a/unchained-spi/src/main/java/unchained/Utils.java b/unchained-spi/src/main/java/unchained/Utils.java index ec8a0aa..3e9a62f 100644 --- a/unchained-spi/src/main/java/unchained/Utils.java +++ b/unchained-spi/src/main/java/unchained/Utils.java @@ -1,27 +1,38 @@ package unchained; +import unchained.factory.SimpleFactoryMethod; + import java.util.Collections; -import java.util.HashMap; import java.util.Map; +import java.util.function.Function; import static unchained.assertion.Assert.assertThat; import static unchained.assertion.Assertions.isNotNull; +import static unchained.contract.Factory.register; final class Utils { private Utils() {} + static final Map environment; + static O forceNotNull(O value, String name) { assertThat(value, name, isNotNull()); return value; } - static final Map environment; + private static void addFactoryMethod(Class type, Function builder) { + register(new SimpleFactoryMethod<>(type, builder)); + } + + private static void addAllFactories() { + addFactoryMethod(Configuration.class, (arguments) -> new SimpleConfiguration()); + addFactoryMethod(MutableConfiguration.class, (arguments) -> new SimpleMutableConfiguration()); + } static { - final Map replica = new HashMap<>(System.getenv().size()); - replica.putAll(System.getenv()); - environment = Collections.unmodifiableMap(replica); + environment = Collections.unmodifiableMap(System.getenv()); + addAllFactories(); } } diff --git a/unchained-spi/src/main/java/unchained/error/AmbiguousBeanSelectionException.java b/unchained-spi/src/main/java/unchained/error/AmbiguousBeanSelectionException.java deleted file mode 100644 index 77a90cb..0000000 --- a/unchained-spi/src/main/java/unchained/error/AmbiguousBeanSelectionException.java +++ /dev/null @@ -1,17 +0,0 @@ -package unchained.error; - -public class AmbiguousBeanSelectionException extends ContextException { - - public AmbiguousBeanSelectionException(String beanName) { - super("Too many beans with name <" + beanName + "> were found"); - } - - public AmbiguousBeanSelectionException(Class beanType) { - super("Too many beans with type <" + beanType + "> were found"); - } - - public AmbiguousBeanSelectionException(String beanName, Class beanType) { - super("Too many beans with name <" + beanName + "> and type <" + beanType + "> were found"); - } - -} diff --git a/unchained-spi/src/main/java/unchained/error/NoSuchBeanException.java b/unchained-spi/src/main/java/unchained/error/NoSuchBeanException.java deleted file mode 100644 index 8269eaf..0000000 --- a/unchained-spi/src/main/java/unchained/error/NoSuchBeanException.java +++ /dev/null @@ -1,17 +0,0 @@ -package unchained.error; - -public class NoSuchBeanException extends ContextException { - - public NoSuchBeanException(String beanName) { - super(String.format("No beans with name <%s> could be found", beanName)); - } - - public NoSuchBeanException(Class beanType) { - super(String.format("No beans of type <%s> could be found", beanType)); - } - - public NoSuchBeanException(String beanName, Class beanType) { - super(String.format("No beans with name <%s> and type <%s> could be found", beanName, beanType)); - } - -} diff --git a/unchained-spi/src/main/java/unchained/error/TooManyBeansException.java b/unchained-spi/src/main/java/unchained/error/TooManyBeansException.java deleted file mode 100644 index 60159a6..0000000 --- a/unchained-spi/src/main/java/unchained/error/TooManyBeansException.java +++ /dev/null @@ -1,9 +0,0 @@ -package unchained.error; - -public class TooManyBeansException extends ContextException { - - public TooManyBeansException(Class type) { - super(String.format("Too many beans of type <%s> were found", type)); - } - -} diff --git a/unchained-spi/src/main/java/unchained/factory/SimpleFactoryMethod.java b/unchained-spi/src/main/java/unchained/factory/SimpleFactoryMethod.java new file mode 100644 index 0000000..4457336 --- /dev/null +++ b/unchained-spi/src/main/java/unchained/factory/SimpleFactoryMethod.java @@ -0,0 +1,27 @@ +package unchained.factory; + +import unchained.contract.Factory; + +import java.util.function.Function; + +public class SimpleFactoryMethod implements Factory { + + private final Class type; + private final Function builder; + + public SimpleFactoryMethod(Class type, Function builder) { + this.type = type; + this.builder = builder; + } + + @Override + public Class type() { + return type; + } + + @Override + public T create(U arguments) { + return builder.apply(arguments); + } + +}