org.dasein.cloud.vcloud.vCloudMethod.java Source code

Java tutorial

Introduction

Here is the source code for org.dasein.cloud.vcloud.vCloudMethod.java

Source

/**
 * Copyright (C) 2009-2014 Dell, 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 org.dasein.cloud.vcloud;

import org.apache.commons.codec.binary.Base64;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.StatusLine;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
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.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
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.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreConnectionPNames;
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.Taggable;
import org.dasein.cloud.dc.DataCenter;
import org.dasein.cloud.dc.Region;
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.dasein.util.uom.time.Minute;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
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.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TreeSet;

/**
 * [Class Documentation]
 * <p>Created by George Reese: 2/4/13 6:31 PM</p>
 *
 * @author George Reese
 */
public class vCloudMethod {
    static public final String[] VERSIONS = { "5.6", "5.1", "1.5", "1.0", "0.9", "0.8" };

    static public final String CAPTURE_VAPP = "captureVApp";
    static public final String COMPOSE_VAPP = "composeVApp";
    static public final String CREATE_DISK = "createDisk";
    static public final String INSTANTIATE_VAPP = "instantiateVApp";

    static public boolean isSupported(@Nonnull String version) {
        for (String v : VERSIONS) {
            if (version.equals(v)) {
                return true;
            }
        }
        return false;
    }

    static public boolean matches(@Nonnull String currentVersion, @Nonnull String minimumVersion,
            @Nullable String maximumVersion) {
        if (currentVersion.equals(minimumVersion)) {
            return true;
        } else if (maximumVersion != null && currentVersion.equals(maximumVersion)) {
            return true;
        }
        if (!isSupported(currentVersion)) {
            return false;
        }
        boolean hasMaximum = (maximumVersion != null);
        boolean hitMaximum = false;

        for (String version : VERSIONS) {
            if (hasMaximum) {
                if (!hitMaximum) {
                    hitMaximum = version.equals(maximumVersion); // we already checked equivalence with the maximum
                } else {
                    if (minimumVersion.equals(version)) { // we already checked equivalence with the minimum
                        return false;
                    }
                    if (version.equals(currentVersion)) {
                        return true;
                    }
                }
            } else {
                if (minimumVersion.equals(version)) { // we already checked equivalence with the minimum
                    return false;
                }
                if (version.equals(currentVersion)) {
                    return true;
                }
            }
        }
        return false;
    }

    static private Logger logger = vCloud.getLogger(vCloudMethod.class);
    static private Logger wire = vCloud.getWireLogger(vCloudMethod.class);

    static public class Org {
        public String token;
        public String endpoint;
        public Version version;
        public Region region;
        public String url;
        private volatile Iterable<VDC> _vdcs;

        public Iterable<VDC> getVdcs() throws InternalException {
            if (_vdcs != null) {
                return _vdcs;
            }
            // There is a caching+recursion issue with authenticate()
            int timeout = 10000;
            while (_vdcs == null) {
                try {
                    Thread.sleep(400L);
                } catch (InterruptedException ignore) {
                    timeout -= 100L;
                    continue;
                }
                timeout -= 400L;
                if (timeout <= 0) {
                    throw new InternalException("Could not populate VDCs");
                }
            }
            return _vdcs;
        }

        public void setVdcs(Iterable<VDC> vdcs) {
            this._vdcs = vdcs;
        }
    }

    static public class Version {
        public String loginUrl;
        public String version;

        public String toString() {
            return (version + " [" + loginUrl + "]");
        }
    }

    static public class VDC {
        public DataCenter dataCenter;
        public HashMap<String, String> actions;
        public int vmQuota = -2;
        public int networkQuota = -2;
    }

    private vCloud provider;

    public vCloudMethod(@Nonnull vCloud provider) {
        this.provider = provider;
    }

    public void checkError(@Nonnull Document xmlDocument) throws CloudException {
        NodeList tasks;

        try {
            tasks = xmlDocument.getElementsByTagName("Task");
        } catch (Throwable ignore) {
            return;
        }
        if (tasks.getLength() < 1) {
            return;
        }
        Node task = tasks.item(0);

        if (task.hasAttributes()) {
            Node status = task.getAttributes().getNamedItem("status");

            if (status != null) {
                String s = status.getNodeValue().trim();

                if (!s.equals("error")) {
                    NodeList elements = task.getChildNodes();

                    for (int i = 0; i < elements.getLength(); i++) {
                        Node element = elements.item(i);

                        if (element.getNodeName().equalsIgnoreCase("Error")) {
                            parseError(element);
                            return;
                        }
                    }
                }
            }
        }
    }

    private void loadOrg(@Nonnull String endpoint, @Nonnull Org org, @Nonnull String orgId)
            throws CloudException, InternalException {
        String xml;

        if (wire.isDebugEnabled()) {
            wire.debug("");
            wire.debug(">>> [GET (" + (new Date()) + ")] -> " + endpoint
                    + " >--------------------------------------------------------------------------------------");
        }
        try {
            HttpClient client = getClient(false);
            HttpGet get = new HttpGet(endpoint);

            get.addHeader("Accept", "application/*+xml;version=" + org.version.version
                    + ",application/*+xml;version=" + org.version.version);
            addAuth(get, org.token);

            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 org");
                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) {
                logger.error("I/O error from server communications: " + e.getMessage());
                throw new InternalException(e);
            }
            int code = response.getStatusLine().getStatusCode();

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

            if (code == HttpServletResponse.SC_NOT_FOUND || code == HttpServletResponse.SC_FORBIDDEN) {
                throw new CloudException("Org URL is invalid");
            } else if (code == HttpServletResponse.SC_UNAUTHORIZED) {
                authenticate(true);
                loadOrg(endpoint, org, orgId);
                return;
            } else if (code == HttpServletResponse.SC_NO_CONTENT) {
                throw new CloudException("No content from org URL");
            } else if (code == HttpServletResponse.SC_OK) {
                try {
                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                        xml = EntityUtils.toString(entity);
                        if (wire.isDebugEnabled()) {
                            wire.debug(xml);
                            wire.debug("");
                        }
                    } else {
                        xml = null;
                    }
                } catch (IOException e) {
                    logger.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                    throw new CloudException(e);
                }
            } else {
                logger.error("Expected OK for GET request, got " + code);
                try {
                    HttpEntity entity = response.getEntity();

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

                vCloudException.Data data = null;

                if (xml != null && !xml.equals("")) {
                    Document doc = parseXML(xml);
                    String docElementTagName = doc.getDocumentElement().getTagName();
                    String nsString = "";
                    if (docElementTagName.contains(":"))
                        nsString = docElementTagName.substring(0, docElementTagName.indexOf(":") + 1);
                    NodeList errors = doc.getElementsByTagName(nsString + "Error");

                    if (errors.getLength() > 0) {
                        data = vCloudException.parseException(code, errors.item(0));
                    }
                }
                if (data == null) {
                    throw new vCloudException(CloudErrorType.GENERAL, code,
                            response.getStatusLine().getReasonPhrase(), "No further information");
                }
                logger.error("[" + code + " : " + data.title + "] " + data.description);
                throw new vCloudException(data);
            }
        } finally {
            if (wire.isDebugEnabled()) {
                wire.debug("<<< [GET (" + (new Date()) + ")] -> " + endpoint
                        + " <--------------------------------------------------------------------------------------");
                wire.debug("");
            }
        }
        if (xml == null) {
            throw new CloudException("No content from org URL");
        }
        Document doc = parseXML(xml);
        String docElementTagName = doc.getDocumentElement().getTagName();
        String nsString = "";
        if (docElementTagName.contains(":"))
            nsString = docElementTagName.substring(0, docElementTagName.indexOf(":") + 1);
        NodeList orgList = doc.getElementsByTagName(nsString + "Org");

