Skip to content
Browse files

Simplify callback system to generalize usage.

  • Loading branch information...
1 parent f87f9b3 commit 7008c62cfa18ee47997324a8d94e97a656431d38 @JakeWharton JakeWharton committed
View
2 checkstyle.xml
@@ -52,7 +52,7 @@
<property name="max" value="100"/>
</module>
<module name="MethodLength"/>
- <module name="ParameterNumber"/>
+ <!--module name="ParameterNumber"/-->
<!-- Checks for whitespace -->
View
65 retrofit/src/main/java/retrofit/http/Callback.java
@@ -1,4 +1,4 @@
-// Copyright 2010 Square, Inc.
+// Copyright 2012 Square, Inc.
package retrofit.http;
/**
@@ -10,65 +10,12 @@
*/
public interface Callback<T> {
- /**
- * Handles a response.
- *
- * @param t response
- */
- void call(T t);
-
- /**
- * The session expired or the account has been disabled. Prompt the user to log in again.
- *
- * @param error message to show user, or null if no message was returned
- */
- void sessionExpired(ServerError error);
-
- /** Couldn't reach the server. Check network settings and try again. */
- void networkError();
-
- /**
- * The server returned a client error. In most cases, this is a programming error, but it can also
- * signify a user input error.
- *
- * @param statusCode the HTTP response code, typically 4XX
- */
- void clientError(T response, int statusCode);
-
- /**
- * We reached the server, but it encountered an error (5xx) or its response was unparseable.
- * Please try again later.
- *
- * @param error message to show user, or null if no message was returned
- * @param statusCode the HTTP response code
- */
- void serverError(ServerError error, int statusCode);
+ /** Successful HTTP response. */
+ void success(T t);
/**
- * An unexpected error occurred. Called if the framework throws an unexpected exception or if the
- * server returns a 400 (Bad Request) error. In either case, the client software likely contains a
- * bug; otherwise, the error would have been caught sooner. The user should try updating their
- * client.
+ * Unsuccessful HTTP response due to network failure, non-2XX status code, or unexpected
+ * exception.
*/
- void unexpectedError(Throwable t);
-
- /** JSON object for parsing server error responses. */
- static final class ServerError {
- public final String message;
-
- public ServerError(String message) {
- this.message = message;
- }
-
- @Override public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- ServerError that = (ServerError) o;
- return message == null ? that.message == null : message.equals(that.message);
- }
-
- @Override public int hashCode() {
- return message != null ? message.hashCode() : 0;
- }
- }
+ void failure(RetrofitError error);
}
View
43 retrofit/src/main/java/retrofit/http/CallbackRunnable.java
@@ -1,13 +1,6 @@
// Copyright 2012 Square, Inc.
package retrofit.http;
-import retrofit.http.Callback.ServerError;
-import retrofit.http.RestException.ClientHttpException;
-import retrofit.http.RestException.NetworkException;
-import retrofit.http.RestException.ServerHttpException;
-import retrofit.http.RestException.UnauthorizedHttpException;
-import retrofit.http.RestException.UnexpectedException;
-
import java.util.concurrent.Executor;
/**
@@ -31,43 +24,13 @@
final Object response = obtainResponse();
callbackExecutor.execute(new Runnable() {
@Override public void run() {
- callback.call((T) response);
- }
- });
- } catch (final ClientHttpException ce) {
- callbackExecutor.execute(new Runnable() {
- @Override public void run() {
- callback.clientError((T) ce.getResponse(), ce.getStatus());
- }
- });
- } catch (final ServerHttpException se) {
- callbackExecutor.execute(new Runnable() {
- @Override public void run() {
- callback.serverError((ServerError) se.getResponse(), se.getStatus());
- }
- });
- } catch (final UnauthorizedHttpException ue) {
- callbackExecutor.execute(new Runnable() {
- @Override public void run() {
- callback.sessionExpired((ServerError) ue.getResponse());
- }
- });
- } catch (final NetworkException ne) {
- callbackExecutor.execute(new Runnable() {
- @Override public void run() {
- callback.networkError();
- }
- });
- } catch (final UnexpectedException ue) {
- callbackExecutor.execute(new Runnable() {
- @Override public void run() {
- callback.unexpectedError(ue.getCause());
+ callback.success((T) response);
}
});
- } catch (final Throwable t) {
+ } catch (final RetrofitError e) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
- callback.unexpectedError(t);
+ callback.failure(e);
}
});
}
View
43 retrofit/src/main/java/retrofit/http/Header.java
@@ -0,0 +1,43 @@
+// Copyright 2012 Square, Inc.
+package retrofit.http;
+
+/** Represents an HTTP header name/value pair. */
+public final class Header {
+ private final String name;
+ private final String value;
+
+ Header(String name, String value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ @Override public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Header header = (Header) o;
+
+ if (name != null ? !name.equals(header.name) : header.name != null) return false;
+ if (value != null ? !value.equals(header.value) : header.value != null) return false;
+
+ return true;
+ }
+
+ @Override public int hashCode() {
+ int result = name != null ? name.hashCode() : 0;
+ result = 31 * result + (value != null ? value.hashCode() : 0);
+ return result;
+ }
+
+ @Override public String toString() {
+ return (name != null ? name : "") + ": " + (value != null ? value : "");
+ }
+}
View
82 retrofit/src/main/java/retrofit/http/RestAdapter.java
@@ -1,3 +1,4 @@
+// Copyright 2012 Square, Inc.
package retrofit.http;
import java.io.IOException;
@@ -15,7 +16,6 @@
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.inject.Provider;
-import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
@@ -23,17 +23,7 @@
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.util.EntityUtils;
-import retrofit.http.Callback.ServerError;
import retrofit.http.HttpProfiler.RequestInformation;
-import retrofit.http.RestException.ClientHttpException;
-import retrofit.http.RestException.HttpException;
-import retrofit.http.RestException.NetworkException;
-import retrofit.http.RestException.ServerHttpException;
-import retrofit.http.RestException.UnauthorizedHttpException;
-import retrofit.http.RestException.UnexpectedException;
-
-import static java.util.logging.Level.WARNING;
-import static org.apache.http.HttpStatus.SC_UNAUTHORIZED;
/**
* Converts Java method calls to Rest calls.
@@ -50,17 +40,18 @@
private final Provider<HttpClient> httpClientProvider;
private final Executor httpExecutor;
private final Executor callbackExecutor;
- private final Headers headers;
+ private final Headers requestHeaders;
private final Converter converter;
private final HttpProfiler profiler;
private RestAdapter(Server server, Provider<HttpClient> httpClientProvider, Executor httpExecutor,
- Executor callbackExecutor, Headers headers, Converter converter, HttpProfiler profiler) {
+ Executor callbackExecutor, Headers requestHeaders, Converter converter,
+ HttpProfiler profiler) {
this.server = server;
this.httpClientProvider = httpClientProvider;
this.httpExecutor = httpExecutor;
this.callbackExecutor = callbackExecutor;
- this.headers = headers;
+ this.requestHeaders = requestHeaders;
this.converter = converter;
this.profiler = profiler;
}
@@ -79,7 +70,7 @@ private RestAdapter(Server server, Provider<HttpClient> httpClientProvider, Exec
* response will be converted to the callback's parameter type using the specified
* {@link Converter}. If the callback parameter type uses a wildcard, the lower bound will be used
* as the conversion type.</li>
- * <li>On the current thread returning the response or throwing a {@link RestException}. The HTTP
+ * <li>On the current thread returning the response or throwing a {@link RetrofitError}. The HTTP
* response will be converted to the method's return type using the specified
* {@link Converter}.</li>
* </ul>
@@ -126,11 +117,7 @@ public Object invoke(Object proxy, final Method method, final Object[] args) {
* Execute an HTTP request.
*
* @return HTTP response object of specified {@code type}.
- * @throws ClientHttpException If HTTP 4XX error occurred.
- * @throws UnauthorizedHttpException If HTTP 401 error occurred.
- * @throws ServerHttpException If HTTP 5XX error occurred.
- * @throws NetworkException If the {@code request} URL was unreachable.
- * @throws UnexpectedException If an exception was thrown while processing the request.
+ * @throws RetrofitError Thrown if any error occurs during the HTTP request.
*/
private Object invokeRequest(Method method, Object[] args, boolean isSynchronousInvocation) {
long start = System.nanoTime();
@@ -141,7 +128,7 @@ private Object invokeRequest(Method method, Object[] args, boolean isSynchronous
.setMethod(method, isSynchronousInvocation)
.setArgs(args)
.setApiUrl(url)
- .setHeaders(headers)
+ .setHeaders(requestHeaders)
.build();
url = request.getURI().toString();
@@ -182,43 +169,30 @@ private Object invokeRequest(Method method, Object[] args, boolean isSynchronous
logResponseBody(url, body, statusCode, elapsedTime);
}
- try {
- if (statusCode >= 200 && statusCode < 300) { // 2XX == successful request
- return converter.to(body, type);
- } else if (statusCode == SC_UNAUTHORIZED) { // 401 == unauthorized user
- ServerError serverError = (ServerError) converter.to(body, ServerError.class);
- throw new UnauthorizedHttpException(url, statusLine.getReasonPhrase(), serverError);
- } else if (statusCode >= 500) { // 5XX == server error
- ServerError serverError = (ServerError) converter.to(body, ServerError.class);
- throw new ServerHttpException(url, statusCode, statusLine.getReasonPhrase(),
- serverError);
- } else { // 4XX == client error
- Object clientError = converter.to(body, type);
- throw new ClientHttpException(url, statusCode, statusLine.getReasonPhrase(),
- clientError);
+ org.apache.http.Header[] realHeaders = response.getAllHeaders();
+ Header[] headers = null;
+ if (realHeaders != null) {
+ headers = new Header[realHeaders.length];
+ for (int i = 0; i < realHeaders.length; i++) {
+ org.apache.http.Header realHeader = realHeaders[i];
+ headers[i] = new Header(realHeader.getName(), realHeader.getValue());
}
- } catch (ConversionException e) {
- LOGGER.log(WARNING, e.getMessage() + " from " + url, e);
- throw new ServerHttpException(url, statusCode, statusLine.getReasonPhrase(), e);
}
- } catch (HttpException e) {
- if (LOGGER.isLoggable(Level.FINE)) {
- LOGGER.fine("Sever returned "
- + e.getStatus()
- + ", "
- + e.getMessage()
- + ". Body: "
- + e.getResponse()
- + ". Url: "
- + e.getUrl());
+
+ if (statusCode >= 200 && statusCode < 300) { // 2XX == successful request
+ try {
+ return converter.to(body, type);
+ } catch (ConversionException e) {
+ throw RetrofitError.conversionError(url, converter, statusCode, headers, body, type, e);
+ }
}
- throw e; // Allow any rest-related exceptions to pass through.
+ throw RetrofitError.httpError(url, converter, statusCode, headers, body, type);
+ } catch (RetrofitError e) {
+ throw e; // Pass through our own errors.
} catch (IOException e) {
- LOGGER.log(WARNING, e.getMessage() + " from " + url, e);
- throw new NetworkException(url, e);
+ throw RetrofitError.networkError(url, e);
} catch (Throwable t) {
- LOGGER.log(WARNING, t.getMessage() + " from " + url, t);
- throw new UnexpectedException(url, t);
+ throw RetrofitError.unexpectedError(url, t);
}
}
}
@@ -251,7 +225,7 @@ private static void logResponseBody(String url, byte[] body, int statusCode, lon
HttpEntity entity = entityReq.getEntity();
contentLength = entity.getContentLength();
- Header entityContentType = entity.getContentType();
+ org.apache.http.Header entityContentType = entity.getContentType();
contentType = entityContentType != null ? entityContentType.getValue() : null;
}
View
102 retrofit/src/main/java/retrofit/http/RestException.java
@@ -1,102 +0,0 @@
-// Copyright 2012 Square, Inc.
-package retrofit.http;
-
-import java.io.IOException;
-
-import static org.apache.http.HttpStatus.SC_UNAUTHORIZED;
-
-public abstract class RestException extends RuntimeException {
- private final String url;
-
- protected RestException(String url, String message) {
- super(message);
- this.url = url;
- }
-
- protected RestException(String url, Throwable t) {
- super(t);
- this.url = url;
- }
-
- protected RestException(String url, String message, Throwable t) {
- super(message, t);
- this.url = url;
- }
-
- public String getUrl() {
- return url;
- }
-
- /** An exception that is the result of an HTTP response. */
- public abstract static class HttpException extends RestException {
- private final int status;
- private final Object response;
-
- protected HttpException(String url, int status, String message, Object response) {
- super(url, message);
- this.status = status;
- this.response = response;
- }
-
- protected HttpException(String url, int status, String message, ConversionException cause) {
- super(url, message, cause);
- this.status = status;
- this.response = null;
- }
-
- public int getStatus() {
- return status;
- }
-
- public Object getResponse() {
- return response;
- }
- }
-
- /**
- * The server returned a client error. In most cases, this is a programming error, but it can
- * also signify a user input error.
- */
- public static class ClientHttpException extends HttpException {
- public ClientHttpException(String url, int status, String message, Object response) {
- super(url, status, message, response);
- }
- }
-
- /** We reached the server, but it encountered an error (5xx) or its response was unparseable. */
- public static class ServerHttpException extends HttpException {
- public ServerHttpException(String url, int status, String message, Object response) {
- super(url, status, message, response);
- }
-
- public ServerHttpException(String url, int status, String message, ConversionException cause) {
- super(url, status, message, cause);
- }
- }
-
- /** The session expired or the account has been disabled. Prompt the user to log in again. */
- public static class UnauthorizedHttpException extends HttpException {
- public UnauthorizedHttpException(String url, String message, Object response) {
- super(url, SC_UNAUTHORIZED, message, response);
- }
- }
-
- /** Couldn't reach the server. Check network settings and try again. */
- public static class NetworkException extends RestException {
- public NetworkException(String url, IOException e) {
- super(url, e);
- }
- }
-
- /**
- * An unexpected error occurred. Called if the framework throws an unexpected exception or if the
- * server returns a 400 (Bad Request) error. In either case, the client software likely contains
- * a bug; otherwise, the error would have been caught sooner. The user should try updating their
- * client.
- */
- public static class UnexpectedException extends RestException {
- public UnexpectedException(String url, Throwable t) {
- super(url, t);
- }
- }
-}
View
104 retrofit/src/main/java/retrofit/http/RetrofitError.java
@@ -0,0 +1,104 @@
+// Copyright 2012 Square, Inc.
+package retrofit.http;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+public class RetrofitError extends RuntimeException {
+ static RetrofitError networkError(String url, IOException exception) {
+ return new RetrofitError(url, 0, null, null, null, null, true, exception);
+ }
+
+ static RetrofitError conversionError(String url, Converter converter, int statusCode,
+ Header[] headers, byte[] body, Type successType, ConversionException exception) {
+ return new RetrofitError(url, statusCode, headers, body, converter, successType, false,
+ exception);
+ }
+
+ static RetrofitError httpError(String url, Converter converter, int statuCode, Header[] headers,
+ byte[] body, Type successType) {
+ return new RetrofitError(url, statuCode, headers, body, converter, successType, false, null);
+ }
+
+ static RetrofitError unexpectedError(String url, Throwable exception) {
+ return new RetrofitError(url, 0, null, null, null, null, false, exception);
+ }
+
+ private final String url;
+ private final Converter converter;
+ private final int statusCode;
+ private final Header[] headers;
+ private final byte[] body;
+ private final Type successType;
+ private final boolean networkError;
+ private final Throwable exception;
+
+ private RetrofitError(String url, int statusCode, Header[] headers, byte[] body,
+ Converter converter, Type successType, boolean networkError, Throwable exception) {
+ this.url = url;
+ this.converter = converter;
+ this.statusCode = statusCode;
+ this.headers = headers;
+ this.body = body;
+ this.successType = successType;
+ this.networkError = networkError;
+ this.exception = exception;
+ }
+
+ /** The request URL which produced the error. */
+ public String getUrl() {
+ return url;
+ }
+
+ /** HTTP status code of the response or 0 if no response received. */
+ public int getStatusCode() {
+ return statusCode;
+ }
+
+ /** Whether or not this error was the result of a network error. */
+ public boolean isNetworkError() {
+ return networkError;
+ }
+
+ /** List of headers returning in the HTTP response, if any. */
+ public Header[] getHeaders() {
+ return headers;
+ }
+
+ /** Raw {@code byte[]} of the HTTP response body, if any. */
+ public byte[] getRawBody() {
+ return body;
+ }
+
+ /**
+ * HTTP response body converted to the type declared by either the interface method return type or
+ * the generic type of the supplied {@link Callback} parameter.
+ */
+ public Object getBody() {
+ if (body == null) {
+ return null;
+ }
+ try {
+ return converter.to(body, successType);
+ } catch (ConversionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** HTTP response body converted to specified {@code type}. */
+ public Object getBodyAs(Type type) {
+ if (body == null) {
+ return null;
+ }
+ try {
+ return converter.to(body, type);
+ } catch (ConversionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** The exception which caused this error, if any. */
+ public Throwable getException() {
+ return exception;
+ }
+}
View
21 retrofit/src/test/java/retrofit/http/HttpRequestBuilderTest.java
@@ -1,19 +1,18 @@
-// Copyright 2011 Square, Inc.
+// Copyright 2012 Square, Inc.
package retrofit.http;
import com.google.gson.Gson;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPut;
-import org.apache.http.client.methods.HttpUriRequest;
-import org.junit.Test;
-
-import javax.inject.Named;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.util.Set;
import java.util.UUID;
+import javax.inject.Named;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.junit.Test;
import static org.fest.assertions.api.Assertions.assertThat;
import static org.fest.assertions.api.Fail.fail;
@@ -218,11 +217,7 @@ public MyJsonObj(String bodyText) {
private static class SimpleResponse {}
private class MyCallback implements Callback<SimpleResponse> {
- @Override public void call(SimpleResponse simpleResponse) {}
- @Override public void sessionExpired(ServerError error) {}
- @Override public void networkError() {}
- @Override public void clientError(SimpleResponse response, int statusCode) {}
- @Override public void serverError(ServerError error, int statusCode) {}
- @Override public void unexpectedError(Throwable t) {}
+ @Override public void success(SimpleResponse simpleResponse) {}
+ @Override public void failure(RetrofitError error) {}
}
}
View
29 retrofit/src/test/java/retrofit/http/RestAdapterTest.java
@@ -20,9 +20,6 @@
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
-import retrofit.http.Callback.ServerError;
-import retrofit.http.RestException.ClientHttpException;
-import retrofit.http.RestException.ServerHttpException;
import javax.inject.Named;
import java.io.IOException;
@@ -34,7 +31,6 @@
import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.isA;
@@ -53,7 +49,6 @@
private static final String GET_DELETE_SIMPLE_URL_WITH_PARAMS = GET_DELETE_SIMPLE_URL + "?";
private static final Gson GSON = new Gson();
private static final Response RESPONSE = new Response("some text");
- private static final ServerError SERVER_ERROR = new ServerError("danger, danger!");
private RestAdapter restAdapter;
private HttpClient mockHttpClient;
@@ -314,7 +309,7 @@
try {
service.post();
fail("Expected client exception.");
- } catch (ClientHttpException expected) {
+ } catch (RetrofitError expected) {
}
verifyAll();
}
@@ -336,7 +331,7 @@
try {
service.post();
fail("Expected server exception");
- } catch (ServerHttpException expected) {
+ } catch (RetrofitError expected) {
}
verifyAll();
}
@@ -538,7 +533,7 @@ private void verifyAll() {
String requestUrl) throws IOException {
expectAsynchronousInvocation();
expectHttpExecution(requestClass, requestUrl, RESPONSE, HttpStatus.SC_CONFLICT);
- expectClientErrorCallbacks(HttpStatus.SC_CONFLICT);
+ expectFailure();
}
private <T extends HttpUriRequest> void expectSyncLifecycleClientError(Class<T> requestClass,
@@ -549,13 +544,13 @@ private void verifyAll() {
private <T extends HttpUriRequest> void expectAsyncLifecycleServerError(Class<T> requestClass,
String requestUrl) throws IOException {
expectAsynchronousInvocation();
- expectHttpExecution(requestClass, requestUrl, SERVER_ERROR, HttpStatus.SC_NOT_IMPLEMENTED);
- expectServerErrorCallbacks(HttpStatus.SC_NOT_IMPLEMENTED);
+ expectHttpExecution(requestClass, requestUrl, new Object(), HttpStatus.SC_NOT_IMPLEMENTED);
+ expectFailure();
}
private <T extends HttpUriRequest> void expectSyncLifecycleServerError(Class<T> requestClass,
String requestUrl) throws IOException {
- expectHttpExecution(requestClass, requestUrl, SERVER_ERROR, HttpStatus.SC_NOT_IMPLEMENTED);
+ expectHttpExecution(requestClass, requestUrl, new Object(), HttpStatus.SC_NOT_IMPLEMENTED);
}
private void expectAsynchronousInvocation() {
@@ -571,17 +566,12 @@ private void expectAsynchronousInvocation() {
}
private void expectCallbacks() {
- mockCallback.call(RESPONSE);
+ mockCallback.success(RESPONSE);
expectLastCall().once();
}
- private void expectClientErrorCallbacks(int statusCode) {
- mockCallback.clientError(RESPONSE, statusCode);
- expectLastCall().once();
- }
-
- private void expectServerErrorCallbacks(int statusCode) {
- mockCallback.serverError(eq(SERVER_ERROR), eq(statusCode));
+ private void expectFailure() {
+ mockCallback.failure(isA(RetrofitError.class));
expectLastCall().once();
}
@@ -594,6 +584,7 @@ private void expectResponseCalls(String jsonToReturn, int statusCode)
expect(mockResponse.getEntity()).andReturn(new StringEntity(jsonToReturn));
expect(mockResponse.getStatusLine()).andReturn(
new BasicStatusLine(HttpVersion.HTTP_1_1, statusCode, ""));
+ expect(mockResponse.getAllHeaders()).andReturn(null);
}
private <T extends HttpUriRequest> void expectSetOnWithRequest(

0 comments on commit 7008c62

Please sign in to comment.
Something went wrong with that request. Please try again.