Les préférences d'une application

Chapitres traités   

La plupart des programmes peuvent être configurés par leurs utilisateurs. Il doivent ensuite enregistrer les préférences définies, puis les restaurer au moment du redémarrage. Dans ce cas de figure, le programme est fourni avec un fichier de configuration que l'utilisateur peut éventuellement régler indépendamment du programme lui-même. Nous montrerons ici une approche simple permettant de stocker les informations de configuration que les applications Java adoptent généralement.


Choix du chapitre Concordance de propriétés - classe Properties

Une concordance de propriétés est une structure de données qui stocke les paires clé / valeur. Les concordances de propriétés permettent souvent de stocker des informations de configuration. Elles possèdent trois caractéristiques particulières :

  1. Les clés et les valeurs sont des chaînes.
  2. Le jeu peut facilement être enregistré dans un fichier et chargé à partir d'un fichier.
  3. Il existe un tableau secondaire pour les valeurs par défaut.

La classe de la plate-forme Java qui implémente une concordance de propriété s'appelle java.util.Properties. Ainsi, les concordances de propriétés sont utiles pour spécifier des options de configuration pour les programmes. Voici un exemple qui permet à un utilisateur de configurer le titre du programme ainsi que le message d'invite. Pour cela, nous passerons par différentes phases :

 

Concordance des propriétés du programme

Properties réglages = new Properties();
réglages.put("titre", "Intitulé de l'application");
réglages.put("invite", "Bienvenue à tous !");

La première chose à faire est de créer l'objet (réglages), représentant l'ensemble des propriétés de l'application et ensuite de spécifier chacune des clés avec la valeur associée au moyen de la méthode put().

 

Création du fichier de configuration

Utilisez la méthode store() pour enregistrer cette liste de propriétés dans un fichier. Ici, nous enregistrons simplement la concordance de propriétés réglages dans le fichier de configuration réglages.properties :

FileOutputStream fichier = new FileOutputStream("réglages.properties");
réglages.store(fichier, "Réglages du logiciel");

Le deuxième argument de la méthode store() est un commentaire inclus en tête du fichier de configuration. Le jeu d'exemple donne le fichier de configuration suivant :

#Réglages du logiciel
#Fri Sep 22 17:52:15 CEST 2006
invite=Bienvenue \!
titre=Titre de l'application

Les fichiers de configuration en Java comporte généralement l'extension *.properties.
.

 

Chargement du fichier de configuration

Une fois que le fichier de configuration est constitué, vous pouvez à tout moment récupérer son jeu de propriétés à partir de la méthode load() de la classe Properties. Ensuite, utilisez la méthode getProperty() pour atteindre la propriété désirée afin d'effectuer les réglages en conséquence :

import java.io.*;
import java.util.Properties;

public class Propriétés {
   public static void main(String[] args) throws IOException {
      // lecture du fichier de propriétés
      System.out.println("Lecture du fichier de propriétés...");
      Properties lecture = new Properties();
      lecture.load(new FileInputStream("réglages.properties"));
      System.out.println("Titre : "+lecture.getProperty("titre"));
      System.out.println("Message : "+lecture.getProperty("invite"));       
   }
}

 

Paramètres par défaut

Si l'utilisateur omet certains paramètres dans le fichier de configuration, il est possible de fournir des paramètres par défaut. La classe Properties possède deux mécanismes pour fournir les paramètres par défaut :

  1. Tout d'abord, dès que vous étudiez la valeur d'une chaîne, vous pouvez spécifier un paramètre par défaut à utiliser lorsque la clé n'est pas présente. En reprenant l'exemple précédant :

    System.out.println("Titre : "+lecture.getProperty("titre", "Titre par défaut"));

    S'il existe une propriété titre dans le tableau des propriétés, le système utilisera la chaîne correspondante délivrée indirectement par par le fichier de configuration. Dans le cas contraire, c'est "Titre par défaut" qui sera utilisé.
  2. S'il vous paraît trop ennuyeux de spécifier le paramètre par défaut pour chaque appel à getProperty(), vous pouvez packager tous les paramètres par défaut dans une concordance de propriété secondaire et fournir cette concordance dans le constructeur de votre tableau de recherche. Voir l'exemple ci-dessous :

En partant du fichier de configuration réglages.properties suivant :

#Réglages du logiciel
#Fri Sep 22 17:52:15 CEST 2006
invite=Bienvenue \!

Et à partir du programme suivant :

import java.io.*;
import java.util.Properties;

