Reiniciar por completo un programa en Java?

TheKill_TnT Mensajes publicados 172 Estado Miembro -  
KX Mensajes publicados 19031 Estado Moderador -
Hola,

Mi pregunta es la siguiente: ¿Cómo reiniciar completamente un programa Java Runtime Environment?
Ya hice una pregunta similar, pero no llegaba hasta ahí. Aquí, me gustaría reiniciar un programa para actualizarlo (se ha descargado de nuevo y la idea es aplicar la actualización).
Teóricamente sería posible con la ayuda de un segundo programa Java (que serviría entonces como instanciador), pero no sé cómo hacerlo.
La idea sería tener algo como esto:
 public void reiniciar(){ Algo.AutreChose(new File("updater.jar")) //Instanciar un archivo JAR } 

Y así que nuestro updater.jar esté hecho de:
 public static void main(String[] args){ //supongamos que todo esto está dentro de try catch ¿eh? File f = new File("programme_update.jar") Algo.AutreChose(f, "programme.jar") //Renombrar el archivo descargado a "programme.jar" Algo.AutreChose(f) //Otra vez instanciar un archivo JAR } 

Y de esta manera finalizar la actualización.
¡Gracias de antemano!

Configuración: Windows / Chrome 71.0.3578.98, Java Development Kit 8

1 respuesta

KX Mensajes publicados 19031 Estado Moderador 3 020
 
Hola, Para ir más allá podrías usar diferentes ClassLoaders, uno para cada versión del programa. La única constraint es que el loader debe estar en el ClassLoader del sistema, por lo que no podrá ser actualizado.
import java.net.URL; import java.net.URLClassLoader; import java.util.Arrays; public class Loader { // n'importe oú sauf dans E:/Workspace/CCM/bin/ public static void main(String[] args) throws Exception { for (int i = 0; i < 10; i++) { URL[] classUrls = { new URL("file:///E:/Workspace/CCM/bin/") }; System.out.println(Arrays.toString(classUrls)); try (URLClassLoader ucl = new URLClassLoader(classUrls)) { System.out.println(ucl); ucl.loadClass("Main").getMethod("main", String[].class).invoke(null, new Object[] { args }); Thread.sleep(2_000); } } } }
public class Main { // compilé dans E:/Workspace/CCM/bin/ public static void main(String[] args) { System.out.println("Hello 1"); } }
Si reclasificas Main con "Hello 2" par exemple, el Loader cargará bien la nueva clase, sin tener que detener el programa Loader. -- La confianza no excluye el control
0
TheKill_TnT Mensajes publicados 172 Estado Miembro 40
 
Claro, puedo usarlo para arrancar una clase, pero si quiero iniciar directamente un archivo JAR (ya que no planeo usar un instalador externo si la gente debe hacer no sé qué maniobra para lanzar el programa...), ¿esta manera funciona?
Yo, eventualmente, habría pensado en un método que ejecute una línea de comandos o directamente un archivo, pero no veo dónde podría ubicarse.
0
KX Mensajes publicados 19031 Estado Moderador 3 020 > TheKill_TnT Mensajes publicados 172 Estado Miembro
 
Sur le même principe tu as le JarClassLoader.

L'idée serait : tu as un Jar que l'utilisateur démarre et dont le fonctionnement est le suivant.
  • On va chercher la dernière version du programme, on copie le Jar dans un dossier
  • Avec un JarClassLoader on charge le Jar et on exécute son main
  • Lorsque l'on veut se mettre à jour, on recommence (dernière version, copie, chargement, exécution)

Après il faudrait bien savoir quelles limites tu t'imposes pour "redémarrer" un programme.
S tu pars sur des lignes de commandes, tu n'auras pas un programme en exécution, mais plusieurs.
Avec les ClassLoader, tu auras toujours le même processus Java en exécution, même après "redémarrage".

Remarque : c'est le même principe de fonctionnement que les serveurs d'applications (Tomcat &co) qui vont démarrer/arrêter des war, mais le serveur en lui même reste ne s'arrête pas, ce qui lui permet d'arrêter certaines applications tout en laissant les autres démarrées.
0
KX Mensajes publicados 19031 Estado Moderador 3 020
 
Un exemple complet :

Tengo un proyecto C:/Workspace/ccm-Thread que crea un thread-1.0-SNAPSHOT.jar con el contenido siguiente :

package ccm; public class MainThread extends Thread { @Override public void run() { for (int i = 0; !isInterrupted(); i++) { System.out.println(i); try { Thread.sleep(1_000); } catch (InterruptedException e) { return; } } } }

Tengo un proyecto C:/Workspace/ccm-Loader que crea un loader-1.0-SNAPSHOT.jar con el contenido siguiente :

package ccm; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.Scanner; import java.util.logging.Level; import java.util.logging.Logger; public class Loader { private static final Path HOME_LATEST = Paths.get("C:/Workspace/ccm-Loader/target/thread-local.jar"); private static final Path CLOUD_LATEST = Paths.get("C:/Workspace/ccm-Thread/target/thread-1.0-SNAPSHOT.jar"); public static void main(String[] args) { downloadLatestThreadJar(); Thread thread = loadLocalThreadJar("ccm.MainThread", args); thread.start(); try (Scanner sc = new Scanner(System.in)) { while (true) { sc.nextLine(); // Appuyer sur Entrée pour mettre à jour et redémarrer try { if (downloadLatestThreadJar()) { thread.interrupt(); thread.join(); thread = loadLocalThreadJar("ccm.MainThread", args); thread.start(); } } catch (Exception e) { Logger.getGlobal().log(Level.WARNING, "Can't update or restart", e); } } } } private static boolean downloadLatestThreadJar() { Logger.getGlobal().info("downloadLatestThreadJar: " + CLOUD_LATEST + " -> " + HOME_LATEST); try { HOME_LATEST.getParent().toFile().mkdirs(); Files.copy(CLOUD_LATEST, HOME_LATEST, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES); Logger.getGlobal().info("downloadLatestThreadJar: success!"); return true; } catch (Exception e) { Logger.getGlobal().log(Level.WARNING, "Can't downloadLatestThreadJar", e); return false; } } private static Thread loadLocalThreadJar(String className, String[] args) { Logger.getGlobal().info("loadThreadJar: " + HOME_LATEST); try (URLClassLoader jcl = new URLClassLoader(new URL[] { HOME_LATEST.toUri().toURL() })) { @SuppressWarnings("unchecked") Class<Thread> threadClass = (Class<Thread>) jcl.loadClass(className); Thread thread = threadClass.newInstance(); thread.setName("Embedded " + className); thread.setContextClassLoader(jcl); Logger.getGlobal().info("loadLocalThreadJar: success!"); return thread; } catch (Exception e) { Logger.getGlobal().log(Level.WARNING, "Can't loadLocalThreadJar", e); return null; } } }


Función: se ejecuta la commande
java -cp loader-1.0-SNAPSHOT.jar ccm.Loader

(Eventualmente se podrá configurar ccm.Loader como Main-Class del Manifest)

El Loader va a tomar el archivo thread-1.0-SNAPSHOT.jar (aquí se podría implementar una descarga del archivo desde la web, por ejemplo) para copiarlo en thread-local.jar, cargarlo y ejecutar ccm.MainThread

En la consola del Loader se puede pulsar Enter, lo que disparará la actualización y el reinicio del thread, salvo en caso de error de actualización; en ese caso se deja ejecutar la versión anterior.
0