I. Introduction▲
Dans les systèmes d'exploitation de type Windows NT, un service (ou service Windows) est un programme qui fonctionne en arrière-plan. Il est similaire à un daemon d'Unix. Un service doit se conformer aux règles d'interface et aux protocoles du Service Control Manager, le composant chargé de la gestion des services.
Les services peuvent être configurés pour démarrer lorsque le système d'exploitation est démarré et fonctionner en arrière-plan tant que Windows est en cours d'exécution. En variante, ils peuvent être lancés manuellement par l'utilisateur ou par un événement qui a besoin du service. Les systèmes d'exploitation de type Windows NT incluent de nombreux services. Ceux-ci sont rattachés à trois comptes d'utilisateur : le compte Système, le compte Service réseau et le compte Service local. Parce que les services sont associés à leurs propres comptes utilisateur dédiés, ils peuvent fonctionner sans qu'un utilisateur soit connecté au système d'exploitation. Les services sont souvent associés à des processus hôtes pour les services Windows.
Source : Wikipédia.
Une application réalisée en Java ne peut être lancée directement en tant que service Windows. Nous allons découvrir comment créer un service Windows spécifique pour lancer cette application. Pour ce faire nous ferons appel à une API nommée JavaServiceJavaService.
II. Prérequis▲
L'OS sur lequel doit s'exécuter le programme doit être de type Microsoft Windows en 32 ou 64 bits. Une JREJava RunTime ENvironnement doit être installée.
III. Création du service▲
III-A. Application Java▲
La première étape consiste à créer une petite application Java qui va nous servir de test. Pour réaliser cette application, j'utilise l'IDE NetBeansNetBeans, mais vous pouvez utiliser n'importe quel autre IDE.
L'application créée consiste en une boucle simple qui affichera un message toutes les cinq secondes. Nous rajouterons plus tard quelques modifications afin d'utiliser au maximum toutes les possibilités offertes par l'API.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
/**
* Class Test pour le tutoriel JavaService
*
@author
Tristan Fleury
*/
public
class
Test {
/**
*
@param
args
the command line arguments
*/
public
static
void
main
(
String[] args) throws
Exception {
while
(
true
){
System.out.println
(
"Ceci est un test"
);
Thread.sleep
(
5000
);
}
}
}
Si vous testez ce code dans votre IDE vous obtiendrez ceci :
Compilez votre code pour obtenir votre jar.
III-B. Création du service▲
III-B-1. Structure / Dossier▲
Pour simplifier la mise en place de l'ensemble, créer un dossier « Test » à la racine de votre système :
III-B-2. JavaService▲
Télécharger l'API iciJavaService en fonction de votre Système d'Exploitation (32 ou 64 bit). Décompressez le fichier dans le dossier Test précédemment créé. Ne garder que le fichier nommé « JavaService.exe » :
Copier votre jar dans le même dossier :
Récupérer le chemin d'accès du fichier jvm.dll, il doit se trouver à cet emplacement :
« C:\Program Files\Java\jdk1.8.0_65\jre\bin\server\jvm.dll »
Ouvrez ensuite une console de commande en tant qu'administrateur :
Déplacez-vous dans le dossier Test :
Entrez ensuite cette commande :
JavaService.exe -install "test" "c:\Program Files\Java\jdk1.8.0_65\jre\bin\server\jvm.dll" -Djava.class.path=c:\Test\test.jar -start Test -method main
La commande se décompose comme suit :
JavaService.exe : c'est le programme appelé pour créer le service ;
-install : déclare au programme qu'il s'agit d'une installation ;
« test » : c'est le nom que l'on souhaite donner au service ;
« c:\Program Files\Java\jdk1.8.0_65\jre\bin\server\jvm.dll » : chemin d'accès à la jvm ;
-Djava.class.path=c:\Test\test.jar : emplacement de notre programme java (jar) ;
-start Test : class principale dans laquelle la méthode de lancement se trouve ;
-method main : la méthode appelée lors du lancement du programme.
Vous pouvez ensuite vérifier que le service a bien été créé en ouvrant le gestionnaire de services Windows (service.msc) :
Vous pouvez démarrer le service en cliquant sur la petite flèche verte en haut du gestionnaire de service :
Le service passe alors « En cours d'exécution » :
Bien sûr à ce stade vous ne voyez pas le résultat de cette exécution, le programme est lancé, mais la sortie standard n'est redirigée vers rien. Nous allons voir comment récupérer cette sortie.
La dernière vérification à faire est d'ouvrir le gestionnaire de tâches et de regarder si un processus nommé JavaService tourne bien. Il y a une petite flèche qui permet d'ouvrir le sous-processus, vous devriez avoir « test » :
IV. Paramètres▲
Maintenant que le service est créé, nous allons pouvoir ajouter des paramètres d'exécution (arguments et fichier de sortie) et voir comment obtenir la sortie standard dans un fichier.
IV-A. Sortie standard▲
Ouvrez la base de registreBase de registre (regedit.exe), naviguez vers HKLM/SYSTEM/CurrentControlSet/Services :
Dans la liste des services, vous devriez trouver un service nommé « test » :
Les paramètres de base peuvent être modifiés comme la Description, le DisplayName etc. Ce qui nous intéresse se trouve dans le dossier Parameters, cliquez dessus et vous obtiendrez ceci :
Vous retrouverez tous les paramètres que nous avons définis dans notre commande de création du service (jvm, class, etc.).
Nous allons ajouter un paramètre afin de définir un fichier pour la sortie standard. Faites un clic droit dans la fenêtre de droite puis cliquez sur « Nouveau » puis « Valeur Chaîne » et entrez « System.out File » :
Double-cliquez sur la chaîne créée pour entrer la valeur suivante : c:\Test\test.txt :
Relancez le service, attendez quelques dizaines de secondes, arrêtez le service et ouvrez le fichier test.txt qui s'est créé dans le dossier c:\Test, vous devriez obtenir ceci :
Nous constatons que la sortie standard est bien redirigée vers ce fichier texte. L'intérêt est donc de pouvoir créer un fichier log pour votre application en utilisant simplement la sortie standard (System.out.println()).
IV-B. Arguments▲
Nous avons vu lors de la création du service que nous pouvions définir une class et une méthode de lancement. Si ce paramètre n'est pas renseigné lors de la création du service, l'API cherchera une class Main et une méthode main. Il est donc possible de choisir n'importe quel nom de méthode à lancer. Vous pouvez directement modifier ces valeurs dans la base de registre en changeant les champs Start Class et Start Method :
Il est également possible de passer des arguments à la méthode appelée. Nous allons modifier la valeur binaire nommée « Start Param Count » et lui donner la valeur 2 (le nombre d'arguments que nous allons passer) :
Puis nous allons ajouter deux nouvelles valeurs chaîne, la première nommée « Start Param Number 0 » avec comme valeur Viduc, et la seconde « Start Param Number 1 » avec comme valeur 5 :
Nous allons modifier notre class java pour utiliser ces paramètres :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
/**
* Class Test pour le tutoriel JavaService
*
@author
Tristan Fleury
*/
public
class
Test {
/**
*
@param
args
the command line arguments
*/
public
static
void
main
(
String[] args) throws
Exception {
try
{
System.out.println
(
"Bonjour "
+
args[0
]);
}
catch
(
ArrayIndexOutOfBoundsException e){
System.out.println
(
"Bonjour, je ne vous connais pas"
);
}
try
{
Integer i=
0
;
while
(
i<
Integer.valueOf
(
args[1
])){
System.out.println
(
"Index de l'argument: "
+
i);
i++
;
Thread.sleep
(
5000
);
}
}
catch
(
ArrayIndexOutOfBoundsException e){
System.out.println
(
"aucune valeur de boucle"
);
}
}
}
Si vous testez ce code dans votre IDE en ajoutant les arguments « Viduc » et « 5 » lors de l'exécution vous obtiendrez ceci :
Compilez votre code et remplacez le fichier jar du service par ce nouveau fichier. Supprimez le fichier test.txt puis relancez le service. Attendez une minute puis arrêter le service. Ouvrez le nouveau fichier test.txt, vous devriez obtenir ceci :
On constate que les arguments sont bien pris en compte. Pour ajouter de nouveaux arguments, répéter l'opération en n'oubliant pas de changer la valeur du champ « Start Param Count » (doit correspondre au nombre d'arguments passés).
V. Arrêt propre du service▲
Il est possible d'ajouter une méthode qui sera appelée par le service lors de son arrêt. Si notre application tourne en boucle sur une écoute de port ou de queue par exemple, il peut être souhaitable d'arrêter tous nos processus proprement lors de l'arrêt du service. Si rien n'est prévu, l'arrêt se fera sous la forme d'un Ctrl+C.
Ouvrez la base de registre pour accéder aux paramètres de votre service. Ajoutez deux valeurs chaîne, la première nommée « Stop Class » avec comme valeur « Test » et la seconde « Stop Method » avec comme valeur « stop ».
Ajoutez ensuite une valeur DWORD 32 bits nommée « Stop Param Count » avec comme valeur « 0 » :
Modifiez votre code comme suit :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
/**
* Class Test pour le tutoriel JavaService
*
@author
Tristan Fleury
*/
public
class
Test {
static
boolean
start =
true
;
static
Test test;
/**
*
@param
args
the command line arguments
*/
public
static
void
main
(
String[] args) throws
Exception {
try
{
System.out.println
(
"Bonjour "
+
args[0
]);
}
catch
(
ArrayIndexOutOfBoundsException e){
System.out.println
(
"Bonjour, je ne vous connais pas"
);
}
test =
new
Test
(
);
test.maMethod
(
);
}
public
void
maMethod
(
) throws
Exception{
while
(
start){
System.out.println
(
"Exécution du test"
);
Thread.sleep
(
5000
);
}
}
public
static
void
stop
(
String[] args){
System.out.println
(
"Arrêt du test"
);
test.start =
false
;
}
}
Compilez votre code et copiez votre fichier jar. N'oubliez pas de supprimer le fichier test.txt pour repartir proprement.
Relancez votre service test, attendez quelques secondes et ensuite arrêtez le service. Vous devriez obtenir ceci :
Il est bien sûr possible d'utiliser une autre class pour la méthode d'arrêt. Il est également possible de passer des arguments à la méthode stop de la même façon que pour la méthode de lancement (ajouter une valeur chaîne : « Stop Param Number X » et n'oubliez pas de mettre à jour la valeur de la chaîne « Stop Param Count »).
VI. Suppression du service▲
Pour supprimer le service créé, il vous suffit d'entrer cette commande :
JavaService -uninstall Test
Cette commande est à lancer avec les droits « En tant qu'administrateur », Test est le nom de votre service.
VII. Autres commandes et options▲
D'autres commandes et options sont disponibles lors de la création du service :
Commandes : pour exécuter ces commandes, placez-vous dans le dossier qui contient le programme JavaService avec une console, entrez JavaService.exe suivi de la commande.
-help (ou -?) Liste les commandes disponibles.
-version Affiche la version actuelle du programme JavaService.
-license (ou -licence) Affiche la licence LGPL.
-install servicename Installe le service nommé avec les options de configuration spécifiques.
-queryconfig servicename (ou -query) Affiche les détails de configuration du service installé.
-status servicename Affiche le statut d'exécution du service.
-uninstall servicename Désinstalle le service.
Les options disponibles lors de la création du service sont les suivantes :
- service_name (obligatoire) - Le nom que vous souhaitez utiliser pour le service. C'est sous ce nom que votre service sera visible dans le gestionnaire de service ;
- jvm_library (obligatoire) - L'emplacement du fichier jvm.dll que vous voulez utiliser pour votre machine virtuelle Java. Pour Sun's Java 2 SDK, c'est généralement {JDK_HOME}\jre\bin\classic\jvm.dll ou {JDK_HOME}\jre\bin\hotspot\jvm.dll ;
- jvm_option* (optionnel) - Pour spécifier différents paramètres à passer à la JVM lors de l'instanciation. Ceux-ci peuvent inclure « -Djava.class.path= » pour spécifier un class path ou « -Xmx128m » pour spécifier la taille maximum de mémoire à 128 MB. Tous les paramètres que vous avez besoin d'utiliser lorsque vous lancez la commande java.exe devraient être spécifiés ici. Il n'y a pas de limite au nombre de paramètres pouvant être passés ;
- -start start_class (oligatoire) - Le nom de la classe que vous souhaitez utiliser quand vous lancez le service. Le nom doit être le nom complet ;
- -method start_method (optionnel) - Le nom de la méthode static qui doit être appelée lorsque vous lancez le service. La méthode doit être de type static, doit retourner un type void et doit accepter en argument un tableau de String (String[]). Si ce paramètre n'est pas présent ce sera une méthode main qui sera lancée ;
- -params start_parameter + (optionnel) - tous les paramètres à passer à la méthode appelée lors du lancement du service. Ils seront passés dans un tableau de String ;
- -stop stop_class (optionnel) - Le nom de la class qui doit être appelée lorsque vous arrêtez le service. Le nom doit être le nom complet. S’il n'y a pas de class stop spécifiée, le processus qui lance la machine virtuelle est simplement arrêté ;
- -method stop_method - (optionnel, mais seulement accepté si la class stop a été spécifiée) - Le nom de la méthode static de la class stop_class qui doit être appelée lors de l'arrêt du service. La méthode doit être de type static, doit retourner un type void et doit accepter un tableau d'argument de type String (String[]). Si ce paramètre n'est pas spécifié la méthode main sera prise par défaut ;
- -params stop_parameter+ (optionnel, mais seulement accepté si la class stop a été spécifiée) - Tous les paramètres à passer à la méthode appelée lors de l'arrêt du service. Ils seront passés dans un tableau de String ;
- -out out_log_file (optionnel) - Un fichier dans lequel la sortie System.out sera redirigée. Si ce paramètre n'est pas spécifié, la sortie ne sera pas redirigée ;
- -err err_log_file (optionnel) - Un fichier dans lequel la sortie System. err sera redirigée. Si ce paramètre n'est pas spécifié, la sortie ne sera pas redirigée ;
- -current current_dir (optionnel) - Un dossier à utiliser comme dossier courant. Si ce paramètre est spécifié, tous les path relatifs du service seront basés sur ce dossier ;
- -path extra_path (optionnel) - L'ajout d'un path pour le service. Ce path sera ajouté au path du système après que le service sera lancé. Il peut être utilisé pour spécifier des chemins de librairies additionnelles dont dépendraient des librairies natives ;
- -depends other_services (optionnel) - Un autre service ou plusieurs services séparés par des virgules qui doivent être lancés avant que votre service java soit démarré. Le ou les services seront démarrés automatiquement si besoin quand votre service sera lancé. De la même façon, votre service sera arrêté si un de ces services est arrêté ;
- -auto or -manual - (optionnel) paramètre qui indique si le service doit être lancé de façon automatique ou manuelle ;
- -shutdown seconds (optionnel) - Spécifie le temps pendant lequel le service peut s'arrêter. Si la valeur est dépassée, la jvm sera stoppée ,
- -user user_name (optionnel) - Spécifie l'utilisateur Windows qui sera employé pour lancer le service (le mot de passe sera requis). Les utilisateurs locaux seront spécifiés comme ceci : .\user_name alors que les utilisateurs domaines seront spécifiés comme suit : domaine\user_name ou user_name@domain ;
- -password password (optionnel) - Spécifie le mot de passe associé au compte utilisé avec le paramètre user ;
- -append or -overwrite - (optionnel) paramètre pour indiquer si la sortie standard et la sortie d'erreur doivent écraser ou ajouter les informations aux fichiers existants ;
- -description service_desc - (optionnel) - Paramètre qui permet de spécifier une description du service. Utiliser des guillemets pour les textes qui incluent des espaces. À noter que cette fonctionnalité n'est supportée que depuis Windows NT ou supérieur.
VIII. Conclusion▲
La création d'un service Windows pour faire tourner une application Java avec la possibilité d'interagir avec l'instance créée est un plus dans le monde Windows. Un exemple d'utilisation serait de créer un composant de gestion autonome d'un Active Directory en Java.
IX. Remerciement▲
Merci à toutes les personnes qui ont contribué à la rédaction de cet article, en particulier Mickael Baron pour sa relecture technique et jacques_jean pour sa relecture orthographique.