In Reactive Streams, the null
value is forbidden. Furthermore, zip
expect that all combined publishers have the same number of elements. Or to put it differently: it short-circuits as soon as one of the publishers completes.
So if you use Mono.empty()
, that Mono
completes immediately and triggers the zip
to complete empty as well.
One possible solution would be to have a "null object" instance of each animal, like this:
public static final Dog NO_DOG = new Dog(...);
public static final Cat NO_CAT = new Cat(...);
public static final Horse NO_HORSE = new Horse(...);
Mono<Dog> dogMono = (condition1) ? Mono.just(dogApliClient.getDog()) : Mono.just(NO_DOG);
Mono<Cat> catMono = (condition2) ? Mono.just(catApliClient.getCat()) : Mono.just(NO_CAT);
Mono<Horse> horseMono = (condition3) ? Mono.just(horseApliClient.getHorse()) : Mono.just(NO_HORSE);
Mono.zip(dogMono, catMono, horseMono)
.map(Animals::fromDogCatAndHorse);
Map<String, Object> fromDogCatAndHorse(Tuple3<Dog, Cat, Horse> tuple) {
Map<String, Object> forJson = new HashMap<>(3);
Dog dog = tuple.getT1();
if (dog = NO_DOG) json.put("dog", null); else json.put("dog", dog);
Cat cat = tuple.getT2();
if (cat = NO_CAT) json.put("cat", null); else json.put("cat", cat);
Horse horse = tuple.getT3();
if (horse = NO_HORSE) json.put("horse", null); else json.put("horse", horse);
return forJson;
}
If you can't define these null object instances, then the solution from @yossarian would work too.
Note that there's still a big issue with the api client calls, though: the Mono.just(apiClient.blockingCall())
pattern.
Here you're essentially shoehorning a blocking call inside what is supposed to be a non-blocking controller...
Ideally these clients would return a Mono<Dog|Cat|Horse>
to reflect a non-blocking nature.
As an example, with a proper non-blocking API, dogMono
can be initialized like this:
Mono<Dog> dogMono = (condition1) ? dogApiClient.getDogAsync() : Mono.just(NO_DOG);