@SuppressWarnings("unchecked")
  public static void validateResponse(HttpURLConnection conn,
      int expectedStatus) throws IOException {
    if (conn.getResponseCode() != expectedStatus) {
      Exception toThrow;
      InputStream es = null;
      try {
        es = conn.getErrorStream();
        Map json = JsonSerialization.mapReader().readValue(es);
        json = (Map) json.get(ERROR_JSON);
        String exClass = (String) json.get(ERROR_CLASSNAME_JSON);
        String exMsg = (String) json.get(ERROR_MESSAGE_JSON);
        if (exClass != null) {
          try {
            ClassLoader cl = HttpExceptionUtils.class.getClassLoader();
            Class klass = cl.loadClass(exClass);
            Preconditions.checkState(Exception.class.isAssignableFrom(klass),
                "Class [%s] is not a subclass of Exception", klass);
            MethodHandle methodHandle = PUBLIC_LOOKUP.findConstructor(
                    klass, EXCEPTION_CONSTRUCTOR_TYPE);
            toThrow = (Exception) methodHandle.invoke(exMsg);
          } catch (Throwable t) {
            toThrow = new IOException(String.format(
                "HTTP status [%d], exception [%s], message [%s], URL [%s]",
                conn.getResponseCode(), exClass, exMsg, conn.getURL()));
          }
        } else {
          String msg = (exMsg != null) ? exMsg : conn.getResponseMessage();
          toThrow = new IOException(String.format(
              "HTTP status [%d], message [%s], URL [%s]",
              conn.getResponseCode(), msg, conn.getURL()));
        }
      } catch (Exception ex) {
        toThrow = new IOException(String.format(
            "HTTP status [%d], message [%s], URL [%s], exception [%s]",
            conn.getResponseCode(), conn.getResponseMessage(), conn.getURL(),
            ex.toString()), ex);
      } finally {
        if (es != null) {
          try {
            es.close();
          } catch (IOException ex) {
            //ignore
          }
        }
      }
      throwEx(toThrow);
    }
  }
@Test
  public void testValidateResponseJsonErrorNonException() throws Exception {
    Map<String, Object> json = new HashMap<String, Object>();
    json.put(HttpExceptionUtils.ERROR_EXCEPTION_JSON, "invalid");
    // test case where the exception classname is not a valid exception class
    json.put(HttpExceptionUtils.ERROR_CLASSNAME_JSON, String.class.getName());
    json.put(HttpExceptionUtils.ERROR_MESSAGE_JSON, "EX");
    Map<String, Object> response = new HashMap<String, Object>();
    response.put(HttpExceptionUtils.ERROR_JSON, json);
    ObjectMapper jsonMapper = new ObjectMapper();
    String msg = jsonMapper.writeValueAsString(response);
    InputStream is = new ByteArrayInputStream(msg.getBytes(StandardCharsets.UTF_8));
    HttpURLConnection conn = Mockito.mock(HttpURLConnection.class);
    Mockito.when(conn.getErrorStream()).thenReturn(is);
    Mockito.when(conn.getResponseMessage()).thenReturn("msg");
    Mockito.when(conn.getResponseCode()).thenReturn(HttpURLConnection.HTTP_BAD_REQUEST);
    LambdaTestUtils.interceptAndValidateMessageContains(IOException.class,
        Arrays.asList(Integer.toString(HttpURLConnection.HTTP_BAD_REQUEST),
          "java.lang.String", "EX"),
        () -> HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_CREATED));
  }