package com.limegroup.gnutella.http;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.AbortableHttpRequest;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.params.HttpParams;
import org.limewire.collection.Cancellable;
import org.limewire.concurrent.ExecutorsHelper;
import org.limewire.http.httpclient.HttpClientUtils;
import org.limewire.http.httpclient.LimeHttpClient;
import org.limewire.logging.Log;
import org.limewire.logging.LogFactory;
import org.limewire.nio.observer.Shutdownable;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
/**
* Default implementation of <tt>HttpExecutor</tt>.
*/
@Singleton
public class DefaultHttpExecutor implements HttpExecutor {
private static final Log LOG = LogFactory.getLog(DefaultHttpExecutor.class);
private static final ExecutorService POOL =
ExecutorsHelper.newThreadPool("HttpClient pool");
private final Provider<LimeHttpClient> clientProvider;
private final HttpParams httpParams;
@Inject
public DefaultHttpExecutor(Provider<LimeHttpClient> clientProvider,
@Named("defaults") HttpParams httpParams) {
this.clientProvider = clientProvider;
this.httpParams = httpParams;
}
@Override
public Shutdownable execute(HttpUriRequest method) {
return execute(method, httpParams);
}
@Override
public Shutdownable execute(HttpUriRequest method, HttpParams params) {
return execute(method, params, new DefaultHttpClientListener());
}
@Override
public Shutdownable execute(HttpUriRequest request, HttpClientListener listener) {
return execute(request, httpParams, listener);
}
@Override
public Shutdownable execute(HttpUriRequest method, HttpParams params, HttpClientListener listener) {
return execute(method, params, listener, POOL);
}
private Shutdownable execute(final HttpUriRequest method, final HttpParams params, final HttpClientListener listener,
ExecutorService executor) {
Runnable r = new Runnable() {
@Override
public void run() {
performRequest(method, params, listener);
}
};
executor.execute(r);
return new Aborter(method);
}
private static class Aborter implements Shutdownable {
private final AbortableHttpRequest toAbort;
Aborter(HttpUriRequest toAbort) {
if(toAbort instanceof AbortableHttpRequest) {
this.toAbort = (AbortableHttpRequest)toAbort;
} else {
this.toAbort = null;
}
}
@Override
public void shutdown() {
if(toAbort != null) {
toAbort.abort();
}
}
}
@Override
public void releaseResources(HttpResponse response) {
HttpClientUtils.releaseConnection(response);
}
@Override
public Shutdownable executeAny(HttpClientListener listener,
ExecutorService executor,
Iterable<? extends HttpUriRequest> methods,
HttpParams params,
Cancellable canceller) {
MultiRequestor r = new MultiRequestor(listener, methods, params, canceller);
executor.execute(r);
return r;
}
/**
* Performs a single request.
* Returns true if no more requests should be processed,
* false if another request should be processed.
*/
private boolean performRequest(HttpUriRequest method, HttpParams params, HttpClientListener listener) {
LOG.debugf("performing request, method: {0}, params: {1}", method, params);
// If we aren't allowed to do this request, skip to the next.
if(!listener.allowRequest(method)) {
return false;
}
LimeHttpClient client = clientProvider.get();
if(params != null) {
client.setParams(params);
}
HttpResponse response;
try {
response = client.execute(method);
} catch (IOException failed) {
LOG.debug("iox", failed);
return !listener.requestFailed(method, null, failed);
} catch (Throwable t) {
LOG.debug("throwable", t);
return !listener.requestFailed(method, null, new IOException(t));
}
return !listener.requestComplete(method, response);
}
/** Runs all requests until the listener told it to not do anymore. */
private class MultiRequestor implements Runnable, Shutdownable {
private boolean shutdown;
private HttpUriRequest currentMethod;
private final Iterable<? extends HttpUriRequest> methods;
private final HttpClientListener listener;
private HttpParams params;
private final Cancellable canceller;
MultiRequestor(HttpClientListener listener,
Iterable<? extends HttpUriRequest> methods, HttpParams params, Cancellable canceller) {
this.methods = methods;
this.listener = listener;
this.params = params;
this.canceller = canceller;
}
@Override
public void run() {
for (HttpUriRequest m : methods) {
synchronized(this) {
if (shutdown) {
LOG.debug("shut down in run");
return;
}
currentMethod = m;
}
if (canceller.isCancelled()) {
LOG.debug("cancelled");
return;
}
if (performRequest(m, params, listener))
return;
}
}
public void shutdown() {
HttpUriRequest m;
LOG.debug("shutting down");
synchronized (this) {
shutdown = true;
m = currentMethod;
}
if(m instanceof AbortableHttpRequest) {
((AbortableHttpRequest)m).abort();
}
}
}
private class DefaultHttpClientListener implements HttpClientListener {
@Override
public boolean allowRequest(HttpUriRequest request) {
return true;
}
@Override
public boolean requestComplete(HttpUriRequest request, HttpResponse response) {
releaseResources(response);
return false;
}
@Override
public boolean requestFailed(HttpUriRequest request, HttpResponse response, IOException exc) {
releaseResources(response);
return false;
}
}
}