Java - AsyncHttpClient - aborting ongoing asynchronous call - java

I am using HttpAsyncClient in order to GET some urls. In some cases, let's say when I receive status HTTP-304, I would like to abandon the ongoing call. I don't want to wait for body, I don't want to spend the machine resources on it. Is there any way of cancelling this? Apparently things like futureResponse.cancel(true) or futureResponse.finalize() do not work.
Future<Response> futureResponse = httpClient.prepareGet("some-url").addHeader("some", "header").execute(new AsyncCompletionHandler<Response>() {
#Override
public State onStatusReceived(HttpResponseStatus status) throws Exception {
logger.info("status {}", status);
// CONDITIONAL FINISH THIS CALL
return super.onStatusReceived(status);
}
#Override
public Response onCompleted(Response response) throws Exception {
logger.info("its ok");
return response;
}
#Override
public void onThrowable(Throwable t) {
logger.error(t);
}
});

From the doc:
You can return ABORT to close the connection.
#Override
public State onStatusReceived(HttpResponseStatus responseStatus) throws Exception {
Integer status = responseStatus.getStatusCode();
if (304 == status) { // CONDITIONAL FINISH THIS CALL
return State.ABORT;
} else {
doSomething()
return ...
}
}

Related

Avoid blocking method to make the code asynchronous

How can I change this code to get rid of thread blocking? Here .get() blocks the thread to receive the result from the future. But can I absolutely avoid blocking? Something like - one thread sends the requests, and the other one receives responses and implements some code. To make it fully asynchronous.
I tried to use CompletableFuture, but couldn't really understand it.
Tried to make a callback method, but wasn't successful as well.
byte[] sendRequest(JSONObject jsonObject, String username, String password) throws IOException, ExecutionException, InterruptedException {
try (AsyncHttpClient client = new AsyncHttpClient()) {
String userPassword;
if (username != null && password != null) {
userPassword = username + ":" + password;
} else {
throw new NullPointerException("Нет логина и/или пароля.");
}
Future future = client.preparePost(apiUrl)
.addHeader("Content-Type", "application/json")
.addHeader("Authorization", "Basic " + DatatypeConverter.printBase64Binary(userPassword.getBytes()))
.setBody(jsonObject.toString().getBytes())
.execute(getHandler());
String response = (String) future.get();
return response.getBytes();
}
}
private AsyncCompletionHandler<String> getHandler() throws IOException {
return new AsyncCompletionHandler<String>() {
#Override
public String onCompleted(Response response) throws IOException {
return response.getResponseBody();
}
#Override
public void onThrowable(Throwable t) {
}
};
}
What I expect:
The program sends a request in the main thread.
Then there is a kind of a callback that waits for a response in an
alternative thread.
Still, the program continues working in the main thread - it goes on with sending more requests.
When the response from the server comes, the callback from the
alternative thread catches it and processes in some way, but it
doesn't correspond with the main thread
You should run your async task in new thread (preferably using ExecutorService or CompletableFuture). Pass CallbackHandler to the Runnable/Callable tasks and once the invocation is complete invoke handler methods.
Alternatively, if all you're worried about is handling async http requests, I'd suggest to not reinvent the wheel and instead use existing solutions. Example of async http client
For other use cases, you can follow the following example.
import java.util.*;
import java.lang.*;
import java.io.*;
class Ideone {
public static void main (String[] args) throws java.lang.Exception {
for (int i=0; i<10; i++) {
new Thread(new MyRunnable(new CallbackHandler())).start();
}
}
static class MyRunnable implements Runnable {
CallbackHandler handler;
public MyRunnable(CallbackHandler handler) {
this.handler = handler;
}
public void run() {
try {
Thread.sleep(100);
} catch(Exception e) {
} finally {
Random r = new Random();
if (r.nextBoolean()) {
handler.onSuccess();
} else {
handler.onError();
}
}
}
}
static class CallbackHandler {
public void onSuccess() {
System.out.println("Success");
}
public void onError() {
System.out.println("Error");
}
}
}

OnErrorReturn does not swallow the termination which caused inside flatMap

Consider this example:
#Test
public void testOnErrorReturn() {
final Observable<String> stringObservable = Observable.defer(new Callable<ObservableSource<String>>() {
#Override
public ObservableSource<String> call() throws Exception {
throw new RuntimeException("Too Bad");
}
});
Observable<String> observable = Observable.intervalRange(0, 5, 0, 500, TimeUnit.MILLISECONDS)
.flatMap(new Function<Long, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(#NonNull Long aLong) throws Exception {
return stringObservable;
}
})
.onErrorReturn(new Function<Throwable, String>() {
#Override
public String apply(#NonNull Throwable throwable) throws Exception {
return "That Passed";
}
})
.doOnComplete(new Action() {
#Override
public void run() throws Exception {
System.out.println("OnComplete first stream.");
}
});
observable.mergeWith(Observable.<String>never())
.doOnNext(new Consumer<String>() {
#Override
public void accept(#NonNull String s) throws Exception {
System.out.println("accept() called with: s = [" + s + "]");
}
})
.test().awaitDone(5, TimeUnit.SECONDS);
}
output:
accept() called with: s = [That Passed]
OnComplete first stream.
It only works if set the onErrorReturn inside the flatMap like this:
return stringObservable.onErrorReturn(new Function<Throwable, String>() {
#Override
public String apply(#NonNull Throwable throwable) throws Exception {
return "That Passed early";
}
});
But I lose the error information down in the chain. How can I keep the stream alive and suppress the termination elegantly?
It depends on what exactly you want to do. One option is to map error and success values inside flatMap to something like Try type and then handle that in outer Observable.
Another option is to delay errors with .flatMap(function, true) then the stream will be alive until completion and you will be able to process successful values and will be notified with composite error in the end.

