All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.openfin.desktop.RuntimeLauncher Maven / Gradle / Ivy

There is a newer version: 11.0.2
Show newest version
package com.openfin.desktop;

import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.util.*;

import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Launch Runtime from embedded RVM
 *
 * @author wche
 * @since 11/19/14
 */
public class RuntimeLauncher {
    private final static Logger logger = LoggerFactory.getLogger(RuntimeLauncher.class.getName());

    private static final String INSTALLER_FILENAME = "OpenFinInstaller";  // default name for installer executable
    private static final String RVM_FILENAME ="OpenFinRVM";
    private static final String WIN_LOCAL_APP_DATA = "LOCALAPPDATA";  // set Windows platform @todo read registry
    private static final String INSTALLER_TMP_DIR_PROP = "com.openfin.temp";  // -Dopenfin.temp=xxx defines directory to extract installer.exe to
    private static final String INSTALLER_LOC_PROP = "com.openfin.installer.location";  // -Dopenfin.installer.location=xxx defines full path of installer
                                                                                    // if set, it is assumed OpenFin installer can be run from the location
                                                                                    // and it does NOT need to be extracted from the jar
    private static final String INSTALLER_WORKDIR_PROP = "com.openfin.installer.workdir"; // work directory of Installer

    private static       String INSTALLER_TMP_DIR;
    private static       String INSTALLER_LOCATION;
    private static final boolean needExtractInstaller;  // if yes, installer is assumed to exist already and no need to extract from the jar

    private static final String OpenFinRvmRegistryKey = "HKEY_CURRENT_USER\\SOFTWARE\\OpenFin\\RVM";
    private static final String WorkingDirRegistryValueName = "installDir";
    private static final String InstallDirectory = "OpenFin";

    private static final String ADAPTER_VERSION_LOCATION;

    private static final String INSTALLER_SECURITY_SCAN_WAIT_TIME_PROP = "openfin.installer.scan.wait.time";
    private static long   INSTALLER_SECURITY_SCAN_WAIT_TIME = 0; // installer.exe is scanned Windows defender on some systems after being extracted.
                                                                          // if run before scan is finished, it may fail.
    static {
        if (java.lang.System.getProperty(INSTALLER_LOC_PROP) != null) {
            INSTALLER_LOCATION = java.lang.System.getProperty(INSTALLER_LOC_PROP);
        } else {
            INSTALLER_LOCATION = null;
        }

        if (java.lang.System.getProperty(INSTALLER_TMP_DIR_PROP) != null) {
            INSTALLER_TMP_DIR = java.lang.System.getProperty(INSTALLER_TMP_DIR_PROP);
        }
//        else if (java.lang.System.getenv(WIN_LOCAL_APP_DATA) != null) {
//            INSTALLER_TMP_DIR = java.lang.System.getenv(WIN_LOCAL_APP_DATA) + File.separator + "OpenFin" + File.separator + "cache"  + File.separator + "java";
//        }
        else {
            INSTALLER_TMP_DIR = java.lang.System.getProperty("java.io.tmpdir");
        }
        if (INSTALLER_LOCATION == null) {
            if (!INSTALLER_TMP_DIR.endsWith(File.separator)) {
                INSTALLER_TMP_DIR += File.separator;
            }
            INSTALLER_TMP_DIR += "openfinjava";
            INSTALLER_LOCATION = INSTALLER_TMP_DIR + File.separator + INSTALLER_FILENAME + ".exe";
            needExtractInstaller = true;
        } else {
            needExtractInstaller = false;
        }
        ADAPTER_VERSION_LOCATION = INSTALLER_TMP_DIR + File.separator + INSTALLER_FILENAME + "JavaAdapter.ver";

        String waitTime = java.lang.System.getProperty(INSTALLER_SECURITY_SCAN_WAIT_TIME_PROP);
        if (waitTime != null) {
            INSTALLER_SECURITY_SCAN_WAIT_TIME = Long.parseLong(waitTime);
        }
    }

    private static String SECURITY_REALM_SETTING = "--security-realm=";

    /**
     * Launch Desktop from the specified remote config
     *
     * @param runtimeConfigUrl remote config location
     *
     */
    public static void launchConfig(String runtimeConfigUrl) {
        if (runtimeConfigUrl == null) {
            throw new IllegalArgumentException("Desktop config must be specified ");
        }
        try {
            if (!getExistingRuntime()) {
                extractInstaller(INSTALLER_FILENAME + ".exe");
            }
            runDesktop(runtimeConfigUrl);
        } catch (Exception e) {
            logger.error("Exception in launchConfig", e);
        }
    }

