¿Cómo puedo reiniciar una aplicación Java?

¿Cómo puedo reiniciar una aplicación Java AWT? Tengo un botón al que he adjuntado un controlador de eventos. ¿Qué código debería usar para reiniciar la aplicación?

Quiero hacer lo mismo que Application.Restart() en una aplicación C #.

Por supuesto, es posible reiniciar una aplicación Java.

El siguiente método muestra una forma de reiniciar una aplicación Java:

 public void restartApplication() { final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"; final File currentJar = new File(MyClassInTheJar.class.getProtectionDomain().getCodeSource().getLocation().toURI()); /* is it a jar file? */ if(!currentJar.getName().endsWith(".jar")) return; /* Build command: java -jar application.jar */ final ArrayList command = new ArrayList(); command.add(javaBin); command.add("-jar"); command.add(currentJar.getPath()); final ProcessBuilder builder = new ProcessBuilder(command); builder.start(); System.exit(0); } 

Básicamente hace lo siguiente:

  1. Encuentra el ejecutable de Java (utilicé el binario de Java aquí, pero eso depende de tus requisitos)
  2. Encuentre la aplicación (un jar en mi caso, usando la clase MyClassInTheJar para encontrar la ubicación del jar)
  3. Cree un comando para reiniciar el jar (usando el binario de Java en este caso)
  4. ¡Ejecutalo! (y así terminando la aplicación actual y comenzando de nuevo)
 import java.io.File; import java.io.IOException; import java.lang.management.ManagementFactory; public class Main { public static void main(String[] args) throws IOException, InterruptedException { StringBuilder cmd = new StringBuilder(); cmd.append(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java "); for (String jvmArg : ManagementFactory.getRuntimeMXBean().getInputArguments()) { cmd.append(jvmArg + " "); } cmd.append("-cp ").append(ManagementFactory.getRuntimeMXBean().getClassPath()).append(" "); cmd.append(Main.class.getName()).append(" "); for (String arg : args) { cmd.append(arg).append(" "); } Runtime.getRuntime().exec(cmd.toString()); System.exit(0); } } 

Dedicado a todos aquellos que dicen que es imposible.

Este progtwig recostack toda la información disponible para reconstruir la línea de comando original. Luego, lo inicia y, dado que es el mismo comando, su aplicación se inicia por segunda vez. Luego salimos del progtwig original, el progtwig hijo sigue ejecutándose (incluso bajo Linux) y hace exactamente lo mismo.

ADVERTENCIA : si ejecuta esto, tenga en cuenta que nunca termina de crear nuevos procesos, similar a una bomba de horquilla .

Básicamente, no puedes. Al menos no de manera confiable.

Para reiniciar un progtwig Java, necesita reiniciar la JVM. Para reiniciar la JVM necesitas

  1. Busque el java que se utilizó. Puede probar con System.getProperty("java.home") pero no hay garantía de que esto realmente apunte al iniciador que se usó para iniciar su aplicación. (El valor devuelto puede no apuntar al JRE utilizado para iniciar la aplicación o podría haber sido anulado por -Djava.home ).

  2. Es probable que desee respetar la configuración de memoria original, etc. ( -Xmx , -Xms , …) por lo que debe determinar qué configuración utilizó para iniciar la primera JVM. Puede intentar usar ManagementFactory.getRuntimeMXBean().getInputArguments() pero no hay garantía de que esto refleje la configuración utilizada. Esto está incluso explicado en la documentación de ese método:

    Por lo general, no todas las opciones de línea de comandos para el comando ‘java’ se pasan a la máquina virtual Java. Por lo tanto, los argumentos de entrada devueltos pueden no incluir todas las opciones de línea de comandos.

  3. Si su progtwig lee la entrada de Standard.in el stdin original se perderá en el reinicio.

  4. Muchos de estos trucos y hacks fallarán en presencia de un SecurityManager .


Por otro lado: no deberías necesitarlo.

Le recomiendo que diseñe su aplicación para que sea fácil limpiar todo y luego crear una nueva instancia de su clase “principal”.

Muchas aplicaciones están diseñadas para hacer nada más que crear una instancia en el método principal:

 public class MainClass { ... public static void main(String[] args) { new MainClass().launch(); } ... } 

Al usar este patrón, debería ser lo suficientemente fácil hacer algo como:

 public class MainClass { ... public static void main(String[] args) { boolean restart; do { restart = new MainClass().launch(); } while (restart); } ... } 

y let launch() devuelve verdadero si y solo si la aplicación se apagó de manera que necesita reiniciarse.

Estrictamente hablando, un progtwig Java no puede reiniciarse ya que para hacerlo debe matar a la JVM en la que se está ejecutando y luego volver a iniciarla, pero una vez que la JVM ya no se ejecuta (se mata), entonces no se puede realizar ninguna acción.

Podría hacer algunos trucos con cargadores de clases personalizados para cargar, empaquetar e iniciar nuevamente los componentes AWT, pero esto probablemente causará muchos dolores de cabeza con respecto al bucle de eventos GUI.

Dependiendo de cómo se lance la aplicación, puede iniciar la JVM en una secuencia de comandos contenedora que contenga un ciclo do / while, que continúa mientras la JVM sale con un código particular, luego la aplicación AWT debería llamar a System.exit(RESTART_CODE) . Por ejemplo, en el pseudocódigo de scripting:

 DO # Launch the awt program EXIT_CODE = # Get the exit code of the last process WHILE (EXIT_CODE == RESTART_CODE) 

La aplicación AWT debe salir de la JVM con algo más que RESTART_CODE en la terminación “normal” que no requiere reinicio.

Eclipse normalmente se reinicia después de instalar un complemento. Lo hacen utilizando un contenedor eclipse.exe (aplicación de inicio) para Windows. Esta aplicación ejecuta el jar del corredor de eclipse central y si la aplicación eclipse java termina con un código de reinicio, eclipse.exe reinicia el banco de trabajo. Puede construir un bit similar de código nativo, script de shell u otro contenedor de código Java para lograr el reinicio.

Si realmente necesita reiniciar su aplicación, podría escribir una aplicación por separado para iniciarla …

Esta página proporciona muchos ejemplos diferentes para diferentes escenarios:

http://www.rgagnon.com/javadetails/java-0014.html

Aunque esta pregunta es antigua y respondida, me encontré con un problema con algunas de las soluciones y decidí agregar mi sugerencia a la mezcla.

El problema con algunas de las soluciones es que construyen una sola cadena de comando. Esto crea problemas cuando algunos parámetros contienen espacios, especialmente java.home .

Por ejemplo, en Windows, la línea

 final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"; 

Podría devolver algo como esto: C:\Program Files\Java\jre7\bin\java

Esta cadena debe estar entre comillas o escapada debido al espacio en Program Files . No es un problema enorme, pero algo molesto y propenso a errores, especialmente en aplicaciones multiplataforma.

Por lo tanto, mi solución crea el comando como una matriz de comandos:

 public static void restart(String[] args) { ArrayList commands = new ArrayList(4 + jvmArgs.size() + args.length); List jvmArgs = ManagementFactory.getRuntimeMXBean().getInputArguments(); // Java commands.add(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"); // Jvm arguments for (String jvmArg : jvmArgs) { commands.add(jvmArg); } // Classpath commands.add("-cp"); commands.add(ManagementFactory.getRuntimeMXBean().getClassPath()); // Class to be executed commands.add(BGAgent.class.getName()); // Command line arguments for (String arg : args) { commands.add(arg); } File workingDir = null; // Null working dir means that the child uses the same working directory String[] env = null; // Null env means that the child uses the same environment String[] commandArray = new String[commands.size()]; commandArray = commands.toArray(commandArray); try { Runtime.getRuntime().exec(commandArray, env, workingDir); System.exit(0); } catch (IOException e) { e.printStackTrace(); } } 

Yo mismo estaba investigando el tema cuando me encontré con esta pregunta.

Independientemente del hecho de que la respuesta ya se haya aceptado, aún me gustaría ofrecer un enfoque alternativo para la integridad. Específicamente, Apache Ant fue una solución muy flexible.

Básicamente, todo se reduce a un archivo de script Ant con una única tarea de ejecución Java (consulte aquí y aquí ) invocada desde un código Java (consulte aquí ). Este código de Java, que puede ser un lanzamiento de método, podría ser una parte de la aplicación que necesita reiniciarse. La aplicación necesita tener una dependencia en la biblioteca Apache Ant (jar).

Cuando sea necesario reiniciar la aplicación, debe llamar al método de inicio y salir de la máquina virtual. La tarea Ant java debe tener las opciones fork y spawn establecidas en true.

Aquí hay un ejemplo de un script Ant:

        ...      

El código para el método de lanzamiento puede verse más o menos así:

 public final void launch(final String antScriptFile) { /* configure Ant and execute the task */ final File buildFile = new File(antScriptFile); final Project p = new Project(); p.setUserProperty("ant.file", buildFile.getAbsolutePath()); final DefaultLogger consoleLogger = new DefaultLogger(); consoleLogger.setErrorPrintStream(System.err); consoleLogger.setOutputPrintStream(System.out); consoleLogger.setMessageOutputLevel(Project.MSG_INFO); p.addBuildListener(consoleLogger); try { p.fireBuildStarted(); p.init(); final ProjectHelper helper = ProjectHelper.getProjectHelper(); p.addReference("ant.projectHelper", helper); helper.parse(p, buildFile); p.executeTarget(p.getDefaultTarget()); p.fireBuildFinished(null); } catch (final BuildException e) { p.fireBuildFinished(e); } /* exit the current VM */ System.exit(0); 

}

Una cosa muy conveniente aquí es que la misma secuencia de comandos se usa para el inicio de la aplicación inicial y para los reinicios.

Windows

 public void restartApp(){ // This launches a new instance of application dirctly, // remember to add some sleep to the start of the cmd file to make sure current instance is // completely terminated, otherwise 2 instances of the application can overlap causing strange // things:) new ProcessBuilder("cmd","/c start /min c:/path/to/script/that/launches/my/application.cmd ^& exit").start(); System.exit(0); } 

/ min para iniciar el script en ventana minimizada

^ y salir para cerrar la ventana de cmd después de finalizar

un script de cmd de muestra podría ser

 @echo off rem add some sleep (eg 10 seconds) to allow the preceding application instance to release any open resources (like ports) and exit gracefully, otherwise the new instance could fail to start sleep 10 set path=C:\someFolder\application_lib\libs;%path% java -jar application.jar 

dormir 10 horas de sueño por 10 segundos

Simplemente agregando información que no está presente en otras respuestas.

Si procfs /proc/self/cmdline está disponible

Si está ejecutando en un entorno que proporciona procfs y, por lo tanto, tiene el sistema de archivos /proc disponible (lo que significa que no es una solución portátil), puede hacer que Java lea /proc/self/cmdline para reiniciarse así, así:

 public static void restart() throws IOException { new ProcessBuilder(getMyOwnCmdLine()).inheritIO().start(); } public static String[] getMyOwnCmdLine() throws IOException { return readFirstLine("/proc/self/cmdline").split("\u0000"); } public static String readFirstLine(final String filename) throws IOException { try (final BufferedReader in = new BufferedReader(new FileReader(filename))) { return in.readLine(); } } 

En los sistemas con /proc/self/cmdline disponible, esta es probablemente la forma más elegante de “reiniciar” el proceso Java actual desde Java. No hay JNI involucrado, y no hay que adivinar los caminos y cosas requeridas.

Muchos sistemas UNIX incluyendo GNU / Linux (incluso Android) hoy en día tienen procfs. Sin embargo, en algunos como FreeBSD, está obsoleto y se está eliminando. Mac OS X es una excepción en el sentido de que no tiene procfs . Windows tampoco tiene procfs . Cygwin tiene procfs pero es invisible para Java porque solo es visible para las aplicaciones que usan las DLL Cygwin en lugar de las llamadas al sistema de Windows, y Java no tiene conocimiento de Cygwin.

No te olvides de usar ProcessBuilder.inheritIO()

El valor predeterminado es que stdin / stdout / stderr (en Java, llamado System.in / System.out / System.err ) del proceso iniciado se configuran en tuberías que permiten que el proceso en ejecución se comunique con el proceso recién iniciado. Si desea reiniciar el proceso actual, es muy probable que esto no sea lo que desea . En su lugar, desearía que stdin / stdout / stderr los mismos que los de la máquina virtual actual. Esto se llama heredado . Puede hacerlo llamando a inheritIO() de su instancia de ProcessBuilder .

Pitfall en Windows

Un caso de uso frecuente de una función restart() es reiniciar la aplicación después de una actualización. La última vez que probé esto en Windows esto fue problemático. Cuando sobrescribió el archivo .jar la aplicación con la nueva versión, la aplicación comenzó a portarse mal y dio excepciones sobre el archivo .jar . Solo digo, en caso de que este sea tu caso de uso. En aquel entonces resolví el problema envolviendo la aplicación en un archivo por lotes y usando un valor de retorno mágico de System.exit() que System.exit() en el archivo por lotes y tuve el archivo por lotes reiniciar la aplicación en su lugar.

Una vieja pregunta y todo eso. Pero esta es otra manera que ofrece algunas ventajas.

En Windows, puede solicitar al planificador de tareas que vuelva a iniciar su aplicación por usted. Esto tiene la ventaja de esperar una cantidad específica de tiempo antes de que la aplicación se reinicie. Puede ir al administrador de tareas y eliminar la tarea y deja de repetir.

 SimpleDateFormat hhmm = new SimpleDateFormat("kk:mm"); Calendar aCal = Calendar.getInstance(); aCal.add(Calendar.SECOND, 65); String nextMinute = hhmm.format(aCal.getTime()); //Task Scheduler Doesn't accept seconds and won't do current minute. String[] create = {"c:\\windows\\system32\\schtasks.exe", "/CREATE", "/F", "/TN", "RestartMyProg", "/SC", "ONCE", "/ST", nextMinute, "/TR", "java -jar c:\\my\\dev\\RestartTest.jar"}; Process proc = Runtime.getRuntime().exec(create, null, null); System.out.println("Exit Now"); try {Thread.sleep(1000);} catch (Exception e){} // just so you can see it better System.exit(0); 

Similar a la respuesta ‘ mejorada ‘ de Yoda, pero con más mejoras (tanto funcional, legibilidad y capacidad de prueba). Ahora es seguro ejecutar y reiniciar tantas veces como la cantidad de argumentos del progtwig.

  • Sin acumulación de opciones de JAVA_TOOL_OPTIONS .
  • Encuentra automáticamente la clase principal.
  • Hereda stdout / stderr actual.

 public static void main(String[] args) throws Exception { if (args.length == 0) return; else args = Arrays.copyOf(args, args.length - 1); List command = new ArrayList<>(32); appendJavaExecutable(command); appendVMArgs(command); appendClassPath(command); appendEntryPoint(command); appendArgs(command, args); System.out.println(command); try { new ProcessBuilder(command).inheritIO().start(); } catch (IOException ex) { ex.printStackTrace(); } } private static void appendJavaExecutable(List cmd) { cmd.add(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"); } private static void appendVMArgs(Collection cmd) { Collection vmArguments = ManagementFactory.getRuntimeMXBean().getInputArguments(); String javaToolOptions = System.getenv("JAVA_TOOL_OPTIONS"); if (javaToolOptions != null) { Collection javaToolOptionsList = Arrays.asList(javaToolOptions.split(" ")); vmArguments = new ArrayList<>(vmArguments); vmArguments.removeAll(javaToolOptionsList); } cmd.addAll(vmArguments); } private static void appendClassPath(List cmd) { cmd.add("-cp"); cmd.add(ManagementFactory.getRuntimeMXBean().getClassPath()); } private static void appendEntryPoint(List cmd) { StackTraceElement[] stackTrace = new Throwable().getStackTrace(); StackTraceElement stackTraceElement = stackTrace[stackTrace.length - 1]; String fullyQualifiedClass = stackTraceElement.getClassName(); String entryMethod = stackTraceElement.getMethodName(); if (!entryMethod.equals("main")) throw new AssertionError("Entry point is not a 'main()': " + fullyQualifiedClass + '.' + entryMethod); cmd.add(fullyQualifiedClass); } private static void appendArgs(List cmd, String[] args) { cmd.addAll(Arrays.asList(args)); } 

V1.1 Corrección de errores: puntero nulo si JAVA_TOOL_OPTIONS no está configurado


Ejemplo:

 $ java -cp Temp.jar Temp abcde [/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b, c, d] [/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b, c] [/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b] [/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a] [/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp] $ 
 System.err.println("Someone is Restarting me..."); setVisible(false); try { Thread.sleep(600); } catch (InterruptedException e1) { e1.printStackTrace(); } setVisible(true); 

Supongo que realmente no desea detener la aplicación, sino “reiniciarla”. Para eso, puede usar esto y agregar su “Restablecer” antes de dormir y después de la ventana invisible.

    Intereting Posts