Présentation de l'exécution native du Wrapper

Présentation

La fonction WrapperManager.exec() est une alternative puissante à Java-Runtime.exec() ou Java-ProcessBuilder. Conçue historiquement pour être plus économe en mémoire que les premières implémentations Java, qui s'appuyaient fortement sur fork() standard, WrapperManager.exec() offre plusieurs configurations de démarrage et vous permet d'affiner le mécanisme système utilisé pour lancer les processus enfants.

Une fonctionnalité clé est sa capacité à lier ou détacher de manière transparente les processus enfants du processus parent. Si le processus Java principal se termine (normalement ou de façon inattendue), le Wrapper nettoie automatiquement tous les processus liés restants qui sont encore en cours d'exécution.

Pour une meilleure cohérence, la fonction WrapperManager.exec() a été implémentée aussi proche que possible de la fonction Runtime.exec() de Java.

Exécution

Démarrage

Cette section commence immédiatement par montrer comment exécuter une commande. (Les détails supplémentaires seront expliqués étape par étape au fil de la lecture de cette page)

WrapperProcess p = WrapperManager.exec("ls -lisa");

La ligne ci-dessus exécute une commande ls avec les paramètres -l, -i, -s et -a, puis assigne un objet WrapperProcess représentant le processus.

Si le chemin complet du fichier binaire (ou du script) n'a pas été fourni, le répertoire de travail courant ainsi que les répertoires du PATH du système seront examinés et le premier résultat trouvé sera exécuté. L'adressage relatif à partir du répertoire de travail courant est également pris en charge.

NOTE

La commande exec accepte la commande soit sous forme d'une seule chaîne String, soit sous forme d'un StringArray. Dans le cas d'un StringArray, chaque paramètre se trouve dans un champ distinct du tableau.

E/S du processus

Le processus enfant créé s'exécute en arrière-plan du système d'exploitation et n'est généralement pas visible, tant qu'il ne s'agit pas d'une application graphique. Pour communiquer avec le processus, la classe WrapperProcess fournit trois méthodes permettant d'accéder à l'entrée du processus ainsi qu'à la sortie et aux messages d'erreur du processus.

Il est recommandé d'encapsuler l'objet StreamObject renvoyé dans un BufferedReader. Pour ouvrir le Reader, veuillez procéder comme décrit.

BufferedReader br = new BufferedReader( new InputStreamReader(p.getWrapperProcessInputStream()));

Cela établit le Reader du InputStream sur lequel l'enfant écrit sa sortie. Pour lire des données depuis le ErrorStream ou écrire des données vers l'enfant, veuillez procéder de manière similaire à cet exemple.

Une fois le Reader/Writer établi, nous pouvons lire/écrire des données de la façon suivante :

String line;
while ((line = br.readLine()) != null)
{
    System.out.println(line);
}
br.close();

NOTE

Veuillez noter qu'en suivant cet exemple, toutes les données seront lues jusqu'à ce que le processus enfant ferme le flux de son côté.

Configuration du processus enfant

Lors de l'exécution d'une commande, WrapperManager offre également la possibilité de configurer le processus enfant. Cela se fait en passant un objet WrapperProcessConfig à la fonction exec(). La section suivante explique les configurations possibles via WrapperProcessConfiguration.

Processus détaché

La méthode setDetached() vous permet de spécifier si le sous-processus sera exécuté de manière « détachée » du Wrapper. Si le processus est marqué comme « détaché », il n'a pas besoin de se terminer lorsque le Wrapper s'arrête. S'il n'est pas marqué, le Wrapper suivra le processus enfant et tentera de le terminer si le processus parent devait se terminer.

NOTE

Par défaut, les processus ne seront pas démarrés de manière détachée de leur processus parent.

Type de démarrage

La méthode setStartType() spécifie un type de démarrage indiquant comment le sous-processus sera démarré par le système d'exploitation.

AVERTISSEMENT

Cette propriété n'a aucun effet sous Windows.

  • FORK_EXEC :

    Il s'agit de la méthode la plus courante sous Linux/UNIX pour créer un processus enfant. Cependant, sur certains systèmes d'exploitation (notamment Solaris), cet appel provoque initialement la duplication de la mémoire du parent pour l'enfant. Sous z/OS, ce type de démarrage n'est pas pris en charge depuis la version initiale. Sur les systèmes HP-UX, le Wrapper basculera automatiquement vers l'utilisation de vfork() plutôt que fork().

  • VFORK_EXEC :

    La fonction vfork() diffère de fork() uniquement lorsque le processus enfant peut partager le code et les données avec le processus parent. Cela accélère considérablement l'activité de clonage, au risque de compromettre l'intégrité du processus parent si vfork() est mal utilisé. Sur certains systèmes, vfork est identique à fork. Sous macOS, ce type de démarrage n'est plus pris en charge à partir de la version 3.7.0 et est résolu en DYNAMIC.

  • POSIX_SPAWN :

    Le processus sera créé (spawn) et n'entraînera aucune duplication de mémoire POSIX_SPAWN API. Ceci n'est disponible que sous Linux, Solaris (10+), AIX, z/OS, MacOS et FreeBSD 8+ (depuis la version 3.5.46).

  • DYNAMIC :

    Le type de démarrage optimal est automatiquement sélectionné en fonction du système d'exploitation sur lequel le Wrapper s'exécute. À partir de la version 3.7.0 du Wrapper, DYNAMIC est résolu en POSIX_SPAWN par défaut. Cependant, si un répertoire de travail personnalisé est configuré sur une plateforme où WrapperProcessConfig.isWorkingDirectorySupported() renvoie false, il bascule automatiquement vers FORK_EXEC afin de garantir que le processus enfant s'exécute dans le répertoire demandé.

