Java tutorial
/** * Copyright (C) 2012-2013 enStratus Networks 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.gogrid; 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.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.conn.params.ConnRoutePNames; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpParams; import org.apache.http.params.HttpProtocolParams; import org.apache.http.protocol.HTTP; import org.apache.http.util.EntityUtils; import org.apache.log4j.Logger; import org.dasein.cloud.CloudException; import org.dasein.cloud.InternalException; import org.dasein.cloud.ProviderContext; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.Date; import java.util.Properties; /** * Represents the interaction point between Dasein Cloud and the underlying REST API. * <p>Created by George Reese: 10/13/12 3:31 PM</p> * @author George Reese * @version 2012.09 initial version * @since 2012.09 */ public class GoGridMethod { static public class Param { private String key; private String value; public Param(@Nonnull String key, @Nonnull String value) throws InternalException { this.key = key; try { this.value = URLEncoder.encode(value, "utf-8"); } catch (UnsupportedEncodingException e) { logger.error("UTF-8 unsupported: " + e.getMessage()); e.printStackTrace(); throw new InternalException(e); } } public @Nonnull String getKey() { return key; } public @Nonnull String getValue() { return value; } } static public final String IMAGE_DELETE = "/api/grid/image/delete"; static public final String IMAGE_EDIT = "/api/grid/image/edit"; static public final String IMAGE_GET = "/api/grid/image/get"; static public final String IMAGE_LIST = "/api/grid/image/list"; static public final String IMAGE_SAVE = "/api/grid/image/save"; static public final String IP_LIST = "/api/grid/ip/list"; static public final String LB_ADD = "/api/grid/loadbalancer/add"; static public final String LB_DELETE = "/api/grid/loadbalancer/delete"; static public final String LB_EDIT = "/api/grid/loadbalancer/edit"; static public final String LB_GET = "/api/grid/loadbalancer/get"; static public final String LB_LIST = "/api/grid/loadbalancer/list"; static public final String LOOKUP_LIST = "/api/common/lookup/list"; static public final String SERVER_ADD = "/api/grid/server/add"; static public final String SERVER_DELETE = "/api/grid/server/delete"; static public final String SERVER_GET = "/api/grid/server/get"; static public final String SERVER_LIST = "/api/grid/server/list"; static public final String SERVER_POWER = "/api/grid/server/power"; static public final String VERSION = "1.9"; static private final Logger logger = GoGrid.getLogger(GoGridMethod.class); static private final Logger wire = GoGrid.getWireLogger(GoGridMethod.class); /** * {"summary":{"total":24,"start":0,"numpages":0,"returned":24},"status":"success","method":"/common/lookup/list", * "list":[{"id":1,"description":"Load Balancer Types","name":"loadbalancer.type","object":"option"}, * {"id":2,"description":"Load Balancer Persistence Types","name":"loadbalancer.persistence","object":"option"}, * {"id":3,"description":"Load Balancer States","name":"loadbalancer.state","object":"option"}, * {"id":4,"description":"Load Balancer OS's","name":"loadbalancer.os","object":"option"}, * {"id":5,"description":"Server Types","name":"server.type","object":"option"}, * {"id":6,"description":"Server Ram","name":"server.ram","object":"option"}, * {"id":7,"description":"Server States","name":"server.state","object":"option"}, * {"id":8,"description":"Server OS's","name":"server.os","object":"option"}, * {"id":9,"description":"IP Address Types","name":"ip.type","object":"option"}, * {"id":10,"description":"IP Address States","name":"ip.state","object":"option"}, * {"id":11,"description":"Job States","name":"job.state","object":"option"}, * {"id":12,"description":"Job Commands","name":"job.command","object":"option"}, * {"id":13,"description":"Job Object Types","name":"job.objecttype","object":"option"}, * {"id":14,"description":"Image States","name":"image.state","object":"option"}, * {"id":15,"description":"Image Types","name":"image.type","object":"option"}, * {"id":16,"description":"Image OS's","name":"image.os","object":"option"}, * {"id":17,"description":"Image OS Architectures","name":"image.architecture","object":"option"}, * {"id":18,"description":"Datacenters","name":"datacenter","object":"option"}, * {"id":19,"description":"Datacenters","name":"loadbalancer.datacenter","object":"option"}, * {"id":20,"description":"Datacenters","name":"server.datacenter","object":"option"}, * {"id":21,"description":"Datacenters","name":"job.datacenter","object":"option"}, * {"id":22,"description":"Datacenters","name":"ip.datacenter","object":"option"}, * {"id":23,"description":"Image Ram","name":"image.minram","object":"option"}, * {"id":24,"description":"GoGrid Server Image Type","name":"image.gsitype","object":"option"}]} */ private GoGrid provider; public GoGridMethod(@Nonnull GoGrid provider) { this.provider = provider; } public @Nullable JSONArray get(@Nonnull String service, @Nullable Param... params) throws CloudException, InternalException { if (logger.isTraceEnabled()) { logger.trace( "ENTER - " + GoGrid.class.getName() + ".get(" + service + "," + Arrays.toString(params) + ")"); } if (wire.isDebugEnabled()) { wire.debug(""); wire.debug(">>> [GET (" + (new Date()) + ")] -> " + service + " >--------------------------------------------------------------------------------------"); } try { ProviderContext ctx = provider.getContext(); if (ctx == null) { throw new CloudException("No context was set for this request"); } String key; try { key = new String(ctx.getAccessPublic(), "utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); throw new InternalException("UTF-8 not supported"); } String endpoint = getEndpoint(ctx, service); if (logger.isDebugEnabled()) { logger.debug("endpoint=" + endpoint); } String signature = sign(ctx); String paramString = "?format=json&v=" + VERSION + "&api_key=" + key + "&sig=" + signature; if (params != null && params.length > 0) { for (Param p : params) { paramString = paramString + "&" + p.getKey() + "=" + p.getValue(); } } if (logger.isDebugEnabled()) { logger.debug("Param string=" + paramString); } HttpGet get = new HttpGet(endpoint + paramString); HttpClient client = getClient(ctx, endpoint.startsWith("https")); if (wire.isDebugEnabled()) { wire.debug(get.getRequestLine().toString()); for (Header header : get.getAllHeaders()) { wire.debug(header.getName() + ": " + header.getValue()); } wire.debug(""); } HttpResponse response; try { response = client.execute(get); if (wire.isDebugEnabled()) { wire.debug(response.getStatusLine().toString()); } } catch (IOException e) { logger.error("I/O error from server communications: " + e.getMessage()); e.printStackTrace(); throw new InternalException(e); } int status = response.getStatusLine().getStatusCode(); if (status == HttpServletResponse.SC_NOT_FOUND) { return null; } if (status == HttpServletResponse.SC_OK) { HttpEntity entity = response.getEntity(); String json; if (entity == null) { return null; } try { json = EntityUtils.toString(entity); if (wire.isDebugEnabled()) { wire.debug(json); } } catch (IOException e) { logger.error("Failed to read JSON entity"); e.printStackTrace(); throw new CloudException(e); } try { JSONObject r = new JSONObject(json); return r.getJSONArray("list"); } catch (JSONException e) { logger.error("Invalid JSON from cloud: " + e.getMessage()); e.printStackTrace(); throw new CloudException(e); } } else if (status == HttpServletResponse.SC_NOT_FOUND) { return null; } else if (status == 400 && service.endsWith("get")) { return null; } throw new GoGridException(new GoGridException.ParsedException(response)); } finally { if (logger.isTraceEnabled()) { logger.trace("EXIT - " + GoGridMethod.class.getName() + ".doGet()"); } if (wire.isDebugEnabled()) { wire.debug("<<< [GET (" + (new Date()) + ")] -> " + service + " <--------------------------------------------------------------------------------------"); wire.debug(""); } } } private @Nonnull HttpClient getClient(@Nonnull ProviderContext ctx, boolean ssl) { HttpParams params = new BasicHttpParams(); HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); //noinspection deprecation HttpProtocolParams.setContentCharset(params, HTTP.UTF_8); HttpProtocolParams.setUserAgent(params, "Dasein Cloud"); 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")); } } return new DefaultHttpClient(params); } public @Nonnull String getEndpoint(@Nonnull ProviderContext ctx, @Nonnull String service) { String endpoint = ctx.getEndpoint(); if (endpoint == null || endpoint.equals("")) { endpoint = "http://api.gogrid.com"; } if (endpoint.endsWith("/") && service.startsWith("/")) { while (endpoint.endsWith("/") && !endpoint.equals("/")) { endpoint = endpoint.substring(0, endpoint.length() - 1); } } if (service.startsWith("/") || endpoint.endsWith("/")) { return endpoint + service; } return endpoint + service; } private @Nonnull String sign(@Nonnull ProviderContext ctx) throws CloudException, InternalException { byte[] publicKey = ctx.getAccessPublic(); byte[] privateKey = ctx.getAccessPrivate(); try { String toSign = (new String(publicKey, "utf-8")) + (new String(privateKey, "utf-8")) + (System.currentTimeMillis() / 1000); if (logger.isDebugEnabled()) { logger.debug("String to sign=" + toSign); } String signature = stupidPHPMD5(toSign); if (logger.isDebugEnabled()) { logger.debug("Signature=" + signature); } return signature; } catch (UnsupportedEncodingException e) { e.printStackTrace(); throw new InternalException("UTF-8 not supported"); } } private @Nonnull String hex(@Nonnull byte[] array) { StringBuilder str = new StringBuilder(); for (byte b : array) { str.append(Integer.toHexString((b & 0xFF) | 0x100).substring(1, 3)); } return str.toString(); } private @Nonnull String stupidPHPMD5(@Nonnull String toSign) throws InternalException, CloudException { try { MessageDigest digest = MessageDigest.getInstance("MD5"); return hex(digest.digest(toSign.getBytes("CP1252"))); } catch (NoSuchAlgorithmException e) { logger.error("No support for MD5: " + e.getMessage()); e.printStackTrace(); throw new InternalException(e); } catch (UnsupportedEncodingException e) { logger.error("No support for CP1252: " + e.getMessage()); e.printStackTrace(); throw new InternalException(e); } } }