Complete CompletableFuture with another CompletableFuture result - java

I'm doing async http call such way
public CompletableFuture<String> doPost(String path, String json) {
CompletableFuture<String> result = new CompletableFuture<>();
Request request = new Request.Builder().url(this.address + path).post(RequestBody.create(json, JSON)).build();
httpClient.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(#NotNull Call call, #NotNull IOException e) {
result.completeExceptionally(new TerminationException());
}
#Override
public void onResponse(#NotNull Call call, #NotNull Response response) throws IOException {
result.complete(response.body().string());
}
});
}
But it is possible that response will have one of the codes that I'll need to retry and the code should be
#Override
public void onResponse(#NotNull Call call, #NotNull Response response) throws IOException {
if (!retries.contains(responce.code()) {
result.complete(response.body().string());
} else {
// Do retry here
}
}
In retry I want to call doPost recursively and use it's return value as a result of initial call.
So it returns some completable future, how complete initial CF with it's result in async way (without doint .get())?
Thanks.

You can use delegation, e.g.
public CompletableFuture<String> doPost(String path, String json) {
CompletableFuture<String> result = new CompletableFuture<>();
doPostImpl(this.address + path, json, result, 10);
return result;
}
private void doPostImpl(
String url, String json, CompletableFuture<String> result, int maxRetries) {
Request request = new Request.Builder()
.url(url).post(RequestBody.create(json, JSON)).build();
httpClient.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(#NotNull Call call, #NotNull IOException e) {
result.completeExceptionally(new TerminationException());
}
#Override
public void onResponse(
#NotNull Call call, #NotNull Response response) throws IOException {
if(maxRetries <= 0 || !retries.contains(response.code())) {
result.complete(response.body().string());
} else {
doPostImpl(url, json, result, maxRetries - 1);
}
}
});
}
The front-end method delegates to a method receiving the target future. When retrying, the implementation method is invoked again with the same future. Therefore, there is no need to transfer results from one future to another.
Taking the question literally, you can transfer the result of a future to another using
future2.whenComplete((value,throwable) -> {
if(throwable != null) future1.completeExceptionally(throwable);
else future1.complete(value);
});
but this could create a dependency chain as long as the number of retries. As shown above, there is no need for that.

Related

Get boolean value from Retrofit2 onResponse