NOTE

Par défaut, les processus seront lancés en utilisant le type de démarrage DYNAMIC.

Répertoire de travail

La méthode setWorkingDirectory() spécifie le répertoire de travail du sous-processus, ou « NULL » si le sous-processus doit hériter du répertoire de travail du processus courant.

Avant la version 3.7.0 du Wrapper, il n'était pas possible de définir le répertoire de travail directement lors de l'utilisation de POSIX_SPAWN. Une solution de contournement suggérée consistait à encapsuler la commande à exécuter dans un script et à effectuer un changement de répertoire avant l'exécution de la commande.

#!/bin/sh

chdir $1
shift
$*

À partir de la version 3.7.0 du Wrapper, la méthode setWorkingDirectory() est prise en charge lors de l'utilisation de POSIX_SPAWN sur la plupart des plateformes Unix modernes, notamment Linux (glibc 2.29 ou ultérieur), macOS (10.15+), FreeBSD (13.1+) et Solaris (11.3+). AIX ne prend pas encore en charge cette fonctionnalité ; toutefois, l'implémentation est compatible avec les versions futures et la prise en charge sera automatique si IBM ajoute cette capacité dans une mise à jour ultérieure du système d'exploitation. Il est possible d'utiliser la méthode isWorkingDirectorySupported() à l'exécution pour vérifier la compatibilité de la plateforme avant de tenter de définir un répertoire de travail.

NOTE

Si cette propriété n'est pas définie, le sous-processus héritera du répertoire de travail de son processus parent.

wrapper.child.allowCWDOnSpawn (obsolète à partir de 3.7.0)

La propriété wrapper.child.allowCWDOnSpawn contrôle si le Wrapper tentera de changer le répertoire de travail pour les types de démarrage POSIX_SPAWN et DYNAMIC.

AVERTISSEMENT

Cette propriété a été initialement introduite à titre expérimental et a été dépréciée dans la version 3.7.0 car elle n'est pas sûre pour les applications multi-threads. Parce qu'elle modifie temporairement le répertoire de travail global de la JVM parente, elle peut introduire de graves conditions de concurrence. Cela peut amener des threads concurrents à résoudre incorrectement les chemins de fichiers relatifs et peut perturber le code natif (JNI) tentant de créer des processus.

Sur la plupart des plateformes Unix modernes, cette propriété n'est pas nécessaire, et la méthode alternative setWorkingDirectory() est préférable (si vous utilisez la version 3.7.0 du Wrapper ou une version ultérieure). Pour vérifier si la définition du répertoire de travail est prise en charge sur la plateforme actuelle et éviter les erreurs d'exécution, utilisez la méthode isWorkingDirectorySupported().

Par défaut, cette propriété est définie sur FALSE.

wrapper.child.allowCWDOnSpawn=TRUE

Définition de l'environnement

La méthode setEnvironment() spécifie l'environnement du sous-processus créé.

Le champ est un tableau de chaînes. Chaque élément contient des paramètres de variables d'environnement au format « name=value ».

NOTE

Si cette propriété n'est pas définie, le sous-processus héritera de l'environnement de son processus parent.

Spécification d'un délai d'attente progressif pour la terminaison de l'enfant

Depuis la version 3.5.5 du Wrapper, il est également possible de spécifier un délai d'attente progressif pour chaque processus individuellement. Le Wrapper donnera au processus enfant la possibilité de se terminer de lui-même proprement, alors que par exemple la méthode java.lang.Process.destroy() provoquera toujours un arrêt forcé, ne donnant au processus aucune chance de s'arrêter de lui-même. Avant la version 3.5.5 du Wrapper, le délai d'attente était toujours de 5 secondes ; afin de conserver une fonctionnalité cohérente, la valeur par défaut du délai d'attente est de 5 secondes.

Le délai d'attente peut être spécifié lors de la création du processus enfant avec la classe WrapperProcessConfig.

WrapperProcessConfig wpConfig = new WrapperProcessConfig().setSoftShutdownTimeout(10);

La configuration de l'exemple ci-dessus indiquera au Wrapper d'attendre jusqu'à 10 secondes pour tous les processus enfants créés avec cette configuration. Si les processus enfants ne se terminent pas après 10 secondes, le Wrapper les tuera de force.

Les valeurs suivantes sont possibles pour un délai d'attente :

  • >0 :

    Le nombre de secondes pendant lesquelles le Wrapper attend que le processus enfant se termine proprement avant une terminaison forcée.

  • 0 :

    Le Wrapper forcera instantanément la terminaison du processus enfant.

  • -1 :

    Le Wrapper ne forcera jamais la terminaison du processus enfant, mais attendra indéfiniment que le processus enfant se termine de lui-même.

Création d'un processus enfant pour l'utilisateur actif

Sous Windows, WrapperProcessConfig.setCreateForActiveUser(boolean) peut être utilisé pour spécifier si les processus enfants doivent être lancés dans la session active courante plutôt que dans la session où le service s'exécute (une session dans laquelle l'utilisateur dispose du privilège SE_TCB_NAME auprès du système d'exploitation). Sur les plateformes non Windows ou lors d'un lancement en mode Console, le paramètre sera ignoré silencieusement.

La valeur par défaut est FALSE.