I am new to reactive programming and I want to transform the following code into non blocking way.
For the sake of simplicity, I created a sample pseudo code based from my original code. Any help will be appreciated.
public Mono<Response> getResponse(List<Provider> providers) {
for (Provider provider : providers) {
Response response = provider.invokeHttpCall().block();
if(response.getMessage() == "Success") {
return Mono.just(response);
}
continue;
}
return Mono.empty();
}
provider.invokeHttpCall() method
#Override
public Mono<Response> invokeHttpCall(){
WebClient webClient = WebClient.create();
return webClient.post()
.uri("/provider").accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Response.class);
}
I tried several tactics to implement this, but still no luck. Either all providers are invoked or I need to block the webclient thread.
Flux.fromIterable(providers)
.concatMap(Provider::invokeHttpCall) // ensures providers are called sequentially
.filter(response -> response.getMessage().equals("Success"))
.next()
reactive is a kind of Stream. Please think it as a Stream and program it reactively.
I give you such followed code.
Firstly, use Flux.fromIterable() to create a flux stream from a List.
Next, use flatmap() and Lambda fuction to emit the invoke into another new thread.
use method filterWhen() and Lambda to get the "Success" response and just get the first "Success" elements. See filterwhen api Doc.
Finally, just use Mono.from() to wrap the Flux and then return the Mono type.
public Mono<Response> getResponse(List<Provider> providers) {
return Mono.from(Flux.fromIterable(providers)
.flatmap(provider ->
Mono.defer(() -> provider.invokeHttpCall())
.filterWhen(response -> response.getMessage() == "Success");
}
if you want to see result and println().
Just use .subsribe() method to excute it.
getResponse.subsribe(System.out::println);
I have a list of following objects with method returning reactive type Mono<?>:
interface GuyWithReactiveReturnTypeMethod {
Mono<String> execute();
}
class ReactiveGuysInvocator {
Mono<String> executeAllGuys(List<GuyWithReactiveReturnTypeMethod> guysToInvoke) {
???
}
}
And I need to invoke all the guys one by one (n's guy result is n+1's guy argument), but I'm not sure how can I iterate over such list.
I thought of flatMaping next guy in a while loop:
public interface GuyWithReactiveReturnTypeMethod {
Mono<String> execute(String string);
}
class ReactiveGuysInvocator {
Mono<String> executeAllGuys(List<GuyWithReactiveReturnTypeMethod> guysToExecute) {
ListIterator<GuyWithReactiveReturnTypeMethod> iterator = guysToExecute.listIterator();
Mono<String> currentResult = Mono.just("start");
while (iterator.hasNext()) {
GuyWithReactiveReturnTypeMethod guyToInvoke = iterator.next();
currentResult = currentResult.flatMap(guyToInvoke::execute)
.doOnNext(object -> System.out.println("Executed!"))
.doOnError(error -> System.out.println("Error"));
}
return currentResult;
}
}
But this approach seems to be completely incorrect.
Does anyone know how could I implement something like this?
UPDATE: flatMap can be easily abused. Make sure that you are doing asynchronous work when using flatMap. Mostly, it seems to me, that you can do pretty well with a minimum of Mono.just.
Flatmap is what you have to do with the constraints you provide.
executeAllGuys(Arrays.asList(new GuyWithReactiveReturnTypeMethod[] {
(s)->Mono.just(s+"1"),
(s)->Mono.just(s+"2"),
(s)->Mono.just(s+"3")}))
.subscribe(System.out::println);
Mono<String> executeAllGuys(List<GuyWithReactiveReturnTypeMethod> guysToExecute) {
// your flow is starting here
Mono<String> stringMono = Mono.just("start");
for ( GuyWithReactiveReturnTypeMethod guyToInvoke: guysToExecute) {
stringMono = stringMono.flatMap(guyToInvoke::execute);
}
return stringMono;
}
Just look at all those Mono.just calls. Why do you want to create N+1 flows to do the job? The real problem is you're creating a new flow every time you execute the interface method. Flatmap stops the current flow and starts a new one with the publisher returned by the flatMap method. Try to think reactive and treat the whole business like a stream. There is no flatMap in Streams. A reactive execution should be done on only a single flow.
A Mono<String> execute(String string) is not a reactive component. It is a reactive producer. A Mono<String> execute(Mono<String> string) is a reactive component.
Make your interface more reactive by taking a Mono in and returning a Mono. Your application is doing a map conversion on at each step. This is "chaining reactive components".
executeAllGuys(Arrays.asList(new GuyWithReactiveReturnTypeMethod[] {
(s)->s.map(str->str+"1"),
(s)->s.map(str->str+"2"),
(s)->s.map(str->str+"3")}))
.subscribe(System.out::println);
Mono executeAllGuys(List guysToExecute) {
// your flow is starting here
Mono stringMono = Mono.just("start");
for ( GuyWithReactiveReturnTypeMethod guyToInvoke: guysToExecute) {
stringMono = guyToInvoke.execute(stringMono);
}
return stringMono;
}
interface GuyWithReactiveReturnTypeMethod {
Mono execute(Mono string);
}
Make your interface less reactive but make your application more reactive by using a Flux instead of a list. You will then have to use reduce to convert a Flux to a Mono. Your application is doing a Map/Reduce function. I don't think a Flux will guarantee execution order of the elements in the flow but it could executeAllGuys more efficiently.
// your flow is starting here
executeAllGuys(Flux.just(
(s)->s+"1",
(s)->s+"2",
(s)->s+"3"))
.subscribe(System.out::println);
Mono executeAllGuys(Flux guysToExecute) {
return guysToExecute.reduce("start", (str, guyToInvoke)->guyToInvoke.execute(str));
}
interface GuyWithReactiveReturnTypeMethod {
String execute(String string);
}
Reference: Reactive Programming: Spring WebFlux: How to build a chain of micro-service calls?
I want to get a string data from another server by webclient object, and put it to another Mono object. But in a webclient, only readable that in .subscribe().
Because responseBody.subscribe() method is async, method test() will be return result object with empty message field before responseBody.subscribe() executed.
Of course, I knew that if I return responseBody object instead of result object, there is no problem. But I want to return not a responseBody object but result object with not empty field of message.
I want to return result when responseBody's subscribe() is completed.
How to change my code?
Please help me.
public Mono<ResultVO> test() {
Mono<ResultVO> result = Mono.just(new ResultVO());
WebClient client = webClientBuilder.baseUrl("http://XXXXXX").build();
Mono<String> responseBody = client.get().uri("/aaaa/bbbbb").retrieve().bodyToMono(String.class);
responseBody.subscribe( s -> {
result.subscribe(g -> g.setMessage(s));
});
return result;
}
...
#Data
public class ResultVO {
private long timestamp;
private String ip;
private String message;
...
}
I expect like this
{
"timestamp": 1566662695203,
"ip": "192.168.1.1",
"message": "c0db76f6-4eb5-4f84-be8d-018d53b453bb"
}
But result data is,
{
"timestamp": 1566662695203,
"ip": "192.168.1.1",
"message": ""
}
Putting this kind of logic into the subscribe method is not recommended, it can easily lead to 'callback hell' and eventually unmaintainable code. Also, I don't see the caller of the shared test method, but chances are that one of the Monos is subscribed twice, which also leads to quite confusing behaviour.
Instead, to combine Monos you can use zip, zipWith, flatMap and a couple of other operators.
One solution with zipWith method:
public Mono<ResultVO> test()
{
WebClient client = WebClient.builder().baseUrl("http://XXXXXX").build();
// dummy representation of another data source (db query, web service call...)
Mono<ResultVO> result = Mono.just(new ResultVO());
Mono<String> responseBody = client.get().uri("/aaaa/bbbbb").retrieve().bodyToMono(String.class);
return result.zipWith(responseBody,
(resultObj, body) -> new ResultVO(resultObj.getTimestamp(), resultObj.getIp(), body));
}
Couple of other notes:
If you are returning JSON through a REST endpoint of your reactive WebFlux application, then you never need to subscribe manually, Spring will do that for you
Avoid using mutable objects (the ones which you modify after creation with setters), instead create new object, this will make your code easier to reason about and less prone to concurrency issues
Useful read about available Reactor operators
First of all, you hardly ever subscribe in your own application.
Think of it this way. Your server is a publisher, that means that your server fetches data and then publishes it to whomever wants it.
The subscriber is usually the end client, that could be a react application, an angular application or any client.
I think you need to read up on the basics of how to use webflux and reactive programming.
This is how to do what you are asking for, with as minimal changes to your code, we map what we fetched to what we want returned.
public Mono<ResultVO> test() {
final WebClient client = webClientBuilder
.baseUrl("http://XXXXXX").build();
return client.get()
.uri("/aaaa/bbbbb")
.retrieve()
.bodyToMono(String.class)
.map(message -> {
final ResultVO resultVO = new ResultVO();
resultVO.setMessage(message);
return resultVO;
}
);
}
I want to implement a typical rest POST call in Lagom. The POST creates an object, and returns it, with a status code of 201.
However, the default return code is 200. It is possible to set a status code, as shown here (https://www.lagomframework.com/documentation/1.3.x/java/ServiceImplementation.html#Handling-headers).
However, I cannot figure out how to do it for a more complicated case. My create is asynchronious, and I return an object instead of a String.
This is the code I have:
#Override
public HeaderServiceCall<OrderRequest.CreateOrderRequest, Order> createOrder() {
UUID orderId = UUID.randomUUID();
ResponseHeader responseHeader = ResponseHeader.OK.withStatus(201);
return (requestHeader, request) -> {
CompletionStage<Order> stage = registry.refFor(OrderEntity.class, orderId.toString())
.ask(buildCreateOrder(orderId, request))
.thenApply(reply -> toApi(reply));
return CompletableFuture.completedFuture(Pair.create(responseHeader, stage.toCompletableFuture()));
};
}
However, the return value should be Pair<ResponseHeader, Order>, not Pair<ResponseHeader, CompletionStage<Order>> which I have now, so it does not compile.
I could of course extract the Order myself, by putting the completionStage into an CompletableFuture and getting that, but that would make the call synchronous and force me to deal with InterruptExceptions etc, which seems a bit complex for something that should be trivial.
What is the correct way to set a status code in Lagom?
You almost have it solved. Instead of creating a new completedFuture you could compose stage with a lambda that builds the final Pair like this:
return stage.thenApply( order -> Pair.create(responseHeader, order));
And putting all the pieces together:
registry.refFor(OrderEntity.class, orderId.toString())
.ask(buildCreateOrder(orderId, request))
.thenApply( reply -> toApi(reply));
.thenApply( order -> Pair.create(responseHeader, order));
When do you use map vs flatMap in RxJava?
Say, for example, we want to map Files containing JSON into Strings that contain the JSON--
Using map, we have to deal with the Exception somehow. But how?:
Observable.from(jsonFile).map(new Func1<File, String>() {
#Override public String call(File file) {
try {
return new Gson().toJson(new FileReader(file), Object.class);
} catch (FileNotFoundException e) {
// So Exception. What to do ?
}
return null; // Not good :(
}
});
Using flatMap, it's much more verbose, but we can forward the problem down the chain of Observables and handle the error if we choose somewhere else and even retry:
Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() {
#Override public Observable<String> call(final File file) {
return Observable.create(new Observable.OnSubscribe<String>() {
#Override public void call(Subscriber<? super String> subscriber) {
try {
String json = new Gson().toJson(new FileReader(file), Object.class);
subscriber.onNext(json);
subscriber.onCompleted();
} catch (FileNotFoundException e) {
subscriber.onError(e);
}
}
});
}
});
I like the simplicity of the map, but the error handling of flatmap (not the verbosity). I haven't seen any best practices on this floating around and I'm curious how this is being used in practice.
map transform one event to another.
flatMap transform one event to zero or more event. (this is taken from IntroToRx)
As you want to transform your json to an object, using map should be enough.
Dealing with the FileNotFoundException is another problem (using map or flatmap wouldn't solve this issue).
To solve your Exception problem, just throw it with a Non checked exception : RX will call the onError handler for you.
Observable.from(jsonFile).map(new Func1<File, String>() {
#Override public String call(File file) {
try {
return new Gson().toJson(new FileReader(file), Object.class);
} catch (FileNotFoundException e) {
// this exception is a part of rx-java
throw OnErrorThrowable.addValueAsLastCause(e, file);
}
}
});
the exact same version with flatmap :
Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() {
#Override public Observable<String> call(File file) {
try {
return Observable.just(new Gson().toJson(new FileReader(file), Object.class));
} catch (FileNotFoundException e) {
// this static method is a part of rx-java. It will return an exception which is associated to the value.
throw OnErrorThrowable.addValueAsLastCause(e, file);
// alternatively, you can return Obersable.empty(); instead of throwing exception
}
}
});
You can return too, in the flatMap version a new Observable that is just an error.
Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() {
#Override public Observable<String> call(File file) {
try {
return Observable.just(new Gson().toJson(new FileReader(file), Object.class));
} catch (FileNotFoundException e) {
return Observable.error(OnErrorThrowable.addValueAsLastCause(e, file));
}
}
});
FlatMap behaves very much like map, the difference is that the function it applies returns an observable itself, so it's perfectly suited to map over asynchronous operations.
In the practical sense, the function Map applies just makes a transformation over the chained response (not returning an Observable); while the function FlatMap applies returns an Observable<T>, that is why FlatMap is recommended if you plan to make an asynchronous call inside the method.
Summary:
Map returns an object of type T
FlatMap returns an Observable.
A clear example can be seen here: http://blog.couchbase.com/why-couchbase-chose-rxjava-new-java-sdk .
Couchbase Java 2.X Client uses Rx to provide asynchronous calls in a convenient way. Since it uses Rx, it has the methods map and FlatMap, the explanation in their documentation might be helpful to understand the general concept.
To handle errors, override onError on your susbcriber.
Subscriber<String> mySubscriber = new Subscriber<String>() {
#Override
public void onNext(String s) { System.out.println(s); }
#Override
public void onCompleted() { }
#Override
public void onError(Throwable e) { }
};
It might help to look at this document: http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/
A good source about how to manage errors with RX can be found at: https://gist.github.com/daschl/db9fcc9d2b932115b679
In your case you need map, since there is only 1 input and 1 output.
map - supplied function simply accepts an item and returns an item which will be emitted further (only once) down.
flatMap - supplied function accepts an item then returns an "Observable", meaning each item of the new "Observable" will be emitted separately further down.
May be code will clear things up for you:
Observable.just("item1").map( str -> {
System.out.println("inside the map " + str);
return str;
}).subscribe(System.out::println);
Observable.just("item2").flatMap( str -> {
System.out.println("inside the flatMap " + str);
return Observable.just(str + "+", str + "++" , str + "+++");
}).subscribe(System.out::println);
Output:
inside the map item1
item1
inside the flatMap item2
item2+
item2++
item2+++
The question is When do you use map vs flatMap in RxJava?. And I think a simple demo is more specific.
When you want to convert item emitted to another type , in your case converting file to String, map and flatMap can both work. But I prefer map operator because it's more clearly.
However in some place, flatMap can do magic work but map can't. For example, I want to get a user's info but I have to first get his id when user login in. Obviously I need two requests and they are in order.
Let's begin.
Observable<LoginResponse> login(String email, String password);
Observable<UserInfo> fetchUserInfo(String userId);
Here are two methods, one for login returned Response, and another for fetching user info.
login(email, password)
.flatMap(response ->
fetchUserInfo(response.id))
.subscribe(userInfo -> {
// get user info and you update ui now
});
As you see, in function flatMap applies, at first I get user id from Response then fetch user info. When two requests are finished, we can do our job such as updating UI or save data into database.
However if you use map you can't write such nice code. In a word, flatMap can help us serialize requests.
The way I think about it is that you use flatMap when the function you wanted to put inside of map() returns an Observable. In which case you might still try to use map() but it would be unpractical. Let me try to explain why.
If in such case you decided to stick with map, you would get an Observable<Observable<Something>>. For example in your case, if we used an imaginary RxGson library, that returned an Observable<String> from it's toJson() method (instead of simply returning a String) it would look like this:
Observable.from(jsonFile).map(new Func1<File, Observable<String>>() {
#Override public Observable<String>> call(File file) {
return new RxGson().toJson(new FileReader(file), Object.class);
}
}); // you get Observable<Observable<String>> here
At this point it would be pretty tricky to subscribe() to such an observable. Inside of it you would get an Observable<String> to which you would again need to subscribe() to get the value. Which is not practical or nice to look at.
So to make it useful one idea is to "flatten" this observable of observables (you might start to see where the name _flat_Map comes from). RxJava provides a few ways to flatten observables and for sake of simplicity lets assume merge is what we want. Merge basically takes a bunch of observables and emits whenever any of them emits. (Lots of people would argue switch would be a better default. But if you're emitting just one value, it doesn't matter anyway.)
So amending our previous snippet we would get:
Observable.from(jsonFile).map(new Func1<File, Observable<String>>() {
#Override public Observable<String>> call(File file) {
return new RxGson().toJson(new FileReader(file), Object.class);
}
}).merge(); // you get Observable<String> here
This is a lot more useful, because subscribing to that (or mapping, or filtering, or...) you just get the String value. (Also, mind you, such variant of merge() does not exist in RxJava, but if you understand the idea of merge then I hope you also understand how that would work.)
So basically because such merge() should probably only ever be useful when it succeeds a map() returning an observable and so you don't have to type this over and over again, flatMap() was created as a shorthand. It applies the mapping function just as a normal map() would, but later instead of emitting the returned values it also "flattens" (or merges) them.
That's the general use case. It is most useful in a codebase that uses Rx allover the place and you've got many methods returning observables, which you want to chain with other methods returning observables.
In your use case it happens to be useful as well, because map() can only transform one value emitted in onNext() into another value emitted in onNext(). But it cannot transform it into multiple values, no value at all or an error. And as akarnokd wrote in his answer (and mind you he's much smarter than me, probably in general, but at least when it comes to RxJava) you shouldn't throw exceptions from your map(). So instead you can use flatMap() and
return Observable.just(value);
when all goes well, but
return Observable.error(exception);
when something fails.
See his answer for a complete snippet: https://stackoverflow.com/a/30330772/1402641
Here is a simple thumb-rule that I use help me decide as when to use flatMap() over map() in Rx's Observable.
Once you come to a decision that you're going to employ a map transformation, you'd write your transformation code to return some Object right?
If what you're returning as end result of your transformation is:
a non-observable object then you'd use just map(). And map() wraps that object in an Observable and emits it.
an Observable object, then you'd use flatMap(). And flatMap() unwraps the Observable, picks the returned object, wraps it with its own Observable and emits it.
Say for example we've a method titleCase(String inputParam) that returns Titled Cased String object of the input param. The return type of this method can be String or Observable<String>.
If the return type of titleCase(..) were to be mere String, then you'd use map(s -> titleCase(s))
If the return type of titleCase(..) were to be Observable<String>, then you'd use flatMap(s -> titleCase(s))
Hope that clarifies.
I just wanted to add that with flatMap, you don't really need to use your own custom Observable inside the function and you can rely on standard factory methods/operators:
Observable.from(jsonFile).flatMap(new Func1<File, Observable<String>>() {
#Override public Observable<String> call(final File file) {
try {
String json = new Gson().toJson(new FileReader(file), Object.class);
return Observable.just(json);
} catch (FileNotFoundException ex) {
return Observable.<String>error(ex);
}
}
});
Generally, you should avoid throwing (Runtime-) exceptions from onXXX methods and callbacks if possible, even though we placed as many safeguards as we could in RxJava.
In that scenario use map, you don't need a new Observable for it.
you should use Exceptions.propagate, which is a wrapper so you can send those checked exceptions to the rx mechanism
Observable<String> obs = Observable.from(jsonFile).map(new Func1<File, String>() {
#Override public String call(File file) {
try {
return new Gson().toJson(new FileReader(file), Object.class);
} catch (FileNotFoundException e) {
throw Exceptions.propagate(t); /will propagate it as error
}
}
});
You then should handle this error in the subscriber
obs.subscribe(new Subscriber<String>() {
#Override
public void onNext(String s) { //valid result }
#Override
public void onCompleted() { }
#Override
public void onError(Throwable e) { //e might be the FileNotFoundException you got }
};);
There is an excellent post for it: http://blog.danlew.net/2015/12/08/error-handling-in-rxjava/
RxJava Map vs FlatMap
They both are Transforming operators but map has 1-1 relation and flatMap has 1-0 or many relation.
map and flatmap emits stream with
map- only 1 element
flatmap - 0/many elements
map emits single element and flatmap emits a stream of elements
Map operator
map(new Function<A, B>() {
#Override
public B apply(A a) throws Exception {
B b = new B(a);
return b;
}
})
FlatMap operator
flatMap(new Function<A, ObservableSource<B>>() {
#Override
public ObservableSource<B> apply(A a) throws Exception {
return foo(a);
}
})
[flatMap vs concatMap]
[Swift map vs flatMap]
In some cases you might end up having chain of observables, wherein your observable would return another observable. 'flatmap' kind of unwraps the second observable which is buried in the first one and let you directly access the data second observable is spitting out while subscribing.
Flatmap maps observables to observables.
Map maps items to items.
Flatmap is more flexible but Map is more lightweight and direct, so it kind of depends on your usecase.
If you are doing ANYTHING async (including switching threads), you should be using Flatmap, as Map will not check if the consumer is disposed (part of the lightweight-ness)