public @Nullable <T> T get(Class<T> classType, @Nonnull String resource)
     throws CloudException, InternalException {
   try {
     return this.<T>get(
         classType, new URI(endpoint + provider.getContext().getAccountNumber() + resource));
   } catch (URISyntaxException e) {
     throw new InternalException(
         "Endpoint misconfiguration ("
             + endpoint
             + provider.getContext().getAccountNumber()
             + resource
             + "): "
             + e.getMessage());
   }
 }
  public AzureMethod(Azure azure) throws CloudException {
    provider = azure;
    ProviderContext ctx = provider.getContext();

    if (ctx == null) {
      throw new AzureConfigException("No context was provided for this request");
    }
    endpoint = ctx.getEndpoint();
    if (endpoint == null) {
      throw new AzureConfigException("No endpoint was provided for this request");
    }
    if (!endpoint.endsWith("/")) {
      endpoint = endpoint + "/";
    }

    this.strategy = provider.getContext().getRequestTrackingStrategy();
  }
  public <T> String post(String resource, T object)
      throws JAXBException, CloudException, InternalException {
    StringWriter stringWriter = new StringWriter();
    JAXBContext jc = JAXBContext.newInstance(object.getClass());
    Marshaller m = jc.createMarshaller();
    m.marshal(object, stringWriter);

    return post(provider.getContext().getAccountNumber(), resource, stringWriter.toString());
  }
  public @Nullable <T> T get(Class<T> classType, @Nonnull URI uri)
      throws CloudException, InternalException {
    InputStream responseAsStream = getAsStream(provider.getContext().getAccountNumber(), uri);

    if (responseAsStream == null) {
      logger.info("Unable to perform HTTP GET at following resource: " + uri.toString());
      return null;
    }
    try {
      JAXBContext context = JAXBContext.newInstance(classType);
      Unmarshaller u = context.createUnmarshaller();
      return (T) u.unmarshal(responseAsStream);
    } catch (Exception ex) {
      logger.error(ex.getMessage());
      throw new InternalException(ex);
    }
  }
  public @Nonnull int getOperationStatus(String requestID)
      throws CloudException, InternalException {
    ProviderContext ctx = provider.getContext();
    Document doc = getAsXML(ctx.getAccountNumber(), "/operations/" + requestID);

    if (doc == null) {
      return -2;
    }

    NodeList entries = doc.getElementsByTagName("Operation");
    Node entry = entries.item(0);

    NodeList s = entry.getChildNodes();

    String status = "";
    String httpCode = "";

    for (int i = 0; i < s.getLength(); i++) {
      Node attribute = s.item(i);
      if (attribute.getNodeType() == Node.TEXT_NODE) {
        continue;
      }
      if (attribute.getNodeName().equalsIgnoreCase("status") && attribute.hasChildNodes()) {
        status = attribute.getFirstChild().getNodeValue().trim();
        continue;
      }
      if (status.length() > 0 && !status.equalsIgnoreCase("inProgress")) {
        if (attribute.getNodeName().equalsIgnoreCase("httpstatuscode")
            && attribute.hasChildNodes()) {
          httpCode = attribute.getFirstChild().getNodeValue().trim();
        }
      }
    }

    if (status.equalsIgnoreCase("succeeded")) {
      return HttpServletResponse.SC_OK;
    } else if (status.equalsIgnoreCase("failed")) {
      String errMsg = checkError(s, httpCode);
      throw new CloudException(errMsg);
    }
    return -1;
  }
  private HttpProxyConfig getHttpProxyConfigData() {
    Properties p = provider.getContext().getCustomProperties();

    HttpProxyConfig httpProxyConfig = null;
    if (p != null && p.getProperty("proxyHost") != null && p.getProperty("proxyPort") != null) {
      if (p.getProperty("proxyPort").length() > 0) {
        httpProxyConfig =
            new HttpProxyConfig(
                p.getProperty("proxyHost"), Integer.parseInt(p.getProperty("proxyPort")));
      }
    } else {
      p = System.getProperties();
      if (p != null && p.getProperty("proxyHost") != null && p.getProperty("proxyPort") != null) {
        if (p.getProperty("proxyPort").length() > 0) {
          httpProxyConfig =
              new HttpProxyConfig(
                  p.getProperty("proxyHost"), Integer.parseInt(p.getProperty("proxyPort")));
        }
      }
    }
    return httpProxyConfig;
  }
/**
 * Handles connectivity to Microsoft Azure services.
 *
 * @author George Reese ([email protected])
 * @version 2012.07 initial version
 * @since 2012.07
 */
public class AzureMethod {
  private static final Logger logger = Azure.getLogger(AzureMethod.class);
  private static final Logger wire = Azure.getWireLogger(AzureMethod.class);

