com.infinities.skyport.openstack.nova.os.SkyportNovaMethod.java Source code

Java tutorial

Introduction

Here is the source code for com.infinities.skyport.openstack.nova.os.SkyportNovaMethod.java

Source

/*******************************************************************************
 * Copyright 2015 InfinitiesSoft Solutions 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.infinities.skyport.openstack.nova.os;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
import org.dasein.cloud.CloudErrorType;
import org.dasein.cloud.CloudException;
import org.dasein.cloud.ContextRequirements;
import org.dasein.cloud.InternalException;
import org.dasein.cloud.ProviderContext;
import org.dasein.cloud.openstack.nova.os.AbstractMethod;
import org.dasein.cloud.openstack.nova.os.AuthenticationContext;
import org.dasein.cloud.openstack.nova.os.NovaException;
import org.dasein.cloud.openstack.nova.os.NovaMethod;
import org.dasein.cloud.openstack.nova.os.NovaOpenStack;
import org.dasein.cloud.openstack.nova.os.OpenStackProvider;
import org.dasein.cloud.openstack.nova.os.ext.hp.db.HPRDBMS;
import org.dasein.cloud.util.APITrace;
import org.dasein.cloud.util.Cache;
import org.dasein.cloud.util.CacheLevel;
import org.dasein.util.CalendarWrapper;
import org.dasein.util.uom.time.Day;
import org.dasein.util.uom.time.TimePeriod;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.google.common.base.Strings;

/**
 * This is a customized version of
 * org.dasein.cloud.openstack.nova.os.NovaMethod.
 * <p>
 * Created by Pohsun Huang: 12/23/15 10:57 AM
 * </p>
 * 
 * @author Pohsun Huang
 * @version 2015.12 initial version
 * @since 2015.12
 */
public class SkyportNovaMethod extends NovaMethod {

    protected NovaOpenStack provider;

    public SkyportNovaMethod(NovaOpenStack provider) {
        super(provider);
        this.provider = provider;
    }

    @Override
    public synchronized @Nullable AuthenticationContext authenticate() throws CloudException, InternalException {
        Logger std = NovaOpenStack.getLogger(NovaOpenStack.class, "std");

        if (std.isTraceEnabled()) {
            std.trace("enter - " + AbstractMethod.class.getName() + ".authenticate()");
        }
        try {
            ProviderContext ctx = provider.getContext();

            if (ctx == null) {
                throw new CloudException("Unable to authenticate due to lack of context");
            }
            String endpoint = ctx.getEndpoint();

            if (endpoint == null) {
                throw new CloudException("No authentication endpoint");
            }
            AuthenticationContext auth;

            if (endpoint.startsWith("ks:")) {
                endpoint = endpoint.substring(3);
                auth = authenticateKeystone(endpoint);
            } else if (endpoint.startsWith("st:")) {
                endpoint = endpoint.substring(3);
                auth = authenticateStandard(endpoint);
            } else {
                if (endpoint.endsWith("1.0") || endpoint.endsWith("1.0/") || endpoint.endsWith("1.1")
                        || endpoint.endsWith("1.1/")) {
                    auth = authenticateStandard(endpoint);
                    if (auth == null) {
                        auth = authenticateSwift(endpoint);
                    }
                    if (auth == null) {
                        auth = authenticateKeystone(endpoint);
                    }
                } else {
                    auth = authenticateKeystone(endpoint);
                    if (auth == null) {
                        auth = authenticateStandard(endpoint);
                    }
                    if (auth == null) {
                        auth = authenticateSwift(endpoint);
                    }
                }
            }
            return auth;
        } finally {
            if (std.isTraceEnabled()) {
                std.trace("exit - " + AbstractMethod.class.getName() + ".authenticate()");
            }
        }
    }

    private @Nullable AuthenticationContext authenticateKeystone(@Nonnull String endpoint)
            throws CloudException, InternalException {
        Logger std = NovaOpenStack.getLogger(NovaOpenStack.class, "std");
        Logger wire = NovaOpenStack.getLogger(NovaOpenStack.class, "wire");

        if (std.isTraceEnabled()) {
            std.trace("enter - " + AbstractMethod.class.getName() + ".authenticateKeystone(" + endpoint + ")");
        }
        if (wire.isDebugEnabled()) {
            wire.debug("KEYSTONE --------------------------------------------------------> " + endpoint);
            wire.debug("");
        }

        HttpClient client = null;

        try {
            String accessPublic = null;
            String accessPrivate = null;
            String urlType = "publicURL";
            String account = provider.getContext().getAccountNumber();
            try {
                List<ContextRequirements.Field> fields = provider.getContextRequirements().getConfigurableValues();
                for (ContextRequirements.Field f : fields) {
                    if (f.type.equals(ContextRequirements.FieldType.KEYPAIR)) {
                        byte[][] keyPair = (byte[][]) provider.getContext().getConfigurationValue(f);
                        accessPublic = new String(keyPair[0], "utf-8");
                        accessPrivate = new String(keyPair[1], "utf-8");
                    }
                    if (f.type.equals(ContextRequirements.FieldType.TEXT) && f.name.equals("urlType")) {
                        String type = (String) provider.getContext().getConfigurationValue(f);
                        if (Strings.isNullOrEmpty(type)) {
                            urlType = "publicURL";
                        } else {
                            urlType = type;
                        }
                    }
                }
            } catch (UnsupportedEncodingException e) {
                std.error("authenticateKeystone(): Unable to read access credentials: " + e.getMessage());
                e.printStackTrace();
                throw new InternalException(e);
            }

            if (std.isInfoEnabled()) {
                std.info("authenticateKeystone(): Attempting keystone authentication...");
            }
            HashMap<String, Object> json = new HashMap<String, Object>();
            HashMap<String, Object> credentials = new HashMap<String, Object>();

            if (provider.getCloudProvider().equals(OpenStackProvider.HP)) {
                if (std.isInfoEnabled()) {
                    std.info("HP authentication");
                }
                credentials.put("accessKey", accessPublic);
                credentials.put("secretKey", accessPrivate);
                json.put("apiAccessKeyCredentials", credentials);
            } else if (provider.getCloudProvider().equals(OpenStackProvider.RACKSPACE)) {
                if (std.isInfoEnabled()) {
                    std.info("Rackspace authentication");
                }
                credentials.put("username", accessPublic);
                credentials.put("apiKey", accessPrivate);
                json.put("RAX-KSKEY:apiKeyCredentials", credentials);
            } else {
                if (std.isInfoEnabled()) {
                    std.info("Standard authentication");
                }
                credentials.put("username", accessPublic);
                credentials.put("password", accessPrivate);
                json.put("passwordCredentials", credentials);
            }
            if (std.isDebugEnabled()) {
                std.debug("authenticateKeystone(): tenantId=" + account);
            }
            if (!provider.getCloudProvider().equals(OpenStackProvider.RACKSPACE)) {
                String acct = account;

                if (provider.getCloudProvider().equals(OpenStackProvider.HP)) {
                    json.put("tenantId", acct);
                } else {
                    // a hack
                    if (acct.length() == 32) {
                        json.put("tenantId", acct);
                    } else {
                        json.put("tenantName", acct);
                    }
                }
            }
            HashMap<String, Object> jsonAuth = new HashMap<String, Object>();

            jsonAuth.put("auth", json);

            client = getClient();
            HttpPost post = new HttpPost(endpoint + "/tokens");

            post.addHeader("Content-Type", "application/json");
            if (wire.isDebugEnabled()) {
                wire.debug(post.getRequestLine().toString());
                for (Header header : post.getAllHeaders()) {
                    wire.debug(header.getName() + ": " + header.getValue());
                }
                wire.debug("");
            }

            String payload = (new JSONObject(jsonAuth)).toString();

            try {
                // noinspection deprecation
                post.setEntity(new StringEntity(payload == null ? "" : payload, "application/json", "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                throw new InternalException(e);
            }
            /*
             * try { wire.debug(EntityUtils.toString(post.getEntity())); }
             * catch( IOException ignore ) { }
             */

            wire.debug("");

            HttpResponse response;

            try {
                APITrace.trace(provider, "POST authenticateKeystone");
                response = client.execute(post);
                if (wire.isDebugEnabled()) {
                    wire.debug(response.getStatusLine().toString());
                    for (Header header : response.getAllHeaders()) {
                        wire.debug(header.getName() + ": " + header.getValue());
                    }
                    wire.debug("");
                }
            } catch (IOException e) {
                std.error("I/O error from server communications: " + e.getMessage());
                e.printStackTrace();
                throw new InternalException(e);
            }
            int code = response.getStatusLine().getStatusCode();

            std.debug("HTTP STATUS: " + code);

            if (code != HttpStatus.SC_OK) {
                if (code == 401 || code == 405) {
                    std.warn("authenticateKeystone(): Authentication failed");
                    return null;
                }
                std.error("authenticateKeystone(): Expected OK, got " + code);
                String data = null;

                try {
                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                        data = EntityUtils.toString(entity);
                        if (wire.isDebugEnabled()) {
                            wire.debug(data);
                            wire.debug("");
                        }
                    }
                } catch (IOException e) {
                    std.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                    e.printStackTrace();
                    throw new CloudException(e);
                }
                NovaException.ExceptionItems items = NovaException.parseException(code, data);

                if (items == null) {
                    items = new NovaException.ExceptionItems();
                    items.code = 404;
                    items.type = CloudErrorType.COMMUNICATION;
                    items.message = "itemNotFound";
                    items.details = "No such object: " + "/tokens";
                }
                std.error("authenticateKeystone(): [" + code + " : " + items.message + "] " + items.details);
                throw new NovaException(items);
            } else {
                String data = null;

                try {
                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                        data = EntityUtils.toString(entity);
                        if (wire.isDebugEnabled()) {
                            wire.debug(data);
                            wire.debug("");
                        }
                    }
                } catch (IOException e) {
                    std.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                    e.printStackTrace();
                    throw new CloudException(e);
                }
                if (data != null && !data.trim().equals("")) {
                    if (std.isInfoEnabled()) {
                        std.info("authenticateKeystone(): Keystone authentication successful");
                    }
                    String id, tenantId;
                    JSONArray catalog;
                    JSONObject token;

                    try {
                        JSONObject rj = new JSONObject(data);
                        JSONObject auth = rj.getJSONObject("access");

                        token = auth.getJSONObject("token");
                        catalog = auth.getJSONArray("serviceCatalog");
                        id = (token.has("id") ? token.getString("id") : null);
                        tenantId = ((token.has("tenantId") && !token.isNull("tenantId"))
                                ? token.getString("tenantId")
                                : null);
                        if (tenantId == null && token.has("tenant") && !token.isNull("tenant")) {
                            JSONObject t = token.getJSONObject("tenant");

                            if (t.has("id") && !t.isNull("id")) {
                                tenantId = t.getString("id");
                            }
                        }
                    } catch (JSONException e) {
                        std.error("authenticateKeystone(): Invalid response from server: " + e.getMessage());
                        e.printStackTrace();
                        throw new CloudException(e);
                    }
                    if (tenantId == null) {
                        tenantId = account;
                    }
                    if (id != null) {
                        HashMap<String, Map<String, String>> services = new HashMap<String, Map<String, String>>();
                        HashMap<String, Map<String, String>> bestVersion = new HashMap<String, Map<String, String>>();
                        String myRegionId = provider.getContext().getRegionId();

                        if (std.isDebugEnabled()) {
                            std.debug("authenticateKeystone(): myRegionId=" + myRegionId);
                        }
                        if (std.isInfoEnabled()) {
                            std.info("authenticateKeystone(): Processing service catalog...");
                        }
                        for (int i = 0; i < catalog.length(); i++) {
                            try {
                                JSONObject service = catalog.getJSONObject(i);
                                /*
                                 * System.out.println(
                                 * "---------------------------------------------------"
                                 * ); System.out.println("Service=");
                                 * System.out.println(service.toString());
                                 * System.out.println(
                                 * "---------------------------------------------------"
                                 * );
                                 */
                                String type = service.getString("type");
                                JSONArray endpoints = service.getJSONArray("endpoints");

                                if (std.isDebugEnabled()) {
                                    std.debug("authenticateKeystone(): type=" + type);
                                }
                                for (int j = 0; j < endpoints.length(); j++) {
                                    JSONObject test = endpoints.getJSONObject(j);
                                    String url = test.getString(urlType);

                                    if (std.isDebugEnabled()) {
                                        std.debug("authenticateKeystone(): endpoint[" + j + "]=" + url);
                                    }
                                    if (url != null) {
                                        String version = test.optString("versionId");

                                        if (version == null || version.equals("")) {
                                            std.debug("No versionId parameter... Parsing URL " + url
                                                    + " for best guess.  (vSadTrombone)");
                                            Pattern p = Pattern.compile("/v(.+?)/|/v(.+?)$");
                                            Matcher m = p.matcher(url);
                                            if (m.find()) {
                                                version = m.group(1);
                                                if (version == null)
                                                    version = m.group(2);
                                            } else {
                                                version = "1.0";
                                            }
                                        }
                                        if (std.isDebugEnabled()) {
                                            std.debug("authenticateKeystone(): version[" + j + "]=" + version);
                                        }
                                        if (NovaOpenStack.isSupported(version)) {
                                            String regionId = (test.has("region") ? test.getString("region")
                                                    : null);

                                            if (std.isDebugEnabled()) {
                                                std.debug("authenticateKeystone(): region[" + j + "]=" + regionId);
                                            }
                                            Map<String, String> map = services.get(type);
                                            Map<String, String> verMap = bestVersion.get(type);

                                            if (map == null) {
                                                map = new HashMap<String, String>();
                                                verMap = new HashMap<String, String>();
                                                if (std.isInfoEnabled()) {
                                                    std.info("authenticateKeystone(): Putting (" + type + "," + map
                                                            + ") into services.");
                                                }
                                                services.put(type, map);
                                                bestVersion.put(type, verMap);
                                            }
                                            if (regionId == null & version.equals("1.0") && !provider
                                                    .getCloudProvider().equals(OpenStackProvider.RACKSPACE)) {
                                                std.warn(
                                                        "authenticateKeystone(): No region defined, making one up based on the URL: "
                                                                + url);
                                                regionId = toRegion(url);
                                                std.warn("authenticateKeystone(): Fabricated region is: "
                                                        + regionId);
                                            } else if (regionId == null
                                                    && (type.equals("compute") || type.equals("object-store"))) {
                                                std.warn(
                                                        "authenticateKeystone(): No region defined for Rackspace, assuming it is pre-OpenStack and skipping");
                                                continue;
                                            }
                                            if (std.isDebugEnabled()) {
                                                std.debug("authenticateKeystone(): finalRegionId=" + regionId);
                                            }
                                            if (std.isInfoEnabled()) {
                                                std.info("authenticateKeystone(): Comparing " + version
                                                        + " against " + verMap.get(type));
                                            }
                                            if (verMap.get(type) == null
                                                    || compareVersions(version, verMap.get(type)) >= 0) {
                                                if (std.isInfoEnabled()) {
                                                    std.info("authenticateKeystone(): Putting (" + regionId + ","
                                                            + url + ") into the " + type + " map.");
                                                }
                                                verMap.put(type, version);
                                                map.put(regionId, url);
                                            } else {
                                                std.warn("authenticateKeystone(): Skipping lower version url " + url
                                                        + " for " + type + " map.");
                                            }

                                            if (myRegionId == null) {
                                                myRegionId = regionId;
                                                if (std.isInfoEnabled()) {
                                                    std.info(
                                                            "authenticateKeystone(): myRegionId now " + myRegionId);
                                                }
                                            }
                                        }
                                    }
                                }
                            } catch (JSONException e) {
                                std.error("authenticateKeystone(): Failed to read JSON from server: "
                                        + e.getMessage());
                                e.printStackTrace();
                                throw new CloudException(e);
                            }
                        }
                        if (std.isDebugEnabled()) {
                            std.debug("services=" + services);
                        }
                        // TODO: remove this when HP has the DBaaS in the
                        // service catalog
                        if (provider.getCloudProvider().equals(OpenStackProvider.HP)
                                && provider.getContext().getAccountNumber().equals("66565797737008")) {
                            HashMap<String, String> endpoints = new HashMap<String, String>();

                            endpoints.put("region-a.geo-1",
                                    "https://region-a.geo-1.dbaas-mysql.hpcloudsvc.com:8779/v1.0/66565797737008");
                            services.put(HPRDBMS.SERVICE, endpoints);
                        }
                        return new AuthenticationContext(myRegionId, id, tenantId, services, null);
                    }
                }
            }
            throw new CloudException("No authentication tokens were provided");
        } finally {
            if (client != null) {
                client.getConnectionManager().shutdown();
            }
            if (std.isTraceEnabled()) {
                std.trace("exit - " + AbstractMethod.class.getName() + ".authenticateKeystone()");
            }
            if (wire.isDebugEnabled()) {
                wire.debug("");
                wire.debug("KEYSTONE --------------------------------------------------------> " + endpoint);
            }
        }
    }

