View Javadoc
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 }