I am trying to make an asynchronous request to the server, which should return me true or false. But the problem is that the function first returns false, and only then the retrofit function is triggered. How can I get the value I need while maintaining the speed of program execution?
My code:
private static boolean find;
public boolean checkAccountByPhone(String phone){
Log.d("dbHandler", "function started...");
DbHandler.getApi().checkAccountByPhone(phone).enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
Log.d("dbHandler", "data is: "+response.body().equals("true"));
// return response.body().equals("true");
Log.d("dbHandler", "sent!");
}
#Override
public void onFailure(Call<String> call, Throwable t) {
Log.d("dbHandler", t.getMessage());
}
});
Log.d("dbHandler", "returned: "+DbHandler.find);
return DbHandler.find; // always false???
}
You can modify logic in Java, this code is for reference to avoid callback.
fun checkAccountByPhone(phone: String):String{
viewModelScope.launch(Dispatchers.IO) {
val response : Response<String> = try {
DbHandler.getApi().checkAccountByPhone(phone)
} catch (ex: Exception) {
ex.logException()
null
}
//you can check response.body() here and can post either response.body() or DbHandler.find
_yourLiveData.post(response.body() or DbHandler.find)
}

how to get the values from json object in retrofit android

All the thing working fine but how to call the the user{first_name,last_name,email}
You have to call a retrofit method in this way,
#FormUrlEncoded
#POST("employer/login")
Call<ResponseModelEmplyrLogin> loginEmployer(
#Field("data") String data);
public void loginEmployer(String data, final MyApiCallbackEmplyrLogin<EmployrDataBean> callback) {
ApiInterface apiService = ApiClient.createService();
Call<ResponseModelEmplyrLogin> call = apiService.loginEmployer(data);
call.enqueue(new Callback<ResponseModelEmplyrLogin>() {
#Override
public void onResponse(Call<ResponseModelEmplyrLogin> call, Response<ResponseModelEmplyrLogin> response) {
//Write your code
}
#Override
public void onFailure(Call<ResponseModelEmplyrLogin> call, Throwable t) {
//Write your code
}
});
}
Check out this link https://www.androidtutorialpoint.com/networking/retrofit-android-tutorial/ ...I think this will be helpful

Making multiple asynchronous HTTP 2.0 requests with okhttp3

I am trying to use okhttp (3.4.1) to implement a HTTP 2.0 based client. Part of my requirement is to implement multiple asynchronous HTTP requests to different URLs with a callback to handle the response at a later time.
However, with my current implementation, I see that I cannot get all my asynchronous requests to use the same TCP connection unless I make a blocking HTTP request from my main thread at the beginning.
I understand that the enque() method used for asynchronous calls engages the dispatcher which seems to spawn a new thread for each of the requests.
Here is my code snippet:
OkHttpClient client = new OkHttpClient();
My async Get Request method looks as follows:
public void AsyncGet(String url) throws Exception {
Request request = new Request.Builder().url(url).build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
new Thread() {
#Override
public void run() {
/* Some code */
}
}.start();
}
});
}
My synchronous Get Request is as follows:
public Response SyncGet(String url) throws Exception {
Request request = new Request.Builder().url(url).build();
Call call = client.newCall(request);
Response response = call.execute();
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
return response;
}
Making a call sequence like the following triggers 2 TCP connections to the server.
AsyncGet(Url);
AsyncGet(Url2);
However, a call sequence like the following makes use of the same TCP connection.
SyncGet(Url);
AsyncGet(Url);
AsyncGet(Url2);
I have not debugged this but, it looks like OkHttp forces us to make a blocking HTTP request on the main thread first to possibly obtain the TCP connection context and then share that with other threads? Or, am I missing something?
You can call async to and set sector to each call later on based on the sector you can distinguish the response of call. Try this code; I hope it will help you!
Create a separate class for api call:
public class RestApi {
protected RestApiResponse serverResponse;
protected Context c;
public RestApi(RestApiResponse serverResponse, Context c) {
this.serverResponse = serverResponse;
this.c = c;
}
public void postDataWithoutAuth(String url,String sector) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
Log.e("response", call.request().body().toString());
serverResponse.onErrorLoaded(call.request().body().toString(), sector);
}
#Override
public void onResponse(Call call, Response response) throws IOException {
serverResponse.onResponseLoaded(response, sector);
}
});
}
}
Then create an interface for callback
public interface RestApiResponse {
public void onResponseLoaded(Response response, String sector);
public void onErrorLoaded(String response, String sector);
}
and access it from your Activity like this
RestApi apicall = new RestApi(this, getBaseContext());
apicall.postDataWithoutAuthUrlEncoded("ur url","your sector");

Return value from inner class in a method

