Repository: square/retrofit
Issue: Promote HttpException to core artifact.
Labels: Enhancement

Change adapter implementations to be deprecated but extend it.

Issue comments:

Pull request review comments:
I suppose this could have been final, and the deprecated types could have delegated to this.
probably not worth those extra lines, though.
2017 ?
Probably can’t compose because `catch` clauses are expecting a specific type.
Not actually a new type. Copied from one of the adapters.

Patch that resolved the issue:
diff --git a/retrofit-adapters/guava/src/main/java/retrofit2/adapter/guava/GuavaCallAdapterFactory.java b/retrofit-adapters/guava/src/main/java/retrofit2/adapter/guava/GuavaCallAdapterFactory.java
index b3624fc3aa..64dd0f5ad5 100644
--- a/retrofit-adapters/guava/src/main/java/retrofit2/adapter/guava/GuavaCallAdapterFactory.java
+++ b/retrofit-adapters/guava/src/main/java/retrofit2/adapter/guava/GuavaCallAdapterFactory.java
@@ -41,8 +41,8 @@
  * There are two configurations supported for the {@code ListenableFuture} type parameter:
  * <ul>
  * <li>Direct body (e.g., {@code ListenableFuture<User>}) returns the deserialized body for 2XX
- * responses, sets {@link HttpException} errors for non-2XX responses, and sets {@link IOException}
- * for network errors.</li>
+ * responses, sets {@link retrofit2.HttpException HttpException} errors for non-2XX responses, and
+ * sets {@link IOException} for network errors.</li>
  * <li>Response wrapped body (e.g., {@code ListenableFuture<Response<User>>}) returns a
  * {@link Response} object for all HTTP responses and sets {@link IOException} for network
  * errors</li>
diff --git a/retrofit-adapters/guava/src/main/java/retrofit2/adapter/guava/HttpException.java b/retrofit-adapters/guava/src/main/java/retrofit2/adapter/guava/HttpException.java
index f18a819554..4eb780eaa1 100644
--- a/retrofit-adapters/guava/src/main/java/retrofit2/adapter/guava/HttpException.java
+++ b/retrofit-adapters/guava/src/main/java/retrofit2/adapter/guava/HttpException.java
@@ -17,33 +17,10 @@
 
 import retrofit2.Response;
 
