/**
*
*/
package com.github.lpezet.antiope2.retrofitted;
import static com.github.lpezet.antiope2.retrofitted.Utils.checkNotNull;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.github.lpezet.antiope2.dao.http.IHttpNetworkIO;
import com.github.lpezet.antiope2.dao.http.IHttpRequest;
import com.github.lpezet.antiope2.dao.http.IHttpResponse;
import com.github.lpezet.antiope2.metrics.IMetricsCollector;
import com.github.lpezet.antiope2.metrics.StubMetricsCollector;
import com.github.lpezet.antiope2.retrofitted.converter.Converter;
/**
* @author Luc Pezet
*/
public class RestAdapter {
private final Map<Class<?>, Map<Method, MethodInfo>> serviceMethodInfoCache = new LinkedHashMap<Class<?>, Map<Method, MethodInfo>>();
private String mEndpoint;
private IHttpNetworkIO<IHttpRequest, IHttpResponse> mNetworkIO;
// private Executor mCallbackExecutor;
private ExecutorService mExecutorService;
private Converter mConverter;
private ErrorHandler mErrorHandler;
private IMetricsCollector mMetricsCollector;
private RestAdapter(String endpoint, IHttpNetworkIO pNetworkIO, ExecutorService executorService, Converter converter, ErrorHandler errorHandler) {
mEndpoint = endpoint;
mNetworkIO = pNetworkIO;
// mCallbackExecutor = callbackExecutor;
mConverter = converter;
mErrorHandler = errorHandler;
mExecutorService = executorService;
}
/** Create an implementation of the API defined by the specified {@code service} interface. */
@SuppressWarnings("unchecked")
public <T> T create(Class<T> service) {
Utils.validateServiceClass(service);
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new RestHandler(mEndpoint, mNetworkIO, mMetricsCollector, mExecutorService, mConverter, getMethodInfoCache(service), mErrorHandler));
}
Map<Method, MethodInfo> getMethodInfoCache(Class<?> service) {
synchronized (serviceMethodInfoCache) {
Map<Method, MethodInfo> methodInfoCache = serviceMethodInfoCache.get(service);
if (methodInfoCache == null) {
methodInfoCache = new LinkedHashMap<Method, MethodInfo>();
serviceMethodInfoCache.put(service, methodInfoCache);
}
return methodInfoCache;
}
}
static MethodInfo getMethodInfo(Map<Method, MethodInfo> cache, Method method) {
synchronized (cache) {
MethodInfo methodInfo = cache.get(method);
if (methodInfo == null) {
methodInfo = new MethodInfo(method);
cache.put(method, methodInfo);
}
return methodInfo;
}
}
/**
* Build a new {@link RestAdapter}.
* <p>
* Calling {@link #endpoint} is required before calling {@link #build()}. All other methods are
* optional.
*/
public static class Builder {
private String endpoint;
private IHttpNetworkIO client;
//private Executor callbackExecutor;
private ExecutorService executorService;
private Converter converter;
private ErrorHandler errorHandler;
private IMetricsCollector metricsCollector;
/** API endpoint URL. */
public Builder endpoint(String url) {
this.endpoint = checkNotNull(url, "endpoint == null");
return this;
}
/** The HTTP client used for requests. */
public Builder client(IHttpNetworkIO client) {
this.client = checkNotNull(client, "client == null");
return this;
}
public Builder metricsCollector(IMetricsCollector pMetricsCollector) {
metricsCollector = pMetricsCollector;
return this;
}
/**
* Executor on which any {@link Callback} methods will be invoked. If this argument is
* {@code null} then callback methods will be run on the same thread as the HTTP client.
*/
/*
public Builder callbackExecutor(Executor callbackExecutor) {
if (callbackExecutor == null) {
callbackExecutor = new Utils.SynchronousExecutor();
}
this.callbackExecutor = callbackExecutor;
return this;
}
*/
public Builder executorService(ExecutorService executorService) {
this.executorService = executorService;
return this;
}
/** The converter used for serialization and deserialization of objects. */
public Builder converter(Converter converter) {
this.converter = checkNotNull(converter, "converter == null");
return this;
}
/**
* The error handler allows you to customize the type of exception thrown for errors on
* synchronous requests.
*/
public Builder errorHandler(ErrorHandler errorHandler) {
this.errorHandler = checkNotNull(errorHandler, "errorHandler == null");
return this;
}
/** Create the {@link RestAdapter} instances. */
public RestAdapter build() {
checkNotNull(endpoint, "Endpoint required.");
ensureSaneDefaults();
return new RestAdapter(endpoint, client, executorService, converter, errorHandler);
}
private void ensureSaneDefaults() {
if (converter == null) {
converter = Platform.get().defaultConverter();
}
if (client == null) {
client = Platform.get().defaultClient();
}
//if (callbackExecutor == null) {
// callbackExecutor = Platform.get().defaultCallbackExecutor();
//}
if (metricsCollector == null) {
metricsCollector = new StubMetricsCollector();
}
if (executorService == null) {
//TODO: Replace by Platgorm.get().defaultExecutorService();
executorService = Executors.newCachedThreadPool();
}
if (errorHandler == null) {
errorHandler = ErrorHandler.DEFAULT;
}
}
}
}