Java tutorial
// 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); } }