[SOLVED] java.lang.RuntimeException: There is no HTTP Context available from here

I got an exception with Play Framework 2.5 while sending a notification e-mail on user action. Here is the code:

public class IpnListener extends Controller {
    @Inject
    private Notif notif;

    @Inject
    private WSClient ws;

   public CompletionStage<Result> validate() {
       return ws.url(/*...*/)
         .thenApply(response -> {
             /* do some validations */
             notif.send(ctx(), notification, user.getEmail());
          });
   }
}
public class Notif {
   @Inject
   private MailerClient mailerClient;

   @Inject
   private HttpExecutionContext executionContext;

  public void send(Http.Context context, Notification notification, String target) {
      /* create an email with template*/
      CompletableFuture.runAsync(() -> {
           try {
               mailerClient.send(email);
           }
           catch (Exception e) {
               e.printStackTrace();
           }
        },
        executionContext.current());
  }
}

Then I get randomly this exception:

java.lang.RuntimeException: There is no HTTP Context available from here.
	at play.mvc.Http$Context.current(Http.java:62)
	at play.mvc.Controller.ctx(Controller.java:27)
	at controllers.listeners.IpnListener.lambda$validate$0(IpnListener.java:69)
	at services.payment.PayPal$Request.lambda$verify$0(PayPal.java:201)
	at java.util.concurrent.CompletableFuture.uniApply(CompletableFuture.java:616)
	at java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:591)
	at java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:457)
	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)
java.lang.RuntimeException: There is no HTTP Context available from here.
	at play.mvc.Http$Context.current(Http.java:62)
	at play.mvc.Controller.ctx(Controller.java:27)
	at controllers.listeners.IpnListener.lambda$validate$0(IpnListener.java:69)
	at services.payment.PayPal$Request.lambda$verify$0(PayPal.java:201)
	at java.util.concurrent.CompletableFuture.uniApply(CompletableFuture.java:616)
	at java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:591)
	at java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:457)
	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)

Do this:

   public CompletionStage<Result> validate() {
       final Http.Context ctx = ctx();
       return ws.url(/*...*/)
         .thenApply(response -> {
             /* do some validations */
             notif.send(ctx, notification, user.getEmail());
          });
   }

And then in Notif you no longer need HttpExecutionContext.

I know this is not exactly what their docs say for 2.5, but in 2.7 they removed the ThreadLocal based Http.Context, so doing it this way will make upgrading to 2.7 way easier.

1 Like

Hi,
Thank you for your reply. I will try to save the context as a final.
I don’t see how to remove the HttpExecutionContext to send my email. The runAsync() method needs it.

Edit:
I use a template to create my email and now I get a similar exception but in the template:

java.lang.RuntimeException: There is no HTTP Context available from here.
	at play.mvc.Http$Context.current(Http.java:62)
	at play.mvc.Http$Context$Implicit.ctx(Http.java:377)
	at play.core.j.PlayMagicForJava$.requestHeader(TemplateMagicForJava.scala:57)
	at views.html.email.creditNotification_Scope0$creditNotification.footer$1(creditNotification.template.scala:51)
	at views.html.email.creditNotification_Scope0$creditNotification.apply(creditNotification.template.scala:63)
	at views.html.email.creditNotification_Scope0$creditNotification.render(creditNotification.template.scala:78)
	at views.html.email.creditNotification.render(creditNotification.template.scala)
	at services.Notif.send(Notif.java:72)
	at controllers.listeners.IpnListener.lambda$validate$0(IpnListener.java:71)
	at services.payment.PayPal$Request.lambda$verify$0(PayPal.java:201)
	at java.util.concurrent.CompletableFuture.uniApply(CompletableFuture.java:616)
	at java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:591)
	at java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:457)
	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)

I use WSClient inside validate().

OK I found that I have to use HttpExecutionContext on both CompletableFuture.runAsync() to send the email and ws.url(/*...*/).thenApplyAsync()to validate the action. That way the context is available everywhere for the template.

    @Inject
    private HttpExecutionContext httpExecutionContext;

    public CompletionStage<Result> validate() {
       final Http.Context ctx = ctx();
       return ws.url(/*...*/)
         .thenApplyAsync(response -> {
             /* do some validations */
             notif.send(ctx, notification, user.getEmail());
          },
          httpExecutionContext.current());
       }

I still have to passe Http.Context to call the lang() method for the first argument of MessagesApi.get().