com.openfin.desktop.RuntimeLauncher Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openfin-desktop-java-adapter Show documentation
Show all versions of openfin-desktop-java-adapter Show documentation
The Java API for OpenFin Runtime
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 com.openfin.desktop.win32.RegistryHelper;
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 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 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 {
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 + RVM_FILENAME + ".exe";
needExtractInstaller = true;
} else {
needExtractInstaller = false;
}
ADAPTER_VERSION_LOCATION = INSTALLER_TMP_DIR + File.separator + RVM_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 (!getExistingInstaller()) {
extractInstaller(RVM_FILENAME + ".exe");
}
runDesktop(null, 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.getManifestLocation() == null) {
if (configuration.getRuntimeVersion() == null) {
throw new IllegalArgumentException("Runtime version must be specified ");
}
}
String executable = configuration.getLaunchRVMPath(); // if set, use it.
if (executable == null) {
executable = getExistingRVM(); // check RVM defined in Registry
}
if (executable == null) {
if (!getExistingInstaller()) {
extractInstaller(RVM_FILENAME + ".exe");
}
}
StringBuffer installerArguments = new StringBuffer();
installerArguments.append(" --config=\"" + createRVMConfig(configuration, connectionUuid) + "\"");
if (configuration.getAdditionalRvmArguments() != null && configuration.getAdditionalRvmArguments().length() > 0) {
installerArguments.append(" ");
installerArguments.append(configuration.getAdditionalRvmArguments());
}
runDesktop(executable, installerArguments.toString());
}
/**
* Checks if runtime executable is already unzipped
*
* @return true if already unzipped
*
*/
private static boolean getExistingInstaller() {
boolean existing = false;
try {
if (needExtractInstaller) {
String exitingV = getCachedVersion();
String currentV = OpenFinRuntime.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 getExistingInstaller", e);
}
return existing;
}
public static String getExistingRVM() {
String path = null;
try {
String rvmPath = RegistryHelper.getRVMInstallDirectory();
File rvmFile = new File(rvmPath + File.separator + RVM_FILENAME + ".exe");
if (rvmFile.exists()) {
path = rvmFile.getPath();
logger.debug(String.format("RVM already exists at %s ", rvmPath));
} else {
logger.debug(String.format("RVM missing at %s, need to unpack ", rvmPath));
}
} catch (Exception e) {
logger.debug("Exception from getExistingRVM", e);
}
return path;
}
/**
* 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 executableOverride, final String runtimeOptions) throws IOException {
Thread thread = new Thread() {
public void run() {
try {
String executable = executableOverride != null ? executableOverride : 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);
Process process = pb.start();
// looks like this is the only way to detach
process.getInputStream().close();
process.getErrorStream().close();
} catch (Exception ex) {
logger.debug("Exception in runDesktop thread", ex);
}
logger.debug("runDesktop thread exiting");
}
};
thread.setName(RuntimeLauncher.class.getName() + ".runRVM");
thread.start();
}
private static Path generateLocalManifestFilePath(RuntimeConfiguration configuration, String connectionUuid) {
createDir(INSTALLER_TMP_DIR);
StringBuilder filename = new StringBuilder();
try {
JSONObject json = new JSONObject();
json.put("uuid", connectionUuid);
json.put("runtime", configuration.getRuntimeVersion());
if (configuration.getSecurityRealm() != null) {
json.put("realm", configuration.getSecurityRealm());
}
if (configuration.getLocalManifestFileName() != null) {
filename.append(configuration.getLocalManifestFileName());
} else {
filename.append("OFJava-");
filename.append(connectionUuid);
filename.append("-");
filename.append(configuration.getRuntimeVersion());
if (configuration.getSecurityRealm() != null) {
filename.append("-");
filename.append(configuration.getSecurityRealm());
}
}
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");
return Paths.get(INSTALLER_TMP_DIR, filename.toString().replaceAll("\\s+",""));
}
/**
* 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 main(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(" C:\\Users\\wenju\\AppData\\Local\\OpenFin\\OpenFinRVM.exe --config=\"file:///C:/Users/wenju/AppData/Local/Temp/openfinjava/OFJava-af7b87ec-bd11-3914-bc6b-a92663b045e5.json\"");
RuntimeLauncher.runDesktop("C:\\Users\\wenju\\AppData\\Local\\OpenFin\\OpenFinRVM.exe", "--config=\"file:///C:/Users/wenju/AppData/Local/Temp/openfinjava/OFJava-af7b87ec-bd11-3914-bc6b-a92663b045e5.json\"");
Thread.sleep(30000);
// java.lang.System.out.println(java.lang.System.getProperty(INSTALLER_LOC_PROP));
}
}