juin
2006
Une application doit pouvoir être configurable facilement, que ce soit par une interface graphique ou des fichiers de configuration. En Java, cela s’effectue généralement via la classe Properties qui permet de lire et d’écrire des propriétés dans un fichier en gérant des associations clef-valeur…
Mais la plupart du temps cela devient vite complexe, car en plus du code pour la lecture/écriture du fichier, chaque propriété doit être récupérée individuellement (et éventuellement parsée puisqu’il s’agit de chaînes de caractères). Le code à écrire n’est pas vraiment des plus compliqués, mais il peut s’avérer assez long et fastidieux, et donc source d’erreurs voir d’oublis…
De plus, si le nombre d’élément configurable est important, il peut être difficile de faire le lien entre le champs de la classe et le nom de la propriété correspondante, et il existe peu de librairie qui facilite cela car jusqu’à présent il n’y avait pas de moyen simple d’associer une clef à un champs d’une classe…
Mais avec les Annotations de Java 5.0, c’est désormais possible….
J’ai commencé à développer une petite librairie qui se charge donc de lire des fichiers *.properties afin de renseigner automatiquement les champs des objets. Ainsi il suffit de « marquer » les champs de n’importe quel objet via une annotation spécifique : @Property. Cette annotation permet d’indiquer la clef qui sera associé à cette propriété. Par exemple cela pourrait donner :
public class MyObject {
@Property("key.name")
private String name = "valeur par défaut";
@Property("key.count")
private int number = 0;
}
Ensuite il « suffit » de récupérer ces annotations en utilisant l’API de réflection de Java pour pouvoir recomposer l’association clef-valeur et donc récupérer la valeur associé si elle existe, afin de pourvoir modifier les valeurs des champs de la classe.
C’est ce que je vous propose avec classe PropertiesManager : Elle permet ainsi de gérer en interne la lecture des *.properties, par exemple depuis un fichier :
PropertiesManager manager = new PropertiesManager();
// chargement d'un fichier *.properties :
try {
manager.load("./config.properties");
} catch (IOException e) {
System.err.println("Erreur lors de la lecture du fichier : " + e);
}
Mais elle permet surtout de « renseigner » les différents champs d’une classe lorsqu’il sont marqué par l’annotation @Property. Ainsi chacun de ses champs verra sa valeur modifié si le fichier *.properties contient la clef associée. Il suffit pour cela d’utiliser le code suivant :
// Création de l'objet :
MyObject myObject = new MyObject();
// Initialisation des champs de l'objet selon les valeurs du fichier *.properties :
PropertyParserResult result = manager.populate(myObject);
// Vérification du traitement :
if (!result.isSuccessful()) {
// Affichage des erreurs :
for (PropertyParserException error : result) {
System.err.println("Erreur : " + error);
}
}
La méthode populate() s’occupe de renseigner les champs de l’objet qu’elle reçoit en paramètre, selon les valeurs des fichiers *.properties chargés précédemment. L’objet retourné nous renseigne sur le résultat de ce traitement en contenant les listes des erreurs survenus. Vous noterez qu’il est possible de l’utiliser dans une boucle for étendu puisque cette classe implémente l’interface Iterable
Pour le moment, seul les types primitifs, les classes Wrappers associées, les enums et les classes String, java.util.Date et java.sql.Date peuvent être utilisés avec cette annotation, mais il est possible de définir son propre Parser et ainsi de gérer ses propres classes (à partir du moment où elles peuvent être représentées par une chaînes de caractères).
Enfin, il est bien entendu possible d’effectuer l’opération inverse, c’est à dire de mettre à jours le fichier *.properties avec les valeurs des champs de votre objet. Il suffit ainsi d’utiliser les méthode parse() et store() pour respectivement récupérer les valeurs depuis l’objet puis sauvegarder le fichier :
// Lecture des valeurs des champs de l'objet :
PropertyParserResult result = manager.parse(myObject);
// Vérification du traitement :
if (!result.isSuccessful()) {
// Affichage des erreurs :
for (PropertyParserException error : result) {
System.err.println("Erreur : " + error);
}
}
// sauvegarde du fichier *.properties :
try {
manager.store("./config.properties");
} catch (IOException e) {
System.err.println("Erreur lors de la l'ecriture du fichier : " + e);
}
Tout ceci utilise l’API de réflection pour « casser » la visibilité des champs de l’objet : il n’y a donc aucun besoin spécifique à respecter mis à part l’utilisation de l’annotation Property (cela permet d’écrire les setteur/getteur selon vos besoins et non pas selon les besoins de cette API).
Cette librairie est disponile sous licence CeCILL (licence compatible avec la GPL) :
- Télécharger la librairie dbd-properties 0.1-alpha.
- Télécharger les sources de dbd-properties 0.1-alpha.
Enfin, notez l’existence de la librairie Args4J (sous licence MIT) qui utilise les mêmes mécanismes pour gérer les paramètres de la ligne de commande…
2 Commentaires + Ajouter un commentaire
Tutoriels
Discussions
- Possibilité d'accéder au type générique en runtime
- jre 1.5, tomcat 6.0 et multi processeurs
- Recuperation du nom des parametres
- [REFLEXION] Connaitre toutes les classes qui implémentent une interface
- Classes, méthodes private
- [ fuite ] memoire
- L'apparition du mot-clé const est-il prévu dans une version à venir du JDK?
- Définition exacte de @Override
- Difference de performances Unix/Windows d'un programme?
>> C’est vraiment la plus grosse modification de tiger, bien plus impactant que les generics.
Je dirais que c’est potentiellement la plus grosse modification, parce que Tiger en lui même ne les utilise quasiment pas…
Mustang devrait changer la donne de ce coté là, en particulier grâce à APT (un outil de Tiger qui permet d’effectuer des traitements sur les annotations AVANT la compilation) qui sera complètement intégré à javac. Cela peut permettre par exemple de générer des erreurs si les annotations d’une librairies sont mal utilisées…
Tres interressant. Plus ca va, et plus je me rends compte que les annotations vont petit a petit tout changer dans notre facon de programmer en java. C’est vraiment la plus grosse modification de tiger, bien plus impactant que les generics.