-/** Exception for an unexpected, non-2xx HTTP response. */
-public final class HttpException extends Exception {
-  private final int code;
-  private final String message;
-  private final transient Response<?> response;
-
+/** @deprecated Use {@link retrofit2.HttpException}. */
+@Deprecated
+public final class HttpException extends retrofit2.HttpException {
   public HttpException(Response<?> response) {
-    super("HTTP " + response.code() + " " + response.message());
-    this.code = response.code();
-    this.message = response.message();
-    this.response = response;
-  }
-
-  /** HTTP status code. */
-  public int code() {
-    return code;
-  }
-
-  /** HTTP status message. */
-  public String message() {
-    return message;
-  }
-
-  /**
-   * The full HTTP response. This may be null if the exception was serialized.
-   */
-  public Response<?> response() {
-    return response;
+    super(response);
   }
 }
diff --git a/retrofit-adapters/java8/src/main/java/retrofit2/adapter/java8/HttpException.java b/retrofit-adapters/java8/src/main/java/retrofit2/adapter/java8/HttpException.java
index 022fa9848e..c2d29cbbbe 100644
--- a/retrofit-adapters/java8/src/main/java/retrofit2/adapter/java8/HttpException.java
+++ b/retrofit-adapters/java8/src/main/java/retrofit2/adapter/java8/HttpException.java
@@ -17,33 +17,10 @@
 
 import retrofit2.Response;
 
-/** Exception for an unexpected, non-2xx HTTP response. */
-public final class HttpException extends Exception {
-  private final int code;
-  private final String message;
-  private final transient Response<?> response;
-
+/** @deprecated Use {@link retrofit2.HttpException}. */
+@Deprecated
+public final class HttpException extends retrofit2.HttpException {
   public HttpException(Response<?> response) {
-    super("HTTP " + response.code() + " " + response.message());
-    this.code = response.code();
-    this.message = response.message();
-    this.response = response;
-  }
-
-  /** HTTP status code. */
-  public int code() {
-    return code;
-  }
-
-  /** HTTP status message. */
-  public String message() {
-    return message;
-  }
-
-  /**
-   * The full HTTP response. This may be null if the exception was serialized.
-   */
-  public Response<?> response() {
-    return response;
+    super(response);
   }
 }
diff --git a/retrofit-adapters/java8/src/main/java/retrofit2/adapter/java8/Java8CallAdapterFactory.java b/retrofit-adapters/java8/src/main/java/retrofit2/adapter/java8/Java8CallAdapterFactory.java
index fd905475d3..35b79f1a3c 100644
--- a/retrofit-adapters/java8/src/main/java/retrofit2/adapter/java8/Java8CallAdapterFactory.java
+++ b/retrofit-adapters/java8/src/main/java/retrofit2/adapter/java8/Java8CallAdapterFactory.java
@@ -40,8 +40,8 @@
  * There are two configurations supported for the {@code CompletableFuture} type parameter:
  * <ul>
  * <li>Direct body (e.g., {@code CompletableFuture<User>}) returns the deserialized body for 2XX
- * responses, sets {@link HttpException} errors for non-2XX responses, and sets {@link IOException}
- * for network errors.</li>
+ * responses, sets {@link retrofit2.HttpException HttpException} errors for non-2XX responses, and
+ * sets {@link IOException} for network errors.</li>
  * <li>Response wrapped body (e.g., {@code CompletableFuture<Response<User>>}) returns a
  * {@link Response} object for all HTTP responses and sets {@link IOException} for network
  * errors</li>
diff --git a/retrofit-adapters/rxjava/src/main/java/retrofit2/adapter/rxjava/HttpException.java b/retrofit-adapters/rxjava/src/main/java/retrofit2/adapter/rxjava/HttpException.java
index 2e501e1216..c81ad938e6 100644
--- a/retrofit-adapters/rxjava/src/main/java/retrofit2/adapter/rxjava/HttpException.java
+++ b/retrofit-adapters/rxjava/src/main/java/retrofit2/adapter/rxjava/HttpException.java
@@ -2,38 +2,10 @@
 
 import retrofit2.Response;
 
-/** Exception for an unexpected, non-2xx HTTP response. */
-public final class HttpException extends Exception {
-  private static String getMessage(Response<?> response) {
-    if (response == null) throw new NullPointerException("response == null");
-    return "HTTP " + response.code() + " " + response.message();
-  }
-
-  private final int code;
-  private final String message;
-  private final transient Response<?> response;
-
+/** @deprecated Use {@link retrofit2.HttpException}. */
+@Deprecated
+public final class HttpException extends retrofit2.HttpException {
   public HttpException(Response<?> response) {
-    super(getMessage(response));
-    this.code = response.code();
-    this.message = response.message();
-    this.response = response;
-  }
-
-  /** HTTP status code. */
-  public int code() {
-    return code;
-  }
-
-  /** HTTP status message. */
-  public String message() {
-    return message;
-  }
-
-  /**
-   * The full HTTP response. This may be null if the exception was serialized.
-   */
-  public Response<?> response() {
-    return response;
+    super(response);
   }
 }
diff --git a/retrofit-adapters/rxjava/src/main/java/retrofit2/adapter/rxjava/RxJavaCallAdapterFactory.java b/retrofit-adapters/rxjava/src/main/java/retrofit2/adapter/rxjava/RxJavaCallAdapterFactory.java
index 184750d059..ee297ad303 100644
--- a/retrofit-adapters/rxjava/src/main/java/retrofit2/adapter/rxjava/RxJavaCallAdapterFactory.java
+++ b/retrofit-adapters/rxjava/src/main/java/retrofit2/adapter/rxjava/RxJavaCallAdapterFactory.java
@@ -20,6 +20,7 @@
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import retrofit2.CallAdapter;
+import retrofit2.HttpException;
 import retrofit2.Response;
 import retrofit2.Retrofit;
 import rx.Completable;
diff --git a/retrofit-adapters/rxjava2/src/main/java/retrofit2/adapter/rxjava2/HttpException.java b/retrofit-adapters/rxjava2/src/main/java/retrofit2/adapter/rxjava2/HttpException.java
index d612b9d5af..6e282e583d 100644
--- a/retrofit-adapters/rxjava2/src/main/java/retrofit2/adapter/rxjava2/HttpException.java
+++ b/retrofit-adapters/rxjava2/src/main/java/retrofit2/adapter/rxjava2/HttpException.java
@@ -17,38 +17,10 @@
 
 import retrofit2.Response;
 
-/** Exception for an unexpected, non-2xx HTTP response. */
-public final class HttpException extends Exception {
-  private static String getMessage(Response<?> response) {
-    if (response == null) throw new NullPointerException("response == null");
-    return "HTTP " + response.code() + " " + response.message();
-  }
-
-  private final int code;
-  private final String message;
-  private final transient Response<?> response;
-
+/** @deprecated Use {@link retrofit2.HttpException}. */
+@Deprecated
+public final class HttpException extends retrofit2.HttpException {
   public HttpException(Response<?> response) {
-    super(getMessage(response));
-    this.code = response.code();
-    this.message = response.message();
-    this.response = response;
-  }
-
-  /** HTTP status code. */
-  public int code() {
-    return code;
-  }
-
-  /** HTTP status message. */
-  public String message() {
-    return message;
-  }
-
-  /**
-   * The full HTTP response. This may be null if the exception was serialized.
-   */
-  public Response<?> response() {
-    return response;
+    super(response);
   }
 }
diff --git a/retrofit-adapters/rxjava2/src/main/java/retrofit2/adapter/rxjava2/RxJava2CallAdapterFactory.java b/retrofit-adapters/rxjava2/src/main/java/retrofit2/adapter/rxjava2/RxJava2CallAdapterFactory.java
index dfe4b317b7..e4b685999d 100644
--- a/retrofit-adapters/rxjava2/src/main/java/retrofit2/adapter/rxjava2/RxJava2CallAdapterFactory.java
+++ b/retrofit-adapters/rxjava2/src/main/java/retrofit2/adapter/rxjava2/RxJava2CallAdapterFactory.java
@@ -26,6 +26,7 @@
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import retrofit2.CallAdapter;
+import retrofit2.HttpException;
 import retrofit2.Response;
 import retrofit2.Retrofit;
 
diff --git a/retrofit/src/main/java/retrofit2/HttpException.java b/retrofit/src/main/java/retrofit2/HttpException.java
new file mode 100644
index 0000000000..0018948edb
--- /dev/null
+++ b/retrofit/src/main/java/retrofit2/HttpException.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 Square, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package retrofit2;
+
+/** Exception for an unexpected, non-2xx HTTP response. */
+public class HttpException extends Exception {
+  private static String getMessage(Response<?> response) {
+    if (response == null) throw new NullPointerException("response == null");
+    return "HTTP " + response.code() + " " + response.message();
+  }
+
+  private final int code;
+  private final String message;
+  private final transient Response<?> response;
+
+  public HttpException(Response<?> response) {
+    super(getMessage(response));
+    this.code = response.code();
+    this.message = response.message();
+    this.response = response;
+  }
+
+  /** HTTP status code. */
+  public int code() {
+    return code;
+  }
+
+  /** HTTP status message. */
+  public String message() {
+    return message;
+  }
+
+  /**
+   * The full HTTP response. This may be null if the exception was serialized.
+   */
+  public Response<?> response() {
+    return response;
+  }
+}


Test patch:
diff --git a/retrofit-adapters/guava/src/test/java/retrofit2/adapter/guava/ListenableFutureTest.java b/retrofit-adapters/guava/src/test/java/retrofit2/adapter/guava/ListenableFutureTest.java
index fb3ccb3da2..f3ceeca624 100644
--- a/retrofit-adapters/guava/src/test/java/retrofit2/adapter/guava/ListenableFutureTest.java
+++ b/retrofit-adapters/guava/src/test/java/retrofit2/adapter/guava/ListenableFutureTest.java
@@ -65,7 +65,9 @@ interface Service {
       future.get();
       fail();
     } catch (ExecutionException e) {
-      assertThat(e.getCause()).isInstanceOf(HttpException.class)
+      assertThat(e.getCause())
+          .isInstanceOf(HttpException.class) // Required for backwards compatibility.
+          .isInstanceOf(retrofit2.HttpException.class)
           .hasMessage("HTTP 404 Client Error");
     }
   }
diff --git a/retrofit-adapters/java8/src/test/java/retrofit2/adapter/java8/CompletableFutureTest.java b/retrofit-adapters/java8/src/test/java/retrofit2/adapter/java8/CompletableFutureTest.java
index d4a00a1dc6..ee06a31eb4 100644
--- a/retrofit-adapters/java8/src/test/java/retrofit2/adapter/java8/CompletableFutureTest.java
+++ b/retrofit-adapters/java8/src/test/java/retrofit2/adapter/java8/CompletableFutureTest.java
@@ -65,7 +65,9 @@ interface Service {
       future.get();
       fail();
     } catch (ExecutionException e) {
-      assertThat(e.getCause()).isInstanceOf(HttpException.class)
+      assertThat(e.getCause())
+          .isInstanceOf(HttpException.class) // Required for backwards compatibility.
+          .isInstanceOf(retrofit2.HttpException.class)
           .hasMessage("HTTP 404 Client Error");
     }
   }
diff --git a/retrofit-adapters/rxjava/src/test/java/retrofit2/adapter/rxjava/CompletableTest.java b/retrofit-adapters/rxjava/src/test/java/retrofit2/adapter/rxjava/CompletableTest.java
index b10ab34aff..3d84b39f22 100644
--- a/retrofit-adapters/rxjava/src/test/java/retrofit2/adapter/rxjava/CompletableTest.java
+++ b/retrofit-adapters/rxjava/src/test/java/retrofit2/adapter/rxjava/CompletableTest.java
@@ -60,6 +60,7 @@ interface Service {
 
     RecordingSubscriber<Void> subscriber = subscriberRule.create();
     service.completable().unsafeSubscribe(subscriber);
+    // Required for backwards compatibility.
     subscriber.assertError(HttpException.class, "HTTP 404 Client Error");
   }
 
diff --git a/retrofit-adapters/rxjava/src/test/java/retrofit2/adapter/rxjava/ObservableTest.java b/retrofit-adapters/rxjava/src/test/java/retrofit2/adapter/rxjava/ObservableTest.java
index 53dcc07709..4a8d8652f2 100644
--- a/retrofit-adapters/rxjava/src/test/java/retrofit2/adapter/rxjava/ObservableTest.java
+++ b/retrofit-adapters/rxjava/src/test/java/retrofit2/adapter/rxjava/ObservableTest.java
@@ -65,6 +65,7 @@ interface Service {
 
     RecordingSubscriber<String> subscriber = subscriberRule.create();
     service.body().unsafeSubscribe(subscriber);
+    // Required for backwards compatibility.
     subscriber.assertError(HttpException.class, "HTTP 404 Client Error");
   }
 
diff --git a/retrofit-adapters/rxjava/src/test/java/retrofit2/adapter/rxjava/SingleTest.java b/retrofit-adapters/rxjava/src/test/java/retrofit2/adapter/rxjava/SingleTest.java
index c4c35ab457..babd05c73a 100644
--- a/retrofit-adapters/rxjava/src/test/java/retrofit2/adapter/rxjava/SingleTest.java
+++ b/retrofit-adapters/rxjava/src/test/java/retrofit2/adapter/rxjava/SingleTest.java
@@ -65,6 +65,7 @@ interface Service {
 
     RecordingSubscriber<String> subscriber = subscriberRule.create();
     service.body().unsafeSubscribe(subscriber);
+    // Required for backwards compatibility.
     subscriber.assertError(HttpException.class, "HTTP 404 Client Error");
   }
 
diff --git a/retrofit-adapters/rxjava2/src/test/java/retrofit2/adapter/rxjava2/CompletableTest.java b/retrofit-adapters/rxjava2/src/test/java/retrofit2/adapter/rxjava2/CompletableTest.java
index 64fb3870ae..999cf31bde 100644
--- a/retrofit-adapters/rxjava2/src/test/java/retrofit2/adapter/rxjava2/CompletableTest.java
+++ b/retrofit-adapters/rxjava2/src/test/java/retrofit2/adapter/rxjava2/CompletableTest.java
@@ -59,6 +59,7 @@ interface Service {
 
     RecordingCompletableObserver observer = observerRule.create();
     service.completable().subscribe(observer);
+    // Required for backwards compatibility.
     observer.assertError(HttpException.class, "HTTP 404 Client Error");
   }
 
diff --git a/retrofit-adapters/rxjava2/src/test/java/retrofit2/adapter/rxjava2/FlowableTest.java b/retrofit-adapters/rxjava2/src/test/java/retrofit2/adapter/rxjava2/FlowableTest.java
index 042b8028dc..22efcee2bf 100644
--- a/retrofit-adapters/rxjava2/src/test/java/retrofit2/adapter/rxjava2/FlowableTest.java
+++ b/retrofit-adapters/rxjava2/src/test/java/retrofit2/adapter/rxjava2/FlowableTest.java
@@ -63,6 +63,7 @@ interface Service {
 
     RecordingSubscriber<String> subscriber = subscriberRule.create();
     service.body().subscribe(subscriber);
+    // Required for backwards compatibility.
     subscriber.assertError(HttpException.class, "HTTP 404 Client Error");
   }
 
diff --git a/retrofit-adapters/rxjava2/src/test/java/retrofit2/adapter/rxjava2/HttpExceptionTest.java b/retrofit-adapters/rxjava2/src/test/java/retrofit2/adapter/rxjava2/HttpExceptionTest.java
deleted file mode 100644
index da781ffd05..0000000000
--- a/retrofit-adapters/rxjava2/src/test/java/retrofit2/adapter/rxjava2/HttpExceptionTest.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2016 Square, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package retrofit2.adapter.rxjava2;
-
-import org.junit.Test;
-import retrofit2.Response;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.fail;
-
-public final class HttpExceptionTest {
-  @Test public void response() {
-    Response<String> response = Response.success("Hi");
-    HttpException exception = new HttpException(response);
-    assertThat(exception.code()).isEqualTo(200);
-    assertThat(exception.message()).isEqualTo("OK");
-    assertThat(exception.response()).isSameAs(response);
-  }
-
-  @Test public void nullResponseThrows() {
-    try {
-      new HttpException(null);
-      fail();
-    } catch (NullPointerException e) {
-      assertThat(e).hasMessage("response == null");
-    }
-  }
-}
diff --git a/retrofit-adapters/rxjava2/src/test/java/retrofit2/adapter/rxjava2/MaybeTest.java b/retrofit-adapters/rxjava2/src/test/java/retrofit2/adapter/rxjava2/MaybeTest.java
index d5eda7d9ec..21f69c68aa 100644
--- a/retrofit-adapters/rxjava2/src/test/java/retrofit2/adapter/rxjava2/MaybeTest.java
+++ b/retrofit-adapters/rxjava2/src/test/java/retrofit2/adapter/rxjava2/MaybeTest.java
@@ -64,6 +64,7 @@ interface Service {
 
     RecordingMaybeObserver<String> observer = observerRule.create();
     service.body().subscribe(observer);
+    // Required for backwards compatibility.
     observer.assertError(HttpException.class, "HTTP 404 Client Error");
   }
 
diff --git a/retrofit-adapters/rxjava2/src/test/java/retrofit2/adapter/rxjava2/ObservableTest.java b/retrofit-adapters/rxjava2/src/test/java/retrofit2/adapter/rxjava2/ObservableTest.java
index be8f5c31a6..e421c16526 100644
--- a/retrofit-adapters/rxjava2/src/test/java/retrofit2/adapter/rxjava2/ObservableTest.java
+++ b/retrofit-adapters/rxjava2/src/test/java/retrofit2/adapter/rxjava2/ObservableTest.java
@@ -63,6 +63,7 @@ interface Service {
 
     RecordingObserver<String> observer = observerRule.create();
     service.body().subscribe(observer);
+    // Required for backwards compatibility.
     observer.assertError(HttpException.class, "HTTP 404 Client Error");
   }
 
diff --git a/retrofit-adapters/rxjava2/src/test/java/retrofit2/adapter/rxjava2/SingleTest.java b/retrofit-adapters/rxjava2/src/test/java/retrofit2/adapter/rxjava2/SingleTest.java
index 9ebe67e94e..dba2dd62aa 100644
--- a/retrofit-adapters/rxjava2/src/test/java/retrofit2/adapter/rxjava2/SingleTest.java
+++ b/retrofit-adapters/rxjava2/src/test/java/retrofit2/adapter/rxjava2/SingleTest.java
@@ -63,6 +63,7 @@ interface Service {
 
     RecordingSingleObserver<String> observer = observerRule.create();
     service.body().subscribe(observer);
+    // Required for backwards compatibility.
     observer.assertError(HttpException.class, "HTTP 404 Client Error");
   }
 
diff --git a/retrofit-adapters/rxjava/src/test/java/retrofit2/adapter/rxjava/HttpExceptionTest.java b/retrofit/src/test/java/retrofit2/HttpExceptionTest.java
similarity index 95%
rename from retrofit-adapters/rxjava/src/test/java/retrofit2/adapter/rxjava/HttpExceptionTest.java
rename to retrofit/src/test/java/retrofit2/HttpExceptionTest.java
index 2e77f06c16..37fa3722c4 100644
--- a/retrofit-adapters/rxjava/src/test/java/retrofit2/adapter/rxjava/HttpExceptionTest.java
+++ b/retrofit/src/test/java/retrofit2/HttpExceptionTest.java
@@ -13,10 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package retrofit2.adapter.rxjava;
+package retrofit2;
 
 import org.junit.Test;
-import retrofit2.Response;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.fail;