Await response and use in another method - java

I need to make two calls to External APIs. In the first one, get a response to use it and invoke another API. I'm creating threads at the time of subscription but when debugging I don't get a correct response from the first API. I should be getting integers greater than 0, but I'm getting 0, indicating that nothing was created in the database.
public class MyApi {
// call first api
public Maybe<ResponseOne> crearOne(OneRequest OneRequest){
return oneApi.createOne(..)
.subscribeOn(Schedulers.io())
.doFinally(...)
.doOnError(...);
}
// call second api
public Completable crearTwo(...){
return TwoApi.createTwo(..)
.subscribeOn(Schedulers.io())
.doFinally(...)
.doOnError(...);
}
// call two methods
public Maybe<MyApiResponse> create(..){
return createOne(..)
.flatMap(response -> Maybe.fromCompletable(
createTwo(response.getValue(), ..) // need response values
))
.map(
// ..
// build MyApiResponse
);
}
}

Related

Call Mono after first Mono is done and return result of first Mono

I want to perform two business operations, in a Webflux environment, in a way that the second operation happens only after the first one is succesfull. After the second one is done, I want to return the result of the first operation. The second operation calls a org.springframework.web.reactive.function.client.WebClient. This is what I have tried until now:
public Mono<ResponseEntity<Resource>> callOperations(){
return service.operation1()
.flatMap(resource -> {
service.operation2();
return resource;
})
.map(ResponseEntity::ok);
}
I also tried with then and subscribe but I can't get the webclient to perform the call and return the result of service.operation1. What must I do?
You need to construct a flow using reactive operators and let WebFlux subscribe to it. In your snippet there is no subscription to service.operation2()
public Mono<ResponseEntity<Resource>> callOperations(){
return service.operation1()
.flatMap(resource -> {
return service.operation2()
.thenReturn(resource);
})
...
}

How to call Synchronus blocking method inside async method in Java?

