Refactoring: moved all code concerned with error-handling in one package
[facebook-utils] / src / main / java / de / juplo / facebook / exceptions / GraphApiErrorHandler.java
diff --git a/src/main/java/de/juplo/facebook/exceptions/GraphApiErrorHandler.java b/src/main/java/de/juplo/facebook/exceptions/GraphApiErrorHandler.java
new file mode 100644 (file)
index 0000000..974bf85
--- /dev/null
@@ -0,0 +1,140 @@
+package de.juplo.facebook.exceptions;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.util.FileCopyUtils;
+import org.springframework.web.client.ResponseErrorHandler;
+
+
+
+/**
+ * Error-Handler for error-messages from the Facebook Graph-API.
+ * <p>
+ * This error-handler handels responses withe the HTTP-status code
+ * {@code 400 BAD REQUEST}. It tries to extract and parse the error-message
+ * from the HTTP-body. Successfully extracted and parsed messages are mapped
+ * to a hierarchy of exceptions, that reflects the hierarchy of the error-
+ * codes and -types.
+ * <p>
+ * If the HTTP-status-code of the response is not {@code 400 BAD REQUEST} or
+ * the HTTP-body could not be extracted or parsed, this error-handler
+ * delegates the handling to its parent.
+ *
+ * @see <a href="https://developers.facebook.com/docs/graph-api/using-graph-api/v2.5#errors">Graph-API Documentation</a>
+ * @see <a href="http://fbdevwiki.com/wiki/Error_codes">Inofficial Wiki For Facebook-Developers</a>
+ * @author Kai Moritz
+ */
+public class GraphApiErrorHandler implements ResponseErrorHandler
+{
+  private final static Logger LOG =
+      LoggerFactory.getLogger(GraphApiErrorHandler.class);
+
+  private final ResponseErrorHandler parent;
+
+
+  public GraphApiErrorHandler(ResponseErrorHandler errorHandler)
+  {
+    this.parent = errorHandler;
+  }
+
+
+  @Override
+  public boolean hasError(ClientHttpResponse response) throws IOException
+  {
+    return
+        HttpStatus.Series.CLIENT_ERROR.equals(response.getStatusCode().series())
+        || this.parent.hasError(response);
+  }
+
+  @Override
+  public void handleError(final ClientHttpResponse response) throws IOException
+  {
+    if (!HttpStatus.BAD_REQUEST.equals(response.getStatusCode()))
+    {
+      // We will only handle 400 BAD REQUEST
+      LOG.debug("ignoring response with status-code {}.", response.getStatusCode());
+      parent.handleError(response);
+      return;
+    }
+
+
+    if (response.getBody() == null)
+    {
+      // There is no body to interpret in the HTTP-message
+      LOG.warn("Could not convert the response into an exception, because there is no message-body.");
+      parent.handleError(response);
+      return;
+    }
+
+    final byte[] body = FileCopyUtils.copyToByteArray(response.getBody());
+    GraphApiException error;
+
+    try
+    {
+      error = GraphApiException.create(body);
+      if (LOG.isInfoEnabled())
+        LOG.info("error-response: {}", new String(body, Charset.forName("UTF-8")));
+    }
+    catch (Exception e)
+    {
+      // The body of the HTTP-message could not be parsed.
+      // Let the parent error-handler try to handle the response.
+
+      LOG.warn("Could not convert the response into an exception, because the body is unparsable: {}", body);
+
+      // To do so, we have to wrap the original response to fill in
+      // the buffered body, if needed
+      ClientHttpResponse buffered = new ClientHttpResponse()
+      {
+        @Override
+        public HttpStatus getStatusCode() throws IOException
+        {
+          return response.getStatusCode();
+        }
+
+        @Override
+        public synchronized InputStream getBody() throws IOException
+        {
+          return new ByteArrayInputStream(body);
+        }
+
+        @Override
+        public HttpHeaders getHeaders()
+        {
+          return response.getHeaders();
+        }
+
+        @Override
+        public String getStatusText() throws IOException
+        {
+          return response.getStatusText();
+        }
+
+        @Override
+        public void close()
+        {
+          response.close();
+        }
+
+        @Override
+        public int getRawStatusCode() throws IOException
+        {
+          return response.getRawStatusCode();
+        }
+      };
+
+      parent.handleError(buffered);
+      return;
+    }
+
+    throw error;
+  }
+}