tutorial starter play instalar framework java playframework playframework-2.0 sbt

starter - Play Framework 2.5 JavaAsync lanzando CompletionException



play framework tutorial (1)

Estoy usando Play 2.5 para crear una aplicación simple. Para un mejor rendimiento, estoy usando la respuesta fragmentada de Akka con la estrategia Java 8 CompletionStage. A continuación se muestra el código por el cual se genera la respuesta fragmentada (funciona bien cuando no se usa CompeededFuture):

@Singleton public class AbstractSource { public Source<ByteString, ?> getChunked(String html) { return Source.<ByteString>actorRef(256, OverflowStrategy.dropNew()) .mapMaterializedValue(sourceActor -> { sourceActor.tell(ByteString.fromString(html), null); sourceActor.tell(new Status.Success(NotUsed.getInstance()), null); return null; }); } }

Y aquí está mi controlador:

@Singleton @AddCSRFToken public class Application extends Controller { @Inject private AbstractSource abstractSource; public CompletionStage<Result> index() { CompletionStage<Source<ByteString, ?>> source = CompletableFuture.supplyAsync(() -> abstractSource.getChunked(index.render(CSRF.getToken(request()).map(t -> t.value()).orElse("no token")).body() ) ); return source.thenApply( chunks -> ok().chunked(chunks)); } }

Ahora cuando estoy ejecutando la aplicación está lanzando siguiente excepción:

play.api.http.HttpErrorHandlerExceptions$$anon$1: Execution exception[[CompletionException: java.lang.RuntimeException: There is no HTTP Context available from here.]] at play.api.http.HttpErrorHandlerExceptions$.throwableToUsefulException(HttpErrorHandler.scala:269) at play.api.http.DefaultHttpErrorHandler.onServerError(HttpErrorHandler.scala:195) at play.api.GlobalSettings$class.onError(GlobalSettings.scala:160) at play.api.DefaultGlobal$.onError(GlobalSettings.scala:188) at play.api.http.GlobalSettingsHttpErrorHandler.onServerError(HttpErrorHandler.scala:98) at play.core.server.netty.PlayRequestHandler$$anonfun$2$$anonfun$apply$1.applyOrElse(PlayRequestHandler.scala:99) at play.core.server.netty.PlayRequestHandler$$anonfun$2$$anonfun$apply$1.applyOrElse(PlayRequestHandler.scala:98) at scala.concurrent.Future$$anonfun$recoverWith$1.apply(Future.scala:344) at scala.concurrent.Future$$anonfun$recoverWith$1.apply(Future.scala:343) at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32) Caused by: java.util.concurrent.CompletionException: java.lang.RuntimeException: There is no HTTP Context available from here. at java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:273) at java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:280) at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1592) at java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1582) at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289) at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056) at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692) at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157) Caused by: java.lang.RuntimeException: There is no HTTP Context available from here. at play.mvc.Http$Context.current(Http.java:57) at play.mvc.Controller.request(Controller.java:36) at com.mabsisa.ui.web.controllers.Application.lambda$index$1(Application.java:31) at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590) ... 5 common frames omitted

No estoy usando el contexto HTTP en ninguna parte, entonces por qué esto no funciona no lo estoy obteniendo. El mismo código funciona cuando se devuelve un resultado normal con una respuesta fragmentada. Por favor ayuda con esto


Debe proporcionar el contexto de ejecución HTTP cuando se trata de CompletableFuture / CompletionStage . En Scala, la información de contexto se pasa a través de implícitos, estos no están disponibles en Java; esta es la razón por la cual Play usa ThreadLocal .

Sin embargo, puede perder esta información cuando se cambian los hilos y es por eso que tiene el problema. Puede pensar que no tiene acceso al contexto HTTP, pero en realidad lo hace; está utilizando request() .

Entonces debe cambiar su código para usar supplyAsync con un Executor:

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#supplyAsync-java.util.function.Supplier-java.util.concurrent.Executor-

De esto:

CompletableFuture.supplyAsync(() -> abstractSource.getChunked(index.render(CSRF.getToken(request()).map(t -> t.value()).orElse("no token")).body() ) );

a esto:

CompletableFuture.supplyAsync(() -> abstractSource.getChunked(index.render(CSRF.getToken(request()).map(t -> t.value()).orElse("no token")).body() ) , ec.current());

donde ec es su contexto: @Inject HttpExecutionContext ec;