public class Propriétés {
   public static void main(String[] args) throws IOException {
      // création d'une concordance de propriété secondaire
      Properties parDéfaut = new Properties();
      parDéfaut.put("titre", "Titre par défaut");
      parDéfaut.put("invite", "Bienvenue à tous !");      

      // lecture du fichier de propriétés
      System.out.println("Lecture du fichier de propriétés...");
      Properties lecture = new Properties(parDéfaut);  // placer le tableau secondaire en paramètre du constructeur
      lecture.load(new FileInputStream("réglages.properties"));
      System.out.println("Titre : "+lecture.getProperty("titre"));
      System.out.println("Message : "+lecture.getProperty("invite"));       
   }
}

Voici ce que nous obtenons :

Le tableau secondaire doit toujours être placé en paramètre du constructeur de la classe Properties qui représente le fichier de configuration.

 

Choix du chapitre Configuration d'un logiciel avec un fichier de propriétés

Afin de bien montrer l'intérêt de configurer un logiciel au travers de préférences définies par l'utilisateur, je vous propose de le visualiser au travers d'une étude traduite sous la forme d'une application graphique. A remarquer qu'il est possible de fabriquer son fichier de configuration manuellement. C'est ce qui a été réalisé dans cet exemple.

Ce logiciel 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 avec fichier de configuration

Voici donc 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 java.awt.event.*;
import java.io.*;
import java.util.Properties;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;

public class Principal extends JFrame {   
   private Properties réglages = new Properties();
   public Principal() throws IOException {
      this.setDefaultCloseOperation(this.EXIT_ON_CLOSE); 
      this.setSize(350, 250);
      réglages.load(new FileInputStream("réglages.properties"));
      this.setTitle(réglages.getProperty("titre")); 
      this.getContentPane().add(new PanneauImage(réglages));
   }
   public static void main(String[] args) throws IOException {
      new Principal().setVisible(true);
   }
}

class PanneauImage extends JPanel {
  private Image image;
  private Texte invite;
  public PanneauImage(Properties réglages) throws IOException { 
     image = ImageIO.read(new File(réglages.getProperty("image")));
     invite = new Texte(réglages.getProperty("message"));
     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) {}   
}

 

Choix du chapitre Archive et ressources

Lorsque votre logiciel est relativement conséquent, généralement beaucoup de fichiers sont créés et utilisés. Il est préférable dans ce cas là de déployer votre application au moyen d'une archive jar. Comme nous l'avons découvert dans l'étude précédente, nous devons passer par le système de ressources afin de permettre la récupération de fichier connexes comme les images et les fichiers de configuration. Ainsi, vous disposez d'un seul fichier, qui plus est compressé, qui empaquette l'ensemble de l'application.

Pour en savoir plus sur les archives et les ressources, reportez-vous sur le cours précédent.

En reprenant l'exemple précédent, voici toute la procédure à suivre, des différentes lignes de code jusqu'à la création de l'archive et le lancement de l'application :

Principal.java
package texte;

import java.awt.event.*;
import java.io.*;
import java.util.Properties;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;

public class Principal extends JFrame {   
   private Properties réglages = new Properties();
   public Principal() throws IOException {
      this.setDefaultCloseOperation(this.EXIT_ON_CLOSE); 
      this.setSize(350, 250);
      réglages.load(this.getClass().getResourceAsStream("ressources/reglages.properties"));
      this.setTitle(réglages.getProperty("titre")); 
      this.getContentPane().add(new PanneauImage(réglages));
   }
   public static void main(String[] args) throws IOException {
      new Principal().setVisible(true);
   }
}

class PanneauImage extends JPanel {
  private Image image;
  private Texte invite;
  public PanneauImage(Properties réglages) throws IOException { 
     image = ImageIO.read(this.getClass().getResource("ressources/"+réglages.getProperty("image")));
     invite = new Texte(réglages.getProperty("message"));
     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) {}   
}

 

Choix du chapitre Propriétés système

Maintenant que nous connaissons la notion de concordance de propriétés, nous pouvons aller un peu plus loin. il existe, en effet, des propriétés bien particulières qui correspondent aux réglages du système d'exploitation. Il s'agit en réalité de variables d'environnement que nous applelons propriétés système.

Les informations de votre système sont effectivement concervées dans un objet Properties renvoyé par la méthode statique getProperties() de la classe System. Pour accéder à une seule clé, vous pouvez alors faire appel à la méthode getProperty(clé) de cette même classe.

