This entails:
* adding akka.pattern.PatternCS.* to enable ask etc. with
CompletionStage
* changing RequestContext to offer an ExecutionContextExecutor for the
CompletionStage.*Async combinators
* splitting up akka.stream.Queue for JavaDSL consistency
143 lines
7.4 KiB
ReStructuredText
143 lines
7.4 KiB
ReStructuredText
.. _handlers-java:
|
||
|
||
Handlers
|
||
========
|
||
|
||
Handlers implement the actual application-defined logic for a certain trace in the routing tree. Most of the leaves of
|
||
the routing tree will be routes created from handlers. Creating a ``Route`` from a handler is achieved using the
|
||
``BasicDirectives.handleWith`` overloads. They come in several forms:
|
||
|
||
* with a single ``Handler`` argument and a variable number of ``RequestVal<?>`` (may be 0)
|
||
* with a number ``n`` of ``RequestVal<T1>`` arguments and a ``HandlerN<T1, .., TN>`` argument
|
||
* with a ``Class<?>`` and/or instance and a method name String argument and a variable number of ``RequestVal<?>`` (may be 0)
|
||
arguments
|
||
|
||
Simple Handler
|
||
--------------
|
||
|
||
In its simplest form a ``Handler`` is a SAM class that defines application behavior
|
||
by inspecting the ``RequestContext`` and returning a ``RouteResult``:
|
||
|
||
.. includecode:: /../../akka-http/src/main/scala/akka/http/javadsl/server/Handler.scala
|
||
:include: handler
|
||
|
||
Such a handler inspects the ``RequestContext`` it receives and uses the ``RequestContext``'s methods to
|
||
create a response:
|
||
|
||
.. includecode:: /../../akka-http-tests/src/test/java/docs/http/javadsl/server/HandlerExampleDocTest.java
|
||
:include: simple-handler
|
||
|
||
The handler can include any kind of logic but must return a ``RouteResult`` in the end which can only
|
||
be created by using one of the ``RequestContext`` methods.
|
||
|
||
A handler instance can be used once or several times as shown in the full example:
|
||
|
||
.. includecode:: /../../akka-http-tests/src/test/java/docs/http/javadsl/server/HandlerExampleDocTest.java
|
||
:include: simple-handler-example-full
|
||
|
||
Handlers and Request Values
|
||
---------------------------
|
||
|
||
In many cases, instead of manually inspecting the request, a handler will make use of :ref:`request-vals-java`
|
||
to extract details from the request. This is possible using one of the other ``handleWith`` overloads that bind
|
||
the values of one or more request values with a ``HandlerN`` instance to produce a ``Route``:
|
||
|
||
.. includecode:: /../../akka-http-tests/src/test/java/docs/http/javadsl/server/HandlerExampleDocTest.java
|
||
:include: handler2
|
||
|
||
The handler here implements multiplication of two integers. However, it doesn't need to specify where these
|
||
parameters come from. In ``handleWith``, as many request values of the matching type have to be specified as the
|
||
handler needs. This can be seen in the full example:
|
||
|
||
.. includecode:: /../../akka-http-tests/src/test/java/docs/http/javadsl/server/HandlerExampleDocTest.java
|
||
:include: handler2-example-full
|
||
|
||
Here, the handler is again being reused. First, in creating a route that expects URI parameters ``x`` and ``y``. This
|
||
route is then used in the route structure. And second, the handler is used with another set of ``RequestVal`` in the
|
||
route structure, this time representing segments from the URI path.
|
||
|
||
Handlers in Java 8
|
||
------------------
|
||
|
||
Handlers are in fact simply classes which extend ``akka.japi.function.FunctionN`` in order to make reasoning
|
||
about the number of handled arguments easier. For example, a :class:`Handler1[String]` is simply a
|
||
``Function2[RequestContext, String, RouteResult]``. You can think of handlers as hot-dogs, where each ``T``
|
||
type represents a sausage, put between the "buns" which are ``RequestContext`` and ``RouteResult``.
|
||
|
||
In Java 8 handlers can be provided as function literals or method references. The previous example can then be written
|
||
like this:
|
||
|
||
.. includecode:: /../../akka-http-tests/src/test/java/docs/http/javadsl/server/HandlerExampleDocTest.java
|
||
:include: handler2-java8-example-full
|
||
|
||
|
||
.. note::
|
||
The reason the ``handleWith##`` methods include the number of handled values is because otherwise (if overloading would
|
||
be used, for all 22 methods) error messages generated by ``javac`` end up being very long and not readable, i.e.
|
||
if one type of a handler does not match the given values, *all* possible candidates would be printed in the error message
|
||
(22 of them), instead of just the one arity-matching method, pointing out that the type does not match.
|
||
|
||
We opted for better error messages as we feel this is more helpful when developing applications,
|
||
instead of having one overloaded method which looks nice when everything works, but procudes hard to read error
|
||
messages if something does not match up.
|
||
|
||
Providing Handlers by Reflection
|
||
--------------------------------
|
||
|
||
Using Java before Java 8, writing out handlers as (anonymous) classes can be unwieldy. Therefore, ``handleReflectively``
|
||
overloads are provided that allow writing handler as simple methods and specifying them by name:
|
||
|
||
.. includecode:: /../../akka-http-tests/src/test/java/docs/http/javadsl/server/HandlerExampleDocTest.java
|
||
:include: reflective
|
||
|
||
The complete calculator example can then be written like this:
|
||
|
||
.. includecode:: /../../akka-http-tests/src/test/java/docs/http/javadsl/server/HandlerExampleDocTest.java
|
||
:include: reflective-example-full
|
||
|
||
There are alternative overloads for ``handleReflectively`` that take a ``Class`` instead of an object instance to refer to
|
||
static methods. The referenced method must be publicly accessible.
|
||
|
||
Deferring Result Creation
|
||
-------------------------
|
||
|
||
Sometimes a handler cannot directly complete the request but needs to do some processing asynchronously. In this case
|
||
the completion of a request needs to be deferred until the result has been generated. This is supported by the routing
|
||
DSL in two ways: either you can use one of the ``handleWithAsyncN`` methods passing an ``AsyncHandlerN`` which
|
||
returns a ``CompletionStage<RouteResult>``, i.e. an eventual ``RouteResult``, or you can also use a regular handler as shown
|
||
above and use ``RequestContext.completeWith`` for completion which takes an ``CompletionStage<RouteResult>`` as an argument.
|
||
|
||
This is demonstrated in the following example. Consider a asynchronous service defined like this
|
||
(making use of Java 8 lambdas):
|
||
|
||
.. includecode:: /../../akka-http-tests/src/test/java/docs/http/javadsl/server/HandlerExampleDocTest.java
|
||
:include: async-service-definition
|
||
|
||
Here the calculator runs the actual calculation in the background and only eventually returns the result. The HTTP
|
||
service should provide a front-end to that service without having to block while waiting for the results. As explained
|
||
above this can be done in two ways.
|
||
|
||
First, you can use ``handleWithAsyncN`` to be able to return a ``CompletionStage<RouteResult>``:
|
||
|
||
.. includecode:: /../../akka-http-tests/src/test/java/docs/http/javadsl/server/HandlerExampleDocTest.java
|
||
:include: async-handler-1
|
||
|
||
The handler invokes the service and then maps the calculation result to a ``RouteResult`` using ``CompletionStage.thenApplyAsync`` and
|
||
returns the resulting ``CompletionStage<RouteResult>``. Note that you should always explicitly provide an executor that designates
|
||
where the future transformation task is executed, using the JDK’s global ForkJoinPool is not recommended.
|
||
|
||
Otherwise, you can also still use ``handleWithN`` and use ``RequestContext.completeWith`` to "convert" a
|
||
``CompletionStage<RouteResult>`` into a ``RouteResult`` as shown here:
|
||
|
||
.. includecode:: /../../akka-http-tests/src/test/java/docs/http/javadsl/server/HandlerExampleDocTest.java
|
||
:include: async-handler-2
|
||
|
||
Using this style, you can decide in your handler if you want to return a direct synchronous result or if you need
|
||
to defer completion.
|
||
|
||
Both alternatives will not block and show the same runtime behavior.
|
||
|
||
Here's the complete example:
|
||
|
||
.. includecode:: /../../akka-http-tests/src/test/java/docs/http/javadsl/server/HandlerExampleDocTest.java
|
||
:include: async-example-full
|