¿Cómo puedo reiniciar una aplicación Java AWT? Tengo un botón al que he adjuntado un controlador de eventos. ¿Qué código debo usar para reiniciar la aplicación?
Quiero hacer lo mismo que Application.Restart()
en una aplicación C #.
¿Cómo puedo reiniciar una aplicación Java AWT? Tengo un botón al que he adjuntado un controlador de eventos. ¿Qué código debo usar para reiniciar la aplicación?
Quiero hacer lo mismo que Application.Restart()
en una aplicación C #.
Respuestas:
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<String> command = new ArrayList<String>();
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:
MyClassInTheJar
clase para encontrar la ubicación del jar)System.exit(0)
termina el proceso hijo tiene la misma respuesta que si esta respuesta realmente funciona y por qué. Si no puede proporcionar una explicación sensata junto con su respuesta, hizo un mal trabajo. La respuesta que proporciona más preguntas de las que responde no es un ejemplo de una respuesta completa. Las buenas respuestas no solo muestran el código, sino que también explican cómo y por qué funcionan, cuáles son los inconvenientes y cuáles son las alternativas. Ni siquiera trataste de cubrir estas cosas.
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 los que dicen que es imposible.
Este programa recopila toda la información disponible para reconstruir la línea de comandos original. Luego, lo lanza y dado que es el mismo comando, tu aplicación se inicia por segunda vez. Luego salimos del programa original, el programa hijo permanece ejecutándose (incluso bajo Linux) y hace exactamente lo mismo.
ADVERTENCIA : Si ejecuta esto, tenga en cuenta que nunca termina creando nuevos procesos, similar a una bomba de horquilla .
ManagementFactory.getRuntimeMXBean().getInputArguments()
solo le dará los argumentos de entrada pasados a la JVM. Falta los parámetros pasados a su aplicación. ej java -jar start.jar -MISSED_PARAM=true
. En un jvm de Oracle, puede recuperar esos parámetros usando System.getProperty("sun.java.command")
.
ProcessBuilder
y inheritIO()
, la VM secundaria se puede iniciar de una manera que la VM principal terminaría.
Básicamente, no puedes. Al menos no de forma fiable. Sin embargo, no debería ser necesario.
Para reiniciar un programa Java, debe reiniciar la JVM. Para reiniciar la JVM necesita
Localice el java
lanzador que se utilizó. Puede intentarlo, System.getProperty("java.home")
pero no hay garantía de que apunte al lanzador que se utilizó para iniciar su aplicación. (Es posible que el valor devuelto no apunte al JRE utilizado para iniciar la aplicación o podría haber sido anulado -Djava.home
).
Se podría suponer que quieren honrar la memoria original configuración etc ( -Xmx
, -Xms
, ...) por lo que necesita para averiguar qué entornos en los que se utilizan para iniciar la primera JVM. Puede intentar usarlo, ManagementFactory.getRuntimeMXBean().getInputArguments()
pero no hay garantía de que refleje la configuración utilizada. Esto incluso se detalla en la documentación de ese método:
Por lo general, no todas las opciones de la 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 la línea de comandos.
Si su programa lee la entrada del Standard.in
stdin original, se perderá en el reinicio.
Muchos de estos trucos y trucos fallarán en presencia de un SecurityManager
.
Le recomiendo que diseñe su aplicación para que sea fácil de limpiar todo y luego cree una nueva instancia de su clase "principal".
Muchas aplicaciones están diseñadas para no 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 bastante fácil hacer algo como:
public class MainClass {
...
public static void main(String[] args) {
boolean restart;
do {
restart = new MainClass().launch();
} while (restart);
}
...
}
y dejar que launch()
devolver verdadero si y sólo si la aplicación se cierra de una manera que tiene que ser reiniciado.
Estrictamente hablando, un programa Java no puede reiniciarse por sí mismo, ya que para hacerlo debe eliminar la JVM en la que se está ejecutando y luego iniciarla de nuevo, pero una vez que la JVM ya no se ejecuta (se elimina), no se puede realizar ninguna acción.
Puede hacer algunos trucos con cargadores de clases personalizados para cargar, empaquetar e iniciar los componentes AWT nuevamente, pero esto probablemente causará muchos dolores de cabeza con respecto al bucle de eventos de la GUI.
Dependiendo de cómo se inicie la aplicación, puede iniciar la JVM en un script de envoltura que contiene un bucle do / while, que continúa mientras la JVM sale con un código en particular, entonces la aplicación AWT tendría que llamar System.exit(RESTART_CODE)
. Por ejemplo, en la secuencia de comandos de pseudocódigo:
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 distinto al RESTART_CODE en la terminación "normal" que no requiere reinicio.
JavaApplicationStub
... No estoy seguro de si hay una manera fácil de evitar eso.
Eclipse normalmente se reinicia después de que se instala un complemento. Lo hacen utilizando un contenedor eclipse.exe (aplicación de inicio) para Windows. Esta aplicación ejecuta el jar del corredor principal de eclipse y si la aplicación java de eclipse termina con un código de relanzamiento, eclipse.exe reinicia el banco de trabajo. Puede crear un código nativo similar, un script de shell u otro contenedor de código Java para lograr el reinicio.
Ventanas
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 la ventana minimizada
^ & salir para cerrar la ventana de cmd después de terminar
un script cmd de muestra podría ser
@echo off
rem add some sleep (e.g. 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 dormir durante 10 segundos
Si realmente necesita reiniciar su aplicación, puede escribir una aplicación separada e iniciarla ...
Esta página proporciona muchos ejemplos diferentes para diferentes escenarios:
Aunque esta pregunta es antigua y está 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 crean una única cadena de comandos. 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 escaparse debido al espacio en Program Files
. No es un gran problema, 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<String> commands = new ArrayList<String>(4 + jvmArgs.size() + args.length);
List<String> 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 está aceptada, me gustaría ofrecer un enfoque alternativo para completar. Específicamente, Apache Ant sirvió como una solución muy flexible.
Básicamente, todo se reduce a un archivo de script Ant con una única tarea de ejecución de Java (consulte aquí y aquí ) invocada desde un código Java (consulte aquí ). Este código Java, que puede ser el inicio de un método , podría ser parte de la aplicación que debe reiniciarse. La aplicación debe tener una dependencia de la biblioteca Apache Ant (jar).
Siempre que la aplicación deba reiniciarse, debe llamar al lanzamiento del método y salir de la VM. La tarea Ant java debe tener las opciones fork y spawn configuradas en true.
Aquí hay un ejemplo de un script Ant:
<project name="applaucher" default="launch" basedir=".">
<target name="launch">
<java classname="package.MasinClass" fork="true" spawn="true">
<jvmarg value="-splash:splash.jpg"/>
<jvmarg value="-D other VM params"/>
<classpath>
<pathelement location="lib-1.jar" />
...
<pathelement location="lib-n.jar" />
</classpath>
</java>
</target>
</project>
El código para el método de lanzamiento puede verse 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 se usa el mismo script para el inicio inicial de la aplicación así como para los reinicios.
Simplemente agregando información que no está presente en otras respuestas.
/proc/self/cmdline
está disponibleSi está ejecutando en un entorno que proporciona procfs y, por lo tanto, tiene el /proc
sistema de archivos disponible (lo que significa que esta no es una solución portátil), puede hacer que Java lea /proc/self/cmdline
para reiniciarse, 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 sistemas con /proc/self/cmdline
disponibilidad, esta es probablemente la forma más elegante de cómo "reiniciar" el proceso actual de Java desde Java. No hay JNI involucrado, y no se requiere adivinar caminos y cosas. Esto también se encargará de todas las opciones de JVM pasadas al java
binario. La línea de comando será exactamente idéntica a la del proceso actual de JVM.
Muchos sistemas UNIX, incluido GNU / Linux (incluido Android) actualmente tienen procfs. Sin embargo, en algunos como FreeBSD, está obsoleto y se está eliminando gradualmente. 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 de Cygwin en lugar de las llamadas al sistema de Windows, y Java no conoce Cygwin.
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 conductos que permiten que el proceso en ejecución se comunique con el proceso recién iniciado. Si desea reiniciar el proceso actual, lo más probable es que esto no sea lo que desea . En su lugar, querrá que stdin
/ stdout
/ stderr
sean los mismos que los de la VM actual. Esto se llama heredado . Puede hacerlo llamando inheritIO()
a su ProcessBuilder
instancia.
Un caso de uso frecuente de una restart()
función es reiniciar la aplicación después de una actualización. La última vez que probé esto en Windows fue problemático. Cuando sobrescribió el .jar
archivo de la aplicación con la nueva versión, la aplicación comenzó a comportarse mal y a dar excepciones sobre el .jar
archivo. Solo digo, en caso de que este sea su caso de uso. En ese entonces, resolví el problema envolviendo la aplicación en un archivo por lotes y usando un valor de retorno mágico del System.exit()
que consulté en el archivo por lotes e hice que el archivo por lotes reiniciara la aplicación en su lugar.
Vieja pregunta y todo eso. Pero esta es otra forma que ofrece algunas ventajas.
En Windows, puede pedirle al programador de tareas que vuelva a iniciar su aplicación. Esto tiene la ventaja de esperar un tiempo específico antes de reiniciar la aplicación. Puede ir al administrador de tareas y eliminar la tarea y deja de repetirse.
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 funcionales como de legibilidad y de prueba). Ahora es seguro de ejecutar y se reinicia tantas veces como la cantidad de argumentos de programa proporcionados.
JAVA_TOOL_OPTIONS
opciones.public static void main(String[] args) throws Exception {
if (args.length == 0)
return;
else
args = Arrays.copyOf(args, args.length - 1);
List<String> 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<String> cmd) {
cmd.add(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java");
}
private static void appendVMArgs(Collection<String> cmd) {
Collection<String> vmArguments = ManagementFactory.getRuntimeMXBean().getInputArguments();
String javaToolOptions = System.getenv("JAVA_TOOL_OPTIONS");
if (javaToolOptions != null) {
Collection<String> javaToolOptionsList = Arrays.asList(javaToolOptions.split(" "));
vmArguments = new ArrayList<>(vmArguments);
vmArguments.removeAll(javaToolOptionsList);
}
cmd.addAll(vmArguments);
}
private static void appendClassPath(List<String> cmd) {
cmd.add("-cp");
cmd.add(ManagementFactory.getRuntimeMXBean().getClassPath());
}
private static void appendEntryPoint(List<String> 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<String> cmd, String[] args) {
cmd.addAll(Arrays.asList(args));
}
V1.1 Bugfix: puntero nulo si JAVA_TOOL_OPTIONS no está configurado
Ejemplo:
$ java -cp Temp.jar Temp a b c d e
[/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 no quieres detener la aplicación, sino "Reiniciarla". Para eso, puede usar esto y agregar su "Reset" antes del sueño y después de la ventana invisible.