提交 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;
* @author Bob Lee (bob@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());
......@@ -51,7 +51,7 @@ public class CallbackResponseHandler<R> implements ResponseHandler<Void> {
* @return parsed 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)) {
try {
entity = HttpClients.copyAndLog(entity, requestUrl, start, dateFormat.get());
......
......@@ -10,6 +10,6 @@ import org.apache.http.HttpMessage;
*/
public interface Headers {
/** Sets headers on the given message, with the specified MIME type */
void setOn(HttpMessage message, String mimeType);
/** Sets headers on the given message */
void setOn(HttpMessage message);
}
......@@ -6,6 +6,7 @@ import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import org.apache.http.HttpMessage;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
......@@ -34,7 +35,7 @@ public enum HttpMethodType {
throws URISyntaxException {
URI uri = getParameterizedUri(builder);
HttpGet request = new HttpGet(uri);
builder.getHeaders().setOn(request, builder.getMimeType());
addHeaders(request, builder);
return request;
}
},
......@@ -45,7 +46,7 @@ public enum HttpMethodType {
URI uri = getUri(builder);
HttpPost request = new HttpPost(uri);
addParams(request, builder);
builder.getHeaders().setOn(request, builder.getMimeType());
addHeaders(request, builder);
return request;
}
},
......@@ -56,7 +57,7 @@ public enum HttpMethodType {
URI uri = getUri(builder);
HttpPut request = new HttpPut(uri);
addParams(request, builder);
builder.getHeaders().setOn(request, builder.getMimeType());
addHeaders(request, builder);
return request;
}
},
......@@ -66,7 +67,7 @@ public enum HttpMethodType {
throws URISyntaxException {
URI uri = getParameterizedUri(builder);
HttpDelete request = new HttpDelete(uri);
builder.getHeaders().setOn(request, builder.getMimeType());
addHeaders(request, builder);
return request;
}
};
......@@ -96,6 +97,17 @@ public enum HttpMethodType {
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
* object.
......@@ -169,4 +181,4 @@ public enum HttpMethodType {
return false;
}
}
\ No newline at end of file
}
......@@ -8,15 +8,6 @@ package retrofit.http;
*/
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
* 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;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
......@@ -22,7 +21,6 @@ import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
......@@ -47,7 +45,7 @@ public class RestAdapter {
private final Converter converter;
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) {
this.server = server;
this.httpClientProvider = httpClientProvider;
......@@ -128,7 +126,7 @@ public class RestAdapter {
ResponseHandler<Void> rh = new CallbackResponseHandler(callback, type, converter, url, start, DATE_FORMAT);
// 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);
}
......@@ -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));
}
/** Sends server call times and response status codes to {@link HttpProfiler}. */
private static class ProfilingResponseHandler<T> implements ResponseHandler<Void> {
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. */
private ProfilingResponseHandler(ResponseHandler<Void> delegate, HttpProfiler<T> profiler,
HttpProfiler.RequestInformation requestInfo, long startTime) {
this.delegate = delegate;
this.profiler = profiler;
this.requestInfo = requestInfo;
this.startTime = startTime;
/**
* Build a new {@link RestAdapter}.
* <p/>
* Calling the following methods is required before calling {@link #build()}:
* <ul>
* <li>{@link #setServer(Server)}</li>
* <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));
}
public void beforeCall() {
try {
beforeCallData.set(profiler.beforeCall());
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Error occurred in HTTP profiler beforeCall().", e);
}
public Builder setServer(Server server) {
this.server = server;
return this;
}
public Builder setClient(final HttpClient client) {
return setClient(new Provider<HttpClient>() {
@Override public HttpClient get() {
return client;
}
});
}
@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();
public Builder setClient(Provider<HttpClient> clientProvider) {
this.clientProvider = clientProvider;
return this;
}
try {
profiler.afterCall(requestInfo, elapsedTime, statusCode, beforeCallData.get());
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Error occurred in HTTP profiler afterCall().", e);
}
public Builder setExecutor(Executor executor) {
this.executor = executor;
return this;
}
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;
}
public RestAdapter build() {
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");
// Pass along the response to the normal handler.
return delegate.handleResponse(httpResponse);
return new RestAdapter(server, clientProvider, executor, mainThread, headers, converter, profiler);
}
}
}
\ No newline at end of file
......@@ -2,7 +2,6 @@
package retrofit.http;
import com.google.gson.Gson;
import org.apache.http.HttpMessage;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
......@@ -23,10 +22,6 @@ import static org.fest.assertions.Fail.fail;
public class HttpRequestBuilderTest {
private static final Gson GSON = new Gson();
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 {
expectParams("");
......@@ -205,7 +200,6 @@ public class HttpRequestBuilderTest {
.setMethod(method)
.setArgs(args)
.setApiUrl(API_URL)
.setHeaders(BLANK_HEADERS)
.build();
}
......
......@@ -23,7 +23,6 @@ import org.junit.Test;
import retrofit.http.Callback.ServerError;
import javax.inject.Named;
import javax.inject.Provider;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
......@@ -66,14 +65,14 @@ public class RestAdapterTest {
mockCallback = createMock(ResponseCallback.class);
mockResponse = createMock(HttpResponse.class);
Server server = new Server("http://host/api/");
Provider<HttpClient> httpClientProvider = new Provider<HttpClient>() {
@Override public HttpClient get() {
return mockHttpClient;
}
};
restAdapter = new RestAdapter(server, httpClientProvider, mockExecutor, mockMainThread, mockHeaders,
new GsonConverter(GSON), HttpProfiler.NONE);
restAdapter = new RestAdapter.Builder()
.setServer("http://host/api/")
.setClient(mockHttpClient)
.setExecutor(mockExecutor)
.setMainThread(mockMainThread)
.setHeaders(mockHeaders)
.setConverter(new GsonConverter(GSON))
.build();
}
@Test public void testServiceDeleteSimple() throws IOException {
......@@ -392,8 +391,7 @@ public class RestAdapterTest {
private <T extends HttpUriRequest> void expectSetOnWithRequest(final Class<T> expectedRequestClass,
final String expectedUri) {
final Capture<HttpMessage> capture = new Capture<HttpMessage>();
final Capture<String> captureMime = new Capture<String>();
mockHeaders.setOn(capture(capture), capture(captureMime));
mockHeaders.setOn(capture(capture));
expectLastCall().andAnswer(new IAnswer<Object>() {
@Override public Object answer() throws Throwable {
T request = expectedRequestClass.cast(capture.getValue());
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册