web-dev-qa-db-fra.com

Injection de dépendances dans les environnements OSGI

Tout d'abord un peu de contexte:

Je travaille sur un code prototype d'application Web basé sur Apache Sling qui est basé sur OSGI et fonctionne sur Apache Felix. Je suis encore relativement nouveau dans OSGI même si je pense avoir compris la plupart des concepts maintenant. Cependant, ce qui me laisse perplexe, c'est que je n'ai pas été en mesure de trouver un framework d'injection de dépendances (DI) "complet". J'ai utilisé avec succès la DI rudimentaire en utilisant les services déclaratifs (DS). Mais je crois comprendre que DS sont utilisés pour référencer - comment mettre cela? - les services et les composants enregistrés OSGI ensemble. Et pour cela, cela fonctionne bien, mais j'utilise personnellement des frameworks DI comme Guice pour câbler des graphiques d'objets entiers ensemble et placer des objets sur les portées correctes (pensez à @RequestScoped ou @SessionScoped par exemple). Cependant, aucun des cadres spécifiques à OSGI que j'ai examinés ne semble soutenir ce concept.

J'ai commencé à lire sur schémas OSGI et iPOJO mais ces frameworks semblent plus concernés par le câblage des services OSGI que par la fourniture d'une solution DI complète. Je dois admettre que je n'ai pas encore fait d'échantillons, donc mon impression pourrait être incorrecte.

Étant une extension de Guice, j'ai expérimenté avec Peaberry , mais j'ai trouvé la documentation très difficile à trouver, et bien que je fasse fonctionner la DI de base, beaucoup de fonctionnalités avancées de guice-servlet (injection automatique dans les filtres , servlets, etc.) ne fonctionnaient pas du tout.

Donc, mes questions sont les suivantes:

  1. Comment les services déclaratifs se comparent-ils aux DI "traditionnels" comme Guice ou Spring? Résolvent-ils le même problème ou sont-ils orientés vers des problèmes différents?
  2. Toutes les solutions spécifiques OSGI que j'ai vues jusqu'à présent n'ont pas le concept de portées pour DI. Par exemple, Guice + guice-servlet a des dépendances étendues à la requête, ce qui rend l'écriture d'applications Web vraiment propre et facile. Est-ce que je viens de manquer cela dans la documentation ou ces préoccupations ne sont-elles couvertes par aucun de ces cadres?
  3. JSR 3 et la DI basée sur OSGI sont-elles deux mondes différents? iPOJO par exemple apporte ses propres annotations et Felix SCR Annotations semblent être un monde entièrement différent.
  4. Quelqu'un a-t-il de l'expérience dans la construction de systèmes basés sur OSGI et de DI? Peut-être même un exemple de code sur github?
  5. Quelqu'un utilise-t-il ensemble différentes technologies comme Guice et iPOJO ou est-ce juste une idée folle?

Désolé pour la question plutôt longue.

Tout commentaire est grandement apprécié.


Mises à jour

Injection scoped: l'injection scoped est un mécanisme utile pour avoir des objets d'un cycle de vie spécifique automatiquement injectés. Pensez, par exemple, qu'une partie de votre code repose sur un objet de session Hibernate qui est créé dans le cadre d'un filtre de servlet. En marquant une dépendance, le conteneur reconstruira automatiquement le graphique d'objet. Peut-être y a-t-il simplement des approches différentes à cela?

JSR 330 vs DS: de toutes vos excellentes réponses, je vois que ce sont deux choses différentes. Cela pose la question, comment gérer les bibliothèques et les frameworks tiers qui utilisent les annotations JSR 330 lorsqu'ils sont utilisés dans un contexte OSGI? Quelle est une bonne approche? Vous exécutez un conteneur JSR 330 dans le Bundle?

J'apprécie toutes vos réponses, vous avez été très utile!

36
ilikeorangutans

Approche générale

Le moyen le plus simple d'avoir une injection de dépendances avec Apache Sling, et celui utilisé dans toute la base de code, est d'utiliser le maven-scr-plugin .

Vous pouvez annoter vos classes Java) puis, au moment de la construction, appeler le plugin SCR, soit en tant que plugin Maven, soit en tant que tâche Ant.

Par exemple, pour enregistrer un servlet, vous pouvez effectuer les opérations suivantes:

@Component // signal that it's OSGI-managed
@Service(Servlet.class) // register as a Servlet service
public class SampleServlet implements Servlet {   
   @Reference SlingRepository repository; // get a reference to the repository    
}

Réponses spécifiques

Comment les services déclaratifs se comparent-ils aux DI "traditionnels" comme Guice ou Spring? Résolvent-ils le même problème ou sont-ils orientés vers des problèmes différents?

