com.ibm.streamsx.rest.StreamsConnection.java Source code

Java tutorial

Introduction

Here is the source code for com.ibm.streamsx.rest.StreamsConnection.java

Source

/*
# Licensed Materials - Property of IBM
# Copyright IBM Corp. 2017
 */
package com.ibm.streamsx.rest;

import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.logging.Logger;

import javax.xml.bind.DatatypeConverter;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.auth.AUTH;
import org.apache.http.client.fluent.Executor;
import org.apache.http.client.fluent.Request;
import org.apache.http.client.fluent.Response;
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import com.ibm.streamsx.topology.internal.streams.InvokeCancel;

/**
 * Connection to IBM Streams
 */
public class StreamsConnection {

    static final Logger traceLog = Logger.getLogger("com.ibm.streamsx.rest.StreamsConnection");

    private final String userName;
    private String url;
    protected String apiKey;
    private boolean allowInsecureHosts = false;

    protected Executor executor;

    /**
     * Connection to IBM Streams
     * 
     * @param userName
     *            String representing the userName to connect to the instance
     * @param authToken
     *            String representing the password to connect to the instance
     * @param url
     *            String representing the root url to the REST API, for example:
     *            https:server:port/streams/rest
     */
    protected StreamsConnection(String userName, String authToken, String url) {
        this.userName = userName;
        String apiCredentials = userName + ":" + authToken;
        apiKey = "Basic " + DatatypeConverter.printBase64Binary(apiCredentials.getBytes(StandardCharsets.UTF_8));

        executor = Executor.newInstance();
        setStreamsRESTURL(url);
    }

    /**
     * sets the REST API url for this connection removing the trailing slash
     * 
     * @param url
     *            String representing the root url to the REST API, for example:
     *            https:server:port/streams/rest/resources
     *            https:server/streams/rest Need to remove the '/resources' for
     *            all other access to be efficient
     */
    protected void setStreamsRESTURL(String iUrl) {
        // set this originally to what comes in to force a 404 if it's not what
        // we wanted
        this.url = iUrl;
        if (!iUrl.isEmpty()) {
            // strip trailing slash
            if (iUrl.charAt(iUrl.length() - 1) == '/') {
                iUrl = iUrl.substring(0, iUrl.length() - 1);
            }

            // IBM Streams returns the REST URI Root as
            // https:server:port/streams/rest/resources
            // whereas Streaming Analytics returns the REST URI as
            // https:server/streams/rest
            // https:server/streams/rest is the common path between them
            String keyword = "streams/rest";
            String endString = "";
            if (getClass() == StreamsConnection.class) {
                endString = "/resources";
            }

            int index = iUrl.indexOf(keyword);
            if (index != -1) {
                String remants = "";
                if (index < iUrl.length()) {
                    remants = iUrl.substring(index + keyword.length(), iUrl.length());
                }
                // verify that there's nothing at the end
                if (remants.equals(endString)) {
                    // make sure we stop at streams/rest
                    this.url = iUrl.substring(0, index + keyword.length());
                }
            }
        }
    }

    /**
     * Connection to IBM Streams
     * 
     * @param userName
     *            String representing the userName to connect to the instance
     * @param authToken
     *            String representing the password to connect to the instance
     * @param url
     *            String representing the root url to the REST API
     * @return a connection to IBM Streams
     */
    public static StreamsConnection createInstance(String userName, String authToken, String url) {
        return new StreamsConnection(userName, authToken, url);
    }

