I have a scenario in which I will get a list of entities from DB using
repository.getAllByIds(ids)
which will return Flux<Entity>
in case of the Flux is empty then i need to call handleAllEntitiesNotFound() else i need to call handleNotFoundEntities()
repository.getAllByIds(ids)
.buffer()
.switchIfEmpty(__ -> handleAllEntitiesNotFound(ids, erroneousEntities))
.flatMap(list -> handleNotFoundEntities(list))
private Flux<Entity> handleAllEntitiesNotFound(List<String> ids, List<ResponseError> erroneousEntities) {
Flux.fromIterable(ids).subscribe(id -> erroneousEntities.add(new ResponseError("Not Found", "Not Found", id)));
return Flux.empty();
}
I'm using buffer() to collect the list into Flux<List<Entity>>
The problem is, when i call the service, it halts, no response no logs no anything, if i removed the line .switchIfEmpty(__ -> handleAllEntitiesNotFound(ids, erroneousEntities)) it works and return a response but without handling the handleAllEntitiesNotFound
What could be the problem using buffer() with switchIfEmpty()
I think you've come to the wrong conclusion here - buffer() and switchIfEmpty() work without a problem together:
Flux.empty()
.buffer()
.switchIfEmpty(Mono.just(List.of(1)))
.subscribe(System.out::println); //Prints "[1]"
However, your handleAllEntitiesNotFound() method is very suspicious. You seem to be passing in an existing list, creating a new Flux to add to it, and then returning an empty Flux. The example isn't runnable so it's impossible to narrow down the exact cause, but there's a few points that could well be the culprit (either individually or in tandem):
Mutating an existing object that's passed into a reactive stream is generally considered bad form. It's much easier and safer to return a new list (and you can merge that list with another if you want when the reactive stream completes.)
You're creating a Flux simply to read from one list, and add elements into another. That's confusing, and makes very little sense. Just use standard Java streams (i.e. ids.stream().map(id -> new ResponseError("Not Found", "Not Found", id)).collect(Collectors.toList()).)
You're returning Flux.empty(), which is almost certainly why there's no response. One would usually expect switchIfEmpty() to return a non-empty Flux, unless you're deliberately just using it as a side-effect.
handleNotFoundEntities seems like a strange choice of name for a method which would seem to be passed the entities that were found.
Related
I am not able to process Mono<List<Object>> to List<Object>. I have read somewhere that flatmap can be used, but I am not able to do that either. I don't want to use .block() method for this scenario.
A code example could be:
PagePublisher<Address> someAddressPage = someService.getPagePublisherForAddress();
Mono<List<Address>> addressListMono =
Flux.from(someAddressPage.items()).collectList();
I need to get List<Address> from Mono<List<Address>>. The method return type is List<Address>.
I am not sure how to go about that and I am relatively new to reactive. Need help in that regard.
Using block() is actually the only way to get the object from a Mono when it is emitted. However, you should avoid it at all costs because it defeats the purpose of using the reactive stack given that it actually blocks the thread waiting for the object to be emitted from the Mono.
This means that while working with reactive programming you should actually work with Mono and Flux so that your application is really reactive. The consequence of this is that you must change the return type of your method to return Mono<List<Address>> instead.
Its impossible to get List<Address> from Mono<List<Address>>. If the List isn't available yet since Mono and Flux are asynchronous/non-blocking by their nature, you can't get it except by waiting until it comes in and that's what blocking is.
Ideally, your method should return Mono<List<Address>> or just Flux<Address> and the caller of this method should subscribe to the results. Since you are using Spring Webflux, complete chain should be reactive.
You can subscribe to the Mono from the calling method and the Subscriber you pass will get the List when it becomes available. E.g.
addressListMono.subscribe(
addressList -> doSomething(addressList),
error -> error.printStackTrace(),
() -> Console.out.println("completed without a value")
)
I'm using RxJava, Room and Retrofit to accomplish Repository pattern in Android. Everything is working except this one thing. I have implemented SearchView for searching data. Now when search view text changes I call search method. here is how it looks like
mLocal.searchMovies(query) //returns Single
mRemote.searchMovies(query) //returns Single
public Single<List<MovieEntity>> search(String query) {
return mLocal.searchMovies(query)
.toMaybe()
.switchIfEmpty(mRemote.searchMovies(query)
.map(data -> {
mLocal.saveAll(data);
return data;
}));
}
mLocal queries the room database and mRemote calls retrofit to fetch data from REST API. The problem is, this only calls mLocal and when room returns empty row network call is not initiated. I have tried everything I possibly and read many articles but I cannot understand how to get it work.
Each of mLocal and mRemote works fine. Only chaining them does not accomplish what I want.
Your chain does not work because .toMaybe() from Single cannot produce Maybe.empty(), which is the result required to have .switchIfEmpty take effect.
You will either need to replace it with Single.onErrorResume so the error condition from a missing entry results in the remote query, or make it return Maybe to accurately represent the tristate of <Result,NotCached,Error>.
The explanation of my problem is the same as in this answer. I will copy relevant part and post my working piece of code after applying it.
Note: I changed to Flowable from Single/MayBe. am not sure how much it affects the original code, hadn't I changed the type of Observables.
Based on the OP's feedback, the allDatas source was infinite but
returned an empty List. The resolution was to apply at least take(1)
to get exactly one response from allDatas and then optionally filter
out the empty List so switchIfEmpty can switch over to the
alternatives
public Flowable<List<MovieEntity>> getMovies() {
return mLocal.allMovies().take(1)
.filter(list -> !list.isEmpty())
.switchIfEmpty(mRemote.allMovies()
.doOnNext(data -> mLocal.saveAll(data))
);
}
So, I'm trying to work with Webflux and I've got a scenario "check if an object exists; if so, do stuff, else - indicate error".
That can be written in reactor as:
public Mono<Void> handleObjectWithSomeId(Mono<IdType> id){
return id.
flatMap(repository::exists). //repository.exists returns Mono<Boolean>
flatMap(e -> e ? e : Mono.error(new DoesntExistException())).
then(
//can be replaced with just(someBusinessLogic())
Mono.fromCallable(this::someBusinessLogic)
);
}
or as:
public Mono<Void> handleObjectWithSomeId(Mono<IdType> id){
return id.
flatMap(repository::exists). //repository.exists returns Mono<Boolean>
flatMap(e -> e ? e : Mono.error(new DoesntExistException())).
map(e -> this.someBusinessLogic()));
}
Let's assume that return type of someBusinessLogic cannot be changed and it has to be simple void, not Mono<Void>.
In both cases if the object won't exist, appropriate Mono.error(...) will be produced.
While I understand that then and flatMap have different semantics, effectively I get the same result. Even though in second case I'm using flatMap against its meaning, I get to skip flatMap and fromCallable in favor of simple map with ignored argument (that seems more readable). My point is, both apporaches have advantages and disadvantages when it comes to readability and code quality.
So, here's a summary:
Using then
pros
is semantically correct
cons
in many cases (like above) requires wrapping in ad-hoc Mono/Flux
Using flatMap
pros
simplifies continued "happy scenario" code
cons
is semantically incorrect
What are other pros/cons of both approaches? What should I take under consideration when choosing an operator?
I've found this reactor issue that states that there is not real difference in speed.
TL, DR: If you care about the result of the previous computation, you can use map(), flatMap() or other map variant. Otherwise, if you just want the previous stream finished, use then().
You can see a detailed log of execution for yourself, by placing an .log() call in both methods:
public Mono<Void> handleObjectWithSomeId(Mono<IdType> id) {
return id.log()
.flatMap(...)
...;
}
Like all other operations in Project Reactor, the semantics for then() and flatMap() are already defined. The context mostly defines how these operators should work together to solve your problem.
Let's consider the context you provided in the question. What flatMap() does is, whenever it gets an event, it executes the mapping function asynchronously.
Since we have a Mono<> after the last flatMap() in the question, it will provide the result of previous single computation, which we ignore. Note that if we had a Flux<> instead, the computation would be done for every element.
On the other hand, then() doesn't care about the preceding sequence of events. It just cares about the completion event:
That's why, in your example it doesn't matter very much which one you use. However, in other contexts you might choose accordingly.
You might also find the Which operator do I need? section Project Reactor Reference helpful.
I'm using a Builder pattern to build upon a model object which combines data from different network calls and I'm having a hard time understanding the best way to take the model object from the first network call and combine the data from the second network call into the original model object.
My actual subscription:
myFirstApiRepository.getFirstModelObjectBuilder()
.flatmap(firstModelObjectBuilder -> mySecondApiRepository.getSomeExtraData(firstModelObjectBuilder))
.observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getMySubscriber());
First network call:
public Observable<FirstModelObject.Builder> getFirstModelObjectBuilder() {
return myFirstApiResource.getSomeData(...)
.flatMap(someData -> Observable.just(new FirstModelObject.Builder()
.setFirstAttribute(someData.getFirstAttribute())
.setSecondAttribute(someData.getSecondAttribute())));
}
Second network call:
public Observable<FirstModelObject> getSomeExtraData(FirstModelObject.Builder builder) {
return mySecondApiResource.getSomeData(...)
.flatMap(aString -> builder.setSomeStringValue(aString)
.build());
}
The problem here is that I have to pass the builder object into the second network call's observable. This makes it very rigid and I'd rather not have my SecondApiRepository rely on and reference a data type which it shouldn't need to reference. It also makes this second method here ".build()" the object, which is not good. So, how can I use the firstModelObject and add data to it from the second network call in a clean way?
If this is just bad design, please let me know. I'm still trying to learn more about RxJava best practices. :)
If your second request relies on first request's result, then check my answer - https://stackoverflow.com/a/41820372/7045114.
If not - just use zip operator:
Combines the emissions of multiple Observables together via a specified function and emit single items for each combination based on the results of this function.
Observable.zip(firstRequest, secondRequest, (firstResult, secondResult) -> {
//process results
})
Background
I have a number of RxJava Observables (either generated from Jersey clients, or stubs using Observable.just(someObject)). All of them should emit exactly one value. I have a component test that mocks out all the Jersey clients and uses Observable.just(someObject), and I see the same behaviour there as when running the production code.
I have several classes that act upon these observables, perform some calculations (& some side-effects - I might make them direct return values later) and return empty void observables.
At one point, in one such class, I'm trying to zip several of my source observables up and then map them - something like the below:
public Observable<Void> doCalculation() {
return Observable.zip(
getObservable1(),
getObservable2(),
getObservable3(),
UnifyingObject::new
).concatMap(unifyingObject -> unifyingObject.processToNewObservable())
}
// in Unifying Object
public Observable<Void> processToNewObservable() {
// ... do some calculation ...
return Observable.empty();
}
The calculating classes are then all combined and waited on:
// Wait for rule computations to complete
List<Observable<Void>> calculations = ...;
Observable.zip(calculations, results -> results)
.toBlocking().lastOrDefault(null);
The problem
The trouble is, processToNewObservable() is never being executed. By process of elimination, I can see it's getObservable1() that's the trouble - if I replace it with Observable.just(null), everything's executed as I'd imagine (but with a null value where I want a real one).
To reiterate, getObservable1() returns an Observable from a Jersey client in production code, but that client is a Mockito mock returning Observable.just(someValue) in my test.
Investigation
If I convert getObservable1() to blocking, then wrap the first value in just(), again, everything executes as I'd imagine (but I don't want to introduce the blocking step):
Observable.zip(
Observable.just(getObservable1().toBlocking().first()),
getObservable2(),
getObservable3(),
UnifyingObject::new
).concatMap(unifyingObject -> unifyingObject.processToNewObservable())
My first thought was that perhaps something else was consuming the value emitted from my observable, and zip was seeing that it was already complete, thus determining that the result of zipping them should be an empty observable. I've tried adding .cache() onto every observable source I can think is related, however, but that hasn't altered the behaviour.
I've also tried adding next / error / complete / finally handlers on getObservable1 (without converting it to blocking) just before the zip, but none of them executed either:
getObservable1()
.doOnNext(...)
.doOnCompleted(...)
.doOnError(...)
.finallyDo(...);
Observable.zip(
getObservable1(),
getObservable2(),
getObservable3(),
UnifyingObject::new
).concatMap(unifyingObject -> unifyingObject.processToNewObservable())
The question
I'm very new to RxJava, so I'm pretty sure I'm missing something fundamental. The question is: what stupid thing could I be doing? If that's not obvious from what I've said so far, what can I do to help diagnose the issue?
The Observable must emit to start the chain. You have to think of your pipeline as a declaration of what will happen when the Observable emits.
You didn't share what was actually being observed, but Observable.just() causes the Observable to emit the wrapped object immediately.
Based on the response in the comment, either one of the getObservable doesn't return any value but just completes or the Mockito mocking does something wrong. The following standalone example works for me. Could you check it and start slowly mutating it to see where things break?
Observable.zip(
Observable.just(1),
Observable.just(2),
Observable.just(3),
(a, b, c) -> new Integer[] { a, b, c })
.concatMap(a -> Observable.from(a))
.subscribe(System.out::println)
;
Note: I didn't find my answer here very satisfying, so I dug in a bit further and found a much smaller reproduction case, so I've asked a new question here: Why does my RxJava Observable emit only to the first consumer?
I've figured out at least part of my troubles (and, apologies to all who tried to answer, I don't think you stood much of a chance given my explanation).
The various classes which perform these calculations were all returning Observable.empty() (as per processToNewObservable() in my original example). As far as I can tell, Observable.zip() doesn't subscribe to the Nth observable it's zipping until the N-1th observable has emitted a value.
My original example claimed it was getObservable1() that was misbehaving - that was actually slight inaccurate, it was a later observable in the parameter list. As I understand it, the reason making it blocking, then turning that value into an Observable again worked is because making it blocking and calling first forced its execution, and I got the side-effects I wanted.
If I change all my calculating classing to return Observable.just(null) instead, everything works: the final zip() of all the calculation classes' observables works through them all, so all the expected side-effects happen.
Returning a null Void seems like I'm definitely Doing Something Wrong from a design point of view, but at least this particular question is answered.