How Convert to Rx java Observable - java

I am trying to convert Void and Response type to Observable corresponding. I tried .just but .create not sure if I can use just or create to do this conversion. Thanks.
void getSomeValue(){
Observable<Response> returnedObservable=getResponse();//How to convert this from Response to Observable<Response>
Observable<Void> returnedObservable=doSomething();//How to convert this from Void to Observable<Void>
}
Void doSomething(){
//some code...
return null;
}
Response getResponse(){
//some code....
return someResponse;
}

Small note: RxJava provides several types of observables (see here). For this case, there is no reason for returning an Observable<Void>, you can use a Completable: in fact, it represents a deferred computation without any value.
You can use the method fromCallable: this defers the execution of getResponse function until the Observable is subscribed. The eager approach consists to use just: it evaluates the function immediately in the current thread.
Observable<Response> response = Observable.fromCallable(this::getResponse);
Observable<Response> response = Observable.just(getResponse());
Same for Completable:
Completable something = Completable.fromAction(this::doSomething);

Related

how to run methods and check for final result in spring WebFlux without transforming

I have a method called fluxFromFileStream that returns a Flux of String. after handling those string and doing some DTO operations, I have to save them into a mongodb database using methods transformAndSaveKpis(kpiHdfsDtoFlux) and transformAndSaveReports(kpiHdfsDtoFlux).
public void transformAndSaveKpisAndReports(InputStream inputStream, DQSCJobName jobName) {
fluxFromFileStream(inputStream)
.flatMap(this::buildDTOFromLine)
.map(kpiHdfsDto -> changeKpiTypeToFreezeIfJobNameIsFrozen(jobName, kpiHdfsDto))
.cache()
.transform(
kpiHdfsDtoFlux -> Flux.zip(transformAndSaveKpis(kpiHdfsDtoFlux), transformAndSaveReports(kpiHdfsDtoFlux))
);
}
The problem with that is the method .transform() is returning a value of flux that I will not need. what I need actually is to verify if the reactive stream has been achieved successfully without any problem, else throw an exception inside my main method (transformAndSaveKpisAndReports).
Before, I was checking the result of the whole stream (including .transform) if it's null then throw an exception, but appears to me that's not really the clean way to do things.
bellow are the methods I'm calling inside the transform method:
private Flux<Kpi> transformAndSaveKpis(Flux<KpiHdfsDto> kpiHdfsDtoFlux) {
return kpiHdfsDtoFlux
.map(this::kpiHdfsDtoToKpiDocument)
.collectList()
.flatMapMany(kpis -> kpiRepository.insertAll(kpis));
}
private Flux<Report> transformAndSaveReports(Flux<KpiHdfsDto> kpiHdfsDtoFlux) {
return kpiHdfsDtoFlux
.flatMap(this::kpiHdfsDtoToReportDocument)
.groupBy(Report::getType)
.flatMap(reportList -> reportRepository.insertAll(reportList.collectList()));
}

Spring Webflux - "scanAvailable": true

I'm using a RouterFunction to define endpoints in my Spring Boot application. My service returns a Mono<Object> and I want to return the result of this when the endpoint is called. I also need to authenticate so I pass a UserPrinciple object through.
Router
#Bean
RouterFunction<ServerResponse> router() {
return route()
.GET("/api/endpoint-name", this::getExample)
.build();
}
private Mono<ServerResponse> getExample(ServerRequest request) {
return ServerResponse.ok().body(fromPublisher(getUserPrincipal().map(service::getSomething), Object.class)).log();
}
private Mono<UserPrincipal> getUserPrincipal() {
return ReactiveSecurityContextHolder.getContext()
.map(ctx -> ctx.getAuthentication())
.map(auth -> auth.getPrincipal())
.map(UserPrincipal.class::cast);
}
Service
public Mono<Object> getSomething(UserPrincipal userPrincipal) {
WebClient webClient = getWebClient(userPrincipal.getJwt());
return webClient.get()
.uri(uriBuilder -> uriBuilder.path("another/server/endpoint").build())
.retrieve()
.bodyToMono(Object.class);
}
The endpoint is returning this:
{
"scanAvailable": true
}
which suggests that I'm passing the Mono into the body of the response instead of passing in the result. However I've used fromPublisher which I thought would resolve this.
I can't find any examples where the service returns a Mono and the route correctly returns the result of the Mono.
How can I correctly pass a Mono/Flux as the body of the response?
im not going to explain the difference between mapand flatMapsince i have already written a quite comprehensive explanation here:
Do you have a test to show differences between the reactor map() and flatMap()?
The problem in the above code is the return of Object. And input parameters of Object into certain functions. The first function is pretty straight forward
Mono<UserPrincipal> = getUserPrincipal();
While the second one gets a bit more hairy:
Mono<Mono<Object> value = getUserPrincipal().map(service::getSomething);
So why are we getting A nested Mono?, well the get something returns a Mono<Object> and the Map return according the the api is Mono<R> where R is what we return from getSomething.
We then stick it into the fromPublisher which will unrap the first Mono ending up trying to serialize the Mono<Object>resulting in the strange response.
{
"scanAvailable": true
}
The answer here is pay more close attention to the type system. The body function takes a Publisher (Mono or Flux) so you don't need the fromPublisher function.
And also changing map to flatMap since the return type from inside a flatMap is a publisher.
ServerResponse.ok()
.body(getUserPrincipal()
.flatMap(service::getSomething), Object.class));

Conditional memoization in Guava

