Errors during parsing of the error-message are wrapped
[facebook-errors] / src / main / java / de / juplo / facebook / errors / GraphApiException.java
1 package de.juplo.facebook.errors;
2
3
4 import com.fasterxml.jackson.core.JsonProcessingException;
5 import com.fasterxml.jackson.databind.DeserializationFeature;
6 import com.fasterxml.jackson.databind.ObjectMapper;
7 import com.fasterxml.jackson.databind.SerializationFeature;
8 import java.io.ByteArrayInputStream;
9 import java.io.IOException;
10 import java.io.InputStream;
11 import org.slf4j.Logger;
12 import org.slf4j.LoggerFactory;
13 import org.springframework.http.HttpHeaders;
14 import org.springframework.http.HttpStatus;
15
16
17
18 /**
19  * Base exception for Facebook Graph-Api exceptions.
20  * 
21  * @author Kai Moritz
22  */
23 public class GraphApiException extends RuntimeException
24 {
25   public enum Type { OAuthException, GraphMethodException }
26
27
28   final static Logger LOG = LoggerFactory.getLogger(GraphApiException.class);
29   final static ObjectMapper OBJECT_MAPPER;
30
31   public final HttpStatus status;
32   public final HttpHeaders headers;
33   public final FacebookErrorMessage error;
34
35
36   static
37   {
38     OBJECT_MAPPER = new ObjectMapper();
39     OBJECT_MAPPER.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
40     OBJECT_MAPPER.configure(DeserializationFeature.ACCEPT_FLOAT_AS_INT, false);
41     OBJECT_MAPPER.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
42   }
43
44
45   public static GraphApiException create(
46       HttpStatus status,
47       HttpHeaders headers,
48       InputStream in
49       )
50   {
51     try
52     {
53       return create(status, headers, OBJECT_MAPPER.readValue(in, FacebookErrorMessage.class));
54     }
55     catch (IOException | RuntimeException e)
56     {
57       return new ErrorResponseParsingErrorException(status, headers, e);
58     }
59   }
60
61   public static GraphApiException create(
62       HttpStatus status,
63       HttpHeaders headers,
64       byte[] message
65       )
66   {
67     return create(status, headers, new ByteArrayInputStream(message));
68   }
69
70   public static GraphApiException create(
71       HttpStatus status,
72       HttpHeaders headers,
73       FacebookErrorMessage error
74       )
75   {
76     // see: http://fbdevwiki.com/wiki/Error_codes
77     switch(error.code)
78     {
79       // 1..99: general errors
80       case 1:     return new UnknownErrorException(status, headers, error);
81       case 2:     return new UnexpectedErrorException(status, headers, error);
82       case 4:     return new ApplicationRequestLimitReachedException(status, headers, error);
83       case 10:    return new AuthorizationMissingException(status, headers, error);
84       case 12:    return new DeprecatedException(status, headers, error);
85       case 17:    return new AccountRequestLimitReachedException(status, headers, error);
86       case 21:    return new PageMigratedException(status, headers, error);
87       case 32:    return new PageRequestLimitReachedException(status, headers, error);
88       // 100..199: graph method errors
89       case 100:   return new UnsupportedGetRequestException(status, headers, error);
90       case 102:   return new UserAccessTokenRequiredException(status, headers, error);
91       case 104:   return new AccessTokenRequiredException(status, headers, error);
92       case 190:   return new AccessTokenExpiredException(status, headers, error);
93       // 200..299: permission errors
94       case 200:
95       case 201:
96       case 202:
97       case 203:
98       case 204:
99       case 205:
100       case 206:
101       case 207:
102       case 208:
103       case 209:
104       case 210:
105       case 211:
106       case 212:
107       case 213:
108       case 214:
109       case 215:
110       case 216:
111       case 217:
112       case 218:
113       case 219:
114       case 220:
115       case 221:
116       case 222:
117       case 223:
118       case 224:
119       case 225:
120       case 226:
121       case 227:
122       case 228:
123       case 229:
124       case 230:
125       case 231:
126       case 232:
127       case 233:
128       case 234:
129       case 235:
130       case 236:
131       case 237:
132       case 238:
133       case 239:
134       case 240:
135       case 241:
136       case 242:
137       case 243:
138       case 244:
139       case 245:
140       case 246:
141       case 247:
142       case 248:
143       case 249:
144       case 250:
145       case 251:
146       case 252:
147       case 253:
148       case 254:
149       case 255:
150       case 256:
151       case 257:
152       case 258:
153       case 259:
154       case 260:
155       case 261:
156       case 262:
157       case 263:
158       case 264:
159       case 265:
160       case 266:
161       case 267:
162       case 268:
163       case 269:
164       case 270:
165       case 271:
166       case 272:
167       case 273:
168       case 274:
169       case 275:
170       case 276:
171       case 277:
172       case 278:
173       case 279:
174       case 280:
175       case 281:
176       case 282:
177       case 283:
178       case 284:
179       case 285:
180       case 286:
181       case 287:
182       case 288:
183       case 289:
184       case 290:
185       case 291:
186       case 292:
187       case 293:
188       case 294:
189       case 295:
190       case 296:
191       case 297:
192       case 298:
193       case 299:   return new AuthorizationMissingException(status, headers, error);
194       // 200..299: permission errors
195       // 300..399: data editing errors ?
196       case 341:   return new TemporaryRateLimitExceededException(status, headers, error);
197       // 400..449: authentication error
198       // 450..499: session errors
199       // 500..599: application messaging errors ?
200       case 506:   return new MultipleConcurrentPostsException(status, headers, error);
201       // 600..699: FQL errors
202       case 613:   return new RateLimitExceededException(status, headers, error);
203       // 700..749: ref errors
204       // 750..799: application integration errors
205       // 900..949: application information errors
206       // 950..999: batch api errors
207       // 1000..1099: event api errors
208       // 1100..1199: live-message errors
209       case 1609005: return new LinkPostFailureException(status, headers, error);
210       case 2200:  return new CallbackVerificationFailedException(status, headers, error);
211
212       default:
213         LOG.info("unmapped error: {}", error);
214         return new UnmappedErrorException(status, headers, error);
215     }
216   }
217
218
219   protected GraphApiException(
220       HttpStatus status,
221       HttpHeaders headers,
222       FacebookErrorMessage error
223       )
224   {
225     super(error.message);
226     this.status = status;
227     this.headers = headers;
228     this.error = error;
229   }
230
231
232   public HttpStatus getStatus()
233   {
234     return status;
235   }
236
237   public HttpHeaders getHeaders()
238   {
239     return headers;
240   }
241
242   public Type getType()
243   {
244     return error.type == null ? null : Type.valueOf(error.type);
245   }
246
247   public Integer getCode()
248   {
249     return error.code;
250   }
251
252   public Integer getSubCode()
253   {
254     return error.subCode;
255   }
256
257   public String getUserTitle()
258   {
259     return error.userTitle;
260   }
261
262   public String getUserMessage()
263   {
264     return error.userMessage;
265   }
266
267   public String getTraceId()
268   {
269     return error.traceId;
270   }
271
272
273   @Override
274   public String toString()
275   {
276     try
277     {
278       return OBJECT_MAPPER.writeValueAsString(error);
279     }
280     catch(JsonProcessingException e)
281     {
282       // This should never happen. But in case of a mistake: be verbose!
283       LOG.error("could not convert message into JSON: {}", e);
284       return e.getMessage();
285     }
286   }
287 }