package xapi.shell; import xapi.inject.X_Inject; import xapi.io.api.HasLiveness; import xapi.io.api.LineReader; import xapi.io.api.StringReader; import xapi.log.X_Log; import xapi.reflect.X_Reflect; import xapi.shell.api.ShellSession; import xapi.shell.service.ShellService; import xapi.util.X_Properties; import xapi.util.X_String; import java.io.File; public class X_Shell { private static String[] args; private static Class<?> main; private X_Shell() {} public static ShellService newService() { return X_Inject.instance(ShellService.class); } public static HasLiveness liveChecker(final Process process) { return new HasLiveness() { @Override public boolean isAlive() { try { int exit = process.exitValue(); X_Log.debug("Process ended with exit code " + exit); return false; } catch (IllegalThreadStateException e) { return true; } } }; } public static void rememberArgs(Class<?> mainClass, String... args) { X_Shell.args = args; main = mainClass; } /** * Launches a java process inside a shell environment. * Currently, only the unix shell works, using xapi-dev-shell/src/main/resources/xapi/sh.sh, * and is only tested on linux. * * @param mainClass - The class name with the main method to run * @param classpath - The classpath for this execution. * @return A {@link ShellSession} used to control the running process. */ public static ShellSession launchJava(Class<?> mainClass, String[] classpath) { return launchJava(mainClass, classpath, new String[0], new String[0]); } /** * Launches a java process inside a shell environment. * Currently, only the unix shell works, using xapi-dev-shell/src/main/resources/xapi/sh.sh, * and is only tested on linux. * * @param mainClass - The class name with the main method to run * @param classpath - The classpath for this execution. * @param vmFlags - Flags to pass to jvm (like system properties) * @param args - Arguments to pass to main method * @return A {@link ShellSession} used to control the running process. */ public static ShellSession launchJava(Class<?> mainClass, String[] classpath, String[] vmFlags, String[] args) { // Check if mainClass is in a folder or in a jar, so we know if we need to add it to the classpath try { exists_check: { String fileLoc = X_Reflect.getFileLoc(mainClass); for (String item : classpath) { if (item.equals(fileLoc)) { break exists_check; } } String[] newClasspath = new String[classpath.length + 1]; System.arraycopy(classpath, 0, newClasspath, 0, classpath.length); newClasspath[classpath.length] = fileLoc; classpath = newClasspath; } } catch (Exception e) { X_Log.warn(ShellSession.class, "Error appending location of ", mainClass, "to classpath", classpath, e); } String javaHome = System.getProperty("java.home"); String javaBin = javaHome + File.separator + "bin" + File.separator + "java"; String[] javaArgs = new String[4 + vmFlags.length + args.length]; javaArgs[0] = javaBin; int pos = 1; if (vmFlags != null && vmFlags.length > 0) { System.arraycopy(vmFlags, 0, javaArgs, pos, vmFlags.length); pos += vmFlags.length; } javaArgs[pos] = "-classpath"; javaArgs[++pos] = X_String.join(File.pathSeparator, classpath).trim(); javaArgs[++pos] = mainClass.getCanonicalName(); X_Log.info(X_Shell.class, "Running java command", mainClass, args); if (args != null && args.length > 0) { System.arraycopy(args, 0, javaArgs, ++pos, args.length); } X_Log.info(X_Shell.class, "Java command", X_String.join(" ", javaArgs)); return globalService().runInShell( false, new StringReader(), new StringReader(), javaArgs); } public static ShellService globalService() { return X_Inject.singleton(ShellService.class); } public static ShellSession launchInShell(String cmd, LineReader stdOut, LineReader stdErr) { X_Log.trace(X_Shell.class, "Running in shell\n", cmd); return globalService().runInShell( false , stdOut , stdErr, cmd); } public static void restartSelf() { if (main == null) { X_Log.error("Cannot use restartSelf until calling rememberArgs(Class<?> mainClass, String[] args)"); return; } String[] arguments = (args == null) ? new String[0] : args; // Get our classpath. String cp = X_Properties.getProperty("java.class.path"); if (cp == null) { X_Log.error("Unable to detect X_Property 'java.class.path'; unable to restart application"); return; } // TODO find vm args somehow? Take as parameter? Just check a bunch of runtime settings? // Finding memory for Xmx would be simple enough, but we need to check management beans for better results launchJava(main, cp.split("[" + File.pathSeparator + "]"), new String[]{}, arguments); } }