Promises chain in Play Framework using Java

I have a controller action where I need to call third-party web services.

My problem is that I do not call one web service. I need to combine 4 to 5 web services. Each web service I call returns a JSON object that I need to process, and based on some logic, I decide to either call another web service (from 4 web services) or return a response to the caller. Here is what I am trying to do:

public static Promise<Result> accounts(){ return WS.url("url1").get().map(response1 -> { JsonNode mynode = response1.asJson(); if (mynode.get("status").asInt()==200){ Promise<JsonNode> jsonPromise = WS.url("url2").get().map(response2->{ return response2.asJson(); }); } return ok(mynode); }); } 

Now, from the documentation, I think I need a chain of promises where every web service call is a promise. But I'm not sure how to do this?

thanks

+5
source share
2 answers

They call it Reactive Composition , and it does it like this:>

 public static Promise<Result> reactiveCombo() { Promise<WSResponse> promise1 = WS.url("url1").get(); Promise<WSResponse> promise2 = WS.url("url2").get(); Promise<WSResponse> promise3 = WS.url("url3").get(); return promise1.flatMap(response1 -> { final JsonNode json1 = response1.asJson(); if (!json1.has("someField")) { return Promise.promise(() -> badRequest()); } return promise2.flatMap(response2 -> { final JsonNode json2 = response2.asJson(); if (json1.get("someField").asText().equals(json2.get("someField").asText())) { return Promise.promise(() -> badRequest()); } return promise3.map(response3 -> { final JsonNode json3 = response3.asJson(); if (json3.get("boolField").asBoolean()) { return badRequest(); } return ok(); }); }); }); } 

For large # calls, you can use Promise.sequence() and get "creative":

 private static Promise<JsonNode> getPromise(String url, Predicate<JsonNode> predicate) { return WS.url(url).get().map(response -> { JsonNode json = response.asJson(); if (predicate.negate().test(json)) { throw new Exception("BUMMER!"); } return json; }); } public static Promise<Result> reactiveCombo(List<String> urls) { List<Promise<JsonNode>> promises = new ArrayList<Promise<JsonNode>>(urls.size()); Predicate<String> predURL = p -> p.contains("goodApi"); Predicate<JsonNode> pred1 = p -> p.has("boolField") && p.get("boolField").asBoolean(); Predicate<JsonNode> pred2 = p -> p.has("someField"); urls.forEach(url -> { Promise<JsonNode> promise = predURL.test(url) ? getPromise(url, pred1) : getPromise(url, pred2); promises.add(promise); }); return Promise.sequence(promises).map(results -> ok()).recover(t -> badRequest()); } 

Additional information + documentation:

Go Reactive with Java 8 and Play Framework (old, but still educational)

Javaws

+6
source

You can use recursion to smooth this structure for any number of Promises. First create a class:

 public static class ChainedWebService { public final Optional<ChainedWebService> next; public final WSRequestHolder wsResponsePromise; private final F.Predicate<JsonNode> predicate; public ChainedWebService(String url, Optional<ChainedWebService> next, F.Predicate<JsonNode> predicate) { this.next = next; this.wsResponsePromise = WS.url(url); this.predicate = predicate; } public F.Promise<Result> processChain() { return wsResponsePromise.get().flatMap(new F.Function<WSResponse, F.Promise<Result>>() { @Override public F.Promise<Result> apply(WSResponse wsResponse) throws Throwable { if (!predicate.test(wsResponse.asJson())) { return F.Promise.pure(badRequest()); } if (!next.isPresent()) { return F.Promise.pure(ok()); } return next.get().processChain(); } }); } } 

And then use it:

 public static F.Promise<Result> reactiveCombo() { ChainedWebService chainedWebService3 = new ChainedWebService( "url3", Optional.<ChainedWebService>empty(), jsonNode -> jsonNode.get("boolField").asBoolean() ); ChainedWebService chainedWebService2 = new ChainedWebService( "url2", Optional.of(chainedWebService3), jsonNode -> jsonNode.get("someField").asText().equals(jsonNode.get("someField").asText())); ChainedWebService chainedWebService1 = new ChainedWebService( "url1", Optional.of(chainedWebService2), jsonNode -> jsonNode.has("someField") ); return chainedWebService1.processChain(); } 

You can implement some basic constructor to make the construction process more reasonable.

Hope this helps!

+4
source

All Articles