Package com.google.gdata.util

Source Code of com.google.gdata.util.ServiceException

/* Copyright (c) 2008 Google Inc.
*
* 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 com.google.gdata.util;

import com.google.gdata.util.common.base.CharEscapers;
import com.google.gdata.util.ErrorContent.LocationType;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* The ServiceException class is the base exception class used to
* indicate an error while processing a GDataRequest.
*
*
*
*
*/
public class ServiceException extends Exception {

  // The instance variables of this class are package-private.
  // This is done so that the class ServiceExceptionInitializer
  // (which is in essence an extension of the HttpURLConnection
  // constructor) can set them without violating the taboo about
  // calling instance methods such as setters on a partially
  // constructed object.

  // Instance variables for HTTP

  /** HTTP error code. */
  int httpErrorCodeOverride = -1;

  /**
   * Optional HTTP headers to be output when HTTP response is generated.
   * It is a mapping from header name to list of header values.
   */
  Map<String, List<String>> httpHeaders;

  /**
   * Optional HTTP content type for a response.
   */
  ContentType responseContentType;

  /**
   * Retrieve HTTP response body or error message.
   * The content type must be set correctly.
   */
  String responseBody;

  // Structured error element.
  ErrorElement errorElement = new ErrorElement();

  /**
   * Set to the exception we are filling during parsing.
   */
  List<ServiceException> siblings =
      new ArrayList<ServiceException>(1);
  {
    siblings.add(this);
  }


  // Server-side constructors for reporting exceptions to client

  public ServiceException(String message) {
    super(nullsafe(message));
    httpHeaders = new HashMap<String, List<String>>();
  }

  public ServiceException(String message, Throwable cause) {
    super(nullsafe(message), cause);
    httpHeaders = new HashMap<String, List<String>>();
  }

  public ServiceException(Throwable cause) {
    super(nullsafe(cause.getMessage()));
    httpHeaders = new HashMap<String, List<String>>();
  }

  /**
   * Initializes the ServiceException using the error response data from an
   * HTTP connection.  This constructor is used in the client library
   * to approximately reconstitute the constructor as it appeared
   * in the server.
   * <p>This constructor uses a ServiceExceptionInitializer to do
   * the work of parsing the connection and calling our setters to
   * initialize our fields.  The initializer object may also create
   * sibling ServiceExceptions.
   * @param httpConn is the http connection from which the error message
   *     (structured or simple) is read
   * @throws IOException if network error receiving the error response
   */
  public ServiceException(HttpURLConnection httpConn) throws IOException {
    super(nullsafe(httpConn.getResponseMessage()));
    ServiceExceptionInitializer initializer =
        new ServiceExceptionInitializer(this);
    try {
      initializer.parse(httpConn);
    } catch (ParseException pe) {
      // Clean up after failed parse
      errorElement = new ErrorElement();
      siblings.clear();
      siblings.add(this);
      responseContentType = ContentType.TEXT_PLAIN;
    }
  }

  /**
   * Initializes the ServiceException using an {@link ErrorContent} object that
   * encapsulates most of the information about the error.  Most ErrorContent
   * instances are declared in a subclass of {@link ErrorDomain} containing all
   * the errors for a GData domain (service or portion of service).
   */
  public ServiceException(ErrorContent errorCode) {
    super(nullsafe(errorCode.getInternalReason()));
    httpHeaders = new HashMap<String, List<String>>();
    this.errorElement = new ErrorElement(errorCode);
  }

  /**
   * Initializes the ServiceException using an {@link ErrorContent} object that
   * encapsulates most of the information about the error, and an embedded
   * exception.  Most ErrorContent instances are declared in a subclass of
   * {@link ErrorDomain} containing all the errors for this GData domain
   * (service or portion of service).
   */
  public ServiceException(ErrorContent errorCode, Throwable cause) {
    super(nullsafe(errorCode.getInternalReason()), cause);
    httpHeaders = new HashMap<String, List<String>>();
    this.errorElement = new ErrorElement(errorCode);
  }

  // Private method used in constructors
  private static String nullsafe(String src) {
     return (src != null) ? src : "Exception message unavailable";
  }

  // Getters and setters
  public int getHttpErrorCodeOverride() { return httpErrorCodeOverride; }
  public void setHttpErrorCodeOverride(int v) { httpErrorCodeOverride = v; }


  public ContentType getResponseContentType() {
    return responseContentType;
  }

  public void setResponseContentType(ContentType v) {
    if (v == null) {
      throw new NullPointerException("Null content type");
    }
    responseContentType = v;
  }

  public String getResponseBody() {
    return responseBody;
  }

