Refactoring: moved FacebookErrorMessage in its own file/class
[facebook-utils] / src / main / java / de / juplo / facebook / client / GraphApiErrorHandler.java
1 package de.juplo.facebook.client;
2
3 import de.juplo.facebook.exceptions.GraphApiException;
4 import java.io.ByteArrayInputStream;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.nio.charset.Charset;
8 import org.slf4j.Logger;
9 import org.slf4j.LoggerFactory;
10 import org.springframework.http.HttpHeaders;
11 import org.springframework.http.HttpStatus;
12 import org.springframework.http.client.ClientHttpResponse;
13 import org.springframework.util.FileCopyUtils;
14 import org.springframework.web.client.ResponseErrorHandler;
15
16
17
18 /**
19  * Error-Handler for error-messages from the Facebook Graph-API.
20  * <p>
21  * This error-handler handels responses withe the HTTP-status code
22  * {@code 400 BAD REQUEST}. It tries to extract and parse the error-message
23  * from the HTTP-body. Successfully extracted and parsed messages are mapped
24  * to a hierarchy of exceptions, that reflects the hierarchy of the error-
25  * codes and -types.
26  * <p>
27  * If the HTTP-status-code of the response is not {@code 400 BAD REQUEST} or
28  * the HTTP-body could not be extracted or parsed, this error-handler
29  * delegates the handling to its parent.
30  *
31  * @see <a href="https://developers.facebook.com/docs/graph-api/using-graph-api/v2.5#errors">Graph-API Documentation</a>
32  * @see <a href="http://fbdevwiki.com/wiki/Error_codes">Inofficial Wiki For Facebook-Developers</a>
33  * @author Kai Moritz
34  */
35 public class GraphApiErrorHandler implements ResponseErrorHandler
36 {
37   private final static Logger LOG =
38       LoggerFactory.getLogger(GraphApiErrorHandler.class);
39
40   private final ResponseErrorHandler parent;
41
42
43   public GraphApiErrorHandler(ResponseErrorHandler errorHandler)
44   {
45     this.parent = errorHandler;
46   }
47
48
49   @Override
50   public boolean hasError(ClientHttpResponse response) throws IOException
51   {
52     return
53         HttpStatus.Series.CLIENT_ERROR.equals(response.getStatusCode().series())
54         || this.parent.hasError(response);
55   }
56
57   @Override
58   public void handleError(final ClientHttpResponse response) throws IOException
59   {
60     if (!HttpStatus.BAD_REQUEST.equals(response.getStatusCode()))
61     {
62       // We will only handle 400 BAD REQUEST
63       LOG.debug("ignoring response with status-code {}.", response.getStatusCode());
64       parent.handleError(response);
65       return;
66     }
67
68
69     if (response.getBody() == null)
70     {
71       // There is no body to interpret in the HTTP-message
72       LOG.warn("Could not convert the response into an exception, because there is no message-body.");
73       parent.handleError(response);
74       return;
75     }
76
77     final byte[] body = FileCopyUtils.copyToByteArray(response.getBody());
78     GraphApiException error;
79
80     try
81     {
82       error = GraphApiException.create(body);
83       if (LOG.isInfoEnabled())
84         LOG.info("error-response: {}", new String(body, Charset.forName("UTF-8")));
85     }
86     catch (Exception e)
87     {
88       // The body of the HTTP-message could not be parsed.
89       // Let the parent error-handler try to handle the response.
90
91       LOG.warn("Could not convert the response into an exception, because the body is unparsable: {}", body);
92
93       // To do so, we have to wrap the original response to fill in
94       // the buffered body, if needed
95       ClientHttpResponse buffered = new ClientHttpResponse()
96       {
97         @Override
98         public HttpStatus getStatusCode() throws IOException
99         {
100           return response.getStatusCode();
101         }
102
103         @Override
104         public synchronized InputStream getBody() throws IOException
105         {
106           return new ByteArrayInputStream(body);
107         }
108
109         @Override
110         public HttpHeaders getHeaders()
111         {
112           return response.getHeaders();
113         }
114
115         @Override
116         public String getStatusText() throws IOException
117         {
118           return response.getStatusText();
119         }
120
121         @Override
122         public void close()
123         {
124           response.close();
125         }
126
127         @Override
128         public int getRawStatusCode() throws IOException
129         {
130           return response.getRawStatusCode();
131         }
132       };
133
134       parent.handleError(buffered);
135       return;
136     }
137
138     throw error;
139   }
140 }