I'm Using Project Reactor library. Here is my scenario.
I want to call the blocking service inside my non blocking method.
I have a three different services, I called those three services from my springboot application. Here is my sample code
public Mono<Example> getValuesFromDifferentServices() {
Mono<Example1> mono1=service.getService1();
Mono<Example2> mono2=service.getService2();
mono1.zipwith(mono2)
.map(value-> {
// some logics then
if(value.getT1().getStatus().equals(value.getT2().getStatus())) {
Mono<Example3> mono3 = service.getService3(true);
mono3.map(f-> {
value.getT1().setSomething(f.getSomething);
return f;
}).subscribe();
}
return value.getT1();
})
}
Note: Above example is not the actual logic. But the implementation is similar to that
Even I tried to subscribe() it, I couldn't get the 3rd service value all the time (uncertainty values). I cannot block() the 3rd service since it is not allowed. How to achieve this?
Update: 3rd Service input would be decided after If condition either it should be true or not Mono<Example3> mono3 = service.getService3(true);
We should call the 3rd service if only the condition matches, otherwise calling the 3rd service is not required and which is not advisable., If condition doesn't match, we should not invoke 3rd service.
This example is a little wierd but as I understand, you want to call the first two services, each give you back a single value.
After that you want to call the third one if necessary and set a value from this into one of the first's field.
Anyway, there is a simple solution, but with more information maybe we can create nicer stream. This stream takes adventages of flatMap, which eagerly subscribes into the inner publisher.
[The example was written in Kotlin, it's very like Java. The only confusig thing here maybe the it variable, which is equals something like this: map(it -> it.sg )]
data class Example(
val name: String,
val status: String,
var value: String? = null
)
class ReactorTest {
#Test
fun test() {
val first = Mono.just(Example("first", "suspended"))
val second = Mono.just(Example("second", "suspended"))
val third = Mono.just(Example("third", "suspended", "thirdValue"))
val stream = first.zipWith(second)
.flatMap { tuple ->
Mono.just(tuple.t1)
.filter { it.status == tuple.t2.status }
.zipWith(third)
.doOnNext {
it.t1.value = it.t2.value
}
.map { it.t1 }
.switchIfEmpty(Mono.just(tuple.t1))
}
StepVerifier.create(stream)
.expectNext(Example("first", "suspended", "thirdValue"))
.verifyComplete()
}
#Test
fun test2() {
val first = Mono.just(Example("first", "suspended"))
val second = Mono.just(Example("second", "active"))
val third = Mono.just(Example("third", "suspended", "thirdValue"))
val stream = first.zipWith(second)
.flatMap { tuple ->
Mono.just(tuple.t1)
.filter { it.status == tuple.t2.status }
.zipWith(third)
.doOnNext {
it.t1.value = it.t2.value
}
.map { it.t1 }
.switchIfEmpty(Mono.just(tuple.t1))
}
StepVerifier.create(stream)
.expectNext(Example("first", "suspended"))
.verifyComplete()
}
}
Side note: if you're using blocking services in your reactive streams, those should be separated into dedicated threadpools. Like:
fun blockingService(): Mono<String> {
//real service use fromCallable
return Mono.just("fromCallableOnServiceCall")
//for real service it may use a dedicated pool
.subscribeOn(Schedulers.boundedElastic())
}

How to wait until List<Mono<List<Object>>> finishes?

It's my first time working with webClient and I am wondering how to wait until List<Mono<>> finishes. I have the following code:
List<Address> addresses = collectAllAddresses(someObject);
List<Mono<List<AnotherAddress>>> monoResponses =
addresses.stream()
.map(address -> webClientGateway.findAddresses(userData, address.getFullAddress()))
.collect(Collectors.toList());
Mono.when(monoResponses).block();
log.info("mono responses");
monoResponses.stream()
.flatMap(it -> Objects.requireNonNull(it.block()).stream()).forEach(it -> log.info("mono responses + {}", it));
and the following findAddresses method:
public Mono<List<AnotherAddress>> findAddresses(UserData userData, String fullAddress) {
if (StringUtils.isEmpty(fullAddress)) {
log.info("Address is empty that why we return Mono.just(Collections.emptyList()");
return Mono.just(Collections.emptyList());
}
return webClient.get()
.uri(path, uri -> uri.queryParam("query", fullAddress).queryParam("count", 1).build())
.header("someHeader", someHeader)
.retrieve()
.bodyToMono(new ParameterizedTypeReference<List<AnotherAddress>>() {
})
.doOnError(e -> log.error("Error occurred!", e));
}
but every time I execute it I always get list of empty objects, I mean I get List but every object in that list is empty (every field of class AnotherAddress is null). What can be wrong?
UDP: more explanations:
I have two microservices. In one microservice (that return another address) there is RestController that sends anotherAddress. In another microservice I want to use WebClient (instead of using threadPool with many threads) to call the RestController from previous microservice. When I have previous implementation for function webClientGateway.findAddresses(userData, address.getFullAddress()) and it returns Mono<List> I tested it and immediately after calling function I call block on result and it works. But now I have following situation, I have many addresses (maybe 5 or 10) and I want send async request for every address and wait until latest finishes and after that I want to do another operation, but instead of getting fullfielded AnotherAddress instance, I am getting 5 empty AnotherAddress instances (every field is null)
Use a Flux instead of a Mono, e.g. something like (untested):
public Flux<AnotherAddress> findAddresses(UserData userData, String fullAddress) {
if (StringUtils.isEmpty(fullAddress)) {
log.info("Address is empty that why we return Mono.just(Collections.emptyList()");
return Flux.empty();
}
return webClient.get()
.uri(path, uri -> uri.queryParam("query", fullAddress).queryParam("count", 1).build())
.header("someHeader", someHeader)
.retrieve()
.bodyToFlux(AnotherAddress.class)
.doOnError(e -> log.error("Error occurred!", e));
}
If you don't need the AnotherAddress list grouped by address the you could use something like (untested):
Flux<AnotherAddress> anotherAddressFlux= Flux.fromIterable(addresses)
.flatMap(address -> webClientGateway.findAddresses(userData, address.getFullAddress()));
If you want to block you can use:
List<AnotherAddress> anotherAddressList = anotherAddressFlux.collectList().block();

RxJava2 - Merge values and side effects from a stream

I'm trying to extract some common logic, based on RxJava2, into reusable components. Let's imagine I have the following piece of code:
someSingle
.doOnSuccess { // update UI based on side effect }
.subscribeOn(...)
.observeOn(...)
.subscribe(
value -> // update UI based on value
throwable -> // handle error
)
I want to wrap this into a reusable component, exposing a method that returns a Flowable of events. The clients will receive events and update the UI accordingly. My goal is not to have any reference of the view inside the reusable component. I want the method to be something like this:
fun reusableMethod(...) : Flowable<Event> { ... }
Event is a sealed class, enclosing two sub types - SideEffectEvent and ValueEvent.
What is the best way to transform the stream from the first snippet, so I can get both the side effect and the value to be emitted as flowable values?
Currently, I have the following solution, but I'm not very happy with it, because it looks a bit clunky and complex:
private val sideEffectEvents = PublishProcessor.create<SideEffectEvent>()
fun reusableMethod(...) =
Flowable.merge(
someSingle.doOnSuccess { sideEffectEvents.onNext(SideEffectEvent()) },
sideEffectEvents
)
.subscribeOn(...)
.observeOn(...)
I have also considered some alternatives:
Notify the client for SideEffectEvents using a callback that is passed to someReusableMethod() - looks very unnatural and having a callback and a stream to subscribe to is not a good code style
Use a single PublishProcessor. Post side effects to it and use it to subscribe to the original Single. Expose a cleanUp() method in the reusable component so the client can dispose of the stream when it decides to.
I'm looking forward to suggestions and ideas.
First of all it doesn't have to be a Flowable. It can be a simple Observable. But the below solution should work in both cases. read more here Observable vs Flowable
This code is not tested, I have written it to give you a simplified idea about how you can achieve this.
// a sealed class representing current state
sealed class ViewState {
object Loading : ViewState() // using object because we do not need any data in cass of loading
data class Success(val data: List<Model>) : ViewState()
data class Error(val t: Throwable) : ViewState()
}
// an observalbe or flowable returning a single object ViewState
// it will always return ViewState class containing either data or error or loading state
return service.getData()
.map { data -> ViewState.Success(data) } // on successful data fetch
.startWith(ViewState.Loading()) // show loading on start of fetch
.onErrorReturn { exception -> ViewState.Error(exception) } // return error state
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
// somewhere in Activity or in multiple activities subscribe to above observable
subscribe({ viewState ->
when {
viewState.Loading -> showProgressView()
viewState.Error -> showErrorView(viewState.t)
viewState.Success -> showData(viewState.data)
else -> IllegalArgumentException("Invalid Response")
}
})
How about this:
Before:
someSingle
.operation1()
.operation2()
.doOnSuccess { // update UI based on side effect }
.operation3()
.operation4()
.subscribeOn(...)
.observeOn(...)
.subscribe(
value -> // update UI based on value
throwable -> // handle error
)
Reusable:
fun reusableMethod(...): Flowable<Event> =
someSingle
.operation1()
.operation2()
.flatMapPublisher {
Single.concat(
Single.just(getSideEffectEvent(it)),
Single.just(it)
.operation3()
.operation4()
.map { value -> getValueEvent(value) }
)
}
.subscribeOn(...)
.observeOn(...)
You can further simplify this using Flowable#startWith, and avoiding Single#concat()

How to return a status code in a Lagom Service call

I want to implement a typical rest POST call in Lagom. The POST creates an object, and returns it, with a status code of 201.
However, the default return code is 200. It is possible to set a status code, as shown here (https://www.lagomframework.com/documentation/1.3.x/java/ServiceImplementation.html#Handling-headers).
However, I cannot figure out how to do it for a more complicated case. My create is asynchronious, and I return an object instead of a String.
This is the code I have:
#Override
public HeaderServiceCall<OrderRequest.CreateOrderRequest, Order> createOrder() {
UUID orderId = UUID.randomUUID();
ResponseHeader responseHeader = ResponseHeader.OK.withStatus(201);
return (requestHeader, request) -> {
CompletionStage<Order> stage = registry.refFor(OrderEntity.class, orderId.toString())
.ask(buildCreateOrder(orderId, request))
.thenApply(reply -> toApi(reply));
return CompletableFuture.completedFuture(Pair.create(responseHeader, stage.toCompletableFuture()));
};
}
However, the return value should be Pair<ResponseHeader, Order>, not Pair<ResponseHeader, CompletionStage<Order>> which I have now, so it does not compile.
I could of course extract the Order myself, by putting the completionStage into an CompletableFuture and getting that, but that would make the call synchronous and force me to deal with InterruptExceptions etc, which seems a bit complex for something that should be trivial.
What is the correct way to set a status code in Lagom?
You almost have it solved. Instead of creating a new completedFuture you could compose stage with a lambda that builds the final Pair like this:
return stage.thenApply( order -> Pair.create(responseHeader, order));
And putting all the pieces together:
registry.refFor(OrderEntity.class, orderId.toString())
.ask(buildCreateOrder(orderId, request))
.thenApply( reply -> toApi(reply));
.thenApply( order -> Pair.create(responseHeader, order));

Categories

Resources