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