net.solarnetwork.node.support.JsonHttpClientSupport.java Source code

Java tutorial

Introduction

Here is the source code for net.solarnetwork.node.support.JsonHttpClientSupport.java

Source

/* ==================================================================
 * JsonHttpClientSupport.java - Oct 6, 2014 12:35:03 PM
 * 
 * Copyright 2007-2014 SolarNetwork.net Dev Team
 * 
 * This program is free software; you can redistribute it and/or 
 * modify it under the terms of the GNU General Public License as 
 * published by the Free Software Foundation; either version 2 of 
 * the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License 
 * along with this program; if not, write to the Free Software 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
 * 02111-1307 USA
 * ==================================================================
 */

package net.solarnetwork.node.support;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.zip.GZIPOutputStream;
import net.solarnetwork.node.RemoteServiceException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * An abstract class to support HTTP based services that use JSON.
 * 
 * <p>
 * The configurable properties of this class are:
 * </p>
 * 
 * <dl class="class-properties">
 * <dt>compress</dt>
 * <dd>Flag to compress the HTTP body content, defaults to <em>false</em>.</dd>
 * 
 * <dt>objectMapper</dt>
 * <dd>The {@link ObjectMapper} to marshall/unmarshall objects to/from JSON
 * with.</dd>
 * </dl>
 * 
 * @author matt
 * @version 1.1
 */
public abstract class JsonHttpClientSupport extends HttpClientSupport {

    /** The JSON MIME type. */
    public static final String JSON_MIME_TYPE = "application/json";

    private ObjectMapper objectMapper;
    private boolean compress = false;

    /**
     * Perform a JSON HTTP request.
     * 
     * @param url
     *        the URL to make the request to
     * @param method
     *        the HTTP method, e.g. {@link HttpClientSupport#HTTP_METHOD_GET}
     * @param data
     *        the optional data to marshall to JSON and upload as the request
     *        content
     * @return the InputStream for the HTTP response
     * @throws IOException
     *         if any IO error occurs
     */
    protected final InputStream doJson(String url, String method, Object data) throws IOException {
        URLConnection conn = getURLConnection(url, method, JSON_MIME_TYPE);
        if (data != null) {
            conn.setRequestProperty("Content-Type", JSON_MIME_TYPE + ";charset=UTF-8");
            if (compress) {
                conn.setRequestProperty("Content-Encoding", "gzip");
            }
            OutputStream out = conn.getOutputStream();
            if (compress) {
                out = new GZIPOutputStream(out);
            }

            if (log.isDebugEnabled()) {
                log.debug("Posting JSON data: {}",
                        objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(data));
            }
            objectMapper.writeValue(out, data);
            out.flush();
            out.close();
        }

        return getInputStreamFromURLConnection(conn);
    }

    /**
     * Perform a JSON GET HTTP request.
     * 
     * @param url
     *        the URL to GET
     * @return the HTTP response InputStream
     * @throws IOException
     *         if any IO error occurs
     */
    protected final InputStream jsonGET(String url) throws IOException {
        return doJson(url, HTTP_METHOD_GET, null);
    }

    /**
     * Perform a JSON POST HTTP request.
     * 
     * @param url
     *        the URL to POST
     * @param data
     *        the object to marshall into JSON
     * @return the HTTP response InputStream
     * @throws IOException
     *         if any IO error occurs
     */
    protected final InputStream jsonPOST(String url, Object data) throws IOException {
        return doJson(url, HTTP_METHOD_POST, data);
    }

    /**
     * Parse a standard {@code Response} HTTP response and return the
     * {@code data} object as the provided type.
     * 
     * @param in
     *        the InputStream to read, which will be closed before returning
     *        from this method
     * @param dataType
     *        the type of object to extract from the response
     * @return the extracted object, or <em>null</em>
     * @throws RemoteServiceException
     *         if the response does not include the success flag
     * @throws IOException
     *         if any IO error occurs
     */
    protected <T> T extractResponseData(InputStream in, Class<T> dataType)
            throws RemoteServiceException, IOException {
        try {
            JsonNode root = getObjectMapper().readTree(in);
            if (root.isObject()) {
                JsonNode child = root.get("success");
                if (child != null && child.asBoolean()) {
                    child = root.get("data");
                    if (child != null) {
                        return objectMapper.treeToValue(child, dataType);
                    }
                    log.debug("Server returned no data for request.");
                    return null;
                }
            }
            throw new RemoteServiceException(
                    "Server response not successful: " + root.get("message") == null ? "(no message)"
                            : root.get("message").asText());
        } finally {
            if (in != null) {
                in.close();
            }
        }
    }

    /**
     * Parse a standard {@code Response} HTTP response and return the
     * {@code data} object as the provided type.
     * 
     * @param in
     *        the InputStream to read, which will be closed before returning
     *        from this method
     * @param dataType
     *        the type of object to extract from the response
     * @return the extracted object, or <em>null</em>
     * @throws RemoteServiceException
     *         if the response does not include the success flag
     * @throws IOException
     *         if any IO error occurs
     * @since 1.1
     */
    protected <T> Collection<T> extractCollectionResponseData(InputStream in, Class<T> dataType)
            throws RemoteServiceException, IOException {
        try {
            JsonNode root = getObjectMapper().readTree(in);
            if (root.isObject()) {
                JsonNode child = root.get("success");
                if (child != null && child.asBoolean()) {
                    child = root.get("data");
                    if (child != null && child.isArray()) {
                        Iterator<JsonNode> children = child.iterator();
                        List<T> result = new ArrayList<T>();
                        while (children.hasNext()) {
                            child = children.next();
                            result.add(objectMapper.treeToValue(child, dataType));
                        }
                        return result;
                    }
                    log.debug("Server returned no data for request.");
                    return null;
                }
            }
            throw new RemoteServiceException(
                    "Server response not successful: " + root.get("message") == null ? "(no message)"
                            : root.get("message").asText());
        } finally {
            if (in != null) {
                in.close();
            }
        }
    }

    /**
     * Test for a successful standard {@code Response} HTTP response.
     * 
     * @param in
     *        the InputStream to read, which will be closed before returning
     *        from this method
     * @throws RemoteServiceException
     *         if the response does not include the success flag
     * @throws IOException
     *         if any IO error occurs
     */
    protected void verifyResponseSuccess(InputStream in) throws RemoteServiceException, IOException {
        try {
            JsonNode root = getObjectMapper().readTree(in);
            if (root.isObject()) {
                JsonNode child = root.get("success");
                if (child != null && child.asBoolean()) {
                    return;
                }
            }
            throw new RemoteServiceException(
                    "Server response not successful: " + root.get("message") == null ? "(no message)"
                            : root.get("message").asText());
        } finally {
            if (in != null) {
                in.close();
            }
        }
    }

    public final ObjectMapper getObjectMapper() {
        return objectMapper;
    }

    public final void setObjectMapper(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    public final boolean isCompress() {
        return compress;
    }

    public final void setCompress(boolean compress) {
        this.compress = compress;
    }

}