Ils résolvent le même problème - l'injection de dépendances. Cependant (voir ci-dessous) ils sont également conçus pour prendre en compte des systèmes dynamiques où des services peuvent apparaître ou disparaître à tout moment.

Toutes les solutions spécifiques OSGI que j'ai vues jusqu'à présent n'ont pas le concept de portées pour DI. Par exemple, Guice + guice-servlet a des dépendances étendues à la requête, ce qui rend l'écriture d'applications Web vraiment propre et facile. Est-ce que je viens de manquer cela dans la documentation ou ces préoccupations ne sont-elles couvertes par aucun de ces cadres?

Je n'ai vu aucune approche dans le monde SCR pour ajouter des services de session ou de demande. Cependant, la SCR est une approche générique et la portée peut être gérée à une couche plus spécifique.

Étant donné que vous utilisez Sling, je pense qu'il y aura peu besoin de liaisons à l'échelle de la session ou de la demande puisque Sling a des objets intégrés pour chaque demande qui sont créés de manière appropriée pour l'utilisateur actuel.

Un bon exemple est la session JCR. Il est automatiquement construit avec les privilèges corrects et il s'agit en pratique d'un DAO à portée de demande. Il en va de même pour le resolveur de ressources Sling.

Si vous avez besoin d'un travail par utilisateur, l'approche la plus simple est d'avoir des services qui reçoivent un JCR Session ou un Sling ResourceResolver et les utiliser pour effectuer le travail dont vous avez besoin. Les résultats seront automatiquement ajustés pour les privilèges de l'utilisateur actuel sans aucun effort supplémentaire.

Les DI basées sur JSR 330 et OSGI sont-elles deux mondes différents? iPOJO par exemple apporte ses propres annotations et les annotations Felix SCR semblent être un monde entièrement différent.

Oui, ils sont différents. Vous devez garder à l'esprit que bien que Spring et Guice soient plus courants, les services OSGi sont plus complexes et prennent en charge plus de cas d'utilisation. Dans OSGi, les offres groupées (et implicitement les services) sont gratuites aller et venir à tout moment.

Cela signifie que lorsque vous avez un composant qui dépend d'un service qui vient de devenir indisponible, votre composant est désactivé. Ou lorsque vous recevez une liste de composants (par exemple, des implémentations de servlet) et que l'un d'entre eux est désactivé, vous en êtes averti. À ma connaissance, ni Spring ni Guice ne le supportent car leurs câblages sont statiques.

C'est une grande flexibilité qu'OSGi vous offre.

Quelqu'un a-t-il de l'expérience dans la construction de systèmes basés sur OSGI et de DI? Peut-être même un exemple de code sur github?

Il y a un grand nombre d'échantillons dans le Sling Samples SVN repository . Vous devriez y trouver la plupart de ce dont vous avez besoin.

Quelqu'un utilise-t-il ensemble différentes technologies comme Guice et iPOJO ou est-ce juste une idée folle?

Si vous avez des frameworks configurés avec des annotations JSR 330, il est logique de les configurer au moment de l'exécution en utilisant Guice ou Spring ou tout ce qui fonctionne pour vous. Cependant, comme Neil Bartlett l'a souligné, cela ne fonctionnera pas entre les bundles.

28
Robert Munteanu

Je voudrais juste ajouter un peu plus d'informations à l'excellente réponse de Robert, en particulier en ce qui concerne JSR330 et DS.

Les services déclaratifs, Blueprint, iPOJO et les autres "modèles de composants" OSGi sont principalement destinés à injecter des services OSGi. Celles-ci sont légèrement plus difficiles à gérer que les dépendances normales, car elles peuvent aller et venir à tout moment, y compris en réponse à des événements externes (par exemple, réseau déconnecté) ou à des actions de l'utilisateur (par exemple, bundle supprimé). Par conséquent, tous ces modèles de composants fournissent une couche supplémentaire cycle de vie sur les infrastructures d'injection de dépendances pures.

C'est la raison principale pour laquelle les annotations DS) sont différentes de celles du JSR330 ... celles du JSR330 ne fournissent pas assez de sémantique pour gérer le cycle de vie. Par exemple, elles ne disent rien sur:

  • Quand la dépendance doit-elle être injectée?
  • Que devons-nous faire lorsque la dépendance n'est pas actuellement disponible (c'est-à-dire qu'elle est facultative ou obligatoire)?
  • Que devons-nous faire lorsqu'un service que nous utilisons disparaît?
  • Pouvons-nous passer dynamiquement d'une instance d'un service à une autre?
  • etc...