Le tableau suivant résume les propriétés système obligatoirement définies dans tout environnement Java :

Propriétés système

Signification

java.vendor
java.vendor.url
java.version
java.home
java.class.version
java.class.path
os.name
os.arch
os.version
file.separator
path.separator
line.separator
user.name
user.home
user.dir

Chaîne désignant le concepteur
URL du concepteur
Version Java
Répertoire d'installation de Java
Version des classes Java
Chemin des classes
Nom du système d'exploitation
Architecture du système d'exploitation
Version du système d'exploitation
Séparateur de fichiers (comme / ou \ )
Séparateur de chemin (comme : ou ; )
Séparateur de ligne (comme \n ou \r\n )
Nom de compte utilisateur
Répertoire personnel de l'utilisateur
Répertoire courant

Votre application peut également définir ses propres propriétés système à l'aide de la méthode statique setProperty() de la classe System.
.

Voici un exemple de code très simple qui recense à l'écran l'ensemble des propriétés du système :

import java.io.IOException;
import java.util.Properties;

public class Principal {
   public static void main(String[] args) throws IOException {
      Properties propriétés = System.getProperties();
      propriétés.store(System.out, "Propriétés du système");
   }
}

Et voici le résultat :

#Propriétés du système
#Mon Sep 25 16:44:44 CEST 2006
java.runtime.name=Java(TM) 2 Runtime Environment, Standard Edition
sun.boot.library.path=C\:\\Program Files\\Java\\jdk1.5.0_05\\jre\\bin
java.vm.version=1.5.0_05-b05
java.vm.vendor=Sun Microsystems Inc.
java.vendor.url=http\://java.sun.com/
path.separator=;
java.vm.name=Java HotSpot(TM) Client VM
file.encoding.pkg=sun.io
user.country=FR
sun.os.patch.level=Service Pack 2
java.vm.specification.name=Java Virtual Machine Specification
user.dir=L\:\\BTS IRIS\\TP Java\\Syst\u00E8me
java.runtime.version=1.5.0_05-b05
java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment
java.endorsed.dirs=C\:\\Program Files\\Java\\jdk1.5.0_05\\jre\\lib\\endorsed
os.arch=x86
java.io.tmpdir=C\:\\DOCUME~1\\HP_PRO~1\\LOCALS~1\\Temp\\
line.separator=\r\n
java.vm.specification.vendor=Sun Microsystems Inc.
user.variant=
os.name=Windows XP
sun.jnu.encoding=Cp1252
java.library.path=C\:\\Program Files\\Java\\jdk1.5.0_05\\jre\\bin;.;C\:\\WINDOWS\\system32;
C\:\\WINDOWS;C\:\\WINDOWS\\System32\\Wbem;c\:\\Python22;
java.specification.name=Java Platform API Specification
java.class.version=49.0
sun.management.compiler=HotSpot Client Compiler
os.version=5.1
user.home=C\:\\Documents and Settings\\HP_Propri\u00E9taire
user.timezone=Europe/Paris
java.awt.printerjob=sun.awt.windows.WPrinterJob
file.encoding=Cp1252
java.specification.version=1.5
java.class.path=L\:\\BTS IRIS\\TP Java\\Syst\u00E8me\\build\\classes
user.name=HP_Propri\u00E9taire
java.vm.specification.version=1.0
java.home=C\:\\Program Files\\Java\\jdk1.5.0_05\\jre
sun.arch.data.model=32
user.language=fr
java.specification.vendor=Sun Microsystems Inc.
awt.toolkit=sun.awt.windows.WToolkit
java.vm.info=mixed mode
java.version=1.5.0_05
java.ext.dirs=C\:\\Program Files\\Java\\jdk1.5.0_05\\jre\\lib\\ext
sun.boot.class.path=C\:\\Program Files\\Java\\jdk1.5.0_05\\jre\\lib\\rt.jar;C\:\\Program Files\\Java\\jdk1.5.0_05\\jre\\lib\\i18n.jar;C\:\\Program Files\\Java\\jdk1.5.0_05\\jre\\lib\\sunrsasign.jar;C\:\\Program Files\\Java\\jdk1.5.0_05\\jre\\lib\\jsse.jar;
java.vendor=Sun Microsystems Inc.
file.separator=\\
java.vendor.url.bug=http\://java.sun.com/cgi-bin/bugreport.cgi
sun.io.unicode.encoding=UnicodeLittle
sun.cpu.endian=little
sun.desktop=windows
sun.cpu.isalist=