    /**
     * Gets a response to an HTTP call
     * 
     * @param inputString
     *            REST call to make
     * @return response from the inputString
     * @throws IOException
     */
    String getResponseString(String inputString) throws IOException {
        String sReturn = "";
        Request request = Request.Get(inputString).addHeader(AUTH.WWW_AUTH_RESP, apiKey).useExpectContinue();

        Response response = executor.execute(request);
        HttpResponse hResponse = response.returnResponse();
        int rcResponse = hResponse.getStatusLine().getStatusCode();

        if (HttpStatus.SC_OK == rcResponse) {
            sReturn = EntityUtils.toString(hResponse.getEntity());
        } else if (HttpStatus.SC_NOT_FOUND == rcResponse) {
            // with a 404 message, we are likely to have a message from Streams
            // but if not, provide a better message
            sReturn = EntityUtils.toString(hResponse.getEntity());
            if ((sReturn != null) && (!sReturn.equals(""))) {
                throw RESTException.create(rcResponse, sReturn);
            } else {
                String httpError = "HttpStatus is " + rcResponse + " for url " + inputString;
                throw new RESTException(rcResponse, httpError);
            }
        } else {
            // all other errors...
            String httpError = "HttpStatus is " + rcResponse + " for url " + inputString;
            throw new RESTException(rcResponse, httpError);
        }
        traceLog.finest("Request: " + inputString);
        traceLog.finest(rcResponse + ": " + sReturn);
        return sReturn;
    }

    /**
     * Gets a list of {@link Instance instances} that are available to this IBM
     * Streams connection
     * 
     * @return List of {@link Instance IBM Streams Instances} available to this
     *         connection
     * @throws IOException
     */
    public List<Instance> getInstances() throws IOException {
        String instancesURL = url + "/instances/";

        String sReturn = getResponseString(instancesURL);
        List<Instance> instanceList = Instance.getInstanceList(this, sReturn);

        return instanceList;
    }

    /**
     * Gets a specific {@link Instance instance} identified by the instanceId at
     * this IBM Streams connection
     * 
     * @param instanceId
     *            name of the instance to be retrieved
     * @return a single {@link Instance}
     * @throws IOException
     */
    public Instance getInstance(String instanceId) throws IOException {
        Instance si = null;
        if (instanceId.equals("")) {
            // should add some fallback code to see if there's only one instance
            throw new IllegalArgumentException("Missing instance name");
        } else {
            String instanceURL = url + "/instances/" + instanceId;
            String sReturn = getResponseString(instanceURL);

            si = Instance.create(this, sReturn);
        }
        return si;
    }

    /**
     * This function is used to disable checking the trusted certificate chain
     * and should never be used in production environments
     * 
     * @param allowInsecure
     *            <ul>
     *            <li>true - disables checking</li>
     *            <li>false - enables checking (default)</li>
     *            </ul>
     * @return a boolean indicating the state of the connection after this
     *         method was called.
     *         <ul>
     *         <li>true - if checking is disabled</li>
     *         <li>false - if checking is enabled</li>
     *         </ul>
     */
    public boolean allowInsecureHosts(boolean allowInsecure) {
        try {
            if ((allowInsecure) && (false == allowInsecureHosts)) {
                CloseableHttpClient httpClient = HttpClients.custom()
                        .setHostnameVerifier(new AllowAllHostnameVerifier())
                        .setSslcontext(new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                            public boolean isTrusted(X509Certificate[] arg0, String arg1)
                                    throws CertificateException {
                                return true;
                            }
                        }).build()).build();
                executor = Executor.newInstance(httpClient);
                allowInsecureHosts = true;
            } else if ((false == allowInsecure) && (true == allowInsecureHosts)) {
                executor = Executor.newInstance();
                allowInsecureHosts = false;
            }
        } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
            executor = Executor.newInstance();
            allowInsecureHosts = false;
        }
        if (allowInsecureHosts) {
            traceLog.info("Insecure Host Connection enabled");
        }
        return allowInsecureHosts;
    }

    /**
     * Cancels a job at this streams connection identified by the jobId
     * 
     * @param jobId
     *            string identifying the job to be cancelled
     * @return a boolean indicating
     *         <ul>
     *         <li>true if the jobId is cancelled</li>
     *         <li>false if the jobId did not get cancelled</li>
     *         </ul>
     * @throws Exception
     */
    public boolean cancelJob(String jobId) throws Exception {
        InvokeCancel cancelJob = new InvokeCancel(new BigInteger(jobId), userName);
        return cancelJob.invoke(false) == 0;
    }
}