Skip to content

Commit

Permalink
!htp akka#17965 more consistent and 22-ified javadsl handleWith
Browse files Browse the repository at this point in the history
  • Loading branch information
ktoso committed Jul 14, 2015
1 parent d8523e9 commit 91af328
Show file tree
Hide file tree
Showing 25 changed files with 364 additions and 221 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
import akka.http.javadsl.testkit.TestRoute;
import org.junit.Test;

public class HandlerExampleSpec extends JUnitRouteTest {
public class HandlerExampleDocTest extends JUnitRouteTest {
@Test
public void testSimpleHandler() {
//#simple-handler-example-full
class TestHandler extends akka.http.javadsl.server.AllDirectives {
//#simple-handler
Handler handler = new Handler() {
@Override
public RouteResult handle(RequestContext ctx) {
public RouteResult apply(RequestContext ctx) {
return ctx.complete("This was a " + ctx.request().method().value() +
" request to "+ctx.request().getUri());
}
Expand Down Expand Up @@ -60,23 +60,23 @@ Route createRoute() {
public void testCalculator() {
//#handler2-example-full
class TestHandler extends akka.http.javadsl.server.AllDirectives {
RequestVal<Integer> xParam = Parameters.intValue("x");
RequestVal<Integer> yParam = Parameters.intValue("y");
final RequestVal<Integer> xParam = Parameters.intValue("x");
final RequestVal<Integer> yParam = Parameters.intValue("y");

RequestVal<Integer> xSegment = PathMatchers.integerNumber();
RequestVal<Integer> ySegment = PathMatchers.integerNumber();
final RequestVal<Integer> xSegment = PathMatchers.integerNumber();
final RequestVal<Integer> ySegment = PathMatchers.integerNumber();

//#handler2
Handler2<Integer, Integer> multiply =
final Handler2<Integer, Integer> multiply =
new Handler2<Integer, Integer>() {
@Override
public RouteResult handle(RequestContext ctx, Integer x, Integer y) {
public RouteResult apply(RequestContext ctx, Integer x, Integer y) {
int result = x * y;
return ctx.complete("x * y = " + result);
}
};

Route multiplyXAndYParam = handleWith(xParam, yParam, multiply);
final Route multiplyXAndYParam = handleWith2(xParam, yParam, multiply);
//#handler2

Route createRoute() {
Expand All @@ -87,7 +87,7 @@ Route createRoute() {
multiplyXAndYParam
),
path("path-multiply", xSegment, ySegment).route(
handleWith(xSegment, ySegment, multiply)
handleWith2(xSegment, ySegment, multiply)
)
)
)
Expand Down Expand Up @@ -124,7 +124,7 @@ public RouteResult multiply(RequestContext ctx, Integer x, Integer y) {
return ctx.complete("x * y = " + result);
}

Route multiplyXAndYParam = handleWith(this, "multiply", xParam, yParam);
Route multiplyXAndYParam = handleWith2(xParam, yParam, this::multiply);
//#reflective

Route createRoute() {
Expand All @@ -135,7 +135,7 @@ Route createRoute() {
multiplyXAndYParam
),
path("path-multiply", xSegment, ySegment).route(
handleWith(this, "multiply", xSegment, ySegment)
handleWith2(xSegment, ySegment, this::multiply)
)
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ public static void main(String[] args) throws IOException {
public Route createRoute() {
// This handler generates responses to `/hello?name=XXX` requests
Route helloRoute =
handleWith(name,
handleWith1(name,
// in Java 8 the following becomes simply
// (ctx, name) -> ctx.complete("Hello " + name + "!")
new Handler1<String>() {
@Override
public RouteResult handle(RequestContext ctx, String name) {
public RouteResult apply(RequestContext ctx, String name) {
return ctx.complete("Hello " + name + "!");
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import java.io.InputStreamReader;
import java.util.concurrent.TimeUnit;

public class HttpServerExampleSpec {
public class HttpServerExampleDocTest {
public static void bindingExample() {
//#binding-example
ActorSystem system = ActorSystem.create();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/

package docs.http.javadsl;

import akka.actor.ActorSystem;
import akka.dispatch.Futures;
import akka.http.javadsl.server.*;
import akka.http.javadsl.server.values.Parameter;
import akka.http.javadsl.server.values.Parameters;
import akka.http.javadsl.server.values.PathMatcher;
import akka.http.javadsl.server.values.PathMatchers;
import scala.concurrent.Future;

import java.io.IOException;
import java.util.concurrent.Callable;

public class LambdaRoutingExample extends HttpApp {
static Parameter<Integer> x = Parameters.intValue("x");
static Parameter<Integer> y = Parameters.intValue("y");

static PathMatcher<Integer> xSegment = PathMatchers.integerNumber();
static PathMatcher<Integer> ySegment = PathMatchers.integerNumber();

public static RouteResult multiply(RequestContext ctx, int x, int y) {
int result = x * y;
return ctx.complete(String.format("%d * %d = %d", x, y, result));
}

public static Future<RouteResult> multiplyAsync(final RequestContext ctx, final int x, final int y) {
return Futures.future(() -> multiply(ctx, x, y), ctx.executionContext());
}

@Override
public Route createRoute() {
return
route(
// matches the empty path
pathSingleSlash().route(
getFromResource("web/calculator.html")
),
// matches paths like this: /add?x=42&y=23
path("add").route(
handleWith2(x, y, LambdaRoutingExample::multiply)
),
path("subtract").route(
handleWith2(x, y, this::subtract)
),
// matches paths like this: /multiply/{x}/{y}
path("multiply", xSegment, ySegment).route(
// bind handler by reflection
handleWith2(xSegment, ySegment, LambdaRoutingExample::multiply)
),
path("multiplyAsync", xSegment, ySegment).route(
// bind async handler
handleWithAsync2(xSegment, ySegment, LambdaRoutingExample::multiplyAsync)
)
);
}

public RouteResult add(RequestContext ctx, Integer xVal, Integer yVal) {
int result = xVal + yVal;
return ctx.complete(String.format("%d + %d = %d", xVal, yVal, result));
}

public RouteResult subtract(RequestContext ctx, Integer xVal, Integer yVal) {
int result = xVal - yVal;
return ctx.complete(String.format("%d - %d = %d", xVal, yVal, result));
}

}
26 changes: 21 additions & 5 deletions akka-docs-dev/rst/java/http/routing-dsl/handlers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ 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
.. includecode:: /../../akka-http/src/main/scala/akka/http/javadsl/server/Handlers.scala
:include: handler

Such a handler inspects the ``RequestContext`` it receives and uses the ``RequestContext``'s methods to
Expand Down Expand Up @@ -49,7 +49,7 @@ The handler here implements multiplication of two integers. However, it doesn't
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:: ../../code/docs/http/javadsl/HandlerExampleSpec.java
.. includecode:: ../../code/docs/http/javadsl/HandlerExampleTest.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
Expand All @@ -59,16 +59,32 @@ 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. The example from before then looks like this:

.. includecode:: /../../akka-http-tests-java8/src/test/java/docs/http/javadsl/server/HandlerExampleSpec.java
.. includecode:: /../../akka-http-tests-java8/src/test/java/docs/http/javadsl/server/HandlerExampleSpecHandlerExampleSpec.java
:include: handler2-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 doesnt match up.


Providing Handlers by Reflection
--------------------------------

Using Java before Java 8, writing out handlers as (anonymous) classes can be unwieldy. Therefore, ``handleWith``
Using Java before Java 8, writing out handlers as (anonymous) classes can be unwieldy. Therefore, ``handleWithReflect``
overloads are provided that allow writing handler as simple methods and specifying them by name:

.. includecode:: ../../code/docs/http/javadsl/HandlerExampleSpec.java
Expand All @@ -79,7 +95,7 @@ The complete calculator example can then be written like this:
.. includecode:: ../../code/docs/http/javadsl/HandlerExampleSpec.java
:include: reflective-example-full

There are alternative overloads for ``handleWith`` that take a ``Class`` instead of an object instance to refer to
There are alternative overloads for ``handleWithReflect`` that take a ``Class`` instead of an object instance to refer to
static methods. The referenced method must be publicly accessible.

Deferring Result Creation
Expand Down
37 changes: 20 additions & 17 deletions akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,27 @@ package akka.http.scaladsl
import java.net.InetSocketAddress
import java.util.concurrent.ConcurrentHashMap
import java.util.{ Collection JCollection }
import javax.net.ssl.{ SSLParameters, SSLContext }
import com.typesafe.config.Config
import scala.util.Try
import scala.util.control.NonFatal
import scala.collection.{ JavaConverters, immutable }
import scala.concurrent.{ ExecutionContext, Promise, Future }
import akka.japi
import javax.net.ssl.{ SSLContext, SSLParameters }

import akka.actor._
import akka.event.LoggingAdapter
import akka.stream.Materializer
import akka.stream.io._
import akka.stream.scaladsl._
import akka.http.impl.util.StreamUtils
import akka.http._
import akka.http.impl.engine.client._
import akka.http.impl.engine.server._
import akka.http.scaladsl.util.FastFuture
import akka.http.scaladsl.model.headers.Host
import akka.http.impl.util.StreamUtils
import akka.http.scaladsl.model._
import akka.http._
import akka.actor._
import akka.http.scaladsl.model.headers.Host
import akka.http.scaladsl.util.FastFuture
import akka.japi
import akka.stream.Materializer
import akka.stream.io._
import akka.stream.scaladsl._
import com.typesafe.config.Config

import scala.collection.immutable
import scala.concurrent.{ ExecutionContext, Future, Promise }
import scala.util.Try
import scala.util.control.NonFatal

class HttpExt(config: Config)(implicit system: ActorSystem) extends akka.actor.Extension {

Expand Down Expand Up @@ -389,8 +391,9 @@ class HttpExt(config: Config)(implicit system: ActorSystem) extends akka.actor.E
* method call the respective connection pools will be restarted and not contribute to the returned future.
*/
def shutdownAllConnectionPools(): Future[Unit] = {
import scala.collection.JavaConverters._
import system.dispatcher

import scala.collection.JavaConverters._
val gateways = hostPoolCache.values().asScala
system.log.info("Initiating orderly shutdown of all active host connections pools...")
Future.sequence(gateways.map(_.flatMap(_.shutdown()))).map(_ ())
Expand Down Expand Up @@ -567,7 +570,7 @@ object Http extends ExtensionId[HttpExt] with ExtensionIdProvider {
new HttpExt(system.settings.config getConfig "akka.http")(system)
}

import JavaConverters._
import scala.collection.JavaConverters._

//# https-context-impl
case class HttpsContext(sslContext: SSLContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@

package akka.http.javadsl.testkit

import akka.actor.ActorSystem
import akka.http.javadsl.server._
import Directives._
import akka.http.scaladsl.model.HttpResponse
import akka.stream.ActorMaterializer
import org.junit.rules.ExternalResource
import org.junit.{ Rule, Assert }
import org.junit.{ Assert, Rule }

import scala.concurrent.duration._
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.http.scaladsl.model.HttpResponse

/**
* A RouteTest that uses JUnit assertions.
Expand Down Expand Up @@ -39,8 +39,8 @@ abstract class JUnitRouteTestBase extends RouteTest {
}

protected def completeWithValueToString[T](value: RequestVal[T]): Route =
handleWith(value, new Handler1[T] {
def handle(ctx: RequestContext, t: T): RouteResult = ctx.complete(t.toString)
handleWith1(value, new Handler1[T] {
def apply(ctx: RequestContext, t: T): RouteResult = ctx.complete(t.toString)
})
}
abstract class JUnitRouteTest extends JUnitRouteTestBase {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,22 @@ RouteResult constantPlusMultiply(RequestContext ctx, int x, int y) {
}

public void test() {
handleWith(xSegment, ySegment, SimpleServerApp8::multiply);
handleWith2(xSegment, ySegment, SimpleServerApp8::multiply);
}

@Override
public Route createRoute() {
Handler addHandler = new Handler() {
@Override
public RouteResult handle(RequestContext ctx) {
public RouteResult apply(RequestContext ctx) {
int xVal = x.get(ctx);
int yVal = y.get(ctx);
int result = xVal + yVal;
return ctx.complete(String.format("%d + %d = %d", xVal, yVal, result));
}
};
Handler2<Integer, Integer> subtractHandler = new Handler2<Integer, Integer>() {
public RouteResult handle(RequestContext ctx, Integer xVal, Integer yVal) {
public RouteResult apply(RequestContext ctx, Integer xVal, Integer yVal) {
int result = xVal - yVal;
return ctx.complete(String.format("%d - %d = %d", xVal, yVal, result));
}
Expand All @@ -63,25 +63,25 @@ public RouteResult handle(RequestContext ctx, Integer xVal, Integer yVal) {
),
// matches paths like this: /add?x=42&y=23
path("add").route(
handleWith(addHandler, x, y)
handleWith(addHandler)
),
path("subtract").route(
handleWith(x, y, subtractHandler)
handleWith2(x, y, subtractHandler)
),
path("divide").route(
handleWith(x, y,
(ctx, x, y) ->
ctx.complete(String.format("%d / %d = %d", x, y, x / y))
handleWith2(x, y,
(ctx, x, y) ->
ctx.complete(String.format("%d / %d = %d", x, y, x / y))
)
),
// matches paths like this: /multiply/{x}/{y}
path("multiply", xSegment, ySegment).route(
// bind handler by reflection
handleWith(xSegment, ySegment, SimpleServerApp8::multiply)
handleWith2(xSegment, ySegment, SimpleServerApp8::multiply)
),
path("multiply-methodref", xSegment, ySegment).route(
// bind handler by reflection
handleWith(xSegment, ySegment, new Test(123)::constantPlusMultiply)
// bind handler by referende to new instance of handler
handleWith2(xSegment, ySegment, new Test(123)::constantPlusMultiply)
)
);
}
Expand Down
Loading

0 comments on commit 91af328

Please sign in to comment.