didil
didil

Reputation: 715

How to avoid "java.lang.RuntimeException: There is no HTTP Context available from here"?

I get 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());
          });
   }
}

The Notif class.

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)

I'm thinking about saving the ctx() value in a variable like that but I'm not sure about it's lifetime:

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

   @Inject
   private WSClient ws;

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

Upvotes: 0

Views: 471

Answers (2)

didil
didil

Reputation: 715

There is no HTTP context available in the thenApply() method called from WSClient so I had to use the HttpExecutionContext with thenApplyAsync().

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

   @Inject
   private WSClient ws;

   @Inject
   private HttpExecutionContext httpExecutionContext;

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

Upvotes: 0

Igmar Palsenberg
Igmar Palsenberg

Reputation: 647

You need to

@Inject HttpExecutionContext ec;

into your controller. On the thenApply() call, supply ec.current() as the executioner.

.thenApply(response -> {

}, ex.current());

Upvotes: 1

Related Questions