How to handle error thown in RxJava chain - java

I am trying to execute following code
testRepository
.exists(data)
.flatMap(x -> {
if (x==null) {
return Observable.error(new Exception("Error"));
}
return Observable.just(x);
})
.flatMap(x -> testRepository.create(x))
.flatMap(x -> {
return Observable.just(x);
});
This code works when no error is thrown in first map. But in case when error is thrown it just hangs.
What is wrong here?
Thanks

You haven't declared error properly, you shouldn't perform explicit converting to error observable via flatMap, because in case positive state you create new observable for each item and combine them after all.
You may just use
.doOnNext(x -> {
if (x == null) throw new IllegalStateException("null item error");
})
And this exception will interrupt stream and will be properly handled in onError callback of subscription.
Last instruction also doesn't make sense, because you convert each element to a single item observable and then combine them back to a similar stream.
Ps: also it needs to be called .subscribe somewhere, but i think it is meant.

Related

What's the easiest way to wait for Mono completion in the background?

We are given a Mono, that's handling some action(say a database update), and returns a value.
We want to add that Mono(transformed) to a special list that contains actions to be completed for example during shutdown.
That mono may be eagerly subscribed after adding to the list, to start processing now, or .subscribe() might not be called meaning it will be only subscribed during shutdown.
During shutdown we can iterate on the list in the following way:
for (Mono mono : specialList) {
Object value = mono.block(); // (do something with value)
}
How to transform the original Mono such that when shutdown code executes, and Mono was previously subscribed(), the action will not be triggered again but instead it will either wait for it to complete or replay it's stored return value?
OK, looks like it is as simple as calling mono.cache(), so this is how I used it in practice
public Mono<Void> addShutdownMono(Mono<Void> mono) {
mono = mono.cache();
Mono<Void> newMono = mono.doFinally(signal -> shutdownMonos.remove(mono));
shutdownMonos.add(mono);
return newMono;
}
public Function<Mono<Void>,Mono<Void>> asShutdownAwaitable() {
return mono -> addShutdownMono(mono);
}
database.doSomeAction()
.as(asShutdownAwaitable)
.subscribe() // Or don't subscribe at all, deferring until shutdown
Here is the actual shutdown code.
It was also important to me that they execute in order of being added, if user chose not to eagerly subscribe them, that's reason for Flux.concat instead of Flux.merge.
public void shutdown() {
Flux.concat(Lists.transform(new ArrayList<>(shutdownMonos), mono -> mono.onErrorResume(err -> {
logger.error("Async exception during shutdown, ignoring", err);
return Mono.empty();
}))
).blockLast();
}

Log inside Mono.error gets outputted twice

