All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.geotab.http.exception.ErrorHandler Maven / Gradle / Ivy

/*
 *
 * 2020 Copyright (C) Geotab Inc. All rights reserved.
 */

package com.geotab.http.exception;

import com.fasterxml.jackson.core.type.TypeReference;
import com.geotab.http.response.BaseResponse;
import com.geotab.model.enumeration.DbUnavailableState;
import com.geotab.model.error.Error;
import com.geotab.model.error.JsonRpcError;
import com.geotab.model.error.JsonRpcErrorCode;
import com.geotab.model.error.JsonRpcErrorData;
import com.geotab.model.serialization.ApiJsonSerializer;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.SerializationException;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;

/**
 * Utility class which checks JSON response for error and transforms it to the corresponding
 * exception.
 */
public final class ErrorHandler {

  protected static final String EXCEPTION_MESSAGE_FORMAT =
      "WebServerInvoker exception in a call to '{%s}': '{%s} {%s}'";

  /**
   * Checks if an error returned from MyGeotab.
   *
   * @param method   The method in MyGeotab API.
   * @param response The actual response from MyGeotab.
   * @throws Exception Exception which can occur while executing the method.
   */
  public static > void checkForError(String method,
      R response) throws Exception {

    JsonRpcError error = response != null ? response.getError() : null;
    if (error == null) {
      return;
    }

    Map innerErrors = null;
    if (response.getError().getData() == null) {
      // All MyGeotab servers honor our spec but GMS may not.
      Pair> legacyError = convertLegacyError(response);
      error = legacyError.getLeft();
      innerErrors = legacyError.getRight();
    }

    String formattedMessage = String
        .format(EXCEPTION_MESSAGE_FORMAT, method, error.getData().getType(), error.getMessage());

    Exception exception = checkForError(formattedMessage,
        new ServerInvokerJsonException(formattedMessage, null, innerErrors, error.getData()),
        JsonRpcErrorCode.getByCode(error.getCode()), error.getData());

    if (exception != null) {
      if (exception instanceof JsonRpcErrorDataException) {
        ((JsonRpcErrorDataException) exception).setErrorData(error.getData());
      }
      throw exception;
    }
  }

  private static Exception checkForError(String message, Exception innerException,
      JsonRpcErrorCode errorCode, JsonRpcErrorData errorData) {
    switch (errorCode) {
      case INVALID_REQUEST:
        return new InvalidRequestException(message, innerException);
      case DB_UNAVAILABLE_GENERAL_ERROR:
      case DB_UNAVAILABLE_CONNECTION_FAILURE:
      case DB_UNAVAILABLE_INITIALIZING:
      case DB_UNAVAILABLE_UNKNOWN_DATABASE:
      case DB_UNAVAILABLE_OPERATION_ABORTED:
        DbUnavailableState state = DbUnavailableState.NONE;
        if (StringUtils.isNotEmpty(errorData.getInfo())) {
          try {
            Map errorDataInfo = ApiJsonSerializer.getInstance()
                .getObjectMapper().readValue(
                    errorData.getInfo(), new TypeReference>() {
                    });
            if (errorDataInfo.containsKey("state")) {
              state = errorDataInfo.get("state");
            }
          } catch (IOException e) {
            throw new IllegalStateException(
                "Can not deserialize " + errorData.getInfo() + "to Map");
          }
        }
        return new DbUnavailableException(message, innerException, state);
      case METHOD_NOT_FOUND:
        return new MissingMethodException(message, innerException);
      case PARSE_ERROR:
        return new SerializationException(message, innerException);
      default:
        return checkForErrorByErrorDataType(message, innerException, errorData);
    }
  }

  private static Exception checkForErrorByErrorDataType(String message, Exception innerException,
      JsonRpcErrorData errorData) {
    if (StringUtils.isNotEmpty(errorData.getType())) {
      switch (errorData.getType()) {
        case "InvalidMyAdminUserException":
          if (innerException instanceof ServerInvokerJsonException
              && ((ServerInvokerJsonException) innerException).getInnerError() != null) {
            Map innerError = ((ServerInvokerJsonException) innerException)
                .getInnerError();
            if (innerError.containsKey("message")) {
              String databaseName = innerError.get("message");
              if (StringUtils.isNotEmpty(databaseName)) {
                return new InvalidMyAdminUserException(databaseName);
              }
            }
          }
          return new InvalidMyAdminUserException();
        case "InvalidUserException":
          return new InvalidUserException();
        case "InactiveUserException":
          return new InactiveUserException();
        case "DbUnavailableException":
          return new DbUnavailableException(message, innerException);
        case "RelationViolatedException":
          return new RelationViolatedException(innerException);
        case "GroupRelationViolatedException":
          return new GroupRelationViolatedException(innerException);
        case "DuplicateException":
          return new DuplicateException(innerException);
        case "PasswordPolicyViolationException":
          return new PasswordPolicyViolationException(message);
        case "RegistrationException":
          return new RegistrationException(message);
        case "OverLimitException":
          return new OverLimitException(message);
        case "InvalidRequestException":
          return new IllegalStateException("Inconsistent JSON-RPC error code");
        case "MethodNotFoundException":
        case "MissingMethodException":
          return new MissingMethodException(message, innerException);
        case "MissingMemberException":
          return new MissingMemberException(message, innerException);
        case "NullReferenceException":
          return new NullPointerException(message);
        case "ArgumentException":
        case "ArgumentOutOfRangeException":
          return new IllegalArgumentException(message, innerException);
        case "JsonSerializerException":
        case "JSONSerializerException":
          return new SerializationException(message, innerException);
        case "InvalidCastException":
          return new ClassCastException(message);
        case "NotSupportedException":
          return new NotImplementedException(message, innerException);
        case "InvalidCertificateException":
          return new CertificateException(message, innerException);
        default:
          return innerException;
      }
    }

    return innerException;
  }

  @SuppressWarnings("Indentation")
  protected static > Pair>
  convertLegacyError(R response) {
    Error[] errors = response.getError().getErrors();

    String message = null;
    String stack = null;
    String name = null;

    Map innerErrors = new HashMap<>(3);

    for (Error item : errors) {
      if (message == null && !(item.getMessage() == null)) {
        message = item.getMessage();
        innerErrors.put("message", message);
      }

      if (name == null && !(item.getName() == null)) {
        name = item.getName();
        innerErrors.put("name", name);
        if (message == null) {
          message = name;
        }
      }
      if (stack == null && !(item.getStackTrace() == null)) {
        stack = item.getStackTrace();
        innerErrors.put("stackTrace", stack);
      }
    }

    JsonRpcError error = response.getError();

    if (name == null) {
      name = error.getName();
      if (message == null) {
        // Reserve default error message in case the original exception provided no message.
        message = name;
      }
    }
    if (message == null) {
      message = error.getMessage();
    }

    if (StringUtils.isNotEmpty(name) && !name.equals(message)) {
      message = name + ": " + message;
    }

    JsonRpcError specError = JsonRpcError.builder()
        .code(JsonRpcErrorCode.INTERNAL_ERROR.getCode())
        .message(message)
        .data(
            JsonRpcErrorData.builder().type(name).requestIndex(response.getRequestIndex()).build())
        .build();

    return Pair.of(specError, innerErrors);
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy