blob: bafab8ca0d9967edc2ac5903b11f7e0c6ad59ac9 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.volley.toolbox;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.VolleyLog;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicReference;
/** Asynchronous extension of the {@link BaseHttpStack} class. */
public abstract class AsyncHttpStack extends BaseHttpStack {
private ExecutorService mBlockingExecutor;
private ExecutorService mNonBlockingExecutor;
public interface OnRequestComplete {
/** Invoked when the stack successfully completes a request. */
void onSuccess(HttpResponse httpResponse);
/** Invoked when the stack throws an {@link AuthFailureError} during a request. */
void onAuthError(AuthFailureError authFailureError);
/** Invoked when the stack throws an {@link IOException} during a request. */
void onError(IOException ioException);
}
/**
* Makes an HTTP request with the given parameters, and calls the {@link OnRequestComplete}
* callback, with either the {@link HttpResponse} or error that was thrown.
*
* @param request to perform
* @param additionalHeaders to be sent together with {@link Request#getHeaders()}
* @param callback to be called after retrieving the {@link HttpResponse} or throwing an error.
*/
public abstract void executeRequest(
Request<?> request, Map<String, String> additionalHeaders, OnRequestComplete callback);
/**
* This method sets the non blocking executor to be used by the stack for non-blocking tasks.
* This method must be called before executing any requests.
*/
@RestrictTo({RestrictTo.Scope.LIBRARY_GROUP})
public void setNonBlockingExecutor(ExecutorService executor) {
mNonBlockingExecutor = executor;
}
/**
* This method sets the blocking executor to be used by the stack for potentially blocking
* tasks. This method must be called before executing any requests.
*/
@RestrictTo({RestrictTo.Scope.LIBRARY_GROUP})
public void setBlockingExecutor(ExecutorService executor) {
mBlockingExecutor = executor;
}
/** Gets blocking executor to perform any potentially blocking tasks. */
protected ExecutorService getBlockingExecutor() {
return mBlockingExecutor;
}
/** Gets non-blocking executor to perform any non-blocking tasks. */
protected ExecutorService getNonBlockingExecutor() {
return mNonBlockingExecutor;
}
/**
* Performs an HTTP request with the given parameters.
*
* @param request the request to perform
* @param additionalHeaders additional headers to be sent together with {@link
* Request#getHeaders()}
* @return the {@link HttpResponse}
* @throws IOException if an I/O error occurs during the request
* @throws AuthFailureError if an authentication failure occurs during the request
*/
@Override
public final HttpResponse executeRequest(
Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
final CountDownLatch latch = new CountDownLatch(1);
final AtomicReference<Response> entry = new AtomicReference<>();
executeRequest(
request,
additionalHeaders,
new OnRequestComplete() {
@Override
public void onSuccess(HttpResponse httpResponse) {
Response response =
new Response(
httpResponse,
/* ioException= */ null,
/* authFailureError= */ null);
entry.set(response);
latch.countDown();
}
@Override
public void onAuthError(AuthFailureError authFailureError) {
Response response =
new Response(
/* httpResponse= */ null,
/* ioException= */ null,
authFailureError);
entry.set(response);
latch.countDown();
}
@Override
public void onError(IOException ioException) {
Response response =
new Response(
/* httpResponse= */ null,
ioException,
/* authFailureError= */ null);
entry.set(response);
latch.countDown();
}
});
try {
latch.await();
} catch (InterruptedException e) {
VolleyLog.e(e, "while waiting for CountDownLatch");
Thread.currentThread().interrupt();
throw new InterruptedIOException(e.toString());
}
Response response = entry.get();
if (response.httpResponse != null) {
return response.httpResponse;
} else if (response.ioException != null) {
throw response.ioException;
} else {
throw response.authFailureError;
}
}
private static class Response {
HttpResponse httpResponse;
IOException ioException;
AuthFailureError authFailureError;
private Response(
@Nullable HttpResponse httpResponse,
@Nullable IOException ioException,
@Nullable AuthFailureError authFailureError) {
this.httpResponse = httpResponse;
this.ioException = ioException;
this.authFailureError = authFailureError;
}
}
}