com.revo.deployr.client.core.impl.RClientImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.revo.deployr.client.core.impl.RClientImpl.java

Source

/*
 * RClientImpl.java
 *
 * Copyright (C) 2010-2016, Microsoft Corporation
 *
 * This program is licensed to you under the terms of Version 2.0 of the
 * Apache License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) for more details.
 *
 */
package com.revo.deployr.client.core.impl;

import com.revo.deployr.client.RClient;
import com.revo.deployr.client.RUser;
import com.revo.deployr.client.core.REndpoints;
import com.revo.deployr.client.call.RCall;
import com.revo.deployr.client.call.AbstractCall;
import com.revo.deployr.client.call.user.LoginCall;
import com.revo.deployr.client.call.user.LogoutCall;
import com.revo.deployr.client.call.user.AboutCall;
import com.revo.deployr.client.call.user.AutosaveCall;
import com.revo.deployr.client.call.project.ProjectCreateCall;
import com.revo.deployr.client.call.repository.RepositoryScriptExecuteCall;
import com.revo.deployr.client.call.repository.RepositoryScriptInterruptCall;
import com.revo.deployr.client.RClientException;
import com.revo.deployr.client.RDataException;
import com.revo.deployr.client.RSecurityException;
import com.revo.deployr.client.RGridException;
import com.revo.deployr.client.auth.RAuthentication;
import com.revo.deployr.client.auth.basic.RBasicAuthentication;

import com.revo.deployr.client.core.RCoreResponse;
import com.revo.deployr.client.core.RCoreResult;
import com.revo.deployr.client.core.RClientExecutor;

import com.revo.deployr.client.data.RData;
import com.revo.deployr.client.util.RDataUtil;

import com.revo.deployr.client.params.AnonymousProjectExecutionOptions;

import com.revo.deployr.client.RProjectExecution;
import com.revo.deployr.client.RScriptExecution;
import com.revo.deployr.client.RRepositoryDirectory;

import com.revo.deployr.client.about.RUserDetails;
import com.revo.deployr.client.about.RUserLimitDetails;
import com.revo.deployr.client.about.RProjectDetails;
import com.revo.deployr.client.about.RProjectExecutionDetails;

import com.revo.deployr.client.util.REntityUtil;

import org.apache.http.Header;
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.CookieStore;
import org.apache.http.params.HttpParams;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.params.ConnPerRouteBean;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.client.params.CookiePolicy;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.cookie.Cookie;
import javax.net.ssl.SSLContext;
import org.apache.http.conn.ssl.*;
import org.apache.http.config.*;
import org.apache.http.conn.socket.*;
import org.apache.http.impl.client.HttpClients;
import java.security.cert.X509Certificate;
import java.security.*;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;

import java.util.*;
import java.net.*;
import java.io.*;

