/*******************************************************************************
*
* Copyright 2011-2014 Spiffy UI Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package org.spiffyui.client.rest;
import java.util.HashMap;
import java.util.Map;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
/**
* <p>
* This exception represents a successful return from a REST call with an error
* payload. This exception parses the payload and returns all the parts of the
* exception.
* </p>
*
* <p>
* This exception follows the <a href="http://www.w3.org/TR/soap12-part1/#soapfault">SOAP fault format</a>.
* The basic structure looks like this:
* </p>
*
* <pre>
* {
* "Fault": {
* "Code": {
* "Value": "Sender",
* "Subcode": {
* "Value": "MessageTimeout"
* }
* },
* "Reason": {
* "Text": "Sender Timeout"
* },
* "Detail": {
* "MaxTime": "P5M"
* }
* }
* }
* </pre>
*
* <p>
* The fields in this class map to this JSON structure.
* </p>
*
*/
public class RESTException extends Exception
{
/**
* This is a constant error code to indicate the response was not well formed JSON
*/
public static final String UNPARSABLE_RESPONSE = "UnparsableJSONResponse";
/**
* This is a constant error code to indicate the JSON returned from the
* server was parsable, but still invalid.
*/
public static final String INVALID_RESPONSE = "InvalidJSONResponse";
/**
* This constant indicates that we were unable to contact the server. That
* might be because we are offline or the server connection timed out.
*/
public static final String NO_SERVER_RESPONSE = "UnableToContactServer";
/**
* This constant indicates that the server returned a 401, but did not
* return the required authentication HTTP header
*/
public static final String NO_AUTH_HEADER = "NoAuthHeader";
/**
* This constant indicates that the server returned a 401, but the required
* HTTP header was invalid
*/
public static final String INVALID_AUTH_HEADER = "InvalidAuthHeader";
/**
* This constant indicates that the server returned a 401, but did not
* return the required authentication HTTP header
*/
public static final String XSS_ERROR = "PotentialXSSError";
/**
* This constant indicates that the core server was not able to contact
* the authentication server.
*/
public static final String AUTH_SERVER_UNAVAILABLE = "AuthServerUnavailable";
/**
* This constant indicates that the server returned an error code, something that
* wasn't in the 200 range, and didn't have any response.
*/
public static final String EMPTY_ERROR_RESPONSE = "EmptyErrorResponse";
private static final long serialVersionUID = -1L;
private String m_code;
private String m_subcode;
private String m_reason;
private int m_responseCode;
private String m_url;
private Map<String, String> m_details = new HashMap<String, String>();
/**
* Creates a new RESTException
*
* @param code the exception code
* @param subcode the optional exception subcode
* @param reason the optional exception reason
* @param details the optional exception details
* @param responseCode
* the HTTP response code
* @param url the URL for this exception
*/
public RESTException(String code, String subcode, String reason,
Map<String, String> details, int responseCode,
String url)
{
if (code == null) {
throw new IllegalArgumentException("RESTException requires a code.");
}
m_code = semiUnescapeHtml(SafeHtmlUtils.fromString(code).asString());
if (subcode != null) {
m_subcode = semiUnescapeHtml(SafeHtmlUtils.fromString(subcode).asString());
}
if (reason != null) {
m_reason = semiUnescapeHtml(SafeHtmlUtils.fromString(reason).asString());
}
m_details = details;
m_responseCode = responseCode;
m_url = url;
}
/**
* When an error response comes back from the server we want to encode it
* as safe HTML since it comes from an untrusted source. However, the GWT
* safe HTML utilities encode an apostrophe as a ' character and we
* need to undo that since it's a common character in error messages.
*
* @param html the HTML to unescape
*
* @return the unescaped HTML
*/
private String semiUnescapeHtml(String html)
{
return html.replace("'", "'");
}
/**
* The code for the exception
*
* @return gets the return code for this exception - required
*/
public String getCode()
{
return m_code;
}
/**
* The subcode for this exception
*
* @return the subcode - optional
*/
public String getSubcode()
{
return m_subcode;
}
/**
* The reason for this exception
*
* @return the exception reason - required
*/
public String getReason()
{
return m_reason;
}
/**
* The details for this exception
*
* @return a map of the details for this exception
*/
public Map<String, String> getDetails()
{
return m_details;
}
/**
* Gets the server response code that came with this error message
*
* @return the HTTP response code
*/
public int getResponseCode()
{
return m_responseCode;
}
/**
* Gets the URL that was called when this RESTException was thrown
*
* @return the URL
*/
public String getUrl()
{
return m_url;
}
/**
* Gets a generic exception representing invalid JSON
*
* @param url the URL where this bad JSON came from
*
* @return the exception
*/
public static RESTException generateInvalidJSONException(String url)
{
return new RESTException(RESTException.INVALID_RESPONSE, "", "",
new HashMap<String, String>(), -1, url);
}
@Override
public String toString()
{
return "RESTException: " + m_code + ", " + m_subcode + ", " + m_reason;
}
}