com.vmware.vchs.api.samples.services.helper.HttpUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.vmware.vchs.api.samples.services.helper.HttpUtils.java

Source

/*
 * Copyright (c) 2014 VMware, Inc. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
 * this file except in compliance with the License. You may obtain a copy of
 * the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package com.vmware.vchs.api.samples.services.helper;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.cert.X509Certificate;
import java.util.GregorianCalendar;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.xml.bind.JAXB;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;

import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
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.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.vmware.vchs.api.samples.SampleConstants;
import com.vmware.vcloud.api.rest.schema_v1_5.QueryResultRecordsType;

/**
 * This class provides the common http functionality using the Apache HttpClient library.
 */
public class HttpUtils {
    /**
     * Executes an http request using the passed in request parameter.
     * 
     * @param request
     *            the HttpRequestBase subclass to make a request with
     * @return the response of the request
     */
    public static HttpResponse httpInvoke(HttpRequestBase request) {
        HttpResponse httpResponse = null;
        HttpClient httpClient = null;

        try {
            // Create a fresh HttpClient.. some samples will make calls to two (or more)
            // urls in a single run, sharing a static non-multithreaded instance causes
            // exceptions. This prevents that by ensuring each call to httpInvoke gets
            // its own instance.
            httpClient = createTrustingHttpClient();
            httpResponse = httpClient.execute(request);
        } catch (ClientProtocolException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return httpResponse;
    }

    /**
     * This method returns a secure HttpClient instance.
     * 
     * @return HttpClient a new secure instance of HttpClient
     */
    static HttpClient createSecureHttpClient() {
        return null;
    }

    /**
     * This method returns an HttpClient instance wrapped to trust all HTTPS certificates.
     * 
     * @return HttpClient a new instance of HttpClient
     */
    static HttpClient createTrustingHttpClient() {
        HttpClient base = new DefaultHttpClient();

        try {
            SSLContext ctx = SSLContext.getInstance("TLS");

            // WARNING: This creates a TrustManager that trusts all certificates and should not be
            // used in production code.
            TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }

                public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
                }

                public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
                }
            } };

            ctx.init(null, trustAllCerts, null);
            SSLSocketFactory ssf = new SSLSocketFactory(ctx);
            ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            ClientConnectionManager ccm = base.getConnectionManager();
            SchemeRegistry sr = ccm.getSchemeRegistry();
            sr.register(new Scheme("https", 443, ssf));

            return new DefaultHttpClient(ccm, base.getParams());
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }

    /**
     * This helper method will create the JAXB context for the provided Class<T> and
     * marshal the provided JAXBElement<T> into a StringEntity.
     * 
     * @param clazz
     * @param jaxb
     * @return
     */
    public static <T> StringEntity marshal(Class<T> clazz, JAXBElement<T> jaxb) {
        JAXBContext jaxbContexts = null;
        OutputStream os = null;

        try {
            jaxbContexts = JAXBContext.newInstance(clazz);
        } catch (JAXBException ex) {
            throw new RuntimeException("Problem creating JAXB Context: ", ex);
        }

        try {
            javax.xml.bind.Marshaller marshaller = jaxbContexts.createMarshaller();
            marshaller.setProperty(javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            os = new ByteArrayOutputStream();
            // Marshal the object via JAXB to XML
            marshaller.marshal(jaxb, os);
        } catch (JAXBException e) {
            throw new RuntimeException("Problem marshalling instantiation VDC template", e);
        }

        try {
            return new StringEntity(os.toString());
        } catch (UnsupportedEncodingException e1) {
            throw new RuntimeException("Problem marshalling instantiation VDC template", e1);
        }
    }

    /**
     * This method will unmarshal the passed in entity using the passed in class type. It will check
     * the content-type to determine if the response is json or xml and use the appropriate
     * deserializer.
     * 
     * @param entity
     *            the entity to unmarshal
     * @param clazz
     *            the class type to base the unmarshal from
     * @return unmarshal an instance of the provided class type
     */
    public static <T> T unmarshal(HttpEntity entity, Class<T> clazz) {
        InputStream is = null;

        try {
            String s = EntityUtils.toString(entity);

            // Check if the response content-type contains the string json.. if so use GSON to
            // convert from json to the provided Class<T> type
            if (entity.getContentType().toString().toLowerCase().contains("json")) {
                GsonBuilder gsonBuilder = new GsonBuilder();
                gsonBuilder.registerTypeAdapter(XMLGregorianCalendar.class,
                        new XMLGregorianClassConverter.Serializer());
                gsonBuilder.registerTypeAdapter(XMLGregorianCalendar.class,
                        new XMLGregorianClassConverter.Deserializer());

                Gson g = gsonBuilder.create();
                return g.fromJson(s, clazz);
            }

            is = new ByteArrayInputStream(s.getBytes("UTF-8"));
            return JAXB.unmarshal(is, clazz);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (null != is) {
                    is.close();
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * This method can be used to query the vCloud Query API. The baseVcdUrl represents the portion
     * of the url up to the /api at the end. The /query is appended. Query parameters allow any of
     * the Query API to be called, and the response is the QueryResultRecordsType which the calling
     * method can then use to parse the response. The version is the version of the vCloud Query API
     * to call, and the token is the vCloud API token provided by the login step via the
     * x-vcloud-authorization response header.
     * 
     * @param baseVcdUrl
     *            the base vCloud API url up to the /api on the end
     * @param queryParameters
     *            any vCloud Query API Parameters
     * @param version
     *            the vCloud Query API version to call against
     * @param token
     *            the vCloud API token retrieved after a successful login
     * @return
     */
    public static QueryResultRecordsType getQueryResults(String baseVcdUrl, String queryParameters, String version,
            String token) {
        URL url = null;
        QueryResultRecordsType results = null;

        try {
            // Construct the URL from the baseVcdUrl to utilize the vCloud Query API to find a
            // matching template
            url = new URL(baseVcdUrl + "/query?" + queryParameters);
        } catch (MalformedURLException e) {
            throw new RuntimeException("Invalid URL: " + baseVcdUrl);
        }

        HttpGet httpGet = new HttpGet(url.toString());
        httpGet.setHeader(HttpHeaders.ACCEPT, SampleConstants.APPLICATION_PLUS_XML_VERSION + version);

        httpGet.setHeader(SampleConstants.VCD_AUTHORIZATION_HEADER, token);

        HttpResponse response = HttpUtils.httpInvoke(httpGet);

        // make sure the status is 200 OK
        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            results = HttpUtils.unmarshal(response.getEntity(), QueryResultRecordsType.class);
        }

        return results;
    }

    /**
     * Gets the string content from the passed in InputStream
     * 
     * @param is
     *            response stream from GET/POST method call
     * @return String content of the passed in InputStream
     */
    public static String getContent(InputStream is) {
        if (is != null) {
            StringBuilder sb = new StringBuilder();
            String line;

            BufferedReader reader = null;

            try {
                reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));

                while ((line = reader.readLine()) != null) {
                    sb.append(line + "\n");
                }
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (null != reader) {
                    try {
                        reader.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        throw new RuntimeException(e);
                    }
                }
            }

            return sb.toString();
        }

        return "";
    }

    public static class XMLGregorianCalendarDeserializer implements JsonDeserializer<XMLGregorianCalendar> {
        public XMLGregorianCalendar deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
                throws JsonParseException {
            JsonObject jo = je.getAsJsonObject();
            GregorianCalendar c = new GregorianCalendar();
            if (null == jo) {
                System.out.println("DESERIALIZING JO IS NULL");
            } else {
                System.out.println("TOS created date is " + jo.getAsString());
            }
            try {
                XMLGregorianCalendar cal = DatatypeFactory.newInstance().newXMLGregorianCalendar();
            } catch (DatatypeConfigurationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace(System.out);
            }
            // Location l = new Location(jo.getAsJsonPrimitive("mProvider").getAsString());
            // l.setAccuracy(jo.getAsJsonPrimitive("mAccuracy").getAsFloat());
            // etc, getting and setting all the data
            return null;
        }
    }

    public static class XMLGregorianClassConverter {
        public static class Serializer implements JsonSerializer {
            public Serializer() {
                super();
            }

            public JsonElement serialize(Object t, Type type, JsonSerializationContext jsonSerializationContext) {
                XMLGregorianCalendar xgcal = (XMLGregorianCalendar) t;
                return new JsonPrimitive(xgcal.toXMLFormat());
            }
        }

        public static class Deserializer implements JsonDeserializer {
            public Object deserialize(JsonElement jsonElement, Type type,
                    JsonDeserializationContext jsonDeserializationContext) {
                try {
                    return DatatypeFactory.newInstance().newXMLGregorianCalendar(jsonElement.getAsString());
                } catch (Exception e) {
                    return null;
                }
            }
        }
    }

    /**
     * This private helper method will construct the Accept or Content-Type headers from 1, 2 or
     * 3 parts depending on if the parts passed in are null or not. The mediaType passed in should
     * be one of application/xml, application/json, or any variance that would typically be
     * acceptable. The classType is optional, so if it's not needed, passing in null will avoid
     * it being added to the final string. Version as well is optional and like classType, if it's
     * null is not added.
     * 
     * @param mediaType
     *            the media type for the Accpet or Content-Type header that the result will apply to
     * @param classType
     *            the class= type, if needed (optional... pass in null to avoid it being used)
     * @param version
     *            the version= value, if needed (optional... pass in null to avoid it being used)
     * @return a concatenated string of the 1, 2 or 3 parts depending on what was provided
     */
    private static String buildMediaType(String mediaType, String classType, String version) {
        StringBuilder sb = new StringBuilder(mediaType);

        if (null != classType && classType.length() > 0) {
            sb.append(";class=");
            sb.append(classType);
        }

        if (null != version && version.length() > 0) {
            sb.append(";version=");
            sb.append(version);
        }

        return sb.toString();
    }

    /**
     * This method will make a GET request using the provided parameters. The URL is assumed to be
     * a complete URL, including any query parameters. The token is the OAUTH2 token provided by
     * the IAM login process. The accept and contentType parameters are used to set the initial
     * Http header media types for Accept and Content-Type respectively. The classType parameter
     * is optional and if specified will add a class=... to the Accept and/or Content-Type headers.
     * The version is optional and will add a version=... to the Accept and/or Content-Type headers.
     * 
     * @param url
     *            the full URL path to the API resource
     * @param token
     *            the OAUTH2 token
     * @param accept
     *            value to be part of the Accept header (e.g. application/json)
     * @param contentType
     *            value to be part of the Content-Type header (e.g. application/xml)
     * @param classType
     *            value to be part of the Accept of Content-Type header (e.g.
     *            class=com.vmware.vchs.iam.api.schema.v2.classes.user.User)
     * @param version
     *            the version of the API to request if provided
     * @return an instance of HttpResponse
     */
    public static HttpResponse httpGet(String url, String token, String accept, String contentType,
            String classType, String version) {
        HttpGet get = new HttpGet(url);
        get.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token);

        if (null != accept) {
            get.setHeader(HttpHeaders.ACCEPT, buildMediaType(accept, classType, version));
        }
        if (null != contentType) {
            get.setHeader(HttpHeaders.CONTENT_TYPE, buildMediaType(contentType, classType, version));
        }

        return httpInvoke(get);
    }

    /**
     * This method will make a GET request using the provided parameters. The URL is assumed to be
     * a complete URL, including any query parameters. The token is the OAUTH2 token provided by
     * the IAM login process. The accept and contentType parameters are used to set the initial
     * Http header media types for Accept and Content-Type respectively. The classType parameter
     * is optional and if specified will add a class=... to the Accept and/or Content-Type headers.
     * The version is optional and will add a version=... to the Accept and/or Content-Type headers.
     * 
     * @param url
     *            the full URL path to the API resource
     * @param token
     *            the OAUTH2 token
     * @param accept
     *            value to be part of the Accept header (e.g. application/json)
     * @param contentType
     *            value to be part of the Content-Type header (e.g. application/xml)
     * @param classType
     *            value to be part of the Accept of Content-Type header (e.g.
     *            class=com.vmware.vchs.iam.api.schema.v2.classes.user.User)
     * @param version
     *            the version of the API to request if provided
     * @return an instance of HttpResponse
     */
    public static HttpResponse httpPost(String url, String token, String accept, String contentType,
            String classType, String version, String requestBody) {
        HttpPost post = new HttpPost(url);
        post.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token);

        HttpEntity entity = null;

        try {
            entity = new StringEntity(requestBody);
            post.setEntity(entity);
        } catch (UnsupportedEncodingException e1) {
            e1.printStackTrace();
        }

        post.setEntity(entity);

        if (null != accept) {
            post.setHeader(HttpHeaders.ACCEPT, buildMediaType(accept, classType, version));
        }
        if (null != contentType) {
            post.setHeader(HttpHeaders.CONTENT_TYPE, buildMediaType(contentType, classType, version));
        }

        return httpInvoke(post);
    }

    /**
     * This method will make a GET request using the provided parameters. The URL is assumed to be
     * a complete URL, including any query parameters. The token is the OAUTH2 token provided by
     * the IAM login process. The accept and contentType parameters are used to set the initial
     * Http header media types for Accept and Content-Type respectively. The classType parameter
     * is optional and if specified will add a class=... to the Accept and/or Content-Type headers.
     * The version is optional and will add a version=... to the Accept and/or Content-Type headers.
     * 
     * @param url
     *            the full URL path to the API resource
     * @param token
     *            the OAUTH2 token
     * @param accept
     *            value to be part of the Accept header (e.g. application/json)
     * @param contentType
     *            value to be part of the Content-Type header (e.g. application/xml)
     * @param classType
     *            value to be part of the Accept of Content-Type header (e.g.
     *            class=com.vmware.vchs.iam.api.schema.v2.classes.user.User)
     * @param version
     *            the version of the API to request if provided
     * @return an instance of HttpResponse
     */
    public static HttpResponse httpPut(String url, String token, String accept, String contentType,
            String classType, String version, String requestBody) {
        HttpPut put = new HttpPut(url);
        put.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token);

        HttpEntity entity = null;

        try {
            entity = new StringEntity(requestBody);
            put.setEntity(entity);
        } catch (UnsupportedEncodingException e1) {
            e1.printStackTrace();
        }

        put.setEntity(entity);

        if (null != accept) {
            put.setHeader(HttpHeaders.ACCEPT, buildMediaType(accept, classType, version));
        }
        if (null != contentType) {
            put.setHeader(HttpHeaders.CONTENT_TYPE, buildMediaType(contentType, classType, version));
        }

        return httpInvoke(put);
    }

    /**
     * This method will make a GET request using the provided parameters. The URL is assumed to be
     * a complete URL, including any query parameters. The token is the OAUTH2 token provided by
     * the IAM login process. The accept and contentType parameters are used to set the initial
     * Http header media types for Accept and Content-Type respectively. The classType parameter
     * is optional and if specified will add a class=... to the Accept and/or Content-Type headers.
     * The version is optional and will add a version=... to the Accept and/or Content-Type headers.
     * 
     * @param url
     *            the full URL path to the API resource
     * @param token
     *            the OAUTH2 token
     * @param accept
     *            value to be part of the Accept header (e.g. application/json)
     * @param contentType
     *            value to be part of the Content-Type header (e.g. application/xml)
     * @param classType
     *            value to be part of the Accept of Content-Type header (e.g.
     *            class=com.vmware.vchs.iam.api.schema.v2.classes.user.User)
     * @param version
     *            the version of the API to request if provided
     * @return an instance of HttpResponse
     */
    public static HttpResponse httpDelete(String url, String token, String accept, String contentType,
            String classType, String version) {
        HttpDelete delete = new HttpDelete(url);
        delete.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token);

        if (null != accept) {
            delete.setHeader(HttpHeaders.ACCEPT, buildMediaType(accept, classType, version));
        }
        if (null != contentType) {
            delete.setHeader(HttpHeaders.CONTENT_TYPE, buildMediaType(contentType, classType, version));
        }

        return httpInvoke(delete);
    }
}