stopping a long poll with rxjava in android

I am doing a long poll to an API from an android client using retrofit and rxjava. In this case, we wait for a 200 or 408 timeout response from an API and handle the response or reconnect to wait again for more data. This works just fine. I need to stop rx from retrying on certain error codes (like a 500) or if I want to interrupt the process, for example my app was background so let's stop the long poll.
retrofitInterface.startPolling() //returns an Observable
.repeat()
.retry()
.subscribe(new Subscriber<List<Stuff>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<Stuff> updates) {
//process stuff
}
}
});
I'm not sure if repeatWhen and retryWhen is the right solution here, where I want to keep repeating and retrying http calls to the API but stop repeating in some condition (say I flip a bool in the class to false) or stop retrying if the status code is a 500 instead of say a 408.
It's easier if you wrap your request answer in object of type <Response<?>>, this gives you control over the error code.
What I did for that use case is throwing a specific exception when I have some specific error code:
public <T> T throwExceptionIfFailure(T res) {
Response result = (Response<?>) res;
if (!result.isSuccessful()) {
try {
String msg = result.errorBody().string();
if (result.code() == 401 || result.code() == 403) {
invalidateToken();
msg = context.getString(R.string.invalid_credential);
} else if (result.code() == 502) {
msg = context.getString(R.string.server_down);
}
throw Exceptions.propagate(new IOException(msg));
} catch (Throwable e) {
throw Exceptions.propagate(e);
}
} else {
return res;
}
}
and I added this method in a map function of RX:
serviceRetrofit.getContacts()
.map(result -> serviceRetrofit.throwExceptionIfFailure(result))
.map(result -> createOrUpdateContact(result))
.retry(4)
.onErrorReturn(error -> handleErrorEvent(error))
.doOnCompleted(() -> emitStoreChange(new Store.StoreChangeEvent()))
.subscribe();

How to make JUnit4 "Wait" for asynchronous job to finish before running tests

I am trying to write a test for my android app that communicates with a cloud service.
Theoretically the flow for the test is supposed to be this:
Send request to the server in a worker thread
Wait for the response from the server
Check the response returned by the server
I am trying to use Espresso's IdlingResource class to accomplish that but it is not working as expected. Here's what I have so far
My Test:
#RunWith(AndroidJUnit4.class)
public class CloudManagerTest {
FirebaseOperationIdlingResource mIdlingResource;
#Before
public void setup() {
mIdlingResource = new FirebaseOperationIdlingResource();
Espresso.registerIdlingResources(mIdlingResource);
}
#Test
public void testAsyncOperation() {
Cloud.CLOUD_MANAGER.getDatabase().getCategories(new OperationResult<List<Category>>() {
#Override
public void onResult(boolean success, List<Category> result) {
mIdlingResource.onOperationEnded();
assertTrue(success);
assertNotNull(result);
}
});
mIdlingResource.onOperationStarted();
}
}
The FirebaseOperationIdlingResource
public class FirebaseOperationIdlingResource implements IdlingResource {
private boolean idleNow = true;
private ResourceCallback callback;
#Override
public String getName() {
return String.valueOf(System.currentTimeMillis());
}
public void onOperationStarted() {
idleNow = false;
}
public void onOperationEnded() {
idleNow = true;
if (callback != null) {
callback.onTransitionToIdle();
}
}
#Override
public boolean isIdleNow() {
synchronized (this) {
return idleNow;
}
}
#Override
public void registerIdleTransitionCallback(ResourceCallback callback) {
this.callback = callback;
}}
When used with Espresso's view matchers the test is executed properly, the activity waits and then check the result.
However plain JUNIT4 assert methods are ignored and JUnit is not waiting for my cloud operation to complete.
Is is possible that IdlingResource only work with Espresso methods ? Or am I doing something wrong ?
I use Awaitility for something like that.
It has a very good guide, here is the basic idea:
Wherever you need to wait:
await().until(newUserIsAdded());
elsewhere:
private Callable<Boolean> newUserIsAdded() {
return new Callable<Boolean>() {
public Boolean call() throws Exception {
return userRepository.size() == 1; // The condition that must be fulfilled
}
};
}
I think this example is pretty similar to what you're doing, so save the result of your asynchronous operation to a field, and check it in the call() method.
Junit will not wait for async tasks to complete. You can use CountDownLatch to block the thread, until you receive response from server or timeout.
Countdown latch is a simple yet elegant solution and does NOT need an external library. It also helps you focus on the actual logic to be tested rather than over-engineering the async wait or waiting for a response
void testBackgroundJob() {
Latch latch = new CountDownLatch(1);
//Do your async job
Service.doSomething(new Callback() {
#Override
public void onResponse(){
ACTUAL_RESULT = SUCCESS;
latch.countDown(); // notify the count down latch
// assertEquals(..
}
});
//Wait for api response async
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
assertEquals(expectedResult, ACTUAL_RESULT);
}

onFailure is never called when a exception is throwed in server

When a called the persist() method from server, a exception is throwed after a validation in uniques of a email.
The problem is that onSuccess method from client is called, instead of onFailure. Here is the code.
RequestContext req = driver.flush();
if (req.isChanged() && !driver.hasErrors()) {
saveButton.setEnabled(false);
req.fire(new Receiver<Void>() {
#Override
public void onSuccess(Void response) {
//anything
}
#Override
public void onFailure(ServerFailure error) {
//anything
}
});
}
public User persist() throws GenericException{ // extends from Exception
//query in database
throw new GenericException("Email must be unique");
//save case is correct
}
Any help?
Why do you think that throwing exception == calling onFailure method? Did you analyzed code - is somewhere exception handler that catches your exceptions and converts them to onFaliure calls?

Categories

Resources