2010-04-28 3 views
2

Je suis nouveau à OSGi et j'ai rencontré plusieurs exemples de services OSGi.Comment utiliser OSGi getServiceReference() right

Par exemple:

import org.osgi.framework.*; 
import org.osgi.service.log.*; 

public class MyActivator implements BundleActivator { 
    public void start(BundleContext context) throws Exception { 
    ServiceReference logRef = 
     context.getServiceReference(LogService.class.getName()); 
    } 
} 

Ma question est, pourquoi utilisez-vous

getServiceReference(LogService.class.getName()) 

au lieu de

getServiceReference("LogService") 

Si vous utilisez LogService.class.getName() vous devez importer l'interface. Cela signifie également que vous devez importer le package org.osgi.services.log dans votre MANIFEST.MF.

N'est-ce pas complètement contre-productif si vous voulez réduire les dépendances pour pousser l'accouplement? Autant que je sache, un avantage des services est que le consommateur de services n'a pas besoin de connaître l'éditeur de service. Mais si vous devez importer une interface spécifique, vous devez clairement savoir qui la fournit. En utilisant seulement une chaîne comme "LogService" vous ne devriez pas savoir que l'interface est fournie par org.osgi.services.log.LogService.

Qu'est-ce qui me manque ici?

+1

La recommandation quand OSGi est de ne PAS programmer contre OSGi (je sais - c'est un peu zen Utilisez des plans/Spring DM, des services déclaratifs, iPOJO ou Guice/Peaberry – SteveD

Répondre

6

On dirait que vous avez mise en œuvre confus et l'interface

Utilisation de l'interface réelle pour le nom (et l'importation de l'interface, que vous finirez par faire de toute façon) reenforces le contrat d'interface que les services sont conçus autour. Vous ne vous souciez pas de l'implémentation d'un LogService, mais vous vous souciez de l'interface. Chaque LogService devra implémenter la même interface, d'où votre utilisation de l'interface pour obtenir le service. Pour tout ce que vous savez le LogService est vraiment un emballage autour de SLF4J fourni par un autre bundle. Tout ce que vous voyez est l'interface. C'est le couplage libre que vous recherchez. Vous n'avez pas besoin d'expédier l'interface avec chaque implémentation. Laissez l'interface de son propre bundle et ayez plusieurs implémentations de cette interface.

Remarque: ServiceTracker est généralement plus facile à utiliser, essayez-le!

Avantages supplémentaires: L'utilisation de l'interface permet d'éviter que le nom de la classe ne comporte des fautes d'orthographe, des littéraux de chaîne excessifs et facilite le refactoring.

Une fois que vous avez obtenu la ServiceReference, vos prochaines lignes couple sera probablement impliquer ceci:

Object logSvc = content.getService(logRef) 

// What can you do with logSvc now?!? It's an object, mostly useless 

// Cast to the interface ... YES! Now you need to import it! 
LogSerivce logger = (LogService)logSvc; 

logger.log(LogService.LOG_INFO, "Interfaces are a contract between implementation and consumer/user"); 
+0

Merci pour votre réponse détaillée! – Jens

0

Comme mentionné basszero vous devriez envisager d'utiliser ServiceTracker. Il est assez facile à utiliser et supporte également un meilleur schéma de programmation. Vous ne devez jamais supposer qu'un ServiceReference que vous avez reçu dans le passé est toujours valide. Le service indiqué par ServiceReference est peut-être parti. Le ServiceTracker vous avertira automatiquement lorsqu'un service est enregistré ou non enregistré.

1

Vous ne pouvez pas utiliser la valeur "LogService" comme un nom de classe pour obtenir ServiceReference, parce que vous devez utiliser nom de classe complet "org.osgi.services.log.LogService".

Si vous importez package ainsi: org.osgi.services.log;resolution:=optional et que vous utilisez ServiceTracker pour le suivi des services dans la méthode BundleActivator.start() Je suggère d'utiliser "org.osgi.services.log.LogService" au lieu de LogService.class.getName() sur ServiceTracker initializazion. Dans ce cas, vous n'obtiendrez pas NoClassDefFoundError/ClassNotFountException au démarrage du bundle.

3

Si vous utilisez le LogService, vous êtes couplé à celui-ci de toute façon. Si vous écrivez un middleware, vous obtiendrez probablement le nom paramétré via un fichier XML ou via une API. Et oui, "LogService" échouera terriblement, vous devez utiliser le nom complet: "org.osgi.service.log.LogService". La principale raison d'utiliser le modèle LogService.class.getName() est d'obtenir un renommage correct lorsque vous refactorisez votre code et minimisez les fautes d'orthographe. La prochaine API OSGi aura très probablement:

ServiceReference<S> getServiceReference(Class<S> type) 

appels pour augmenter la sécurité de type.

De toute façon, je n'utiliserais jamais ces API de bas niveau à moins de développer un middleware. Si vous dépendez réellement d'une classe concrète, DS est infiniment plus simple, et encore plus lorsque vous l'utilisez avec les annotations bnd (http://enroute.osgi.org/doc/217-ds.html). Si vous développez un middleware, vous obtenez généralement les classes de service sans connaître la classe réelle, via une chaîne ou un objet de classe. L'API OSGi basée sur les chaînes est utilisée dans ces cas car elle nous permet d'être plus paresseux en ne créant pas de chargeur de classe jusqu'au dernier moment. Je pense que la plus grosse erreur que nous avons faite dans OSGi il y a 12 ans est de ne pas inclure les concepts DS dans le noyau ... :-(