import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonRootName;
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.SerializableString;
+import com.fasterxml.jackson.core.io.CharacterEscapes;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
/**
*
* @author Kai Moritz
*/
-@org.codehaus.jackson.map.annotate.JsonDeserialize(using = GraphApiExceptionJackson1Deserializer.class)
-@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = GraphApiExceptionJackson2Deserializer.class)
public class GraphApiException extends OAuth2Exception
{
- private final String type;
- private final int code;
+ public enum Type { OAuthException, GraphMethodException }
- private int httpErrorCode;
+ final static Logger LOG = LoggerFactory.getLogger(GraphApiException.class);
+ final static ObjectMapper OBJECT_MAPPER;
- public GraphApiException(String message, String type, int code)
+ private final FacebookErrorMessage error;
+
+
+ static
{
- super(message);
- this.type = type;
- this.code = code;
+ OBJECT_MAPPER = new ObjectMapper();
+ OBJECT_MAPPER.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
+ OBJECT_MAPPER.configure(DeserializationFeature.ACCEPT_FLOAT_AS_INT, false);
+ OBJECT_MAPPER.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
+ OBJECT_MAPPER.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
}
- public String getType()
+ public static GraphApiException create(InputStream in)
+ throws
+ IOException,
+ JsonParseException,
+ JsonMappingException
{
- return type;
+ return create(OBJECT_MAPPER.readValue(in, FacebookErrorMessage.class));
}
- public int getCode()
+ public static GraphApiException create(byte[] message)
+ throws
+ IOException,
+ JsonParseException,
+ JsonMappingException
{
- return code;
+ return create(OBJECT_MAPPER.readValue(message, FacebookErrorMessage.class));
}
- @Override
- public int getHttpErrorCode()
+ public static GraphApiException create(FacebookErrorMessage error)
+ {
+ // see: http://fbdevwiki.com/wiki/Error_codes
+ switch(error.code)
+ {
+ // 1..99: general errors
+ case 1: return new UnknownErrorException(error);
+ case 2: return new UnexpectedErrorException(error);
+ case 21: return new PageMigratedException(error);
+ // 100..199: graph method errors
+ case 100: return new UnsupportedGetRequestException(error);
+ case 104: return new AccessTokenRequiredException(error);
+ // 200..299: permission errors
+ // 300..399: data editing errors
+ // 400..449: authentication error
+ // 450..499: session errors
+ // 500..599: application messaging errors
+ // 600..699: FQL errors
+ case 613: return new RateExceededException(error);
+ // 700..749: ref errors
+ // 750..799: application integration errors
+ // 900..949: application information errors
+ // 950..999: batch api errors
+ // 1000..1099: event api errors
+ // 1100..1199: live-message errors
+ case 2200: return new CallbackVerificationFailedException(error);
+
+ default:
+ LOG.info("unmapped error: {}", error);
+ return new UnmappedErrorException(error);
+ }
+ }
+
+
+ protected GraphApiException(FacebookErrorMessage error)
+ {
+ super(error.message);
+ this.error = error;
+ }
+
+
+ public Type getType()
{
- return httpErrorCode == 0 ? super.getHttpErrorCode() : httpErrorCode;
+ return error.type == null ? null : Type.valueOf(error.type);
}
- public void setHttpErrorCode(int httpErrorCode)
+ public Integer getCode()
{
- this.httpErrorCode = httpErrorCode;
+ return error.code;
}
+ public Integer getSubCode()
+ {
+ return error.subCode;
+ }
+
+ public String getUserTitle()
+ {
+ return error.userTitle;
+ }
+
+ public String getUserMessage()
+ {
+ return error.userMessage;
+ }
+
+ public String getTraceId()
+ {
+ return error.traceId;
+ }
+
+
@Override
public String toString()
{
- StringBuilder builder = new StringBuilder();
- builder.append("{error:{\"message\":\"");
- builder.append(getMessage().replaceAll("\"", "\\\""));
- builder.append("\",\"type\":");
- builder.append(type.replaceAll("\"", "\\\""));
- builder.append("\",\"code\":");
- builder.append(code);
- builder.append("}}");
- return builder.toString();
+ try
+ {
+ return OBJECT_MAPPER.writeValueAsString(error);
+ }
+ catch(JsonProcessingException e)
+ {
+ // This should never happen. But in case of a mistake: be verbose!
+ LOG.error("could not convert message into JSON: {}", e);
+ return e.getMessage();
+ }
}
/**
* This class represents an error message from the Graph-API
*
- * @see https://developers.facebook.com/docs/graph-api/using-graph-api/v2.5#errors
+ * @see <a href="https://developers.facebook.com/docs/graph-api/using-graph-api/v2.5#errors">Graph-API Documentation</a>
*/
@JsonRootName("error")
@JsonPropertyOrder({ "message", "type", "code", "error_subcode", "error_user_title", "error_user_msg", "fbtrace_id" })
@JsonProperty("fbtrace_id")
String traceId;
}
+
+ public static class CustomCharacterEscapes extends CharacterEscapes
+ {
+ private final int[] _asciiEscapes;
+
+
+ public CustomCharacterEscapes()
+ {
+ _asciiEscapes = standardAsciiEscapesForJSON();
+ _asciiEscapes['/'] = CharacterEscapes.ESCAPE_CUSTOM;
+ }
+
+
+ @Override
+ public int[] getEscapeCodesForAscii()
+ {
+ return _asciiEscapes;
+ }
+
+ @Override
+ public SerializableString getEscapeSequence(int i)
+ {
+ if(i == '/')
+ {
+ return new SerializableString()
+ {
+ @Override
+ public String getValue()
+ {
+ return "\\/";
+ }
+
+ @Override
+ public int charLength()
+ {
+ return 2;
+ }
+
+ @Override
+ public char[] asQuotedChars()
+ {
+ return new char[]{'\\','/'};
+ }
+
+ @Override
+ public byte[] asUnquotedUTF8()
+ {
+ return new byte[]{'\\','/'};
+ }
+
+ @Override
+ public byte[] asQuotedUTF8()
+ {
+ return new byte[]{'\\','/'};
+ }
+
+ @Override
+ public int appendQuotedUTF8(byte[] buffer, int offset)
+ {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+
+ @Override
+ public int appendQuoted(char[] buffer, int offset)
+ {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+
+ @Override
+ public int appendUnquotedUTF8(byte[] buffer, int offset)
+ {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+
+ @Override
+ public int appendUnquoted(char[] buffer, int offset)
+ {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+
+ @Override
+ public int writeQuotedUTF8(OutputStream out) throws IOException
+ {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+
+ @Override
+ public int writeUnquotedUTF8(OutputStream out) throws IOException
+ {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+
+ @Override
+ public int putQuotedUTF8(ByteBuffer buffer) throws IOException
+ {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+
+ @Override
+ public int putUnquotedUTF8(ByteBuffer out) throws IOException
+ {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+ };
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
}