  public static class AzureResponse {
    public int httpCode;
    public Object body;
  }

  private class HttpProxyConfig {
    private String host;
    private Integer port;

    public HttpProxyConfig(String host, Integer port) {
      this.host = host;
      this.port = port;
    }

    public String getHost() {
      return host;
    }

    public Integer getPort() {
      return port;
    }
  }

  private String endpoint;
  private Azure provider;
  private RequestTrackingStrategy strategy;

  public AzureMethod(Azure azure) throws CloudException {
    provider = azure;
    ProviderContext ctx = provider.getContext();

    if (ctx == null) {
      throw new AzureConfigException("No context was provided for this request");
    }
    endpoint = ctx.getEndpoint();
    if (endpoint == null) {
      throw new AzureConfigException("No endpoint was provided for this request");
    }
    if (!endpoint.endsWith("/")) {
      endpoint = endpoint + "/";
    }

    this.strategy = provider.getContext().getRequestTrackingStrategy();
  }

  public @Nullable InputStream getAsStream(@Nonnull String account, @Nonnull String resource)
      throws CloudException, InternalException {
    try {
      return getAsStream(account, new URI(endpoint + account + resource));
    } catch (URISyntaxException e) {
      throw new InternalException(
          "Endpoint misconfiguration (" + endpoint + account + resource + "): " + e.getMessage());
    }
  }

  public @Nullable InputStream getAsStream(@Nonnull String account, @Nonnull URI uri)
      throws CloudException, InternalException {
    logger.trace("enter - " + AzureMethod.class.getName() + ".get(" + account + "," + uri + ")");
    wire.debug("--------------------------------------------------------> " + uri.toASCIIString());

    try {
      HttpClient client = getClient();
      HttpUriRequest get = new HttpGet(uri);

      if (uri.toString().indexOf("/services/images") > -1) {
        get.addHeader("x-ms-version", "2012-08-01");
      } else if (uri.toString().contains("/services/vmimages")) {
        get.addHeader("x-ms-version", "2014-05-01");
      } else {
        get.addHeader("x-ms-version", "2012-03-01");
      }

      if (strategy != null && strategy.getSendAsHeader()) {
        get.addHeader(strategy.getHeaderName(), strategy.getRequestId());
      }

      wire.debug(get.getRequestLine().toString());
      for (Header header : get.getAllHeaders()) {
        wire.debug(header.getName() + ": " + header.getValue());
      }

      HttpResponse response;
      StatusLine status;

      try {
        response = client.execute(get);
        status = response.getStatusLine();
      } catch (IOException e) {
        logger.error(
            "get(): Failed to execute HTTP request due to a cloud I/O error: " + e.getMessage());
        throw new CloudException(e);
      }

      logger.debug("get(): HTTP Status " + status);

      Header[] headers = response.getAllHeaders();

      wire.debug(status.toString());
      for (Header h : headers) {
        if (h.getValue() != null) {
          wire.debug(h.getName() + ": " + h.getValue().trim());
        } else {
          wire.debug(h.getName() + ":");
        }
      }

      if (status.getStatusCode() == HttpServletResponse.SC_NOT_FOUND) {
        return null;
      }
      if (status.getStatusCode() != HttpServletResponse.SC_OK
          && status.getStatusCode() != HttpServletResponse.SC_NON_AUTHORITATIVE_INFORMATION) {
        logger.error("get(): Expected OK for GET request, got " + status.getStatusCode());

        HttpEntity entity = response.getEntity();
        String body;

        if (entity == null) {
          throw new AzureException(
              CloudErrorType.GENERAL,
              status.getStatusCode(),
              status.getReasonPhrase(),
              "An error was returned without explanation");
        }
        try {
          body = EntityUtils.toString(entity);
        } catch (IOException e) {
          throw new AzureException(
              CloudErrorType.GENERAL,
              status.getStatusCode(),
              status.getReasonPhrase(),
              e.getMessage());
        }

        wire.debug(body);

        AzureException.ExceptionItems items =
            AzureException.parseException(status.getStatusCode(), body);

        if (items == null) {
          return null;
        }
        logger.error(
            "get(): [" + status.getStatusCode() + " : " + items.message + "] " + items.details);
        throw new AzureException(items);
      } else {
        HttpEntity entity = response.getEntity();

        if (entity == null) {
          return null;
        }
        InputStream input;

        try {
          input = entity.getContent();
        } catch (IOException e) {
          logger.error(
              "get(): Failed to read response error due to a cloud I/O error: " + e.getMessage());
          throw new CloudException(e);
        }

        wire.debug("---> Binary Data <---");
        return input;
      }
    } finally {
      logger.trace("exit - " + AzureMethod.class.getName() + ".getStream()");
      wire.debug(
          "--------------------------------------------------------> " + uri.toASCIIString());
    }
  }

