com.cloud.stack.CloudStackClient.java Source code

Java tutorial

Introduction

Here is the source code for com.cloud.stack.CloudStackClient.java

Source

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.stack;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;

import org.apache.log4j.Logger;

import com.cloud.bridge.util.JsonAccessor;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;

/**
 * CloudStackClient implements a simple CloudStack client object, it can be used to execute CloudStack commands 
 * with JSON response
 * 
 */
public class CloudStackClient {
    protected final static Logger logger = Logger.getLogger(CloudStackClient.class);

    private String _serviceUrl;

    private long _pollIntervalMs = 2000; // 1 second polling interval
    private long _pollTimeoutMs = 600000; // 10 minutes polling timeout

    public CloudStackClient(String serviceRootUrl) {
        assert (serviceRootUrl != null);

        if (!serviceRootUrl.endsWith("/"))
            _serviceUrl = serviceRootUrl + "/api?";
        else
            _serviceUrl = serviceRootUrl + "api?";
    }

    public CloudStackClient(String cloudStackServiceHost, int port, boolean bSslEnabled) {
        StringBuffer sb = new StringBuffer();
        if (!bSslEnabled) {
            sb.append("http://" + cloudStackServiceHost);
            if (port != 80)
                sb.append(":").append(port);
        } else {
            sb.append("https://" + cloudStackServiceHost);
            if (port != 443)
                sb.append(":").append(port);
        }

        //
        // If the CloudStack root context path has been from /client to some other name
        // use the first constructor instead
        //
        sb.append("/client/api");
        sb.append("?");
        _serviceUrl = sb.toString();
    }

    public CloudStackClient setPollInterval(long intervalMs) {
        _pollIntervalMs = intervalMs;
        return this;
    }

    public CloudStackClient setPollTimeout(long pollTimeoutMs) {
        _pollTimeoutMs = pollTimeoutMs;
        return this;
    }

    public <T> T call(CloudStackCommand cmd, String apiKey, String secretKey, boolean followToAsyncResult,
            String responseName, String responseObjName, Class<T> responseClz) throws Exception {

        assert (responseName != null);

        JsonAccessor json = execute(cmd, apiKey, secretKey);
        if (followToAsyncResult && json.tryEval(responseName + ".jobid") != null) {
            long startMs = System.currentTimeMillis();
            while (System.currentTimeMillis() - startMs < _pollTimeoutMs) {
                CloudStackCommand queryJobCmd = new CloudStackCommand("queryAsyncJobResult");
                queryJobCmd.setParam("jobId", json.getAsString(responseName + ".jobid"));

                JsonAccessor queryAsyncJobResponse = execute(queryJobCmd, apiKey, secretKey);

                if (queryAsyncJobResponse.tryEval("queryasyncjobresultresponse") != null) {
                    int jobStatus = queryAsyncJobResponse.getAsInt("queryasyncjobresultresponse.jobstatus");
                    switch (jobStatus) {
                    case 2:
                        throw new Exception(
                                queryAsyncJobResponse.getAsString("queryasyncjobresultresponse.jobresult.errorcode")
                                        + " " + queryAsyncJobResponse
                                                .getAsString("queryasyncjobresultresponse.jobresult.errortext"));

                    case 0:
                        try {
                            Thread.sleep(_pollIntervalMs);
                        } catch (Exception e) {
                        }
                        break;

                    case 1:
                        if (responseObjName != null)
                            return (T) (new Gson()).fromJson(
                                    queryAsyncJobResponse
                                            .eval("queryasyncjobresultresponse.jobresult." + responseObjName),
                                    responseClz);
                        else
                            return (T) (new Gson()).fromJson(
                                    queryAsyncJobResponse.eval("queryasyncjobresultresponse.jobresult"),
                                    responseClz);

                    default:
                        assert (false);
                        throw new Exception("Operation failed - invalid job status response");
                    }
                } else {
                    throw new Exception("Operation failed - invalid JSON response");
                }
            }

            throw new Exception("Operation failed - async-job query timed out");
        } else {
            if (responseObjName != null)
                return (T) (new Gson()).fromJson(json.eval(responseName + "." + responseObjName), responseClz);
            else
                return (T) (new Gson()).fromJson(json.eval(responseName), responseClz);
        }
    }

    // collectionType example :  new TypeToken<List<String>>() {}.getType();
    public <T> List<T> listCall(CloudStackCommand cmd, String apiKey, String secretKey, String responseName,
            String responseObjName, Type collectionType) throws Exception {

        assert (responseName != null);

        JsonAccessor json = execute(cmd, apiKey, secretKey);

        if (responseObjName != null)
            try {
                return (new Gson()).fromJson(json.eval(responseName + "." + responseObjName), collectionType);
            } catch (Exception e) {
                // this happens because responseObjName won't exist if there are no objects in the list.
                logger.debug("CloudSatck API response doesn't contain responseObjName:" + responseObjName
                        + " because response is empty");
                return null;
            }
        return (new Gson()).fromJson(json.eval(responseName), collectionType);
    }

    public JsonAccessor execute(CloudStackCommand cmd, String apiKey, String secretKey) throws Exception {
        JsonParser parser = new JsonParser();
        URL url = new URL(_serviceUrl + cmd.signCommand(apiKey, secretKey));

        if (logger.isDebugEnabled())
            logger.debug("Cloud API call + [" + url.toString() + "]");

        URLConnection connect = url.openConnection();

        int statusCode;
        statusCode = ((HttpURLConnection) connect).getResponseCode();
        if (statusCode >= 400) {
            logger.error("Cloud API call + [" + url.toString() + "] failed with status code: " + statusCode);
            String errorMessage = ((HttpURLConnection) connect).getResponseMessage();
            if (errorMessage == null) {
                errorMessage = connect.getHeaderField("X-Description");
            }

            if (errorMessage == null) {
                errorMessage = "CloudStack API call HTTP response error, HTTP status code: " + statusCode;
            }

            throw new IOException(errorMessage);
        }

        InputStream inputStream = connect.getInputStream();
        JsonElement jsonElement = parser.parse(new InputStreamReader(inputStream));
        if (jsonElement == null) {
            logger.error(
                    "Cloud API call + [" + url.toString() + "] failed: unable to parse expected JSON response");

            throw new IOException("CloudStack API call error : invalid JSON response");
        }

        if (logger.isDebugEnabled())
            logger.debug("Cloud API call + [" + url.toString() + "] returned: " + jsonElement.toString());
        return new JsonAccessor(jsonElement);
    }
}