2

I'm playing around with the Play Framework (v2.2.2), and I'm trying to figure out how to suspend an HTTP request. I'm trying to create a handshake between users, meaning, I want user A to be able to fire off a request and wait until user B "connects". Once the user B has connected, user A's request should return with some information (the info is irrelevant; let's just say some JSON for now).

In another app I've worked on, I use continuations to essentially suspend and replay an HTTP request, so I have something like this...

@Override public JsonResponse doGet(HttpServletRequest request, HttpServletResponse response) { Continuation reqContinuation = ContinuationSupport.getContinuation(request); if (reqContinuation.isInitial()) { ... reqContinuation.addContinuationListener(new ContinuationListener() { public void onTimeout(Continuation c) {...} public void onComplete(Continuation c) {...} }); ... reqContinuation.suspend(); return null; } else { // check results and return JsonResponse with data } } 

... and at some point, user B will connect and the continuation will be resumed/completed in a different servlet. Now, I'm trying to figure out how to do this in Play. I've set up my route...

GET /test controllers.TestApp.test() 

... and I have my Action...

public static Promise<Result> test() { Promise<JsonResponse> promise = Promise.promise(new Function0<JsonResponse>() { public JsonResponse apply() { // what do I do now...? // I need to wait for user B to connect } }); return promise.map(new Function<JsonResponse, Result>() { public Result apply(JsonResponse json) { return ok(json); } }); } 

I'm having a hard time understanding how to construct my Promise. Essentially, I need to tell user A "hey, you're waiting on user B, so here's a promise that user B will eventually connect to you, or else I'll let you know when you don't have to wait anymore".

How do I suspend the request such that I can return a promise of user B connecting? How do I wait for user B to connect?

1 Answer 1

2

You need to create a Promise that can be redeemed later. Strangely, the Play/Java library (F.java) doesn't seem to expose this API, so you have to reach into the Scala Promise class.

Create a small Scala helper class for yourself, PromiseUtility.scala:

import scala.concurrent.Promise object PromiseUtility { def newPromise[T]() = Promise[T]() } 

You can then do something like this in a controller (note, I don't fully understand your use case, so this is just a rough outline of how to use these Promises):

if (needToWaitForUserB()) { // Create an unredeemed Scala Promise scala.concurrent.Promise<Json> unredeemed = PromiseUtility.newPromise(); // Store it somewhere so you can access it later, e.g. a ConcurrentMap keyed by userId storeUnredeemed(userId, unredeemed); // Wrap as an F.Promise and, when redeemed later on, convert to a Result return F.Promise.wrap(unredeemed.future()).map(new Function<Json, Result>() { @Override public Result apply(Json json) { return ok(json); } }); } // [..] // In some other part of the code where user B connects scala.concurrent.Promise<Json> unredeemed = getUnredeemed(userId); unredeemed.success(jsonDataForUserB); 
Sign up to request clarification or add additional context in comments.

5 Comments

I'm getting an error with scala.concurrent.Promise.apply();... method cannot be resolved. also, should F.Promise and new Function be from play.libs.F.java?
Yea, sorry, accessing scala.concurrent.Promise.apply() from Java code is a little trickier: you either use Promise$.MODULE$, which is ugly (see: Trait Companion Object is not visible in Java) or you create a tiny Scala wrapper that exposes the apply method in a cleaner way, as in the updated example above.
awesome! I did something similar last night... def newPromise[T](): Promise[T] = { Promise[T]() }... and it seemed to work as well
this works perfectly. only thing is, IntelliJ things there's an error: Incompatible types. Required: scala.concurrent.Promise<java.lang.String> Found: scala.concurrent.Promise<java.lang.Object> even if I change it to be scala.concurrent.Promise<Object> unredeemed = PromiseUtility.newPromise();
You probably don't have Scala setup correctly in IntelliJ then.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.