    /**
     * Launch the specified version of Desktop
     *
     * @param configuration Runtime configuration
     * @param connectionUuid UUID of the connection
     * @throws IOException if IO errors are thrown
     *
     */
    static void launchVersion(RuntimeConfiguration configuration, String connectionUuid) throws IOException {
        if (configuration.getRuntimeVersion() == null) {
            throw new IllegalArgumentException("Desktop version must be specified ");
        }
        if (!getExistingRuntime()) {
            //extractZip(INSTALLER_FILENAME + ".zip");
            extractInstaller(INSTALLER_FILENAME + ".exe");
        }
        StringBuffer installerArguments = new StringBuffer();
        if (needExtractInstaller) {
            installerArguments.append(configuration.isShowInstallerUI() ? " " : " --no-installer-ui ");
            installerArguments.append(" --config=\"" + createRVMConfig(configuration, connectionUuid) + "\"");
            if (configuration.getRuntimeAssetURL() != null && configuration.getRuntimeAssetURL().length() > 0) {
                installerArguments.append(" --assetsUrl=\"" + configuration.getRuntimeAssetURL() + "\"");
            }
            if (configuration.getAdditionalRvmArguments() != null && configuration.getAdditionalRvmArguments().length() > 0) {
                installerArguments.append(" ");
                installerArguments.append(configuration.getAdditionalRvmArguments());
            }
        } else {
            logger.debug("skip checking existing installer");
            installerArguments.append(String.format(" %s", createRVMConfig(configuration, connectionUuid)));
        }
        runDesktop(installerArguments.toString());
    }

    /**
     * Checks if runtime executable is already unzipped
     *
     * @return true if already unzipped
     *
     */
    private static boolean getExistingRuntime() {
        boolean existing = false;
        try {
            if (needExtractInstaller) {
                String exitingV = getCachedVersion();
                String currentV = System.getAdapterVersion();
                if (exitingV != null && currentV != null && currentV.equals(exitingV)) {
                    File vfile = new File(INSTALLER_LOCATION);
                    if (vfile.exists()) {
                        existing = true;
                        logger.info("already exists, skip unpacking " + INSTALLER_LOCATION);
                    }
                } else {
                    logger.debug("outdated cached adapter version, unpacking installer");
                }
            } else {
                logger.debug("skip checking existing installer");
                existing = true;
            }
        } catch (Exception e) {
            existing = true;
            logger.debug("Exception from getExistingRuntime", e);
        }
        return existing;
    }

    /**
     * Get cached version number of java adapter
     * @return v# of java adapter
     */
    private static String getCachedVersion() {
        String version = null;
        try {
            File vfile = new File(ADAPTER_VERSION_LOCATION);
            if (vfile.exists()) {
                BufferedReader reader = new BufferedReader(new FileReader(vfile));
                version = reader.readLine();
                reader.close();
            }
        } catch (Exception e) {
            logger.debug("Exception in getCachedVersion", e);
        }
        logger.debug("found cached adapter version " + version + " current version " + System.getAdapterVersion());
        return version;
    }

    private static void extractInstaller(String zipName) {
        try {
            if (needExtractInstaller) {
                logger.info("loading resource " + zipName);
                InputStream in = RuntimeLauncher.class.getClassLoader().getResourceAsStream(zipName);
                if (in != null) {
                    createDir(INSTALLER_TMP_DIR);
                    String filePath = INSTALLER_TMP_DIR + File.separator + zipName;
                    extractFile(in, filePath);
                    in.close();
                    updateCachedVersion();
                } else {
                    logger.error("resource " + zipName + " missing ");
                }
            } else {
                logger.info("do not need to extract " + zipName);
            }
        } catch (Exception e) {
            logger.error("Exception in extractZip", e);
        }
    }

    /**
     *
     * Save current version number of java adapter to local file
     *
     */
    private static void updateCachedVersion() {
        String version = System.getAdapterVersion();
        if (version != null) {
            try {
                logger.debug("Updating " + ADAPTER_VERSION_LOCATION);
                File vfile = new File(ADAPTER_VERSION_LOCATION);
                BufferedWriter writer = new BufferedWriter(new FileWriter(vfile, false));
                writer.write(version);
                writer.close();
            } catch (Exception e) {
                logger.debug("Exception in updateCachedVersion", e);
            }
            logger.debug("set existing adapter version " + version );
        }
    }

