Apache Commons CLI
Un article de JTips.
| Auteur : Alexis Hassler |
| Formation(s) sur le sujet : N/A |
|
Développer une application en ligne de commande avec Apache Commons CLI.
|
|
Ou comment gérer les paramètres passés au programme...
Sommaire |
Problématique
Développer une application en ligne de commande en Java ne pose pas de problème en soit. En effet, il suffit de développer une méthode main et de traiter les arguments reçus dans la variable args.
public static void main(String[] args) {
String premiereOption = args[0];
...
}
Dans une application de ce type, les arguments servent à paramétrer le comportement du programme et à injecter des valeurs à utiliser dans le traitements. Le problème, c'est que cette façon de procéder ne permet pas facilement de gérer des passages d'arguments nommés, optionnels ou obligatoire, à la Posix ou à la GNU. Il est bien possible de passer des propriétés, par l'option -D, mais là encore, le coté verbeux peut rebuter plus d'un utilisateur.
On notera que passer des arguments au programme n'est pas la seule façon de procéder. On peut aussi utiliser des fichiers properties, exploiter les possibilités de l'API Preference de Java ou utiliser des variables d'environnement.
Formats d'arguments
Options et données
Tout d'abord, il faut bien distinguer dans le pratiques le passage d'option et le passage de données en argument. Une donnée serait apr exemple le nom d'un fichier, alors que l'option serait par exemple "-f" pour indiquer qu'il faut prendre en compte un fichier. On voit ici que l'option n'est qu'un flag transmis à l'application.
Généralement, la données est passée de façon brute alors que l'option est préfixées, par un tiret ou un double tiret, sous Linux, ou par un slash sous Windows. Les données et options peuvent être associées dans une liste d'arguments, en les juxtaposant ou en les séparant par un signe prédéfini, comme "égal", par exemple.
Dans ce premier exemple, la donnée foo.tar.gz concerne l'option f uniquement parce qu'elle lui succède dans la liste des arguments :
tar -f foo.tar.gz
Dans ce deuxième exemple, le fait que la donnée foo.tar.gz concerne l'option file est indiqué par le signe "=". On notera que le choix de ce signe dépend uniquement d'une convention et qu'il pourrait être replcé par ":", ">", ou tout autre signe.
tar --file=foo.tar.gz
Propriétés Java
Les propriétés Java sont préfixées par les deux caractère "-D" et utilisent le séparateur "=" entre l'option et la valeur. L'option est souvent constituée d'un ensemble de mots séparés par des points ("."). passées au format -Dsewatech.doitfast=true. Ce type d'argument est géré automatique lorsqu'il est passé à la JVM, mais pas quand il est passé au programme.
Avec ce premier format, l'argument peut être lu dans les system properties.
java -Dsewatech.doit=true fr.sewatech.example.CLI
public static void main(String[] args) {
System.out.println(System.getProperty("sewatech.doit"));
}
Par contre, avec ce format-ci, l'argument est passé dans le tableau args et doit être traité manuellement.
java fr.sewatech.example.CLI -Dsewatech.doit=true
public static void main(String[] args) {
System.out.println(args[0]);
}
|
|
Options Posix
Le format d'option Posix est court, sur un caractère, avec un tiret ('-') devant chaque option ou devant un ensemble d'option. Par exemple, pour la commande tar, chaque lettre derrière le tiret correspond à une option :
tar -zxvlf foo.tar.gz
Options GNU
Le format d'option GNU est plus long, mais plus lisible pour un humain. Chaque option est précédée par un double tiret et est écrite sous forme d'un mot ou d'une suite de motes séparés par des tirets simples.
tar --gzip --verbose --extract --check-links --file=foo.tar.gz
Apache Commons CLI
L'utilitaire Commons CLI a été développer pour faciliter le travail du développeur. Il se charge de vérifier la liste des arguments, nous aide à présenter un message d'erreur en cas de problème, puis nous assiste dans la lecture des options et des valeurs associées.
Créer les options
Avant de créer les options, il faut instancier leur conteneur, de type Options.
Options options = new Options();
Puis chaque option peut être créé directement en appelant la méthode addOption, dont il existe trois variantes.
// Ajoute une option -a, sans argument, avec une description
options.addOption("a", false, "Option a");
// Ajoute une option -b, ou --blabla, avec un argument, avec une description
options.addOption("b", "blabla", true, "Option a");
La troisième variante prend un objet Option en paramètre. Celui-ci doit être instancié via l'OptionBuilder. une partie de sa configuration peut se faire avec le builder, le reste de la configuration se fait ultérieurement sur l'option. Cette façon de procéder est plus verbeuse, mais apporte plus de souplesse.
// Création de l'option -o, ou --out-file, avec un argument
Option outFileOption = OptionBuilder.hasArg()
.withLongOpt("out-file")
.withDescription("URL du fichier cible")
.create('o');
// L'option est obligatoire
outFileOption.setRequired(true);
// L'option est ajoutée à la liste
options.addOption(outFileOption);
Analyser les options et arguments
L'analyse des options et arguments transmis se fait par un CommandLineParser. Apache CLI en propose trois implémentations qui se différencient essentiellement par leur traitement des options longues.
- BasicParser ne reconnait que l'espace comme séparateur entre les options et les arguments ; par exemple, --in-file=in.txt, n'est pas reconnu par ce parser.
- GnuParser sait traiter le signe égal pour associer une option longue et un argument (en plus de l'espace).
- PosixParser sait traiter le signe égal pour associer une option longue et un argument (en plus de l'espace). Il sait aussi séparer des options à un caractère accolées, comme -zxvlf pour la commande tar.
Le BasicParser est donc rarement intéressant, sauf à vouloir imposer une rigueur teintée de pauvreté à l'utilisateur. Le PosixParser est généralement le plus souple d'utilisation. Selon la documentation, le GnuParser est adapté au traitement des options de -Dxxx=yyy, mais je n'ai pas (encore ?) trouvé celà convaincant.
La méthode parse renvoie un objet de type CommandLine qui nous permettra de lire toutes les options et tous les arguments.
CommandLineParser parser = new PosixParser(); CommandLine cmd = parser.parse(options, args);
Lecture des options et arguments
Le plus facile reste à faire : lire les options et arguments... Pour cela, on utilise l'objet de type CommandLine produit par le parser, et on lui demande les options, avec le méthode hasOption ou getOptions, puis les valeurs avec la méthode getOptionValue de CommandLine ou getValue de Option.
// Option obligatoire, je ne teste donc pas sa présence
outFile = cmd.getOptionValue('o');
// Option facultative, je teste sa présence
if (cmd.hasOption('a')) {
aValue = cmd.getOptionValue('a');
}
// Option facultative, je passe une valeur par défaut
bValue = cmd.getOptionValue('b', "Valeur par défaut pour b") ;
Erreurs de validation
En cas d'erreur lors de l'analyse des arguments, une ParseException se produit et fournit un message d'erreur qui indique quelles options posent problème.
System.err.println("Parsing failed : " + e.getMessage());
La ParseException a cependant le défaut de ne proposer qu'un message en anglais, sans donner de détail sur les options incriminées. Pour avoir plus de détails, il faut accéder aux sous-classes.
- MissingOptionException est levée lorsqu'il manque une ou plusieurs options obligatoires. La méthode getMissingOptions() permet d'avoir la liste des options manquantes.
- MissingArgumentException est levée lorsqu'il manque un argument à une option. La méthode getOption() permet de connaître l'option qui nécessite un argument.
- UnrecognizedOptionException est levée lorsqu'une option non prévue est passée. La méthode getOption() permet de connaître l'option inconnue.
- AlreadySelectedException est levée lorsque plusieurs options d'un même groupe exclusif sont passées. La méthode getOption() permet de connaître l'option qui a déclenché l'exception et getOptionGroup() permet de connaître le groupe..
Afficher l'aide
La classe HelpFormatter permet d'afficher un texte d'aide qui liste les options et arguments attendus, ainsi que le texte d'accompagnement.
HelpFormatter formatter = new HelpFormatter(); formatter.printHelp( "CLI", options );
Cette aide est affichée, de façon classique, sur les options --help ou --usage, et, éventuellement, quand l'utilisateur fait une erreur de saisie.
Conclusion
Cet exemple a été réalisé avec Apache Commons CLI 1.2. Il montre une façon simple d'exploiter cet utilitaire, qui peut encore être amélioré par l'utilisation d'une configuration externe (fichier XML ou properties).
|




