Transpose from Consumer to CompletableFuture - java

I'm currently using an API which I unfortunately cannot change easily. This API has some methods in the style of this:
public void getOffers(Consumer<List<Offer>> offersConsumer) {
final Call<List<Offer>> offers = auctionService.getOffers();
handleGetOffers(offersConsumer, offers);
}
It's a web api using retrofit, and it enables me to process the response in a consumer, but I much rather want to work with CompletableFutures.
I'm using the data I receive from this endpoint to compose an interface in a game, and therefore compose an inventory, that basically acts as a frontend to the api. What I want to do, is to have my composing method to wait for the consumer to finish, and then provide the processed results. This is what I have so far, but I don't know how to do the step from the consumer to the CompletableFuture:
#Override
public CompletableFuture<Inventory> get(Player player) {
return CompletableFuture.supplyAsync(() -> {
auctionAPI.getOffers(offers -> {
//process the offers, then return the result of the processing, in form of an "Inventory"-Object.
}
});
});
}
I now need to return the result of the processing after all the Items have been received and then processed. How can I achieve this?

Something along the lines should work:
#Override
public CompletableFuture<Inventory> get(Player player) {
CompletableFuture<Inventory> result = new CompletableFuture<>();
CompletableFuture.supplyAsync(() -> {
auctionAPI.getOffers(offers -> {
//process the offers, then return the result of the processing, in form of an "Inventory"-Object.
result.complete(inventory);
}
});
return null;
});
return result;
}

Related

How detect all the asynchronous operations are finished on Retrofit like Promise in Javascript?

I have an application that makes dynamic requests by Retrofit and i want to detect if all have finished with the best practice like I know how to make in javascript with Promise.
In Retrofit today I'm doing like this, I receive an array from objects and i will make x request depending on the size of the array.
So when I start the function I pick the size from an array and every time my Retrofit makes a call I put in my variable successCounter++ and errorCounter++, when sum from this 2 variables is equal my array size, so this is the end of the asynchronous functions.
But I don't know if this is a good practice an example from my peace of code:
String filePath = system.getMp_user_name() + "/categorias/" + mpCategory.getImg();
downloadImage("category", filePath, mpCategory.getImg(),
new SynService.ApiImageCallback() {
public void onResponse(Boolean success, Integer requestCounter, Integer errorRequestCounter){
if(success){
categoryImgSuccessCounter++;
Log.d(TAG, "Imagem baixada e armazenada com sucesso");
if(categoryImgSuccessCounter.equals(arrayCategorySize)) {
HashMap<String, String> responseObj = new HashMap<>();
responseObj.put("success", "1");
responseObj.put("message", "Sincronização Completada com Sucesso");
callback.onResponse(responseObj);
}
} else {
categoryImgErrorCounter++;
Log.d(TAG, "Não foi possível fazer o download da imagem");
HashMap<String, String> responseObj = new HashMap<>();
responseObj.put("success", "0");
responseObj.put("message", "Houve um erro no download das imagens e o processo parou");
callback.onResponse(responseObj);
}
Integer total = categoryImgSuccessCounter + categoryImgErrorCounter;
if(total.equals(arrayCategorySize)) {
categoryImgFinished = true;
}
}
});
How can I detect when all the request from Retrofit is finished without a counter?
In javascript is just this:
async function foo(things) {
const results = [];
for (const thing of things) {
// Good: all asynchronous operations are immediately started.
results.push(bar(thing));
}
// Now that all the asynchronous operations are running, here we wait until they all complete.
return baz(await Promise.all(results));
}
Use the Interceptorof Retrofit to intercept each request and do the counter increment/decrement and shares the callback on the basis of that.
Interceptors are a powerful mechanism that can monitor, rewrite, and retry calls.
Interceptor - How to?
If it's okay to add one dependency then add RxJava and it has good number of operator which are useful for multithreading and it's callback.
To wait until all your requests will be done is to use Retrofit2 in conjunction with RxJava2with its zip function.
Zip combine the emissions of multiple Observables together via a specified function and emit single items for each combination based on the results of this function
Add RxJava2CallAdapterFactory as a Call adapter when building your Retrofit instance:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
Your service methods can now use any of the above types as their return type.
interface MyService {
#GET("/user")
Observable<User> getUser();
}
MyService retrofitApi = retrofit.create(MyService.class);
Use that service to create the collection of request and zip those requests.
// You need to use `Observable` type for the response.
List<Observable<?>> requests = new ArrayList<>();
// Make a collection of all requests
requests.add(retrofitApi.getUser());
requests.add(retrofitApi.getUser());
requests.add(retrofitApi.getUser());
// Zip all requests with the Function, which will receive the results.
Observable.zip(
requests,
new Function<Object[], Object>() {
#Override
public Object apply(Object[] objects) throws Exception {
// Objects[] is an array of combined results of completed requests
// do something with those results and emit new event
return new Object();
}
})
// After all requests had been performed the next observer will receive the Object, returned from Function
.subscribe(
// Will be triggered if all requests will end successfully (4xx and 5xx also are successful requests too)
new Consumer<Object>() {
#Override
public void accept(Object o) throws Exception {
//Do something on successful completion of all requests
}
},
// Will be triggered if any error during requests will happen
new Consumer<Throwable>() {
#Override
public void accept(Throwable e) throws Exception {
//Do something on error completion of requests
}
}
);

