[RESOLU] Faire des patches pour son application Java

Hello la jeunesse,

Voilà, je développe une application web, en Java.
Le fait est que l’on développe plusieurs versions, et pour l’instant, on ne fait pas vraiment des patches.
Ce matin, j’ai décidé de résoudre ce problème, en me basant sur ce principe :

  1. Je regroupe tous les fichiers de mon appli web qui doivent être mis à jour par le patch.
  2. Je zippe tout ça dans un fichier patch.zip.
  3. J’ai créé une petite application Java qui va lire ce fichier zip, et qui va décompresser les fichiers au bon endroit.
  4. Je compile mon application.
    Pour faire plus simple, j’ai mis mon fichier patch.zip dans le fichier jar de mon application. Donc au final, je n’ai qu’un seul fichier.

A ce niveau là, j’ai un fichier pouet.jar, qui lorsque je l’exécute, réalise ce que je veux. C’est la joie.

Mais voilà, je suis perfectionniste, et j’ai décidé que je voulais créer un executable pour mon application (afin d’éviter d’avoir à forcer l’utilisateur d’installer un JRE juste pour installer le patch). Du coup, j’utilise l’utilitaire launch4j, qui me crée un fichier patch.exe (qui wrappe mon pouet.jar en fait).
Le problème est que maintenant, mon code qui lit le fichier ZIP contenu dans le fichier JAR ne fonctionne plus, puisque le fichier ZIP est contenu dans le JAR lui même contenu dans le EXE (vous suivez ?).

Comment faire pour résoudre ce problème, sachant que :

  1. Je ne souhaite pas utiliser autre chose que le Java pour créer mon application.
  2. Je souhaite vraiment avoir un exécutable pour mon fichier de patch (et non un .JAR).
  3. Je souhaite qu’il n’y ait qu’UN seul fichier par patch (sinon, j’aurais fait en sorte d’avoir mon patch.exe et mon patch.zip, qui lui, ne contient que les ficheirs à mettre à jour).

Alors, une idée ? Y a moyen de paramètrer launch4j* pour résoudre mon problème ?

  • Si un autre outil me permet de créer un exécutable à partir de mon fichier JAR tout en me résolvant mon problème, je suis prenneur !

Merci merci.

A première vue comme ça, je dirais qu’il y a moyen sans problème, ça m’aiderai que tu me fasses voir ton code pour lire ton fichier zip, car si tu utilises bien l’idée de ‹ Ressource › pour accéder à ton Zip et l’idée de ‹ Flux › pour le lire et extraire les fichiers, ca devrait poutrer.

Un commentaire sur ça, à mon avis launch4f nécessite quand même une JRE pour exécuter ton programme, il se charge juste d’extraire le jar de l’exécutable et de lancer la JVM.

PS: C’est fout ce que ça parle Java en ce moment :stuck_out_tongue:

ZGoblin,

Voilà grosso modo mon code. Alors je préviens, il est un peu tordu, parce que d’abord je lis le .JAR contenant mon appli, puis ensuite, je lis le fichier patch.zip qui se trouve dans “/resources/patch/patch.zip”.

... // Je récupère le fichier JAR de mon appli. String jarName = getClass().getResource("/resources").getFile(); System.out.println("File #1 --> " + jarName); // Je garde juste le XXX dans "file:/XXX!...." jarName = jarName.substring(6, jarName.indexOf("!/")); System.out.println("File #2 --> " + jarName); JarFile jf = new JarFile(jarName); // Dans mon fichier JAR, je cherche le fichier patch.zip JarEntry je = jf.getJarEntry("resources/patch/patch.zip"); if (je == null) { return; } // Je crée un ZipInputStream pour lire mon fichier patch.zip... ZipInputStream in = new ZipInputStream(jf.getInputStream(je)); int nb = jf.size(); ZipEntry entry = null; File file = null; while (nb > 0) { nb--; entry = in.getNextEntry(); // A partir d'ici, je fais le traitement nécessaire sur chaque entrée contenu dans patch.zip

Voilà. Ce code marche très bien.

Par contre, si je crée mon fichier patch.exe avec launch4j, j’obtiens ce message d’erreur, ce qui est normal, puisque dans mon code, je me base sur le fait que je travaille avec un fichier .JAR, alors qu’ici, il va trouver un fichier .EXE :

File #1 -> file:/D:/dev/Web-Patches/patches%20Gen3/patch.exe!/resources File #2 -> D:/dev/Web-Patches/patches%20Gen3/patch.exe java.util.zip.ZipException: The system cannot find the path specified at java.util.zip.ZipFile.open(Native Method) at java.util.zip.ZipFile.<init>(Unknown Source) at java.util.jar.JarFile.<init>(Unknown Source) at java.util.jar.JarFile.<init>(Unknown Source) at com.ge.med.service.assetplus.webpatcher.Patcher.run(Patcher.java:59)

Il y a une manière beaucoup plus élégante pour faire ça, je regarde ce soir en rentrant du taf et j’éditerai ce message.

Edit :

Je vais faire simple, voici un code que je viens de testé, ca devrait passer sans problème avec launch4j. Je te laisse l’adapter pour correspondre à tes besoins.

[code]package org.zobi;

import java.io.;
import java.util.zip.
;

