java - volver - uso de while netbeans
¿Cómo puedo reiniciar una aplicación Java? (13)
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<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();
}
}
¿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 #.
Básicamente, no puedes. Al menos no de manera confiable.
Para reiniciar un programa Java, necesita reiniciar la JVM. Para reiniciar la JVM necesitas
Busque el
java
que se utilizó. Puede probar conSystem.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
).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 usarManagementFactory.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.
Si su programa lee la entrada de
Standard.in
el stdin original se perderá en el reinicio.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.
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.
Estrictamente hablando, un programa 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.
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:
- Encuentra el ejecutable de Java (utilicé el binario de Java aquí, pero eso depende de tus requisitos)
- Encuentre la aplicación (un jar en mi caso, usando la clase
MyClassInTheJar
para encontrar la ubicación del jar) - Cree un comando para reiniciar el jar (usando el binario de Java en este caso)
- ¡Ejecutalo! (y así terminando la aplicación actual y comenzando de nuevo)
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:
Similar a la respuesta '' improved '' 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 programa.
- 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<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 Corrección de errores: 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]
$
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);
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 here y here ) invocada desde un código Java (consulte here ). 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:
<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 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 (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 horas de sueño por 10 segundos
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.
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 programa recopila 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 programa original, el programa 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 .