daniel9x
daniel9x

Reputation: 805

Issue with a WS verifier method when migrating from Play 2.4 to Play 2.5

I have a method I need to refactor, as F.Promise has been deprecated in Play 2.5. It's pretty readable actually. It sends a request and authenticates via a custom security token and returns true if the response is 200.

public boolean verify(final String xSassToken){

    WSRequest request = WS.url(mdVerifyXSassTokenURL)
            .setHeader("X-SASS", xSassToken)
            .setMethod("GET");

    final F.Promise<WSResponse> responsePromise = request.execute();

    try {
        final WSResponse response = responsePromise.get(10000);
        int status = response.getStatus();
        if(status == 200 ) { //ok
            return true;
        }
    } catch (Exception e) {
        return false;
    }

    return false;
}

First thing I had to do was change this line:

final F.Promise<WSResponse> responsePromise = request.execute();

To this:

final CompletionStage<WSResponse> responsePromise = request.execute();

However, CompletionStage(T) doesn't have an equivalent get() method so I'm not sure the quickest and easiest way to get a WSResponse that I can verify the status of.

Upvotes: 0

Views: 110

Answers (1)

marcospereira
marcospereira

Reputation: 12214

Yes, it does not. At least not directly.

What you are doing is "wrong" in the context of PlayFramework. get is a blocking call and you should avoid blocking as much as possible. That is why WS offers a non blocking API and a way to handle asynchronous results. So, first, you should probably rewrite your verify code to be async:

public CompletionStage<Boolean> verify(final String xSassToken) {
    return WS.url(mdVerifyXSassTokenURL)
             .setHeader("X-SASS", xSassToken)
             .setMethod("GET")
             .execute()
             .thenApply(response -> response.getStatus() == Http.Status.OK);
}

Notice how I'm using thenApply to return a new a java.util.concurrent.CompletionStage instead of a plain boolean. That means that the code calling verify can also do the same. Per instance, an action at your controller can do something like this:

public class MyController extends Controller {

    public CompletionStage<Result> action() {
        return verify("whatever").thenApply(success -> {
            if (success) return ok("successful request");
            else return badRequest("xSassToken was not valid");
        });
    }

    public CompletionStage<Boolean> verify(final String xSassToken) { ... }
}

This way your application will be able to handle a bigger workload without hanging.


Edit:

Since you have to maintain compatibility, this is what I would do to both evolve the design and also to keep code compatible while migrating:

/**
 * @param xSassToken the token to be validated
 * @return if the token is valid or not
 * 
 * @deprecated Will be removed. Use {@link #verifyToken(String)} instead since it is non blocking.
 */
@Deprecated
public boolean verify(final String xSassToken) {
    try {
        return verifyToken(xSassToken).toCompletableFuture().get(10, TimeUnit.SECONDS);
    } catch (Exception e) {
        return false;
    }
}

public CompletionStage<Boolean> verifyToken(final String xSassToken) {
    return WS.url(mdVerifyXSassTokenURL)
            .setHeader("X-SASS", xSassToken)
            .setMethod("GET")
            .execute()
            .thenApply(response -> response.getStatus() == Http.Status.OK);
}

Basically, deprecate the old verify method and suggest users to migrate to new one.

Upvotes: 1

Related Questions