Doc
(Traduction (un peu aménagée) du tutoriel de Knoplerfich)
Un service OSGi est une instance d’un objet Java enregistré dans un framework OSGi avec un ensemble de propriétés. N’importe quel objet Java peut être enregistré comme un service, mais ils implémentent le plus souvent une interface bien connue.
La lecture du chapitre 4.10 des spécifications R3 d’OSGi est chaudement recommandée, de même que la javadoc sur le BundleContext, qui contient un tas d’infos.
Le client d’un service est toujours un bundle OSGi, c’est à dire un bout de code qui implémente l’interface BundleActivator, ce qui permet de l’exécuter dans le framework d’OSGi.
Chaque bundle peut ou non déclarer des services, et peut aussi (ou non) utiliser les services d’autres bundles. Il n’y a pas de limite au nombre de services déclarés et/ou utilisés, à part les limites liées à la mémoire disponible. Cependant, la publication/déclaration et l’utilisation des services peuvent toutes deux être limitées grâce à la gestion de sécurité de Java.
Déclaration d’un objet de classe Long comme un service :
public class monBundle implements BundleActivator {
[...]
public void start(BundleContext context) {
Long i = new Long(20);
Hashtable props = new Hashtable();
props.put("description", "This an long value");
bc.registerService(Long.class.getName(), i, props);
}
[...]
}
Remarque : a service can also be registered as several interfaces. In this case, the object must implement all of the interfaces. [Nota : moi j’ai pas compris le sens de cette phrase, ou plutôt je comprends les mots, je comprends l’ensemble des mots, mais je vois pas ce que signifie concrètement "être déclaré comme plusieurs interfaces"...]
La factory d’un service OSGi est une classe spéciale ServiceFactory, qui peut créer des instances individuelles d’objets services pour différents bundles.
Parfois, un service a besoin d’être configuré différemment selon le bundle qui l’utilise. Par exemple, le service de log a besoin de pouvoir mémoriser l’id du bundle qui l’utilise, faute de quoi il serait difficile de s’y retrouver.
Une factory de service est déclarée exactement de la même façon qu’un service normal, en utilisant registerService. La seule différence est qu’il y a une "étape d’indirection" avant que le service soit effectivement pris en compte.
Le client qui va utiliser le service n’aura pas besoin, et ne devrait pas, s’occuper de savoir si un objet est généré par une factiry ou par un objet normal.
Déclaration d’une factory de service :
class LongFactory implements ServiceFactory {
public Object getService(Bundle bundle, ServiceRegistration reg) {
// each bundle gets a Long with it's own id
return new Long(bundle.getBundleId());
}
void ungetService(Bundle bundle, ServiceRegistration reg, Object service) {
// nothing needed in this case
}
}
ServiceFactory factory = new LongFactory();
bc.registerService(Long.class.getName(), factory, new Hashtable());
Remarque : le framework dissimulera les objets service générés. C’est pourquoi un objet service au plus peut être généré par bundle client.
Le service est une notion qui permet de définir des outils très divers, dont voici quelques exemples :
Exporter des fonctionnalités d’un bundle à un autre
Importer les fonctionnalités d’autres bundles
Surveiller les évènements en provenance d’autres bundles
Rendre accessibles des outils externes à d’autres bundles
Rendre accessible du code Java tournant sous OSGi à un réseau externe
Configurer des bundles, via le Configuration Manager
Globalement, les services sont la méthode par laquelle les bundles devraient communiquer les uns avec les autres.
On accède toujours aux services par ServiceReferences, qui est le seul à pouvoir atteindre un objet service.
Pour accéder à un service, la procédure suivante doit toujours être utilisée (avec la possibilité de l’encapsuler dans un code) :
- Obtenir un ServiceReference
- Obtenir le service de cet objet, en utilisant BundleContext.getService()
Les ServiceReference sont retrouvés par :
- une utilisation explicite de BundleContext.getService(). Ceci renvoie soit le service de plus grand rang de la classe, soit null
- une utilisation explicite de BundleContext.getServices(), qui renvoie soit la liste de tous les services correspondants, soit null
- le retour d’un appel à ServiceListener
- une utilisation de la classe "utilitaire" ServiceTracker
Dans tous les cas, sauf le premier, le client peut préciser un ensemble de caractéristiques que le service doit avoir. Ces propriétés sont spécifiées par des filtres LDAP. Aucun ou plusieurs services peuvent correspondre à ces critères.
Un filtre typique contient un nom de classe et un jeu de caractéristiques ? L’exemple ci-dessous sélectionne un service HTTP ayant une propriété "port" dont la valeur est 80.
Dès qu’un ServiceReference est disponible, l’objet service peut être accédé grâcé à un cast :
(bc = le BundleContext reçu dans les paramètres de la méthode, sr étant je suppose le ServiceReference qui correspond aux critères)
Ensuite, l’objet service peut être utilisé comme n’importe quel objet Java. En fait, c’est un objet Java totalement normal, ou, pour être plus précis, c’est le même objet que celui qui a été créé par le bundle qui propose le service.
Dès qu’un service n’est plus utile, le client devrait le relacher en utilisant BundleContext.ungetService() :
Tous les services d’un bundle client sont automatiquement relachés quand ce bundle s’arrête. Aucun "nettoyage" des services n’est donc nécessaire dans la méthode BundleActivator.stop().
Un service ne devrait être relaché que lorsqu’il est vraiment devenu inutile. Certains services peuvent conserver un statut pour chacun des bundles qui les utilisent, et relacher le service peut donc réinitialiser ce statut, ce qui peut ou pas être le but. Un exemple est le HttpService, qui va supprimer toutes les servlets et ressources du client quand il est relâché.
[...]
Exemple 1
Démarche
Se placer dans mestests (CD C :\Users\Eric\Documents\mestests) où il y a equinox.jar
Créer un dossier Alerteur
Aller dedans (cd Alerteur)
Créer une classe AlerteurActivator.java, dans le package PAlerteur
La compiler en plaçant le class dans un sous-répertoire PAlerteur
Créer un manifeste (.../Alerteur/toto.mf)
Générer le Jar
Installer dans OSGi
-mestests/Alerteur/AlerteurActivator.java- |
package PAlerteur; |
-mesTests/Alerteur/toto.mf- |
Manifest-Version: 1.0 |
Osgi répond : bundle id is 64.
Test n° 5
Fin du test 5
Exemple 2
Se placer dans mestests (bêtement avec cd C :\Users\Eric\Documents\mestests) où il y a equinox.jar
Créer un dossier Alerteur
Aller dedans (cd Alerteur)
Créer une classe Alerteur.java dans un package PAlerteur
La compiler en plaçant le class dans un sous-répertoire PAlerteur
Créer une classe AlerteurActivator.java, dans le même package
La compiler aussi, toujours en mettant le .class dans PAlerteur
Créer un manifeste (.../Alerteur/toto.mf)
Générer le Jar
Installer dans OSGi
-mestests/Alerteur/Alerteur.java- |
package PAlerteur; |
-mestests/Alerteur/AlerteurActivator.java- |
package PAlerteur; |
-mesTests/Alerteur/toto.mf- |
Manifest-Version: 1.0 |
Osgi répond : bundle id is 65.
Test n° 6
Ceci est un message affiché par Alerteur
Fin du test 6
Exemple 3
Se placer dans mestests (CD C :\Users\Eric\Documents\mestests) où il y a equinox.jar
Créer un dossier Alerteur
Aller dedans (cd Alerteur)
Créer une interface IAlerteur.java dans un package PAlerteur
La compiler en plaçant le class dans un sous-répertoire PAlerteur
Créer une classe Alerteur.java qui implémente IAlerteur, dans un package PAlerteur
La compiler en plaçant le class dans un sous-répertoire PAlerteur
Créer une classe AlerteurActivator.java qui utilise Alerteur, dans le même package
La compiler aussi, toujours en mettant le .class dans PAlerteur
Créer un manifeste (.../Alerteur/toto.mf)
Générer le Jar
Installer dans OSGi
-mestests/Alerteur/IAlerteur.java- |
package PAlerteur; |
-mestests/Alerteur/Alerteur.java- |
package PAlerteur; |
-mestests/Alerteur/AlerteurActivator.java- |
package PAlerteur; |
-mesTests/Alerteur/toto.mf- |
Manifest-Version: 1.0 |
Osgi répond : bundle id is 67.
Test n° 8
Ceci est un message affiché par Alerteur
Fin du test 8
Exemple 4
Se placer dans mestests (CD C :\Users\Eric\Documents\mestests) où il y a equinox.jar
Créer un dossier Alerteur
Aller dedans (cd Alerteur)
Créer une interface IAlerteur.java dans un package PIAlerteur
La compiler en plaçant le class dans un sous-répertoire PIAlerteur
Créer le manifeste du 1er bundle (.../Alerteur/IAlerteur.mf)
Installer le 1er bundle dans OSGi
Créer une classe Alerteur.java, dans un package PAlerteur, et qui implémente IAlerteur
La compiler en plaçant le class dans un sous-répertoire PAlerteur
Créer une classe AlerteurActivator.java qui utilise Alerteur, dans le même package
La compiler aussi, toujours en mettant le .class dans PAlerteur
Créer un manifeste (.../Alerteur/Alerteur.mf)
Générer le Jar
Installer dans OSGi
-mestests/Alerteur/IAlerteur.java- |
package PIAlerteur; |
-mestests/Alerteur/IAlerteur.mf- |
Manifest-Version: 1.0 |
-mestests/Alerteur/Alerteur.java- |
package PAlerteur; |
-mestests/Alerteur/AlerteurActivator.java- |
package PAlerteur; |
- mesTests/Alerteur/Alerteur.mf- |
Manifest-Version: 1.0 |
Osgi répond : bundle id is 72.
Test n° 9
Ceci est un message affiché par Alerteur
Exemple 5
Les opérations sont identiques, on change juste AlerteurActivator.java
-mestests/Alerteur/AlerteurActivator.java- |
package PAlerteur; |
Osgi répond : bundle id is 74.
Exemple n°5
Ceci est un message affiché par Alerteur
id State Bundle
0 Active org.eclipse.osgi_3.3.1.R33x_v20070828
69 RESOLVED IAlerteur_1.0.0
74 ACTIVE Alerteur_1.0.0
{PAlerteur.Alerteur}={category=misc, service.id=21}
Registrered by bundle : file:Alerteur/Alerteur.jar [74]
No bundles using service.
Exemple 6
EN COURS DE CREATION !!!!
Pour l’instant, tout ça c’est des hypothèses !!!
On veut créer un package qui va utiliser les services du bundle Alerteur (package PAlerteur).
Nous allons donc créer simplement un activator qui appelera ce service.
Il va donc instancier la classe Alerteur du package PAlerteur.
Mais pour cela il faudra que cette classe soit accessible dans PAlerteur au sein d’OSGi.
Or, dans Alerteur.mf nous n’avons pas déclaré ce package comme exporté : OSGi ne saura pas résoudre l’import.
Il faut donc commencer par modifier Alerteur.mf pour ajouter PAlerteur comme package exporté.
- mesTests/Alerteur/Alerteur.mf- |
Manifest-Version: 1.0 |
Il reste à regénérer Alerteur.jar puis à le réinstaller sous OSGi. Au lieu de faire uninstall n / install file :... on peut utiliser la commande
où n est l’id du bundle Alerteur (obtenu par la commande ss si besoin). Il reste à faire start n pour redémarrer le service Alerteur.
Pour le bundle de test, on a le code suivant :
-mestests/Alerteur/AlerteurActivator.java- |
package CAlerteur; |
-mestests/Alerteur/CAlerteur.mf- |
Manifest-Version: 1.0 |
On l’installe sous OSGi, on le démarre et... on obtient le message suivant :
Je n’ai jamais réussi à faire fonctionner ServiceTracker sous OSGi/Equinox... J’ai pris modèle sur un tutoriel, mais OSGi me répond quand je démarre mon bundle que la classe ServiceTracker est introuvable, j’ai vérifié, elle est bien dans equinox.jar, avec le bon chemin...
Il y a sans doute une grosse connerie, mais j’ai pas vu quoi !
-mestests/Alerteur/AlerteurActivator.java- |
package CAlerteur; |
Dans les exemples de client de service, on implémente Alerteur et pas IAlerteur, et ça me surprend...
le 3 septembre 2017 par Eric
le 1er septembre 2017 par Bibi
le 24 juin 2017 par Eric
le 29 mai 2017 par roland