I have two Mono
s that either return a 404
or yield a result
.
How can I combine these two Mono
s that
- the
result
is emitted as soon as one theMono
s completes successfully. - an
Optional.empty
is returned as soon as allMono
s yield a404
or face any other error?
I tried
Mono<Result> mono0 = client.get()
.uri(uri1)
.retrieve()
.bodyToMono(Result.class)
.onErrorResume(e -> Mono.never());
Mono<Result> mono1 = client.get()
.uri(uri2)
.retrieve()
.bodyToMono(Result.class)
.onErrorResume(e -> Mono.never());
return Mono.first(mono0, mono1)
.blockOptional()
The problem with this approach is that it never completes if both Mono
s yield an error...
I think I need something like OnErrorDetach
....!?
To make the problem clearer I created a TestCase:
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
class DummyTest {
public Optional<String> getResult(Mono<String> m1, Mono<String> m2) {
return Mono.first(m1, m2)
// DoSomethingHere ?!??!?
.blockOptional();
}
@Test
void testFirstSuccessfullAndSecondErrorMono() {
Optional<String> result = getResult(Mono.just("Something"), Mono.error(new RuntimeException()));
assertThat(result, is(Optional.of("Something")));
}
@Test
void testSecondSuccessfullAndFirstErrorMono() {
Optional<String> result = getResult(Mono.error(new RuntimeException()), Mono.just("Something"));
assertThat(result, is(Optional.of("Something")));
}
@Test
void testTwoErrorMonosYieldEmpty() {
Optional<String> result =
getResult(Mono.error(new RuntimeException()), Mono.error(new RuntimeException()));
assertThat(result, is(Optional.empty()));
}
}
The problem with Mono.first()
is that it takes the first signal (not the first value), so you end up trying to delay the corresponding Mono
forever to get it not to replay the completion signal. You instead want something to take the first value.
Instead of .onErrorResume(e -> Mono.never());
, use .onErrorResume(e -> Mono.empty());
. You can then use:
Flux.merge(mono0, mono1).next();
The merge()
(as oppose to concat()
) combined with next()
should ensure the first value is taken and the other ignored. You can of course still blockOptional()
if you wish.