Equivalent of VertX CompositeFuture in RxJava

The VertX example for when you need to query multiple asynchronous resources and use them all in a single operation is:
Future<HttpServer> httpServerFuture = Future.future();
httpServer.listen(httpServerFuture.completer());
Future<NetServer> netServerFuture = Future.future();
netServer.listen(netServerFuture.completer());
CompositeFuture.all(httpServerFuture, netServerFuture).setHandler(ar -> {
if (ar.succeeded()) {
// All servers started
} else {
// At least one server failed
}
});
We need to query two different databases and then use the results in business logic, but the flow is equivalent.
What's the VertX/RxJava equivalent?
Currently people are doing this by nesting a new .flatMap() call every time they need a new variable. I'm left feeling there must be a better way...
We don't actually need the queries to be concurrent but we need to cache both results and pass them to the business logic at the same time some how.
there are many ways to do this, but i've tried to pick an approach that tacks closely to your sample:
#Override
public void start(Future<Void> startFuture) throws Exception {
final HttpServer httpServer = vertx.createHttpServer();
final Completable initializeHttpServer = httpServer.rxListen().toCompletable();
final NetServer netServer = vertx.createNetServer();
final Completable initializeNetServer = netServer.rxListen().toCompletable();
initializeHttpServer.andThen(initializeNetServer)
.subscribe(
() -> { /* All servers started */ },
error -> { /* At least one server failed */ }
);
}
the rxListen() invocations are converted into Completable instances, which are then run serially upon subscription.
the subscriber's onComplete callback will be invoked when both servers are done binding to their respective ports, or...
the onError callback will be invoked if an exception occurs
(also, fwiw, "nesting" flatMap operations for something as trivial as this shouldn't be necessary. "chaining" such operations, however, would be idiomatic usage).
hope that helps!
--UPDATE--
having read the question more carefully, i now see that you were actually asking about how to handle the results of two discrete asynchronous operations.
an alternative to flatMap'ing your way to combining the results would be to use the zip operator, like so:
#Override
public void start(Future<Void> startFuture) throws Exception {
final Single<String> dbQuery1 = Single.fromCallable(() -> { return "db-query-result-1"; });
final Single<String> dbQuery2 = Single.fromCallable(() -> { return "db-query-result-2"; });
Single.zip(dbQuery1, dbQuery2, (result1, result2) -> {
// handle the results from both db queries
// (with Pair being a standard tuple-like class)
return new Pair(result1, result2);
})
.subscribe(
pair -> {
// handle the results
},
error -> {
// something went wrong
}
);
}
per the docs, zip allows you to specify a series of reactive types (Single, Observable, etc) along with a function to transform all the results at once, with the central idea being that it will not emit anything until all the sources have emitted once (or more, depending on the reactive type).

RxJava: How can I wrap a multi-step operation, returning the step complete to observers?

Lets say I have a login and user data method, representing HTTP calls:
Single<LoginResponse> login();
Single<UserData> userData();
I need to call login() then userData(). If both succeed, the user is logged in.
I know how to wrap them up in a e.g. Completable:
Completable performLogin() {
login().doOnSuccess(this::storeLoginResponse)
.flatMap(userData())
.doOnSuccess(this::storeUserData)
.doOnError(this::wipeLoginData)
.toCompletable();
}
So the UI then says
showLoading();
performLogin().subscribe(() -> {
stopLoading();
onLoginSuccess();
}, error -> {
stopLoading();
onLoginFailure();
});
What if the UI needs to show which stage of the loading is happening? As in, when the login() call completes and the userData() call starts it will change the UI?
What I thought of is something like
Observable<LoginStage> performLogin() {
return Observable.create(emitter -> {
login.doOnSuccess(response -> {
storeLoginResponse(response)
emitter.onNext(LOGIN_COMPLETE)
}).flatMap(userData())
.subscribe(userData -> {
storeUserData(userData);
emitter.onNext(USER_DATA_COMPLETE)
emitter.onComplete();
}, error -> {
wipeLoginData();
emitter.onError(error);
});
});
}
But it feels like there's a nicer or more Rx-y way to do it.
You can use hot observables and chain one observable to another Subject and pass all items form one emission to another if you need it.
#Test
public void chainObservablesWithSubject() throws InterruptedException {
Observable<Long> observable = Observable.from(Arrays.asList(1l, 2l, 3l, 4l));
Subject<Long, Long> chainObservable = ReplaySubject.create(1);
observable.subscribe(chainObservable);
chainObservable.subscribe(System.out::println, (e) -> System.err.println(e.getMessage()), System.out::println);
}
You can check more examples here https://github.com/politrons/reactive/blob/master/src/test/java/rx/observables/connectable/HotObservable.java

How to call a non-rx network call that depends on an rx network call

I have a network call that returns an Observable, and I have another network call that it is not rx that depends on the first Observable and I need to somehow convert it all with Rx.
Observable<Response> responseObservable = apiclient.executeRequest(request);
After executing I need to do another http call that does not return an Observable:
responseObservable.map(response - > execute the no rx network call using the response.id)
noRxClient.getInformation(response.id, new Action1<Information>() {
#Override
public void call(Information information) {
//Need to return information with page response
}
});
After then I need to call this method to render the response
renderResponse(response, information);
How can I connect the non-rx call with the rx and then call render response all with RxJava?
You can wrap your async non-rx calls into Observable using Observable.fromEmitter (RxJava1) or Observable.create (RxJava2) and Observable.fromCallable (for non-async calls):
private Observable<Information> wrapGetInformation(String responseId) {
return Observable.create(emitter -> {
noRxClient.getInformation(responseId, new Action1<Information>() {
#Override
public void call(Information information) {
emitter.onNext(information);
emitter.onComplete();
//also wrap exceptions into emitter.onError(Throwable)
}
});
});
}
private Observalbe<RenderedResponse> wrapRenderResponse(Response response, Information information) {
return Observable.fromCallable(() -> {
return renderResponse(response, information);
//exceptions automatically wrapped
});
}
And combine results using overloaded flatMap operator:
apiclient.executeRequest(request)
.flatMap(response -> wrapGetInformation(response.id),
(response, information) -> wrapRenderResponse(response, information))
)
//apply Schedulers
.subscribe(...)

