Fichier d`archive Java - JAR - programmer en java, programme java

Transcription

Fichier d`archive Java - JAR - programmer en java, programme java
Fichiers d'archives java - *.jar
Chapitres traités
Originaux
Les fichiers d'archive Java (fichiers JAR) sont les valises Java. ils constituent le moyen standard et portable d'empaqueter toutes les
parties de votre application Java dans un ensemble compact à distribuer ou à installer. Vous pouvez tout mettre dans un fichier JAR : des
fichiers de classe Java, des objets sérialisés, des fichiers de données, des images des sons , etc.
Un fichier JAR peut même contenir plusieurs signatures numériques attestant de l'intégrité et de l'authenticité des données. Une signature peut être attachée au
fichier global ou à ses éléments individuels.
La machine virtuelle Java reconnaît les fichiers JAR et sait charger des fichiers de classe directement d'une archive. Il est ainsi possible d'empaqueter les classes de
votre application dans un fichier JAR et de les placer dans votre répertoire de travail. Vous pouvez faire de même pour les applets, en indiquant le fichier JAR dans
l'attribut archive de la balise HTML <applet>.
Il est possible de récupérer d'autres types de fichiers (données, images, etc.) contenus dans votre fichier JAR. En outre, votre code n'a pas besoin de savoir si une
ressource est un fichier ou un élément d'une archive JAR.
Qu'un fichier de classe ou de données soit un élément d'un fichier JAR, un fichier individuel du chemin des classes, ou une applet sur un serveur distant, vous
pourrez toujours y faire référence de manière standard, et laisser le chargeur de classes de Java trouver son emplacement.
Originaux
Afin de bien montrer l'intérêt de l'archivage de vos différents développements, je vous propose de le visualiser au travers d'une étude traduite d'une part sous la forme
d'une application graphique, et d'autre part sous la forme d'une page Web, c'est-à-dire au travers d'une applet Java.
Cette étude comporte plusieurs classes ainsi qu'une image à faire afficher en papier peint. Par ailleurs le texte qui s'affiche est sensible au mouvement du curseur
de la souris, puisque lorsque ce dernier se déplace sur le texte, celui-ci change alors de couleur. Le texte reprend ensuite sa couleur d'origine lorsque le curseur de
la souris s'en va en dehors de la zone de texte.
Application fenêtrée
Voici donc le premier exemple qui correspond à notre application Java, avec le résultat obtenu suivi de l'architecture de notre arborescence ainsi que le codage de
l'ensemble de ces classes écrit dans le même fichier "Principal.java".
Principal.java
package texte;
import
import
import
import
import
java.awt.event.*;
java.io.*;
javax.imageio.ImageIO;
javax.swing.*;
java.awt.*;
public class Principal extends JFrame {
public Principal() throws IOException {
this.setDefaultCloseOperation(this.EXIT_ON_CLOSE);
this.setSize(350, 250);
this.setTitle("C'est chouette...");
PanneauImage panneau = new PanneauImage(ImageIO.read(new File("chouette.jpg")));
this.getContentPane().add(panneau);
}
public static void main(String[] args) throws IOException {
new Principal().setVisible(true);
}
}
class PanneauImage extends JPanel {
private Image image;
private Texte invite = new Texte("C'est chouette...");
public PanneauImage(Image image) {
this.image = image;
invite.setCouleurSurvol(Color.red);
invite.setCouleurNormale(Color.blue);
this.add(invite);
}
protected void paintComponent(Graphics g) {
g.drawImage(image, 0, 0, this);
}
}
class Texte extends JLabel implements MouseListener {
private Color couleurSurvol, couleurNormale;
public Texte(String invite) {
super(invite);
this.setFont(new Font("Verdana", Font.BOLD, 28));
this.addMouseListener(this);
}
public void setCouleurSurvol(Color couleur) {
couleurSurvol = couleur;
}
public void setCouleurNormale(Color couleur) {
this.setForeground(couleurNormale = couleur);
}
public void mouseEntered(MouseEvent e) {
this.setForeground(couleurSurvol);
}
public void mouseExited(MouseEvent e) {
this.setForeground(couleurNormale);
}
public void mouseClicked(MouseEvent e) {}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
}
Applet Java
Cette fois-ci, nous avons une application Web. Toute la partie IHM se trouve donc dans une applet.
Ant dans NetBeans
NetBeans
est
un
environnement
de
développement intéressant puisqu'il dispose
d'un certain nombre d'outils déjà intégré
sans faire appel à des plugins particuliers.
Surtout, il s'agit d'un IDE gratuit qui plus est
offre de très nombreuses fonctionnalités
dans le monde de Java.
Il propose notamment l'outil Ant qui est
l'équivalent du makefile dans le monde du
langage C++.
Les processus de build est conduit dans Ant
par l'intermédiaire de fichiers XML, dans
lequel les projets, les dépendances et les
phases de travail sont définis sous forme de
tâches. Dans le cas le plus simple, Ant
effectue la compilation d'arborescences de
packages avec du code source Java et
l'exécution des classes. Du fait qu'avec Ant,
on
peut
faire
appel
aux
systèmes
d'exploitation, il est également possible de
piloter les tâches (task) du processus build
considérablement plus importantes : la
palette des tâches s'étend de la copie du
code source depuis des systèmes de contrôle
de
version
tels
que
CVS
ou
d'un
environnement de développement à la
création de fichiers jar, war, ear, et pour ce
faire, elle s'appuie sur un ensemble de
tâches prédéfinies.
Dans le cas de NetBeans, vous avez deux
fichiers XML qui traitent du processus de
build : le fichier standard build.xml qui fait
appel à un autre fichier de description buildimpl.xml. C'est à l'intérieur de ce dernier que
sont décrites toutes les tâches relatives à
l'ensemble de construction d'un projet,
comme la compilation, la fabrication des
répertoires, l'exécution éventuelle, etc.
TexteApplet.java
package texte;
import
import
import
import
import
import
java.awt.event.*;
java.io.*;
java.net.*;
javax.imageio.ImageIO;
javax.swing.*;
java.awt.*;
public class TexteApplet extends javax.swing.JApplet {
public void init() {
this.setSize(350, 250);
PanneauImage panneau = new PanneauImage(getImage(getDocumentBase(), "chouette.jpg"));
this.getContentPane().add(panneau);
}
}
class PanneauImage extends JPanel {
private Image image;
private Texte invite = new Texte("C'est chouette...");
public PanneauImage(Image image) {
this.image = image;
invite.setCouleurSurvol(Color.red);
invite.setCouleurNormale(Color.blue);
this.add(invite);
}
protected void paintComponent(Graphics g) {
g.drawImage(image, 0, 0, this);
}
Tout est déjà fait dans NetBeans sauf qu'il
est possible de proposer de nouvelles tâches
ou de modifier celles qui sont déjà décrites
pour proposer d'autres alternatives.
Nous allons justement modifier le processus
de fabrication afin de l'adapter à notre
contexte. En effet, par défaut, lorsque nous
compilons des applets, les fichiers <*.class>
correspondant sont placés automatiquement
dans la zone privée de l'application Web,
c'est-à-dire dans le répertoire <WEB-INF/
classes>
se
qui
empêche
le
bon
fonctionnement de l'application Web. Nous
allons, dans ce fichier de descriptions des
tâches,
proposer
un
déplacement
automatique de ces fichiers <*.class> pour
les placer dans la zone publique, c'est-à-dire
au niveau de la page Web, et ceci après
chaque phase de compilation.
Dans ce fichier de configuration, des zones
ont été prévues afin de proposer d'autres
}
class Texte extends JLabel implements MouseListener {
private Color couleurSurvol, couleurNormale;
public Texte(String invite) {
super(invite);
this.setFont(new Font("Verdana", Font.BOLD, 28));
this.addMouseListener(this);
}
public void setCouleurSurvol(Color couleur) {
couleurSurvol = couleur;
}
public void setCouleurNormale(Color couleur) {
this.setForeground(couleurNormale = couleur);
}
public void mouseEntered(MouseEvent e) {
this.setForeground(couleurSurvol);
}
public void mouseExited(MouseEvent e) {
this.setForeground(couleurNormale);
}
public void mouseClicked(MouseEvent e) {}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
}
index.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>C'est chouette...</title>
</head>
<body>
<h2 align="center">C'est chouette...</h2>
<hr />
<div align="center">
<applet code="texte.TexteApplet.class" width="350" height="250"></applet>
</div>
</body>
</html>
tâches personnalisées. Il en existe une
notamment qui est souvent très utile, il
s'agit de la zone "-post-compile" :
<target name="-post-compile">
<!-- Empty placeholder ... -->
<!-- You can override this target ... -->
</target>
Nous utiliserons donc la tâche <move> afin
d'obtenir notre déplacement :
<target name="-post-compile">
<move todir="${build.web.dir}">
<fileset dir="${build.classes.dir}" />
</move>
</target>
Tâche move
permet le déplacement ou le renommage de
fichiers ou un répertoire, ou pour les
renommer, nous pouvons utiliser l'élément
fileset, qui permet de manipuler des jeux de
fichiers. Voici les attributs possibles pour
cette tâche move :
file
Spécifie
le
fichier
à
déplacer.
Obligatoire,
en
l'absence
de
spécification de fileset.
tofile
Spécifie le fichier de destination du
déplacement. tofile ou todir doit être
spécifié.
todir
Le répertoire vers lequel a lieu le
déplacement. tofile ou todir doit être
spécifié.
Voici quelques exemples d'utilisation :
<move file="faux.java" tofile="vrai.java" /
>
renomme un fichier.
<move file="echec.java" todir="jeu/" />
Analyse de l'applet
déplacement d'un fichier.
L'applet TexteApplet de cette étude utilise trois classes : l'applet elle-même TexteApplet, PanneauImage et Texte. Nous
savons que la balise <applet> fait référence au fichier de classes qui contient la classe applet :
<move todir="premier">
<fileset dir="deuxième" />
</move>
<applet code="texte.TexteApplet.class" width="350" height="250"></applet>
renomme le répertoire.
Lorsque le navigateur lit cette ligne, il se connecte au serveur Web et recherche le fichier "TexteApplet.class". Le
chargeur de classes de la machine virtuelle du navigateur charge la classe TexteApplet de ce fichier. Lors du
processus de chargement, le chargeur de classes doit résoudre les références aux autres classes utilisées par cette
classe. Il sait alors qu'il a éventuellement besoin de plus d'une classe pour exécuter l'applet et, en ce cas, se
connecte de nouveau au serveur Web, un pour chaque fichier de classe. Le chargement d'une applet peut alors
nécessiter plusieurs minutes pour peu que le réseau soit lent.
Il faut bien comprendre que ce temps de chargement n'est pas dû à la taille des fichiers classes, relativement
petits, mais à la surcharge considérable découlant d'une connexion avec un serveur Web.
Java supporte donc maintenant une méthode améliorée pour le chargement de fichiers de classes : elle permet de
rassembler tous les fichiers de classes nécessaires en un seul fichier. Ce fichier peut alors être chargé grâce à une
requête HTTP unique vers ce serveur. Ces fichiers sont appelés fichiers JAR (acronyme de "Java Archive"). Ils
peuvent contenir à la fois des fichiers de classes et d'autres types de fichiers comme des fichiers images et audios.
Les fichiers JAR peuvent également être compressés au format de compression classique ZIP, ce qui réduit le
temps de téléchargement.
tâche fileset
Les jeux de fichiers (marqueurs <fileset>)
sont utilisés pour spécifier des ensembles de
fichiers. Ces marqueurs sont normalement
des
marqueurs
internes
à
d'autres
marqueurs comme move, copy, delete, etc.
dir
Répertoire d'origine pour la définition
des fichiers.
includes
Liste des noms de fichiers, séparés par
des virgules. Si omis, tous les fichiers
sont ajoutés.
excludes
Liste de tous les fichiers à exclure.
Compression des fichiers
Il est possible de compresser des éléments de fichier JAR avec le standard de compression ZIP. Les fichiers JAR sont
totalement compatibles avec les archives ZIP bien connues des utilisateurs Windows.
La compression accélère le transfert de classes sur un réseau. Un rapide survol de la distribution SDK montre
qu'un fichier de classe classique se trouve réduit d'environ 40% après compression. Les fichiers texte de type
HTML ou ASCII sont réduits de plus de 75%, donc à un quart de leur taille d'origine ! Par contre, les fichiers
images ne gagnent rien à être compressés ; les formats d'image courants possèdent une compression interne.
En termes d'envoi sur le réseau, la compression n'est pas le seul avantage d'un fichier JAR. Placer toutes les
classes dans un seul fichier JAR les rend téléchargeables en une seule transaction. Supprimer le coût des
requêtes HTTP est une réelle économie, dans la mesure où un fichier de classe est généralement de petite taille, et
qu'une applet en utilise beaucoup. D'un autre côté, sur une connexion à faible débit, le temps de démarrage peut
augmenter si un gros fichier JAR doit être descendu avant que l'applet ne soit lancée.
tâche copy
Permet de copier plusieurs fichiers dans un
répertoire. Les fichiers du répertoire source
ne sont copiés que s'ils sont plus récents que
ceux du répertoire de destination ou
lorsqu'ils n'existent pas déjà dans le
répertoire de destination.
file
Le fichier à copier. Au cas où aucun
élément fileset n'est spécifié, file est un
attribut obligatoire.
tofile
Fichier de destination. Est utilisé si
l'attribut file est défini.
Création d'une archive par l'intermédiare de l'utilitaire jar
L'utilitaire jar livré avec le SDK permet de créer et de lire les fichiers JAR. Dans l'installation par défaut, cet outil se trouve
dans le répertoire <jdk/bin>. Son interface utilisateur ressemble à la commande tar (tape archive) du monde d'UNIX.
La commande jar doit se conformer à la syntaxe suivante :
todir
Répertoire de destination. A spécifier
lors de la définition de l'attribut file ou
d'un jeu de fichiers.
jar options Fichier1 Fichier2 ...
Le tableau suivant liste les options possibles avec la commande jar. Elles sont tout-à-fait analogues aux options
de la commande tar utilisée par le système UNIX.
L'exemple suivant copie tous les fichiers
*.class dans un autre répertoire :
<copy todir="../../jars">
<fileset dir="tmp" includes="**/*.class"
/>
</move>
Option
Description
c
Crée une nouvelle archive vide et y place des fichiers. Une liste de fichiers d'entrée et/ou de répertoires doit
être spécifiée comme argument final de la commande jar. Le nouveau fichier JAR possède un fichier METAINF/MANIFEST.MF comme premier élément créé automatiquement. Si l'un des noms de fichier indiqué est un
répertoire, le programme jar le traite de façon récursive.
C
Change temporairement le répertoire. Par exemple : jar cfv archive.jar -C classes *.class change en direction
du sous-répertoire classes pour ajouter les fichiers classes qui y sont stockés.
e
Crée un point d'entrée dans le manifeste (Fichier JAR exécutables).
jarfile
f
Indique que l'archive correspond au fichier JAR dont le nom est spécifié sur la ligne de commande. Si cette
option n'est pas fournie, jar lit un fichier JAR à partir de l'entrée standard (lors de la création d'un fichier JAR)
et/ou envoie un fichier JAR vers la sortie standard (lors de l'extraction d'un fichier JAR).
basedir
i
Crée un fichier d'indice (pour accélérer les recherches dans une grande archive).
m
Ajoute un fichier manifest au fichier JAR. Un manifeste est la description du contenu et de l'origine de
l'archive. Toute archive possède un manifest par défaut, mais vous pouvez en fournir un spécial si vous
souhaitez authentifier le contenu de l'archive.
M
Utilisé avec la commande c et u pour indiquer à la commande jar qu'elle ne doit pas créer de manifeste par
défaut.
t
Affiche le contenu de l'archive
u
Met à jour le contenu d'une archive. Tous les fichiers énumérés sur la ligne de commande sont ajoutés à
l'archive.
v
Sortie de messages très détaillés.
x
Extrait le contenu d'une archive. Tous les fichiers et les répertoires énumérés sur la ligne de commande sont
extraits et créés dans le répertoire de travail courant. Si aucun fichier ou répertoire n'est spécifié, tous les
fichiers et les répertoires de l'archive sont extraits.
0
Stockage sans compression ZIP. Attention : Cette option est le chiffre 0 et non pas la lettre O.
La commande la plus courante pour créer un nouveau fichier JAR est la suivante :
jar cf FichierJAR Fichier1 Fichier2 ...
Dans l'exemple de notre applet voici ce que nous pouvons écrire :
jar cvf texte.jar texte/*.class chouette.jpg
Nous stipulons ainsi que nous désirons créer une archive texte.jar qui sera composée de l'ensemble des fichiers
*.class qui se trouve dans le paquetage texte avec en plus le fichier image chouette.jpg. Ci-dessous, nous
retrouvons le même exemple suivi d'une consultation de l'archive afin de contrôler son contenu. Vous remarquez
au passage la fabrication automatique du fichier MANIFEST.MF dans le répertoire <META-INF>.
L'expression **/ englobe tous les sousrépertoires.
tâche jar
Permet la création d'une archive jar.
Nom du fichier jar à créer.
Répertoire source, à partir duquel les
fichiers à archiver dans le fichier jar
doivent être lus.
compress
est par défaut true ; autrement dit, le
fichier est compressé. false empêche la
compression.
includes
liste de fichiers ou de modèles séparés
par des virgules, qui doivent être
utilisés.
excludes
Liste des fichiers ou des modèles
séparés par des virgules, qui ne doivent
pas être utilisés.
manifest
Permet la
manifest.
spécification
d'un
fichier
Voici comment créer l'archive correspondant
à l'applet de notre étude qui correspond à la
même archive fabriquée ci-contre :
<target name="-post-compile">
<jar jarfile="${build.web.dir}/texte.jar">
<fileset dir="${build.classes.dir}" />
<fileset dir="${build.web.dir}"
includes="chouette.jpg" />
</jar>
</target>
Voici-ci dessous le résultat correspondant :
Les options que nous avons souvent besoin sont les lettres c, t et x qui indiquent respectivement la création d'une archive, la liste du contenu et l'extraction des
fichiers. f signifie que l'argument suivant sera le nom du fichier JAR sur lequel opérer. v (verbose) demande à jar d'être plus bavard lorsqu'il affiche des
renseignements sur les fichiers : tailles heures de modification, ratios de compression.
Les éléments suivants de la ligne de commande (c'est-à-dire, tout ce qui est différent des lettres indiquant à jar ce qu'il doit faire sur lequel opérer) sont
considérés comme des éléments d'archive. Si vous créez une archive, les fichiers et les répertoires indiqués sont archivés. Lors d'une extraction, seuls les noms
des fichiers indiqués sont extraits (si vous n'indiquez aucun nom de fichier, tout le contenu de l'archive est extrait).
Utilisation de l'archive dans la page Web
Une fois le fichier JAR créé, il faudra y faire référence dans la balise <applet> de la façon suivante :
<div align="center">
<applet code="texte.TexteApplet.class"
archive="texte.jar"
width="350"
height="250"></applet>
</div>
L'attribut code doit toujours être présent. Il indique au navigateur le nom de l'applet. archive désigne tout simplement une source d'emplacement possible de la
classe applet et des autres fichiers. Chaque fois qu'un fichier de classes, d'images ou de son est requis, un navigateur acceptant les fichiers JAR commence par
rechercher ces fichiers JAR dans la liste archive. Si le ou les fichiers ne sont pas trouvés dans l'archive, ils seront alors recherchés sur le serveur WEB.
Packaging des applications
Nous quittons maintenant le monde des applets et passons au packaging des applications Java. Lorsque vous livrez une application, vous ne souhaitez généralement pas
déployer tout un ensemble de fichiers classe. Comme pour les applets, vous devez packager les fichiers de classe ainsi que d'autres ressources exigées par votre
programme dans un fichier JAR. Lorsque le programme est packagé, il peut être chargé par une commande simple ou, si le système d'exploitaiton est correctement
configuré, par un double-clic sur le fichier JAR.
Le manifest
Vous pouvez packager des programmes d'application, des composants de programme (parfois appelés des JavaBeans) et des bibiothèques de code dans des fichiers JAR.
Par exemple, la bibliothèque du runtime de la JDK est contenue dans un très grand fichier rt.jar.
Un fichier JAR contient des classes, des images et d'autres ressources, ainsi qu'un fichier manifeste qui décrit les caractéristiques particulières de l'archive. Le
fichier manifest est appelé MANIFEST.MF et se trouve dans un sous-répertoire <META-INF> du fichier JAR.
Effectivement, la commande jar ajoute automatiquement un répertoire appelé <META-INF> à notre archive. Le répertoire <META-INF> gère les fichiers décrivant le
contenu du fichier JAR. Il contient toujours au moins le fichier MANIFEST.MF. Ce fichier contient une liste des noms de fichiers contenus dans l'archive et, pour
chacun d'eux, un ensemble d'attributs pouvant être définis au niveau utilisateur.
1. Le manifest minimum légal est un peu terne :
Manifest-version: 1.0
2. Des manifestes complexes peuvent posséder d'avantage de données. Celles-ci sont regroupées en sections. La première s'appelle Main. Elle s'applique à la totalité
du fichier JAR. D'autres données peuvent spécifier les propriétés des entités nommées telles que des fichiers individuels, les packages ou les URL. Ces données
doivent commencer par une entrée Name. Les sections sont séparées par des lignes blanches :
Manifest-version: 1.0
Lignes décrivant cette archive
Name: Texte.class
Lignes décrivant ce fichier
Name: texte
Lignes décrivant ce paquetage
3. Pour créer le manifeste, placez les lignes que vous voulez éditer au manifest dans un fichier texte. Puis exécutez :
jar cfm FichierJAR FichierManifest FichierClasse ...
4. Par exemple, pour créer un nouveau fichier JAR avec un manifeste, exécutez :
jar cfm texte.jar manifest.mf texte/*.class chouette.jpg
5. Pour actualiser un fichier JAR existant, placez les ajouts dans un fichier texte et utilisez une commande telle que :
jar ufm texte.jar manifest-additions.mf
Rendre un fichier JAR exécutable
En plus des attributs, quelques valeurs spéciales peuvent être ajoutées au manifeste. L'une d'elles, Main-Class, vous permet de spécifier la classe contenant la méthode
principale main() pour une application contenue dans le fichier JAR :
Main-Class: texte.Principal
Si vous ajoutez cela au manifeste de votre fichier JAR (en utilisant l'option m décrite précédemment), vous pouvez exécuter directement votre application à partir d'un
fichier JAR :
java -jar texte.jar
Depuis la version Java SE 6.0, vous pouvez utiliser l'option e de la commande jar pour spécifier le point d'entrée de votre programme (la classe principale de
l'application qui doit être invoquée en premier par le lanceur de programme Java). Ainsi, vous n'avez plus besoin de vous préoccuper du manifeste. Tout ce fait
automatiquement :
jar cvfe texte.jar texte.Principal
Comme au-dessus, vous pouvez exécuter directement votre application à partir d'e la commande suivante :
java -jar texte.jar
Dans certaines configurations du système d'exploitation, vous pouvez lancer l'application en double-cliquant sur l'icône du fichier Jar. Voici les comportements
pour les divers systèmes d'exploitation :
1. Sous Windows, l'installateur d'exécution Java crée une association de fichier pour l'extension ".jar" qui lance le fichier avec la commande javaw -jar (à la différence
de la commande java, la commande javaw n'ouvre pas de fenêtre shell).
2. Sous Solaris, le système d'exploitation reconnaît le "nombre magique" d'un fichier Jar et le lance avec la commande java -jar.
3. Sous Mac OS X, le système d'exploitation reconnaît l'extension de fichier ".jar" et exécute le programme Java lorsque vous double-cliquez sur un fichier JAR.
Toutefois, un programme Java d'un fichier Jar ne possède pas le même aspect qu'une application native. Sous Windows, vous pouvez employer des utilitaires
d'enveloppe tiers qui transforment les fichiers JAR en exécutables Windows. Une enveloppe est un programme Windows possédant l'extension ".exe" bien connue
qui localise et lance la machine virtuelle Java (JVM) ou indique à l'utilisateur ce qu'il faut faire lorsque nous ne trouvons aucune JVM.
Sous Macintosh, la situation est un peu plus simple. L'utilitaire de paquetage d'application MRJAppBuilder permet de transformer un fichier JAR en une application
Mac de premier niveau.
Récapitulation
Pour packager une application, réunissez tous les fichiers nécessaires à votre application dans un fichier JAR et ajoutez-y un manifeste spécifiant la classe principale de
votre programme - celle qui doit être invoquée en premier par le lanceur de programme Java.
La dernière ligne du manifeste doir se terminer par un caractère de nouvelle ligne, faute de quoi le manifeste ne pourra être lu correctement. Produire un fichier texte
contenant simplement la ligne Main-Class sans terminaison est une erreur commune.
En reprenant, notre application du début, voici donc en image toute la procédure à suivre :
Voici maintenant le contenu de notre archive texte.jar :
Ajouter une autre archive (bibliothèque non connue de la JVM) à votre projet
Il arrive souvent que nous ayons besoin d'utiliser une autre bibliothèque en même temps que l'archive relative à votre projet. Pour
que cette bibliothèque soit accessible par votre projet, il est juste nécessaire de préciser où elle se situe au moyen de l'attribut ClassPath: .
Prenons par exemple un projet qui est archivé sous le nom de <StockerPhoto.jar>. Ce projet, pour fonctionner correctement,
doit impérativement disposer de la bibliothèque <metadata-extractor-2.3.1.jar> qui se trouve dans le répertoire <lib>.
L'organisation vous est montrée ci-contre. Voici donc ce que devra comporter le manifest de <StockerPhotos.jar> :
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.6.5
Created-By: 1.6.0-b105 (Sun Microsystems Inc.)
Main-Class: Stocker
Class-Path: lib/metadata-extractor-2.3.1.jar
X-COMMENT: Main-Class will be added automatically by build
Les ressources
Il est assez fréquent, à terme, d'avoir besoin de changer le titre d'une fenêtre, de changer un ensemble de messages ou tout simplement de préférer une autre image de fond
de celle prévue initialement. Nous pouvons avoir deux approches pour réaliser ces changements. Soit nous changeons dans le code source les références à ces différents
éléments ce qui nécessite, bien entendu, de tout recompiler. Ou alors nous plaçons ces éléments dans des fichiers séparés afin de proposer les changements à l'extérieur
du programme suivant le désir de l'utilisateur à l'aide d'un tout petit éditeur de texte. L'application se charge ensuite de lire le contenu de ces fichiers afin de configurer
correctement les objets requis. Lorsque vous placez des valeurs à l'extérieur de votre programme, ces valeurs sont considérées comme des ressources.
En reprenant l'exemple de notre application, nous pourrions avoir comme type de ressources, le titre de la fenêtre, le message d'invite, ainsi que l'image de fond :
Ainsi, les classes employées à la fois dans les applets et les applications utilisent généralement des fichiers de données ou de configuration associés tels que :
1. des fichiers d'images et de son ;
2. des fichiers de texte contenant les messages et les libellés des boutons ;
3. des fichiers de données binaires, par exemple pour indiquer la disposition d'une carte ;
4. des fichiers contenant l'ensemble des items des menus afin de prendre en compte la langue du pays.
En Java, un fichier associé de ce type est appelée une ressource.
.
Récupérer les valeurs des ressources
Où devons nous placer ces fichiers ressources ? Bien sûr, il serait pratique de les placer au même endroit que les autres programmes, par exemple dans un fichier JAR.
Le chargeur de classe sait comment parcourir chacun des emplacements possibles jusqu'à retrouver le fichier de classes. Toutefois, dans notre cas, nous devons
répéter le processus de recherche manuellement pour localiser les fichiers de ressources associés. La fonctionnalité de chargement de ressource automatise cette
tâche.
Nous suivrons donc les étapes suivantes pour charger une ressource :
1. Charger l'objet Class pour la classe possédant une ressource, par exemple Principal.class, ou si nous somme dans l'objet représentant la dite classe this.getClass
().
2. Appeler la méthode getRessource() ou la méthode getRessourceAsStream() suivant que vous désirez obtenir le fichier ressource en tant qu'URL ou directement les
valeurs de ce fichier au travers d'un flux adapté. Cette deuxième méthode est souvent préférable.
3. Si la ressource est un fichier image ou audio, la lire directement à l'aides des méthodes getImage() ou getAudioClip() pour les applets, et pour l'image passer par la
classe ImageIO si vous êtes sur une application.
Le chargeur de classes mémorise l'emplacement où il a chargé la classe ; il peut alors rechercher dans cet emplacement la ressource associée.
.
Par exemple, vous pouvez utiliser les instructions suivantes pour créer l'icône de l'application à partir du fichier image "icône.gif" en suivant cette procédure :
URL url = Principal.class.getRessource("icône.gif");
ImageIcon icône = new ImageIcon(url);
Cela revient à rechercher le fichier "icône.gif" au même endroit que celui où vous avez trouvé Principal.class.
Pour lire un fichier texte "titre.txt" représentant le titre de votre application, vous pouvez, par exemple, utiliser les instructions suivantes :
InputStream flux = Principal.class.getRessourceAsStream("titre.txt");
Pour lire ensuite à partir de ce flux, vous devez prendre ensuite un flux de plus niveau afin d'adapter le contenu au type requis. Ici, nous devons récupérer un
texte, il faudra donc prendre un flux capable de retrouver ce texte. Pour une entrée, nous avons besoin de la classe Scanner.
Scanner titre = new Scanner(flux);
this.setTitle(titre.nextLine());
Structuration de vos ressources
Il est possible de placer vos fichiers ressources dans un répertoire particulier afin d'éviter de les mélanger avec les fichiers de classes. Vous pouvez même hiérarchiser les
noms des ressources. Par exemple, nous pouvons placer toutes nos ressources dans un répertoire appelé justement <ressources>. La localisation se fera alors de la façon
suivante :
ressources/titre.txt
Ce nom de ressource relatif est interprété à partir du package de la classe chargeant la ressource. Remarquez l'utilisation obligatoire du séparateur /, quel que soit
le séparateur de répertoire du système d'exploitation sur lequel se trouvent finalement les fichiers de ressources. Ainsi, sous Windows, le chargeur de ressources
convertit automatiquement / en séparateur \.
Le dispositif de chargement de ressources se limite à l'automatisation du chargement des fichiers. Il n'existe pas de méthodes standard pour interpréter le contenu
d'un fichier de ressources. Chaque programme doit interpréter à sa façon le contenu de ses fichiers de ressources.
Codage de l'application en tenant compte des ressources
A titre d'exemple, voici le codage correspondant au ressources données par la structure ci-dessous :
Principal.java
package texte;
import
import
import
import
import
import
java.awt.event.*;
java.io.*;
java.util.Scanner;
javax.imageio.ImageIO;
javax.swing.*;
java.awt.*;
public class Principal extends JFrame {
public Principal() throws IOException {
this.setDefaultCloseOperation(this.EXIT_ON_CLOSE);
this.setSize(350, 250);
Scanner titre = new Scanner(this.getClass().getResourceAsStream("ressources/titre.txt"));
this.setTitle(titre.nextLine());
Image image = ImageIO.read(this.getClass().getResource("ressources/chouette.jpg"));
PanneauImage panneau = new PanneauImage(image);
this.getContentPane().add(panneau);
}
public static void main(String[] args) throws IOException {
new Principal().setVisible(true);
}
}
class PanneauImage extends JPanel {
private Image image;
private Texte invite = new Texte("C'est chouette...");
public PanneauImage(Image image) {
Scanner message = new Scanner(this.getClass().getResourceAsStream("ressources/message.txt"));
invite = new Texte(message.nextLine());
this.image = image;
invite.setCouleurSurvol(Color.red);
invite.setCouleurNormale(Color.blue);
this.add(invite);
}
protected void paintComponent(Graphics g) {
g.drawImage(image, 0, 0, this);
}
}
class Texte extends JLabel implements MouseListener {
private Color couleurSurvol, couleurNormale;
public Texte(String invite) {
super(invite);
this.setFont(new Font("Verdana", Font.BOLD, 28));
this.addMouseListener(this);
}
public void setCouleurSurvol(Color couleur) {
couleurSurvol = couleur;
}
public void setCouleurNormale(Color couleur) {
this.setForeground(couleurNormale = couleur);
}
public void mouseEntered(MouseEvent e) {
this.setForeground(couleurSurvol);
}
public void mouseExited(MouseEvent e) {
this.setForeground(couleurNormale);
}
public void mouseClicked(MouseEvent e) {}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
}
Fabrication de l'archive afin d'intégrer ces ressources
Nous commençons a avoir pas mal de fichiers, et du coup, il est largement souhaitable de packager la totalité de ces éléments dans une archive. Vous avez ci-dessous en
images, l'ensemble de la procédure à suivre :
Vérouiller un paquetage
Nous pouvons vérouiller (seal) un paquetage en langage Java pour empêcher d'autres classes Java de s'installer. Un paquetage doit être vérouillé si vous utilisez des
classes, des méthodes et des attributs visibles pour le paquetage dans votre code. Sans cela, d'autres classes peuvent se placer dans le même paquetage et ainsi obtenir
un accès aux fonctionnalités qui lui sont visibles.
Par exemple, si vous vérouillez la paquetage texte, aucune classe extérieure ne peut être définie par l'instruction :
package texte;
public class UneClasseQuelconque {
...
}
Mise en place du vérouillage
Pour ce faire, déposez toutes les classes du paquetage dans un fichier JAR. Par défaut, les paquetages d'un fichier JAR ne sont pas vérouillés.
1. Vous pouvez modifier cela en écrivant la ligne suivante dans la section principale du manifeste :
Sealed: true
2. Pour chaque paquetage, vous pouvez spécifier si vous désirez qu'il soit vérouillé ou non, en ajoutant une section supplémentaire au manifeste du fichier JAR :
Name: org/manu/util
Sealed: true
Name: org/manu/texte
Sealed: true
3. Pour verrouiller un paquetage, créer un fichier texte avec les instruction du fichier texte. Puis lancez la commande jar de la manière habituelle :
jar cfm archive.jar manifest.mf fichiers à ajouter