  public void setResponseBody(String body) {
    if (body == null) {
      throw new NullPointerException("Null response body");
    }
    responseBody = body;
    // Create and invoke a ServiceExceptionInitializer to
    // do the dirty work of reading the stream, possibly
    // parsing the result if it is a GData structured error,
    // and set the instance variables accordingly.
    // In C++ terms, SEI is a friend class.
    ServiceExceptionInitializer initializer =
        new ServiceExceptionInitializer(this);
    try {
      initializer.parse(responseContentType, responseBody);
    } catch (ParseException pe) {
        throw new RuntimeException(pe.getMessage(), pe);
    }
  }

  /**
   * Set HTTP response type and body simultaneously.
   * They are inherently coupled together: a body without a content type
   * is meaningless; a content type without a body is useless.
   */
  public void setResponse(ContentType contentType, String body) {
    if (contentType == null) {
      throw new NullPointerException("Null content type");
    }
    if (body == null) {
      throw new NullPointerException("Null response body");
    }
    responseContentType = contentType;
    setResponseBody(body);
  }

  /** Generate error message in XML format. */

  public String toXmlErrorMessage() {
    StringBuilder sb = new StringBuilder();
    sb.append("<errors xmlns='http://schemas.google.com/g/2005'>\n");
    for (ServiceException sibling : siblings) {
      addXmlError(sibling, sb);
    }
    sb.append("</errors>\n");
    return sb.toString();
  }

  private String escape(String src) {
    return CharEscapers.xmlEscaper().escape(src);
  }

  private void addXmlError(ServiceException se, StringBuilder sb) {
    // Simplistic StringBuffer implementation because the XML is trivial.
    sb.append("<error>\n");

    String domainName = se.getDomainName();
    sb.append("<domain>");
    sb.append(escape(domainName));
    sb.append("</domain>\n");

    String codeName = se.getCodeName();
    sb.append("<code>");
    sb.append(escape(codeName));
    sb.append("</code>\n");

    String location = se.getLocation();
    LocationType locationType = se.getLocationType();
    if (locationType == null) {
      locationType = LocationType.OTHER;
    }
    if (location != null) {
      sb.append("<location type='");
      sb.append(escape(locationType.toString()));
      sb.append("'>");
      sb.append(escape(location));
      sb.append("</location>\n");
    }

    String internalReason = se.getInternalReason();
    if (internalReason != null) {
      sb.append("<internalReason>");
      sb.append(escape(internalReason));
      sb.append("</internalReason>\n");
    }

    String extendedHelp = se.getExtendedHelp();
    if (extendedHelp != null) {
      sb.append("<extendedHelp>");
      sb.append(escape(extendedHelp));
      sb.append("</extendedHelp>\n");
    }

    String sendReport = se.getSendReport();
    if (sendReport != null) {
      sb.append("<sendReport>");
      sb.append(escape(sendReport));
      sb.append("</sendReport>\n");
    }

    String debugInfo = se.getDebugInfo();
    if (debugInfo != null) {
      sb.append("<debugInfo>");
      sb.append(escape(debugInfo));
      sb.append("</debugInfo>\n");
    }

    sb.append("</error>\n");
  }

  /**
   * Return the internal HTTP headers in modifiable form.
   */
  public Map<String, List<String>> getHttpHeaders() { return httpHeaders; }


