提交 2646fdf0 编写于 作者: J Jason Holmes

Merge pull request #70 from square/jw/builder

Introduce builder pattern to instantiate RestAdapter.
...@@ -21,7 +21,7 @@ import java.util.logging.Logger; ...@@ -21,7 +21,7 @@ import java.util.logging.Logger;
* @author Bob Lee (bob@squareup.com) * @author Bob Lee (bob@squareup.com)
* @author Jake Wharton (jw@squareup.com) * @author Jake Wharton (jw@squareup.com)
*/ */
public class CallbackResponseHandler<R> implements ResponseHandler<Void> { class CallbackResponseHandler<R> implements ResponseHandler<Void> {
private static final Logger LOGGER = Logger.getLogger(CallbackResponseHandler.class.getName()); private static final Logger LOGGER = Logger.getLogger(CallbackResponseHandler.class.getName());
...@@ -51,7 +51,7 @@ public class CallbackResponseHandler<R> implements ResponseHandler<Void> { ...@@ -51,7 +51,7 @@ public class CallbackResponseHandler<R> implements ResponseHandler<Void> {
* @return parsed response * @return parsed response
* @throws ConversionException if the server returns an unexpected response * @throws ConversionException if the server returns an unexpected response
*/ */
protected Object parse(HttpEntity entity, Type type) throws ConversionException { private Object parse(HttpEntity entity, Type type) throws ConversionException {
if (LOGGER.isLoggable(Level.FINE)) { if (LOGGER.isLoggable(Level.FINE)) {
try { try {
entity = HttpClients.copyAndLog(entity, requestUrl, start, dateFormat.get()); entity = HttpClients.copyAndLog(entity, requestUrl, start, dateFormat.get());
......
...@@ -10,6 +10,6 @@ import org.apache.http.HttpMessage; ...@@ -10,6 +10,6 @@ import org.apache.http.HttpMessage;
*/ */
public interface Headers { public interface Headers {
/** Sets headers on the given message, with the specified MIME type */ /** Sets headers on the given message */
void setOn(HttpMessage message, String mimeType); void setOn(HttpMessage message);
} }
...@@ -6,6 +6,7 @@ import java.lang.reflect.Method; ...@@ -6,6 +6,7 @@ import java.lang.reflect.Method;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.List; import java.util.List;
import org.apache.http.HttpMessage;
import org.apache.http.NameValuePair; import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpDelete;
...@@ -34,7 +35,7 @@ public enum HttpMethodType { ...@@ -34,7 +35,7 @@ public enum HttpMethodType {
throws URISyntaxException { throws URISyntaxException {
URI uri = getParameterizedUri(builder); URI uri = getParameterizedUri(builder);
HttpGet request = new HttpGet(uri); HttpGet request = new HttpGet(uri);
builder.getHeaders().setOn(request, builder.getMimeType()); addHeaders(request, builder);
return request; return request;
} }
}, },
...@@ -45,7 +46,7 @@ public enum HttpMethodType { ...@@ -45,7 +46,7 @@ public enum HttpMethodType {
URI uri = getUri(builder); URI uri = getUri(builder);
HttpPost request = new HttpPost(uri); HttpPost request = new HttpPost(uri);
addParams(request, builder); addParams(request, builder);
builder.getHeaders().setOn(request, builder.getMimeType()); addHeaders(request, builder);
return request; return request;
} }
}, },
...@@ -56,7 +57,7 @@ public enum HttpMethodType { ...@@ -56,7 +57,7 @@ public enum HttpMethodType {
URI uri = getUri(builder); URI uri = getUri(builder);
HttpPut request = new HttpPut(uri); HttpPut request = new HttpPut(uri);
addParams(request, builder); addParams(request, builder);
builder.getHeaders().setOn(request, builder.getMimeType()); addHeaders(request, builder);
return request; return request;
} }
}, },
...@@ -66,7 +67,7 @@ public enum HttpMethodType { ...@@ -66,7 +67,7 @@ public enum HttpMethodType {
throws URISyntaxException { throws URISyntaxException {
URI uri = getParameterizedUri(builder); URI uri = getParameterizedUri(builder);
HttpDelete request = new HttpDelete(uri); HttpDelete request = new HttpDelete(uri);
builder.getHeaders().setOn(request, builder.getMimeType()); addHeaders(request, builder);
return request; return request;
} }
}; };
...@@ -96,6 +97,17 @@ public enum HttpMethodType { ...@@ -96,6 +97,17 @@ public enum HttpMethodType {
queryString, null); queryString, null);
} }
private static void addHeaders(HttpMessage message, HttpRequestBuilder builder) {
String mimeType = builder.getMimeType();
if (mimeType != null) {
message.addHeader("Content-Type", mimeType);
}
Headers headers = builder.getHeaders();
if (headers != null) {
headers.setOn(message);
}
}
/** /**
* Adds all but the last method argument as parameters of HTTP request * Adds all but the last method argument as parameters of HTTP request
* object. * object.
......
...@@ -8,15 +8,6 @@ package retrofit.http; ...@@ -8,15 +8,6 @@ package retrofit.http;
*/ */
public interface HttpProfiler<T> { public interface HttpProfiler<T> {
HttpProfiler<Void> NONE = new HttpProfiler<Void>() {
@Override public Void beforeCall() {
return null;
}
@Override public void afterCall(RequestInformation requestInfo,
long elapsedTime, int statusCode, Void beforeCallData) {
}
};
/** /**
* Invoked before an HTTP method call. The object returned by this method will be * Invoked before an HTTP method call. The object returned by this method will be
* passed to {@link #afterCall} when the call returns. * passed to {@link #afterCall} when the call returns.
......
// Copyright 2012 Square, Inc.
package retrofit.http;
import org.apache.http.HttpResponse;
import org.apache.http.client.ResponseHandler;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
/** Sends server call times and response status codes to {@link retrofit.http.HttpProfiler}. */
class ProfilingResponseHandler<T> implements ResponseHandler<Void> {
private static final Logger LOGGER = Logger.getLogger(ProfilingResponseHandler.class.getSimpleName());
private final ResponseHandler<Void> delegate;
private final HttpProfiler<T> profiler;
private final HttpProfiler.RequestInformation requestInfo;
private final long startTime;
private final AtomicReference<T> beforeCallData = new AtomicReference<T>();
/** Wraps the delegate response handler. */
ProfilingResponseHandler(ResponseHandler<Void> delegate, HttpProfiler<T> profiler,
HttpProfiler.RequestInformation requestInfo, long startTime) {
this.delegate = delegate;
this.profiler = profiler;
this.requestInfo = requestInfo;
this.startTime = startTime;
}
public void beforeCall() {
try {
beforeCallData.set(profiler.beforeCall());
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Error occurred in HTTP profiler beforeCall().", e);
}
}
@Override public Void handleResponse(HttpResponse httpResponse) throws IOException {
// Intercept the response and send data to profiler.
long elapsedTime = System.currentTimeMillis() - startTime;
int statusCode = httpResponse.getStatusLine().getStatusCode();
try {
profiler.afterCall(requestInfo, elapsedTime, statusCode, beforeCallData.get());
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Error occurred in HTTP profiler afterCall().", e);
}
// Pass along the response to the normal handler.
return delegate.handleResponse(httpResponse);
}
}
...@@ -2,7 +2,6 @@ package retrofit.http; ...@@ -2,7 +2,6 @@ package retrofit.http;
import org.apache.http.Header; import org.apache.http.Header;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient; import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler; import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
...@@ -22,7 +21,6 @@ import java.util.Date; ...@@ -22,7 +21,6 @@ import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
...@@ -47,7 +45,7 @@ public class RestAdapter { ...@@ -47,7 +45,7 @@ public class RestAdapter {
private final Converter converter; private final Converter converter;
private final HttpProfiler profiler; private final HttpProfiler profiler;
public RestAdapter(Server server, Provider<HttpClient> httpClientProvider, Executor executor, MainThread mainThread, private RestAdapter(Server server, Provider<HttpClient> httpClientProvider, Executor executor, MainThread mainThread,
Headers headers, Converter converter, HttpProfiler profiler) { Headers headers, Converter converter, HttpProfiler profiler) {
this.server = server; this.server = server;
this.httpClientProvider = httpClientProvider; this.httpClientProvider = httpClientProvider;
...@@ -128,7 +126,7 @@ public class RestAdapter { ...@@ -128,7 +126,7 @@ public class RestAdapter {
ResponseHandler<Void> rh = new CallbackResponseHandler(callback, type, converter, url, start, DATE_FORMAT); ResponseHandler<Void> rh = new CallbackResponseHandler(callback, type, converter, url, start, DATE_FORMAT);
// Optionally wrap the response handler for server call profiling. // Optionally wrap the response handler for server call profiling.
if (profiler != HttpProfiler.NONE) { if (profiler != null) {
rh = createProfiler(rh, (HttpProfiler<?>) profiler, getRequestInfo(method, request), start); rh = createProfiler(rh, (HttpProfiler<?>) profiler, getRequestInfo(method, request), start);
} }
...@@ -248,44 +246,84 @@ public class RestAdapter { ...@@ -248,44 +246,84 @@ public class RestAdapter {
String.format("Last parameter of %s must be of type Callback<X,Y,Z> or Callback<? super X,..,..>.", method)); String.format("Last parameter of %s must be of type Callback<X,Y,Z> or Callback<? super X,..,..>.", method));
} }
/** Sends server call times and response status codes to {@link HttpProfiler}. */ /**
private static class ProfilingResponseHandler<T> implements ResponseHandler<Void> { * Build a new {@link RestAdapter}.
private final ResponseHandler<Void> delegate; * <p/>
private final HttpProfiler<T> profiler; * Calling the following methods is required before calling {@link #build()}:
private final HttpProfiler.RequestInformation requestInfo; * <ul>
private final long startTime; * <li>{@link #setServer(Server)}</li>
private final AtomicReference<T> beforeCallData = new AtomicReference<T>(); * <li>{@link #setClient(javax.inject.Provider)}</li>
* <li>{@link #setExecutor(java.util.concurrent.Executor)}</li>
* <li>{@link #setMainThread(MainThread)}</li>
* <li>{@link #setConverter(Converter)}</li>
* </ul>
*/
public static class Builder {
private Server server;
private Provider<HttpClient> clientProvider;
private Executor executor;
private MainThread mainThread;
private Headers headers;
private Converter converter;
private HttpProfiler profiler;
public Builder setServer(String endpoint) {
return setServer(new Server(endpoint));
}
/** Wraps the delegate response handler. */ public Builder setServer(Server server) {
private ProfilingResponseHandler(ResponseHandler<Void> delegate, HttpProfiler<T> profiler, this.server = server;
HttpProfiler.RequestInformation requestInfo, long startTime) { return this;
this.delegate = delegate;
this.profiler = profiler;
this.requestInfo = requestInfo;
this.startTime = startTime;
} }
public void beforeCall() { public Builder setClient(final HttpClient client) {
try { return setClient(new Provider<HttpClient>() {
beforeCallData.set(profiler.beforeCall()); @Override public HttpClient get() {
} catch (Exception e) { return client;
LOGGER.log(Level.SEVERE, "Error occurred in HTTP profiler beforeCall().", e);
} }
});
} }
@Override public Void handleResponse(HttpResponse httpResponse) throws IOException { public Builder setClient(Provider<HttpClient> clientProvider) {
// Intercept the response and send data to profiler. this.clientProvider = clientProvider;
long elapsedTime = System.currentTimeMillis() - startTime; return this;
int statusCode = httpResponse.getStatusLine().getStatusCode(); }
try { public Builder setExecutor(Executor executor) {
profiler.afterCall(requestInfo, elapsedTime, statusCode, beforeCallData.get()); this.executor = executor;
} catch (Exception e) { return this;
LOGGER.log(Level.SEVERE, "Error occurred in HTTP profiler afterCall().", e); }
public Builder setMainThread(MainThread mainThread) {
this.mainThread = mainThread;
return this;
}
public Builder setHeaders(Headers headers) {
this.headers = headers;
return this;
}
public Builder setConverter(Converter converter) {
this.converter = converter;
return this;
}
public Builder setProfiler(HttpProfiler profiler) {
this.profiler = profiler;
return this;
} }
// Pass along the response to the normal handler. public RestAdapter build() {
return delegate.handleResponse(httpResponse); if (server == null) throw new NullPointerException("server");
if (clientProvider == null) throw new NullPointerException("clientProvider");
if (converter == null) throw new NullPointerException("converter");
// TODO Remove the following two when we support synchronous invocation as they will be allowed to be null.
if (executor == null) throw new NullPointerException("executor");
if (mainThread == null) throw new NullPointerException("mainThread");
return new RestAdapter(server, clientProvider, executor, mainThread, headers, converter, profiler);
} }
} }
} }
\ No newline at end of file
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
package retrofit.http; package retrofit.http;
import com.google.gson.Gson; import com.google.gson.Gson;
import org.apache.http.HttpMessage;
import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPut; import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.methods.HttpUriRequest;
...@@ -23,10 +22,6 @@ import static org.fest.assertions.Fail.fail; ...@@ -23,10 +22,6 @@ import static org.fest.assertions.Fail.fail;
public class HttpRequestBuilderTest { public class HttpRequestBuilderTest {
private static final Gson GSON = new Gson(); private static final Gson GSON = new Gson();
private static final String API_URL = "http://taqueria.com/lengua/taco"; private static final String API_URL = "http://taqueria.com/lengua/taco";
private static final Headers BLANK_HEADERS = new Headers() {
@Override public void setOn(HttpMessage message, String mimeType) {
}
};
@Test public void testRegex() throws Exception { @Test public void testRegex() throws Exception {
expectParams(""); expectParams("");
...@@ -205,7 +200,6 @@ public class HttpRequestBuilderTest { ...@@ -205,7 +200,6 @@ public class HttpRequestBuilderTest {
.setMethod(method) .setMethod(method)
.setArgs(args) .setArgs(args)
.setApiUrl(API_URL) .setApiUrl(API_URL)
.setHeaders(BLANK_HEADERS)
.build(); .build();
} }
......
...@@ -23,7 +23,6 @@ import org.junit.Test; ...@@ -23,7 +23,6 @@ import org.junit.Test;
import retrofit.http.Callback.ServerError; import retrofit.http.Callback.ServerError;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Provider;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
...@@ -66,14 +65,14 @@ public class RestAdapterTest { ...@@ -66,14 +65,14 @@ public class RestAdapterTest {
mockCallback = createMock(ResponseCallback.class); mockCallback = createMock(ResponseCallback.class);
mockResponse = createMock(HttpResponse.class); mockResponse = createMock(HttpResponse.class);
Server server = new Server("http://host/api/"); restAdapter = new RestAdapter.Builder()
Provider<HttpClient> httpClientProvider = new Provider<HttpClient>() { .setServer("http://host/api/")
@Override public HttpClient get() { .setClient(mockHttpClient)
return mockHttpClient; .setExecutor(mockExecutor)
} .setMainThread(mockMainThread)
}; .setHeaders(mockHeaders)
restAdapter = new RestAdapter(server, httpClientProvider, mockExecutor, mockMainThread, mockHeaders, .setConverter(new GsonConverter(GSON))
new GsonConverter(GSON), HttpProfiler.NONE); .build();
} }
@Test public void testServiceDeleteSimple() throws IOException { @Test public void testServiceDeleteSimple() throws IOException {
...@@ -392,8 +391,7 @@ public class RestAdapterTest { ...@@ -392,8 +391,7 @@ public class RestAdapterTest {
private <T extends HttpUriRequest> void expectSetOnWithRequest(final Class<T> expectedRequestClass, private <T extends HttpUriRequest> void expectSetOnWithRequest(final Class<T> expectedRequestClass,
final String expectedUri) { final String expectedUri) {
final Capture<HttpMessage> capture = new Capture<HttpMessage>(); final Capture<HttpMessage> capture = new Capture<HttpMessage>();
final Capture<String> captureMime = new Capture<String>(); mockHeaders.setOn(capture(capture));
mockHeaders.setOn(capture(capture), capture(captureMime));
expectLastCall().andAnswer(new IAnswer<Object>() { expectLastCall().andAnswer(new IAnswer<Object>() {
@Override public Object answer() throws Throwable { @Override public Object answer() throws Throwable {
T request = expectedRequestClass.cast(capture.getValue()); T request = expectedRequestClass.cast(capture.getValue());
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册