A Retrofit CallAdapter for Guava's ListenableFuture
npx @tessl/cli install tessl/maven-com-squareup-retrofit2--adapter-guava@3.0.00
# Retrofit Guava Adapter
1
2
A Retrofit CallAdapter for Guava's ListenableFuture that enables seamless integration between Retrofit HTTP client and Guava's asynchronous programming model. This adapter automatically converts Retrofit's Call objects into ListenableFuture instances, allowing service methods to return ListenableFuture instead of Call objects.
3
4
## Package Information
5
6
- **Package Name**: adapter-guava
7
- **Package Coordinates**: com.squareup.retrofit2:adapter-guava
8
- **Package Type**: maven
9
- **Language**: Java
10
- **Installation**:
11
```xml
12
<dependency>
13
<groupId>com.squareup.retrofit2</groupId>
14
<artifactId>adapter-guava</artifactId>
15
<version>3.0.0</version>
16
</dependency>
17
```
18
Gradle:
19
```groovy
20
implementation 'com.squareup.retrofit2:adapter-guava:3.0.0'
21
```
22
23
## Core Imports
24
25
```java
26
import retrofit2.adapter.guava.GuavaCallAdapterFactory;
27
import com.google.common.util.concurrent.ListenableFuture;
28
import retrofit2.HttpException; // For error handling
29
```
30
31
For deprecated HttpException (not recommended):
32
```java
33
import retrofit2.adapter.guava.HttpException; // Deprecated - use retrofit2.HttpException instead
34
```
35
36
## Basic Usage
37
38
```java
39
import retrofit2.Retrofit;
40
import retrofit2.adapter.guava.GuavaCallAdapterFactory;
41
import com.google.common.util.concurrent.ListenableFuture;
42
43
// Add the adapter to Retrofit
44
Retrofit retrofit = new Retrofit.Builder()
45
.baseUrl("https://api.example.com/")
46
.addCallAdapterFactory(GuavaCallAdapterFactory.create())
47
.build();
48
49
// Define service interface with ListenableFuture return types
50
interface ApiService {
51
@GET("users/{id}")
52
ListenableFuture<User> getUser(@Path("id") String userId);
53
54
@GET("users/{id}")
55
ListenableFuture<Response<User>> getUserWithResponse(@Path("id") String userId);
56
}
57
58
// Use the service
59
ApiService service = retrofit.create(ApiService.class);
60
ListenableFuture<User> userFuture = service.getUser("123");
61
62
// Add callbacks using Guava's Futures utility
63
Futures.addCallback(userFuture, new FutureCallback<User>() {
64
@Override
65
public void onSuccess(User user) {
66
// Handle successful response
67
}
68
69
@Override
70
public void onFailure(Throwable throwable) {
71
// Handle error (retrofit2.HttpException for HTTP errors, IOException for network errors)
72
}
73
}, MoreExecutors.directExecutor());
74
```
75
76
## Architecture
77
78
The adapter provides two main response handling modes:
79
80
- **Direct Body Mode** (`ListenableFuture<T>`): Returns the deserialized response body for successful HTTP responses (2XX), and sets exceptions for errors
81
- **Response Wrapped Mode** (`ListenableFuture<Response<T>>`): Returns the complete Response object for all HTTP responses, allowing access to headers and status codes
82
83
The adapter automatically handles:
84
- HTTP error responses (non-2XX) by setting retrofit2.HttpException
85
- Network failures by propagating IOException
86
- Future cancellation by cancelling the underlying Retrofit Call through `CallCancelListenableFuture`
87
88
**Internal Implementation:**
89
- Uses `BodyCallAdapter` for direct body responses (`ListenableFuture<T>`)
90
- Uses `ResponseCallAdapter` for Response-wrapped responses (`ListenableFuture<Response<T>>`)
91
- Employs `CallCancelListenableFuture` (extends Guava's `AbstractFuture`) to handle proper cancellation propagation
92
93
## Capabilities
94
95
### GuavaCallAdapterFactory
96
97
Factory class for creating Guava ListenableFuture call adapters.
98
99
```java { .api }
100
/**
101
* A call adapter factory which creates Guava futures.
102
* Adding this class to Retrofit allows you to return ListenableFuture from service methods.
103
*/
104
public final class GuavaCallAdapterFactory extends CallAdapter.Factory {
105
106
/**
107
* Creates a new instance of GuavaCallAdapterFactory
108
* @return GuavaCallAdapterFactory instance
109
*/
110
public static GuavaCallAdapterFactory create();
111
112
/**
113
* Returns a call adapter for interface methods that return ListenableFuture
114
* @param returnType the return type to adapt
115
* @param annotations annotations on the declaring method
116
* @param retrofit the current Retrofit instance
117
* @return CallAdapter for ListenableFuture types, or null if not applicable
118
*/
119
@Override
120
public @Nullable CallAdapter<?, ?> get(
121
Type returnType,
122
Annotation[] annotations,
123
Retrofit retrofit
124
);
125
}
126
```
127
128
### Supported Return Types
129
130
The adapter supports two configurations for ListenableFuture type parameters:
131
132
```java { .api }
133
// Direct body - returns deserialized body for 2XX responses
134
// Sets retrofit2.HttpException for non-2XX responses, IOException for network errors
135
ListenableFuture<User> getUser();
136
137
// Response wrapped - returns Response object for all HTTP responses
138
// Sets IOException for network errors only
139
ListenableFuture<Response<User>> getUserWithResponse();
140
```
141
142
### HttpException (Deprecated)
143
144
```java { .api }
145
/**
146
* @deprecated Use retrofit2.HttpException instead
147
*/
148
@Deprecated
149
public final class HttpException extends retrofit2.HttpException {
150
151
/**
152
* Creates HTTP exception from Retrofit Response
153
* @param response the HTTP response that caused the exception
154
*/
155
public HttpException(Response<?> response);
156
}
157
```
158
159
## Error Handling
160
161
The adapter provides comprehensive error handling:
162
163
- **HTTP Errors (non-2XX responses)**: For direct body mode (`ListenableFuture<T>`), the future is set with a `retrofit2.HttpException` containing the response details
164
- **Network Errors**: IOException is propagated for network-level failures
165
- **Invalid Return Types**: `IllegalStateException` is thrown for:
166
- Non-parameterized ListenableFuture return types: "ListenableFuture return type must be parameterized as ListenableFuture<Foo> or ListenableFuture<? extends Foo>"
167
- Non-parameterized Response types in Response-wrapped mode: "Response must be parameterized as Response<Foo> or Response<? extends Foo>"
168
- **Future Cancellation**: Cancelling the returned ListenableFuture automatically cancels the underlying Retrofit Call
169
170
## Type Safety
171
172
The adapter maintains full type safety:
173
174
```java { .api }
175
// Parameterized types are required
176
ListenableFuture<User> validReturn(); // ✓ Valid
177
ListenableFuture<Response<User>> validReturn(); // ✓ Valid
178
179
ListenableFuture invalidReturn(); // ✗ IllegalStateException
180
ListenableFuture<Response> invalidReturn(); // ✗ IllegalStateException
181
```
182
183
## Integration with Guava
184
185
The adapter creates ListenableFuture instances that are fully compatible with Guava's concurrency utilities:
186
187
- **Futures.addCallback()**: Add success/failure callbacks
188
- **Futures.transform()**: Transform results asynchronously
189
- **Futures.allAsList()**: Combine multiple futures
190
- **Future cancellation**: Properly cancels underlying HTTP calls
191
- **Executor integration**: Work with any Executor for callback execution
192
193
## Dependencies
194
195
Required dependencies:
196
- **retrofit2**: Core Retrofit library
197
- **com.google.common.util.concurrent**: Guava's ListenableFuture and related utilities
198
- **javax.annotation.Nullable**: For null safety annotations
199
200
**Module Configuration:**
201
- **Automatic Module Name**: `retrofit2.adapter.guava`
202
- **Gradle Configuration**: API dependencies on both `retrofit2` and `guava`