   // Override the default Throwable toString() implementation to add
   // the response body (either an explicitly set one or the default XML
   // error message) to the resulting output.  This is useful because the
   // Throwable toString() output is included in exception stack traces,
   // so this means the full response will be visible in traces.
  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append(super.toString());
    if (responseBody != null) {
      sb.append('\n');
      sb.append(responseBody);
    }
    //   purposes, but don't do this until all GData services are converted.
    return sb.toString();
  }

  // Error model getters and setters
 
  /**
   * Return error domain.
   *
   * <p>Defaults to "GData", indicating an error that has
   * not yet been upgraded to the new architecture.
   */
  public String getDomainName() {
    String domainName = errorElement.getDomainName();
    return (domainName != null) ? domainName : "GData";
  }

  /**
   * Set error domain.
   *
   * @throws NullPointerException if {@code domain} is {@code null}.
   */
  public void setDomain(String domain) {
    errorElement.setDomain(domain);
  }

  /**
   * Return error code.
   *
   * <p>Defaults to the class name of {@code this}.
   */
  public String getCodeName() {
    String codeName = errorElement.getCodeName();
    return (codeName != null) ? codeName : getClass().getSimpleName();
  }

  /**
   * Set error code.
   *
   * @throws NullPointerException if {@code code} is {@code null}.
   */
  public void setCode(String code) {
    errorElement.setCode(code);
  }
 
  /**
   * Return error location.
   */
  public String getLocation() {
    return errorElement.getLocation();
  }
 
  /**
   * Return error location type.
   */
  public LocationType getLocationType() {
    return errorElement.getLocationType();
  }
 
  /**
   * Set XPath-based error location.
   * This must be a valid XPath expression sibling to the atom:entry
   * element (or the atom:feed element if we are not in an entry).
   *
   * @throws NullPointerException if {@code location} is {@code null}.
   */
  public void setXpathLocation(String location) {
    errorElement.setXpathLocation(location);
  }

  /**
   * Set header name for an error in a header.
   *
   * @throws NullPointerException if {@code location} is {@code null}.
   */
  public void setHeaderLocation(String location) {
    errorElement.setHeaderLocation(location);
  }

  /**
   * Set generic error location.
   *
   * @throws NullPointerException if {@code location} is {@code null}.
   */
  public void setLocation(String location) {
    errorElement.setLocation(location);
  }
 
  /**
   * Return error internal reason.
   *
   * <p>Defaults to the message set at construction time.
   */
  public String getInternalReason() {
    String internalReason = errorElement.getInternalReason();
    return (internalReason != null) ? internalReason : super.getMessage();
  }
 
  /**
   * Return message: same as getInternalReason.
   */
  @Override
  public String getMessage() {
    return getInternalReason();
  }
 
  /**
   * Sets the internal reason of the error.
   *
   * @throws NullPointerException if {@code internalReason} is {@code null}.
   */
  public void setInternalReason(String internalReason) {
    errorElement.setInternalReason(internalReason);
  }

  /**
   * Return URI for extended help
   */
  public String getExtendedHelp() {
    return errorElement.getExtendedHelp();
  }

  /**
   * Set URI for extended help.
   *
   * @throws NullPointerException if {@code extendedHelp} is {@code null}.
   */
  public void setExtendedHelp(String extendedHelp) {
    errorElement.setExtendedHelp(extendedHelp);
  }

  /**
   * Return URI to send report to.
   */
  public String getSendReport() {
    return errorElement.getSendReport();
  }

  /**
   * Set URI to send report to.
   *
   * @throws NullPointerException if {@code sendReport} is {@code null}.
   */
  public void setSendReport(String sendReport) {
    errorElement.setSendReport(sendReport);
  }
 
  /**
   * Return debugging information.
   * Defaults to the stack trace.
   */
  public String getDebugInfo() {
    // until we figure out how to determine who gets it.
    if (true) {
     return null;
    } else {
      if (errorElement.getDebugInfo() == null) {
        return generateTrace(this, new StringBuilder(10000));
      } else {
        return errorElement.getDebugInfo();
      }
    }
  }

  /**
   * Set debugging information.
   *
   * @throws NullPointerException if {@code debugInfo} is {@code null}.
   */
  public void setDebugInfo(String debugInfo) {
    errorElement.setDebugInfo(debugInfo);
  }
 
  // Do what printStackTrace does, but to the StringBuilder.
  private String generateTrace(Throwable th, StringBuilder sb) {
    sb.append(toString());
    sb.append('\n');
    for (StackTraceElement element : getStackTrace()) {
      sb.append("\tat ");
      sb.append(element.toString());
      sb.append('\n');
    }
    Throwable cause = getCause();
    if (cause != null) {
      sb.append("Caused by: ");
      sb.append(cause.toString());
      sb.append('\n');
      generateTrace(cause, sb);
    }
    return sb.toString();
  }
 
  // Logic for handling sibling exceptions.

  // All the siblings, including this one.  We keep ourselves
  // in the sibling list so that all siblings can share a common
  // list.  There is no hierarchy among siblings, so throwing any
  // of them will produce the same result.

  /**
   * Return an unmodifiable copy of the sibling list.
   */
  public List<ServiceException> getSiblings() {
    return Collections.unmodifiableList(
        new ArrayList<ServiceException>(siblings));
  }

  /**
   * Make {@tt this} and {@tt newbie} siblings, returning {@tt this}.
   * All sibling exceptions are jointly converted to an
   * error message when any of them are thrown.
   */
  public ServiceException addSibling(ServiceException newbie) {
    if (newbie == null) {
      throw new NullPointerException("Null exception being added");
    }
    for (ServiceException newbieSibling : newbie.siblings) {
      if (!siblings.contains(newbieSibling)) {
        siblings.add(newbieSibling);
      }
      newbieSibling.siblings = siblings;
    }
    return this;
  }

  /**
   * Return true if this ServiceException matches the specified
   * {@link ErrorContent} in domain name and code name. Sibling exceptions are
   * not checked.
   */
  public boolean matches(ErrorContent code) {
    return getDomainName().equals(code.getDomainName())
        && getCodeName().equals(code.getCodeName());
  }

  /**
   * Return true if this ServiceException or any of its sibling exceptions
   * matches the specified {@link ErrorContent} in domain name and code name.
   * If you want to know <i>which</i> particular ServiceException matched, call
   * {@link #getSiblings} and examine the individual ServiceExceptions with
   * {@link #matches}.
   */
  public boolean matchesAny(ErrorContent errorCode) {
    for (ServiceException se : siblings) {
      if (se.matches(errorCode)) {
        return true;
      }
    }
    return false;
  }
}
TOP

Related Classes of com.google.gdata.util.ServiceException

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.