I am building a Login system for an Android app. I am using OkHttp to connect to my server and get a JSON response.
I have defined a class with the login return data (right now just a true/false response based on whether the user exists in the database), and then written the code to connect to the server, as shown below:
class UserLogin {
boolean status;
public void setStatus(boolean status) {
this.status = status;
}
public boolean getStatus() {
return status;
}
}
public class ClientServerInterface {
OkHttpClient client = new OkHttpClient();
boolean login(Request request) {
final Gson gson = new Gson();
client.newCall(request).enqueue(new Callback() {
UserLogin login;
#Override
public void onFailure(Call call, IOException e) {
}
#Override
public void onResponse(Call call, Response response) throws IOException {
login = gson.fromJson(response.body().charStream(), UserLogin.class);
login.setStatus(login.status);
}
});
// need to return the boolean response (status) here
}
}
The code which passes the Request variable to the login method works perfectly. I want login to return a boolean response so that I can pass that to other methods in other classes.
However, because the UserLogin object is defined in the callback I can't access it in the parent method. I have made a getStatus method but not sure how to use it properly to get the status in the main login method.
The code which passes the Request variable to the login method works
perfectly. I want login to return a boolean response so that I can
pass that to other methods in other classes.
you can't. enqueue executes the code in Async way. You don't know when the callback is invoked. What you could do is to add the Callback as parameter to your login method. E.g.
boolean login(Request request, final Callback callback) {
and either pass it to enqueue,
client.newCall(request).enqueue(callback);
or call the callback manually. E.g.
#Override
public void onResponse(Call call, Response response) throws IOException {
if (callback != null) {
callback.onResponse(call, response);
}
}
in both cases the caller of login will receive the callback on the provided object and, accordingly to the content it receives, can decide wha actions undertake
You can do this using a SynchronousQueue:
final SynchronousQueue<Boolean> queue = new SynchronousQueue<>();
client.newCall(request).enqueue(new Callback() {
UserLogin login;
#Override
public void onFailure(Call call, IOException e) {
queue.put(false);
}
#Override
public void onResponse(Call call, Response response) throws IOException {
queue.put(true);
}
});
return queue.take();
Add loginStatus variable to class like below and one more to indicate login operation completion.
public class ClientServerInterface {
OkHttpClient client = new OkHttpClient();
private boolean loginStatus = false;
private boolean isLoginOperationDone = false;
boolean login(Request request) {
final Gson gson = new Gson();
client.newCall(request).enqueue(new Callback() {
UserLogin login;
#Override
public void onFailure(Call call, IOException e) {
}
#Override
public void onResponse(Call call, Response response) throws IOException {
login = gson.fromJson(response.body().charStream(), UserLogin.class);
loginStatus = login.setStatus(login.status);
isLoginOperationDone = true;
}
});
// need to return the boolean response (status) here
while( !isLoginOperationDone )
{
//not to do anything.
}
return loginStatus;
}
}
Note that this might be a little hacky but will do solve your problem.
The way to go is with AsyncTask. Override the doInBackground method to perform the http requests and get the result by overriding the onPostExecute method.
Read more here: http://developer.android.com/reference/android/os/AsyncTask.html
An even better way to go is to run a background Service for all your API calls.

Jersey async request handling with programmatical resource registration

We used
org.glassfish.jersey.server.model.ResourceMethod$Builder
to register the method and the handler.
ResourceMethod.Builder methodBuilder = resourceBuilder.addMethod(httpMethod);
methodBuilder.produces(restContext.getProduceContent()).handledBy(inflector);
methodBuilder.consumes(restContext.getConsumeContent()).handledBy(inflector);
The handler class implements the org.glassfish.jersey.process.Inflector<ContainerRequestContext, Response>
public class CommonMethodInflector implements Inflector<ContainerRequestContext, Response>
{
#Override
public Response apply(ContainerRequestContext request)
{
//sync bloc
//using resqest object we do processing in different maner
incRestFeRequestCounters(request.getMethod());
Response response = processIncoming(request);`enter code here`
}
}
Could you please help us in creating the async handler.
our requirement in short:
At runtime only we know the http method and other resources to register.
So, we can not use annotations for resource & httpMethod registration.
We need only programmatic resource registration.
In handler We need the request object so that we can access what method and what json body in it.
we need to make the async response as we are doing huge operation in the processing request.
The first thing you need to do is to suspend the resource method:
ResourceMethod.Builder methodBuilder = resourceBuilder.addMethod(httpMethod)
.suspend(AsyncResponse.NO_TIMEOUT, TimeUnit.Seconds);
Then you have few choices how to process the request in async mode:
"Arbitrary" handler class
builder.addMethod("GET")
// Suspend without time limit.
.suspended(AsyncResponse.NO_TIMEOUT, TimeUnit.SECONDS)
.handledBy(MyHandler.class, MyHandler.class.getMethod("handle", AsyncResponse.class));
and
public class MyHandler {
public void handle(final #Suspended AsyncResponse response) {
Executors.newSingleThreadExecutor().submit(new Runnable() {
#Override
public void run() {
// Simulate long-running operation.
try {
Thread.sleep(1000);
} catch (final InterruptedException ie) {
// NOOP
}
response.resume("Hello World!");
}
});
}
}
Inflector class
Resource.builder("helloworld")
.addMethod("GET")
// Suspend without time limit.
.suspended(AsyncResponse.NO_TIMEOUT, TimeUnit.SECONDS)
// Can be registered only as a class otherwise the
// #Context injection would be out of request scope.
.handledBy(MyAsyncInflector.class);
and
public class MyAsyncInflector implements Inflector<ContainerRequestContext, Response> {
#Context
private AsyncResponse response;
#Override
public Response apply(final ContainerRequestContext context) {
Executors.newSingleThreadExecutor().submit(new Runnable() {
#Override
public void run() {
// Simulate long-running operation.
try {
Thread.sleep(1000);
} catch (final InterruptedException ie) {
// NOOP
}
response.resume("Hello World!");
}
});
return null;
}
}
Annonymous Inflector
Resource.builder("helloworld")
.addMethod("GET")
// Suspend without time limit.
.suspended(AsyncResponse.NO_TIMEOUT, TimeUnit.SECONDS)
.handledBy(new Inflector<ContainerRequestContext, Response>() {
#Inject
private javax.inject.Provider<AsyncResponse> responseProvider;
#Override
public Response apply(final ContainerRequestContext context) {
// Make sure we're in request scope.
final AsyncResponse response = responseProvider.get();
Executors.newSingleThreadExecutor().submit(new Runnable() {
#Override
public void run() {
// Simulate long-running operation.
try {
Thread.sleep(1000);
} catch (final InterruptedException ie) {
// NOOP
}
response.resume("Hello World!");
}
});
return null;
}
});

Categories

Resources