    private @Nullable AuthenticationContext authenticateStandard(@Nonnull String endpointUrls)
            throws CloudException, InternalException {
        Logger std = NovaOpenStack.getLogger(NovaOpenStack.class, "std");
        Logger wire = NovaOpenStack.getLogger(NovaOpenStack.class, "wire");

        if (std.isTraceEnabled()) {
            std.trace("enter - " + AbstractMethod.class.getName() + ".authenticateStandard(" + endpointUrls + ")");
        }
        try {
            String accessPublic = null;
            String accessPrivate = null;
            String account = provider.getContext().getAccountNumber();
            try {
                List<ContextRequirements.Field> fields = provider.getContextRequirements().getConfigurableValues();
                for (ContextRequirements.Field f : fields) {
                    if (f.type.equals(ContextRequirements.FieldType.KEYPAIR)) {
                        byte[][] keyPair = (byte[][]) provider.getContext().getConfigurationValue(f);
                        accessPublic = new String(keyPair[0], "utf-8");
                        accessPrivate = new String(keyPair[1], "utf-8");
                    }
                }
            } catch (UnsupportedEncodingException e) {
                std.error("authenticateKeystone(): Unable to read access credentials: " + e.getMessage());
                e.printStackTrace();
                throw new InternalException(e);
            }
            String[] endpoints;

            if (endpointUrls.indexOf(',') > 0) {
                endpoints = endpointUrls.split(",");
            } else {
                endpoints = new String[] { endpointUrls };
            }
            HashMap<String, Map<String, String>> services = new HashMap<String, Map<String, String>>();
            String authToken = null, myRegion = provider.getContext().getRegionId();
            String tenantId = account;

            for (String endpoint : endpoints) {
                if (wire.isDebugEnabled()) {
                    wire.debug("STANDARD --------------------------------------------------------> " + endpoint);
                    wire.debug("");
                }

                HttpClient client = null;

                try {
                    ProviderContext ctx = provider.getContext();
                    client = getClient();
                    HttpGet get = new HttpGet(endpoint);

                    get.addHeader("Content-Type", "application/json");
                    get.addHeader("X-Auth-User", accessPublic);
                    get.addHeader("X-Auth-Key", accessPrivate);
                    get.addHeader("X-Auth-Project-Id", account);
                    if (wire.isDebugEnabled()) {
                        wire.debug(get.getRequestLine().toString());
                        for (Header header : get.getAllHeaders()) {
                            wire.debug(header.getName() + ": " + header.getValue());
                        }
                        wire.debug("");
                    }
                    HttpResponse response;

                    try {
                        APITrace.trace(provider, "GET authenticateStandard");
                        response = client.execute(get);
                        if (wire.isDebugEnabled()) {
                            wire.debug(response.getStatusLine().toString());
                            for (Header header : response.getAllHeaders()) {
                                wire.debug(header.getName() + ": " + header.getValue());
                            }
                            wire.debug("");
                        }
                    } catch (IOException e) {
                        std.error("I/O error from server communications: " + e.getMessage());
                        e.printStackTrace();
                        throw new InternalException(e);
                    }
                    int code = response.getStatusLine().getStatusCode();

                    std.debug("HTTP STATUS: " + code);
                    if (code != HttpStatus.SC_NO_CONTENT) {
                        if (code == HttpStatus.SC_FORBIDDEN || code == HttpStatus.SC_UNAUTHORIZED) {
                            return null;
                        }
                        std.error("authenticateStandard(): Expected NO CONTENT for an authentication request, got "
                                + code);
                        String data = null;

                        try {
                            HttpEntity entity = response.getEntity();

                            if (entity != null) {
                                data = EntityUtils.toString(entity);
                                if (wire.isDebugEnabled()) {
                                    wire.debug(data);
                                    wire.debug("");
                                }
                            }
                        } catch (IOException e) {
                            std.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                            e.printStackTrace();
                            throw new CloudException(e);
                        }
                        if (code == HttpStatus.SC_INTERNAL_SERVER_ERROR && data.contains("<faultstring>")) {
                            return null;
                        }
                        if (wire.isDebugEnabled()) {
                            wire.debug(response);
                        }
                        wire.debug("");
                        NovaException.ExceptionItems items = NovaException.parseException(code, data);

                        if (items.type.equals(CloudErrorType.AUTHENTICATION)) {
                            return null;
                        }
                        std.error(
                                "authenticateStandard(): [" + code + " : " + items.message + "] " + items.details);
                        throw new NovaException(items);
                    } else {
                        String cdnUrl = null, computeUrl = null, objectUrl = null;
                        String thisRegion = toRegion(endpoint);

                        if (myRegion == null) {
                            myRegion = thisRegion;
                        }
                        for (Header h : response.getAllHeaders()) {
                            if (h.getName().equalsIgnoreCase("x-auth-token") && myRegion.equals(thisRegion)) {
                                authToken = h.getValue().trim();
                            } else if (h.getName().equalsIgnoreCase("x-server-management-url")) {
                                String url = h.getValue().trim();

                                if (url.endsWith("/")) {
                                    url = url.substring(0, url.length() - 1);
                                }
                                if (endpoint.endsWith("v1.0")) {
                                    url = url + "/v1.0";
                                }
                                computeUrl = url;
                            } else if (h.getName().equalsIgnoreCase("x-storage-url")) {
                                objectUrl = h.getValue().trim();
                            } else if (h.getName().equalsIgnoreCase("x-cdn-management-url")) {
                                cdnUrl = h.getValue().trim();
                            }
                        }
                        if (computeUrl != null) {
                            Map<String, String> map = services.get("compute");

                            if (map == null) {
                                map = new HashMap<String, String>();
                                map.put(thisRegion, computeUrl);
                            }
                            services.put("compute", map);
                        }
                        if (objectUrl != null) {
                            Map<String, String> map = services.get("object-store");

                            if (map == null) {
                                map = new HashMap<String, String>();
                                map.put(thisRegion, objectUrl);
                            }
                            services.put("object-store", map);
                        }
                        if (cdnUrl != null) {
                            Map<String, String> map = services.get("cdn");

                            if (map == null) {
                                map = new HashMap<String, String>();
                                map.put(thisRegion, cdnUrl);

                            }
                            services.put("cdn", map);
                        }
                    }
                } finally {
                    if (client != null) {
                        client.getConnectionManager().shutdown();
                    }
                    if (wire.isDebugEnabled()) {
                        wire.debug("");
                        wire.debug(
                                "STANDARD --------------------------------------------------------> " + endpoint);
                    }
                }
            }
            if (authToken == null) {
                std.warn("authenticateStandard(): No authentication token in response");
                throw new CloudException("No authentication token in cloud response");
            }
            return new AuthenticationContext(myRegion, authToken, tenantId, services, null);
        } finally {
            if (std.isTraceEnabled()) {
                std.trace("exit - " + AbstractMethod.class.getName() + ".authenticateStandard()");
            }
        }
    }

