septembre
2009
Je continue à parcourir la documentation des premiers builds de Java 7, et je viens de voir que la classe ProcessBuilder
a subit quelques petits ajustements qui s’avèreront surement très utile !
Lorsqu’on tente d’appeler un programme externe depuis une application Java, on se retrouve vite confronté à un problème un peu particulier : les entrée/sorties du processus sont liée par un buffer à l’application Java qui l’a créé, et ces données doivent être traitées par ce dernier sous peine d’inter-blocage, comme par exemple avec le code suivant qui semble pourtant anodin :
new ProcessBuilder("ma-commande", "mon-arg")
.start() // Démarrage du process
.waitFor(); // Attente de la fin du process
En effet, si on ne traite pas ces données le processus pourrait remplir le buffer et se retrouvé bloqué en écriture, pendant que notre application pourrait attendre indéfiniment la fin du processus. Il faut traiter cela manuellement selon plusieurs critères, ce qui peut s’avérer assez lourd ! Bien sûr il y a des solutions à cela, mais ce comportement est troublant car il diffère de ce qu’on à l’habitude de retrouver dans les autres langages, où les entrée/sorties ne sont pas « reliés » de la sorte par défaut…
Java 7 ne vas pas changer le comportement par défaut (car cela casserait toute la compatibilité ascendante), mais permettre d’utiliser un comportement similaire.
En fait on gagne tout un mécanisme de redirection des E/S du process. La classe ProcessBuilder
se voit enrichit de plusieurs méthodes simplifiant la gestion des redirection : redirectInput()
, redirectOutput()
et redirectError()
.
Ces méthodes permettront de gérer trois modes de redirection :
ProcessBuilder builder = ...
/* = 1 = PIPE
* Les entrée/sorties du process sont redirigées vers un buffer de communication.
* C'est le comportement par défaut qui implique que l'application
* devra traiter les données via la méthode Process.get*Stream() correspondante.
*/
builder.redirectInput(ProcessBuilder.Redirect.PIPE);
builder.redirectOutput(ProcessBuilder.Redirect.PIPE);
builder.redirectError(ProcessBuilder.Redirect.PIPE);
/* = 2 = Files
* La entrée/sorties du process sont directement lue/redirigée depuis/vers un fichier.
* La méthode Process.get*Stream() correspondante renverra null,
* et il n'y a donc aucun traitement à faire au sein de l'application Java
*/
builder.redirectInput(ProcessBuilder.Redirect.from(new File("in.txt")));
builder.redirectOutput(ProcessBuilder.Redirect.to(new File("out.txt")));
builder.redirectError(ProcessBuilder.Redirect.to(new File("err.txt")));
// A noter l'existence de la version courte (strictement équivalent) :
builder.redirectInput(new File("in.txt"));
builder.redirectOutput(new File("out.txt"));
builder.redirectError(new File("err.txt"));
// De même qu'il est possible d'ajouter les données de sortie à la fin du fichier :
builder.redirectOutput(ProcessBuilder.Redirect.appendTo(new File("out.txt")));
builder.redirectError(ProcessBuilder.Redirect.appendTo(new File("err.txt")));
/* = 3 = INHERIT
* La entrée/sorties du process sont identique à l'application Java.
* La méthode Process.get*Stream() correspondante renverra null,
* et il n'y a donc aucun traitement à faire au sein de l'application Java
*/
builder.redirectInput(ProcessBuilder.Redirect.INHERIT);
builder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
builder.redirectError(ProcessBuilder.Redirect.INHERIT);
Cette dernière méthode permettant de renvoyer les messages vers la console sans aucun traitement, comme c’est généralement la cas par défaut dans les autres langages.
A noter également l’existence de la méthode inheritIO()
qui permet de définir rapidement le type de redirection INHERIT sur toutes les E/S…
Ainsi désormais le code suivant sera parfaitement correct :
new ProcessBuilder("ma-commande", "mon-arg")
.inheritIO() // E/S hérité du process Java
.start() // Démarrage du process
.waitFor(); // Attente de la fin du process
Tutoriels
Discussions
- Difference de performances Unix/Windows d'un programme?
- Recuperation du nom des parametres
- Classes, méthodes private
- [REFLEXION] Connaitre toutes les classes qui implémentent une interface
- L'apparition du mot-clé const est-il prévu dans une version à venir du JDK?
- Définition exacte de @Override
- jre 1.5, tomcat 6.0 et multi processeurs
- [ fuite ] memoire
- Possibilité d'accéder au type générique en runtime