For some strange reason, the "User not found" log gets outputted twice in the logs of my application, despite calling findUserById only once.
I am not sure what causes this problem.
Is there a better way to approach this (logging and throwing an exception)?
Please note that the findById call is of an API.
Edit:
It appears that the exception is thrown only once. Also, if I replace Mono.error with Mono.defer, the log is printed twice as well.
public Mono<User> getUser(String id) {
Mono<User> thisIsEmpty = getNoUser(); // Assume that this is empty
return Mono.defer(() -> thisIsEmpty.switchIfEmpty(Mono.defer(() -> findUserById(id))));
}
public Mono<User> findUserById(String id) {
log.info("This is printed once.");
Mono<User> user = repository.findById(id).switchIfEmpty(Mono.error(() -> { // findById is an API call of a library I use
log.error("User not found (this is printed twice)"); // Gets printed twice
throw new UserException(MY_ERROR_CODE, 401);
}));
user.subscribe(User -> ... // Do something if it is not empty
return user;
}
Well, from Mono#error docs:
Create a Mono that terminates with an error immediately after being subscribed to. The Throwable is generated by a Supplier, invoked each time there is a subscription and allowing for lazy instantiation.
First your subscription is here:
user.subscribe(User -> ...
And I assume you have another subscription in code, that uses getUser.
That's why it's printed twice.
As #Eugene mention your problem is that you probably subscribe twice.
If you want to call it once then your method should be rewriten
public Mono<User> findUserById(String id) {
log.info("This is printed once.");
return repository.findById(id)
.switchIfEmpty(Mono.error(() -> { // findById is an API call of a library I use
log.error("User not found (this is printed twice)"); // Gets printed twice
throw new UserException(MY_ERROR_CODE, 401);
}))
.flatMap(user-> {
// do smth here
return Mono.just(user);
});
return user;
}
Reactive is a bit different so when you return mono it wont be executed until you subscribe to it (or block which you should not do until you must).
So basically what is happening in your case first time you "execute" when you do
user.subscribe(User -> ... // Do something if it is not empty
Second time is probably you have controller which is returning mono and spring boot is subscribing
Also not anything you do inside of you service subscribe wont be sent outside of it. You should use flatMap or any other Reactive operators for that.

RxJava Combining Multiple Observer after filter

Following is my Current Code
private final List<Disposable> subscriptions = new ArrayList<>();
for (Instrument instrument : instruments) {
// Waiting for OrderBook to generate Reliable results.
GenericBook Book =
service
.getBook(instrument.getData())
.filter(gob -> onBookUpdate(gob))
.blockingFirst();
subscriptions.add(
service
.getBook(instrument.getData())
.subscribe(
gob -> {
try {
onBookUpdate(gob);
} catch (Exception e) {
logger.error("Error on subscription:", e);
}
},
e -> logger.error("Error on subscription:", e)));
}
So what it does is for each instrument it first Block wait till the output of onBookUpdate(gob) Becomes true. onBookUpdate(gob) returns boolean.
Once we have first onBookUpdate as true then i Will push that subscriber into subscriptions variable.
This slow down as I have to wait foreach instrument and then move on the next instrument.
My Goal is to run all these in parallel then wait all to finish and push them to subscriptions variable.
I tried zip but didn't work
List<Observable<GenericOrderBook>> obsList = null;
for (Instrument instrument : instruments) {
// This throws nullException.
obsList.add(service
.getBook(instrument.getData())
.filter(gob -> onBookUpdate(gob))
.take(1));
}
}
// Some how wait over here until all get first onBookUpdate as true.
String o = Observable.zip(obsList, (i) -> i[0]).blockingLast();
When using observables etc, one should embrace them wholeheartedly. One of the premises for embracing is to separate the configuration and construction of your pipeline from its execution.
In other words, configure your pipeline upfront and then, when the data is available, send the data through it.
Furthermore, embracing observables implies avoiding for-loops.
I'm not 100% what your use case is but what I'd suggest is to create a pipeline that takes an instrument as input and returns a subscription...
So something like
service.getBook(instrument.getData())
.flatMap(gob -> {
onBookUpdate(gob);
return gob;
});
That will return an Observable that you can subscribe to and add the result to the subscriptions.
Then create a seed observable that pumps the instrument objects into it.
Not sure of some of the details of your API, so come back to me if this is not clear or I've made a wrong assumption.
I am assuming instruments to be a List. If yes, then you can do something like this,
Observable
.fromIterable(instruments)
// Returns item from instrument list one by one and passes it to getBook()
.flatmap(
instrument -> getBook(instrument.getData())
)
.filter(
gob -> onBookUpdate(gob)
)
// onComplete will be called if no items from filter
.switchIfEmpty(Observable.empty())
.subscribe(
onBookUpdateResponse -> // Do what you want,
error -> new Throwable(error)
);
Hope this helps.

Using Java `CompletablyFuture.exceptionally` to optionally continue through exceptions

I have a series of asynchronous tasks chained together using Java CompletableFutures. The code looks something like this:
CompletableFuture<Result> doTasks(final TaskId id) {
return firstTask.workAsync(id)
.thenComposeAsync(__ -> secondTask.workAsync(id))
.thenComposeAsync(__ -> thirdTask.workAsync(id))
.thenApplyAsync(__ -> fourthTask.workAsync(id));
}
However, firstTask.workAsync throws an exception indicating that the work has already been completed, which is OK in this situation, so I would like to just ignore it and continue through the chain.
Of course, I could just wrap that bit in a separate function where I can handle the exception, but is there a way to handle it directly in the CompletableFuture chain and continue to throw all other exceptions?
A co-worker suggested I use CompletableFuture.exceptionally, but all of the examples online that I see are totally useless and just return null, which looks like it would kill the chain. How would I use that in this case?
CompletableFuture.exceptionally can be used to continue when getting an exception in a CompletableFuture. In a nutshell, you need to check the type of the exception, and if it's an exception you want to continue on, you can return a new CompletableFuture, which can be empty since the result is not used down the chain.
CompletableFuture<Result> doTasks(final TaskId id) {
return firstTask.workAsync(id)
.exceptionally(t -> {
// Will continue down the chain if matches
if (t instanceof TotallyOkException) {
return null;
}
// This will throw an ExecutionException. I convert it to a RuntimeException here
// because I don't want to add throws statements up the chain.
throw new RuntimeException(t);
})
.thenComposeAsync(__ -> secondTask.workAsync(id))
.thenComposeAsync(__ -> thirdTask.workAsync(id))
.thenApplyAsync(__ -> fourthTask.workAsync(id));
}
In this case, it will throw all non-TotallyOkException exceptions.
Returning null in your exceptionally function will not, in itself, kill the chain. The only way it will kill the chain is a result of lack of null handling in the downstream function and causing a NullPointerException.
Your exceptionally function can be set up to handle some types of exception and not others. For example:
return firstTask.workAsync(id)
.thenComposeAsync(firstResult -> secondTask.workAsync(id))
.exceptionally(t -> {
if (t instanceof TransientException) {
return getUsingBackupMethod(id);
}
throw new RuntimeException(t);
});
This exceptionally function will (effectively) catch an exception thrown from either of the first two tasks.

RxJava polling + manual refresh

I have a list a want to refresh every minute.
For example the user list here : https://github.com/android10/Android-CleanArchitecture/blob/master/domain/src/main/java/com/fernandocejas/android10/sample/domain/interactor/GetUserList.java
I add a periodical refresh using repeatWhen :
public Observable<List<User>> buildUseCaseObservable(Void unused) {
return this.userRepository
.users()
.repeatWhen(new Function<Observable<Object>, ObservableSource<?>>() {
#Override
public ObservableSource<?> apply(Observable<Object> objectObservable) throws Exception {
return objectObservable.delay(1, TimeUnit.MINUTES);
}
});
}
It works fine this way, calling onNext every minute.
But if I want to refresh immediately this list (because of user's action or because of a notification), I don't know how to perform that.
Should I cancel/dispose the observable and restart a new one ?
Thanks
From your code I understand that the users list is generated and emitted upon subscription.
Here are some solutions I can think of, instead of unsubscribing and resubscribing upon the event to which you want to react immediately:
Instead of using the repeatWhen operator, use the interval creation operator combined with the flatMap to invoke the subscription to a new Observable every minute and use the merge operator to add reaction to the other event in which you are interested. Something like this:
#Test
public void intervalObservableAndImmediateReaction() throws InterruptedException {
Observable<String> obs = Observable.interval(1, TimeUnit.SECONDS)
.cast(Object.class)
.mergeWith(
Observable.just("mockedUserClick")
.delay(500, TimeUnit.MILLISECONDS))
.flatMap(
timeOrClick -> Observable.just("Generated upon subscription")
);
obs.subscribe(System.out::println);
Thread.currentThread().sleep(3000); //to see the prints before ending the test
}
or adjusted to your needs (but the principal is the same):
Observable.interval(1, TimeUnit.MINUTES)
.mergeWith(RxView.clicks(buttonView))
.flatMap(timeOrClick -> this.userRepository.users());
You can use the flatMap operator as before, even while keeping you working current implementation and without merging to an interval - just keep your working code and in another area of the programme chain it to the RxBinding of your choosing:
RxView.touches(yourViewVariable)
.flatMatp(motionEvent -> this.userRepository.users())
.subscribe(theObserver);
Note that in this solution the subscription is done independently to the two observables. You'll probably be better off if you use different observers, or manage a subject or something on that line. A small test I ran showed one subscriber handled subscribing to 2 different observables with no problem (in Rxjava1 - didn't check in Rxjava2 yet), but it feels iffy to me.
If you aren't concerned with adjusting the refresh time after one of the other observables emits data you can do something like the following:
// Specific example of a user manually requesting
val request = Observable.create<String> { emitter ->
refresh.setOnClickListener {
emitter.onNext("Click Request")
}
}
.observeOn(Schedulers.io())
.flatMap {
userRepository.users()
}
// Refresh based off of your original work, could use something like interval as well
val interval = userRepository.users()
.subscribeOn(Schedulers.io())
.repeatWhen { objectObservable ->
objectObservable.delay(1, TimeUnit.MINUTES)
}
// Combine them so that both emissions are received you can even add on another source
Observable.merge(request,interval)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
contents.text = it.toString()
}, {
contents.text = it.toString()
},{
println(contents.text)
})
Then you don't have to dispose and resubscribe every time

Categories

Resources