How to get an element that caused an exception in Flux? - java

Let's say I have an array of ids: [9, 8, 7, 6].
I do some processing and one element causes to throw an exception. I want to handle this situation on my own way (let's say log it) and let the other elements go with the flow.
How can I know which one was it? I need to have this element in my onError processing.
Flux.fromArray(myArray)
.flatMap(element -> {
var foo = processMyEl(element);
return anotherProcess(foo); // this returns Mono
})
.onErrorOperator(element -> handleMyError(element)) // this line is what I need
So, what I saw, there's this almost nice .onErrorContinue((error, obj) -> that emits an error and an object.
But this obj is not the element that caused the exception but the object that did so. It happens inside of my processing methods and it doesn't have to be the same type of object every time.
.onErrorReturn(...) - not really what I want
.doOnError(error -> - no information of my element
.onErrorResume(error -> - same as above
there were suggestions that I can create my own Exception and pass there the element and then retrieve it from the exception. But how should I throw the exception?
Should I go with an old way of try catch:
Flux.fromArray(myArray)
.flatMap(el -> {
try {
var foo = processMyEl(el);
return anotherProcess(foo); // this returns Mono
} catch (Exception e) {
return Mono.error(new MyException(el));
}
})
.onErrorOperator(error -> handleMyError(error.getElement()))
It doesn't look well
Edit:
Not only it looks bad, but also doesn't work. The exception is not caught at all and triggers directly doOnTerminate() and stops the whole stream
Update:
Thanks to #JEY I used .onErrorResume() inside flatMap.
I also transformed first method to be a reactive stream by Mono.defer(() -> Mono.just(processMyEl(el))).
Just as a note: using Mono.defer() allows me to use onErrorResume since Mono.just() cannot signal errors.
Final code looks like this:
Flux.fromArray(myArray)
.flatMap(element -> Mono.defer(() -> Mono.just(processMyEl(element)))
.onErrorResume(th -> handleMyError(element, th))
)
.flatMap(foo -> anotherProcess(foo)
.onErrorResume(th -> handleMyError(foo, th)
)
Where:
private Mono<> handleMyError(el, th) {
// handling code
return Mono.empty()
}

As requested by #Kamil I'll add my comments as an answer:
You should just handle the error in the flatMap and return a Mono.empty() to discard it do something like:
Flux.fromArray(myArray)
.flatMap(el -> anotherProcess(processMyEl(el)).onErrorResume(th -> handleError(th, el))
With handle error like:
Mono<Void> handleError(Throwable th, Object element) {
LOG.error("An error occurred on {}", element, th);
return Mono.empty()
}
Or if you want to do something more complex that require async:
Mono<Void> handleError(Throwable th, Object element) {
return doSomethingThaReturnFluxOrMono(element).then();
}

} catch (Exception e) {
throw new MyException(el, e);
}

Related

How can catch MonoError?

I need to catch MonoError and stop an application with ErrorResponse, but the application works as I did not expect.
My code:
return checkText(text)
.then(getWordsFromText(text))
.map(keyWords -> new SuccessfulResponse(keyWords))
.onErrorResume(
throwable -> {
return Mono.just(new ErrorResponse(throwable.getMessage()));
});
public Mono<Void> checkText(String text) {
if (text == null) {
return Mono.error(new Exception("wrong text"));
}
return Mono.empty();
}
my problem is that if text param is null -> I fall into getWordsFromText method. This is an incorrect execution, because if the text parameter is equal to null, then the application must exit with an error (with ErrorResponse).
I fixed it as (replacing 'then' to 'flatMap'):
return checkText(text)
.flatMap(voidParam -> getWordsFromText(text)) //replaced 'then' to 'flatMap'
.map(keyWords -> new SuccessfulResponse(keyWords))
.onErrorResume(
throwable -> {
return Mono.just(new ErrorResponse(throwable.getMessage()));
});
and now it's working correctly. If text param is null I miss the call getWordsFromText method and fall in error handling (onErrorResume).
But I think using flatMap in my case is not a good idea, I don't like how it looks: .flatMap(voidParam -> ...
Can you have any ideas how possible to do better? (without 'flatMap')
In the first snippet, the call to getWordsFromText() is made while building your main reactive pipeline, before it is even subscribed to (i.e. at assembly time). The reason it works as intended in the second snippet is that flatMap only creates the inner publishers (and subsequently subscribes to them) as it receives elements from upstream (i.e. at subscription time).
In this case if you want to replace the flatMap you could try this: .then(Mono.fromCallable(() -> getWordsFromText(text)))

Validation and throwing an exception inside a Flux is not working

I am trying to validate the values of a list using a reactor.core.publisher.Flux inside a try catch, but when map throws the exception the catch doesn't catch it at all. I don't really understand what's happening here. Some help would be appreciate.
This is exactly what I am trying to do:
public Flux<Something> execute(final List<Line> lines) {
try {
return this.getFlux(lines)
.map(line -> this.validateLine(line))//this throws my custom exception if the condition applies
.map(line -> this.doSomething(line))
.map(line -> this.doSomethingElse(line));
} catch (myCustomException e) {
return something;
}
}
I can see the validate method works well and throws the exception by debugging but the catch doesn't seem to be working and I can't see what is wrong.
You would need a terminal operation applied onto the end of the stream. Streams are evaluated lazily.

Preventing RxJava from wrapping my custom exception into composite one

I was given a task to make implementation using RxJava, and safeguard in such way, that if any error happens, it gets wrapped into custom exception.
Problem is, that regardless of what I do RxJavaPlugin decides to wrap my exception into CompositeException even when there is only one. Which fails tests.
I've tried everything I could find on the google all the way up to actually overwriting RxJavaPlugin's global error handler, but it ignores attempted changes.
implementation of function that is supposed to throw it at the moment of writing this post
Single<BigDecimal> summarizeConfirmedTransactions() throws SummarizationException {
try{
Observable<Transaction> observableTransactions = transactions.get()
.doOnError(throwable -> {
Exceptions.propagate(throwable);
});
Observable<Confirmation> observableConfirmations = confirmations.get()
.doOnError(throwable -> {
Exceptions.propagate(throwable);
});
return Observable.zip(observableTransactions, observableConfirmations, (t, c) -> new ConfirmableTransaction(t.transactionId, c.isConfirmed, t.value))
.filter(confirmableTransaction -> confirmableTransaction.isConfirmed)
.map(t -> t.value)
.reduce(BigDecimal::add)
.toSingle()
.doOnError(throwable -> {
Exceptions.propagate(throwable);
});
}catch(Exception e)
{
throw new SummarizationException(e.getMessage());
}
Assertion in test fails because exception ultimately thrown is CompositeException with single exception inside of it. and I am required to have it be of SummarizationException.class
big thanks in advance.
Edit:
On request, here is code used to test the solution. I am not allowed to modify this one.
#Test
public void shouldWrapErrorIntoException() {
final ConfirmedTransactionSummarizer summarizer =
new ConfirmedTransactionSummarizer(ALL_CONFIRMED::transactions, () -> Observable.error(new RuntimeException("Booom")));
summarizer
.summarizeConfirmedTransactions()
.subscribe(testObserver);
testObserver.assertError(SummarizationException.class);
testObserver.assertErrorMessage("Booom");
}
PS. I've asked the giver of the task, he said that "I'm the only one with such problem" and that I should not overcomplicate things and go for easiest solution.... which results in composite exception of 3 exceptions - one of which is my exception wrap and other two are instances of RuntimeException inserted by test code.
Ok, so after a little bit of more digging, and with helpful tip from a friend I've managed to nail down intention of the task:
What I was supposed to do there was:
Single<BigDecimal> summarizeConfirmedTransactions() throws SummarizationException {
Observable<Transaction> observableTransactions = transactions.get();
Observable<Confirmation> observableConfirmations = confirmations.get();
return Observable.zip(observableTransactions, observableConfirmations,
(t, c) -> new ConfirmableTransaction(c.isConfirmed, t.value))
.filter(confirmableTransaction -> confirmableTransaction.isConfirmed)
.map(t -> t.value)
.reduce(BigDecimal::add)
.toSingle()
.onErrorResumeNext(th -> Single.error(new SummarizationException(th.getMessage())));
}
TL:DR I was not supposed to "wrap" errors into thrown exception but wrap them into error response containing exception.
One way to handle this is by using try-catch block in your test, unwraping CompositeException and then asserting caught exception.
fun testSummarizationException() {
try {
summarizeConfirmedTransactions()
} catch (ex: Exception) {
val customException = (ex as? CompositeException)?.exceptions?.get(0)
// assert to make sure thrown exception is of custom type
assert(customException is SummarizationException)
}
}
This is where CompositeException is unwrapped to get custom exception.
val customException = (ex as? CompositeException)?.exceptions?.get(0)
Exception is type-casted to CompositeException if it's permissible. If casting is not allowed for this type, this will return null and fails the test.

Project Reactor: Designing a reactive API

I have a map function which defined as follows: Mono<OUT> map(IN in)
Here's a concrete example:
public Mono<Integer> map(String s) {
return Mono.fromCallable(() -> {
try {
Thread.sleep(1_000); // simulate HTTP request
return 1;
} catch (Exception e) {}
return -1; // need to return something.
});
}
The problem is that in case of an error (i.e. IOException), we still need to return some output. There's also a possibility that there might no be an answer (but no error occurred)
One solution could be an Optional::empty but I think it's cumbersome. Preferably, I'd like to return Mono::empty if an error occurred.
The reason is, Mono::empty gets consumed by the subscriber without any further handling. Here's an example:
Flux.just(
Mono.just("123"),
Mono.empty(),
Mono.just("456")
).flatMap(s -> s)
.subscribe(System.out::println);
The output would be:
123
456
How can achieve the same behaviour?
What should map look like?
EDIT:
Rethinking it, maybe I better off return some container (like Optional) or a custom one (Result) which can be empty.
If I understand correctly, here's what you need:
return Mono.fromCallable(() -> {
Thread.sleep(1_000); // simulate HTTP request
return 1;
}).onErrorResume(_ -> Mono.empty())

Aggregate runtime exceptions in Java 8 streams

Let's say I have a method which throws a runtime exception. I'm using a Stream to call this method on items in a list.
class ABC {
public void doStuff(MyObject myObj) {
if (...) {
throw new IllegalStateException("Fire! Fear! Foes! Awake!");
}
// do stuff...
}
public void doStuffOnList(List<MyObject> myObjs) {
try {
myObjs.stream().forEach(ABC:doStuff);
} catch(AggregateRuntimeException??? are) {
...
}
}
}
Now I want all items in the list to be processed, and any runtime exceptions on individual items to be collected into an "aggregate" runtime exception which will be thrown at the end.
In my real code, I am making 3rd party API calls which may throw runtime exceptions. I want to make sure that all items are processed and any errors reported at the end.
I can think of a few ways to hack this out, such as a map() function which catches and returns the exception (..shudder..). But is there a native way to do this? If not, is there another way to implement it cleanly?
In this simple case where the doStuff method is void and you only care about the exceptions, you can keep things simple:
myObjs.stream()
.flatMap(o -> {
try {
ABC.doStuff(o);
return null;
} catch (RuntimeException ex) {
return Stream.of(ex);
}
})
// now a stream of thrown exceptions.
// can collect them to list or reduce into one exception
.reduce((ex1, ex2) -> {
ex1.addSuppressed(ex2);
return ex1;
}).ifPresent(ex -> {
throw ex;
});
However, if your requirements are more complicated and you prefer to stick with the standard library, CompletableFuture can serve to represent "either success or failure" (albeit with some warts):
public static void doStuffOnList(List<MyObject> myObjs) {
myObjs.stream()
.flatMap(o -> completedFuture(o)
.thenAccept(ABC::doStuff)
.handle((x, ex) -> ex != null ? Stream.of(ex) : null)
.join()
).reduce((ex1, ex2) -> {
ex1.addSuppressed(ex2);
return ex1;
}).ifPresent(ex -> {
throw new RuntimeException(ex);
});
}
There are already some implementations of Try monad for Java. I found better-java8-monads library, for example. Using it, you can write in the following style.
Suppose you want to map your values and track all the exceptions:
public String doStuff(String s) {
if(s.startsWith("a")) {
throw new IllegalArgumentException("Incorrect string: "+s);
}
return s.trim();
}
Let's have some input:
List<String> input = Arrays.asList("aaa", "b", "abc ", " qqq ");
Now we can map them to successful tries and pass to your method, then collect successfully handled data and failures separately:
Map<Boolean, List<Try<String>>> result = input.stream()
.map(Try::successful).map(t -> t.map(this::doStuff))
.collect(Collectors.partitioningBy(Try::isSuccess));
After that you can process successful entries:
System.out.println(result.get(true).stream()
.map(t -> t.orElse(null)).collect(Collectors.joining(",")));
And do something with all the exceptions:
result.get(false).stream().forEach(t -> t.onFailure(System.out::println));
The output is:
b,qqq
java.lang.IllegalArgumentException: Incorrect string: aaa
java.lang.IllegalArgumentException: Incorrect string: abc
I personally don't like how this library is designed, but probably it will be suitable for you.
Here's a gist with complete example.
Here's a variation on the theme of mapping-to-exceptions.
Start with your existing doStuff method. Note that this conforms to the functional interface Consumer<MyObject>.
public void doStuff(MyObject myObj) {
if (...) {
throw new IllegalStateException("Fire! Fear! Foes! Awake!");
}
// do stuff...
}
Now write a higher-order function that wraps this and turns this into a function that might or might not return an exception. We want to call this from flatMap, so the way "might or might not" is expressed is by returning a stream containing the exception or an empty stream. I'll use RuntimeException as the exception type here, but of course it could be anything. (In fact it might be useful to use this technique with checked exceptions.)
<T> Function<T,Stream<RuntimeException>> ex(Consumer<T> cons) {
return t -> {
try {
cons.accept(t);
return Stream.empty();
} catch (RuntimeException re) {
return Stream.of(re);
}
};
}
Now rewrite doStuffOnList to use this within a stream:
void doStuffOnList(List<MyObject> myObjs) {
List<RuntimeException> exs =
myObjs.stream()
.flatMap(ex(this::doStuff))
.collect(Collectors.toList());
System.out.println("Exceptions: " + exs);
}
The only possible way I can imagine is to map values in a list to a monad, that will represent the result of your processing execution (either success with value or failure with throwable). And then fold your stream into single result with aggregated list of values or one exception with list of suppressed ones from the previous steps.
public Result<?> doStuff(List<?> list) {
return list.stream().map(this::process).reduce(RESULT_MERGER)
}
public Result<SomeType> process(Object listItem) {
try {
Object result = /* Do the processing */ listItem;
return Result.success(result);
} catch (Exception e) {
return Result.failure(e);
}
}
public static final BinaryOperator<Result<?>> RESULT_MERGER = (left, right) -> left.merge(right)
Result implementation may vary but I think you get the idea.

Categories

Resources