        for (int i = 0; i < orgList.getLength(); i++) {
            Node orgNode = orgList.item(i);

            if (orgNode.hasAttributes()) {
                Node type = orgNode.getAttributes().getNamedItem("type");

                if (type != null && type.getNodeValue().trim().equals(getMediaTypeForOrg())) {
                    Node name = orgNode.getAttributes().getNamedItem("name");

                    if (name != null && name.getNodeValue().trim().equals(orgId)) {
                        Node href = orgNode.getAttributes().getNamedItem("href");

                        if (href != null) {
                            Region region = new Region();
                            String url = href.getNodeValue().trim();

                            region.setActive(true);
                            region.setAvailable(true);
                            if (provider.isCompat()) {
                                region.setProviderRegionId("/org/" + url.substring(url.lastIndexOf('/') + 1));
                            } else {
                                region.setProviderRegionId(url.substring(url.lastIndexOf('/') + 1));
                            }
                            region.setJurisdiction("US");
                            region.setName(name.getNodeValue().trim());

                            org.endpoint = url.substring(0, url.lastIndexOf("/api/org"));
                            org.region = region;
                            org.url = url;
                            return;
                        }
                    }
                }
            }
        }
        throw new CloudException("Could not find " + orgId + " among listed orgs");
    }

    public @Nonnull Org authenticate(boolean force) throws CloudException, InternalException {
        Cache<Org> cache = Cache.getInstance(provider, "vCloudOrgs", Org.class, CacheLevel.CLOUD_ACCOUNT,
                new TimePeriod<Minute>(25, TimePeriod.MINUTE));
        ProviderContext ctx = provider.getContext();

        if (ctx == null) {
            throw new CloudException("No context was defined for this request");
        }
        String accountNumber = ctx.getAccountNumber();
        Iterable<Org> orgs = cache.get(ctx);
        Iterator<Org> it = ((force || orgs == null) ? null : orgs.iterator());

        if (it == null || !it.hasNext()) {
            String endpoint = getVersion().loginUrl;

            if (wire.isDebugEnabled()) {
                wire.debug("");
                wire.debug(">>> [POST (" + (new Date()) + ")] -> " + endpoint
                        + " >--------------------------------------------------------------------------------------");
            }
            try {
                HttpClient client = getClient(true);
                HttpPost method = new HttpPost(endpoint);
                Org org = new Org();

                org.version = getVersion();
                method.addHeader("Accept", "application/*+xml;version=" + org.version.version
                        + ",application/*+xml;version=" + org.version.version);

                String accessPublic = null;
                String accessPrivate = null;
                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) {
                    throw new InternalException(e);
                }

                String password = accessPrivate;
                String userName;

                if (matches(getAPIVersion(), "0.8", "0.8")) {
                    userName = accessPublic;
                } else if (getAPIVersion().equals("5.6")) {
                    userName = accessPublic;
                } else {
                    userName = accessPublic + "@" + ctx.getAccountNumber();
                }
                String auth = new String(Base64.encodeBase64((userName + ":" + password).getBytes()));

                method.addHeader("Authorization", "Basic " + auth);
                if (wire.isDebugEnabled()) {
                    wire.debug(method.getRequestLine().toString());
                    for (Header header : method.getAllHeaders()) {
                        wire.debug(header.getName() + ": " + header.getValue());
                    }
                    wire.debug("");
                }
                HttpResponse response;
                StatusLine status;

                try {
                    APITrace.trace(provider, "POST sessions");
                    response = client.execute(method);
                    if (wire.isDebugEnabled()) {
                        wire.debug(response.getStatusLine().toString());
                        for (Header header : response.getAllHeaders()) {
                            wire.debug(header.getName() + ": " + header.getValue());
                        }
                        wire.debug("");
                    }
                    status = response.getStatusLine();
                } catch (IOException e) {
                    throw new CloudException(e);
                }
                if (status.getStatusCode() == HttpServletResponse.SC_OK) {
                    if (matches(getAPIVersion(), "0.8", "0.8")) {
                        for (Header h : response.getHeaders("Set-Cookie")) {
                            String value = h.getValue();

                            if (value != null) {
                                value = value.trim();
                                if (value.startsWith("vcloud-token")) {
                                    value = value.substring("vcloud-token=".length());

                                    int idx = value.indexOf(";");

                                    if (idx == -1) {
                                        org.token = value;
                                    } else {
                                        org.token = value.substring(0, idx);
                                    }
                                }
                            }
                        }
                    } else {
                        org.token = response.getFirstHeader("x-vcloud-authorization").getValue();
                    }
                    if (org.token == null) {
                        throw new CloudException(CloudErrorType.AUTHENTICATION, 200, "Token Empty",
                                "No token was provided");
                    }
                    HttpEntity entity = response.getEntity();
                    String body;

                    try {
                        body = EntityUtils.toString(entity);
                        if (wire.isDebugEnabled()) {
                            wire.debug(body);
                            wire.debug("");
                        }
                    } catch (IOException e) {
                        throw new CloudException(CloudErrorType.GENERAL, status.getStatusCode(),
                                status.getReasonPhrase(), e.getMessage());
                    }
                    try {
                        ByteArrayInputStream bas = new ByteArrayInputStream(body.getBytes());

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

                        bas.close();
                        if (matches(org.version.version, "1.5", null)) {
                            NodeList orgNodes = doc.getElementsByTagName("Link");
                            String orgList = null;

                            for (int i = 0; i < orgNodes.getLength(); i++) {
                                Node orgNode = orgNodes.item(i);

                                if (orgNode.hasAttributes()) {
                                    Node type = orgNode.getAttributes().getNamedItem("type");

                                    if (type != null && type.getNodeValue().trim().equals(getMediaTypeForOrg())) {
                                        Node name = orgNode.getAttributes().getNamedItem("name");

                                        if (name != null && name.getNodeValue().trim().equals(accountNumber)) {
                                            Node href = orgNode.getAttributes().getNamedItem("href");

                                            if (href != null) {
                                                Region region = new Region();
                                                String url = href.getNodeValue().trim();

                                                region.setActive(true);
                                                region.setAvailable(true);
                                                if (provider.isCompat()) {
                                                    region.setProviderRegionId(
                                                            "/org/" + url.substring(url.lastIndexOf('/') + 1));
                                                } else {
                                                    region.setProviderRegionId(
                                                            url.substring(url.lastIndexOf('/') + 1));
                                                }
                                                region.setJurisdiction("US");
                                                region.setName(name.getNodeValue().trim());

                                                org.endpoint = url.substring(0, url.lastIndexOf("/api/org"));
                                                org.region = region;
                                                org.url = url;
                                            }
                                        }
                                    }
                                    if (type != null
                                            && type.getNodeValue().trim().equals(getMediaTypeForOrgList())) {
                                        Node href = orgNode.getAttributes().getNamedItem("href");

                                        if (href != null) {
                                            orgList = href.getNodeValue().trim();
                                        }
                                    }
                                }
                            }
                            if (org.endpoint == null && orgList != null) {
                                loadOrg(orgList, org, accountNumber);
                            }
                        } else {
                            NodeList orgNodes = doc.getElementsByTagName("Org");

                            for (int i = 0; i < orgNodes.getLength(); i++) {
                                Node orgNode = orgNodes.item(i);

                                if (orgNode.hasAttributes()) {
                                    Node name = orgNode.getAttributes().getNamedItem("name");
                                    Node href = orgNode.getAttributes().getNamedItem("href");

                                    if (href != null) {
                                        String url = href.getNodeValue().trim();
                                        Region region = new Region();

                                        if (!url.endsWith("/org/" + accountNumber)) {
                                            continue;
                                        }
                                        region.setActive(true);
                                        region.setAvailable(true);
                                        if (provider.isCompat()) {
                                            region.setProviderRegionId(
                                                    "/org/" + url.substring(url.lastIndexOf('/') + 1));
                                        } else {
                                            region.setProviderRegionId(url.substring(url.lastIndexOf('/') + 1));
                                        }
                                        region.setJurisdiction("US");
                                        region.setName(name == null ? accountNumber : name.getNodeValue().trim());
                                        org.endpoint = url.substring(0, url.lastIndexOf("/org/"));
                                        org.region = region;
                                        org.url = url;
                                    }
                                }
                            }
                        }
                    } catch (IOException e) {
                        throw new CloudException(CloudErrorType.GENERAL, status.getStatusCode(),
                                status.getReasonPhrase(), e.getMessage());
                    } catch (ParserConfigurationException e) {
                        throw new CloudException(CloudErrorType.GENERAL, status.getStatusCode(),
                                status.getReasonPhrase(), e.getMessage());
                    } catch (SAXException e) {
                        throw new CloudException(CloudErrorType.GENERAL, status.getStatusCode(),
                                status.getReasonPhrase(), e.getMessage());
                    }
                } else {
                    HttpEntity entity = response.getEntity();

                    if (entity != null) {
                        String body;

                        try {
                            body = EntityUtils.toString(entity);
                            if (wire.isDebugEnabled()) {
                                wire.debug(body);
                                wire.debug("");
                            }
                        } catch (IOException e) {
                            throw new CloudException(CloudErrorType.GENERAL, status.getStatusCode(),
                                    status.getReasonPhrase(), e.getMessage());
                        }
                        vCloudException.Data data = null;

                        if (body != null && !body.equals("")) {
                            Document doc = parseXML(body);
                            String docElementTagName = doc.getDocumentElement().getTagName();
                            String nsString = "";
                            if (docElementTagName.contains(":"))
                                nsString = docElementTagName.substring(0, docElementTagName.indexOf(":") + 1);
                            NodeList errors = doc.getElementsByTagName(nsString + "Error");

                            if (errors.getLength() > 0) {
                                data = vCloudException.parseException(status.getStatusCode(), errors.item(0));
                            }
                        }
                        if (data == null) {
                            throw new vCloudException(CloudErrorType.GENERAL, status.getStatusCode(),
                                    response.getStatusLine().getReasonPhrase(), "No further information");
                        }
                        logger.error("[" + status.getStatusCode() + " : " + data.title + "] " + data.description);
                        throw new vCloudException(data);
                    }
                    throw new CloudException(CloudErrorType.AUTHENTICATION, status.getStatusCode(),
                            status.getReasonPhrase(), "Authentication failed");
                }
                if (org.endpoint == null) {
                    throw new CloudException(CloudErrorType.GENERAL, status.getStatusCode(), "No Org",
                            "No org was identified for " + ctx.getAccountNumber());
                }
                cache.put(ctx, Collections.singletonList(org));
                loadVDCs(org);
                return org;
            } finally {
                if (wire.isDebugEnabled()) {
                    wire.debug("<<< [POST (" + (new Date()) + ")] -> " + endpoint
                            + " <--------------------------------------------------------------------------------------");
                    wire.debug("");
                }
            }
        } else {
            return it.next();
        }
    }

    private void addAuth(HttpRequestBase method, @Nonnull String token) throws CloudException, InternalException {
        if (matches(getAPIVersion(), "0.8", "0.8")) {
            method.addHeader("Cookie", "vcloud-token=" + token);
        } else {
            method.addHeader("x-vcloud-authorization", token);
        }
    }

    public @Nullable String delete(@Nonnull String resource, @Nonnull String id)
            throws CloudException, InternalException {
        if (logger.isTraceEnabled()) {
            logger.trace("ENTER: " + vCloudMethod.class.getName() + ".delete(" + resource + "," + id + ")");
        }
        try {
            Org org = authenticate(false);
            String endpoint = toURL(resource, id);
            HttpClient client = null;

            if (wire.isDebugEnabled()) {
                wire.debug("");
                wire.debug(">>> [DELETE (" + (new Date()) + ")] -> " + endpoint
                        + " >--------------------------------------------------------------------------------------");
            }
            try {
                client = getClient(false);
                HttpDelete delete = new HttpDelete(endpoint);

                delete.addHeader("Accept", "application/*+xml;version=" + org.version.version
                        + ",application/*+xml;version=" + org.version.version);
                addAuth(delete, org.token);

                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 " + 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) {
                    logger.error("I/O error from server communications: " + e.getMessage());
                    throw new InternalException(e);
                }
                int code = response.getStatusLine().getStatusCode();

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

                if (code == HttpServletResponse.SC_UNAUTHORIZED) {
                    authenticate(true);
                    return delete(resource, id);
                } else if (code != HttpServletResponse.SC_NOT_FOUND && code != HttpServletResponse.SC_NO_CONTENT
                        && code != HttpServletResponse.SC_OK && code != HttpServletResponse.SC_ACCEPTED) {
                    logger.error("DELETE request got unexpected " + code);
                    String xml = null;

                    try {
                        HttpEntity entity = response.getEntity();

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

                    vCloudException.Data data = null;

                    if (xml != null && !xml.equals("")) {
                        Document doc = parseXML(xml);
                        String docElementTagName = doc.getDocumentElement().getTagName();
                        String nsString = "";
                        if (docElementTagName.contains(":"))
                            nsString = docElementTagName.substring(0, docElementTagName.indexOf(":") + 1);
                        NodeList errors = doc.getElementsByTagName(nsString + "Error");

                        if (errors.getLength() > 0) {
                            data = vCloudException.parseException(code, errors.item(0));
                        }
                    }
                    if (data == null) {
                        throw new vCloudException(CloudErrorType.GENERAL, code,
                                response.getStatusLine().getReasonPhrase(), "No further information");
                    }
                    logger.error("[" + code + " : " + data.title + "] " + data.description);
                    throw new vCloudException(data);
                } else {
                    String xml = null;

                    try {
                        HttpEntity entity = response.getEntity();

                        if (entity != null) {
                            xml = EntityUtils.toString(entity);
                            if (wire.isDebugEnabled()) {
                                wire.debug(xml);
                                wire.debug("");
                            }
                        }
                    } catch (IOException e) {
                        logger.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                        throw new CloudException(e);
                    }
                    return xml;
                }
            } finally {
                if (client != null) {
                    client.getConnectionManager().shutdown();
                }
                if (wire.isDebugEnabled()) {
                    wire.debug("<<< [DELETE (" + (new Date()) + ")] -> " + endpoint
                            + " <--------------------------------------------------------------------------------------");
                    wire.debug("");
                }
            }
        } finally {
            if (logger.isTraceEnabled()) {
                logger.trace("EXIT: " + vCloudMethod.class.getName() + ".delete()");
            }

        }
    }

    public @Nullable String get(@Nonnull String resource, @Nullable String id)
            throws CloudException, InternalException {
        if (logger.isTraceEnabled()) {
            logger.trace("ENTER: " + vCloudMethod.class.getName() + ".get(" + resource + "," + id + ")");
        }
        try {
            Org org = authenticate(false);
            String endpoint = toURL(resource, id);
            HttpClient client = null;

            if (wire.isDebugEnabled()) {
                wire.debug("");
                wire.debug(">>> [GET (" + (new Date()) + ")] -> " + endpoint
                        + " >--------------------------------------------------------------------------------------");
            }
            try {
                client = getClient(false);
                HttpGet get = new HttpGet(endpoint);

                get.addHeader("Accept", "application/*+xml;version=" + org.version.version
                        + ",application/*+xml;version=" + org.version.version);

                addAuth(get, org.token);

                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 " + 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) {
                    logger.error("I/O error from server communications: " + e.getMessage());
                    throw new InternalException(e);
                }
                int code = response.getStatusLine().getStatusCode();

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

                if (code == HttpServletResponse.SC_NOT_FOUND || code == HttpServletResponse.SC_FORBIDDEN) {
                    return null;
                } else if (code == HttpServletResponse.SC_UNAUTHORIZED) {
                    if (matches(getAPIVersion(), "1.0", null)) {
                        authenticate(true);
                        return get(resource, id);
                    }
                    return null;
                } else if (code == HttpServletResponse.SC_NO_CONTENT) {
                    return "";
                } else if (code == HttpServletResponse.SC_OK) {
                    String xml = null;

                    try {
                        HttpEntity entity = response.getEntity();

                        if (entity != null) {
                            xml = EntityUtils.toString(entity);
                            if (wire.isDebugEnabled()) {
                                wire.debug(xml);
                                wire.debug("");
                            }
                        }
                    } catch (IOException e) {
                        logger.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                        throw new CloudException(e);
                    }
                    return xml;
                } else {
                    logger.error("Expected OK for GET request, got " + code);
                    String xml = null;

                    try {
                        HttpEntity entity = response.getEntity();

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

                    vCloudException.Data data = null;

                    if (xml != null && !xml.equals("")) {
                        Document doc = parseXML(xml);
                        String docElementTagName = doc.getDocumentElement().getTagName();
                        String nsString = "";
                        if (docElementTagName.contains(":"))
                            nsString = docElementTagName.substring(0, docElementTagName.indexOf(":") + 1);
                        NodeList errors = doc.getElementsByTagName(nsString + "Error");

                        if (errors.getLength() > 0) {
                            data = vCloudException.parseException(code, errors.item(0));
                        }
                    }
                    if (data == null) {
                        throw new vCloudException(CloudErrorType.GENERAL, code,
                                response.getStatusLine().getReasonPhrase(), "No further information");
                    }
                    logger.error("[" + code + " : " + data.title + "] " + data.description);
                    throw new vCloudException(data);
                }
            } finally {
                if (client != null) {
                    client.getConnectionManager().shutdown();
                }

                if (wire.isDebugEnabled()) {
                    wire.debug("<<< [GET (" + (new Date()) + ")] -> " + endpoint
                            + " <--------------------------------------------------------------------------------------");
                    wire.debug("");
                }
            }
        } finally {
            if (logger.isTraceEnabled()) {
                logger.trace("EXIT: " + vCloudMethod.class.getName() + ".get()");
            }

        }
    }

    public @Nonnull String getAction(@Nonnull String endpoint) {
        String[] parts = endpoint.split("/");

        return parts[parts.length - 1];
    }

    public @Nonnull String getAPIVersion() throws CloudException, InternalException {
        return getVersion().version;
    }

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

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

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

        try {
            uri = new URI(endpoint);
            targetPort = uri.getPort();
            if (targetPort < 1) {
                targetPort = (ssl ? 443 : 80);
            }
        } catch (URISyntaxException e) {
            throw new CloudException(e);
        }
        HttpHost targetHost = new HttpHost(uri.getHost(), targetPort, uri.getScheme());
        HttpParams params = new BasicHttpParams();

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

        params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 10000);
        params.setParameter(CoreConnectionPNames.SO_TIMEOUT, 300000);

        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() {

                            public boolean isTrusted(X509Certificate[] x509Certificates, String s)
                                    throws CertificateException {
                                return true;
                            }
                        }, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)));
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }
        if (forAuthentication) {
            String accessPublic = null;
            String accessPrivate = null;
            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) {
                throw new InternalException(e);
            }
            String password = accessPrivate;
            String userName;

            if (matches(getAPIVersion(), "0.8", "0.8")) {
                userName = accessPublic;
            } else {
                userName = accessPublic + "@" + ctx.getAccountNumber();
            }

            client.getCredentialsProvider().setCredentials(
                    new AuthScope(targetHost.getHostName(), targetHost.getPort()),
                    new UsernamePasswordCredentials(userName, password));
        }
        return client;
    }

    public @Nonnull String getMediaTypeForActionAddCatalog() {
        return "application/vnd.vmware.admin.catalog+xml";
    }

    public @Nonnull String getMediaTypeForActionAttachVolume() {
        return "application/vnd.vmware.vcloud.diskAttachOrDetachParams+xml";
    }

    public @Nonnull String getMediaTypeForActionInstantiateVApp() {
        return "application/vnd.vmware.vcloud.instantiateVAppTemplateParams+xml";
    }

    public @Nonnull String getMediaTypeForActionCaptureVApp() {
        return "application/vnd.vmware.vcloud.captureVAppParams+xml";
    }

    public @Nonnull String getMediaTypeForActionComposeVApp() {
        return "application/vnd.vmware.vcloud.composeVAppParams+xml";
    }

    public @Nonnull String getMediaTypeForActionCreateDisk() {
        return "application/vnd.vmware.vcloud.diskCreateParams+xml";
    }

    public @Nonnull String getMediaTypeForActionDeployVApp() {
        return "application/vnd.vmware.vcloud.deployVAppParams+xml";
    }

    public @Nonnull String getMediaTypeForActionUndeployVApp() {
        return "application/vnd.vmware.vcloud.undeployVAppParams+xml";
    }

    public @Nonnull String getMediaTypeForCatalog() {
        return "application/vnd.vmware.vcloud.catalog+xml";
    }

    public @Nonnull String getMediaTypeForCatalogItem() {
        return "application/vnd.vmware.vcloud.catalogItem+xml";
    }

    public @Nonnull String getMediaTypeForDisk() {
        return "application/vnd.vmware.vcloud.disk+xml";
    }

    public @Nonnull String getMediaTypeForGuestCustomizationSection() {
        return "application/vnd.vmware.vcloud.guestCustomizationSection+xml";
    }

    public @Nonnull String getMediaTypeForMetadata() {
        return "application/vnd.vmware.vcloud.metadata+xml";
    }

    public @Nonnull String getMediaTypeForNetworkConnectionSection() {
        return "application/vnd.vmware.vcloud.networkConnectionSection+xml";
    }

    public @Nonnull String getMediaTypeForOrg() {
        return "application/vnd.vmware.vcloud.org+xml";
    }

    public @Nonnull String getMediaTypeForOrgList() {
        return "application/vnd.vmware.vcloud.orgList+xml";
    }

    public @Nonnull String getMediaTypeForRasdItem() {
        return "application/vnd.vmware.vcloud.rasdItem+xml";
    }

    public @Nonnull String getMediaTypeForVApp() {
        return "application/vnd.vmware.vcloud.vApp+xml";
    }

    public @Nonnull String getMediaTypeForVAppTemplate() {
        return "application/vnd.vmware.vcloud.vAppTemplate+xml";
    }

    public @Nonnull String getMediaTypeForVM() {
        return "application/vnd.vmware.vcloud.vm+xml";
    }

    public @Nonnull String getMediaTypeForVDC() {
        return "application/vnd.vmware.vcloud.vdc+xml";
    }

    public int getNetworkQuota() throws CloudException, InternalException {
        int quota = -2;

        for (VDC vdc : authenticate(false).getVdcs()) {
            int q = vdc.networkQuota;

            if (q > -1) {
                if (quota == -2) {
                    quota = q;
                } else {
                    quota += q;
                }
            }
        }
        return quota;
    }

    public @Nonnull String getOrgName(@Nonnull String href) throws CloudException, InternalException {
        String id = provider.toID(href);
        String xml = get("org", id);

        if (xml == null) {
            return id;
        }
        Document doc = parseXML(xml);
        String docElementTagName = doc.getDocumentElement().getTagName();
        String nsString = "";
        if (docElementTagName.contains(":"))
            nsString = docElementTagName.substring(0, docElementTagName.indexOf(":") + 1);
        NodeList orgs = doc.getElementsByTagName(nsString + "Org");

        if (orgs.getLength() < 1) {
            return id;
        }
        Node org = orgs.item(0);

        if (!org.hasAttributes()) {
            return id;
        }

        Node name = org.getAttributes().getNamedItem("name");

        if (name == null) {
            return id;
        }
        return name.getNodeValue().trim();
    }

    public @Nonnull Region getRegion() throws CloudException, InternalException {
        return authenticate(false).region;
    }

    private @Nonnull Version getVersion() throws CloudException, InternalException {
        Cache<Version> cache = Cache.getInstance(provider, "vCloudVersions", Version.class, CacheLevel.CLOUD,
                new TimePeriod<Day>(1, TimePeriod.DAY));

        ProviderContext ctx = provider.getContext();

        if (ctx == null) {
            throw new CloudException("No context was defined for this request");
        }
        {
            Iterable<Version> versions = cache.get(ctx);

            Iterator<Version> it = (versions == null ? null : versions.iterator());

            if (it != null && it.hasNext()) {
                return it.next();
            }
        }
        // TODO: how does vCHS do version discovery?
        if (ctx.getCloud().getEndpoint().startsWith("https://vchs")) {
            // This is a complete hack that needs to be changed to reflect vCHS version discovery
            Version version = new Version();

            version.loginUrl = ctx.getCloud().getEndpoint() + "/api/vchs/sessions";
            version.version = "5.6";
            cache.put(ctx, Collections.singletonList(version));
            return version;
        }
        if (wire.isDebugEnabled()) {
            wire.debug("");
            wire.debug(">>> [GET (" + (new Date()) + ")] -> " + ctx.getCloud().getEndpoint()
                    + " >--------------------------------------------------------------------------------------");
        }
        try {
            final String[] preferred = provider.getVersionPreference();
            HttpClient client = getClient(false);
            HttpGet method = new HttpGet(ctx.getCloud().getEndpoint() + "/api/versions");

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

            try {
                APITrace.trace(provider, "GET versions");
                response = client.execute(method);
                if (wire.isDebugEnabled()) {
                    wire.debug(response.getStatusLine().toString());
                    for (Header header : response.getAllHeaders()) {
                        wire.debug(header.getName() + ": " + header.getValue());
                    }
                    wire.debug("");
                }
                status = response.getStatusLine();
            } catch (IOException e) {
                throw new CloudException(e);
            }
            if (status.getStatusCode() == HttpServletResponse.SC_OK) {
                HttpEntity entity = response.getEntity();
                String body;

                try {
                    body = EntityUtils.toString(entity);
                    if (wire.isDebugEnabled()) {
                        wire.debug(body);
                        wire.debug("");
                    }
                } catch (IOException e) {
                    throw new CloudException(CloudErrorType.GENERAL, status.getStatusCode(),
                            status.getReasonPhrase(), e.getMessage());
                }
                try {
                    ByteArrayInputStream bas = new ByteArrayInputStream(body.getBytes());

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

                    bas.close();

                    NodeList versions = doc.getElementsByTagName("VersionInfo");
                    TreeSet<Version> set = new TreeSet<Version>(new Comparator<Version>() {
                        public int compare(Version version1, Version version2) {
                            if (version1.equals(version2)) {
                                return 0;
                            }
                            if (preferred != null) {
                                for (String v : preferred) {
                                    if (v.equals(version1.version)) {
                                        return -1;
                                    } else if (v.equals(version2.version)) {
                                        return 1;
                                    }
                                }
                            }
                            for (String v : VERSIONS) {
                                if (v.equals(version1.version)) {
                                    return -1;
                                } else if (v.equals(version2.version)) {
                                    return 1;
                                }
                            }
                            return -version1.version.compareTo(version2.version);
                        }
                    });
                    for (int i = 0; i < versions.getLength(); i++) {
                        Node versionInfo = versions.item(i);
                        NodeList vattrs = versionInfo.getChildNodes();
                        String version = null;
                        String url = null;

                        for (int j = 0; j < vattrs.getLength(); j++) {
                            Node attr = vattrs.item(j);

                            if (attr.getNodeName().equalsIgnoreCase("Version") && attr.hasChildNodes()) {
                                version = attr.getFirstChild().getNodeValue().trim();
                            } else if (attr.getNodeName().equalsIgnoreCase("LoginUrl") && attr.hasChildNodes()) {
                                url = attr.getFirstChild().getNodeValue().trim();
                            }
                        }
                        if (version == null || url == null || !isSupported(version)) {

                            continue;
                        }
                        Version v = new Version();
                        v.version = version;
                        v.loginUrl = url;
                        set.add(v);
                    }
                    if (set.isEmpty()) {
                        throw new CloudException("Unable to identify a supported version");
                    }
                    Version v = set.iterator().next();

                    cache.put(ctx, set);
                    return v;
                } catch (IOException e) {
                    throw new CloudException(CloudErrorType.GENERAL, status.getStatusCode(),
                            status.getReasonPhrase(), e.getMessage());
                } catch (ParserConfigurationException e) {
                    throw new CloudException(CloudErrorType.GENERAL, status.getStatusCode(),
                            status.getReasonPhrase(), e.getMessage());
                } catch (SAXException e) {
                    throw new CloudException(CloudErrorType.GENERAL, status.getStatusCode(),
                            status.getReasonPhrase(), e.getMessage());
                }
            } else {

                logger.error("Expected OK for GET request, got " + status.getStatusCode());
                String xml = null;

                try {
                    HttpEntity entity = response.getEntity();

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

                vCloudException.Data data = null;

                if (xml != null && !xml.equals("")) {
                    Document doc = parseXML(xml);
                    String docElementTagName = doc.getDocumentElement().getTagName();
                    String nsString = "";
                    if (docElementTagName.contains(":"))
                        nsString = docElementTagName.substring(0, docElementTagName.indexOf(":") + 1);
                    NodeList errors = doc.getElementsByTagName(nsString + "Error");

                    if (errors.getLength() > 0) {
                        data = vCloudException.parseException(status.getStatusCode(), errors.item(0));
                    }
                }
                if (data == null) {
                    throw new vCloudException(CloudErrorType.GENERAL, status.getStatusCode(),
                            response.getStatusLine().getReasonPhrase(), "No further information");
                }
                logger.error("[" + status.getStatusCode() + " : " + data.title + "] " + data.description);
                throw new vCloudException(data);
            }
        } finally {
            if (wire.isDebugEnabled()) {
                wire.debug("<<< [GET (" + (new Date()) + ")] -> " + ctx.getEndpoint()
                        + " <--------------------------------------------------------------------------------------");
                wire.debug("");
            }
        }
    }

    public int getVMQuota() throws CloudException, InternalException {
        int quota = -2;

        for (VDC vdc : authenticate(false).getVdcs()) {
            int q = vdc.vmQuota;

            if (q > -1) {
                if (quota == -2) {
                    quota = q;
                } else {
                    quota += q;
                }
            }
        }
        return quota;
    }

    public Collection<DataCenter> listDataCenters() throws CloudException, InternalException {
        ArrayList<DataCenter> dcs = new ArrayList<DataCenter>();

        for (VDC vdc : authenticate(false).getVdcs()) {
            dcs.add(vdc.dataCenter);
        }
        return dcs;
    }

    private void loadVDC(@Nonnull VDC vdc, @Nonnull String id) throws CloudException, InternalException {
        String xml = get("vdc", id);

        if (xml != null) {
            Document doc = parseXML(xml);
            String docElementTagName = doc.getDocumentElement().getTagName();
            String nsString = "";
            if (docElementTagName.contains(":"))
                nsString = docElementTagName.substring(0, docElementTagName.indexOf(":") + 1);
            NodeList vdcs = doc.getElementsByTagName(nsString + "Vdc");

            if (vdcs.getLength() < 1) {
                return;
            }
            NodeList attributes = vdcs.item(0).getChildNodes();

            for (int i = 0; i < attributes.getLength(); i++) {
                Node attribute = attributes.item(i);
                if (attribute.getNodeName().contains(":"))
                    nsString = attribute.getNodeName().substring(0, attribute.getNodeName().indexOf(":") + 1);
                else
                    nsString = "";

                if (attribute.getNodeName().equalsIgnoreCase(nsString + "Link") && attribute.hasAttributes()) {
                    Node rel = attribute.getAttributes().getNamedItem("rel");

                    if (rel.getNodeValue().trim().equalsIgnoreCase("add")) {
                        Node type = attribute.getAttributes().getNamedItem("type");
                        Node href = attribute.getAttributes().getNamedItem("href");

                        if (type != null && href != null) {
                            vdc.actions.put(type.getNodeValue().trim(), href.getNodeValue().trim());
                        }
                    }
                } else if (attribute.getNodeName().equalsIgnoreCase(nsString + "VmQuota")
                        && attribute.hasChildNodes()) {
                    try {
                        vdc.vmQuota = Integer.parseInt(attribute.getFirstChild().getNodeValue().trim());
                    } catch (NumberFormatException ignore) {
                        // ignore
                    }
                } else if (attribute.getNodeName().equalsIgnoreCase(nsString + "NetworkQuota")
                        && attribute.hasChildNodes()) {
                    try {
                        vdc.networkQuota = Integer.parseInt(attribute.getFirstChild().getNodeValue().trim());
                    } catch (NumberFormatException ignore) {
                        // ignore
                    }
                } else if (attribute.getNodeName().equalsIgnoreCase(nsString + "IsEnabled")
                        && attribute.hasChildNodes()) {
                    boolean enabled = attribute.getFirstChild().getNodeValue().trim().equalsIgnoreCase("true");

                    if (!enabled) {
                        vdc.dataCenter.setActive(false);
                        vdc.dataCenter.setAvailable(false);
                    }
                }
            }
        }
    }

    private void loadVDCs(@Nonnull Org org) throws CloudException, InternalException {
        if (wire.isDebugEnabled()) {
            wire.debug("");
            wire.debug(">>> [GET (" + (new Date()) + ")] -> " + org.url
                    + " >--------------------------------------------------------------------------------------");
        }
        try {
            HttpClient client = getClient(false);
            HttpGet method = new HttpGet(org.url);

            method.addHeader("Accept", "application/*+xml;version=" + org.version.version
                    + ",application/*+xml;version=" + org.version.version);

            addAuth(method, org.token);

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

            try {
                APITrace.trace(provider, "GET org");
                response = client.execute(method);
                if (wire.isDebugEnabled()) {
                    wire.debug(response.getStatusLine().toString());
                    for (Header header : response.getAllHeaders()) {
                        wire.debug(header.getName() + ": " + header.getValue());
                    }
                    wire.debug("");
                }
                status = response.getStatusLine();
            } catch (IOException e) {
                throw new CloudException(e);
            }
            if (status.getStatusCode() == HttpServletResponse.SC_OK) {
                HttpEntity entity = response.getEntity();
                String body;

                try {
                    body = EntityUtils.toString(entity);
                    if (wire.isDebugEnabled()) {
                        wire.debug(body);
                        wire.debug("");
                    }
                } catch (IOException e) {
                    throw new CloudException(CloudErrorType.GENERAL, status.getStatusCode(),
                            status.getReasonPhrase(), e.getMessage());
                }
                try {
                    ByteArrayInputStream bas = new ByteArrayInputStream(body.getBytes());

                    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                    DocumentBuilder parser = factory.newDocumentBuilder();
                    ArrayList<VDC> vdcs = new ArrayList<VDC>();
                    Document doc = parser.parse(bas);
                    bas.close();

                    NodeList links = doc.getElementsByTagName("Link");

                    for (int i = 0; i < links.getLength(); i++) {
                        Node link = links.item(i);

                        if (link.hasAttributes()) {
                            Node type = link.getAttributes().getNamedItem("type");

                            if (type != null
                                    && type.getNodeValue().trim().equals("application/vnd.vmware.vcloud.vdc+xml")) {
                                Node name = link.getAttributes().getNamedItem("name");

                                if (name != null) {
                                    DataCenter dc = new DataCenter();
                                    VDC vdc = new VDC();

                                    vdc.actions = new HashMap<String, String>();
                                    dc.setActive(true);
                                    dc.setAvailable(true);
                                    dc.setName(name.getNodeValue().trim());
                                    dc.setRegionId(org.region.getProviderRegionId());
                                    Node href = link.getAttributes().getNamedItem("href");

                                    if (href != null) {
                                        String id = provider.toID(href.getNodeValue().trim());

                                        dc.setProviderDataCenterId(id);
                                        vdc.dataCenter = dc;
                                        loadVDC(vdc, id);
                                        vdcs.add(vdc);
                                    }
                                }
                            }
                        }
                    }
                    org.setVdcs(vdcs);
                } catch (IOException e) {
                    throw new CloudException(CloudErrorType.GENERAL, status.getStatusCode(),
                            status.getReasonPhrase(), e.getMessage());
                } catch (ParserConfigurationException e) {
                    throw new CloudException(CloudErrorType.GENERAL, status.getStatusCode(),
                            status.getReasonPhrase(), e.getMessage());
                } catch (SAXException e) {
                    throw new CloudException(CloudErrorType.GENERAL, status.getStatusCode(),
                            status.getReasonPhrase(), e.getMessage());
                }
            } else {
                logger.error("Expected OK for GET request, got " + status.getStatusCode());
                String xml = null;

                try {
                    HttpEntity entity = response.getEntity();

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

                vCloudException.Data data = null;

                if (xml != null && !xml.equals("")) {
                    Document doc = parseXML(xml);
                    String docElementTagName = doc.getDocumentElement().getTagName();
                    String nsString = "";
                    if (docElementTagName.contains(":"))
                        nsString = docElementTagName.substring(0, docElementTagName.indexOf(":") + 1);
                    NodeList errors = doc.getElementsByTagName(nsString + "Error");

                    if (errors.getLength() > 0) {
                        data = vCloudException.parseException(status.getStatusCode(), errors.item(0));
                    }
                }
                if (data == null) {
                    throw new vCloudException(CloudErrorType.GENERAL, status.getStatusCode(),
                            response.getStatusLine().getReasonPhrase(), "No further information");
                }
                logger.error("[" + status.getStatusCode() + " : " + data.title + "] " + data.description);
                throw new vCloudException(data);
            }
        } finally {
            if (wire.isDebugEnabled()) {
                wire.debug("<<< [GET (" + (new Date()) + ")] -> " + org.url
                        + " <--------------------------------------------------------------------------------------");
                wire.debug("");
            }
        }
    }

    public void parseError(@Nonnull Node errorNode) throws CloudException {
        NodeList attributes = errorNode.getChildNodes();
        CloudErrorType type = CloudErrorType.GENERAL;
        String message = "Unknown";
        String major = "";
        String minor = "";

        Node n = errorNode.getAttributes().getNamedItem("minorErrorCode");

        if (n != null) {
            minor = n.getNodeValue().trim();
        }
        n = errorNode.getAttributes().getNamedItem("majorErrorCode");

        if (n != null) {
            major = n.getNodeValue().trim();
        }
        n = errorNode.getAttributes().getNamedItem("message");

        if (n != null) {
            message = n.getNodeValue().trim();
        }
        for (int i = 0; i < attributes.getLength(); i++) {
            Node attr = attributes.item(i);

            if (attr.getNodeName().equalsIgnoreCase("message") && attr.hasChildNodes()) {
                message = attr.getFirstChild().getNodeValue().trim();
            } else if (attr.getNodeName().equalsIgnoreCase("majorErrorCode") && attr.hasChildNodes()) {
                major = attr.getFirstChild().getNodeValue().trim();
            } else if (attr.getNodeName().equalsIgnoreCase("minorErrorCode") && attr.hasChildNodes()) {
                minor = attr.getFirstChild().getNodeValue().trim();
            }
        }
        throw new CloudException(type, 200, major + ":" + minor, message);
    }

    public void parseMetaData(@Nonnull Taggable resource, @Nonnull String xml)
            throws CloudException, InternalException {
        Document doc = parseXML(xml);
        String docElementTagName = doc.getDocumentElement().getTagName();
        String nsString = "";
        if (docElementTagName.contains(":"))
            nsString = docElementTagName.substring(0, docElementTagName.indexOf(":") + 1);
        NodeList md = doc.getElementsByTagName(nsString + "MetadataEntry");

        for (int i = 0; i < md.getLength(); i++) {
            Node entry = md.item(i);

            if (entry.hasChildNodes()) {
                NodeList parts = entry.getChildNodes();
                String key = null, value = null;

                for (int j = 0; j < parts.getLength(); j++) {
                    Node part = parts.item(j);
                    if (part.getNodeName().contains(":"))
                        nsString = part.getNodeName().substring(0, part.getNodeName().indexOf(":") + 1);
                    else
                        nsString = "";

                    if (part.getNodeName().equalsIgnoreCase(nsString + "Key") && part.hasChildNodes()) {
                        key = part.getFirstChild().getNodeValue().trim();
                    } else if (part.getNodeName().equalsIgnoreCase(nsString + "TypedValue")
                            && part.hasChildNodes()) {
                        NodeList values = part.getChildNodes();

                        for (int k = 0; k < values.getLength(); k++) {
                            Node v = values.item(k);

                            if (v.getNodeName().equalsIgnoreCase(nsString + "Value") && v.hasChildNodes()) {
                                value = v.getFirstChild().getNodeValue().trim();
                            }
                        }
                    } else if (part.getNodeName().equalsIgnoreCase(nsString + "Value") && part.hasChildNodes()) {
                        value = part.getFirstChild().getNodeValue().trim();
                    }
                }
                if (key != null && value != null) {
                    resource.setTag(key, value);
                }
            }
        }
    }

    public @Nonnull Document parseXML(@Nonnull String xml) throws CloudException, InternalException {
        try {
            ByteArrayInputStream bas = new ByteArrayInputStream(xml.getBytes());

            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder parser = factory.newDocumentBuilder();

            return parser.parse(bas);
        } catch (ParserConfigurationException e) {
            throw new InternalException(e);
        } catch (SAXException e) {
            throw new CloudException(e);
        } catch (IOException e) {
            throw new InternalException(e);
        }
    }

    public @Nonnull String post(@Nonnull String action, @Nullable String vdcId, @Nullable String payload)
            throws CloudException, InternalException {
        if (logger.isTraceEnabled()) {
            logger.trace("ENTER: " + vCloudMethod.class.getName() + ".post(" + action + ")");
        }
        try {
            Org org = authenticate(false);
            String endpoint;
            VDC vdc = null;

            for (VDC v : org.getVdcs()) {
                if (vdcId == null) {
                    vdc = v;
                    break;
                } else if (v.dataCenter.getProviderDataCenterId().equals(vdcId)) {
                    vdc = v;
                    break;
                } else if (vdc == null) {
                    vdc = v;
                }
            }
            if (vdc == null) {
                throw new CloudException("No VDC was identified for this request (requested " + vdcId + ")");
            }
            String contentType;

            if (action.equals(INSTANTIATE_VAPP)) {
                contentType = getMediaTypeForActionInstantiateVApp();
                endpoint = vdc.actions.get(contentType);
            } else if (action.equals(COMPOSE_VAPP)) {
                contentType = getMediaTypeForActionComposeVApp();
                endpoint = vdc.actions.get(contentType);
            } else if (action.equals(CAPTURE_VAPP)) {
                contentType = getMediaTypeForActionCaptureVApp();
                endpoint = vdc.actions.get(contentType);
            } else if (action.equals(CREATE_DISK)) {
                contentType = getMediaTypeForActionCreateDisk();
                endpoint = vdc.actions.get(contentType);
            } else {
                throw new CloudException("Unknown content type for post");
            }
            if (endpoint == null) {
                throw new CloudException("No endpoint for " + action);
            }
            return post(action, endpoint, contentType, payload);
        } finally {
            if (logger.isTraceEnabled()) {
                logger.trace("EXIT: " + vCloudMethod.class.getName() + ".post()");
            }
        }
    }

    public @Nonnull String post(@Nonnull String action, @Nonnull String endpoint, @Nullable String contentType,
            @Nullable String payload) throws CloudException, InternalException {
        if (logger.isTraceEnabled()) {
            logger.trace("ENTER: " + vCloudMethod.class.getName() + ".post(" + endpoint + ")");
        }
        try {
            HttpClient client = null;
            if (wire.isDebugEnabled()) {
                wire.debug("");
                wire.debug(">>> [POST (" + (new Date()) + ")] -> " + endpoint
                        + " >--------------------------------------------------------------------------------------");
            }
            try {
                Org org = authenticate(false);
                client = getClient(false);
                HttpPost post = new HttpPost(endpoint);

                post.addHeader("Accept", "application/*+xml;version=" + org.version.version
                        + ",application/*+xml;version=" + org.version.version);
                addAuth(post, org.token);

                if (contentType != null) {
                    post.addHeader("Content-Type", contentType);
                }

                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 {
                    APITrace.trace(provider, "POST " + action);
                    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) {
                    logger.error("I/O error from server communications: " + e.getMessage());
                    throw new InternalException(e);
                }
                int code = response.getStatusLine().getStatusCode();

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

                if (code == HttpServletResponse.SC_NOT_FOUND) {
                    throw new CloudException("No action match for " + endpoint);
                } else if (code == HttpServletResponse.SC_UNAUTHORIZED) {
                    authenticate(true);
                    return post(action, endpoint, contentType, payload);
                } else if (code == HttpServletResponse.SC_NO_CONTENT) {
                    return "";
                } else if (code == HttpServletResponse.SC_OK || code == HttpServletResponse.SC_CREATED
                        || code == HttpServletResponse.SC_ACCEPTED) {
                    String xml = null;

                    try {
                        HttpEntity entity = response.getEntity();

                        if (entity != null) {
                            xml = EntityUtils.toString(entity);
                            if (wire.isDebugEnabled()) {
                                wire.debug(xml);
                                wire.debug("");
                            }
                        }
                    } catch (IOException e) {
                        logger.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                        throw new CloudException(e);
                    }
                    return xml;
                } else {
                    logger.error("Expected OK or CREATED or NO_CONTENT or ACCEPTED for POST request, got " + code);
                    String xml = null;

                    try {
                        HttpEntity entity = response.getEntity();

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

                    vCloudException.Data data = null;

                    if (xml != null && !xml.equals("")) {
                        Document doc = parseXML(xml);
                        String docElementTagName = doc.getDocumentElement().getTagName();
                        String nsString = "";
                        if (docElementTagName.contains(":"))
                            nsString = docElementTagName.substring(0, docElementTagName.indexOf(":") + 1);
                        NodeList errors = doc.getElementsByTagName(nsString + "Error");

                        if (errors.getLength() > 0) {
                            data = vCloudException.parseException(code, errors.item(0));
                        }
                    }
                    if (data == null) {
                        throw new vCloudException(CloudErrorType.GENERAL, code,
                                response.getStatusLine().getReasonPhrase(), "No further information");
                    }
                    logger.error("[" + code + " : " + data.title + "] " + data.description);
                    throw new vCloudException(data);
                }
            } finally {
                if (client != null) {
                    client.getConnectionManager().shutdown();
                }
                if (wire.isDebugEnabled()) {
                    wire.debug("<<< [POST (" + (new Date()) + ")] -> " + endpoint
                            + " <--------------------------------------------------------------------------------------");
                    wire.debug("");
                }
            }
        } finally {
            if (logger.isTraceEnabled()) {
                logger.trace("EXIT: " + vCloudMethod.class.getName() + ".post()");
            }
        }
    }

    public void postMetaData(@Nonnull String resource, @Nonnull String id, @Nonnull Map<String, Object> metadata)
            throws CloudException, InternalException {
        String apiVersion = getAPIVersion();
        StringBuilder xml = new StringBuilder();

        xml.append("<Metadata xmlns=\"http://www.vmware.com/vcloud/v1.5\" ");
        xml.append("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">");
        for (Map.Entry<String, Object> entry : metadata.entrySet()) {
            Object value = entry.getValue();

            if (value != null) {
                xml.append("<MetadataEntry>");
                xml.append("<Key>").append(vCloud.escapeXml(entry.getKey())).append("</Key>");
                if (vCloudMethod.matches(apiVersion, "5.1", null)) {
                    xml.append("<TypedValue xsi:type=\"MetadataStringValue\">");
                }
                xml.append("<Value>").append(vCloud.escapeXml(value.toString())).append("</Value>");
                if (vCloudMethod.matches(apiVersion, "5.1", null)) {
                    xml.append("</TypedValue>");
                }
                xml.append("</MetadataEntry>");
            }
        }
        xml.append("</Metadata>");
        post("metaData", toURL(resource, id) + "/metadata", getMediaTypeForMetadata(), xml.toString());
    }

    public @Nonnull String put(@Nonnull String action, @Nonnull String endpoint, @Nullable String contentType,
            @Nullable String payload) throws CloudException, InternalException {
        if (logger.isTraceEnabled()) {
            logger.trace("ENTER: " + vCloudMethod.class.getName() + ".put(" + endpoint + ")");
        }
        try {
            HttpClient client = null;
            if (wire.isDebugEnabled()) {
                wire.debug("");
                wire.debug(">>> [PUT (" + (new Date()) + ")] -> " + endpoint
                        + " >--------------------------------------------------------------------------------------");
            }
            try {
                Org org = authenticate(false);
                client = getClient(false);
                HttpPut put = new HttpPut(endpoint);

                put.addHeader("Accept", "application/*+xml;version=" + org.version.version
                        + ",application/*+xml;version=" + org.version.version);

                addAuth(put, org.token);

                if (contentType != null) {
                    put.addHeader("Content-Type", contentType);
                }

                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 " + action);
                    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) {
                    logger.error("I/O error from server communications: " + e.getMessage());
                    throw new InternalException(e);
                }
                int code = response.getStatusLine().getStatusCode();

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

                if (code == HttpServletResponse.SC_NOT_FOUND) {
                    throw new CloudException("No action match for " + endpoint);
                } else if (code == HttpServletResponse.SC_UNAUTHORIZED) {
                    authenticate(true);
                    return post(action, endpoint, contentType, payload);
                } else if (code == HttpServletResponse.SC_NO_CONTENT) {
                    return "";
                } else if (code == HttpServletResponse.SC_OK || code == HttpServletResponse.SC_CREATED
                        || code == HttpServletResponse.SC_ACCEPTED) {
                    String xml = null;

                    try {
                        HttpEntity entity = response.getEntity();

                        if (entity != null) {
                            xml = EntityUtils.toString(entity);
                            if (wire.isDebugEnabled()) {
                                wire.debug(xml);
                                wire.debug("");
                            }
                        }
                    } catch (IOException e) {
                        logger.error("Failed to read response error due to a cloud I/O error: " + e.getMessage());
                        throw new CloudException(e);
                    }
                    return xml;
                } else {
                    logger.error("Expected OK or CREATED or NO_CONTENT or ACCEPTED for POST request, got " + code);
                    String xml = null;

                    try {
                        HttpEntity entity = response.getEntity();

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

                    vCloudException.Data data = null;

                    if (xml != null && !xml.equals("")) {
                        Document doc = parseXML(xml);
                        String docElementTagName = doc.getDocumentElement().getTagName();
                        String nsString = "";
                        if (docElementTagName.contains(":"))
                            nsString = docElementTagName.substring(0, docElementTagName.indexOf(":") + 1);
                        NodeList errors = doc.getElementsByTagName(nsString + "Error");

                        if (errors.getLength() > 0) {
                            data = vCloudException.parseException(code, errors.item(0));
                        }
                    }
                    if (data == null) {
                        throw new vCloudException(CloudErrorType.GENERAL, code,
                                response.getStatusLine().getReasonPhrase(), "No further information");
                    }
                    logger.error("[" + code + " : " + data.title + "] " + data.description);
                    throw new vCloudException(data);
                }
            } finally {
                if (client != null) {
                    client.getConnectionManager().shutdown();
                }
                if (wire.isDebugEnabled()) {
                    wire.debug("<<< [PUT (" + (new Date()) + ")] -> " + endpoint
                            + " <--------------------------------------------------------------------------------------");
                    wire.debug("");
                }
            }
        } finally {
            if (logger.isTraceEnabled()) {
                logger.trace("EXIT: " + vCloudMethod.class.getName() + ".put()");
            }
        }
    }

    public @Nonnull String toAdminURL(@Nonnull String resource, @Nullable String id)
            throws CloudException, InternalException {
        Org org = authenticate(false);
        String url;

        if (id == null) {
            if (matches(org.version.version, "1.5", null)) {
                url = org.endpoint + "/api/admin/" + resource;
            } else {
                url = org.endpoint + "/api/v" + org.version.version + "/admin/" + resource;
            }
        } else {
            String r = (provider.isCompat() ? id : ("/" + resource + "/" + id));

            if (matches(org.version.version, "1.5", null)) {
                url = org.endpoint + "/api/admin" + r;
            } else {
                url = org.endpoint + "/api/v" + org.version.version + "/admin" + r;
            }
        }
        return url;
    }

    public @Nonnull String toURL(@Nonnull String resource, @Nullable String id)
            throws CloudException, InternalException {
        Org org = authenticate(false);
        String url;

        if (id == null) {
            if (matches(org.version.version, "1.5", null)) {
                url = org.endpoint + "/api/" + resource;
            } else {
                url = org.endpoint + "/" + resource;
            }
        } else {
            String r = (provider.isCompat() ? id : ("/" + resource + "/" + id));

            if (matches(org.version.version, "1.5", null)) {
                url = org.endpoint + "/api" + r;
            } else {
                url = org.endpoint + r;
            }
        }
        return url;
    }

    public void waitFor(@Nullable String xmlTask) throws CloudException {
        long timeout = System.currentTimeMillis() + (CalendarWrapper.MINUTE * 30L);
        String taskId = null;

        int passCount = 1;
        while (timeout > System.currentTimeMillis()) {
            if (xmlTask == null || xmlTask.equals("")) {
                return;
            }
            NodeList tasks;

            try {
                Document doc = parseXML(xmlTask);
                String docElementTagName = doc.getDocumentElement().getTagName();
                String nsString = "";
                if (docElementTagName.contains(":"))
                    nsString = docElementTagName.substring(0, docElementTagName.indexOf(":") + 1);
                tasks = doc.getElementsByTagName(nsString + "Task");
            } catch (Throwable ignore) {
                return;
            }
            if (tasks.getLength() < 1) {
                return;
            }
            Node task = tasks.item(0);

            if (task.hasAttributes()) {
                Node status = task.getAttributes().getNamedItem("status");

                if (status != null) {
                    String s = status.getNodeValue().trim();

                    if (s.equals("success")) {
                        return;
                    } else if (s.equals("error")) {
                        NodeList elements = task.getChildNodes();

                        for (int i = 0; i < elements.getLength(); i++) {
                            Node element = elements.item(i);

                            if (element.getNodeName().equalsIgnoreCase("Error")) {
                                parseError(element);
                                return;
                            }
                        }
                    }
                }
                if (taskId == null) {
                    Node href = task.getAttributes().getNamedItem("href");

                    if (href == null) {
                        return;
                    }
                    taskId = provider.toID(href.getNodeValue().trim());
                }
            }
            try {
                if (passCount > 10) {
                    Thread.sleep(10 * CalendarWrapper.SECOND);
                } else {
                    Thread.sleep(passCount * CalendarWrapper.SECOND);
                }
            } catch (InterruptedException ignore) {
            }
            try {
                xmlTask = get("task", taskId);
            } catch (Throwable ignore) {
            }
            passCount += 1;
        }
        logger.warn("Task timed out: " + taskId);
    }
}