import java.util.concurrent.Executors;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ExecutorService;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class RClientImpl implements RClient, RClientExecutor {

    private Log log = LogFactory.getLog(RClientImpl.class);
    private final static String XSRF_HEADER = "X-XSRF-TOKEN";

    private DefaultHttpClient httpClient;
    private ExecutorService eService;
    private String serverurl;
    private String httpcookie;
    private String csrf;
    private SSLSocketFactory sslSocketFactory;
    private RLiveContext liveContext;

    private ArrayList<Integer> GRID_EXCEPTION_CODES = new ArrayList<Integer>(
            Arrays.asList(910, 911, 912, 913, 914, 915, 916, 917, 918, 919));

    public RClientImpl(String serverurl) throws RClientException, RSecurityException {

        this(serverurl, 200, false);
    }

    public RClientImpl(String serverurl, int concurrentCallLimit) throws RClientException, RSecurityException {

        this(serverurl, concurrentCallLimit, false);
    }

    public RClientImpl(String serverurl, int concurrentCallLimit, boolean allowSelfSignedSSLCert)
            throws RClientException, RSecurityException {

        log.debug("Creating client connection: serverurl=" + serverurl + ", concurrentCallLimit="
                + concurrentCallLimit + ", allowSelfSignedSSLCert=" + allowSelfSignedSSLCert);

        this.serverurl = serverurl;

        // Create and initialize HTTP parameters
        HttpParams httpParams = new BasicHttpParams();
        // Set Infinite Connection and Socket Timeouts.
        HttpConnectionParams.setConnectionTimeout(httpParams, 0);
        HttpConnectionParams.setSoTimeout(httpParams, 0);
        ConnManagerParams.setMaxTotalConnections(httpParams, concurrentCallLimit);
        ConnManagerParams.setMaxConnectionsPerRoute(httpParams, new ConnPerRouteBean(concurrentCallLimit));
        HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1);

        // Create and initialize scheme registry 
        SchemeRegistry schemeRegistry = new SchemeRegistry();
        schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));

        if (allowSelfSignedSSLCert) {
            /*
             * Register scheme for "https" that bypasses
             * SSL cert trusted-origin verification check
             * which makes it possible to connect to a
             * DeployR server using a self-signed certificate.
             *
             * Recommended for prototyping and testing only,
             * not recommended for production environments.
             */
            TrustStrategy blindTrust = new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] certificate, String authType) {
                    return true;
                }
            };
            try {
                sslSocketFactory = new SSLSocketFactory(blindTrust, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
                schemeRegistry.register(new Scheme("https", 8443, sslSocketFactory));
            } catch (GeneralSecurityException gsex) {
                String exMsg = "Self-signed SSL cert config failed, " + gsex.getMessage();
                log.debug(exMsg);
                throw new RSecurityException(exMsg, 0);
            }
        }

        // Create a HttpClient with the ThreadSafeClientConnManager.
        // This connection manager must be used if more than one thread will
        // be using the HttpClient.
        ClientConnectionManager cm = new ThreadSafeClientConnManager(httpParams, schemeRegistry);

        httpClient = new DefaultHttpClient(cm, httpParams);

        // Enable cookie handling by setting cookie policy on HttpClient.
        httpClient.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.BEST_MATCH);

        log.debug("Created client connection: httpClient=" + httpClient);

        eService = Executors.newCachedThreadPool();

    }

    public RUser login(RAuthentication rAuthentication) throws RClientException, RSecurityException {

        return login(rAuthentication, false);
    }

    public RUser login(RAuthentication rAuthentication, boolean disableautosave)
            throws RClientException, RSecurityException {

        RCall rCall = null;

        if (rAuthentication instanceof RBasicAuthentication) {

            RBasicAuthentication basic = (RBasicAuthentication) rAuthentication;
            rCall = new LoginCall(basic.getUsername(), basic.getPassword(), disableautosave);
        }

        RCoreResult rResult = processCall(rCall);

        Map<String, String> identity = rResult.getIdentity();
        Map<String, Integer> limits = rResult.getLimits();

        //
        // Store cookie from response header, we no longer return this value in
        // the DeployR response markup as of `8.0.5`
        //
        CookieStore cookieStore = httpClient.getCookieStore();
        Cookie cookie = cookieStore.getCookies().get(0);
        this.httpcookie = (cookie != null ? cookie.getValue() : null);

        //
        // - Store `X-XSRF-TOKEN` from `/r/uer/login` for future authenticated 
        //   requests
        //         
        for (Header header : rResult.getHeaders()) {
            if (header.getName().equals(XSRF_HEADER)) {
                this.csrf = header.getValue();
            }
        }

        RUserDetails userDetails = REntityUtil.getUserDetails(identity, limits, csrf);

        liveContext = new RLiveContext(this, serverurl, httpcookie);

        return new RUserImpl(userDetails, liveContext);
    }

    public void logout(RUser user) throws RClientException, RSecurityException {

        RUserDetails about = (user != null) ? ((RUserImpl) user).about : null;
        RCall rCall = new LogoutCall(about);
        processCall(rCall);
    }

    public RScriptExecution executeScript(String scriptName, String scriptAuthor, String scriptVersion)
            throws RClientException, RSecurityException, RDataException, RGridException {
        return executeScript(scriptName, RRepositoryDirectory.ROOT, scriptAuthor, scriptVersion, null);
    }

    public RScriptExecution executeScript(String scriptName, String scriptAuthor, String scriptVersion,
            AnonymousProjectExecutionOptions executionOptions)
            throws RClientException, RSecurityException, RDataException, RGridException {
        return executeScript(scriptName, RRepositoryDirectory.ROOT, scriptAuthor, scriptVersion, executionOptions);
    }

    public RScriptExecution executeScript(String scriptName, String scriptDirectory, String scriptAuthor,
            String scriptVersion) throws RClientException, RSecurityException, RDataException, RGridException {
        return executeScript(scriptName, scriptDirectory, scriptAuthor, scriptVersion, null);
    }

    public RScriptExecution executeScript(String scriptName, String scriptDirectory, String scriptAuthor,
            String scriptVersion, AnonymousProjectExecutionOptions executionOptions)
            throws RClientException, RSecurityException, RDataException, RGridException {

        RCall rCall = new RepositoryScriptExecuteCall(scriptName, scriptDirectory, scriptAuthor, scriptVersion,
                null, executionOptions);

        RCoreResult rResult = processCallOnGrid(rCall);

        Map projectMap = rResult.getProject();
        RProjectDetails projectDetails = REntityUtil.getProjectDetails(projectMap);

        Map execution = (Map) rResult.getExecution();
        List<RData> workspaceObjects = rResult.getRObjects();
        List<Map> repofiles = rResult.getRepoFiles();
        String error = rResult.getError();
        int errorCode = rResult.getErrorCode();
        liveContext = new RLiveContext(this, serverurl, httpcookie);

        RProjectExecutionDetails execDetails = REntityUtil.getProjectExecutionDetails(projectDetails, execution,
                workspaceObjects, repofiles, error, errorCode, liveContext);
        RScriptExecution rExecution = new RScriptExecutionImpl(projectDetails, execDetails, liveContext);

        boolean success = rResult.isSuccess();

        log.debug("executeScript: success=" + success + " error=" + error + " errorCode=" + errorCode);

        return rExecution;
    }

    public RScriptExecution executeExternal(String externalSource,
            AnonymousProjectExecutionOptions executionOptions)
            throws RClientException, RSecurityException, RDataException, RGridException {

        RCall rCall = new RepositoryScriptExecuteCall(null, null, null, null, externalSource, executionOptions);
        RCoreResult rResult = processCallOnGrid(rCall);

        Map projectMap = rResult.getProject();
        RProjectDetails projectDetails = REntityUtil.getProjectDetails(projectMap);

        Map execution = (Map) rResult.getExecution();
        List<RData> workspaceObjects = rResult.getRObjects();
        List<Map> repofiles = rResult.getRepoFiles();
        String error = rResult.getError();
        int errorCode = rResult.getErrorCode();
        liveContext = new RLiveContext(this, serverurl, httpcookie);

        RProjectExecutionDetails execDetails = REntityUtil.getProjectExecutionDetails(projectDetails, execution,
                workspaceObjects, repofiles, error, errorCode, liveContext);
        RScriptExecution rExecution = new RScriptExecutionImpl(projectDetails, execDetails, liveContext);

        boolean success = rResult.isSuccess();

        log.debug("executeExternal: success=" + success + " error=" + error + " errorCode=" + errorCode);

        return rExecution;
    }

    public URL renderScript(String scriptName, String scriptDirectory, String scriptAuthor, String scriptVersion,
            AnonymousProjectExecutionOptions options) throws RClientException, RDataException {

        URL renderURL;

        try {

            String urlPath = serverurl + REndpoints.RREPOSITORYSCRIPTRENDER;
            if (httpcookie != null) {
                urlPath += ";jsessionid=" + httpcookie;
            }
            URIBuilder builder = new URIBuilder(urlPath);

            builder.addParameter("filename", scriptName);
            builder.addParameter("directory", scriptDirectory);
            builder.addParameter("author", scriptAuthor);

            if (scriptVersion != null) {
                builder.addParameter("version", scriptVersion);
            }

            if (options != null) {

                builder.addParameter("blackbox", Boolean.toString(options.blackbox));
                builder.addParameter("recycle", Boolean.toString(options.recycle));

                if (options.rinputs != null) {
                    String markup = RDataUtil.toJSON(options.rinputs);
                    builder.addParameter("inputs", markup);
                }

                String objectNames = null;
                if (options.routputs != null) {
                    for (String object : options.routputs) {
                        if (objectNames != null) {
                            objectNames = objectNames + "," + object;
                        } else {
                            objectNames = object;
                        }
                    }
                }
                builder.addParameter("robjects", objectNames);

                builder.addParameter("tag", options.tag);
                builder.addParameter("echooff", Boolean.toString(options.echooff));
                builder.addParameter("consoleoff", Boolean.toString(options.consoleoff));
                builder.addParameter("encodeDataFramePrimitiveAsVector",
                        Boolean.toString(options.encodeDataFramePrimitiveAsVector));

                if (options.preloadWorkspace != null) {
                    builder.addParameter("preloadobjectname", options.preloadWorkspace.filename);
                    builder.addParameter("preloadobjectauthor", options.preloadWorkspace.author);
                    builder.addParameter("preloadobjectversion", options.preloadWorkspace.version);
                }

                if (options.preloadDirectory != null) {
                    builder.addParameter("preloadfilename", options.preloadWorkspace.filename);
                    builder.addParameter("preloadfileauthor", options.preloadWorkspace.author);
                    builder.addParameter("preloadfileversion", options.preloadWorkspace.version);
                }

                if (options.adoptionOptions != null) {
                    builder.addParameter("adoptworkspace", options.adoptionOptions.adoptWorkspace);
                    builder.addParameter("adoptdirectory", options.adoptionOptions.adoptDirectory);
                    builder.addParameter("adoptpackages", options.adoptionOptions.adoptPackages);
                }

                if (options.storageOptions != null) {
                    builder.addParameter("storefile", options.storageOptions.files);
                    builder.addParameter("storeobjct", options.storageOptions.objects);
                    builder.addParameter("storeworkspace", options.storageOptions.workspace);
                    builder.addParameter("storenewversion", Boolean.toString(options.storageOptions.newversion));
                    builder.addParameter("storepublic", Boolean.toString(options.storageOptions.published));
                }

                builder.addParameter("nan", options.nan);
                builder.addParameter("infinity", options.infinity);
                builder.addParameter("graphics", options.graphicsDevice);
                builder.addParameter("graphicswidth", Integer.toString(options.graphicsWidth));
                builder.addParameter("graphicsheight", Integer.toString(options.graphicsHeight));

            }

            renderURL = builder.build().toURL();
        } catch (Exception uex) {
            throw new RClientException("Render url: ex=" + uex.getMessage());
        }
        return renderURL;
    }

    public void interruptScript() throws RClientException, RSecurityException {

        RCall rCall = new RepositoryScriptInterruptCall();
        RCoreResult rResult = processCall(rCall);

        String error = rResult.getError();
        int errorCode = rResult.getErrorCode();
        boolean success = rResult.isSuccess();

        log.debug("interruptScript: success=" + success + " error=" + error + " errorCode=" + errorCode);
    }

    public void release() {

        try {
            logout(null);
        } catch (Exception ex) {
            // Ignore any exceptions on forced logout operation.
        }

        try {
            eService.shutdown();
        } catch (Exception ex) {
            log.debug("Exception shutting down the executor service.", ex);
        }

        log.debug("RClient eService.isShutdown=" + eService.isShutdown());

        try {
            httpClient.getConnectionManager().shutdown();
        } catch (Exception ex) {
            log.debug("Exception shutting down the HTTP connection manager.", ex);
        }

        log.debug("RClient shutdown completes.");
    }

    // RClientExecutor interface implementation.

    public RCoreResult processCall(RCall rCall) throws RClientException, RSecurityException {

        RCoreResponse pResponse = execute(rCall);

        RCoreResult rResult = null;

        try {
            rResult = pResponse.get();

            log.debug("processCall: pCoreResult.statusMsg=" + rResult.getStatusMsg() + ", statusCode="
                    + rResult.getStatusCode());

            if (rResult.getStatusCode() == 200) {
                if (!rResult.isSuccess())
                    throw new RClientException(rResult.getError());
            } else if (rResult.getStatusCode() == 401) {
                throw new RSecurityException("Unauthenticated call.", 401);
            } else if (rResult.getStatusCode() == 403) {
                throw new RSecurityException("Unauthorized call.", 403);
            } else if (rResult.getStatusCode() == 409) {
                throw new RSecurityException("Conflict on call.", 409);
            } else {
                throw new RClientException(rResult.getStatusMsg(), rResult.getStatusCode());
            }

        } catch (RSecurityException psex) {
            throw psex;
        } catch (RClientException pcex) {
            throw pcex;
        } catch (Exception ex) {
            throw new RClientException(ex.getMessage(), ex);
        }

        return rResult;
    }

    public RCoreResult processCallOnGrid(RCall rCall) throws RClientException, RSecurityException, RGridException {

        RCoreResponse pResponse = execute(rCall);

        RCoreResult rResult = null;

        try {
            rResult = pResponse.get();

            log.debug("processCallOnGrid: pCoreResult.statusMsg=" + rResult.getStatusMsg() + ", statusCode="
                    + rResult.getStatusCode());

            if (rResult.getStatusCode() == 200) {

                if (!rResult.isSuccess()) {

                    if (GRID_EXCEPTION_CODES.contains(rResult.getErrorCode())) {
                        throw new RGridException(rResult.getError(), rResult.getErrorCode());
                    } else {
                        throw new RClientException(rResult.getError(), rResult.getErrorCode());
                    }
                }

            } else if (rResult.getStatusCode() == 401) {
                throw new RSecurityException("Unauthenticated call.", 401);
            } else if (rResult.getStatusCode() == 403) {
                throw new RSecurityException("Unauthorized call.", 403);
            } else if (rResult.getStatusCode() == 409) {
                throw new RSecurityException("Conflict on call.", 409);
            } else {
                throw new RClientException(rResult.getError(), rResult.getErrorCode());
            }

        } catch (RSecurityException secex) {
            throw secex;
        } catch (RClientException cex) {
            if (GRID_EXCEPTION_CODES.contains(rResult.getErrorCode())) {
                throw new RGridException(rResult.getError(), rResult.getErrorCode());
            } else {
                throw cex;
            }

        } catch (RGridException gex) {
            throw gex;
        } catch (Exception ex) {
            throw new RClientException(ex.getMessage(), ex);
        }

        return rResult;
    }

    public InputStream download(URIBuilder builder) throws RClientException {

        try {
            log.debug("download: uri builder=" + builder);
            String uriPath = builder.build().toString();
            HttpGet request = new HttpGet(uriPath);
            HttpResponse response = httpClient.execute(request);
            return response.getEntity().getContent();

        } catch (Exception ex) {
            String exMsg = builder + " download failed, " + ex.getMessage();
            throw new RClientException(exMsg, ex);
        }

    }

    // Private method implementations.

    public RCoreResponse execute(RCall call) {

        AbstractCall abstractCall = (AbstractCall) call;
        abstractCall.addHeader(XSRF_HEADER, csrf);

        // Provide httpClient and DeployR server url context to RCall.
        abstractCall.setClient(httpClient, serverurl);
        Callable callable = (Callable) call;
        // Wrap Callable in FutureTask for execution by the Executor Service.
        FutureTask task = new FutureTask(callable);
        abstractCall.setFuture(task);
        eService.submit(task);

        return (RCoreResponse) call;
    }
}