public class ZipTest {
private InputStream inputStream;

private ZipInputStream zipInputStream;

private static final String zipLocation = "/data/zip/MonFichierZip.zip";

public ZipTest() {
	ZipEntry ze;
	File f;

	inputStream = ZipTest.class.getResourceAsStream(zipLocation);
	zipInputStream = new ZipInputStream(inputStream);

	try {
		ze = zipInputStream.getNextEntry();
		// Tant qu'il y a des entrées
		while (ze != null) {
			// Crée le dossier contenant le fichier une fois déziper, si il
			// n'existe pas
			f = new File("/home/loyl/" + ze.toString());
			f.getParentFile().mkdirs();
			// Ouvre l'entrée le fichier à déziper en écriture, le crée s'il
			// n'existe pas
			FileOutputStream fos = new FileOutputStream(f);
			// Dézipe le fichier
			int lu = -1;
			byte[] tampon = new byte[4096];
			do {
				lu = zipInputStream.read(tampon);
				if (lu > 0)
					fos.write(tampon, 0, lu);
			} while (lu > 0);
			// Finir proprement
			fos.flush();
			// Fermer le fichier à déziper
			fos.close();
			// Passer au suivant
			ze = zipInputStream.getNextEntry();
		}
		// Fermer le fichier zip
		zipInputStream.close();
	} catch (IOException e) {
		e.printStackTrace();
	}
}

public static void main(String... args) {
	new ZipTest();
}

}[/code]

Merci ZGoblin pour ce bout de code. Je l’essaierais demain matin au boulot, mais je ne crois pas qu’il fasse ce que j’attends en fait (ou alors je suis trop pessimiste) :

  • Lire le contenu d’un fichier Zip, pas de soucis.
  • Lire le contenu d’un fichier Jar, pas de soucis.
  • Lire le contenu d’un fichier Zip contenu dans un fichier Jar, pas de soucis. C’était d’ailleurs l’exemple que je donnais, que je pourrais quelque peu raccourcir grâce à ton aide.
  • Lire le contenu d’un fichier Zip contenu dans un fichier Jar contenu dans le fichier Exe créé par launch4j, ça je ne sais pas faire, et je ne suis pas sûr que ton code le fasse. Après tout, le package zip (ou jar) de java “indique à Java” comment lire un fichier Zip (ou Jar) et traiter son contenu, mais il n’y a rien qui indique à Java comment traiter le fichier .exe créé par launch4j.

Alors soit j’ai mal exprimé mon problème (et je m’en excuse), soit je suis trop pessimiste à la vue de ton exemple ZGoblin. Toutefois, afin d’en être bien sûr, je le testerais demain matin, et j’éditerais…

Mon code devrait marcher car il ne cafouille pas avec des URL, je ne suis même pas sur que ton code passe sous Linux ou sous MacOSX et c’est quand même plus clean d’utiliser les deux lignes suivantes :

inputStream = ZipTest.class.getResourceAsStream(zipLocation); zipInputStream = new ZipInputStream(inputStream);

Maintenant, je n’est pas testé avec Launch4j donc tiens moi au courant.

ZGoblin, tu es mon Dieu pour la journée :stuck_out_tongue: :stuck_out_tongue:
Merci beaucoup.

Effectivement, ton code fonctionne très bien y compris une fois que mon application JAR est encapsulée dans le .Exe créé par launch4j.

En fait, c’est la ligne là qui fait toute la différence :

inputStream = ZipTest.class.getResourceAsStream(zipLocation);

puisqu’elle permet de récupérer la resource - mon fichier Zip - à partir de la classe ZipTest, et non plus en recherchant le fichier Jar de mon application.

Edit:
Pour la lecture du flux zipInputStream et l’écriture dans le fichier de sortie, on peut faire ça en 2 lignes (mais bon, ça revient strictement au même) :

while ((lu = zipInputStream.read(tampon)) > 0) { fos.write(tampon, 0, lu); }

[quote=« rorotaz, post:7, topic: 27140 »]ZGoblin, tu es mon Dieu pour la journée :stuck_out_tongue: :stuck_out_tongue:
Merci beaucoup.

Effectivement, ton code fonctionne très bien y compris une fois que mon application JAR est encapsulée dans le .Exe créé par launch4j.

En fait, c’est la ligne là qui fait toute la différence :

inputStream = ZipTest.class.getResourceAsStream(zipLocation);

puisqu’elle permet de récupérer la resource - mon fichier Zip - à partir de la classe ZipTest, et non plus en recherchant le fichier Jar de mon application.

Edit:
Pour la lecture du flux zipInputStream et l’écriture dans le fichier de sortie, on peut faire ça en 2 lignes (mais bon, ça revient strictement au même) :

while ((lu = zipInputStream.read(tampon)) > 0) { fos.write(tampon, 0, lu); }[/quote]

C’est cool alors si ça marche :stuck_out_tongue: , juste une petite précision : si tu fais une application qui contient plusieurs jar, il faut que ton Zip soit dans le même jar que la class ZipTest.

Pour ta remarque, ton code extrait juste ton fichier zip du jar et il fait très bien, c’est ce qu’on peut faire de plus propre. Le code que j’ai mis (qui n’est pas de moi, je l’ai pompé sur internet) extrait les fichiers contenu dans le fichier zip.

Voilà, si tu as d’autres soucis, n’hésite pas.