View Javadoc

1   package com.atlassian.httpclient.api;
2   
3   import com.atlassian.util.concurrent.Promise;
4   import com.google.common.base.Function;
5   
6   import javax.annotation.Nullable;
7   
8   import static com.atlassian.httpclient.api.ResponsePromiseMapFunction.StatusRange;
9   import static com.google.common.base.Preconditions.checkNotNull;
10  
11  public final class DefaultResponseTransformation<T> implements ResponseTransformation<T> {
12      private final ResponsePromiseMapFunction<T> mapFunctions;
13      private final Function<Throwable, ? extends T> failFunction;
14  
15      private DefaultResponseTransformation(ResponsePromiseMapFunction<T> mapFunctions, Function<Throwable, ? extends T> failFunction) {
16          this.mapFunctions = mapFunctions;
17          this.failFunction = failFunction;
18      }
19  
20      @Override
21      public Function<Throwable, ? extends T> getFailFunction() {
22          return failFunction;
23      }
24  
25      @Override
26      public Function<Response, T> getSuccessFunctions() {
27          return mapFunctions;
28      }
29  
30      @Override
31      public Promise<T> apply(final ResponsePromise responsePromise) {
32          return responsePromise.transform(this);
33      }
34  
35      public static <T> Builder<T> builder() {
36          return new DefaultResponseTransformationBuilder<T>();
37      }
38  
39      private static class DefaultResponseTransformationBuilder<T> implements Builder<T> {
40          private final ResponsePromiseMapFunction.ResponsePromiseMapFunctionBuilder<T> builder = ResponsePromiseMapFunction.builder();
41          private Function<Throwable, ? extends T> failFunction = defaultThrowableHandler();
42  
43          @Override
44          public Builder<T> on(final HttpStatus status, final Function<Response, ? extends T> f) {
45              return addSingle(status, f);
46          }
47  
48          @Override
49          public Builder<T> on(int statusCode, Function<Response, ? extends T> f) {
50              return addSingle(statusCode, f);
51          }
52  
53          @Override
54          public Builder<T> informational(Function<Response, ? extends T> f) {
55              return addRange(HttpStatus.CONTINUE, f);
56          }
57  
58          // 2xx
59          @Override
60          public Builder<T> successful(Function<Response, ? extends T> f) {
61              return addRange(HttpStatus.OK, f);
62          }
63  
64          @Override
65          public Builder<T> ok(Function<Response, ? extends T> f) {
66              return addSingle(HttpStatus.OK, f);
67          }
68  
69          @Override
70          public Builder<T> created(Function<Response, ? extends T> f) {
71              return addSingle(HttpStatus.CREATED, f);
72          }
73  
74          @Override
75          public Builder<T> noContent(Function<Response, ? extends T> f) {
76              return addSingle(HttpStatus.NO_CONTENT, f);
77          }
78  
79          // 3xx
80          @Override
81          public Builder<T> redirection(Function<Response, ? extends T> f) {
82              return addRange(HttpStatus.MULTIPLE_CHOICES, f);
83          }
84  
85          @Override
86          public Builder<T> seeOther(Function<Response, ? extends T> f) {
87              return addSingle(HttpStatus.SEE_OTHER, f);
88          }
89  
90          @Override
91          public Builder<T> notModified(Function<Response, ? extends T> f) {
92              return addSingle(HttpStatus.NOT_MODIFIED, f);
93          }
94  
95          // 4xx
96          @Override
97          public Builder<T> clientError(Function<Response, ? extends T> f) {
98              return addRange(HttpStatus.BAD_REQUEST, f);
99          }
100 
101         @Override
102         public Builder<T> badRequest(Function<Response, ? extends T> f) {
103             return addSingle(HttpStatus.BAD_REQUEST, f);
104         }
105 
106         @Override
107         public Builder<T> unauthorized(Function<Response, ? extends T> f) {
108             return addSingle(HttpStatus.UNAUTHORIZED, f);
109         }
110 
111         @Override
112         public Builder<T> forbidden(Function<Response, ? extends T> f) {
113             return addSingle(HttpStatus.FORBIDDEN, f);
114         }
115 
116         @Override
117         public Builder<T> notFound(Function<Response, ? extends T> f) {
118             return addSingle(HttpStatus.NOT_FOUND, f);
119         }
120 
121         @Override
122         public Builder<T> conflict(Function<Response, ? extends T> f) {
123             return addSingle(HttpStatus.CONFLICT, f);
124         }
125 
126         // 5xx
127         @Override
128         public Builder<T> serverError(Function<Response, ? extends T> f) {
129             return addRange(HttpStatus.INTERNAL_SERVER_ERROR, f);
130         }
131 
132         @Override
133         public Builder<T> internalServerError(Function<Response, ? extends T> f) {
134             return addSingle(HttpStatus.INTERNAL_SERVER_ERROR, f);
135         }
136 
137         @Override
138         public Builder<T> serviceUnavailable(Function<Response, ? extends T> f) {
139             return addSingle(HttpStatus.SERVICE_UNAVAILABLE, f);
140         }
141 
142         // 4xx and 5xx
143         @Override
144         public Builder<T> error(Function<Response, ? extends T> f) {
145             builder.addStatusRangeFunction(
146                     new OrStatusRange(new HundredsStatusRange(HttpStatus.BAD_REQUEST),
147                             new HundredsStatusRange(HttpStatus.INTERNAL_SERVER_ERROR)), f);
148 
149             return this;
150         }
151 
152         // 1xx, 3xx, 4xx and 5xx
153         @Override
154         public Builder<T> notSuccessful(Function<Response, ? extends T> f) {
155             builder.addStatusRangeFunction(new NotInStatusRange(new HundredsStatusRange(HttpStatus.OK)), f);
156             return this;
157         }
158 
159         @Override
160         public Builder<T> others(Function<Response, ? extends T> f) {
161             builder.setOthersFunction(f);
162             return this;
163         }
164 
165         @Override
166         public Builder<T> otherwise(final Function<Throwable, T> callback) {
167             others(new Function<Response, T>() {
168                 @Override
169                 public T apply(@Nullable Response input) {
170                     return callback.apply(new UnexpectedResponseException(input));
171                 }
172             });
173             fail(callback);
174             return this;
175         }
176 
177         @Override
178         public Builder<T> done(final Function<Response, T> f) {
179             others(new Function<Response, T>() {
180                 @Override
181                 public T apply(@Nullable Response input) {
182                     return f.apply(input);
183                 }
184             });
185             return this;
186         }
187 
188         @Override
189         public Builder<T> fail(Function<Throwable, ? extends T> f) {
190             this.failFunction = f;
191             return this;
192         }
193 
194         private DefaultResponseTransformationBuilder<T> addSingle(HttpStatus status, Function<Response, ? extends T> f) {
195             return addSingle(status.code, f);
196         }
197 
198         private DefaultResponseTransformationBuilder<T> addSingle(int statusCode, Function<Response, ? extends T> f) {
199             builder.addStatusRangeFunction(new SingleStatusRange(statusCode), f);
200             return this;
201         }
202 
203         private DefaultResponseTransformationBuilder<T> addRange(HttpStatus status, Function<Response, ? extends T> f) {
204             builder.addStatusRangeFunction(new HundredsStatusRange(status), f);
205             return this;
206         }
207 
208 
209         private Function<Throwable, ? extends T> defaultThrowableHandler() {
210             return new Function<Throwable, T>() {
211                 @Override
212                 public T apply(Throwable throwable) {
213                     if (throwable instanceof RuntimeException) {
214                         throw (RuntimeException) throwable;
215                     }
216                     throw new ResponseTransformationException(throwable);
217                 }
218             };
219         }
220 
221         @Override
222         public ResponseTransformation<T> build() {
223             return new DefaultResponseTransformation<T>(builder.build(), failFunction);
224         }
225     }
226 
227     static final class SingleStatusRange implements StatusRange {
228         private final int statusCode;
229 
230         SingleStatusRange(int statusCode) {
231             this.statusCode = checkNotNull(statusCode);
232         }
233 
234         @Override
235         public boolean isIn(int code) {
236             return this.statusCode == code;
237         }
238     }
239 
240     static final class HundredsStatusRange implements StatusRange {
241         private final HttpStatus status;
242 
243         private HundredsStatusRange(HttpStatus status) {
244             this.status = checkNotNull(status);
245         }
246 
247         @Override
248         public boolean isIn(int code) {
249             final int diff = code - status.code;
250             return 0 <= diff && diff < 100;
251         }
252     }
253 
254     static final class NotInStatusRange implements StatusRange {
255         private final StatusRange range;
256 
257         private NotInStatusRange(StatusRange range) {
258             this.range = checkNotNull(range);
259         }
260 
261         @Override
262         public boolean isIn(int code) {
263             return !range.isIn(code);
264         }
265     }
266 
267     static final class OrStatusRange implements StatusRange {
268         private final StatusRange one;
269         private final StatusRange two;
270 
271         private OrStatusRange(StatusRange one, StatusRange two) {
272             this.one = checkNotNull(one);
273             this.two = checkNotNull(two);
274         }
275 
276         @Override
277         public boolean isIn(int code) {
278             return one.isIn(code) || two.isIn(code);
279         }
280     }
281 }