FiJI est un installeur Java sous licence GPL. Il permet de distribuer
simplement des applications sans que l’utilisateur ait à modifier des
scripts de lancements (pour modifier un CLASSPATH
par exemple).
Auto installeur fiji-1.0-1.jar.
Pour installer FiJI, vous devez au préalable installer une machine virtuelle Java 1.2 or 1.3. On peut en télécharger une gratuitement sur le site web de Sun.
La dernière version de FiJI est disponible sur ma page
personnelle. Pour l’installer,
se rendre dans le répertoire de l’archive Jar et taper
java -jar fiji-0.4.jar
. Les utilisateurs de Windows et Solaris peuvent
se contenter de cliquer sur le fichier pour lancer l’installation. FiJI
étant distribué avec lui-même, sous forme d’archive Jar
auto-extractible, vous pouvez vous faire une idée de ses capacités lors
de son installation.
Note: Sous Windows, le système peut se plaindre de ne pas disposer
d’assez d’espace d’environnement. Dans ce cas, ajouter la ligne
shell=command.com /E:8000 /P
à votre fichier config.sys puis
rebooter le système. D’autre part, il est préférable de ne pas altérer
la casse des noms de fichiers (il existe une option sous Windows NT pour
travailler avec des noms de fichier distinguant la casse).
Pour compiler FiJI, vous devez installer Ant version 1.2. Vous pouvez le
télécharger sur le site d’Apache. Taper
simplement ant
dans le répertoire d’installation de FiJI pour le
recompiler.
Examinons le fichier install.xml du répertoire de FiJI (qui permet de générer l’archive d’installation de FiJI) :
<?xml version="1.0" encoding="iso-8859-1"?>
<install jar="/tmp/fiji-0.4.jar"
logo="lib/logo.gif">
<text title="FiJI"
text="This program will install Fiji on\
your computer. Hit [|>] to continue\
or [O] to abort installation."/>
<license file="lib/LICENSE"/>
<copy dir="tmp"
property="fiji.home"
defaults="Windows=c:\Fiji,Unix=/usr/local/fiji">
<filter file="bin/fiji" from="@HOME@" to="${fiji.home}"/>
<filter file="bin/fiji.bat" from="@HOME@" to="${fiji.home}"/>
</copy>
<link from="${fiji.home}/bin/fiji"
to="/usr/local/bin"
mandatory="no"/>
<append file="c:\autoexec.bat"
line="set PATH=%PATH%;${fiji.home}\bin"
os="Windows"
mandatory="no"/>
<text title="Congratulation !"
text="FiJI have been installed succesfully.\
Hit [O] to terminate this program."/>
</install>
Ce fichier XML est un script pour la procédure d’installation. Chaque élément y représente un écran.
Comme vous pouvez le constater, ce script est assez simple et facile à coder (quelques minutes lorsqu’on connaît la syntaxe des les éléments). Pour produire le fichier Jar d’installation, se placer dans le répertoire du script et taper fiji. Il est aussi possible de passer le chemin du script sur la ligne de commande. On peut aussi indiquer au programme que l’on souhaite être tenu au courant de l’avancement des opérations avec l’option -v. On obtient de l’aide avec -?.
Pour lancer l’installeur Jar produit, l’utilisateur doit se placer dans le répertoire de l’archive et taper java -jar nom-archive.jar sur la ligne de commande, ou bien double cliquer ce fichier (sous Windows ou Solaris).
Les éléments du fichier install.xml correspondent à des tâches (sauf pour l’élément racine et les filtres). L’élément racine install accepte deux attributs :
<install jar="../fiji-0.3.jar"
logo="lib/logo.gif">
...
</install>
Le premier, jar, donne le nom du fichier Jar produit. Le second indique l’URL du logo (l’image à gauche de la fenêtre). Ce dernier attribut n’est pas obligatoire (aucune image n’est affichée s’il est absent).
Il est possible de personnaliser l’interface de l’installeur avec l’élément theme (qui doit venir juste après install). Il peut comporter 6 attributs (primary1, primary2, primary3, secondary1, secondary2 et secondary3) représentant chacun une couleur codée en hexadecimal. Ces six couleurs permettent de définir un thème pour Swing (look & feel Metal), comme indiqué sur cette page. Par exemple, l’élément suivant définit le thème de l’installeur de FiJI:
<theme primary2="6eb2ce"
primary3="d5fedc"/>
Afin que les scripts d’installation soient portables, les chemins des fichiers doivent être écrits avec une notation URL (chemins Unix). En pratique, cela consiste à remplacer, sous Windows, les antislashs par des slashs. Donc on écrira foo/bar.txt à la place de foo\bar.txt. D’autre part, les chemins relatifs le sont par rapport au répertoire du script install.xml.
Chaque écran de l’installeur est traité par une tâche donnée. Ces tâches sont :
Cette tâche affiche un simple texte (dans l’attribut text) précédé d’un titre (dans l’attribut title). Par exemple :
<text title="FiJI"
text="This program will install Fiji on\
your computer. Hit [>] to continue\
or [O] to abort installation."/>
Produit l’écran suivant :
Cet écran est le premier de l’installeur de FiJI lui-même. Ces deux attributs sont obligatoires. Un retour à la ligne est indiqué par un anti-slash.
Cette tâche affiche une licence pour agrément. L’utilisateur doit l’accepter (en cliquant sur le message I accept the terms of this License) ou arrêter l’installation. Par exemple :
<license file="lib/LICENSE"/>
Affiche l’écran :
Cet élément affiche le fichier LICENSE du sous répertoire lib (tous les chemins sont relatifs au répertoire du script install.xml, sauf s’ils sont absolus, donc s’ils commencent par “/”).
Cette tâche copie le contenu d’un répertoire (celui de l’application à installer) dans un répertoire choisi par l’utilisateur. Ce répertoire a été zippé lors de la génération de l’archive Jar.
<copy dir="tmp"
property="fiji.home"
defaults="Windows=c:\Fiji,Unix=/usr/local/fiji">
<filter file="bin/fiji" from="@HOME@"
to="${fiji.home}"/>
<filter file="bin/fiji.bat" from="@HOME@"
to="${fiji.home}"/>
</copy>
Affiche l’écran suivant :
L’attribut dir indique le répertoire source à copier. property communique au programme le nom de la propriété par laquelle on peut récupérer le répertoire d’installation. Il est possible d’utiliser cette propriété dans une autre tâche. Par exemple, l’attribut to de l’élément filter est \${fiji.home}, qui sera remplacé par le répertoire d’installation. Le dernier attribut, defaults indique une liste des répertoires d’installation par défaut suivant les plateformes (séparés par des virgules).
Des éléments filter peuvent être inclus dans l’élément copy. Ils permettent de filtrer certains fichiers. Par exemple, l’élément suivant :
<filter file="bin/fiji" from="@HOME@"
to="${fiji.home}"/>
Va filtrer le fichier bin/fiji (du répertoies après installation) en
remplaçant la chaîne @HOME@
par ${fiji.home}
qui est le répertoire
d’installation (dont le nom est extrait de la propriété indiquée par
l’attribut property de l’élément copy). Par conséquent, le script
suivant :
#!/bin/sh
java -jar @HOME@/fiji.jar -v "$@"
Donnera, après application du filtre :
#!/bin/sh
java -jar /usr/local/fiji/fiji.jar -v "$@"
Sur le disque de l’utilisateur, si ce dernier a choisi /usr/local/fiji comme répertoire d’installation (celui par défaut pour les systèmes Unix).
Lorsque l’utilisateur clique sur le bouton [>], les fichiers sont recopiés et la barre de progression indique l’avancement de la copie.
Cette tâche est spécifique aux système Unix (elle ne sera pas exécutée sur d’autres systèmes). Considérons l’élément suivant :
<link from="${fiji.home}/bin/fiji" to="/usr/local/bin"/>
Il donnera l’écran :
Cette tâche réalise deux choses :
chmod a+rx ${fiji.home}/bin/fiji
).ln -sf ${fiji.home}/bin/fiji /usr/local/bin
.L’utilisateur peut alors taper fiji
sur la ligne de commande pour
lancer l’application.
Cette tâche est spécifique aux système Unix sous gestionnaire de fenêtre KDE (elle ne sera pas exécutée sur d’autres systèmes). Considérons l’élément suivant :
<kdeDesktop exec="fiji"
icon="${fiji.home}/prj/fiji/lib/fiji.gif"
term="1"/>
Il donnera l’écran :
Cette tâche propose d’ajouter une icône pour lancer l’application sur le bureau de l’utilisateur. L’attribut exec indique l’exécutable à lancer lorsqu’on clique sur l’icône. icon indique le chemin (sur le système sur lequel le programme est installé) vers l’icône. term indique si l’on doit lancer le programme dans un terminal (valeur 1) ou non (0).
Cette tâche ajoute une ligne à un fichier. Elle permet par exemple de mettre à jour un fichier de configuration (comme autoexec.bat ou /etc/profile). L’élément correspondant est le suivant:
<append file="c:\autoexec.bat"
line="set PATH=%PATH%;${fiji.home}\bin"
os="Windows"/>
Cet élément affichera l’écran suivant:
Cette tâche affiche la ligne (dans l’attribut line) ajoutée à un fichier dont la valeur par défaut est dans file. L’attribut os indique l’OS ciblé.
Toutes les tâches comportent des attributs communs:
Ces attributs permettent de personnaliser l’interface, mais surtout de la traduire.
Pour expliquer le fonctionnement de ce programme, il nous faut examiner deux étapes : la génération de l’installeur Jar et l’extraction réalisée par l’installeur.
L’installeur (qui est un fichier Jar) est généré de la manière suivante :
Cette méthode utilisant la sérialisation a le mérite d’être simple. La configuration des classes de l’installeur est réalisée lors de la construction de l’arbre des objets (lors du parsing XML) par appel des méthodes setFoo(). De plus, cette méthode est légère : les classes sérialisées pèsent moins de 700 octets compressés, ce qui est moins que le fichier XML lui-même (et si l’on utilisait un parser XML, il faudrait l’inclure dans l’archive, ce qui l’alourdirait).
Je me suis laissé dire que l’on peut rencontrer des problèmes lors de la désérialisation de classes issues de VMs de différentes versions, mais aucune classe de l’API Java n’est sérialisée et je n’ai pas rencontré de problèmes lors de mes tests (avec des machines virtuelles 1.1, 1.2 et 1.3).
La deuxième étape (extraction de l’archive) fonctionne de la manière suivante :
Ce texte n’est qu’un simple survol du fonctionnement de FiJI, et je vous invite à examiner le code de plus près pour plus de détails (le code n’est cependant pas encore commenté).
FiJI est un système extensible: il est possible de développer ses propres tâches pour répondre à des besoins spécifiques. Je prendrai comme exemple la tâche Link qui crée un lien (pour les système Unix) d’un script de lancement vers un répertoire du PATH.
Pour créer un tel lien, il nous faut connaître les fichiers source et destination de ce lien. L’élément pour cette tâche aura donc l’allure suivante:
<link from="${fiji.home}/bin/fiji" to="/usr/local/bin"/>
L’élément Link donne le nom de la classe à développer (Link
avec une
majuscule, pour se conformer aux conventions de nommage Java).
L’attribut from indique le fichier source alors que to indique le
répertoire de destination du lien.
Lorsque le parser XML de FiJI rencontre cet élément, il tente
d’instancier la classe Link
puis invoque les méthodes setFrom()
et
setTo()
sur cet objet. Le système des tâches de FiJI est extensible
dans la mesure où le programme ne sait rien des tâches qu’il construit,
mais procède par introspection pour construire l’installeur. Il n’est
donc pas nécessaire de modifier le code de FiJI pour y ajouter de
nouvelles tâches.
D’autre part, notre classe doit étendre le classe abstraite
casa.fiji.installer.Task
pour être une tâche valide. Elle doit aussi
appartenir au paquet casa.fiji.installer
.
Pour finir, il me faut encore parler des ressources et classes à ajouter au jar de l’installeur. En effet, ce fichier est construit par FiJI et doit contenir toutes les classes et ressources nécessaires à l’installeur généré, on doit donc lui indiquer.
En ce qui concerne les classes, cela est fait automatiquement (par
introspection) par la classe parente Task
: elle ajoute aux fichiers à
placer dans l’installeur la classe de la tâche et son interface
graphique (dont le nom est, par convention, celui de la classe auquel on
ajoute UI
, donc si notre tâche s’appelle Link
, son interface
graphique doit être codée dans une classe s’appelant LinkUI
). Si l’on
doit ajouter une autre classe, on doit le faire dans le constructeur de
notre tâche avec la méthode addClasses()
à laquelle on passe un
tableau de chaînes contenant le nom qualifié des classes (avec leur
paquet). Dans notre exemple, il n’est pas nécessaire d’ajouter des
classes, donc cette méthode n’est pas appelée.
Par contre, on doit ajouter un fichier de ressource pour une icône, ce
que l’on fait avec la méthode addResources()
à laquelle on passe un
tableau des fichiers à ajouter. On notera que la référence à l’interface
graphique de notre tâche est marquée transient
car elle ne doit être
sérialisée (il est inutile et dangereux de sérialiser des éléments
d’interface graphique).
Nous en savons assez pour comprendre la première partie du source de la
classe Link
que voici:
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package casa.fiji.installer;
import casa.fiji.util.Misc;
import java.awt.*;
import javax.swing.*;
import java.io.*;
import java.util.*;
/**
* The link task. It creates a link from a script (or executable) to a
* directory in the PATH. For Unix systems only.
*/
public class Link extends Task {
/**
* The from part of the link.
*/
String from=null;
/**
* The from part of the link, expanded.
*/
String fromExpanded=null;
/**
* The to part of the link.
*/
String to=null;
/**
* The to part of the link, expanded.
*/
String toExpanded=null;
/**
* The reference to the GUI.
*/
transient LinkUI ui=null;
/**
* The resources used by this task.
*/
static final String[] resources={"lib/home.gif"};
/**
* Constructor. Adds resources to the jar installer.
*/
public Link() {
super();
addResources(resources);
}
/**
*Sets the from part of the link.
*/
public void setFrom(String from) {this.from=from;}
/**
* Sets the to part of the link.
*/
public void setTo(String to) {this.to=to;}
Cette première partie du code est appelée lors de la constuction de
l’archive de l’installeur (pour intialiser la tâche Link
). Toutes les
tâches sont contenues dans un vecteur de la classe
casa.fiji.install.Install
qui est la classe principale de
l’installeur. Cet objet (et les tâches intialisées qu’il contient) est
sérialisé dans l’archive de l’installeur. Lors de l’exécution de
l’installeur, cet objet est désérialisé puis exécuté, ce qui lancera la
procédure d’installation.
Nous allons maintenant voir la partie du code exécutée lors de l’installation.
Pour commencer, l’installeur doit savoir si la tâche doit être exécutée
(certaines sont spécifiques à des plateformes ou configurations). Il
appelle donc la méthode isValid()
qui renvoie un booléen. Dans notre
cas, nous renvoyons true
si la plateforme est un Unix.
Ensuite, l’installeur doit récupérer l’interface graphique de la tâche.
Pour ce faire, il appelle la méthode getUI()
. Notre méthode instancie
un objet LinkUI
et le renvoie.
Les méthodes ci-dessus sont invoquées lors du lancement de l’installeur. Les suivantes le sont lors de l’exécution d’une tâche:
La méthode init()
est appelée lorsque l’interface graphique de la
tâche est affichée. Elle se contente d’évaluer les expressions pour
from et to, c’est à dire, remplacer les références à des propriétés
par leur valeur. Par exemple, \${fiji.home}/bin/fiji sera remplacé par
/usr/local/fiji/bin/fiji si le répertoire d’installation est
/usr/local/fiji. On notera qu’après évaluation, on enregistre ces
valeurs dans des variables suffixées par Expanded, de manière à ce que
si l’utilisateur revient en arrière, ces expressions soient réévaluées.
Pour finir, l’installeur appelle la méthode next()
lorsque
l’utilisateur clique sur le bouton [>] pour passer à l’étape
suivante. Cette méthode crée le lien et renvoie une valeur booléenne
indiquant que l’opération s’est bien déroulée. Si c’est le cas,
l’installeur passe à l’étape suivante, sinon, un message d’erreur est
affiché et l’on reste à l’étape courante.
Voici le source de cette partie du code:
/**
* Indicates if the system running the installer should run this task.
* That is if it is a Unix like system.
*/
public boolean isValid() {
return isUnix();
}
/**
* Returns the GUI for this task.
*/
public Component getUI() {
ui=new LinkUI(this);
return ui;
}
/**
* Init is called when the task is shown in the installer. It expands
* the from and to parts of the link (evaluating expressions).
*/
public void init() {
fromExpanded=Misc.expand(from);
toExpanded=Misc.expand(to);
ui.setFrom(fromExpanded);
ui.setTo(toExpanded);
}
/**
* Creates the link (when the user clicks on the next button).
*/
public boolean next() {
// build the chmod command
toExpanded=ui.getTo();
String command="chmod a+x "+fromExpanded;
String error=Misc.execute(command);
// if error, display an error message
if(error!=null) {
JOptionPane.showMessageDialog(ui,
"Unbable to change the mode:\n"+command+'\n'+error,
"Error",JOptionPane.ERROR_MESSAGE);
return false;
}
// build the ln command
command="ln -sf "+fromExpanded+" "+toExpanded;
error=Misc.execute(command);
// if error, display an error message
if(error!=null) {
JOptionPane.showMessageDialog(ui,
"Unbable to make the link:\n"+command+'\n'+error,
"Error",JOptionPane.ERROR_MESSAGE);
return false;
}
return true;
}
Pour finir, il nous faut fournir une interface graphique pour notre
tâche. Cette interface est implémentée dans la classe LinkUI
dont
voici le source:
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package casa.fiji.installer;
import casa.fiji.util.Misc;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* The GUI for the link task.
*/
public class LinkUI extends JPanel implements ActionListener {
/**
* The refernce to the task.
*/
Link link=null;
/**
* The text field for the directory.
*/
JTextField field=new JTextField();
/**
* The label for the from part of the link.
*/
JTextField label=new JTextField();
/**
* Home button.
*/
JButton buttonHome=new JButton(new
ImageIcon(ClassLoader.getSystemResource("lib/home.gif")));
/**
* The constructor. GUI initialization.
*/
public LinkUI(Link link) {
this.link=link;
ui();
}
/**
* GUI initialization.
*/
void ui() {
setLayout(new GridBagLayout());
Misc.add(this,new JLabel("Link"), 0, 0, 2, 1, 0.0, 0.0,
GridBagConstraints.NORTH, GridBagConstraints.NONE,
new Insets(10, 10, 10, 10));
Misc.add(this,new JLabel("This program will make a link from:"),
0, 1, 2, 1, 0.0, 1.0,GridBagConstraints.SOUTHWEST,
GridBagConstraints.NONE,new Insets(10, 10, 5, 10));
Misc.add(this,label, 0, 2, 2, 1, 1.0, 0.0,
GridBagConstraints.SOUTHWEST, GridBagConstraints.HORIZONTAL,
new Insets(0, 10, 10, 10));
Misc.add(this,new JLabel("to a directory in your PATH:"),
0, 3, 2, 1, 0.0, 0.0,GridBagConstraints.NORTHWEST,
GridBagConstraints.NONE,new Insets(10, 10, 5, 10));
Misc.add(this,field, 0, 4, 1, 1, 1.0, 1.0,
GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL,
new Insets(0, 10, 10, 5));
Misc.add(this,buttonHome, 1, 4, 1, 1, 0.0, 1.0,
GridBagConstraints.NORTHEAST, GridBagConstraints.NONE,
new Insets(0, 0, 10, 10));
buttonHome.addActionListener(this);
buttonHome.setMargin(new Insets(0,0,0,0));
label.setEditable(false);
}
/**
* User actions processing.
*/
public void actionPerformed(ActionEvent e) {
if(e.getSource()==buttonHome) {
JFileChooser chooser=new
JFileChooser(System.getProperty("user.home"));
chooser.setDialogType(JFileChooser.OPEN_DIALOG|
JFileChooser.DIRECTORIES_ONLY);
int returnVal=chooser.showOpenDialog(this);
File dir=chooser.getCurrentDirectory();
if(dir.isDirectory()) field.setText(dir.getAbsolutePath());
}
}
/**
* Sets the to part of the link.
*/
public void setTo(String to) {field.setText(to);}
/**
* Returns the to part of the link.
*/
public String getTo() {return field.getText();}
/**
* Sets the from part of the link.
*/
public void setFrom(String from) {label.setText(from);}
}
Cette classe est assez classique si ce n’est qu’elle doit dériver
(directement ou non) de JPanel
. D’autre part, on notera qu’elle
utilise add()
de la classe casa.fiji.util.Misc
qui permet d’ajouter
un composant à un GridBagLayout
. On prendra soin de ne pas coder des
interfaces avec des coordonnées en dur sans quoi elles ne sont pas
portables (pas même entre systèmes identiques).
Lorsqu’une tâche a été codée, les classes correspondantes doivent être placée dans le CLASSPATH de FiJI pour être prise en compte.
J’espère que cet apperçu sera suffisant pour vous mettre le pied à l’étrier. Pour ma part, je pense que la meilleure solution pour commencer est de partir de cet exemple et de le modifier pour vos besoins. Si vous avez des questions, n’hésitez pas à me les envoyer par mail.
Ce programme étant sous licence GPL, toute tâche doit l’être également. Cependant, il est tout à fait possible d’utiliser FiJI pour distribuer tout programme (même commercial).
Pour utiliser cette tâche dans un build file, il est nécessaire de placer l’archive fiji.jar dans le répertoire lib de son installation de Ant.
Cette tâche permet de générer des installeurs FiJI depuis un build file Ant.
La déclaration de la tâche (sous l’élément <project>
) est la suivante:
<taskdef name="fiji" classname="net.cafebabe.fiji.builder.FijiTask"/>
Attribut | Description | Requis |
---|---|---|
file | Fichier de définition de l’installeur à générer. | Oui |
verbose | La valeur yes affiche à l’écran des messages d’information pendant la génération de l’installeur. | Non (la valeur est no par défaut) |
Cette tâche n’accepte ni élément ni texte encapsulés.
Pour générer un installeur dont le fichier de définition est fiji/install.xml en mode verbeux, on pourra écrire la tâche suivante :
<fiji file="fiji/install.xml"
verbose="true"/>
Ce logiciel est distribué sous licence GPL. Vous trouverez une copie de cette licence dans le répertoire d’installation de FiJI, dans le fichier LICENSE.
Cette version corrige un bug dans le chargement du fichier de configuration (avec un message “String index out of range: 1”). Merci à Daphné Costantini pour ce bug report. Corrige aussi un bug de l’installeur de l’appli.
net.cafebabe.fiji
).src
de l’élément copy
a été renommé dir
, prop
a
été renommé property
. L’attribut target
de l’élément filter
a
été renommé file
.Task
(la méthode done()
, assez
complexe à implémenter, a été remplacée par next()
dont le
principe est beaucoup plus simple. L’écriture de nouvelles tâches
devrait en être grandement simplifié.Enjoy!