doTask() is a method that performs something if previousStepResult is successful, otherwise logs and return previousStepResult as is. Below code works fine and conclude() is also executed but only when there no exceptions (errors). But if there are exceptions (which are well handled) the code returns false. So in case of error, the next step is called fine and as said, logs and return false. However conclude is not called at all. I am not sure if there is an impact of exceptions inside each step preventing thenAcceptAsync to be executed or what. Any clues ?
return CompletableFuture.supplyAsync(() -> doTask("task1", true), taskExecutor).
thenApplyAsync(previousStepResult -> doTask("task2", previousStepResult),taskExecutor).
thenApplyAsync(previousStepResult -> doTask("task3", previousStepResult),taskExecutor).
thenAcceptAsync(previousStepResult -> conclude(previousStepResult),taskExecutor);
Use CompletableFuture::handle or CompletableFuture::handleAsync after the step that is likely to throw error. This would result in further steps being called as it is, else would be skipped.
Related
I am currently creating a retry mechanism for performing requests using the failsafe-lib.
The issue: the RetryPolicy that I defined includes several timeout-related exceptions, but when I use the failsafe-lib (Failsafe.with(someFallback, somePolicy).get(() -> performRequest), exceptions that I did not specify (DataBufferLimitException) to be handled are negated instead of being thrown.
Now I understand that the FailsafeExecutor(...).get() method takes a CheckedSupplier, and this (possibly) might cause in negation of unchecked exceptions (please correct me if I'm wrong, this is just an assumption). However, I am still curious if I have done something wrong and if there is something that I can do to resolve this issue.
Below I have a simplified version of my code:
public Response performRequest() {
RetryPolicy<Object> retryPolicy = RetryPolicy.builder()
.withDelay(Duration.ofMillis(60_000L))
.handle(exceptionA, exceptionB, ...)
.withMaxRetries(3)
.onSuccess(o -> log.info("someRandomMessage"))
.onFailure(o -> log.warn("someRandomWarnMessage"))
.onRetriesExceeded(o -> log.error("someRandomErrorMessage"))
.build();
Fallback<Object> fallback = Fallback.of(event -> {
Throwable rootException = ExceptionUtils.getRootCause(event.getLastException());
if (rootException instanceof TimeoutException || rootException instanceof ConnectException) {
throw new someRandomException(rootException.getMessage());
}
}
);
Response response Failsafe.with(fallback, retryPolicy).get(() -> someRequest);
return response;
The scenario that is performed with this code:
We perform a request and (during testing) we expect to see an unchecked exception. However, this exception is 'swallowed' by functionality of the failsafe-lib, while I in fact want to see back this exception. I know this is more on my end, but I'm not sure how to fix this issue. Any tips, alternatives or corrections are much appreciated.
Found my own mistake: if the if-statement was not triggered, no exception would be thrown and null would be returned. This resulted in an empty response, etc.
So I'm writing unit tests in which I'm testing capability to blacklist and unblacklist users (which is a feature in my code that is itself working fine).
Here's a sample command that works as expected:
assertThrows(ExecutionException.class, () -> onlineStore.lookup("533"));
If I blacklist user "533", and then run the above command, it works fine, because an ExecutionException is raised (because you're trying to lookup a user who is blacklisted). Similarly, if I had NOT blacklisted user "533" but still ran the above command, the test would fail, which is expected too for similar reason (i.e. no exception is now thrown as you're NOT fetching a blacklisted user).
However if I have a List of user IDs called userIds (which user "533" is now part of) and I blacklist them all (funtionality which I know is working fine), and then run the command below:
userIds.stream().map(id -> assertDoesNotThrow(() -> onlineStore.lookup(id)));
... the test passes, even through it should have FAILED. Why ? Because all users are now blacklisted, so when fetching these users, ExecutionExceptions should have been thrown ..
If I now, replace the streams command above with either of the following, they work as expected:
assertThrows(ExecutionException.class, () -> onlineStore.lookup("533"));
assertDoesNotThrow(() -> onlineStore.lookup("533"));
So this all leads me to believe that for some reason, when going through Java Streams, thrown ExecutionExceptions aren't getting caught.
Any explanation for this behavior ?
You're not calling any terminal operation on the stream, so your assertion is never executed.
You're abusing map(), which is supposed to create a new stream by transforming every element. What you actually want to do is to execute a method which has a side effect on every element. That's what forEach is for (and it's also a terminal operation which actually consumes the stream):
userIds.stream().forEach(id -> assertDoesNotThrow(() -> onlineStore.lookup(id)));
I have a lot of cases where boolean variables begin a reactive sequence and in case they are a certain value (mostly false) an exception is supposed to be thrown and the sequence broken. For example
Mono.just(foo.isValid())
.flatMap(b -> b ? Mono.empty() : Mono.error(new FooNotValidException(foo.detail1, foo.detail2)))
.then(bar.doProcess())
.flatMap(b -> b ? Mono.empty() : Mono.error(new BarProcessingNotSuccessful(bar.detail1, bar.detail2)))
....
Here if foo is not valid bar is not executed and sequence is broken with a detailed exception, same if bar processing fails.
that up there is the shortest i managed to get it to, but there is a lot of repetition so i am wondering if this can be made any less verbose?
From Borises comment i rewrote the above code example to
Mono.fromRunnable(foo::isValidOrThrow) // may throw FooNotValidException
.then(Mono.fromRunnable(bar::doProcessOrThrow)) // may throw BarProcesingNotSuccessful
....
This looks much nicer.
Consider this example:
I have a file downloading in sequence. If one download fails, it should move to next.
Psudo code:
Observable.from(urls)
.concatMap(url -> downloadObservable(url))
There is no option for moving to next url if the download fails.
There is no way to skip with onErrorResumeNext() as I just want to move to next url. Can anyone help?
There is an operator for this: concatMapDelayError since 1.3. In general, if there is a reason errors could be delayed until all sources have been consumed fully, there is likely a opNameDelayError operator for it.
Observable.from(urls)
.concatMapDelayError(url -> downloadObservable(url))
.doOnError(error -> {
if (error instanceof CompositeException) {
System.out.println(((CompositeException)error).getExceptions().size());
} else {
System.out.println(1);
}
});
(The doOnError addendum comes from the updated OP's cross post on the RxJava issue list.)
If you are using RxJava 1, a quick and dirty solution is to return null when the download fails and then filter them out:
Observable
.from(urls)
.concatMap(url -> downloadObservable(url).onErrorReturn(null))
.filter(result -> result != null)
A nicer solution would be to create a wrapper for the result having a method like wasSuccessful() for checking in the filter and a method like getResult() for extracting the result from the wrapper. This way you don't have to handle nulls.
According to: https://github.com/ReactiveX/RxJava/issues/3870 there is no way to do this. Of course you can introduce some other error handling, i.e. handle error inside downloadObservable then filter null answers.
You have to think that is a pipeline so, in case you don't want to stop the emission of the pipeline, you have to control the error and return something in order to continue with the next emission.
The only way to use onErrorResumeNext and not stop the emission after that, is if it´s executed in a flatMap
Observable.from(urls)
.flatMap(url -> downloadObservable(url)
.onErrorResumeNext(t -> Observable.just("Something went wrong"))))
You can see an example here https://github.com/politrons/reactive/blob/master/src/test/java/rx/observables/errors/ObservableExceptions.java
I'm working with RxJava and I don't know where the right place to check arguments would be. For example, say I have the following:
public Completable createDirectory(final String path) {
return Completable.create(emitter -> {
final File directory = new File(path);
final boolean createdSuccessfully = directory.mkDirs();
if (createdSuccessfully) {
emitter.onComplete();
} else {
emitter.onError(new IOException("Failed to create directory.");
}
}
}
Would it be better to check for a null path in the root of the method, or at the start of the completable? I'm leaning towards the former, but I'm interested in the pros and cons of both approaches.
I would say: it depends on what you are trying to achieve.
If you check for null-value on method-entry, before Completeable.create, you would throw the NPE/ IllegalArgumentException on calling-thread. This would follow the 'fail fast'-philosophy. Possible pros:
* fails fast on method-invocation
* Exception in callstack of calling-thread
If you check in Completable.create-lambda, it will be invoked on lambda-evaluation (subscription).
Example: Call createDirectory-Method on calling-thread, add subscribeOn and subscribe: NPE / IllegalArgumentException will be thrown on subscribeOn-Scheduler-Thread on subscription.
It will only be thrown, when you subscribe to returned Completable. Maybe you create an Completable but never subscribe to it. Then no exception will be thrown. This could be a pro for checking in lambda. If you would not check, a NPE would be thrown anyways on subscribeOn thread. Exception thrown on subscribeOn thread could be negative, because you only see the trace for subscribedOn-thread. Sometimes it is not easy to see the flow, when switching threads with only on callstack. You would not see that createDirectory-method was invoked. You would only see the lambda invocation plus some reactive-stack-polution.
I would say: use fail fast, if you subscribe to created Completable at some point. If it is possible, that no subscription to created Completable happens at any time, it would probably use some Precondition with a message, because it will only throw, when subscribed to (lazy). Anyways, if you check with Objects#requireNonNull() without any message in the lambda, you could just discard checking for null, because the NPE will be thrown anyway.