I know how to memoize a single object. However, I'd like to memoize only if some condition is met. I'm calling a service that sometimes returns a response that is not successful. I'd like to memoize only if the service's response if successful.
MyResponse myResponse = myService.call()
boolean success = myResponse.isSuccessful();
And my cache is created like so:
private Supplier<MyResponse> cache;
private void createCache() {
this.cache = Suppliers
.memoizeWithExpiration(myService::call, timeout,
TimeUnit.MINUTES);
}
Question: Is it possible to somehow cache the response only if the response is successful using the Supplier passed to the memoizeWithExpiration method?
The only workaround I found to do this is to, when retrieving the value, call cache.get() first, check if the object stored in cache is successful, and if it's not, call createCache() again to clear it and then get the value again. This way if the subsequent service call returns a valid object, it will get stored, and if not, every subsequent call will clear the cache and call the service again.
public MyResponse getResponse() {
MyResponse myResponse = cache.get();
if (myResponse.isSuccess()) {
return myResponse;
} else {
createCache();
return cache.get();
}
}
However, in this solution, if the cache is empty and the service returns unsuccessful response, it will get called again immediately.
You can create a method callUntilSuccess in Service class or in any other suitable place (here I'm assuming it is in your Service). You could also define a maximum number of tries in this method and after that it will return null, so you could avoid calling your service indefinitely (this suggestion isn't implemented in the code supplied bellow but it is very easy to do so). As the Guava method expects a Supplier, you can even create a lambda with this logic and pass it directly to the memoizeWithExpiration method.
public MyResponse callUntilSuccess() {
MyResponse response = myService.call();
while (!response.isSuccessful()) {
response = myService.call();
}
return response;
}
Then do the memoization in this way:
private void createCache() {
this.cache = Suppliers
.memoizeWithExpiration(myService::callUntilSuccess, timeout,
TimeUnit.MINUTES);
}
Could this be what you are looking for?
private void createCache() {
this.cache = Suppliers.memoizeWithExpiration(
Suppliers.compose(
response -> (response.isSuccess() ? response : null),
myService::call
),
timeout,
TimeUnit.MINUTES
);
}
Here, it will cache the response, or null, depending on whether it was successful.
More info on compose here https://github.com/google/guava/blob/master/guava/src/com/google/common/base/Suppliers.java#L45
EDIT:
If you need to cache the value on success, and leave the cache empty on failure, while returning the failed request, then you are almost there yourself, just change return logic a bit in getResponse, like this:
public MyResponse getResponse() {
final MyResponse myResponse = cache.get();
if (!myResponse.isSuccess()) {
this.createCache(); // clear cache
}
return myResponse; // don't call .get() again!
}

How run one Single after another using source from first

I have 2 Single. In first I receive token and in second I need use it, and then I must save some info from second single and return completable.
I thought do this with completable and andThen, like this:
String token;
IStoreProvider storeProvider;
IWebProvider webProvider;
public Completable getUserInfo(){
return Completable.fromSingle(Completable
.fromSingle(storeProvider
.getToken()
.doOnSuccess(x->token=x))
.andThen(webProvider.getUserInfo(token)
.doOnSuccess(x->storeProvider.saveUserInfo(x)));
interface IStoreProvider{
Single<String> getToken();
Completable saveUserInfo(UserInfo userInfo);
}
interface IWebProvider{
Single<UserInfo> getUserInfo(token);
}
But its dosen't work. How I can do it?
Your stream is created before its executed, so during creation token=null and therefore you will get null in getUserInfo.
flatMap will help you.
public Completable getUserInfo() {
return storeProvider.getToken()
.flatMap(token -> webProvider.getUserInfo(token))
.flatMapCompletable(userInfo -> storeProvider.saveUserInfo(userInfo));
}

RxAndroid - execute another request based on a specific result?

i am using RxAndroid/RxJava for the first time and trying to figure out how to implement a chain of requests but each next request made is dependent on the result of the other.
example:
private Boolean isUserEligible(){
..
}
private String registerDevice()
..
}
private String login(){
..
}
As far as i know, the Observable can only execute all of the above methods or one by one like below:
// Fetch from both simultaneously
Observable<String> zipped
= Observable.zip(isUserEligible(), registerDevice(),login(), new Func2<String, String, String>() {
});
Observable<String> concatenated = Observable.concat(isUserEligible(), registerDevice(),login());
what if i want to do something like this
//execute usUserEligible first and if eligible, execute registerDevice, else execute login().
Thanks in advance
Assuming all of these methods return observables, you could write:
Observable<String> response = isUserEligible()
.flatMap(isEligible -> isEligible ? registerDevice() : login());
Without retro-lambda; you could write:
Observable<String> response = isUserEligible()
.flatMap(new Func1<Boolean, Observable<String>>() {
public Observable<String> call(final Boolean isEligible) {
return isEligible ? registerDevice() : login();
}
});
This is a use case for a flatmap.
http://reactivex.io/documentation/operators/flatmap.html
Create the mapping from the first result to a second observable, here you can use the result of the first function to input it into the second.
final Func1<Boolean, Observable<String>> registerFunc = isEligible -> {
return registerDevice(isEligible)
};
Now you have to create your chain of calls and flatMaps: do the first call, and flatmap the resulting Observable with the function you just created. This will again return an Observable. you can keep chaining it here with other flatmaps
isUserEligible().flatMap(registerFunc);
Be aware that all your functions need to return Observables to make this possible.

Categories