com.elevenpaths.latch.sdk.impl.LatchSDKImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.elevenpaths.latch.sdk.impl.LatchSDKImpl.java

Source

/*
 * Latch Open-Xchange Plugin
 * Copyright (C) 2014 Eleven Paths
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License, version 2.1 as published by the Free Software Foundation.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 */

package com.elevenpaths.latch.sdk.impl;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import java.util.Map;

import com.elevenpaths.latch.Latch;
import com.elevenpaths.latch.LatchErrorException;
import com.elevenpaths.latch.LatchResponse;
import com.elevenpaths.latch.plugins.openexchange.util.Utils;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

/**
 * A Latch SDK implementation.
 * <p>
 * The Java Latch SDK requires that developers choose the best way for
 * them to implement HTTP connections with the Latch backend and
 * overload the <code>HTTP_GET</code> method appropriately.
 * <p>
 * On this way, they can adapt the code to the environment and
 * conditions in which it will run. For example, if the code will run
 * in a container that offers some capabilities to manage HTTP
 * connection pools.
 * <p>
 * In this implementation the standard <code>HttpURLConnection</code>
 * class provided by the JVM will be used. This means that the CA that
 * signs the Latch backend server certificate should be as trusted CA
 * in the JVM trust store.
 * <p>
 * It also means that the standard way to set HTTP proxy host and port
 * (through JVM system properties) can be used if needed. At this
 * moment setting a proxy just for this HTTP connections is not
 * supported in the SDK.
 * <p>
 * Besides, an alternate implementation of the status method is also
 * provided. This implementation, instead of returning the bulk JSON
 * object returns an enumerated type <code>LatchSDKStatus</code>.
 * <p>
 * The enumerated type <code>LatchSDKStatus</code> has an additional
 * <code>UNKNOWN</code> value to represent that something has gone
 * wrong checking the status of the latch so the application using the
 * SDK can choose what to do.
 * 
 * TODO: Certificate Pinning.
 */
public class LatchSDKImpl extends Latch {

    /** Static SLF4J logger */
    private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(LatchSDKImpl.class);

    /**
     * The application id the SDK will use when communicating with the
     * Latch backend. This is needed because the field in the parent
     * class is private.
     */
    protected String applicationId = null;

    /**
     * Constructs a <code>LatchSDKImpl</code> with the provided
     * application id and secret. These values are different for each
     * application and can be obtained from the developer area of the
     * Latch web site.
     * 
     * @param applicationId
     *        The application id that will be used when communicating
     *        with the Latch backend.
     * @param secret
     *        The secret that will be used when communicating with the
     *        Latch backend.
     */
    public LatchSDKImpl(String applicationId, String secret) {

        super(applicationId, secret);

        /* Internal reference for later use as parent field is private */

        this.applicationId = applicationId;

    }

    /**
     * Makes an HTTP GET request to the provided URL, adding the
     * provided headers. The output is parsed and returned as a JSON
     * element with Google GSON library.
     * <p>
     * If something goes wrong during the connection a null element
     * will be returned.
     * 
     * @param URL
     *        The URL to where the GET request should be make.
     * @param headers
     *        The headers that should be added to the GET request.
     * 
     * @return The JSON returned by the Latch backend server or a null
     *         element if something has gone wrong.
     */
    public JsonElement HTTP_GET(String URL, Map<String, String> headers) {

        Utils.checkMethodRequiredParameter(LOG, "URL", URL);
        Utils.checkMethodRequiredParameter(LOG, "headers", headers);

        JsonElement rv = null;
        InputStream is = null;
        InputStreamReader isr = null;

        try {

            URL theURL = new URL(URL);
            HttpURLConnection theConnection = (HttpURLConnection) theURL.openConnection();

            Iterator<String> iterator = headers.keySet().iterator();

            while (iterator.hasNext()) {
                String headerName = iterator.next();
                theConnection.setRequestProperty(headerName, headers.get(headerName));
            }

            JsonParser parser = new JsonParser();

            is = theConnection.getInputStream();
            isr = new InputStreamReader(is);

            rv = parser.parse(isr);

        } catch (MalformedURLException e) {
            LOG.error("The URL is malformed (" + URL + ")", e);
        } catch (IOException e) {
            LOG.error("And exception has been thrown when communicating with Latch backend", e);
        } finally {

            if (isr != null) {

                try {
                    isr.close();
                } catch (IOException e) {
                }

            }

            if (is != null) {

                try {
                    is.close();
                } catch (IOException e) {
                }

            }

        }

        return rv;

    }

    /**
     * Returns the status of the latch identified by the provided
     * account id and, if not null, the provided operation id.
     * <p>
     * If something has gone wrong trying to get the latch status an
     * special <code>LatchSDKStatus</code> value will be returned (
     * <code>UNKNOWN</code>), so the application can choose what to
     * do.
     * <p>
     * If the backend returns a JSON with an error, this error is
     * returned as a <code>LatchErrorException</code>. It's also
     * responsibility of the application using this method to choose
     * what to do.
     * 
     * @param accountId
     *        The account id that identifies the latch being checked.
     * @param operationId
     *        The operation id that identifies the latch being
     *        checked. If the application has no operations, it should
     *        be null.
     * 
     * @return The status of the latch.
     * 
     * @throws LatchErrorException
     *         If the backend returns a JSON with an error.
     */
    public LatchSDKStatus parsedStatus(String accountId, String operationId) throws LatchErrorException {

        Utils.checkMethodRequiredParameter(LOG, "accountId", accountId);

        /* The default status is UNKNOWN */

        LatchSDKStatus rv = LatchSDKStatus.UNKNOWN;

        /* Connect to the server and get the JSON response to the status request */

        LatchResponse latchResponse = null;

        try {

            if (operationId == null) {
                latchResponse = status(accountId);
            } else {
                latchResponse = operationStatus(accountId, operationId);
            }

        } catch (Exception e) {
            LOG.error("And exception has been thrown when communicating with Latch backend", e);
            return rv;
        }

        /* Check if a response has been received and parsed correctly */

        if (latchResponse != null) {

            if (latchResponse.getData() != null) {

                /* The backend has responded with data */

                JsonObject jsonObject = (JsonObject) latchResponse.getData();

                if (jsonObject.has("operations")) {

                    JsonObject operations = jsonObject.getAsJsonObject("operations");
                    JsonObject latch = null;

                    if (operationId == null) {
                        if (operations.has(applicationId)) {
                            latch = operations.getAsJsonObject(applicationId);
                        }
                    } else {
                        if (operations.has(operationId)) {
                            latch = operations.getAsJsonObject(operationId);
                        }
                    }

                    if (latch != null && latch.has("status")) {
                        if ("off".equals(latch.get("status").getAsString())) {
                            rv = LatchSDKStatus.LOCKED;
                        } else if ("on".equals(latch.get("status").getAsString())) {
                            rv = LatchSDKStatus.UNLOCKED;
                        }
                    }

                }

            } else if (latchResponse.getError() != null) {

                /* The backend has responded with an application error message */

                LOG.error(
                        "The backend has responded with an application error message: " + latchResponse.getError());
                throw new LatchErrorException(latchResponse.getError());

            }

        }

        return rv;

    }

}