  public @Nullable Document getAsXML(@Nonnull String account, @Nonnull String resource)
      throws CloudException, InternalException {
    try {
      return getAsXML(account, new URI(endpoint + account + resource));
    } catch (URISyntaxException e) {
      throw new InternalException(
          "Endpoint misconfiguration (" + endpoint + account + resource + "): " + e.getMessage());
    }
  }

  public @Nullable <T> T get(Class<T> classType, @Nonnull String resource)
      throws CloudException, InternalException {
    try {
      return this.<T>get(
          classType, new URI(endpoint + provider.getContext().getAccountNumber() + resource));
    } catch (URISyntaxException e) {
      throw new InternalException(
          "Endpoint misconfiguration ("
              + endpoint
              + provider.getContext().getAccountNumber()
              + resource
              + "): "
              + e.getMessage());
    }
  }

  public @Nullable <T> T get(Class<T> classType, @Nonnull URI uri)
      throws CloudException, InternalException {
    InputStream responseAsStream = getAsStream(provider.getContext().getAccountNumber(), uri);

    if (responseAsStream == null) {
      logger.info("Unable to perform HTTP GET at following resource: " + uri.toString());
      return null;
    }
    try {
      JAXBContext context = JAXBContext.newInstance(classType);
      Unmarshaller u = context.createUnmarshaller();
      return (T) u.unmarshal(responseAsStream);
    } catch (Exception ex) {
      logger.error(ex.getMessage());
      throw new InternalException(ex);
    }
  }

  public <T> String post(String resource, T object)
      throws JAXBException, CloudException, InternalException {
    StringWriter stringWriter = new StringWriter();
    JAXBContext jc = JAXBContext.newInstance(object.getClass());
    Marshaller m = jc.createMarshaller();
    m.marshal(object, stringWriter);

    return post(provider.getContext().getAccountNumber(), resource, stringWriter.toString());
  }

  public <T> String put(String resource, T object)
      throws JAXBException, CloudException, InternalException {
    StringWriter stringWriter = new StringWriter();
    JAXBContext jc = JAXBContext.newInstance(object.getClass());
    Marshaller m = jc.createMarshaller();
    m.marshal(object, stringWriter);

    return invoke(
        "PUT", provider.getContext().getAccountNumber(), resource, stringWriter.toString());
  }

  public @Nullable Document getAsXML(@Nonnull String account, @Nonnull URI uri)
      throws CloudException, InternalException {
    InputStream stream = getAsStream(account, uri);
    if (stream == null) return null;

    return parseResponse(stream, true);
  }

  protected @Nonnull HttpClient getClient() throws CloudException, InternalException {
    ProviderContext ctx = provider.getContext();

    if (ctx == null) {
      throw new AzureConfigException("No context was defined for this request");
    }
    String endpoint = ctx.getEndpoint();

    if (endpoint == null) {
      throw new AzureConfigException("No cloud endpoint was defined");
    }
    boolean ssl = endpoint.startsWith("https");
    int targetPort;

    try {
      URI uri = new URI(endpoint);

      targetPort = uri.getPort();
      if (targetPort < 1) {
        targetPort = (ssl ? 443 : 80);
      }
    } catch (URISyntaxException e) {
      throw new AzureConfigException(e);
    }
    HttpParams params = new BasicHttpParams();
    SchemeRegistry registry = new SchemeRegistry();

    try {
      registry.register(
          new Scheme(
              ssl ? "https" : "http",
              targetPort,
              new AzureSSLSocketFactory(new AzureX509(provider))));
    } catch (KeyManagementException e) {
      e.printStackTrace();
      throw new InternalException(e);
    } catch (UnrecoverableKeyException e) {
      e.printStackTrace();
      throw new InternalException(e);
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
      throw new InternalException(e);
    } catch (KeyStoreException e) {
      e.printStackTrace();
      throw new InternalException(e);
    }

    HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
    HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
    HttpProtocolParams.setUserAgent(params, "Dasein Cloud");
    params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 10000);
    params.setParameter(CoreConnectionPNames.SO_TIMEOUT, 300000);

    HttpProxyConfig httpProxyConfig = getHttpProxyConfigData();
    if (httpProxyConfig != null) {
      params.setParameter(
          ConnRoutePNames.DEFAULT_PROXY,
          new HttpHost(httpProxyConfig.getHost(), httpProxyConfig.getPort()));
      registry.register(new Scheme("http", httpProxyConfig.getPort(), new PlainSocketFactory()));
    }

    ClientConnectionManager ccm = new ThreadSafeClientConnManager(registry);