    private @Nullable AuthenticationContext authenticateSwift(@Nonnull String endpoint)
            throws CloudException, InternalException {
        Logger std = NovaOpenStack.getLogger(NovaOpenStack.class, "std");
        Logger wire = NovaOpenStack.getLogger(NovaOpenStack.class, "wire");

        if (std.isTraceEnabled()) {
            std.trace("enter - " + AbstractMethod.class.getName() + ".authenticate()");
        }
        String accessPublic = null;
        String accessPrivate = null;
        String accountNum = provider.getContext().getAccountNumber();
        try {
            List<ContextRequirements.Field> fields = provider.getContextRequirements().getConfigurableValues();
            for (ContextRequirements.Field f : fields) {
                if (f.type.equals(ContextRequirements.FieldType.KEYPAIR)) {
                    byte[][] keyPair = (byte[][]) provider.getContext().getConfigurationValue(f);
                    accessPublic = new String(keyPair[0], "utf-8");
                    accessPrivate = new String(keyPair[1], "utf-8");
                }
            }
        } catch (UnsupportedEncodingException e) {
            std.error("authenticateKeystone(): Unable to read access credentials: " + e.getMessage());
            e.printStackTrace();
            throw new InternalException(e);
        }
        String tenantId = accountNum;
        String authToken = null, storageToken = null;
        String thisRegion = toRegion(endpoint);

        if (wire.isDebugEnabled()) {
            wire.debug("--------------------------------------------------------> " + endpoint);
            wire.debug("");
        }

        HttpClient client = null;
        try {
            client = getClient();
            HttpGet get = new HttpGet(endpoint);

            ProviderContext ctx = provider.getContext();
            String account;

            if (accessPublic.length() < 1) {
                account = accountNum;
            } else {
                String pk = accessPublic;

                if (pk.equals("-----")) {
                    account = accountNum;
                } else {
                    account = accountNum + ":" + accessPublic;
                }
            }
            get.addHeader("Content-Type", "application/json");
            get.addHeader("X-Auth-User", account);
            get.addHeader("X-Auth-Key", accessPrivate);
            if (wire.isDebugEnabled()) {
                wire.debug(get.getRequestLine().toString());
                for (Header header : get.getAllHeaders()) {
                    wire.debug(header.getName() + ": " + header.getValue());
                }
                wire.debug("");
            }
            HttpResponse response;

            try {
                APITrace.trace(provider, "GET authenticateSwift");
                response = client.execute(get);
                if (wire.isDebugEnabled()) {
                    wire.debug(response.getStatusLine().toString());
                    for (Header header : response.getAllHeaders()) {
                        wire.debug(header.getName() + ": " + header.getValue());
                    }
                    wire.debug("");
                }
            } catch (IOException e) {
                std.error("I/O error from server communications: " + e.getMessage());
                e.printStackTrace();
                throw new InternalException(e);
            }
            int code = response.getStatusLine().getStatusCode();

            std.debug("HTTP STATUS: " + code);
            if (code != HttpStatus.SC_NO_CONTENT && code != HttpStatus.SC_OK) {
                if (code == HttpStatus.SC_FORBIDDEN || code == HttpStatus.SC_UNAUTHORIZED) {
                    return null;
                }
                std.error("authenticate(): Expected NO CONTENT for an authentication request, got " + code);
                String data = null;

                try {
                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                        data = EntityUtils.toString(entity);
                        if (wire.isDebugEnabled()) {
                            wire.debug(data);
                            wire.debug("");
                        }
                    }
                } catch (IOException e) {
                    std.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                    e.printStackTrace();
                    throw new CloudException(e);
                }
                NovaException.ExceptionItems items = NovaException.parseException(code, data);

                if (items.type.equals(CloudErrorType.AUTHENTICATION)) {
                    return null;
                }
                std.error("authenticate(): [" + code + " : " + items.message + "] " + items.details);
                throw new NovaException(items);
            } else {
                HashMap<String, Map<String, String>> services = new HashMap<String, Map<String, String>>();

                for (Header h : response.getAllHeaders()) {
                    if (h.getName().equalsIgnoreCase("x-auth-token")) {
                        authToken = h.getValue().trim();
                    } else if (h.getName().equalsIgnoreCase("x-server-management-url")) {
                        Map<String, String> map = services.get("compute");

                        if (map == null) {
                            map = new HashMap<String, String>();
                            map.put(thisRegion, h.getValue().trim());
                        }
                        services.put("compute", map);
                    } else if (h.getName().equalsIgnoreCase("x-storage-url")) {
                        Map<String, String> map = services.get("object-store");

                        if (map == null) {
                            map = new HashMap<String, String>();
                            map.put(thisRegion, h.getValue().trim());
                        }
                        services.put("object-store", map);
                    } else if (h.getName().equalsIgnoreCase("x-cdn-management-url")) {
                        Map<String, String> map = services.get("cdn");

                        if (map == null) {
                            map = new HashMap<String, String>();
                            map.put(thisRegion, h.getValue().trim());
                        }
                        services.put("cdn", map);
                    } else if (h.getName().equalsIgnoreCase("x-storage-token")) {
                        storageToken = h.getValue().trim();
                    }
                }
                if (authToken == null) {
                    std.warn("authenticate(): No authentication token in response");
                    throw new CloudException("No authentication token in cloud response");
                }
                return new AuthenticationContext(thisRegion, authToken, tenantId, services, storageToken);
            }
        } finally {
            if (client != null) {
                client.getConnectionManager().shutdown();
            }
            if (std.isTraceEnabled()) {
                std.trace("exit - " + AbstractMethod.class.getName() + ".authenticate()");
            }
            if (wire.isDebugEnabled()) {
                wire.debug("");
                wire.debug("--------------------------------------------------------> " + endpoint);
            }
        }
    }

    @Override
    public void deleteResource(@Nonnull final String service, @Nonnull final String resource,
            @Nonnull final String resourceId, @Nullable final String suffix)
            throws CloudException, InternalException {
        AuthenticationContext context = provider.getAuthenticationContext();
        String endpoint = context.getServiceUrl(service);

        if (endpoint == null) {
            throw new CloudException("No " + service + " endpoint exists");
        }
        String resourceUri = resource + "/" + resourceId;
        if (suffix != null) {
            resourceUri = resource + "/" + resourceId + "/" + suffix;
        }
        try {
            delete(context.getAuthToken(), endpoint, resourceUri);
        } catch (NovaException ex) {
            if (ex.getHttpCode() == HttpStatus.SC_UNAUTHORIZED) {
                Cache<AuthenticationContext> cache = Cache.getInstance(provider, "authenticationContext",
                        AuthenticationContext.class, CacheLevel.REGION_ACCOUNT,
                        new TimePeriod<Day>(1, TimePeriod.DAY));
                cache.clear();
                deleteResource(service, resource, resourceId, suffix);
            } else {
                throw ex;
            }
        }
    }

    @Override
    protected void delete(@Nonnull final String authToken, @Nonnull final String endpoint,
            @Nonnull final String resource) throws CloudException, InternalException {
        Logger std = NovaOpenStack.getLogger(NovaOpenStack.class, "std");
        Logger wire = NovaOpenStack.getLogger(NovaOpenStack.class, "wire");

        if (std.isTraceEnabled()) {
            std.trace("enter - " + AbstractMethod.class.getName() + ".delete(" + authToken + "," + endpoint + ","
                    + resource + ")");
        }
        if (wire.isDebugEnabled()) {
            wire.debug("--------------------------------------------------------> " + endpoint + resource);
            wire.debug("");
        }
        HttpClient client = null;
        try {
            client = getClient();
            HttpDelete delete = new HttpDelete(endpoint + resource);

            delete.addHeader("Content-Type", "application/json");
            delete.addHeader("X-Auth-Token", authToken);
            if (wire.isDebugEnabled()) {
                wire.debug(delete.getRequestLine().toString());
                for (Header header : delete.getAllHeaders()) {
                    wire.debug(header.getName() + ": " + header.getValue());
                }
                wire.debug("");
            }
            HttpResponse response;

            try {
                APITrace.trace(provider, "DELETE " + toAPIResource(resource));
                response = client.execute(delete);
                if (wire.isDebugEnabled()) {
                    wire.debug(response.getStatusLine().toString());
                    for (Header header : response.getAllHeaders()) {
                        wire.debug(header.getName() + ": " + header.getValue());
                    }
                    wire.debug("");
                }
            } catch (IOException e) {
                std.error("I/O error from server communications: " + e.getMessage());
                e.printStackTrace();
                throw new InternalException(e);
            }
            int code = response.getStatusLine().getStatusCode();

            std.debug("HTTP STATUS: " + code);
            if (code != HttpStatus.SC_NO_CONTENT && code != HttpStatus.SC_ACCEPTED && code != HttpStatus.SC_OK) {
                std.error("delete(): Expected NO CONTENT for DELETE request, got " + code);
                String data = null;

                try {
                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                        data = EntityUtils.toString(entity);
                        if (wire.isDebugEnabled()) {
                            wire.debug(data);
                            wire.debug("");
                        }
                    }
                } catch (IOException e) {
                    std.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                    e.printStackTrace();
                    throw new CloudException(e);
                }
                NovaException.ExceptionItems items = NovaException.parseException(code, data);

                if (items == null) {
                    items = new NovaException.ExceptionItems();
                    items.code = 404;
                    items.type = CloudErrorType.COMMUNICATION;
                    items.message = "itemNotFound";
                    items.details = "No such object: " + resource;
                }
                std.error("delete(): [" + code + " : " + items.message + "] " + items.details);
                throw new NovaException(items);
            } else {
                wire.debug("");
            }
        } finally {
            if (client != null) {
                client.getConnectionManager().shutdown();
            }
            if (std.isTraceEnabled()) {
                std.trace("exit - " + AbstractMethod.class.getName() + ".delete()");
            }
            if (wire.isDebugEnabled()) {
                wire.debug("");
                wire.debug("--------------------------------------------------------> " + endpoint + resource);
            }
        }
    }

    @Override
    public @Nullable String[] getItemList(@Nonnull final String service, @Nonnull final String resource,
            final boolean suffix) throws CloudException, InternalException {
        AuthenticationContext context = provider.getAuthenticationContext();
        String endpoint = context.getServiceUrl(service);

        if (endpoint == null) {
            throw new CloudException("No " + service + " URL has been established in " + context.getMyRegion());
        }
        String resourceUri = resource;
        if (suffix) {
            resourceUri += "/detail";
        }
        try {
            String response = getString(context.getAuthToken(), endpoint, resourceUri);

            if (response == null) {
                return null;
            }
            if (response.length() < 1) {
                return new String[0];
            }
            String[] items = response.split("\n");

            if (items == null || items.length < 1) {
                return new String[] { response.trim() };
            }
            for (int i = 0; i < items.length; i++) {
                items[i] = items[i].trim();
            }
            return items;
        } catch (NovaException ex) {
            if (ex.getHttpCode() == HttpStatus.SC_UNAUTHORIZED) {
                Cache<AuthenticationContext> cache = Cache.getInstance(provider, "authenticationContext",
                        AuthenticationContext.class, CacheLevel.REGION_ACCOUNT,
                        new TimePeriod<Day>(1, TimePeriod.DAY));
                cache.clear();
                return getItemList(service, resource, suffix);
            } else {
                throw ex;
            }
        }
    }

    @Override
    public @Nullable JSONObject getResource(@Nonnull final String service, @Nonnull final String resource,
            @Nullable final String resourceId, final boolean suffix) throws CloudException, InternalException {
        AuthenticationContext context = provider.getAuthenticationContext();
        String endpoint = context.getServiceUrl(service);

        if (endpoint == null) {
            throw new CloudException("No " + service + " URL has been established in " + context.getMyRegion());
        }
        String resourceUri = resource;
        if (resourceId != null) {
            if (resourceId.startsWith("?")) {
                resourceUri += resourceId;
            } else {
                resourceUri += "/" + resourceId;
            }
        } else if (suffix) {
            resourceUri += "/detail";
        }
        try {
            String response = getString(context.getAuthToken(), endpoint, resourceUri);

            if (response == null) {
                return null;
            }
            try {
                return new JSONObject(response);
            } catch (JSONException e) {
                throw new CloudException(CloudErrorType.COMMUNICATION, 200, "invalidJson", response);
            }
        } catch (NovaException ex) {
            if (ex.getHttpCode() == HttpStatus.SC_UNAUTHORIZED) {
                Cache<AuthenticationContext> cache = Cache.getInstance(provider, "authenticationContext",
                        AuthenticationContext.class, CacheLevel.REGION_ACCOUNT,
                        new TimePeriod<Day>(1, TimePeriod.DAY));
                cache.clear();
                return getResource(service, resource, resourceId, suffix);
            } else {
                throw ex;
            }
        }
    }

    @Override
    protected @Nullable String getString(@Nonnull String authToken, @Nonnull String endpoint,
            @Nonnull String resource) throws CloudException, InternalException {
        Logger std = NovaOpenStack.getLogger(NovaOpenStack.class, "std");
        Logger wire = NovaOpenStack.getLogger(NovaOpenStack.class, "wire");

        if (std.isTraceEnabled()) {
            std.trace("enter - " + AbstractMethod.class.getName() + ".getString(" + authToken + "," + endpoint + ","
                    + resource + ")");
        }
        if (wire.isDebugEnabled()) {
            wire.debug("--------------------------------------------------------> " + endpoint + resource);
            wire.debug("");
        }
        HttpClient client = null;
        try {
            client = getClient();
            HttpGet get = new HttpGet(resource == null ? endpoint : endpoint + resource);

            get.addHeader("Content-Type", "application/json");
            get.addHeader("X-Auth-Token", authToken);

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

            try {
                APITrace.trace(provider, "GET " + toAPIResource(resource));
                response = client.execute(get);
                if (wire.isDebugEnabled()) {
                    wire.debug(response.getStatusLine().toString());
                    for (Header header : response.getAllHeaders()) {
                        wire.debug(header.getName() + ": " + header.getValue());
                    }
                    wire.debug("");
                }
            } catch (IOException e) {
                std.error("I/O error from server communications: " + e.getMessage());
                e.printStackTrace();
                throw new InternalException(e);
            }
            int code = response.getStatusLine().getStatusCode();

            std.debug("HTTP STATUS: " + code);

            if (code == HttpStatus.SC_NOT_FOUND) {
                return null;
            }
            if (code == HttpStatus.SC_BAD_REQUEST) {
                std.error("Expected OK for GET request, got " + code);
                String data = null;

                try {
                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                        data = EntityUtils.toString(entity);
                        if (wire.isDebugEnabled()) {
                            wire.debug(data);
                            wire.debug("");
                        }
                    }
                } catch (IOException e) {
                    std.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                    e.printStackTrace();
                    throw new CloudException(e);
                }
                try {
                    JSONObject err = (new JSONObject(data)).getJSONObject("badRequest");
                    String msg = err.getString("message");

                    if (msg.contains("id should be integer")) {
                        return null;
                    }
                } catch (JSONException e) {
                    // ignore
                }
                NovaException.ExceptionItems items = NovaException.parseException(code, data);

                if (items == null) {
                    return null;
                }
                if (provider.getMajorVersion() == 1 && provider.getMinorVersion() == 0 && items.message != null
                        && (items.message.contains("not found") || items.message.contains("unknown"))) {
                    return null;
                }
                std.error("getString(): [" + code + " : " + items.message + "] " + items.details);
                throw new NovaException(items);
            }
            if (code != HttpStatus.SC_NO_CONTENT && code != HttpStatus.SC_OK
                    && code != HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION) {
                std.error("Expected OK for GET request, got " + code);
                String data = null;

                try {
                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                        data = EntityUtils.toString(entity);
                        if (wire.isDebugEnabled()) {
                            wire.debug(data);
                            wire.debug("");
                        }
                    }
                } catch (IOException e) {
                    std.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                    e.printStackTrace();
                    throw new CloudException(e);
                }
                NovaException.ExceptionItems items = NovaException.parseException(code, data);

                if (items == null) {
                    return null;
                }
                if (items.code == HttpStatus.SC_UNAUTHORIZED) {
                    std.error("getString(): [" + code + " : " + items.message + "] " + items.details);
                    throw new NovaException(items);
                }
                if (provider.getMajorVersion() == 1 && provider.getMinorVersion() == 0 && items.message != null
                        && (items.message.contains("not found") || items.message.contains("unknown"))) {
                    return null;
                }
                std.error("getString(): [" + code + " : " + items.message + "] " + items.details);
                throw new NovaException(items);
            } else {
                String data = null;

                try {
                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                        data = EntityUtils.toString(entity);
                        if (wire.isDebugEnabled()) {
                            wire.debug(data);
                            wire.debug("");
                        }
                    }
                } catch (IOException e) {
                    std.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                    e.printStackTrace();
                    throw new CloudException(e);
                }
                return data;
            }
        } finally {
            if (client != null) {
                client.getConnectionManager().shutdown();
            }
            if (std.isTraceEnabled()) {
                std.trace("exit - " + AbstractMethod.class.getName() + ".getString()");
            }
            if (wire.isDebugEnabled()) {
                wire.debug("");
                wire.debug("--------------------------------------------------------> " + endpoint + resource);
            }
        }
    }

    @Override
    protected @Nullable InputStream getStream(@Nonnull String authToken, @Nonnull String endpoint,
            @Nonnull String resource) throws CloudException, InternalException {
        Logger std = NovaOpenStack.getLogger(NovaOpenStack.class, "std");
        Logger wire = NovaOpenStack.getLogger(NovaOpenStack.class, "wire");

        if (std.isTraceEnabled()) {
            std.trace("enter - " + AbstractMethod.class.getName() + ".getStream(" + authToken + "," + endpoint + ","
                    + resource + ")");
        }
        if (wire.isDebugEnabled()) {
            wire.debug("--------------------------------------------------------> " + endpoint + resource);
            wire.debug("");
        }
        try {
            HttpClient client = getClient();
            HttpGet get = new HttpGet(endpoint + resource);

            get.addHeader("Content-Type", "application/json");
            get.addHeader("X-Auth-Token", authToken);

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

            try {
                APITrace.trace(provider, "GET " + toAPIResource(resource));
                response = client.execute(get);
                if (wire.isDebugEnabled()) {
                    wire.debug(response.getStatusLine().toString());
                    for (Header header : response.getAllHeaders()) {
                        wire.debug(header.getName() + ": " + header.getValue());
                    }
                    wire.debug("");
                }
            } catch (IOException e) {
                std.error("I/O error from server communications: " + e.getMessage());
                e.printStackTrace();
                throw new InternalException(e);
            }
            int code = response.getStatusLine().getStatusCode();

            std.debug("HTTP STATUS: " + code);
            if (code == HttpStatus.SC_NOT_FOUND) {
                return null;
            }
            if (code != HttpStatus.SC_OK && code != HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION) {
                std.error("Expected OK for GET request, got " + code);
                String data = null;

                try {
                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                        data = EntityUtils.toString(entity);
                        if (wire.isDebugEnabled()) {
                            wire.debug(data);
                            wire.debug("");
                        }
                    }
                } catch (IOException e) {
                    std.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                    e.printStackTrace();
                    throw new CloudException(e);
                }
                NovaException.ExceptionItems items = NovaException.parseException(code, data);

                if (items == null) {
                    return null;
                }
                std.error("getStream(): [" + code + " : " + items.message + "] " + items.details);
                throw new NovaException(items);
            } else {
                InputStream input = null;

                try {
                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                        input = entity.getContent();
                        if (wire.isDebugEnabled()) {
                            wire.debug(" ---- BINARY DATA ---- ");
                            wire.debug("");
                        }
                    }
                } catch (IOException e) {
                    std.error("getStream(): Failed to read response error due to a cloud I/O error: "
                            + e.getMessage());
                    if (std.isTraceEnabled()) {
                        e.printStackTrace();
                    }
                    throw new CloudException(e);
                }
                if (wire.isDebugEnabled()) {
                    wire.debug("---> Binary Data <---");
                }
                wire.debug("");
                return input;
            }
        } finally {
            if (std.isTraceEnabled()) {
                std.trace("exit - " + AbstractMethod.class.getName() + ".getStream()");
            }
            if (wire.isDebugEnabled()) {
                wire.debug("");
                wire.debug("--------------------------------------------------------> " + endpoint + resource);
            }
        }
    }

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

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

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

        HttpParams params = new BasicHttpParams();

        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        // noinspection deprecation
        HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
        HttpProtocolParams.setUserAgent(params, "");

        Properties p = ctx.getCustomProperties();

        if (p != null) {
            String proxyHost = p.getProperty("proxyHost");
            String proxyPort = p.getProperty("proxyPort");

            if (proxyHost != null) {
                int port = 0;

                if (proxyPort != null && proxyPort.length() > 0) {
                    port = Integer.parseInt(proxyPort);
                }
                params.setParameter(ConnRoutePNames.DEFAULT_PROXY,
                        new HttpHost(proxyHost, port, ssl ? "https" : "http"));
            }
        }
        DefaultHttpClient client = new DefaultHttpClient(params);

        if (provider.isInsecure()) {
            try {
                client.getConnectionManager().getSchemeRegistry()
                        .register(new Scheme("https", 443, new SSLSocketFactory(new TrustStrategy() {

                            @Override
                            public boolean isTrusted(X509Certificate[] x509Certificates, String s)
                                    throws CertificateException {
                                return true;
                            }
                        }, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)));
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }
        return client;
    }

    @Override
    public @Nullable Map<String, String> headResource(@Nonnull final String service,
            @Nullable final String resource, @Nullable final String resourceId)
            throws CloudException, InternalException {
        AuthenticationContext context = provider.getAuthenticationContext();
        String endpoint = context.getServiceUrl(service);

        if (endpoint == null) {
            throw new CloudException("No " + service + " URL has been established in " + context.getMyRegion());
        }
        String resourceUri = resource;
        if (resource == null && resourceId == null) {
            resourceUri = "/";
        } else if (resource == null) {
            resourceUri = "/" + resourceId;
        } else if (resourceId != null) {
            resourceUri += "/" + resourceId;
        }
        try {
            return head(context.getAuthToken(), endpoint, resourceUri);
        } catch (NovaException ex) {
            if (ex.getHttpCode() == HttpStatus.SC_UNAUTHORIZED) {
                Cache<AuthenticationContext> cache = Cache.getInstance(provider, "authenticationContext",
                        AuthenticationContext.class, CacheLevel.REGION_ACCOUNT,
                        new TimePeriod<Day>(1, TimePeriod.DAY));
                cache.clear();
                return headResource(service, resource, resourceId);
            } else {
                throw ex;
            }
        }
    }

    @Override
    protected @Nullable Map<String, String> head(@Nonnull String authToken, @Nonnull String endpoint,
            @Nonnull String resource) throws CloudException, InternalException {
        Logger std = NovaOpenStack.getLogger(NovaOpenStack.class, "std");
        Logger wire = NovaOpenStack.getLogger(NovaOpenStack.class, "wire");

        if (std.isTraceEnabled()) {
            std.trace("enter - " + AbstractMethod.class.getName() + ".head(" + authToken + "," + endpoint + ","
                    + resource + ")");
        }
        if (wire.isDebugEnabled()) {
            wire.debug("--------------------------------------------------------> " + endpoint + resource);
            wire.debug("");
        }
        HttpClient client = null;
        try {
            client = getClient();
            HttpHead head = new HttpHead(endpoint + resource);

            head.addHeader("X-Auth-Token", authToken);
            if (wire.isDebugEnabled()) {
                wire.debug(head.getRequestLine().toString());
                for (Header header : head.getAllHeaders()) {
                    wire.debug(header.getName() + ": " + header.getValue());
                }
                wire.debug("");
            }
            HttpResponse response;

            try {
                APITrace.trace(provider, "HEAD " + toAPIResource(resource));
                response = client.execute(head);
                if (wire.isDebugEnabled()) {
                    wire.debug(response.getStatusLine().toString());
                    for (Header header : response.getAllHeaders()) {
                        wire.debug(header.getName() + ": " + header.getValue());
                    }
                    wire.debug("");
                }
            } catch (IOException e) {
                std.error("I/O error from server communications: " + e.getMessage());
                e.printStackTrace();
                throw new InternalException(e);
            }
            int code = response.getStatusLine().getStatusCode();

            std.debug("HTTP STATUS: " + code);
            if (code != HttpStatus.SC_NO_CONTENT && code != HttpStatus.SC_OK) {
                if (code == HttpStatus.SC_NOT_FOUND) {
                    return null;
                }
                std.error("Expected OK for HEAD request, got " + code);
                String data = null;

                try {
                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                        data = EntityUtils.toString(entity);
                        if (wire.isDebugEnabled()) {
                            wire.debug(data);
                            wire.debug("");
                        }
                    }
                } catch (IOException e) {
                    std.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                    e.printStackTrace();
                    throw new CloudException(e);
                }
                NovaException.ExceptionItems items = NovaException.parseException(code, data);

                if (items == null) {
                    return null;
                }
                std.error("head(): [" + code + " : " + items.message + "] " + items.details);
                throw new NovaException(items);
            }
            HashMap<String, String> map = new HashMap<String, String>();

            for (Header h : response.getAllHeaders()) {
                map.put(h.getName().trim(), h.getValue().trim());
            }
            return map;
        } finally {
            if (client != null) {
                client.getConnectionManager().shutdown();
            }
            if (std.isTraceEnabled()) {
                std.trace("exit - " + AbstractMethod.class.getName() + ".head()");
            }
            if (wire.isDebugEnabled()) {
                wire.debug("");
                wire.debug("--------------------------------------------------------> " + endpoint + resource);
            }
        }
    }

    @Override
    public void postResourceHeaders(final String service, final String resource, final String resourceId,
            final Map<String, String> headers) throws CloudException, InternalException {
        AuthenticationContext context = provider.getAuthenticationContext();
        String endpoint = context.getServiceUrl(service);

        if (endpoint == null) {
            throw new CloudException("No " + service + " has been established in " + context.getMyRegion());
        }
        if (resourceId == null) {
            throw new InternalException("No container was specified");
        }
        try {
            postHeaders(context.getAuthToken(), endpoint, resource + "/" + resourceId, headers);
        } catch (NovaException ex) {
            if (ex.getHttpCode() == HttpStatus.SC_UNAUTHORIZED) {
                Cache<AuthenticationContext> cache = Cache.getInstance(provider, "authenticationContext",
                        AuthenticationContext.class, CacheLevel.REGION_ACCOUNT,
                        new TimePeriod<Day>(1, TimePeriod.DAY));
                cache.clear();
                postResourceHeaders(service, resource, resourceId, headers);
            } else {
                throw ex;
            }
        }
    }

    @Override
    @SuppressWarnings("unused")
    protected @Nullable String postHeaders(@Nonnull String authToken, @Nonnull String endpoint,
            @Nonnull String resource, @Nonnull Map<String, String> customHeaders)
            throws CloudException, InternalException {
        Logger std = NovaOpenStack.getLogger(NovaOpenStack.class, "std");
        Logger wire = NovaOpenStack.getLogger(NovaOpenStack.class, "wire");

        if (std.isTraceEnabled()) {
            std.trace("enter - " + AbstractMethod.class.getName() + ".postString(" + authToken + "," + endpoint
                    + "," + resource + "," + customHeaders + ")");
        }
        if (wire.isDebugEnabled()) {
            wire.debug("---------------------------------------------------------------------------------"
                    + endpoint + resource);
            wire.debug("");
        }
        HttpClient client = null;
        try {
            client = getClient();
            HttpPost post = new HttpPost(endpoint + resource);

            post.addHeader("Content-Type", "application/json");
            post.addHeader("X-Auth-Token", authToken);
            if (customHeaders != null) {
                for (Map.Entry<String, String> entry : customHeaders.entrySet()) {
                    String val = (entry.getValue() == null ? "" : entry.getValue());

                    post.addHeader(entry.getKey(), val);
                }
            }
            if (wire.isDebugEnabled()) {
                wire.debug(post.getRequestLine().toString());
                for (Header header : post.getAllHeaders()) {
                    wire.debug(header.getName() + ": " + header.getValue());
                }
                wire.debug("");
            }

            HttpResponse response;

            try {
                APITrace.trace(provider, "POST " + toAPIResource(resource));
                response = client.execute(post);
                if (wire.isDebugEnabled()) {
                    wire.debug(response.getStatusLine().toString());
                    for (Header header : response.getAllHeaders()) {
                        wire.debug(header.getName() + ": " + header.getValue());
                    }
                    wire.debug("");
                }
            } catch (IOException e) {
                std.error("I/O error from server communications: " + e.getMessage());
                e.printStackTrace();
                throw new InternalException(e);
            }
            int code = response.getStatusLine().getStatusCode();

            std.debug("HTTP STATUS: " + code);

            if (code == HttpStatus.SC_REQUEST_TOO_LONG || code == HttpStatus.SC_REQUEST_URI_TOO_LONG) {
                String data = null;

                try {
                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                        data = EntityUtils.toString(entity);
                        if (wire.isDebugEnabled()) {
                            wire.debug(data);
                            wire.debug("");
                        }
                    }
                } catch (IOException e) {
                    std.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                    e.printStackTrace();
                    throw new CloudException(e);
                }
                try {
                    if (data != null) {
                        JSONObject ob = new JSONObject(data);

                        if (ob.has("overLimit")) {
                            ob = ob.getJSONObject("overLimit");
                            if (ob.has("retryAfter")) {
                                int min = ob.getInt("retryAfter");

                                if (min < 1) {
                                    throw new CloudException(CloudErrorType.CAPACITY, 413, "Over Limit",
                                            ob.has("message") ? ob.getString("message") : "Over Limit");
                                }
                                try {
                                    Thread.sleep(CalendarWrapper.MINUTE * min);
                                } catch (InterruptedException ignore) {
                                }
                                return postHeaders(authToken, endpoint, resource, customHeaders);
                            }
                        }
                    }
                } catch (JSONException e) {
                    throw new CloudException(e);
                }
            }

            if (code != HttpStatus.SC_ACCEPTED && code != HttpStatus.SC_NO_CONTENT) {
                std.error("postString(): Expected ACCEPTED for POST request, got " + code);
                String data = null;

                try {
                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                        data = EntityUtils.toString(entity);
                        if (wire.isDebugEnabled()) {
                            wire.debug(data);
                            wire.debug("");
                        }
                    }
                } catch (IOException e) {
                    std.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                    e.printStackTrace();
                    throw new CloudException(e);
                }
                NovaException.ExceptionItems items = NovaException.parseException(code, data);

                if (items == null) {
                    items = new NovaException.ExceptionItems();
                    items.code = 404;
                    items.type = CloudErrorType.COMMUNICATION;
                    items.message = "itemNotFound";
                    items.details = "No such object: " + resource;
                }
                std.error("postString(): [" + code + " : " + items.message + "] " + items.details);
                throw new NovaException(items);
            } else {
                if (code == HttpStatus.SC_ACCEPTED) {
                    String data = null;

                    try {
                        HttpEntity entity = response.getEntity();

                        if (entity != null) {
                            data = EntityUtils.toString(entity);
                            if (wire.isDebugEnabled()) {
                                wire.debug(data);
                                wire.debug("");
                            }
                        }
                    } catch (IOException e) {
                        std.error("Failed to read response due to a cloud I/O error: " + e.getMessage());
                        e.printStackTrace();
                        throw new CloudException(e);
                    }
                    if (data != null && !data.trim().equals("")) {
                        return data;
                    }
                }
                return null;
            }
        } finally {
            if (client != null) {
                client.getConnectionManager().shutdown();
            }
            if (std.isTraceEnabled()) {
                std.trace("exit - " + AbstractMethod.class.getName() + ".postString()");
            }
            if (wire.isDebugEnabled()) {
                wire.debug("");
                wire.debug("---------------------------------------------------------------------------------"
                        + endpoint + resource);
            }
        }
    }

    @Override
    public @Nullable JSONObject postString(@Nonnull final String service, @Nonnull final String resource,
            @Nullable final String resourceId, @Nonnull final String extra, @Nonnull final JSONObject body)
            throws CloudException, InternalException {
        AuthenticationContext context = provider.getAuthenticationContext();
        String endpoint = context.getServiceUrl(service);

        if (endpoint == null) {
            throw new CloudException("No " + service + " endpoint exists");
        }
        try {
            String response = postString(context.getAuthToken(), endpoint,
                    resource + "/" + resourceId + "/" + extra, body.toString());

            if (response == null) {
                return null;
            }
            try {
                return new JSONObject(response);
            } catch (JSONException e) {
                throw new CloudException(CloudErrorType.COMMUNICATION, 200, "invalidJson", response);
            }
        } catch (NovaException ex) {
            if (ex.getHttpCode() == HttpStatus.SC_UNAUTHORIZED) {
                Cache<AuthenticationContext> cache = Cache.getInstance(provider, "authenticationContext",
                        AuthenticationContext.class, CacheLevel.REGION_ACCOUNT,
                        new TimePeriod<Day>(1, TimePeriod.DAY));
                cache.clear();
                return postString(service, resource, resourceId, extra, body);
            } else {
                throw ex;
            }
        }
    }

    @Override
    public @Nullable JSONObject postString(@Nonnull final String service, @Nonnull final String resource,
            @Nullable final String resourceId, @Nonnull final JSONObject body, final boolean suffix)
            throws CloudException, InternalException {
        AuthenticationContext context = provider.getAuthenticationContext();

        String resourceUri = resource;
        if (resourceId != null) {
            resourceUri += "/" + (suffix ? (resourceId + "/action") : resourceId);
        }
        String endpoint = context.getServiceUrl(service);

        if (endpoint == null) {
            throw new CloudException("No " + service + " endpoint exists");
        }
        try {
            String response = postString(context.getAuthToken(), endpoint, resourceUri, body.toString());
            if (response == null) {
                return null;
            }
            try {
                return new JSONObject(response);
            } catch (JSONException e) {
                throw new CloudException(CloudErrorType.COMMUNICATION, 200, "invalidJson", response);
            }
        } catch (NovaException ex) {
            if (ex.getHttpCode() == HttpStatus.SC_UNAUTHORIZED) {
                Cache<AuthenticationContext> cache = Cache.getInstance(provider, "authenticationContext",
                        AuthenticationContext.class, CacheLevel.REGION_ACCOUNT,
                        new TimePeriod<Day>(1, TimePeriod.DAY));
                cache.clear();
                return postString(service, resource, resourceId, body, suffix);
            } else {
                throw ex;
            }
        }
    }

    @Override
    protected @Nullable String postString(@Nonnull String authToken, @Nonnull String endpoint,
            @Nonnull String resource, @Nonnull String payload) throws CloudException, InternalException {
        Logger std = NovaOpenStack.getLogger(NovaOpenStack.class, "std");
        Logger wire = NovaOpenStack.getLogger(NovaOpenStack.class, "wire");

        if (std.isTraceEnabled()) {
            std.trace("enter - " + AbstractMethod.class.getName() + ".postString(" + authToken + "," + endpoint
                    + "," + resource + "," + payload + ")");
        }
        if (wire.isDebugEnabled()) {
            wire.debug("---------------------------------------------------------------------------------"
                    + endpoint + resource);
            wire.debug("");
        }
        HttpClient client = null;
        try {
            client = getClient();
            HttpPost post = new HttpPost(endpoint + resource);

            post.addHeader("Content-Type", "application/json");
            post.addHeader("X-Auth-Token", authToken);
            if (wire.isDebugEnabled()) {
                wire.debug(post.getRequestLine().toString());
                for (Header header : post.getAllHeaders()) {
                    wire.debug(header.getName() + ": " + header.getValue());
                }
                wire.debug("");
            }
            if (payload != null) {
                try {
                    // noinspection deprecation
                    post.setEntity(new StringEntity(payload == null ? "" : payload, "application/json", "UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    throw new InternalException(e);
                }
                try {
                    wire.debug(EntityUtils.toString(post.getEntity()));
                } catch (IOException ignore) {
                }

                wire.debug("");
            }
            HttpResponse response;

            try {
                std.debug("POST " + toAPIResource(resource));
                response = client.execute(post);
                if (wire.isDebugEnabled()) {
                    wire.debug(response.getStatusLine().toString());
                    for (Header header : response.getAllHeaders()) {
                        wire.debug(header.getName() + ": " + header.getValue());
                    }
                    wire.debug("");
                }
            } catch (IOException e) {
                std.error("I/O error from server communications: " + e.getMessage());
                e.printStackTrace();
                throw new InternalException("Communication error while reading from cloud endpoint", e);
            }
            int code = response.getStatusLine().getStatusCode();

            std.debug("HTTP STATUS: " + code);
            if (code == HttpStatus.SC_REQUEST_TOO_LONG || code == HttpStatus.SC_REQUEST_URI_TOO_LONG) {
                String data = null;

                try {
                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                        data = EntityUtils.toString(entity);
                        if (wire.isDebugEnabled()) {
                            wire.debug(data);
                            wire.debug("");
                        }
                    }
                } catch (IOException e) {
                    std.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                    e.printStackTrace();
                    throw new CloudException(e);
                }
                try {
                    if (data != null) {
                        JSONObject ob = new JSONObject(data);
                        if (ob.has("overLimit")) {
                            ob = ob.getJSONObject("overLimit");
                            int min = ob.optInt("retryAfter", 0);

                            if (min < 1) {
                                throw new CloudException(CloudErrorType.CAPACITY, 413, "Over Limit",
                                        ob.has("message") ? ob.getString("message") : "Over Limit");
                            }
                            try {
                                Thread.sleep(CalendarWrapper.MINUTE * min);
                            } catch (InterruptedException ignore) {
                            }
                            return postString(authToken, endpoint, resource, payload);
                        }
                    }
                } catch (JSONException e) {
                    throw new CloudException(e);
                }
            }
            if (code != HttpStatus.SC_OK && code != HttpStatus.SC_ACCEPTED && code != HttpStatus.SC_NO_CONTENT
                    && code != HttpStatus.SC_CREATED) {
                std.error("postString(): Expected OK, ACCEPTED, or NO CONTENT for POST request, got " + code);
                String data = null;

                try {
                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                        data = EntityUtils.toString(entity);
                        if (wire.isDebugEnabled()) {
                            wire.debug(data);
                            wire.debug("");
                        }
                    }
                } catch (IOException e) {
                    std.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                    e.printStackTrace();
                    throw new CloudException(e);
                }
                NovaException.ExceptionItems items = NovaException.parseException(code, data);

                if (items == null) {
                    items = new NovaException.ExceptionItems();
                    items.code = 404;
                    items.type = CloudErrorType.COMMUNICATION;
                    items.message = "itemNotFound";
                    items.details = "No such object: " + resource;
                }
                std.error("postString(): [" + code + " : " + items.message + "] " + items.details);
                throw new NovaException(items);
            } else {
                if (code != HttpStatus.SC_NO_CONTENT) {
                    String data = null;

                    try {
                        HttpEntity entity = response.getEntity();

                        if (entity != null) {
                            data = EntityUtils.toString(entity);
                            if (wire.isDebugEnabled()) {
                                wire.debug(data);
                                wire.debug("");
                            }
                        }
                    } catch (IOException e) {
                        std.error("Failed to read response due to a cloud I/O error: " + e.getMessage());
                        e.printStackTrace();
                        throw new CloudException(e);
                    }
                    if (data != null && !data.trim().equals("")) {
                        return data;
                    } else if (code == HttpStatus.SC_ACCEPTED) {
                        Header[] headers = response.getAllHeaders();

                        for (Header h : headers) {
                            if (h.getName().equalsIgnoreCase("Location")) {
                                return "{\"location\" : \"" + h.getValue().trim() + "\"}";
                            }
                        }
                    }
                }
                return null;
            }
        } finally {
            if (client != null) {
                client.getConnectionManager().shutdown();
            }
            if (std.isTraceEnabled()) {
                std.trace("exit - " + AbstractMethod.class.getName() + ".postString()");
            }
            if (wire.isDebugEnabled()) {
                wire.debug("");
                wire.debug("---------------------------------------------------------------------------------"
                        + endpoint + resource);
            }
        }
    }

    @Override
    @SuppressWarnings("unused")
    protected @Nullable String postStream(@Nonnull String authToken, @Nonnull String endpoint,
            @Nonnull String resource, @Nonnull String md5Hash, @Nonnull InputStream stream)
            throws CloudException, InternalException {
        Logger std = NovaOpenStack.getLogger(NovaOpenStack.class, "std");
        Logger wire = NovaOpenStack.getLogger(NovaOpenStack.class, "wire");

        if (std.isTraceEnabled()) {
            std.trace("enter - " + AbstractMethod.class.getName() + ".postStream(" + authToken + "," + endpoint
                    + "," + resource + "," + md5Hash + ",INPUTSTREAM)");
        }
        if (wire.isDebugEnabled()) {
            wire.debug("---------------------------------------------------------------------------------"
                    + endpoint + resource);
            wire.debug("");
        }
        HttpClient client = null;
        try {
            client = getClient();
            HttpPost post = new HttpPost(endpoint + resource);

            post.addHeader("Content-Type", "application/octet-stream");
            post.addHeader("X-Auth-Token", authToken);

            if (wire.isDebugEnabled()) {
                wire.debug(post.getRequestLine().toString());
                for (Header header : post.getAllHeaders()) {
                    wire.debug(header.getName() + ": " + header.getValue());
                }
                wire.debug("");
            }
            post.setEntity(new InputStreamEntity(stream, -1));
            wire.debug(" ---- BINARY DATA ---- ");
            wire.debug("");

            HttpResponse response;

            try {
                APITrace.trace(provider, "POST " + toAPIResource(resource));
                response = client.execute(post);
                if (wire.isDebugEnabled()) {
                    wire.debug(response.getStatusLine().toString());
                    for (Header header : response.getAllHeaders()) {
                        wire.debug(header.getName() + ": " + header.getValue());
                    }
                    wire.debug("");
                }
            } catch (IOException e) {
                std.error("I/O error from server communications: " + e.getMessage());
                e.printStackTrace();
                throw new InternalException(e);
            }
            int code = response.getStatusLine().getStatusCode();

            std.debug("HTTP STATUS: " + code);

            String responseHash = null;

            for (Header h : post.getAllHeaders()) {
                if (h.getName().equalsIgnoreCase("ETag")) {
                    responseHash = h.getValue();
                }
            }
            if (responseHash != null && md5Hash != null && !responseHash.equals(md5Hash)) {
                throw new CloudException("MD5 hash values do not match, probably data corruption");
            }
            if (code != HttpStatus.SC_ACCEPTED && code != HttpStatus.SC_NO_CONTENT) {
                std.error("postStream(): Expected ACCEPTED or NO CONTENT for POST request, got " + code);
                String data = null;

                try {
                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                        data = EntityUtils.toString(entity);
                        if (wire.isDebugEnabled()) {
                            wire.debug(data);
                            wire.debug("");
                        }
                    }
                } catch (IOException e) {
                    std.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                    e.printStackTrace();
                    throw new CloudException(e);
                }
                NovaException.ExceptionItems items = NovaException.parseException(code, data);

                if (items == null) {
                    items = new NovaException.ExceptionItems();
                    items.code = 404;
                    items.type = CloudErrorType.COMMUNICATION;
                    items.message = "itemNotFound";
                    items.details = "No such object: " + resource;
                }
                std.error("postString(): [" + code + " : " + items.message + "] " + items.details);
                throw new NovaException(items);
            } else {
                wire.debug("");
                if (code == HttpStatus.SC_ACCEPTED) {
                    String data = null;

                    try {
                        HttpEntity entity = response.getEntity();

                        if (entity != null) {
                            data = EntityUtils.toString(entity);
                            if (wire.isDebugEnabled()) {
                                wire.debug(data);
                                wire.debug("");
                            }
                        }
                    } catch (IOException e) {
                        std.error("Failed to read response due to a cloud I/O error: " + e.getMessage());
                        e.printStackTrace();
                        throw new CloudException(e);
                    }
                    if (data != null && !data.trim().equals("")) {
                        return data;
                    }
                }
                return null;
            }
        } finally {
            if (client != null) {
                client.getConnectionManager().shutdown();
            }
            if (std.isTraceEnabled()) {
                std.trace("exit - " + NovaOpenStack.class.getName() + ".postStream()");
            }
            if (wire.isDebugEnabled()) {
                wire.debug("");
                wire.debug("---------------------------------------------------------------------------------"
                        + endpoint + resource);
            }
        }
    }

    @Override
    public void putResourceHeaders(@Nonnull final String service, @Nullable final String resource,
            @Nullable final String resourceId, @Nonnull final Map<String, String> headers)
            throws CloudException, InternalException {
        AuthenticationContext context = provider.getAuthenticationContext();
        String endpoint = context.getServiceUrl(service);

        if (endpoint == null) {
            throw new CloudException("No " + service + " has been established in " + context.getMyRegion());
        }
        String resourceUri = resource;
        if (resource == null && resourceId == null) {
            resourceUri = "/";
        } else if (resource == null) {
            resourceUri = "/" + resourceId;
        } else if (resourceId != null) {
            resourceUri += "/" + resourceId;
        }
        try {
            putHeaders(context.getAuthToken(), endpoint, resourceUri, headers);
        } catch (NovaException ex) {
            if (ex.getHttpCode() == HttpStatus.SC_UNAUTHORIZED) {
                Cache<AuthenticationContext> cache = Cache.getInstance(provider, "authenticationContext",
                        AuthenticationContext.class, CacheLevel.REGION_ACCOUNT,
                        new TimePeriod<Day>(1, TimePeriod.DAY));
                cache.clear();
                putResourceHeaders(service, resource, resourceId, headers);
            } else {
                throw ex;
            }
        }
    }

    @Override
    @SuppressWarnings("unused")
    protected @Nonnull String putHeaders(@Nonnull String authToken, @Nonnull String endpoint,
            @Nonnull String resource, @Nonnull Map<String, String> customHeaders)
            throws CloudException, InternalException {
        Logger std = NovaOpenStack.getLogger(NovaOpenStack.class, "std");
        Logger wire = NovaOpenStack.getLogger(NovaOpenStack.class, "wire");

        if (std.isTraceEnabled()) {
            std.trace("enter - " + AbstractMethod.class.getName() + ".putHeaders(" + authToken + "," + endpoint
                    + "," + resource + "," + customHeaders + ")");
        }
        if (wire.isDebugEnabled()) {
            wire.debug("---------------------------------------------------------------------------------"
                    + endpoint + resource);
            wire.debug("");
        }
        HttpClient client = null;
        try {
            client = getClient();
            HttpPut put = new HttpPut(endpoint + resource);

            put.addHeader("Content-Type", "application/json");
            put.addHeader("X-Auth-Token", authToken);
            if (customHeaders != null) {
                for (Map.Entry<String, String> entry : customHeaders.entrySet()) {
                    String val = (entry.getValue() == null ? "" : entry.getValue());

                    put.addHeader(entry.getKey(), val);
                }
            }

            if (wire.isDebugEnabled()) {
                wire.debug(put.getRequestLine().toString());
                for (Header header : put.getAllHeaders()) {
                    wire.debug(header.getName() + ": " + header.getValue());
                }
                wire.debug("");
            }
            HttpResponse response;

            try {
                APITrace.trace(provider, "PUT " + toAPIResource(resource));
                response = client.execute(put);
                if (wire.isDebugEnabled()) {
                    wire.debug(response.getStatusLine().toString());
                    for (Header header : response.getAllHeaders()) {
                        wire.debug(header.getName() + ": " + header.getValue());
                    }
                    wire.debug("");
                }
            } catch (IOException e) {
                std.error("I/O error from server communications: " + e.getMessage());
                e.printStackTrace();
                throw new InternalException(e);
            }
            int code = response.getStatusLine().getStatusCode();

            std.debug("HTTP STATUS: " + code);

            if (code != HttpStatus.SC_CREATED && code != HttpStatus.SC_ACCEPTED
                    && code != HttpStatus.SC_NO_CONTENT) {
                std.error("putString(): Expected CREATED, ACCEPTED, or NO CONTENT for put request, got " + code);
                String data = null;

                try {
                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                        data = EntityUtils.toString(entity);
                        if (wire.isDebugEnabled()) {
                            wire.debug(data);
                            wire.debug("");
                        }
                    }
                } catch (IOException e) {
                    std.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                    e.printStackTrace();
                    throw new CloudException(e);
                }
                NovaException.ExceptionItems items = NovaException.parseException(code, data);

                if (items == null) {
                    items = new NovaException.ExceptionItems();
                    items.code = 404;
                    items.type = CloudErrorType.COMMUNICATION;
                    items.message = "itemNotFound";
                    items.details = "No such object: " + resource;
                }
                std.error("putString(): [" + code + " : " + items.message + "] " + items.details);
                throw new NovaException(items);
            } else {
                if (code == HttpStatus.SC_ACCEPTED || code == HttpStatus.SC_CREATED) {
                    String data = null;

                    try {
                        HttpEntity entity = response.getEntity();

                        if (entity != null) {
                            data = EntityUtils.toString(entity);
                            if (wire.isDebugEnabled()) {
                                wire.debug(data);
                                wire.debug("");
                            }
                        }
                    } catch (IOException e) {
                        std.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                        e.printStackTrace();
                        throw new CloudException(e);
                    }
                    if (data != null && !data.trim().equals("")) {
                        return data;
                    }
                }
                return null;
            }
        } finally {
            if (client != null) {
                client.getConnectionManager().shutdown();
            }
            if (std.isTraceEnabled()) {
                std.trace("exit - " + AbstractMethod.class.getName() + ".putString()");
            }
            if (wire.isDebugEnabled()) {
                wire.debug("");
                wire.debug("---------------------------------------------------------------------------------"
                        + endpoint + resource);
            }
        }
    }

    @Override
    public @Nullable JSONObject putString(@Nonnull final String service, @Nonnull final String resource,
            @Nullable final String resourceId, @Nonnull final JSONObject body, final String suffix)
            throws CloudException, InternalException {
        AuthenticationContext context = provider.getAuthenticationContext();
        String resourceUri = resource;
        if (resourceId != null) {
            resourceUri += "/" + (suffix != null ? (resourceId + "/" + suffix) : resourceId);
        }
        String endpoint = context.getServiceUrl(service);
        if (endpoint == null) {
            throw new CloudException("No " + service + " endpoint exists");
        }
        try {
            String response = putString(context.getAuthToken(), endpoint, resourceUri, body.toString());
            if (response == null) {
                return null;
            }
            try {
                return new JSONObject(response);
            } catch (JSONException e) {
                throw new CloudException(CloudErrorType.COMMUNICATION, 200, "invalidJson", response);
            }
        } catch (NovaException ex) {
            if (ex.getHttpCode() == HttpStatus.SC_UNAUTHORIZED) {
                Cache<AuthenticationContext> cache = Cache.getInstance(provider, "authenticationContext",
                        AuthenticationContext.class, CacheLevel.REGION_ACCOUNT,
                        new TimePeriod<Day>(1, TimePeriod.DAY));
                cache.clear();
                return putString(service, resource, resourceId, body, suffix);
            } else {
                throw ex;
            }
        }
    }

    @Override
    protected @Nullable String putString(@Nonnull String authToken, @Nonnull String endpoint,
            @Nonnull String resource, @Nullable String payload) throws CloudException, InternalException {
        Logger std = NovaOpenStack.getLogger(NovaOpenStack.class, "std");
        Logger wire = NovaOpenStack.getLogger(NovaOpenStack.class, "wire");

        if (std.isTraceEnabled()) {
            std.trace("enter - " + AbstractMethod.class.getName() + ".putString(" + authToken + "," + endpoint + ","
                    + resource + "," + payload + ")");
        }
        if (wire.isDebugEnabled()) {
            wire.debug("---------------------------------------------------------------------------------"
                    + endpoint + resource);
            wire.debug("");
        }
        HttpClient client = null;
        try {
            client = getClient();
            HttpPut put = new HttpPut(endpoint + resource);

            put.addHeader("Content-Type", "application/json");
            put.addHeader("X-Auth-Token", authToken);

            if (wire.isDebugEnabled()) {
                wire.debug(put.getRequestLine().toString());
                for (Header header : put.getAllHeaders()) {
                    wire.debug(header.getName() + ": " + header.getValue());
                }
                wire.debug("");
            }
            if (payload != null) {
                try {
                    // noinspection deprecation
                    put.setEntity(new StringEntity(payload == null ? "" : payload, "application/json", "UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    throw new InternalException(e);
                }
                try {
                    wire.debug(EntityUtils.toString(put.getEntity()));
                } catch (IOException ignore) {
                }

                wire.debug("");
            }
            HttpResponse response;

            try {
                APITrace.trace(provider, "PUT " + toAPIResource(resource));
                response = client.execute(put);
                if (wire.isDebugEnabled()) {
                    wire.debug(response.getStatusLine().toString());
                    for (Header header : response.getAllHeaders()) {
                        wire.debug(header.getName() + ": " + header.getValue());
                    }
                    wire.debug("");
                }
            } catch (IOException e) {
                std.error("I/O error from server communications: " + e.getMessage());
                e.printStackTrace();
                throw new InternalException(e);
            }
            int code = response.getStatusLine().getStatusCode();

            std.debug("HTTP STATUS: " + code);

            if (code != HttpStatus.SC_CREATED && code != HttpStatus.SC_ACCEPTED && code != HttpStatus.SC_NO_CONTENT
                    && code != HttpStatus.SC_OK) {
                std.error("putString(): Expected CREATED, ACCEPTED, or NO CONTENT for put request, got " + code);
                String data = null;

                try {
                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                        data = EntityUtils.toString(entity);
                        if (wire.isDebugEnabled()) {
                            wire.debug(data);
                            wire.debug("");
                        }
                    }
                } catch (IOException e) {
                    std.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                    e.printStackTrace();
                    throw new CloudException(e);
                }
                NovaException.ExceptionItems items = NovaException.parseException(code, data);

                if (items == null) {
                    items = new NovaException.ExceptionItems();
                    items.code = 404;
                    items.type = CloudErrorType.COMMUNICATION;
                    items.message = "itemNotFound";
                    items.details = "No such object: " + resource;
                }
                std.error("putString(): [" + code + " : " + items.message + "] " + items.details);
                throw new NovaException(items);
            } else {
                if (code == HttpStatus.SC_ACCEPTED || code == HttpStatus.SC_CREATED) {
                    String data = null;

                    try {
                        HttpEntity entity = response.getEntity();

                        if (entity != null) {
                            data = EntityUtils.toString(entity);
                            if (wire.isDebugEnabled()) {
                                wire.debug(data);
                                wire.debug("");
                            }
                        }
                    } catch (IOException e) {
                        std.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                        e.printStackTrace();
                        throw new CloudException(e);
                    }
                    if (data != null && !data.trim().equals("")) {
                        return data;
                    }
                }
                return null;
            }
        } finally {
            if (client != null) {
                client.getConnectionManager().shutdown();
            }
            if (std.isTraceEnabled()) {
                std.trace("exit - " + AbstractMethod.class.getName() + ".putString()");
            }
            if (wire.isDebugEnabled()) {
                wire.debug("");
                wire.debug("---------------------------------------------------------------------------------"
                        + endpoint + resource);
            }
        }
    }

    @Override
    protected @Nullable String putStream(@Nonnull String authToken, @Nonnull String endpoint,
            @Nonnull String resource, @Nullable String md5Hash, @Nonnull InputStream stream)
            throws CloudException, InternalException {
        Logger std = NovaOpenStack.getLogger(NovaOpenStack.class, "std");
        Logger wire = NovaOpenStack.getLogger(NovaOpenStack.class, "wire");

        if (std.isTraceEnabled()) {
            std.trace("enter - " + AbstractMethod.class.getName() + ".putStream(" + authToken + "," + endpoint + ","
                    + resource + "," + md5Hash + ",INPUTSTREAM)");
        }
        if (wire.isDebugEnabled()) {
            wire.debug("---------------------------------------------------------------------------------"
                    + endpoint + resource);
            wire.debug("");
        }
        HttpClient client = null;
        try {
            client = getClient();
            HttpPut put = new HttpPut(endpoint + resource);

            put.addHeader("Content-Type", "application/octet-stream");
            put.addHeader("X-Auth-Token", authToken);
            if (md5Hash != null) {
                put.addHeader("ETag", md5Hash);
            }

            if (wire.isDebugEnabled()) {
                wire.debug(put.getRequestLine().toString());
                for (Header header : put.getAllHeaders()) {
                    wire.debug(header.getName() + ": " + header.getValue());
                }
                wire.debug("");
            }
            put.setEntity(new InputStreamEntity(stream, -1, ContentType.APPLICATION_OCTET_STREAM));
            wire.debug(" ---- BINARY DATA ---- ");
            wire.debug("");

            HttpResponse response;

            try {
                APITrace.trace(provider, "PUT " + toAPIResource(resource));
                response = client.execute(put);
                if (wire.isDebugEnabled()) {
                    wire.debug(response.getStatusLine().toString());
                    for (Header header : response.getAllHeaders()) {
                        wire.debug(header.getName() + ": " + header.getValue());
                    }
                    wire.debug("");
                }
            } catch (IOException e) {
                std.error("I/O error from server communications: " + e.getMessage());
                e.printStackTrace();
                throw new InternalException(e);
            }
            int code = response.getStatusLine().getStatusCode();

            std.debug("HTTP STATUS: " + code);

            String responseHash = null;

            for (Header h : put.getAllHeaders()) {
                if (h.getName().equalsIgnoreCase("ETag")) {
                    responseHash = h.getValue();
                }
            }
            if (responseHash != null && md5Hash != null && !responseHash.equals(md5Hash)) {
                throw new CloudException("MD5 hash values do not match, probably data corruption");
            }
            if (code != HttpStatus.SC_CREATED && code != HttpStatus.SC_ACCEPTED
                    && code != HttpStatus.SC_NO_CONTENT) {
                std.error("putStream(): Expected CREATED, ACCEPTED, or NO CONTENT for PUT request, got " + code);
                String data = null;

                try {
                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                        data = EntityUtils.toString(entity);
                        if (wire.isDebugEnabled()) {
                            wire.debug(data);
                            wire.debug("");
                        }
                    }
                } catch (IOException e) {
                    std.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                    e.printStackTrace();
                    throw new CloudException(e);
                }
                NovaException.ExceptionItems items = NovaException.parseException(code, data);

                if (items == null) {
                    items = new NovaException.ExceptionItems();
                    items.code = 404;
                    items.type = CloudErrorType.COMMUNICATION;
                    items.message = "itemNotFound";
                    items.details = "No such object: " + resource;
                }
                std.error("putStream(): [" + code + " : " + items.message + "] " + items.details);
                throw new NovaException(items);
            } else {
                if (code == HttpStatus.SC_ACCEPTED) {
                    String data = null;

                    try {
                        HttpEntity entity = response.getEntity();

                        if (entity != null) {
                            data = EntityUtils.toString(entity);
                            if (wire.isDebugEnabled()) {
                                wire.debug(data);
                                wire.debug("");
                            }
                        }
                    } catch (IOException e) {
                        std.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                        e.printStackTrace();
                        throw new CloudException(e);
                    }
                    if (data != null && !data.trim().equals("")) {
                        return data;
                    }
                }
                return null;
            }
        } finally {
            if (client != null) {
                client.getConnectionManager().shutdown();
            }
            if (std.isTraceEnabled()) {
                std.trace("exit - " + NovaOpenStack.class.getName() + ".putStream()");
            }
            if (wire.isDebugEnabled()) {
                wire.debug("");
                wire.debug("---------------------------------------------------------------------------------"
                        + endpoint + resource);
            }
        }
    }

    private @Nonnull String toRegion(@Nonnull String endpoint) {
        Logger logger = NovaOpenStack.getLogger(NovaOpenStack.class, "std");

        if (logger.isTraceEnabled()) {
            logger.trace("enter - " + AbstractMethod.class.getName() + ".toRegion(" + endpoint + ")");
        }
        try {
            if (logger.isInfoEnabled()) {
                logger.info("Looking up official region for " + endpoint);
            }
            String host;

            try {
                URI uri = new URI(endpoint);

                host = uri.getHost();
            } catch (URISyntaxException e) {
                host = endpoint;
            }
            String[] parts = host.split("\\.");
            String regionId;

            if (parts.length < 3) {
                regionId = host;
            } else if (parts.length == 3) {
                regionId = parts[0];
            } else {
                regionId = parts[0] + "." + parts[1];
            }
            if (logger.isDebugEnabled()) {
                logger.debug("regionId=" + regionId);
            }
            return regionId;
        } finally {
            if (logger.isTraceEnabled()) {
                logger.trace("exit - " + AbstractMethod.class.getName() + ".toRegion()");
            }
        }
    }

    private @Nonnegative int compareVersions(@Nullable String ver1, @Nullable String ver2)
            throws InternalException {
        Logger logger = NovaOpenStack.getLogger(NovaOpenStack.class, "std");

        if (logger.isTraceEnabled()) {
            logger.trace(
                    "enter - " + AbstractMethod.class.getName() + ".compareVersions(" + ver1 + "," + ver2 + ")");
        }
        int result = 0;

        if (ver1 == null && ver2 == null) {
            return 0;
        } else if (ver1 == null) {
            ver1 = "1.0";
        } else if (ver2 == null) {
            ver2 = "1.0";
        }
        // Assumes only x.x granularity. Anything more will require rewrite of
        // this component.
        try {
            String majorStr1 = ver1.contains(".") ? ver1.substring(0, ver1.indexOf(".")) : ver1;
            String minorStr1 = ver1.contains(".") ? ver1.substring(ver1.indexOf(".") + 1) : "0";
            String majorStr2 = ver2.contains(".") ? ver2.substring(0, ver2.indexOf(".")) : ver2;
            String minorStr2 = ver2.contains(".") ? ver2.substring(ver2.indexOf(".") + 1) : "0";
            int major1 = Integer.parseInt(majorStr1);
            int minor1 = Integer.parseInt(minorStr1);
            int major2 = Integer.parseInt(majorStr2);
            int minor2 = Integer.parseInt(minorStr2);
            result = major1 - major2;
            if (result == 0)
                result = minor1 - minor2;
        } catch (Exception e) {
            // Something really stupid showed up in the version string...
            throw new InternalException(e);
        } finally {
            if (logger.isTraceEnabled()) {
                logger.trace("exit - " + AbstractMethod.class.getName() + ".compareVersions(" + ver1 + "," + ver2
                        + ") = " + result);
            }
        }

        return result;
    }

    private @Nonnull String toAPIResource(@Nonnull String resource) {
        if (resource == null || resource.equals("/") || resource.length() < 2) {
            return resource;
        }
        while (resource.startsWith("/")) {
            if (resource.equals("/")) {
                return "/";
            }
            resource = resource.substring(1);
        }
        int idx = resource.indexOf("/");

        if (idx > 0) {
            return resource.substring(0, idx);
        }
        return resource;
    }

    public @Nullable JSONObject putServers(@Nonnull final String resource, @Nullable final String resourceId,
            @Nonnull final JSONObject body, final String action) throws CloudException, InternalException {
        AuthenticationContext context = provider.getAuthenticationContext();

        String resourceUri = resource;
        if (resourceId != null) {
            resourceUri = resource + "/" + (action != null ? (resourceId + "/" + action) : resourceId);
        }
        String endpoint = context.getComputeUrl();

        if (endpoint == null) {
            throw new CloudException("No compute endpoint exists");
        }

        if (resourceUri != null && (!endpoint.endsWith("/") && !resourceUri.startsWith("/"))) {
            endpoint = endpoint + "/";
        }
        try {
            String response = putString(context.getAuthToken(), endpoint, resourceUri, body.toString());

            if (response == null) {
                return null;
            }
            try {
                return new JSONObject(response);
            } catch (JSONException e) {
                throw new CloudException(CloudErrorType.COMMUNICATION, 200, "invalidJson", response);
            }
        } catch (NovaException ex) {
            if (ex.getHttpCode() == HttpStatus.SC_UNAUTHORIZED) {
                Cache<AuthenticationContext> cache = Cache.getInstance(provider, "authenticationContext",
                        AuthenticationContext.class, CacheLevel.REGION_ACCOUNT,
                        new TimePeriod<Day>(1, TimePeriod.DAY));
                cache.clear();
                return putServers(resource, resourceId, body, action);
            } else {
                throw ex;
            }
        }
    }
}