I was reading some http webservice api from playframework, link below.
I am not able to understand how this nested work with the flatmap.
Can someone give me some hints how to crack down this big chunk of functions call.
from http://www.playframework.com/documentation/2.2.x/JavaWS
Composing results
If you want to make multiple calls in sequence,
this can be achieved using flatMap:
public static Promise<Result> index() {
final Promise<Result> resultPromise = WS.url(feedUrl).get().flatMap(
new Function<WS.Response, Promise<Result>>() {
public Promise<Result> apply(WS.Response response) {
return WS.url(response.asJson().findPath("commentsUrl").asText()).get().map(
new Function<WS.Response, Result>() {
public Result apply(WS.Response response) {
return ok("Number of comments: " + response.asJson().findPath("count").asInt());
}
}
);
}
}
);
return resultPromise;
}
flatMap and map are common Scala (or more generally, functional programming) functions. They both accept a function as a parameter. In order to translate the Play WS API into Java (and pretty much everything else), Scala's function type needed to be re-implemented in Java, so that you can take full advantage of the WS library. It's being done here in a similar way the Scala compiler does it. Function<A,B> is an abstract type that requires an apply method. The parameter(s) of apply are the parameters of the function, and the return type of apply is that of the function.
If you have this function in Java:
public String int2String(Integer integer) {
return integer.toString();
}
It would be equivalent to this:
new Function<Integer, String>() {
public String apply(Integer integer) {
return integer.toString();
}
}
Let's start simpler with the case of just one WS call. WS.url(...).get() returns a Promise<WS.Response>. Promise is a container class for a promised value. In order to handle the value it contains (or will eventually contain), we need to use the map function. For a Promise<WS.Response>, map will accept a Function<WS.Response, T> as a parameter, where T is the type you want to map the response to.
As an example, let's define a Function that will just return the body of the WS.Response in a Play HTTP Result:
Function<WS.Response, Result> echo = new Function<WS.Response, Result>() {
public Result apply(WS.Response response) {
return ok(response.asText());
}
}
Now let's use this Function in a WS call in a controller:
public static Promise<Result> index() {
final Promise<Result> resultPromise = WS.url("http://google.com").get().map(echo);
return resultPromise;
}
Once the Promise has been fulfilled, the echo function defined earlier will be executed inside map, returning the Promise<Result>. The two previous blocks of code could also be written like this (combined into one with an anonymous function):
public static Promise<Result> index() {
final Promise<Result> resultPromise = WS.url("http://google.com").get().map(
new Function<WS.Response, Result>() {
public Result apply(WS.Response response) {
return ok(response.asText());
}
}
);
return resultPromise;
}
As a crude example, let's say we need to make two WS calls. The second WS call will depend on the first. Perhaps the first call will give us some URL we will use to make the second WS call.
This is where flatMap comes into picture. We will need two functions to accomplish this task. The first function is what is passed to flatMap, which will be executed when the first WS.Response is received. This first function will use the first response to make the second WS call, which returns another Promise<WS.Response> that must be mapped to get our final result. So we map the second result with a second function that translates the WS.Response into our Result.
So what happened? If we used map instead of flatMap in both instances, the chain of events would go something like this:
The first get() returned a Promise<WS.Response>, then we map the contained WS.Response to a Promise<Result>. That, however, would leave us with a Promise<Promise<WS.Response>>, which is not very desirable. Using flatMap in the outer function instead will flatten the Promises into a single Promise<Result>. Simiarly, if you were doing 3 or more nested calls, you would map each result to an inner function, and have just one flatMap at the outer level to flatten everything at the end.
This all of course looks much more beautiful in Scala.
Related
A regular Spring Cloud Stream function looks like this (taken from the docs):
#Bean
public Function<String, String> toUpperCase() {
return s -> s.toUpperCase();
}
Considering not using a reactive approach, I wonder whether it's possible to make different transformations based on custom logic and/or send the result to a different "out" binding? Something like this:
#Bean
public Function<String, String> transform() {
return s -> {
if (s.equals("A")) {
return s.toUpperCase(); //this wants to be sent to toUpperCase-out-0
} else if (s.equals("B")) {
return s.toLowerCase(); //this wants to be sent to toLowerCase-out-0
} else {
return "unsupported"; //this wants to be sent to unsupported-out-0
}
};
}
Also, here we have the same return type (String) but maybe it could be required to return objects of different classes from each branch (by using Object/abstract class/etc. as a return type of the whole function).
I can imagine a solution with a Consumer instead of Function in which we make different StreamBridge calls, but maybe it's possible to do the same with a Function?
Don't use a Function, use a Consumer instead and send the outputs using the StreamBridge.
https://docs.spring.io/spring-cloud-stream/docs/current/reference/html/spring-cloud-stream.html#_sending_arbitrary_data_to_an_output_e_g_foreign_event_driven_sources
I am using Java 11 and project Reactor (from Spring). I need to make a http call to a rest api (I can only make it once in the whole flow).
With the response I need to compute two things:
Check if a document exists in the database (mongodb). If it does not exists then create it and return it. Otherwise just return it.
Compute some logic on the response and we are done.
In pseudo code it is something like this:
public void computeData(String id) {
httpClient.getData(id) // Returns a Mono<Data>
.flatMap(data -> getDocument(data.getDocumenId()))
// Issue here is we need access to the data object consumed in the previous flatMap but at the same time we also need the document object we get from the previous flatMap
.flatMap(document -> calculateValue(document, data))
.subscribe();
}
public Mono<Document> getDocument(String id) {
// Check if document exists
// If not create document
return document;
}
public Mono<Value> calculateValue(Document doc, Data data) {
// Do something...
return value;
}
The issue is that calculateValue needs the return value from http.getData but this was already consumed on the first flatMap but we also need the document object we get from the previous flatMap.
I tried to solve this issue using Mono.zip like below:
public void computeData(String id) {
final Mono<Data> dataMono = httpClient.getData(id);
Mono.zip(
new Mono<Mono<Document>>() {
#Override
public void subscribe(CoreSubscriber<? super Mono<Document>> actual) {
final Mono<Document> documentMono = dataMono.flatMap(data -> getDocument(data.getDocumentId()))
actual.onNext(documentMono);
}
},
new Mono<Mono<Value>>() {
#Override
public void subscribe(CoreSubscriber<? super Mono<Value>> actual) {
actual.onNext(dataMono);
}
}
)
.flatMap(objects -> {
final Mono<Document> documentMono = objects.getT1();
final Mono<Data> dataMono = objects.getT2();
return Mono.zip(documentMono, dataMono, (document, data) -> calculateValue(document, data))
})
}
But this is executing the httpClient.getData(id) twice which goes against my constrain of only calling it once. I understand why it is being executed twice (I subscribe to it twice).
Maybe my solution design can be improved somewhere but I do not see where. To me this sounds like a "normal" issue when designing reactive code but I could not find a suitable solution to it so far.
My question is, how can accomplish this flow in a reactive and non blocking way and only making one call to the rest api?
PS; I could add all the logic inside one single map but that would force me to subscribe to one of the Mono inside the map which is not recommended and I want to avoid following this approach.
EDIT regarding #caco3 comment
I need to subscribe inside the map because both getDocument and calculateValue methods return a Mono.
So, if I wanted to put all the logic inside one single map it would be something like:
public void computeData(String id) {
httpClient.getData(id)
.map(data -> getDocument(data).subscribe(s -> calculateValue(s, data)))
.subscribe();
}
You do not have to subscribe inside map, just continue building the reactive chain inside the flatMap:
getData(id) // Mono<Data>
.flatMap(data -> getDocument(data.getDocumentId()) // Mono<Document>
.switchIfEmpty(createDocument(data.getDocumentId())) // Mono<Document>
.flatMap(document -> calculateValue(document, data)) // Mono<Value>
)
.subscribe()
Boiling it down, your problem is analogous to:
Mono.just(1)
.flatMap(original -> process(original))
.flatMap(processed -> I need access to the original value and the processed value!
System.out.println(original); //Won't work
);
private static Mono<String> process(int in) {
return Mono.just(in + " is an integer").delayElement(Duration.ofSeconds(2));
}
(Silly example, I know.)
The problem is that map() (and by extension, flatMap()) are transformations - you get access to the new value, and the old one goes away. So in your second flatMap() call, you've got access to 1 is an integer, but not the original value (1.)
The solution here is to, instead of mapping to the new value, map to some kind of merged result that contains both the original and new values. Reactor provides a built in type for that - a Tuple. So editing our original example, we'd have:
Mono.just(1)
.flatMap(original -> operation(original))
.flatMap(processed -> //Help - I need access to the original value and the processed value!
System.out.println(processed.getT1()); //Original
System.out.println(processed.getT2()); //Processed
///etc.
);
private static Mono<Tuple2<Integer, String>> operation(int in) {
return Mono.just(in + " is an integer").delayElement(Duration.ofSeconds(2))
.map(newValue -> Tuples.of(in, newValue));
}
You can use the same strategy to "hold on" to both document and data - no need for inner subscribes or anything of the sort :-)
I am trying to implement the BodyExtractor interface get the body from Mono< ClientResponse> as an Object instead of it in Mono.
I could not find any example of BodyExtractor implementation. I am wondering is this a good idea to implement it or is there any other way to get the body as an object.
Below is the code line that I currently have
public Mono<ResponseEntity<Mono<JsonNode>>> processUnmappedApiRequest(ServerHttpRequest request, JsonNode body) {
RequestData reqData = this.prepareReqMetadata(request, body);
Mono<ClientResponse> response = commonConnector.getApiResponse(reqData);
return response.map(respData -> {
int latestVersion = respData.headers().header("version").size() == 0 ? getLatestVersion(request) :
Integer.parseInt(respData.headers().header("version").get(0));
List converterList;
if((converterList = converterSequenceProvider.getConverterList(reqData.getRequestPath(), latestVersion, reqData.getVersion())) != null){
return ResponseEntity.ok().body(respData.bodyToMono(JsonNode.class).map(respBody -> convertToDesiredVersion(converterList, respBody)));
}
return ResponseEntity.ok().body(respData.bodyToMono(JsonNode.class));
});
}
In this method my return type is Mono< ResponseEntity< Mono< JsonNode>>> and I am trying to convert it to Mono< ResponseEntity< JsonNode>> because my team is not agreeing with Mono inside a Mono.
So the main point here is I don't want to use bodyToMono method and I am not sure how to use body method.
Please help me out here.
If you are trying to return just a Mono object, you can use the flatMap method instead of map, so you can avoid something like Mono<Mono<X>> and get just Mono<X>.
map
Transform the item emitted by this Mono by applying a synchronous
function to it.
flatMap
Transform the item emitted by this Mono asynchronously, returning the
value emitted by another Mono (possibly changing the value type).
Also, there is a method on ServerResponse.BodyBuilder syncBody which can take normal body and return it in Mono. Map function's argument is already a non wrapped object, so you can do something like:
JsonNode jsonNode=transform(clientResponse);
return ResponseEntity.ok().syncbody(jsonNode);
I am trying to re-write a scala example of a POC project from Manning's "Akka in Action" in Java. The project is a small Http server for creating events and buying tickets.
I am at a point when an actor can send an Optional<Event> to my RestApi. Depending on whether the value is present I should complete the call with OK, else NOT_FOUND.
In Scala the snippet looks as follow:
get {
// GET /events/:event
onSuccess(getEvent(event)) {
_.fold(complete(NotFound))(e => complete(OK, e))
}
}
...where getEvent returns an Option[Event] (equivalent of java's Optional<Event>). This is how I rewrote it in Java:
get(() -> onSuccess(() -> getEvent(event), eventGetRoute()))
...
//and eventGetRoute() is a function:
private Function<Optional<Event>, Route> eventGetRoute() {
return maybeEvent -> maybeEvent.map(event -> complete(OK, event, Jackson.marshaller())).orElseGet(() -> complete(NOT_FOUND));
}
This doesn't compile: Bad return type in lambda expression: Route cannot be converted to RouteAdapter. The longer (and first) complete returns a RouteAdapter and the second one returns a Route. If I re-write the above function like this:
private Function<Optional<Event>, Route> eventGetRoute() {
return maybeEvent -> {
if(maybeEvent.isPresent()) {
return complete(OK, maybeEvent.get(), Jackson.marshaller());
}
return complete(NOT_FOUND);
};
}
...then the compiler doesn't complain, but then it is not right way to map an Optional.
Java doesn't have fold method for Optional (not in SE8 at least), which allows passing the fallback-to value first.
I'm curious whether it is possible to write this function in respecting functional style.
Update:
As asked in the comments, these are the signatures of the complete methods from akka-http javadsl library:
def complete(status: StatusCode): Route = RouteAdapter(
D.complete(status.asScala))
and
def complete[T](status: StatusCode, value: T, marshaller: Marshaller[T, RequestEntity]) = RouteAdapter {
D.complete(ToResponseMarshallable(value)(fromToEntityMarshaller(status.asScala)(marshaller)))
}
What is return type of complete(OK, maybeEvent.get(), Jackson.marshaller())?
I assume RouteAdapter. If so cast it to Route so chain will be binded to Route not RouteAdaper and at the end will not have troubles with casting from super class to subclass.
The application i'm writing performs an initial API call with Retrofit which returns a URL. That response is then .flatMap'd into another API call depending on the text contained in the URL. However, the two secondary API calls are defined to return different response models.
To make things clearer, here is some code:
APIService service = retrofit.create(APIService.class);
service.getURL() // returns response model containing a URL.
.flatMap(new Function<GetURLResponse, ObservableSource<?>>() {
#Override
public ObservableSource<?> apply(GetURLResponse getURLResponse) throws Exception {
// Determine whether the returned url is for "first.com".
if (getURLResponse.url.contains("first.com")) {
return service.first(getURLResponse.url);
}
// Otherwise, the URL is not for "first.com", so use our other service method.
return service.second(getURLResponse.url);
}
})
Here are the interface definitions for service.first() and service.second():
#GET
Observable<retrofit2.Response<ResponseBody>> first(#Url String url);
#GET
Observable<SecondModel> second(#Url String url);
How can I better handle these two different possible types (retrofit2.Response<ResponseBody> and SecondModel) for the rest of the stream? Eg. If the initial URL contains first.com then the service.first() API call should fire, and operators down the stream should received a retrofit2.Response<ResponseBody>. Conversely, if the initial URL does not contain first.com, the service.second() API call should fire and operators down the stream should receive a SecondModel.
The easiest way would be to have both your model classes implement an interface and return that interface, alternatively the models could both extend an abstract class to achieve the same effect. You would then do an instanceOf check to see which model it is and continue with your preferred transformations.
That having said you mentioning downstream operators, makes me think that this would cause an annoying amount of checks. So what I would do is split the stream using the publish operator, and then apply your further transformations to each sub-stream. Finally you should merge the two streams back together and return a single model encompassing both models.
Below a code example to get you started.
Observable<Integer> fooObservableSecondaryRequest(String foo) {
return Observable.just(1);
}
Observable<Integer> booObservableSecondaryRequest(String boo) {
return Observable.just(2);
}
Observable<String> stringObservable = Observable.just("foo", "boo");
stringObservable.publish(shared -> Observable.merge(
shared.filter(a -> a.equals("foo"))
.flatMap(fooString -> fooObservableSecondaryRequest(fooString))
.map(num -> num * 2),
shared.filter(a -> a.equals("boo"))
.flatMap(booString -> booObservableSecondaryRequest(booString))
.map(num -> num * 10)
)).subscribe(result -> System.out.println(result)); // will print 2 and 20