    return new DefaultHttpClient(ccm, params);
  }

  private HttpProxyConfig getHttpProxyConfigData() {
    Properties p = provider.getContext().getCustomProperties();

    HttpProxyConfig httpProxyConfig = null;
    if (p != null && p.getProperty("proxyHost") != null && p.getProperty("proxyPort") != null) {
      if (p.getProperty("proxyPort").length() > 0) {
        httpProxyConfig =
            new HttpProxyConfig(
                p.getProperty("proxyHost"), Integer.parseInt(p.getProperty("proxyPort")));
      }
    } else {
      p = System.getProperties();
      if (p != null && p.getProperty("proxyHost") != null && p.getProperty("proxyPort") != null) {
        if (p.getProperty("proxyPort").length() > 0) {
          httpProxyConfig =
              new HttpProxyConfig(
                  p.getProperty("proxyHost"), Integer.parseInt(p.getProperty("proxyPort")));
        }
      }
    }
    return httpProxyConfig;
  }

  public @Nonnull Document parseResponse(@Nonnull String responseBody, boolean withWireLogging)
      throws CloudException, InternalException {
    try {
      if (wire != null && wire.isDebugEnabled()) {
        String[] lines = responseBody.split("\n");

        if (lines.length < 1) {
          lines = new String[] {responseBody};
        }
        for (String l : lines) {
          wire.debug(l);
        }
        wire.debug("");
      }
      ByteArrayInputStream bas = new ByteArrayInputStream(responseBody.getBytes());

      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      DocumentBuilder parser = factory.newDocumentBuilder();
      Document doc = parser.parse(bas);

      bas.close();
      return doc;
    } catch (IOException e) {
      throw new CloudException(e);
    } catch (ParserConfigurationException e) {
      throw new CloudException(e);
    } catch (SAXException e) {
      throw new CloudException(e);
    }
  }

  public @Nonnull Document parseResponse(
      @Nonnull InputStream responseBodyAsStream, boolean withWireLogging)
      throws CloudException, InternalException {
    try {
      BufferedReader in = new BufferedReader(new InputStreamReader(responseBodyAsStream));
      StringBuilder sb = new StringBuilder();
      String line;

      while ((line = in.readLine()) != null) {
        sb.append(line);
        sb.append("\n");
      }
      in.close();
      return parseResponse(sb.toString(), withWireLogging);
    } catch (IOException e) {
      throw new CloudException(e);
    }
  }

  public String post(@Nonnull String account, @Nonnull String resource, @Nonnull String body)
      throws CloudException, InternalException {
    if (logger.isTraceEnabled()) {
      logger.trace(
          "enter - " + AzureMethod.class.getName() + ".post(" + account + "," + resource + ")");
    }
    if (wire.isDebugEnabled()) {
      wire.debug(
          "POST --------------------------------------------------------> "
              + endpoint
              + account
              + resource);
      wire.debug("");
    }
    String requestId = null;
    try {
      HttpClient client = getClient();
      String url = endpoint + account + resource;

      HttpPost post = new HttpPost(url);

      if (url.toLowerCase().contains("operations")) {
        post.addHeader("x-ms-version", "2014-02-01");
      } else if (url.toLowerCase().contains("/deployments")) {
        post.addHeader("x-ms-version", "2014-05-01");
      } else {
        post.addHeader("x-ms-version", "2012-03-01");
      }

      if (strategy != null && strategy.getSendAsHeader()) {
        post.addHeader(strategy.getHeaderName(), strategy.getRequestId());
      }

      // If it is networking configuration services
      if (url.endsWith("/services/networking/media")) {
        post.addHeader("Content-Type", "text/plain;charset=UTF-8");
      } else {
        post.addHeader("Content-Type", "application/xml;charset=UTF-8");
      }

      if (wire.isDebugEnabled()) {
        wire.debug(post.getRequestLine().toString());
        for (Header header : post.getAllHeaders()) {
          wire.debug(header.getName() + ": " + header.getValue());
        }
        wire.debug("");
        if (body != null) {
          wire.debug(body);
          wire.debug("");
        }
      }
      if (body != null) {
        try {
          if (url.endsWith("/services/networking/media")) {
            post.setEntity(new StringEntity(body, "text/plain", "utf-8"));
          } else {
            post.setEntity(new StringEntity(body, "application/xml", "utf-8"));
          }

        } catch (UnsupportedEncodingException e) {
          throw new InternalException(e);
        }
      }
      HttpResponse response;
      StatusLine status;

      try {
        response = client.execute(post);
        status = response.getStatusLine();
      } catch (IOException e) {
        logger.error(
            "post(): Failed to execute HTTP request due to a cloud I/O error: " + e.getMessage());
        if (logger.isTraceEnabled()) {
          e.printStackTrace();
        }
        throw new CloudException(e);
      }
      if (logger.isDebugEnabled()) {
        logger.debug("post(): HTTP Status " + status);
      }
      Header[] headers = response.getAllHeaders();

      if (wire.isDebugEnabled()) {
        wire.debug(status.toString());
      }
      for (Header h : headers) {
        if (h.getValue() != null) {
          if (wire.isDebugEnabled()) {
            wire.debug(h.getName() + ": " + h.getValue().trim());
          }
          if (h.getName().equalsIgnoreCase("x-ms-request-id")) {
            requestId = h.getValue().trim();
          }
        } else {
          if (wire.isDebugEnabled()) {
            wire.debug(h.getName() + ":");
          }
        }
      }
      if (wire.isDebugEnabled()) {
        wire.debug("");
      }

      if (status.getStatusCode() != HttpServletResponse.SC_OK
          && status.getStatusCode() != HttpServletResponse.SC_CREATED
          && status.getStatusCode() != HttpServletResponse.SC_ACCEPTED) {
        logger.error("post(): Expected OK for GET request, got " + status.getStatusCode());

        HttpEntity entity = response.getEntity();

        if (entity == null) {
          throw new AzureException(
              CloudErrorType.GENERAL,
              status.getStatusCode(),
              status.getReasonPhrase(),
              "An error was returned without explanation");
        }
        try {
          body = EntityUtils.toString(entity);
        } catch (IOException e) {
          throw new AzureException(
              CloudErrorType.GENERAL,
              status.getStatusCode(),
              status.getReasonPhrase(),
              e.getMessage());
        }
        if (wire.isDebugEnabled()) {
          wire.debug(body);
        }
        wire.debug("");
        AzureException.ExceptionItems items =
            AzureException.parseException(status.getStatusCode(), body);

        if (items == null) {
          throw new CloudException(
              CloudErrorType.GENERAL, status.getStatusCode(), "Unknown", "Unknown");
        }
        logger.error(
            "post(): [" + status.getStatusCode() + " : " + items.message + "] " + items.details);
        throw new AzureException(items);
      }
    } finally {
      if (logger.isTraceEnabled()) {
        logger.trace("exit - " + AzureMethod.class.getName() + ".post()");
      }
      if (wire.isDebugEnabled()) {
        wire.debug("");
        wire.debug(
            "POST --------------------------------------------------------> "
                + endpoint
                + account
                + resource);
      }
    }
    return requestId;
  }

  protected HttpRequestBase getMethod(String httpMethod, String url) {
    HttpRequestBase method = null;
    if (httpMethod.equals("GET")) {
      method = new HttpGet(url);
    } else if (httpMethod.equals("POST")) {
      method = new HttpPost(url);
    } else if (httpMethod.equals("PUT")) {
      method = new HttpPut(url);
    } else if (httpMethod.equals("DELETE")) {
      method = new HttpDelete(url);
    } else if (httpMethod.equals("HEAD")) {
      method = new HttpHead(url);
    } else if (httpMethod.equals("OPTIONS")) {
      method = new HttpOptions(url);
    } else if (httpMethod.equals("HEAD")) {
      method = new HttpTrace(url);
    } else {
      method = new HttpGet(url);
    }
    return method;
  }

  public String invoke(
      @Nonnull String method,
      @Nonnull String account,
      @Nonnull String resource,
      @Nonnull String body)
      throws CloudException, InternalException {
    if (logger.isTraceEnabled()) {
      logger.trace(
          "enter - " + AzureMethod.class.getName() + ".post(" + account + "," + resource + ")");
    }
    if (wire.isDebugEnabled()) {
      wire.debug(
          "POST --------------------------------------------------------> "
              + endpoint
              + account
              + resource);
      wire.debug("");
    }
    String requestId = null;
    try {
      HttpClient client = getClient();
      String url = endpoint + account + resource;

      HttpRequestBase httpMethod = getMethod(method, url);

      // If it is networking configuration services
      if (httpMethod instanceof HttpPut) {
        if (url.endsWith("/services/networking/media")) {
          httpMethod.addHeader("Content-Type", "text/plain");
        } else {
          httpMethod.addHeader("Content-Type", "application/xml;charset=UTF-8");
        }
      } else {
        httpMethod.addHeader("Content-Type", "application/xml;charset=UTF-8");
      }

      // dmayne version is older for anything to do with images and for disk deletion
      if (url.indexOf("/services/images") > -1
          || (httpMethod instanceof HttpDelete && url.indexOf("/services/disks") > -1)) {
        httpMethod.addHeader("x-ms-version", "2012-08-01");
      } else if (httpMethod instanceof HttpDelete && url.indexOf("services/vmimages") > -1) {
        httpMethod.addHeader("x-ms-version", "2014-06-01");
      } else if (httpMethod instanceof HttpDelete
          && url.contains("deployments")
          && url.endsWith("?comp=media")) {
        httpMethod.addHeader("x-ms-version", "2013-08-01");
      } else {
        httpMethod.addHeader("x-ms-version", "2012-03-01");
      }

      if (strategy != null && strategy.getSendAsHeader()) {
        httpMethod.addHeader(strategy.getHeaderName(), strategy.getRequestId());
      }

      if (wire.isDebugEnabled()) {
        wire.debug(httpMethod.getRequestLine().toString());
        for (Header header : httpMethod.getAllHeaders()) {
          wire.debug(header.getName() + ": " + header.getValue());
        }
        wire.debug("");
        if (body != null) {
          wire.debug(body);
          wire.debug("");
        }
      }

      if (httpMethod instanceof HttpEntityEnclosingRequestBase) {

        HttpEntityEnclosingRequestBase entityEnclosingMethod =
            (HttpEntityEnclosingRequestBase) httpMethod;

        if (body != null) {
          try {
            entityEnclosingMethod.setEntity(new StringEntity(body, "application/xml", "utf-8"));
          } catch (UnsupportedEncodingException e) {
            throw new CloudException(e);
          }
        }
      }

      HttpResponse response;
      StatusLine status;

      try {
        response = client.execute(httpMethod);
        status = response.getStatusLine();
      } catch (IOException e) {
        logger.error(
            "post(): Failed to execute HTTP request due to a cloud I/O error: " + e.getMessage());
        if (logger.isTraceEnabled()) {
          e.printStackTrace();
        }
        throw new CloudException(e);
      }
      if (logger.isDebugEnabled()) {
        logger.debug("post(): HTTP Status " + status);
      }
      Header[] headers = response.getAllHeaders();

      if (wire.isDebugEnabled()) {
        wire.debug(status.toString());
      }
      for (Header h : headers) {
        if (h.getValue() != null) {
          if (wire.isDebugEnabled()) {
            wire.debug(h.getName() + ": " + h.getValue().trim());
          }
          if (h.getName().equalsIgnoreCase("x-ms-request-id")) {
            requestId = h.getValue().trim();
          }
        } else {
          if (wire.isDebugEnabled()) {
            wire.debug(h.getName() + ":");
          }
        }
      }
      if (wire.isDebugEnabled()) {
        wire.debug("");
      }
      if (status.getStatusCode() == HttpServletResponse.SC_TEMPORARY_REDIRECT) {
        logger.warn("Expected OK, got " + status.getStatusCode());

        String responseBody = "";

        HttpEntity entity = response.getEntity();

        if (entity == null) {
          throw new AzureException(
              CloudErrorType.GENERAL,
              status.getStatusCode(),
              status.getReasonPhrase(),
              "An error was returned without explanation");
        }
        try {
          responseBody = EntityUtils.toString(entity);
        } catch (IOException e) {
          throw new AzureException(
              CloudErrorType.GENERAL,
              status.getStatusCode(),
              status.getReasonPhrase(),
              e.getMessage());
        }
        logger.debug(responseBody);
        logger.debug("https: char " + responseBody.indexOf("https://"));
        logger.debug("account number: char " + responseBody.indexOf(account));
        String tempEndpoint =
            responseBody.substring(
                responseBody.indexOf("https://"),
                responseBody.indexOf(account) - responseBody.indexOf("https://"));
        logger.debug("temp redirect location: " + tempEndpoint);
        tempRedirectInvoke(tempEndpoint, method, account, resource, body);
      } else if (status.getStatusCode() != HttpServletResponse.SC_OK
          && status.getStatusCode() != HttpServletResponse.SC_CREATED
          && status.getStatusCode() != HttpServletResponse.SC_ACCEPTED) {
        logger.error("post(): Expected OK for GET request, got " + status.getStatusCode());

        HttpEntity entity = response.getEntity();

        if (entity == null) {
          throw new AzureException(
              CloudErrorType.GENERAL,
              status.getStatusCode(),
              status.getReasonPhrase(),
              "An error was returned without explanation");
        }
        try {
          body = EntityUtils.toString(entity);
        } catch (IOException e) {
          throw new AzureException(
              CloudErrorType.GENERAL,
              status.getStatusCode(),
              status.getReasonPhrase(),
              e.getMessage());
        }
        if (wire.isDebugEnabled()) {
          wire.debug(body);
        }
        wire.debug("");
        AzureException.ExceptionItems items =
            AzureException.parseException(status.getStatusCode(), body);

        if (items == null) {
          throw new CloudException(
              CloudErrorType.GENERAL, status.getStatusCode(), "Unknown", "Unknown");
        }
        logger.error(
            "post(): [" + status.getStatusCode() + " : " + items.message + "] " + items.details);
        throw new AzureException(items);
      }
    } finally {
      if (logger.isTraceEnabled()) {
        logger.trace("exit - " + AzureMethod.class.getName() + ".post()");
      }
      if (wire.isDebugEnabled()) {
        wire.debug("");
        wire.debug(
            "POST --------------------------------------------------------> "
                + endpoint
                + account
                + resource);
      }
    }
    return requestId;
  }

  public void tempRedirectInvoke(
      @Nonnull String tempEndpoint,
      @Nonnull String method,
      @Nonnull String account,
      @Nonnull String resource,
      @Nonnull String body)
      throws CloudException, InternalException {
    if (logger.isTraceEnabled()) {
      logger.trace(
          "enter - " + AzureMethod.class.getName() + ".post(" + account + "," + resource + ")");
    }
    if (wire.isDebugEnabled()) {
      wire.debug(
          "POST --------------------------------------------------------> "
              + endpoint
              + account
              + resource);
      wire.debug("");
    }
    try {
      HttpClient client = getClient();
      String url = tempEndpoint + account + resource;

      HttpRequestBase httpMethod = getMethod(method, url);

      // If it is networking configuration services
      if (httpMethod instanceof HttpPut) {
        if (url.endsWith("/services/networking/media")) {
          httpMethod.addHeader("Content-Type", "text/plain");
        } else {
          httpMethod.addHeader("Content-Type", "application/xml;charset=UTF-8");
        }
      } else {
        httpMethod.addHeader("Content-Type", "application/xml;charset=UTF-8");
      }

      // dmayne version is older for anything to do with images and for disk deletion
      if (url.indexOf("/services/images") > -1
          || (httpMethod instanceof HttpDelete && url.indexOf("/services/disks") > -1)) {
        httpMethod.addHeader("x-ms-version", "2012-08-01");
      } else {
        httpMethod.addHeader("x-ms-version", "2012-03-01");
      }

      if (strategy != null && strategy.getSendAsHeader()) {
        httpMethod.addHeader(strategy.getHeaderName(), strategy.getRequestId());
      }

      if (wire.isDebugEnabled()) {
        wire.debug(httpMethod.getRequestLine().toString());
        for (Header header : httpMethod.getAllHeaders()) {
          wire.debug(header.getName() + ": " + header.getValue());
        }
        wire.debug("");
        if (body != null) {
          wire.debug(body);
          wire.debug("");
        }
      }

      if (httpMethod instanceof HttpEntityEnclosingRequestBase) {

        HttpEntityEnclosingRequestBase entityEnclosingMethod =
            (HttpEntityEnclosingRequestBase) httpMethod;

        if (body != null) {
          try {
            entityEnclosingMethod.setEntity(new StringEntity(body, "application/xml", "utf-8"));
          } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
        }
      }

      HttpResponse response;
      StatusLine status;

      try {
        response = client.execute(httpMethod);
        status = response.getStatusLine();
      } catch (IOException e) {
        logger.error(
            "post(): Failed to execute HTTP request due to a cloud I/O error: " + e.getMessage());
        if (logger.isTraceEnabled()) {
          e.printStackTrace();
        }
        throw new CloudException(e);
      }
      if (logger.isDebugEnabled()) {
        logger.debug("post(): HTTP Status " + status);
      }
      Header[] headers = response.getAllHeaders();

      if (wire.isDebugEnabled()) {
        wire.debug(status.toString());
        for (Header h : headers) {
          if (h.getValue() != null) {
            wire.debug(h.getName() + ": " + h.getValue().trim());
          } else {
            wire.debug(h.getName() + ":");
          }
        }
        wire.debug("");
      }
      if (status.getStatusCode() != HttpServletResponse.SC_OK
          && status.getStatusCode() != HttpServletResponse.SC_CREATED
          && status.getStatusCode() != HttpServletResponse.SC_ACCEPTED) {
        logger.error("post(): Expected OK for GET request, got " + status.getStatusCode());

        HttpEntity entity = response.getEntity();

        if (entity == null) {
          throw new AzureException(
              CloudErrorType.GENERAL,
              status.getStatusCode(),
              status.getReasonPhrase(),
              "An error was returned without explanation");
        }
        try {
          body = EntityUtils.toString(entity);
        } catch (IOException e) {
          throw new AzureException(
              CloudErrorType.GENERAL,
              status.getStatusCode(),
              status.getReasonPhrase(),
              e.getMessage());
        }
        if (wire.isDebugEnabled()) {
          wire.debug(body);
        }
        wire.debug("");
        AzureException.ExceptionItems items =
            AzureException.parseException(status.getStatusCode(), body);

        if (items == null) {
          throw new CloudException(
              CloudErrorType.GENERAL, status.getStatusCode(), "Unknown", "Unknown");
        }
        logger.error(
            "post(): [" + status.getStatusCode() + " : " + items.message + "] " + items.details);
        throw new AzureException(items);
      }
    } finally {
      if (logger.isTraceEnabled()) {
        logger.trace("exit - " + AzureMethod.class.getName() + ".post()");
      }
      if (wire.isDebugEnabled()) {
        wire.debug("");
        wire.debug(
            "POST --------------------------------------------------------> "
                + endpoint
                + account
                + resource);
      }
    }
  }

  public @Nonnull int getOperationStatus(String requestID)
      throws CloudException, InternalException {
    ProviderContext ctx = provider.getContext();
    Document doc = getAsXML(ctx.getAccountNumber(), "/operations/" + requestID);

    if (doc == null) {
      return -2;
    }

    NodeList entries = doc.getElementsByTagName("Operation");
    Node entry = entries.item(0);

    NodeList s = entry.getChildNodes();

    String status = "";
    String httpCode = "";

    for (int i = 0; i < s.getLength(); i++) {
      Node attribute = s.item(i);
      if (attribute.getNodeType() == Node.TEXT_NODE) {
        continue;
      }
      if (attribute.getNodeName().equalsIgnoreCase("status") && attribute.hasChildNodes()) {
        status = attribute.getFirstChild().getNodeValue().trim();
        continue;
      }
      if (status.length() > 0 && !status.equalsIgnoreCase("inProgress")) {
        if (attribute.getNodeName().equalsIgnoreCase("httpstatuscode")
            && attribute.hasChildNodes()) {
          httpCode = attribute.getFirstChild().getNodeValue().trim();
        }
      }
    }

    if (status.equalsIgnoreCase("succeeded")) {
      return HttpServletResponse.SC_OK;
    } else if (status.equalsIgnoreCase("failed")) {
      String errMsg = checkError(s, httpCode);
      throw new CloudException(errMsg);
    }
    return -1;
  }

  private String checkError(NodeList s, String httpCode) throws CloudException, InternalException {
    String errMsg = httpCode + ": ";
    for (int i = 0; i < s.getLength(); i++) {
      Node attribute = s.item(i);
      if (attribute.getNodeType() == Node.TEXT_NODE) {
        continue;
      }
      if (attribute.getNodeName().equalsIgnoreCase("Error") && attribute.hasChildNodes()) {
        NodeList errors = attribute.getChildNodes();
        for (int error = 0; error < errors.getLength(); error++) {
          Node node = errors.item(error);
          if (node.getNodeName().equalsIgnoreCase("code") && node.hasChildNodes()) {
            errMsg = errMsg + node.getFirstChild().getNodeValue().trim();
            continue;
          }
          if (node.getNodeName().equalsIgnoreCase("message") && node.hasChildNodes()) {
            errMsg = errMsg + ". reason: " + node.getFirstChild().getNodeValue().trim();
          }
        }
      }
    }
    return errMsg;
  }
}
  protected @Nonnull HttpClient getClient() throws CloudException, InternalException {
    ProviderContext ctx = provider.getContext();

    if (ctx == null) {
      throw new AzureConfigException("No context was defined for this request");
    }
    String endpoint = ctx.getEndpoint();

    if (endpoint == null) {
      throw new AzureConfigException("No cloud endpoint was defined");
    }
    boolean ssl = endpoint.startsWith("https");
    int targetPort;

    try {
      URI uri = new URI(endpoint);

      targetPort = uri.getPort();
      if (targetPort < 1) {
        targetPort = (ssl ? 443 : 80);
      }
    } catch (URISyntaxException e) {
      throw new AzureConfigException(e);
    }
    HttpParams params = new BasicHttpParams();
    SchemeRegistry registry = new SchemeRegistry();

    try {
      registry.register(
          new Scheme(
              ssl ? "https" : "http",
              targetPort,
              new AzureSSLSocketFactory(new AzureX509(provider))));
    } catch (KeyManagementException e) {
      e.printStackTrace();
      throw new InternalException(e);
    } catch (UnrecoverableKeyException e) {
      e.printStackTrace();
      throw new InternalException(e);
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
      throw new InternalException(e);
    } catch (KeyStoreException e) {
      e.printStackTrace();
      throw new InternalException(e);
    }

    HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
    HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
    HttpProtocolParams.setUserAgent(params, "Dasein Cloud");
    params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 10000);
    params.setParameter(CoreConnectionPNames.SO_TIMEOUT, 300000);

    HttpProxyConfig httpProxyConfig = getHttpProxyConfigData();
    if (httpProxyConfig != null) {
      params.setParameter(
          ConnRoutePNames.DEFAULT_PROXY,
          new HttpHost(httpProxyConfig.getHost(), httpProxyConfig.getPort()));
      registry.register(new Scheme("http", httpProxyConfig.getPort(), new PlainSocketFactory()));
    }

    ClientConnectionManager ccm = new ThreadSafeClientConnManager(registry);

    return new DefaultHttpClient(ccm, params);
  }