Issue
I'm currently making my first ever reactive project with Spring WebFlux. Unfortunately, I already got a task assigned that I cannot seem to find a solution for. I would like to receive a Mono and perform operations on it, depending on what other Monos complete. My idea goes like this:
private Mono<ResourceModel> setResourcesIfExisting(String resourceName) {
return resourceService.findByFilter(resourceName)
.flatMap(res -> service1.findByFilter(resourceName).map(res::setResource))
.flatMap(res -> service2.findByFilter(resourceName).map(res::setResource))
.flatMap(res -> service3.findByFilter(resourceName).map(res::setResource))
;
}
A Mono will be found by the resourceService, note that this is already the same type that I want to return. But now, I have some other services that deliver resources and in therory there should be exactly one fulfilling resource mono across all of those services.
From my tests, it always works if I use one of those flatMaps but as soon as I have two or more consecutively the list that gets collected on the higher level method results empty (I am using the above method for each element of a Flux<String>
).
Would someone be able to give me a pointed why this happens? As said above, I barely understand what I'm doing but tried out a lot with no success so far.
Solution
The reason for the mentioned output is due to this.
- Let's assume you gets
Mono<ResourceModel>
fromresourceService.findByFilter(resourceName)
which is not empty. - But after the first
.flatmap
by callingservice1.findByFilter(resourceName)
it may return empty. - After returning empty, other
flatmap
s are not called as no element will be returned asflatmap
is called upon each element.
Alternatively you can use the following to achieve what you expected
private Mono<ResourceModel> setResourcesIfExisting(String resourceName) {
return resourceService.findByFilter(resourceName)
.flatMap(res -> service1.findByFilter(resourceName)
.switchIfEmpty(service2.findByFilter(resourceName))
.switchIfEmpty(service3.findByFilter(resourceName))
.doOnEach(stringSignal -> res.setResource(stringSignal.get()))
.thenReturn(res));
}
thenReturn
will return the ResourceModel
even if all the service1,2,3 return empty.
Answered By - ashanshamika