Subscribe based on other emissions

I have an observable that emits values. Based on these values I need to subscribe/unsubscribe to/from another Observable.
Is there a handy way of doing so? A convenient way instead creating a field for the subscription and handling it manually?
Example:
Observable A emits Booleans. If it emits true then a subscription should be made to Observable B - if false this subscription should be unsubscribed.
I'm not sure if we're 100% on the same page but I think you're missing one point. Maybe you'll think I'm nitpicking, but I think it will be good to get our terms straight.
Observable starts emitting values when a Subscriber subscribes to it. So unless you're thinking about two separate Subscribers you can't react to an emitted value with a subscription because the Observer won't emit anything.
That said... what (I think) you wanna do could be done this way:
Observable<Boolean> observableA = /* observable A initialization */;
final Observable<SomeObject> observableB = /* observable B initialization */;
observableA
.flatMap(new Func1<Boolean, Observable<SomeObject>>() {
#Override
public Observable<SomeObject> call(Boolean aBoolean) {
if (!aBoolean) {
throw new IllegalStateException("A dummy exception that is here just to cause the subscription to finish with error.");
}
return observableB;
}
})
.subscribe(
new Action1<SomeObject>() {
#Override
public void call(SomeObject someObject) {
// THIS IS A PART OF THE SUBSCRIBER TO OBSERVABLE B.
// THIS METHOD WILL BE CALLED ONLY IF THE OBSERVABLE A RETURNED TRUE
}
},
new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
// A dummy Action1 so the subscription does not crash on the Exception
}
});
If all of observables has the same type or you can combine whatever you want based on values.
Observable.from(new int[]{1,2,3,4,5})
.filter(i -> i < 5) // filter out something
.flatMap(i -> {
if (i < 2) { // subscribe on some observable, based on item value
return Observable.just(i);
} else {
return Observable.just(3);
}
})

Categories