Malheureusement, parce que les modèles de composants sont principalement axés sur les services - c'est-à-dire les liens entre bundles - ils sont comparativement spartiates en ce qui concerne le câblage des dépendances à l'intérieur du bundle (bien que Blueprint offre un support pour cela).

Il ne devrait y avoir aucun problème en utilisant un framework DI existant pour le câblage des dépendances à l'intérieur du bundle. Par exemple, j'ai eu un client qui a utilisé Guice pour câbler les pièces internes de certains composants des services déclaratifs. Cependant, j'ai tendance à remettre en question la valeur de cela, car si vous avez besoin de DI dans votre bundle, cela suggère que votre bundle est peut-être trop gros et incohérent.

Notez qu'il est très important de NE PAS utiliser un framework DI traditionnel pour câbler des composants entre bundles. Si le framework DI a besoin d'accéder à une classe à partir d'un autre bundle, cet autre bundle doit exposer ses détails d'implémentation, ce qui rompt l'encapsulation que nous recherchons dans OSGi.

14
Neil Bartlett

J'ai une certaine expérience dans la création d'applications en utilisant Aries Blueprint. Il a quelques fonctionnalités très intéressantes concernant les services OSGi et le support de l'administrateur de configuration.

Si vous recherchez de bons exemples, jetez un œil au code d'Apache Karaf qui utilise un plan pour tout son câblage. Voir http://svn.Apache.org/repos/asf/karaf/

J'ai également des tutoriels pour Blueprint et Apache Karaf sur mon site Web: http://www.liquid-reality.de/display/liquid/Karaf+Tutorials

Dans votre environnement avec le felix intégré, ce sera un peu différent car vous ne disposez pas des fonctionnalités de gestion de Karaf mais vous devez simplement installer les mêmes bundles et cela devrait fonctionner correctement.

3

Je peux recommander Bnd et si vous utilisez Eclipse IDE également Bndtools. Avec cela, vous pouvez éviter de décrire DS en XML et utiliser des annotations à la place. Il y a un annotation spéciale Reference pour DI. Celle-ci a également un filtre où vous ne pouvez référencer qu'un sous-ensemble spécial de services.

2
christian.vogel

Se heurter à un problème d'architecture similaire ici - comme Robert l'a mentionné ci-dessus dans sa réponse:

Si vous avez besoin d'un travail par utilisateur, l'approche la plus simple consiste à avoir des services qui reçoivent une session JCR ou un Sling ResourceResolver et à les utiliser pour effectuer le travail dont vous avez besoin. Les résultats seront automatiquement ajustés pour les privilèges de l'utilisateur actuel sans aucun effort supplémentaire.

En extrapolant à partir de cela (et de ce que je suis en train de coder), une approche consisterait à ajouter @param resourceResolver à n'importe quel @Service afin que vous puissiez transmettre l'objet à portée de demande appropriée à utiliser dans la chaîne d'exécution.

Plus précisément, nous avons une couche XXXXService/XXXXDao, appelée à partir d'équivalents XXXXServlet/XXXXViewHelper/JSP. Donc, gérer tous ces composants via OSGI @Service annotations, nous pouvons facilement câbler la pile entière.

L'inconvénient ici est que vous devez joncher la conception de votre interface avec les paramètres ResourceResolver ou Sessions.

A l'origine, nous avons essayé d'injecter ResourceResolverFactory dans la couche DAO, afin de pouvoir accéder facilement à la session à volonté via l'usine. Cependant, nous interagissons avec la session à plusieurs points de la hiérarchie et plusieurs fois par requête. Cela a abouti à des exceptions fermées par session.

Existe-t-il un moyen d'atteindre cette par-requête ResourceResolver de manière fiable sans avoir à le passer dans chaque méthode de service?

Avec l'injection à la portée de la demande sur les couches de service, vous pouvez à la place simplement passer le ResourceResolver en tant qu'arg du constructeur et utiliser une variable d'instance à la place. Bien sûr, l'inconvénient ici est que vous devez penser au code de service de la portée de la demande par rapport au code de service de la portée du prototype et séparer en conséquence.

Cela semble être un problème courant où vous voulez séparer les préoccupations en code de service/DAO, laissant les interactions JCR dans le DAO, analogue à Hibernate comment pouvez-vous facilement obtenir la demande par requête Session pour effectuer des opérations de repo?

1
radmike

J'utilise osgi et DI pour le présent projet, j'ai choisi le plan Gemini car il s'agit de la deuxième version de SPRING DYNAMIC MODULES , Sur la base de ces informations, je vous suggère de lire Spring Dynamic Modules en action . Ce livre vous aidera à comprendre certaines parties et explique comment construire une architecture et pourquoi est-ce bon :)

1
Sergii Zagriichuk