    /**
     * Create directory for the specified path
     *
     * @param path
     */
    private static void createDir(String path) {
        File dir = new File(path);
        if (!dir.exists()) {
            logger.debug("creating dirs " + path);
            dir.mkdirs();
        }
    }

    /**
     * Copies a ZipInputStream to a file in the path
     *
     * @param zipIn zip input stream
     * @param filePath destination file path
     * @throws IOException
     *
     */
    private static void extractFile(InputStream zipIn, String filePath) throws IOException {
        logger.info("extracting " + filePath);
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath));
        byte[] bytesIn = new byte[2048];
        int read = 0;
        while ((read = zipIn.read(bytesIn)) != -1) {
            bos.write(bytesIn, 0, read);
        }
        bos.close();
        if (INSTALLER_SECURITY_SCAN_WAIT_TIME > 0) {
            logger.debug("sleep to wait for security scan " + INSTALLER_SECURITY_SCAN_WAIT_TIME);
            try {
                Thread.sleep(INSTALLER_SECURITY_SCAN_WAIT_TIME);
            } catch (InterruptedException e) {
            }
        }
    }

    /**
     * Run runtime executable in command line
     *
     * @param runtimeOptions command line options
     * @throws IOException
     *
     */
    private static void runDesktop(final String runtimeOptions) throws IOException {
        Thread thread = new Thread() {
            public void run() {
                try {
                    String executable = INSTALLER_LOCATION;
                    logger.info("starting: " + executable + " " + runtimeOptions);
                    StringTokenizer tokenizer = new StringTokenizer(runtimeOptions, " ");
                    List args = new ArrayList();
                    args.add(executable);
                    while (tokenizer.hasMoreTokens()) {
                        args.add(tokenizer.nextToken());
                    }
                    ProcessBuilder pb = new ProcessBuilder(args);
                    String workDir = java.lang.System.getProperty(INSTALLER_WORKDIR_PROP);
                    if (workDir != null) {
                        logger.info("with working directory: " + workDir);
                        pb.directory(new File(workDir));
                    }
                    Process process = pb.start();
                    StreamReader seInfo = new StreamReader(process.getInputStream(), false);
                    StreamReader seError = new StreamReader(process.getErrorStream(), true);
                    seInfo.start();
                    seError.start();
                    int status = process.waitFor();
                    seInfo.join();
                    seError.join();
                    logger.info("launching process returned " + status);
                } catch (Exception ex) {
                    logger.debug("Exception in runDesktop thread", ex);
                }
                logger.debug("runDesktop thread exiting");
            }
        };
        thread.setName(RuntimeLauncher.class.getName() + ".runDesktop");
        thread.start();
    }

    private static Path generateLocalManifestFilePath(RuntimeConfiguration configuration, String connectionUuid) {
        createDir(INSTALLER_TMP_DIR);
        StringBuilder filename = new StringBuilder("OFJava-");
        try {
            JSONObject json = new JSONObject();
            json.put("uuid", connectionUuid);
            json.put("runtime", configuration.getRuntimeVersion());
            if (configuration.getSecurityRealm() != null) {
                json.put("realm", configuration.getSecurityRealm());
            }
            byte[] bytes = json.toString().getBytes("UTF-8");
            filename.append(UUID.nameUUIDFromBytes(bytes).toString());
            logger.debug(String.format("Generating manifest file name %s from %s", filename, json.toString()));
        } catch (Exception ex) {
            logger.error("Error generating manifest filename, Using random UUID", ex);
            filename.append(UUID.randomUUID().toString());
        }
        filename.append(".json");
        Path filePath = Paths.get(INSTALLER_TMP_DIR, filename.toString());
        return filePath;
    }

    /**
     * Dynamically create RVM remote config file
     *
     * @param configuration Runtime configuration
     * @param connectionUuid UUID of desktopConnection
     * @return file name
     * @throws Exception
     *
     */
    private static String createRVMConfig(RuntimeConfiguration configuration, String connectionUuid) throws IOException {
        if (configuration.getManifestLocation() == null) {
            String configText = configuration.generateRuntimeConfig();
            logger.debug(configText);
            Path configPath = generateLocalManifestFilePath(configuration, connectionUuid);
            OutputStream configFile = Files.newOutputStream(configPath);
            PrintWriter out = new PrintWriter(configFile);
            out.print(configText);
            out.close();
            configuration.setGeneratedManifestLocation(Paths.get(configPath.toUri()).toUri().toString());
            return configuration.getGeneratedManifestLocation();
        } else {
            // download the manifest and parse runtime version and security realm
            URL url = new URL(configuration.getManifestLocation());
            BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
            StringBuilder buffer = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                buffer.append(line);
            }
            reader.close();
            JSONObject manifest = new JSONObject(buffer.toString());
            logger.debug("Got app manifest", manifest);
            if (manifest.has("assetsUrl")) {
                String assetsUrl = manifest.getString("assetsUrl");
                logger.debug("Parsed assetsUrl " + assetsUrl);
                configuration.setRuntimeAssetURL(assetsUrl);
            }
            if (manifest.has("runtime")){
                JSONObject runtime = manifest.getJSONObject("runtime");
                if (runtime.has("version")){
                    configuration.setRuntimeVersion(runtime.getString("version"));
                    logger.debug("Parsed runtime version " + configuration.getRuntimeVersion());
                } else {
                    throw new IllegalArgumentException("Missing runtime version setting from remote manifest");
                }
                if (runtime.has("fallbackVersion")) {
                    configuration.setRuntimeFallbackVersion(runtime.getString("fallbackVersion"));
                }
                if (runtime.has("arguments")) {
                    String arguments = runtime.getString("arguments");
                    int index = arguments.indexOf(SECURITY_REALM_SETTING);
                    if (index >= 0) {
                        StringTokenizer tokenizer = new StringTokenizer(arguments, " ");
                        while (tokenizer.hasMoreTokens()) {
                            String token = tokenizer.nextToken();
                            if (token.startsWith(SECURITY_REALM_SETTING)) {
                                String securityRealm = token.substring(SECURITY_REALM_SETTING.length());
                                if (securityRealm.length() > 0) {
                                    configuration.setSecurityRealm(securityRealm);
                                    logger.debug("Parsed security realm " + configuration.getSecurityRealm());
                                }
                            }
                        }
                    }
                }
            } else  {
                throw new IllegalArgumentException("Missing runtime setting from remote manifest");
            }
            return configuration.getManifestLocation();
        }
    }

    private static class StreamReader extends Thread {
        private InputStream in;
        private boolean isErrorStream;
        StreamReader(InputStream in, boolean isErrorStream) {
            this.in = in;
            this.isErrorStream = isErrorStream;
            this.setName(RuntimeLauncher.class.getName() + ".streamReader");
        }
        @Override
        public void run() {
            logger.debug("starting");
            BufferedReader br = null;
            try {
                br = new BufferedReader(new InputStreamReader(in));
                String line = null;
                while ((line = br.readLine()) != null) {
                    if (isErrorStream) {
                        logger.error(line);
                    } else {
                        logger.debug(line);
                    }
                }
            } catch (Exception e) {
                logger.error("Error reading stream", e);
            } finally {
                try {
                    if (br != null) {
                        br.close();
                    }
                } catch (IOException e) {
                    logger.error("Error closing stream", e);
                }
            }
            logger.debug("exiting");
        }
    }

    public static void main2(final String[] args) throws URISyntaxException, IOException, InterruptedException {
        String RUNTIME_APP_CONFIG = "OpenFinAppConfig";
        String RUNTIME_VERSION = "OpenFinRelease";
        String runOptions = java.lang.System.getProperty(RUNTIME_APP_CONFIG);
        String runtimeVersion = java.lang.System.getProperty(RUNTIME_VERSION);
    //        if (runOptions != null) {
    //            launchConfig(runOptions);
    //        } else {
    //            launchVersion(runtimeVersion, "", "");
    //        }

    //        RuntimeLauncher.runDesktop("--no-installer-ui --config=\"C:\\Users\\richard\\AppData\\Local\\Temp\\60c64938-878a-4cbf-8b46-3b44b3503255.json");

    //        java.lang.System.exit(0);


    //        extractInstaller(INSTALLER_FILENAME + ".exe");
    //        java.lang.System.out.printf("Starting " + INSTALLER_LOCATION);
    //        Thread.sleep(5000);
    //        Process process = Runtime.getRuntime().exec(INSTALLER_LOCATION);

        java.lang.System.out.println(java.lang.System.getProperty(INSTALLER_LOC_PROP));

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy