Redémarrer complètement un programme en Java ?

Fermé
TheKill_TnT Messages postés 148 Date d'inscription lundi 15 mai 2017 Statut Membre Dernière intervention 22 juin 2019 - Modifié le 16 janv. 2019 à 13:59
KX Messages postés 16734 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 24 avril 2024 - 17 janv. 2019 à 12:35
Bonjour,

Ma question est la suivante: Comment redémarrer complètement un programme Java ?
J'ai déjà posé une question similaire, mais elle n'allais pas jusque là. Ici, j'aimerais redémarrer un programme afin de le mettre à jour (il a été retéléchargé et l'idée et d'appliquer la mise à jour).
Théoriquement ça serait possible à l'aide d'un deuxième programme java (qui servirait alors d'instaciateur) mais je ne sais pas comment faire.
L'idée serait d'avoir quelque chose comme ceci:
public void redemarrer(){

QuelqueChose.AutreChose(new File("updater.jar")) //Instancier un fichier JAR


}

Et donc que notre updater.jar soit fait de:
public static void main(String[] args){
//imaginons que tout celà est derrière try catch hein
File f = new File("programme_update.jar")
QuelqueChose.AutreChose(f, "programme.jar") //Renommer le fichier téléchargé en "programme.jar"
QuelqueChose.AutreChose(f) //Encore instancier un fichier JAR
}
 

Et de cette manière finaliser la mise à jour.
Merci d'avance !

Configuration: Windows / Chrome 71.0.3578.98, Java Dev Kit 8
A voir également:

1 réponse

KX Messages postés 16734 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 24 avril 2024 3 015
16 janv. 2019 à 18:23
Bonjour,

Pour aller plus loin tu pourrais utiliser des ClassLoader différents, un pour chaque version du programme.
La seule contrainte c'est que le loader doit être dans le ClassLoader système, il ne pourra donc pas être mis à jour.

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 je recompile Main avec "Hello 2" par exemple, le Loader me chargera bien la nouvelle classe, sans avoir à arrêter le programme Loader.
0
TheKill_TnT Messages postés 148 Date d'inscription lundi 15 mai 2017 Statut Membre Dernière intervention 22 juin 2019 32
17 janv. 2019 à 06:51
Certes je peux l'utiliser pour démarrer une class mais si je veux démarrer directement un fichier JAR (Étant donné que je ne compte pas utiliser d'installeur externe si les gens doivent faire je ne sais trop quelle manip pour lancer le programme...), cette manière fonctionne ?
Moi j'aurais éventuellement pensé à une méthode qui exécute une ligne de commande ou directement un fichier, mais je vois pas où ça peut se trouver.
0
KX Messages postés 16734 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 24 avril 2024 3 015 > TheKill_TnT Messages postés 148 Date d'inscription lundi 15 mai 2017 Statut Membre Dernière intervention 22 juin 2019
17 janv. 2019 à 08:04
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 Messages postés 16734 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 24 avril 2024 3 015
17 janv. 2019 à 12:35
Un exemple complet :

J'ai un projet C:/Workspace/ccm-Thread qui créé un thread-1.0-SNAPSHOT.jar avec le contenu suivant :

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;
            }
        }
    }
}

J'ai un projet C:/Workspace/ccm-Loader qui créé un loader-1.0-SNAPSHOT.jar avec le contenu suivant :

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;
        }
    }
}

Fonctionnement : on lance la commande
java -cp loader-1.0-SNAPSHOT.jar ccm.Loader

(Éventuellement on pourra configurer ccm.Loader comme Main-Class du Manifest)

Le Loader va prendre le fichier thread-1.0-SNAPSHOT.jar (on pourrait ici mettre en place un téléchargement du fichier sur le web par exemple) pour le copier dans thread-local.jar, le charger et exécuter ccm.MainThread

Dans la console du Loader on peut appuyer sur Entrée, ce qui va déclencher la mise à jour et le redémarrage du thread, sauf en cas d'erreur de mise à jour, dans ce cas on laisse la version précédente s'exécuter.
0