Tutoriel pour développer et déployer un service web SOAP multibundle sous Eclipse et Karaf et export en service OSGI grâce à Blueprint

Création d'un service SOAP et export en service OSGI

Olivier ROZIER

Après notre article traitant de la création d'un service web RESTful multibundle pour Karaf sous Eclipse, nous allons voir comment créer, sous Eclipse, un service web SOAP OSGI multibundle destiné à être déployé sous Karaf.

De la même manière qu'un service web REST est accessible par un client REST, un service web SOAP est accessible par un client SOAP. Dans ce tutoriel, nous mettrons néanmoins en place un proxy qui nous permettra d'interroger le service SOAP à partir d'une servlet en exportant ce service SOAP en tant que service OSGI.

Notre service web SOAP sera donc interrogeable à la fois par un client SOAP classique, mais également par une interface web classique sans se soucier de la technologie CXF. Ceci allégera donc la manière d'accéder à notre service web SOAP. Nous verrons que cette transformation d'un service SOAP en service OSGI, relativement pratique, est rendue possible très facilement par Blueprint.

Comme dans le tutoriel relatif au développement d'un service web RESTful multibundle, nous procéderons à la création d'une distribution de Karaf personnalisée qui contiendra notre service web SOAP. Bien sûr, vous pourrez toujours installer le service web SOAP indépendamment de Karaf en utilisant un fichier archive « Kar » comme nous le présentons ici ou une feature comme nous le présentons ici.

Pour réagir au contenu de ce tutoriel, un espace de dialogue vous est proposé sur le forum Commentez

- Titre : Tutoriel pour développer et déployer un service web SOAP multibundle sous Eclipse et Karaf et export en service OSGI grâce à Blueprint

- Auteur : Olivier ROZIER

- Parution : 10 mai 2017

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2017 Olivier ROZIER. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Rappel

- II-A. OSGI

- II-B. Architecture logicielle adoptée

- III. Configuration logicielle

- IV. Mise en place des dépendances entre bundles

- V. Création des différents « bundle »

- V-A. Création du bundle DAO

- V-B. Création du bundle métier

- V-C. Création du bundle Web

- V-D. Création du bundle de génération de l'archive Kar

- V-E. Création du bundle de génération d'une distribution personnalisée

- VI. Export du service SOAP en service OSGI

- VII. Création d'une interface utilisateur d'interrogation de notre service web SOAP indépendante de CXF

- VIII. Code source de l'exemple

- VIII-A. Création des tables dans la base de données

- VIII-B. Code JAVA de l'exemple

- IX. Conclusion

I - Introduction

La base du service web SOAP que nous vous proposons de réaliser dans ce tutoriel sera identique à celle du service web RESTful que nous avons faite dans notre précédent tutoriel. Elle sera constituée de plusieurs « bundle » déployés sous Karaf. Ces « bundle » seront créés à partir d'un projet Eclipse composé de sous-modules Maven. Chacun de ces sous-modules s'occupera d'une couche applicative.

Ainsi, tout comme dans notre précédent tutoriel, trois « bundle » principaux constitueront notre service web SOAP :

- un bundle constituant la couche d'accès aux données et définissant le modèle (couche DAO) ;

- un bundle constituant la couche métier ;

- un bundle constituant la couche service.

Le développement d'un tel service web sera tout aussi rapide. Seules les bibliothèques changent !

Pour ceux d'entre vous qui ont suivi pas à pas le tutoriel cité ci-dessus, sachez que la dépendance à « jaxrs » est remplacée par la dépendance à « jaxws » dans le « bundle » constituant la couche service. Les seuls changements majeurs se situent donc dans ce « bundle ».

L'ensemble des « bundle » que nous développerons sera géré par Blueprint que nous avons déjà utilisé dans les tutoriels :

- Tutoriel pour développer et déployer un service web RESTful de base sous Eclipse et Karaf ;

- Tutoriel pour développer et déployer un service web RESTful OSGI multibundle sous Eclipse et Karaf;

- Tutoriel pour mettre en place et exécuter les tests unitaires sur un projet multibundle déployé sous Karaf et développé sous Eclipse.

Mais avant d'entrer plus en détail dans le développement de notre service web SOAP, revoyons certains points évoqués dans le tutoriel « Tutoriel pour développer et déployer un service Web RESTFUL OSGI multibundle sous Eclipse et Karaf »

II - Rappel

Avant d'aller de l'avant, rappelons la manière dont fonctionnent les applications OSGI, ce que signifie le concept de « bundle » et comment nous l'utilisons dans la création de notre service web SOAP.

II-A - OSGI

Pour rappel, une application OSGI est un ensemble de « bundle » qui ne sont autres que des composants qu'on peut installer, arrêter, désinstaller, mettre à jour et tout ceci de manière dynamique, autrement dit, à chaud.

L'ensemble de ces composants forme donc des services dont on peut éviter l'arrêt total puisque OSGI gère lui-même le cycle de vie des composants constituant ces services.

Ainsi, la mise à jour d'un « bundle » n'entraîne pas l'arrêt de l'application. De la même manière, il est possible d'utiliser un même « bundle » sous des versions différentes au sein de notre serveur OSGI (voir le paragraphe « Mise à jour d'un bundle » dans le tutoriel « Tutoriel pour développer et déployer un service web RESTful OSGI multibundle sous Eclipse et Karaf ») sans pour autant avoir de conflit entre ces différentes versions.

A fortiori, plusieurs versions d'un même service peuvent cohabiter au sein de notre serveur OSGI.

En outre, il n'est pas nécessaire de redémarrer le serveur OSGI pour qu'une nouvelle version de bundle soit prise en compte, car OSGI est basé sur un annuaire de services qui lui permet de détecter les nouveaux services offerts par chacun des « bundle » qui le composent.

OSGI garantit donc une granularité plus fine des applications. C'est pourquoi nous pouvons facilement décomposer nos applications en couches différentes avec une manipulation relativement aisée de chaque couche applicative. Pour cela, nous pouvons affecter un « bundle » à chaque couche. C'est ce que nous avons appliqué dans notre précédent tutoriel (« Tutoriel pour développer et déployer un service web RESTful OSGI multibundle sous Eclipse et Karaf ») et que nous appliquerons encore dans celui-ci.

II-B - Architecture logicielle adoptée

Avant de vous lancer dans la lecture de ce tutoriel, nous ne saurions trop vous conseiller de comprendre l'architecture de notre futur service web SOAP en lisant le paragraphe suivant https://olivier-rozier.developpez.com/tutoriels/rest/restful-karaf-multibundle/#LII faisant partie de notre tutoriel de création d'un service web RESTFul OSGI multibundle.

Comprenez que la couche service « REST » utilisant l'API JAX-RS sera remplacée, dans notre service web SOAP, par la couche service « SOAP » utilisant l'API JAX-WS.

Il s'agira, en fait de la seule différence en notre service REST et notre service SOAP.

III - Configuration logicielle

Les logiciels utilisés dans ce tutoriel sont les suivants :

- Eclipse Neon ;

- Karaf 4.1.0 ;

- JDK 1.8.

Les frameworks utilisés sont :

- Hibernate ;

- Apache CXF.

IV - Mise en place des dépendances entre bundles

Nous rédigerons ce tutoriel pas à pas de la même manière que ce que nous avons fait pour le tutoriel traitant de la réalisation d'un service web REST.

Ainsi, les lecteurs qui auront lu notre précédent tutoriel pourront lire en diagonale pour parfaire le développement de services web OSGI et ceux qui ne l'auront pas lu pourront comprendre le développement OSGI en créant un service web SOAP.

Dans ce paragraphe, nous allons créer notre projet Maven. Tout au long de ce tutoriel, nous créerons les sous-modules Maven rattachés à ce projet. Chaque sous-module constituera un bundle dans Karaf et possédera un fichier nommé « blueprint.xml » qui permettra au framework Blueprint, installé dans Karaf, de gérer les injections (dépendances) entre bundle.

1. Créez le projet Maven par le menu « File » → « New » → « Project » :

2. Dans la fenêtre qui s'affiche, choisissez « General » puis « Project » :

3. Nommez le projet « garageWS » puis cliquez sur « Finish » :

4. Cliquez droit sur le projet « garageWS » puis choisissez « Configure » → « Convert to Maven Project » :

5. Renseignez la fenêtre qui apparaît de la manière suivante :
Group Id : com.exemple.garageWS
Artefact Id : garageWS
Version : 1.0.0
Packaging : pom

Cliquez sur « Finish ».

Notre nouveau projet ne contient qu'un fichier « pom.xml ». Ce nouveau projet, par l'intermédiaire du fichier « pom.xml », englobera les sous-projets qui contiennent le code. Voici le contenu provisoire de ce fichier :

pom.xml

1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

2. <modelVersion>4.0.0</modelVersion>

3. <groupId>com.exemple.garageWS</groupId>

4. <artifactId>garageWS</artifactId>

5. <version>1.0.0</version>

6. <packaging>pom</packaging>

7. </project>

Ce fichier contiendra également toutes les dépendances du projet et leur version. Ainsi, un changement de version de dépendance dans le projet n'impacterait que ce fichier.

N'oubliez pas de compléter ce fichier avec les dépendances en même temps que vous complétez les fichiers « pom.xml » des autres modules avec leurs dépendances !

V - Création des différents « bundle »

Nous y sommes : il est désormais temps de développer nos « bundle ». Rappelons que les « bundle » peuvent être vus comme des composants qui, assemblés les uns avec les autres, forment des services. Chaque « bundle » peut être installé, arrêté, désinstallé et mis à jour indépendamment des autres et ce dynamiquement.

On a donc un modèle SOA intra JVM !

Chacun de nos « bundle » constituera une couche applicative particulière de notre service web SOAP.

V-A - Création du bundle DAO

Le bundle DAO sera chargé d'accéder aux données stockées en base de données. Dans cette base de données, nous nous contenterons d'interroger une seule table à l'aide de JPA. Pour un exemple de service web utilisant JPA et accédant à plusieurs tables, n'hésitez pas à consulter le tutoriel « Tutoriel pour développer et déployer un service web RESTful OSGI multibundle sous Eclipse et Karaf ».

Le sous-module que nous créerons se nommera « garageWS_modele ». Il sera constitué de deux « packages » :

- com.exemple.garageWS.modele
Ce package contiendra une classe Voiture qui constituera notre entité bean pour JPA. Il s'agira d'un POJO (classe Java qui n'implémente aucune interface particulière ni n'hérite d'aucune classe mère spécifique) mappé vers une table de la base de données grâce à des métadata via l'API JPA ;

- com.exemple.garageWS.modele.dao
Ce package contiendra une interface nommée VoitureDao et une classe nommée VoitureDaoImpl implémentant cette interface.
L'interface VoitureDao définira toutes les méthodes accessibles par les « bundle » qui utiliseront le bundle modèle. Il s'agira de l'interface sous laquelle le service Blueprint sera enregistré dans l'annuaire de services.
La classe VoitureDaoImpl constituera le bean qui fournira le service.

La création du module d'accès aux données se résume aux étapes suivantes :

1. Cliquez droit sur le projet « garageWS » → « New » → « Project ». Dans la fenêtre qui apparaît, saisissez « maven » dans le filtre puis choisissez « Maven module ».

2. Cliquez sur le bouton « Next » ;

3. Dans la fenêtre qui apparaît, remplissez le nom du module (par exemple « garageWS_modele ») et ne cochez pas la case « Create a simple project (skip archetype selection) » afin de pouvoir choisir l'archétype correspondant à Blueprint.

4. Cliquez sur le bouton « Next » ;

5. Dans le champ « Filter » de la fenêtre qui apparaît, saisissez « blueprint » puis, dans la liste qui apparaît après quelques instants, choisissez la ligne correspondant à « karaf-blueprint-archetype » puis cliquez sur le bouton « Next » :

6. Renseignez la fenêtre qui apparaît de cette manière :
Group Id : com.exemple.garageWS
Artefact Id : garageWS_modele
Version : 1.0.0
Package : com.exemple.garageWS.modele

Dans la section « Properties available from archetype », attribuez la valeur « com.exemple.garageWS.modele » suivi d'une frappe sur la touche « Entrée » de votre clavier puis cliquez sur le bouton « Finish » :

Le fichier « garageWS/pom.xml » est alors complété par le nom de notre nouveau module :

En outre, le module nommé « garageWS_modele » a été créé dans l'explorateur de projet :

Dans ce module, le but est de créer une classe « Voiture » dans le package « com.exemple.garageWS.modele » puis de créer un package « com.exemple.garageWS.modele.dao » qui contiendra les classes et les interfaces qui seront implémentées par Blueprint ;

7. Dans le package « com.exemple.garageWS.modele », supprimez l'interface « MyService.java » et la classe « MyServiceImpl.java » puis faites un clic droit sur ce package et choisissez « New » → « Class » :

8. Notre nouvelle classe s'appellera « Voiture ». Remplissez alors la fenêtre qui apparaît de la manière suivante, puis cliquez sur le bouton « Finish » :

9. Dans l'éditeur, ajoutez les propriétés et les méthodes suivantes :

Voiture.java

1. package com.exemple.garageWS.modele;

3. //Packages propres a JAXB

4. import javax.xml.bind.annotation.XmlRootElement;

5. import javax.xml.bind.annotation.XmlAccessorType;

6. import javax.xml.bind.annotation.XmlAccessType;

7. import javax.xml.bind.annotation.XmlType;

9. //Packages propres a JPA

10. import javax.persistence.Entity;

11. import javax.persistence.GeneratedValue;

12. import javax.persistence.GenerationType;

13. import javax.persistence.Id;

14. import javax.persistence.Table;

15. import javax.persistence.Column;

17. /**

18. * La classe Voiture est un objet JAVA contenant des methodes get et set

19. * <p/>

20. * En ajoutant l'annotation @XmlRootElement, nous offrons la possibilite a JAXB de transformer cet objet en document XML et inversement.

21. * <p/>

22. * La representation XML d'une voiture ressemblera a ceci :

23. * <Voiture>

24. * <idVoiture>1</idVoiture>

25. * <marque>RENAULT</marque>

26. * <modele>SCENIC</modele>

27. * <categorie>Monospace</categorie>

28. * <couleur>Gris</couleur>

29. * <prix>20000</prix>

30. * </Voiture>

31. */

32. @XmlRootElement(name = "Voiture")

33. @XmlAccessorType(XmlAccessType.FIELD)

34. @XmlType(propOrder={"idVoiture", "marque", "modele","categorie","couleur", "prix"}) //Fixe l'ordre des champs dans le fichier XML

35. @Entity

36. @Table(name="voiture",schema="garage")

37. public class Voiture

38. {

39. @Id //specifie la cle primaire

40. @Column(name = "id", nullable = false)

41. @GeneratedValue(strategy = GenerationType.IDENTITY)

42. private int idVoiture;

44. @Column(name = "marque", length = 30)

45. private String marque;

47. @Column(name = "modele", length = 30)

48. private String modele;

50. @Column(name = "categorie", length = 30)

51. private String categorie;

53. @Column(name = "colori", length = 30)

54. private String couleur;

56. @Column(name = "prix")

57. private int prix;

60. public long getIdVoiture()

61. {

62. return idVoiture;

63. }

65. public void setIdVoiture(int idVoiture)

66. {

67. this.idVoiture = idVoiture;

68. }

70. public String getMarque()

71. {

72. return marque;

73. }

75. public void setMarque(String marque)

76. {

77. this.marque = marque;

78. }

80. public String getModele()

81. {

82. return modele;

83. }

85. public void setModele(String modele)

86. {

87. this.modele = modele;

88. }

90. public String getCategorie()

91. {

92. return categorie;

93. }

95. public void setCategorie(String categorie)

96. {

97. this.categorie = categorie;

98. }

100. public String getCouleur()

101. {

102. return couleur;

103. }

105. public void setCouleur(String colori)

106. {

107. this.couleur = colori;

108. }

110. public int getPrix()

111. {

112. return prix;

113. }

115. public void setPrix(int prix)

116. {

117. this.prix = prix;

118. }

121. @Override

122. public String toString()

123. {

124. return getClass().getSimpleName() + "[idVoiture=" + idVoiture + ", marque=" + marque + ", modele=" + modele + ", categorie=" + categorie + ", couleur=" + couleur + "]";

125. }

126. }

Dès lors, vous constatez des erreurs de compilation concernant les « packages » propres à JPA. En effet, ces « packages » ne sont pas connus de notre module. Pour qu'ils le soient, il est nécessaire de compléter le fichier « garageWS_modele/pom.xml » en ajoutant la balise <dependencies> qui signale les dépendances Maven à importer au projet ;

10. Complétez le fichier « garageWS_modele/pom.xml » avec la balise <dependencies> et les balises filles associées en indiquant la dépendance Maven associée à JPA et nécessaire à notre projet. Le fichier « garageWS_modele/pom.xml » doit être celui-ci à l'issue de votre complétion :

garageWS_modele/pom.xml

1. <?xml version="1.0" encoding="UTF-8"?>

2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4. <modelVersion>4.0.0</modelVersion>

6. <parent>

7. <artifactId>garageWS</artifactId>

8. <groupId>com.exemple.garageWS</groupId>

9. <version>1.0.0</version>

10. </parent>

12. <groupId>com.exemple.garageWS</groupId>

13. <artifactId>garageWS_modele</artifactId>

14. <version>1.0.0</version>

15. <packaging>bundle</packaging>

17. <name>garageWS_modele Blueprint Bundle</name>

18. <description>garageWS_modele OSGi blueprint bundle project.</description>

20. <dependencies>

22. <!-- JPA -->

23. <dependency>

24. <groupId>org.eclipse.persistence</groupId>

25. <artifactId>javax.persistence</artifactId>

26. </dependency>

28. </dependencies>

30. <build>

31. <plugins>

32. <plugin>

33. <groupId>org.apache.felix</groupId>

34. <artifactId>maven-bundle-plugin</artifactId>

35. <version>3.2.0</version>

36. <extensions>true</extensions>

37. <configuration>

38. <instructions>

39. <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>

40. <Bundle-Version>${project.version}</Bundle-Version>

41. <Export-Package>com.exemple.garageWS.modele*;version=${project.version}</Export-Package>

42. <Import-Package>*</Import-Package>

43. </instructions>

44. </configuration>

45. </plugin>

46. <plugin>

47. <groupId>org.apache.maven.plugins</groupId>

48. <artifactId>maven-compiler-plugin</artifactId>

49. <configuration>

50. <source>1.8</source>

51. <target>1.8</target>

52. <maxmem>256M</maxmem>

53. </configuration>

54. </plugin>

55. </plugins>

56. </build>

58. </project>

Des erreurs de compilation apparaissent alors dans ce fichier XML. En effet, la version de la dépendance Maven concernant JPA n'est pas renseignée.

Nous allons la renseigner dans le fichier « garageWS/pom.xml ». De cette manière, si nous souhaitons changer la version de JPA dans le projet, nous n'aurons qu'à le faire au niveau de ce fichier et non pas dans le fichier « pom.xml » de chaque sous-module.

Le fichier « garageWS/pom.xml » ressemblera donc à ceci :

garageWS/pom.xml

1. <?xml version="1.0" encoding="UTF-8"?>

2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

3. <modelVersion>4.0.0</modelVersion>

5. <groupId>com.exemple.garageWS</groupId>

6. <artifactId>garageWS</artifactId>

7. <version>1.0.0</version>

8. <packaging>pom</packaging>

10. <modules>

11. <module>garageWS_modele</module>

12. </modules>

14. <properties>

15. <jpa.version>2.1.1</jpa.version>

16. <project.build.outputEncoding>UTF-8</project.build.outputEncoding>

17. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

18. </properties>

20. <dependencyManagement>

21. <dependencies>

23. <!-- JPA -->

24. <dependency>

25. <groupId>org.eclipse.persistence</groupId>

26. <artifactId>javax.persistence</artifactId>

27. <version>${jpa.version}</version>

28. <scope>provided</scope> <!-- Le container ou le JDK devra fournir la dependance lors de l'execution -->

29. </dependency>

31. </dependencies>

32. </dependencyManagement>

34. </project>

Peut-être avez-vous remarqué, dans ce fichier « garageWS/pom.xml » que nous avons « variabilisé » la version des bibliothèques dont doit disposer notre application pour fonctionner. Cette « variabilisation » permet de simplifier la mise à jour des versions en cas de besoin puisque toutes les versions sont immédiatement visibles dans la balise<properties>. Nul besoin de parcourir tout le fichier »pom.xml » à la recherche du numéro de version d'une bibliothèque.

Il est désormais temps de créer la classe et l'interface qui seront utilisées par Blueprint pour l'injection des dépendances concernant notre bundle d'accès aux données. Nous créerons donc un package « com.exemple.garageWS.modele.dao » qui contiendra une interface nommée « VoitureDao » et une classe implémentant cette interface et qui sera nommée « VoitureDaoImpl ».

11. Commencez par créer le package « com.exemple.garageWS.modele.dao » par un clic droit sur le répertoire « src/main/jave » du module « garageWS_modele » dans l'explorateur de projet puis choisissez « New » → « Package ». Remplissez la fenêtre qui apparaît comme indiqué ci-dessous :

Cliquez sur le bouton « Finish » ;

12. Cliquez droit sur le nouveau package et choisissez « New » → « Interface » afin de créer l'interface « VoitureDao » :

La fenêtre ci-dessous apparaît. Renseignez la section « Name » avec le nom « VoitureDao » :

13. Dans le même temps, créez la classe « VoitureDaoImpl » par clic droit sur le package « com.exemple.garageWS.modele.dao » et en choisissant « New » → « Class ». Dans la fenêtre qui apparaît, tapez « VoitureDaoImpl » dans la section « Name » comme le présente la fenêtre ci-dessous :

L'architecture du sous-module « garageWS_modele » est la suivante :

14. Complétez l'interface « VoitureDao » de la manière suivante :

VoitureDao.java

1. package com.exemple.garageWS.modele.dao;

3. import java.util.List;

4. import com.exemple.garageWS.modele.Voiture;

6. public interface VoitureDao

7. {

8. public Voiture recupVoiture(final int voitureId);

9. public List<Voiture> recupToutesVoitures();

10. public Voiture ajouterVoiture(String marque, String modele, String categorie, String couleur, String prix);

11. }

Cette interface définit les méthodes disponibles dans le service web. Ces méthodes doivent être implémentées dans la classe « VoitureDaoImpl » ;

15. Complétez l'interface « VoitureDaoImpl » de la manière suivante :

VoitureDaoImpl.java

1. package com.exemple.garageWS.modele.dao;

3. import javax.persistence.EntityManager;

4. import javax.persistence.PersistenceContext;

5. import javax.persistence.criteria.CriteriaBuilder;

6. import javax.persistence.criteria.CriteriaQuery;

7. import javax.persistence.criteria.Root;

8. import javax.persistence.TypedQuery;

10. import java.util.List;

11. import org.osgi.service.log.LogService;

13. import com.exemple.garageWS.modele.Voiture;

16. public class VoitureDaoImpl implements VoitureDao

17. {

18. //@PersistenceContext permet de recuperer l'entityManager. Le unitName est celui fourni dans le fichier persistence.xml (dans la balise <persistence-unit name="garage" transaction-type="JTA">)

19. //

20. @PersistenceContext(name = "garage", unitName = "garage")

21. protected EntityManager entityManager;

23. protected LogService logService;

25. /**

26. * La methode recupVoiture permet de recuperer une voiture a partir de son identifiant

27. */

28. @Override

29. public Voiture recupVoiture(final int voitureId)

30. {

31. logService.log(LogService.LOG_INFO, getClass().getSimpleName() + "[recupVoiture(" + voitureId + ")]");

32. if(entityManager == null)

33. {

34. logService.log(LogService.LOG_INFO, getClass().getSimpleName() + "/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\");

35. logService.log(LogService.LOG_INFO, getClass().getSimpleName() + "/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\EntityManager est null/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\");

36. logService.log(LogService.LOG_INFO, getClass().getSimpleName() + "/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\/!\\");

37. }

38. return entityManager.find(Voiture.class, voitureId);

39. }

42. /**

43. * La methode recupToutesVoitures permet de recuperer toutes les voitures

44. */

45. @Override

46. public List<Voiture> recupToutesVoitures()

47. {

48. logService.log(LogService.LOG_INFO, getClass().getSimpleName() + "[recupToutesVoitures()]");

50. CriteriaBuilder cb = entityManager.getCriteriaBuilder();

51. CriteriaQuery<Voiture> cq = cb.createQuery(Voiture.class);

52. Root<Voiture> rootEntry = cq.from(Voiture.class);

53. CriteriaQuery<Voiture> all = cq.select(rootEntry);

54. TypedQuery<Voiture> allQuery = entityManager.createQuery(all);

55. return allQuery.getResultList();

56. }

59. /**

60. * La methode recupToutesVoitures permet de recuperer toutes les voitures

61. */

62. @Override

63. public Voiture ajouterVoiture(String marque, String modele, String categorie, String couleur, String prix)

64. {

65. logService.log(LogService.LOG_INFO, getClass().getSimpleName() + "[ajouterVoiture(" + marque + "," + modele + "," + categorie + "," + couleur + "," + prix + ")]");

67. Voiture voiture = new Voiture();

68. voiture.setMarque(marque);

69. voiture.setModele(modele);

70. voiture.setCategorie(categorie);

71. voiture.setCouleur(couleur);

72. voiture.setPrix(Integer.parseInt(prix));

74. entityManager.persist(voiture);

75. return voiture;

76. }

79. public void setLogService(final LogService logService)

80. {

81. this.logService = logService;

82. }

83. }

Comme pour la classe « Voiture » tout à l'heure, on constate immédiatement une erreur de compilation concernant la classe « org.osgi.service.log.LogService ». Il nous faut donc inclure une dépendance Maven dans le fichier « garageWS_modele/pom.xml » ;

16. Complétez le fichier « garageWS_modele/pom.xml » de la manière suivante :

garageWS_modele/pom.xml

1. <?xml version="1.0" encoding="UTF-8"?>

2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4. <modelVersion>4.0.0</modelVersion>

6. <parent>

7. <artifactId>garageWS</artifactId>

8. <groupId>com.exemple.garageWS</groupId>

9. <version>1.0.0</version>

10. </parent>

12. <artifactId>garageWS_modele</artifactId>

13. <version>1.0.0</version>

14. <packaging>bundle</packaging>

16. <name>garageWS_modele Blueprint Bundle</name>

17. <description>garageWS_modele OSGi blueprint bundle project.</description>

19. <dependencies>

21. <!-- JPA -->

22. <dependency>

23. <groupId>org.eclipse.persistence</groupId>

24. <artifactId>javax.persistence</artifactId>

25. </dependency>

27. <!-- Log -->

28. <dependency>

29. <groupId>org.osgi</groupId>

30. <artifactId>org.osgi.service.log</artifactId>

31. </dependency>

33. </dependencies>

35. <build>

36. <plugins>

37. <plugin>

38. <groupId>org.apache.felix</groupId>

39. <artifactId>maven-bundle-plugin</artifactId>

40. <version>3.2.0</version>

41. <extensions>true</extensions>

42. <configuration>

43. <instructions>

44. <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>

45. <Bundle-Version>${project.version}</Bundle-Version>

46. <Export-Package>com.exemple.garageWS.modele*;version=${project.version}</Export-Package>

47. <Import-Package>*</Import-Package>

48. </instructions>

49. </configuration>

50. </plugin>

51. <plugin>

52. <groupId>org.apache.maven.plugins</groupId>

53. <artifactId>maven-compiler-plugin</artifactId>

54. <configuration>

55. <source>1.8</source>

56. <target>1.8</target>

57. <maxmem>256M</maxmem>

58. </configuration>

59. </plugin>

60. </plugins>

61. </build>

63. </project>

Un problème de compilation apparaît alors dans le fichier ci-dessus. Pour les mêmes raisons que tout à l'heure, il est donc nécessaire de préciser la version de la dépendance « org.osgi/org.osgi.service.log » dans le fichier garageWS/pom.xml ». Ce fichier doit donc être modifié de la manière suivante :

garageWS/pom.xml

1. <?xml version="1.0" encoding="UTF-8"?>

2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

3. <modelVersion>4.0.0</modelVersion>

5. <groupId>com.exemple.garageWS</groupId>

6. <artifactId>garageWS</artifactId>

7. <version>1.0.0</version>

8. <packaging>pom</packaging>

10. <modules>

11. <module>garageWS_modele</module>

12. </modules>

14. <properties>

15. <jpa.version>2.1.1</jpa.version>

16. <osgi.log.version>1.3.0</osgi.log.version>

17. <project.build.outputEncoding>UTF-8</project.build.outputEncoding>

18. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

19. </properties>

21. <dependencyManagement>

22. <dependencies>

24. <!-- JPA -->

25. <dependency>

26. <groupId>org.eclipse.persistence</groupId>

27. <artifactId>javax.persistence</artifactId>

28. <version>${jpa.version}</version>

29. <scope>provided</scope> <!-- Le container ou le JDK devra fournir la dependance lors de l'execution -->

30. </dependency>

32. <dependency>

33. <groupId>org.osgi</groupId>

34. <artifactId>org.osgi.service.log</artifactId>

35. <version>${osgi.log.version}</version>

36. <scope>provided</scope> <!-- Le container ou le JDK devra fournir la dependance lors de l'execution -->

37. </dependency>

39. </dependencies>

40. </dependencyManagement>

42. </project>

Dans la classe « VoitureDaoImpl », nous avons déclaré une propriété de type EntityManager. Il faut savoir que le bundle du container JPA implémente la spécification du service JPA OSGI qui trace les « bundle » d'unité de persistance et crée un service « EntityManagerFactory » dès que toutes les dépendances sont rencontrées.

Pour chaque unité de persistance JPA (balise <persistence-unit> dans le fichier « persistence.xml »), le container détermine d'abord quel fournisseur de persistance utiliser en analysant la propriété « provider » du fichier « persistence.xml » associé au bundle. Il tracera ensuite un service «PersistenceProvider » qui correspond au nom indiqué. Si aucune propriété n'est définie, alors le premier fournisseur de persistance (« PersistenceProvider ») trouvé sera utilisé.

Une unité de persistance définit l'ensemble des classes qui sont liées ou groupées par l'application et qui doivent être liées à un seul et même magasin de données.

Dès que le fournisseur de persistance et la source de données sont disponibles, le service « EntityManagerFactory » est créé. Ce service fournit des instances d'EntityManager.

Toutes ces instances sont configurées pour se connecter à la même base de données et pour utiliser les mêmes paramètres. L'API EntityManagerest utilisée pour accéder à une base de données dans une unité de travail particulière. Elle est utilisée pour créer et supprimer des instances d'entité persistantes, pour trouver des entités par leur clé primaire et pour interroger toutes les entités.

Blueprint gère lui-même les EntityManagerFactory et l'injection des EntityManager dans les bean via l'annotation @PersistenceContext.

17. Forts de l'explication ci-dessus, nous allons créer le fichier « persistence.xml » en cliquant droit sur le répertoire « src/main/resources » puis en choisissant « New » → « Other » puis, dans « General », choisir « Folder » :

Nous nommerons ce nouveau répertoire « META-INF » comme le présente la capture d'écran ci-dessous :

18. Faites un clic droit sur le nouveau répertoire « META-INF » puis choisissez « New » → « File » et nommez le fichier « persistence.xml » comme le présente l'écran ci-dessous :

19. Nous choisirons d'utiliser Hibernate en tant que fournisseur de persistance. Pour cela, nous écrirons la valeur « org.hibernate.jpa.HibernatePersistenceProvider » dans la balise <provider>.
Nous utiliserons JTA en tant que type de transaction de l'unité de persistance. On déclarera donc un nom JNDI pour la source de données JTA qui sera utilisée pour obtenir des connexions.
Nous pouvons donc remplir le fichier « garageWS_modele/persistence.xml » de la manière suivante :

garageWS_modele/persistence.xml

1. <?xml version="1.0" encoding="UTF-8"?>

2. <persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"

3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence

5. http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"

6. version="2.1">

9. <!-- ******************************************************************* -->

10. <!-- JTA (Java Transaction API). Elle fournit des interfaces Java standards entre un gestionnaire de transactions et les differentes parties

11. impliquees dans un systeme de transactions distribuees : le gestionnaire de ressources, le serveur d'application et les applications transactionnelles.

12. JTA est un protocole de commit a deux phases :

13. - 1re phase : chaque partie prenant part a la transaction distribuee s'engage a verrouiller les donnees concernees et à valider ces donnees une fois la transaction terminee

14. - 2e phase : chaque partie valide les changements des données. Cette phase est obligatoire, des lors que les parties se sont engagees.

16. Ce protocole de commit a deux phases fonctionne plutot bien sur les transactions courtes, mais est totalement inefficace en cas de transaction lente

17. où le risque d'une deconnexion ou bien d'un crash entre les deux phases est eleve, car les verrous restent poses apres la premiere phase et ne sont liberes

18. qu'apres la deuxieme phase.                                              -->

19. <!-- ******************************************************************* -->

21. <persistence-unit name="garage" transaction-type="JTA">

22. <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

24. <!-- On specifie le nom global JNDI de la datasource qui sera utilisee par le container.

25. Ce nom est la valeur de la propriété "osgi.jndi.service.name" fournie par la commande Karaf "service:list DataSource"-->

26. <jta-data-source>osgi:service/javax.sql.DataSource/(osgi.jndi.service.name=garage)</jta-data-source>

28. <!-- On precise l'ensemble des classes qui sont gerees par l'entityManager dans l'application -->

29. <class>com.exemple.garageWS.modele.Voiture</class>

31. <!-- On exclut les classes qui ne sont pas listees ci-dessus -->

32. <exclude-unlisted-classes>true</exclude-unlisted-classes>

34. <properties>

36. <!-- On precise le schema par defaut. On peut aussi le faire via l'attribut @Table(name="voiture",schema="garage") dans la classe entite -->

37. <property name="hibernate.default_schema" value="garage"/>

39. <!-- La propriete hibernate.dialect definit le dialect SQL de notre base de donnees.Hibernate s'appuiera sur ce dialect

40. pour optimiser l'execution des requetes en utilisant les proprietes specifiques a la base de donnees.

41. Dans notre cas, on utilisera le dialect de PostgreSql-->

42. <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>

44. <!-- La propriete  hibernate.hbm2ddl.auto=none specifie que le schema de la table n'est pas automatiquement cree quand l'application est deployee.

45. Si la propriete a la valeur "create-drop", les tables de la base de donnees sont creees au deploiement de l'application et supprimees lors de l'undeployed ou de l'arret du serveur.

46. Si la propriete a la valeur "update", le schema de la base de donnees est mis a jour ou cree, mais son contenu n'est pas supprime.

47. En production, un utilisateur n'a que tres rarement le privilege de creer ou supprimer des tables.-->

48. <property name="hibernate.hbm2ddl.auto" value="none"/>

50. <!-- On log les requetes executees par hibernate pour faciliter le debogage -->

51. <property name="hibernate.show_sql" value="true"/>

53. <!-- On met des commentaires dans toutes les requetes SQL generees pour faciliter la comprehension des requetes-->

54. <property name="use_sql_comments" value="true"/>

56. </properties>

57. </persistence-unit>

58. </persistence>

20. Dans ce fichier « persistence.xml » nous venons de déclarer une « datasource » (une source de données) que nous avons appelée « garage ». Il ne faut donc pas oublier de créer cette source de données. La création d'une source de données se fait extrêmement rapidement dans Karaf par l'utilisation de « pax-jdbc » qui n'est autre qu'une implémentation de service JDBC OSGI.
Dans le paragraphe « IV.D Création du bundle de génération de l'archive Kar », nous utiliserons la version 0.9.0 de « pax-jdbc » qui contient les pilotes nécessaires à notre base de données PostgreSQL. La création d'une source de données avec « pax-jdbc » se limite à la création d'un fichier dont le nom est de la forme « org.ops4j.datasource-NOM_DE_LA_DATASOURCE.cfg » et qui est placé dans le répertoire « etc » de Karaf.
Dans le paragraphe « IV.E Création du bundle de génération d'une distribution personnalisée », nous verrons que nous n'aurons pas à nous préoccuper de l'emplacement de notre fichier.
Pour l'instant, créez le fichier « org.ops4j.datasource-garage.cfg », « garage » étant le nom de notre source de données. Le contenu de ce fichier est le suivant (bien sûr, vous adapterez son contenu avec les renseignements permettant l'accès à votre base de données) :

org.ops4j.datasource-garage.cfg

1. osgi.jdbc.driver.name=PostgreSQL JDBC Driver-pool

2. serverName=999.999.999.999

3. databaseName=garage

4. portNumber=4002

5. user=olivier

6. password=XXXXXXX

7. dataSourceName=garage

8. pool.maxTotal=10000

21. Il nous faut renseigner Blueprint sur la configuration de notre bundle. Pour cela, on utilise le fichier « my-service.xml ». Dans un souci de clarté, renommez ce fichier, disponible dans le répertoire « src/main/resources/OSGI-INF/blueprint », en « blueprint.xml » comme le présente la capture d'écran ci-dessous :

22. Le fichier « blueprint.xml » a déjà été rempli en partie par Eclipse. Remplacez son contenu par le contenu ci-dessous afin de décrire la liaison qu'il y a entre les différents composants de notre application :

garageWS_modele/blueprint.xml

1. <?xml version="1.0" encoding="UTF-8"?>

2. <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"

3. xmlns:jpa="http://aries.apache.org/xmlns/jpa/v2.0.0"

4. xmlns:tx="http://aries.apache.org/xmlns/transactions/v1.2.0"

5. default-activation="lazy">

7. <!-- On active JPA -->

8. <jpa:enable />

10. <!-- Definition de l'enregistrement du service dans le registre de services OSGI. On renseigne le bean qui fournit le service via l'attribut ref -->

11. <service ref="voitureDao" interface="com.exemple.garageWS.modele.dao.VoitureDao">

12. <service-properties>

13. <entry key="service.exported.interfaces" value="*" />

14. </service-properties>

15. </service>

17. <!-- Les runtimes d'applications OSGI traitent les services distants differemment de ceux en local a cause de la semantique d'invocation par defaut qui est differente

18. (passage par reference local versus passage par valeur distant). Pour eviter qu'une application appelle accidentellement un service exporte qui est uniquement designe

19. par un appel par "passage de valeur", il faut cacher le lookup local.

20. La solution est d'exporter le meme bean deux fois : une fois pour les appels distants et une autre pour les appels locaux.

21. En d'autres termes, on ajoute un autre element avec la meme configuration mais sans la propriete "service.exported.interfaces"-->

22. <service ref="voitureDao" interface="com.exemple.garageWS.modele.dao.VoitureDao"/>

24. <!-- Publication du bean en tant que service OSGI-->

25. <!-- Ce bean possede des proprietes a injecter. L'injection de ces proprietes (ici la propriete logService) est faite

26. immediatement apres la creation du bean  -->

27. <bean id="voitureDao" class="com.exemple.garageWS.modele.dao.VoitureDaoImpl">

28. <property name="logService" ref="logService" />

30. <!-- Etalissement des transactions pour toutes les methodes DAO. L'application n'a pas besoin de creer l'entity manager que nous avons declare.

31. La OSGI runtime creera cet entity manager et l'injectera partout ou il est requis. La balise "<tx:transaction>" permet de demander au runtime

32. d'envelopper les methodes dans une transaction.-->

33. <tx:transaction method="*"/>

34. </bean>

36. <!-- Definition des dependances -->

37. <reference id="logService" interface="org.osgi.service.log.LogService" />

39. </blueprint>

23. Afin que JPA crée l'EntityManagerFactory, il vous faut renseigner une balise <Meta-Persistence> dans le fichier « garageWS_modele/pom.xml ». Cette balise permet d'indiquer l'emplacement des fichiers « persistence.xml ». Il est également nécessaire de préciser le nom de l'unité de persistance dans une balise <JPA-PersistenceUnits>.
Le contenu du fichier « garageWS_modele/pom.xml » sera donc celui-ci :

garageWS_modele/pom.xml

1. <?xml version="1.0" encoding="UTF-8"?>

2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4. <modelVersion>4.0.0</modelVersion>

6. <parent>

7. <artifactId>garageWS</artifactId>

8. <groupId>com.exemple.garageWS</groupId>

9. <version>1.0.0</version>

10. </parent>

12. <artifactId>garageWS_modele</artifactId>

13. <version>1.0.0</version>

14. <packaging>bundle</packaging>

16. <name>garageWS_modele Blueprint Bundle</name>

17. <description>garageWS_modele OSGi blueprint bundle project.</description>

19. <dependencies>

21. <!-- JPA -->

22. <dependency>

23. <groupId>org.eclipse.persistence</groupId>

24. <artifactId>javax.persistence</artifactId>

25. </dependency>

27. <!-- Log -->

28. <dependency>

29. <groupId>org.osgi</groupId>

30. <artifactId>org.osgi.service.log</artifactId>

31. </dependency>

33. </dependencies>

35. <build>

36. <plugins>

37. <plugin>

38. <groupId>org.apache.felix</groupId>

39. <artifactId>maven-bundle-plugin</artifactId>

40. <version>3.2.0</version>

41. <extensions>true</extensions>

42. <configuration>

43. <instructions>

44. <JPA-PersistenceUnits>garage</JPA-PersistenceUnits>

46. <!-- La balise <Meta-Persistence> pointe sur le fichier "persistence.xml" afin que JPA cree une EntityManagerFactory

47. pour ce bundle-->

48. <Meta-Persistence>META-INF/persistence.xml</Meta-Persistence>

50. <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>

51. <Bundle-Version>${project.version}</Bundle-Version>

52. <Export-Package>com.exemple.garageWS.modele*;version=${project.version}</Export-Package>

53. <Import-Package>

54. <!-- Necessaire pour le proxy de Javassist sous peine d'avoir le message "java.lang.RuntimeException: by java.lang.NoClassDefFoundError: org/hibernate/proxy/HibernateProxy" -->

55. org.hibernate.proxy,javassist.util.proxy,*

56. </Import-Package>

57. </instructions>

58. </configuration>

59. </plugin>

60. <plugin>

61. <groupId>org.apache.maven.plugins</groupId>

62. <artifactId>maven-compiler-plugin</artifactId>

63. <configuration>

64. <source>1.8</source>

65. <target>1.8</target>

66. <maxmem>256M</maxmem>

67. </configuration>

68. </plugin>

69. </plugins>

70. </build>

72. </project>

Remarquez la balise <Import-Package> que nous avons complétée. Il est effectivement nécessaire d'importer les classes proxy nécessaires à la bonne exécution de notre service web.

Notre sous-module « garageWS_modele » est désormais terminé. Attaquons-nous maintenant au sous-module métier nommé « garageWS_metier » qui sera chargé de gérer la couche métier de notre application en s'appuyant sur le sous-module que nous venons de réaliser.

V-B - Création du bundle métier

Le bundle métier jouera le rôle d'intermédiaire entre la couche DAO et la couche service.

Tout comme le module DAO que nous venons de faire, il comportera une interface et une classe implémentant cette interface qui seront utilisées par Blueprint. Un fichier « blueprint.xml » permettra d'injecter notre service DAO précédemment réalisé dans cette classe métier par l'intermédiaire d'une propriété.

Le sous-module que nous créerons se nommera « garageWS_metier ». Il sera constitué d'un « package » nommé com.exemple.garageWS.metierqui contiendra une interface nommée VoitureMetier et une classe nommée VoitureMetierImpl implémentant cette interface.

L'interface VoitureMetier définira toutes les méthodes accessibles par les « bundle » qui utiliseront le « bundle » métier. Il s'agira de l'interface sous laquelle le service Blueprint sera enregistré dans l'annuaire de services.

La classe VoitureMetierImpl constituera le bean qui fournira le service et fera appel au bundle « garageWS_modele » par l'intermédiaire d'une de ses propriétés qui ne sera autre qu'une instance de la classe VoitureDao.

Voyons comment créer ce sous-module métier.

1. Cliquez droit sur le projet « garageWS » puis choisissez « New » → « Other » comme le présente l'image ci-dessous :

2. Dans la zone de saisie, tapez « maven » puis, dans les lignes qui apparaissent, double-cliquez sur « Maven Module » :

3. La fenêtre ci-dessous apparaît. Remplissez-la en nommant notre nouveau module « garageWS_metier » et en gardant décochée la case « Create a simple project (skip archetype selection » afin de pouvoir choisir l'archetype correspondant à Blueprint :

4. Dans la fenêtre qui apparaît, saisissez « blueprint » dans le champ « Filter » puis sélectionnez la ligne dont l'artefact Id est « karaf-blueprint-archetype » avant d'appuyer sur le bouton « Next » :

5. Renseignez la fenêtre qui apparaît de cette manière :
Group Id : com.exemple.garageWS
Artefact Id : garageWS_metier
Version : 1.0.0
Package : com.exemple.garageWS.metier

Dans la section « Properties available from archetype », attribuez la valeur « com.exemple.garageWS.modele » suivi d'une frappe sur la touche « Entrée » de votre clavier, puis cliquez sur le bouton « Finish » :

6. Le nouveau sous-module doit apparaître dans l'explorateur de projet.

Vous pouvez constater qu'une interface nommée « MyService.java » et une classe nommée « MyServiceImpl.java » ont été créées automatiquement. Nous nous empresserons, dans un souci de clarté, de renommer ces entités respectivement en « VoitureMetier.java » et « VoitureMetierImpl.java ». La classe « VoitureMetierImpl » sera l'implémentation de l'interface « VoitureMetier ». Le renommage des fichiers se fera par un clic droit dessus puis, dans le menu qui apparaît, en choisissant « Refactor » puis « Rename ».

Ce procédé changera également le nom de l'interface et de la classe à l'intérieur des fichiers.

Dans l'interface « VoitureMetier », il sera nécessaire de créer des méthodes qui nous permettront de tirer profit des méthodes que nous avons écrites dans l'interface « VoitureDao » du module « garageWS_modele ». Veuillez donc copier-coller le code source de l'interface « VoitureMetier » ci-dessous dans le fichier « VoitureMetier.java » :

garageWS_metier/VoitureMetier.java

1. package com.exemple.garageWS.metier;

3. import java.util.List;

4. import com.exemple.garageWS.modele.Voiture;

6. public interface VoitureMetier

7. {

8. public Voiture getVoitureById(int voitureId);

9. public List<Voiture> getAllVoitureRecords();

10. public Voiture addVoiture(String marque, String modele, String categorie, String couleur, String prix);

11. }

Vous constatez une erreur de compilation dans Eclipse concernant l'utilisation de la classe « Voiture » dans notre interface. Comme vous le savez désormais, il est nécessaire de préciser la dépendance de notre sous-module métier à notre sous-module DAO dans le fichier « garageWS_metier/pom.xml » ;

7. Complétez le fichier « garageWS_metier/pom.xml » de la manière suivante :

garageWS_metier/pom.xml

1. <?xml version="1.0" encoding="UTF-8"?>

2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4. <modelVersion>4.0.0</modelVersion>

6. <parent>

7. <artifactId>garageWS</artifactId>

8. <groupId>com.exemple.garageWS</groupId>

9. <version>1.0.0</version>

10. </parent>

12. <artifactId>garageWS_metier</artifactId>

13. <version>1.0.0</version>

14. <packaging>bundle</packaging>

16. <name>garageWS_metier Blueprint Bundle</name>

17. <description>garageWS_metier OSGi blueprint bundle project.</description>

19. <dependencies>

20. <dependency>

21. <groupId>${project.groupId}</groupId>

22. <artifactId>garageWS_modele</artifactId>

23. <version>[1.0.0,2.0)</version>

24. <type>bundle</type>

25. </dependency>

26. </dependencies>

29. <build>

30. <plugins>

31. <plugin>

32. <groupId>org.apache.felix</groupId>

33. <artifactId>maven-bundle-plugin</artifactId>

34. <version>3.2.0</version>

35. <extensions>true</extensions>

36. <configuration>

37. <instructions>

38. <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>

39. <Bundle-Version>${project.version}</Bundle-Version>

40. <Export-Package>com.exemple.garageWS.metier*;version=${project.version}</Export-Package>

41. <Import-Package>*</Import-Package>

42. </instructions>

43. </configuration>

44. </plugin>

45. <plugin>

46. <groupId>org.apache.maven.plugins</groupId>

47. <artifactId>maven-compiler-plugin</artifactId>

48. <configuration>

49. <source>1.8</source>

50. <target>1.8</target>

51. <maxmem>256M</maxmem>

52. </configuration>

53. </plugin>

54. </plugins>

55. </build>

57. </project>

Faut-il encore vous faire remarquer que nous nous sommes contentés d'ajouter la balise <dependencies>ainsi que ses balises filles ?

8. Il est temps désormais de vous occuper de la classe implémentant notre interface « VoitureMetier ». Dans cette classe, un objet implémentant l'interface « VoitureDao » devra absolument faire partie des propriétés. Blueprint injectera notre service DAO dans cette propriété.

Copiez-collez le code de la classe « VoitureMetierImpl » ci-dessous dans le fichier « VoitureMetierImpl.java » :

VoitureMetierImpl.java

1. package com.exemple.garageWS.metier;

3. import java.util.List;

4. import org.slf4j.Logger;

5. import org.slf4j.LoggerFactory;

7. import com.exemple.garageWS.modele.Voiture;

8. import com.exemple.garageWS.modele.dao.VoitureDao;

10. public class VoitureMetierImpl implements VoitureMetier

11. {

12. private VoitureDao voitureDao;

14. //Recuperation du logger de la classe VoitureDaoImpl

15. private static final Logger LOG = LoggerFactory.getLogger(VoitureDao.class);

17. public Voiture getVoitureById(int voitureId)

18. {

19. LOG.info(getClass().getSimpleName() + "[getVoitureById({})]", voitureId);

20. return voitureDao.recupVoiture(voitureId);

21. }

23. public List<Voiture> getAllVoitureRecords()

24. {

25. LOG.info(getClass().getSimpleName() + "[getAllVoitureRecords()]");

26. return voitureDao.recupToutesVoitures();

27. }

29. public Voiture addVoiture(String marque, String modele, String categorie, String couleur, String prix)

30. {

31. LOG.info(getClass().getSimpleName() + "[addVoiture(" + marque + "," + modele + "," + categorie + "," + couleur + "," + prix +")]");

32. return voitureDao.ajouterVoiture(marque, modele, categorie, couleur, prix);

33. }

35. public void setVoitureDao(final VoitureDao voitureDao)

36. {

37. this.voitureDao = voitureDao;

38. }

39. }

Comme pour l'interface précédente, il nous faut compléter le fichier « garageWS_metier/pom.xml » afin de préciser la dépendance de notre classe avec la bibliothèque « slf4j ». Voici donc le contenu de notre fichier « garageWS_metier/pom.xml » après l'ajout de cette dépendance :

garageWS_metier/pom.xml

1. <?xml version="1.0" encoding="UTF-8"?>

2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4. <modelVersion>4.0.0</modelVersion>

6. <parent>

7. <artifactId>garageWS</artifactId>

8. <groupId>com.exemple.garageWS</groupId>

9. <version>1.0.0</version>

10. </parent>

12. <artifactId>garageWS_metier</artifactId>

13. <version>1.0.0</version>

14. <packaging>bundle</packaging>

16. <name>garageWS_metier Blueprint Bundle</name>

17. <description>garageWS_metier OSGi blueprint bundle project.</description>

19. <dependencies>

20. <dependency>

21. <groupId>${project.groupId}</groupId>

22. <artifactId>garageWS_modele</artifactId>

23. <version>[1.0.0,2.0)</version>

24. <type>bundle</type>

25. </dependency>

27. <dependency>

28. <groupId>org.slf4j</groupId>

29. <artifactId>slf4j-api</artifactId>

30. </dependency>

31. </dependencies>

34. <build>

35. <plugins>

36. <plugin>

37. <groupId>org.apache.felix</groupId>

38. <artifactId>maven-bundle-plugin</artifactId>

39. <version>3.2.0</version>

40. <extensions>true</extensions>

41. <configuration>

42. <instructions>

43. <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>

44. <Bundle-Version>${project.version}</Bundle-Version>

45. <Export-Package>com.exemple.garageWS.metier*;version=${project.version}</Export-Package>

46. <Import-Package>*</Import-Package>

47. </instructions>

48. </configuration>

49. </plugin>

50. <plugin>

51. <groupId>org.apache.maven.plugins</groupId>

52. <artifactId>maven-compiler-plugin</artifactId>

53. <configuration>

54. <source>1.8</source>

55. <target>1.8</target>

56. <maxmem>256M</maxmem>

57. </configuration>

58. </plugin>

59. </plugins>

60. </build>

62. </project>

9. Dans le même temps, il est nécessaire de préciser la version de la bibliothèque « SLF4J ». Comme nous l'avons fait dans le sous-module précédent, nous allons le signaler dans le fichier « garageWS/pom.xml » ;

garageWS/pom.xml

1. <?xml version="1.0" encoding="UTF-8"?>

2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

3. <modelVersion>4.0.0</modelVersion>

5. <groupId>com.exemple.garageWS</groupId>

6. <artifactId>garageWS</artifactId>

7. <version>1.0.0</version>

8. <packaging>pom</packaging>

10. <modules>

11. <module>garageWS_modele</module>

12. <module>garageWS_metier</module>

13. <module>garageWS_jaxws</module>

14. </modules>

16. <properties>

17. <jpa.version>2.1.1</jpa.version>

18. <osgi.log.version>1.3.0</osgi.log.version>

19. <slf4j-version>1.6.1</slf4j-version>

20. <project.build.outputEncoding>UTF-8</project.build.outputEncoding>

21. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

22. </properties>

24. <dependencyManagement>

25. <dependencies>

27. <!-- Module utile au sous-module "garageWS_metier" -->

28. <dependency>

29. <groupId>${project.groupId}</groupId>

30. <artifactId>garageWS_modele</artifactId>

31. </dependency>

33. <!-- JPA -->

34. <dependency>

35. <groupId>org.eclipse.persistence</groupId>

36. <artifactId>javax.persistence</artifactId>

37. <version>${jpa.version}</version>

38. <scope>provided</scope> <!-- Le container ou le JDK devra fournir la dependance lors de l'execution -->

39. </dependency>

41. <dependency>

42. <groupId>org.osgi</groupId>

43. <artifactId>org.osgi.service.log</artifactId>

44. <version>${osgi.log.version}</version>

45. <scope>provided</scope> <!-- Le container ou le JDK devra fournir la dependance lors de l'execution -->

46. </dependency>

48. <dependency>

49. <groupId>org.slf4j</groupId>

50. <artifactId>slf4j-api</artifactId>

51. <version>${slf4j-version}</version>

52. </dependency>

54. </dependencies>

55. </dependencyManagement>

57. </project>

Remarquez, dans le fichier « garageWS/pom.xml », la mise en place de la dépendance concernant le sous-module « garageWS_metier ». En effet, ce sous-module a besoin du sous-module « garageWS_modele » pour s'exécuter. Il est donc nécessaire d'indiquer cette dépendance dans le fichier « pom.xml » du projet.

Concernant la version de cette dépendance, comme nous l'avons évoqué dans le paragraphe « Mise à jour d'un bundle » du tutoriel « Tutoriel pour développer et déployer un service Web RESTFUL OSGI multibundle sous Eclipse et Karaf », il est judicieux de la préciser dans le fichier « garageWS_modele/pom.xml » afin de faciliter la mise à jour du bundle.

10. Il nous reste désormais à modifier le contenu du fichier « garageWS_metier/my-service.xml  » afin de faire comprendre à Blueprint l'articulation de notre sous-module métier.
Mais avant cela, renommez ce fichier « my-service.xml » du module « garageWS_metier » en « blueprint.xml », afin de faciliter la maintenance ultérieure, par un clic droit sur le fichier puis « Rename » ;

11. Modifiez le fichier « garageWS_metier/blueprint.xml » de la manière suivante :

garageWS_metier/blueprint.xml

1. <?xml version="1.0" encoding="UTF-8"?>

3. <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" default-activation="lazy">

5. <!-- Definition de l'enregistrement du service dans le registre de services OSGI. On renseigne le bean qui fournit le service via l'attribut ref -->

6. <service ref="voitureMetier" interface="com.exemple.garageWS.metier.VoitureMetier">

7. <service-properties>

8. <entry key="service.exported.interfaces" value="*" />

9. </service-properties>

10. </service>

12. <!-- Les runtimes d'applications OSGI traitent les services distants differemment de ceux en local a cause de la semantique d'invocation par defaut qui est differente

13. (passage par reference local versus passage par valeur distant). Pour eviter qu'une application appelle accidentellement un service exporte qui est uniquement designe

14. par un appel par "passage de valeur", il faut cacher le lookup local.

15. La solution est d'exporter le meme bean deux fois : une fois pour les appels distants et une autre pour les appels locaux.

16. En d'autres termes, on ajoute un autre element avec la meme configuration mais sans la propriete "service.exported.interfaces"-->

17. <service ref="voitureMetier" interface="com.exemple.garageWS.metier.VoitureMetier"/>

19. <bean id="voitureMetier" class="com.exemple.garageWS.metier.VoitureMetierImpl">

20. <property name="voitureDao" ref="voitureDao"/>

21. </bean>

23. <!-- Definition des dependances -->

24. <reference id="voitureDao" interface="com.exemple.garageWS.modele.dao.VoitureDao"/>

26. </blueprint>

Notre sous-module métier est déjà terminé ! Il ne nous reste plus qu'à créer le sous-module propre à la couche « service » qui constituera la porte d'entrée de notre service web SOAP.

V-C - Création du bundle Web

Le sous-module « garageWS_jaxws » permettra d'accéder au service web. Il s'agira de l'interface entre l'utilisateur et la couche métier.

À l'issue de ce paragraphe, ce bundle ne sera pas OSGI : il ne s'agira que d'un bundle qui déclarera notre service web SOAP. Nous verrons, dans le paragraphe « V Export du service SOAP en service OSGI » comment utiliser ce service comme un service OSGI.

Pour accéder au service web, nous créerons une classe implémentant l'interface nécessaire à Blueprint et qui sera annotée par @WebService. Les méthodes disponibles dans ce service SOAP seront déclarées public.

Le sous-module que nous créerons se nommera « garageWS_jaxws ». Il sera constitué d'un « package » nommé . Ce package contiendra une interface nommée VoitureService et une classe nommée VoitureServiceImpl implémentant cette interface.

L'interface VoitureService définira toutes les méthodes accessibles par les « bundle » qui utiliseront le bundle modèle. Il s'agira de l'interface sous laquelle le service Blueprint sera enregistré dans l'annuaire de services.

La classe VoitureServiceImpl constituera le bean qui fournira le service et fera appel au bundle « garageWS_metier » par l'intermédiaire d'une de ses propriétés qui ne sera autre qu'une instance de la classe .

1. De la même manière que vous avez créé les sous-modules « garageWS_modele » et « garageWS_metier », créez le sous-module « garageWS_jaxws » par un clic droit sur le projet « garageWS » puis « New » → « Other » → « Maven Module » puis cliquez sur « Next » :

2. Dans la fenêtre qui apparaît, après avoir saisi « blueprint » dans le filtre, double-cliquez sur la ligne dont l'Artefact Id est égal à « karaf-blueprint-archetype » et remplissez la fenêtre qui s'affiche de la manière suivante :
Group Id : com.exemple.garageWS
Artefact Id : garageWS_jaxws
Version : 1.0.0
Package : com.exemple.garageWS.jaxws

3. Cliquez sur « Finish ».
Le nouveau sous-module est désormais créé dans l'explorateur de projet :

4. Renommez les fichiers « MyService.java » et « MyServiceImpl.java » respectivement en « VoitureService.java » et « VoitureServiceImpl.java » ;

5. Modifiez l'interface « VoitureService » en créant les méthodes appelées par l'utilisateur du service web. Ce sont ces méthodes qui utiliseront les méthodes du sous-module « garageWS_metier ». Pour cela, copiez-coller le code ci-dessous dans le fichier « « VoitureService.java » :

VoitureService.java

1. package com.exemple.garageWS.jaxws;

3. import java.util.List;

5. import javax.jws.WebService;

7. import com.exemple.garageWS.modele.Voiture;

9. @WebService

10. public interface VoitureService

11. {

12. public Voiture getVoiture(final int voitureId);

13. public List<Voiture> getAllVoiture();

14. public Voiture ajoutVoiture(String marque, String modele, String categorie, String couleur, String prix);

15. }

Remarquez la présence de l'annotation @WebServicepour indiquer à « jaxws » que cette interface implémentera un service web. Autrement dit que les méthodes de cette interface pourront être appelées par un client du service.

6. Complétez le fichier « garageWS_jaxws/pom.xml » afin de satisfaire la dépendance au sous-module « garageWS_metier » (qui utilise lui-même le sous-module « garageWS_modele »). Ainsi, le contenu de ce fichier sera celui-ci :

garageWS_jaxws/pom.xml

1. <?xml version="1.0" encoding="UTF-8"?>

2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4. <modelVersion>4.0.0</modelVersion>

6. <parent>

7. <artifactId>garageWS</artifactId>

8. <groupId>com.exemple.garageWS</groupId>

9. <version>1.0.0</version>

10. </parent>

12. <artifactId>garageWS_jaxws</artifactId>

13. <version>1.0.0</version>

14. <packaging>bundle</packaging>

16. <name>garageWS_jaxws Blueprint Bundle</name>

17. <description>garageWS_jaxws OSGi blueprint bundle project.</description>

19. <dependencies>

20. <dependency>

21. <groupId>${project.groupId}</groupId>

22. <artifactId>garageWS_metier</artifactId>

23. <version>[1.0.0,2.0)</version>

24. <type>bundle</type>

25. </dependency>

26. </dependencies>

30. <build>

31. <plugins>

32. <plugin>

33. <groupId>org.apache.felix</groupId>

34. <artifactId>maven-bundle-plugin</artifactId>

35. <version>3.2.0</version>

36. <extensions>true</extensions>

37. <configuration>

38. <instructions>

39. <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>

40. <Bundle-Version>${project.version}</Bundle-Version>

41. <Export-Package>com.exemple.garageWS.jaxws*;version=${project.version}</Export-Package>

42. <Import-Package>*</Import-Package>

43. </instructions>

44. </configuration>

45. </plugin>

46. <plugin>

47. <groupId>org.apache.maven.plugins</groupId>

48. <artifactId>maven-compiler-plugin</artifactId>

49. <configuration>

50. <source>1.8</source>

51. <target>1.8</target>

52. <maxmem>256M</maxmem>

53. </configuration>

54. </plugin>

55. </plugins>

56. </build>

58. </project>

7. Il est également nécessaire d'indiquer, dans le fichier « garageWS/pom.xml » cette nouvelle dépendance au sous-module « garageWS_metier ». Le contenu du fichier « garageWS/pom.xml » devient donc celui-ci :

garageWS/pom.xml

1. <?xml version="1.0" encoding="UTF-8"?>

2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

3. <modelVersion>4.0.0</modelVersion>

5. <groupId>com.exemple.garageWS</groupId>

6. <artifactId>garageWS</artifactId>

7. <version>1.0.0</version>

8. <packaging>pom</packaging>

10. <modules>

11. <module>garageWS_modele</module>

12. <module>garageWS_metier</module>

13. <module>garageWS_jaxws</module>

14. </modules>

16. <properties>

17. <jpa.version>2.1.1</jpa.version>

18. <osgi.log.version>1.3.0</osgi.log.version>

19. <slf4j-version>1.6.1</slf4j-version>

20. <project.build.outputEncoding>UTF-8</project.build.outputEncoding>

21. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

22. </properties>

24. <dependencyManagement>

25. <dependencies>

27. <!-- Module utile au sous-module "garageWS_metier" -->

28. <dependency>

29. <groupId>${project.groupId}</groupId>

30. <artifactId>garageWS_modele</artifactId>

31. </dependency>

33. <!-- Module utile au sous-module "garageWS_jaxws" -->

34. <dependency>

35. <groupId>${project.groupId}</groupId>

36. <artifactId>garageWS_metier</artifactId>

37. </dependency>

39. <!-- JPA -->

40. <dependency>

41. <groupId>org.eclipse.persistence</groupId>

42. <artifactId>javax.persistence</artifactId>

43. <version>${jpa.version}</version>

44. <scope>provided</scope> <!-- Le container ou le JDK devra fournir la dependance lors de l'execution -->

45. </dependency>

47. <dependency>

48. <groupId>org.osgi</groupId>

49. <artifactId>org.osgi.service.log</artifactId>

50. <version>${osgi.log.version}</version>

51. <scope>provided</scope> <!-- Le container ou le JDK devra fournir la dependance lors de l'execution -->

52. </dependency>

54. <dependency>

55. <groupId>org.slf4j</groupId>

56. <artifactId>slf4j-api</artifactId>

57. <version>${slf4j-version}</version>

58. </dependency>

60. </dependencies>

61. </dependencyManagement>

63. </project>

8. Modifiez désormais la déclaration de la classe « VoitureServiceImpl » dans le fichier « VoitureServiceImpl.java » en y copiant-collant le code ci-dessous :

VoitureServiceImpl.java

1. package com.exemple.garageWS.jaxws;

3. import java.util.List;

5. import com.exemple.garageWS.metier.VoitureMetier;

6. import com.exemple.garageWS.modele.Voiture;

8. import org.slf4j.Logger;

9. import org.slf4j.LoggerFactory;

12. public class VoitureServiceImpl implements VoitureService

13. {

14. private VoitureMetier voitureMetier;

16. //Recuperation du logger de la classe VoitureMetierImpl

17. private static final Logger LOG = LoggerFactory.getLogger(VoitureMetier.class);

19. public Voiture getVoiture(int voitureId)

20. {

21. LOG.info(getClass().getSimpleName() + "[getVoiture({})]", voitureId);

22. return voitureMetier.getVoitureById(voitureId);

23. }

25. public List<Voiture> getAllVoiture()

26. {

27. LOG.info(getClass().getSimpleName() + "[getAllVoiture()]");

28. return voitureMetier.getAllVoitureRecords();

29. }

31. public Voiture ajoutVoiture(String marque, String modele, String categorie, String couleur, String prix)

32. {

33. LOG.info(getClass().getSimpleName() + "[ajoutVoiture(" + marque + "," + modele + "," + categorie + "," + couleur + "," + prix + ")]");

34. return voitureMetier.addVoiture(marque, modele, categorie, couleur, prix);

35. }

37. public void setVoitureMetier( final VoitureMetier voitureMetier)

38. {

39. this.voitureMetier = voitureMetier;

40. }

42. }

9. Suite aux erreurs de compilation apparaissant, modifiez le fichier « garageWS_jaxws » en ajoutant les dépendances aux bibliothèques « jaxws » et « SLF4J » comme ceci :

garageWS_jaxws/pom.xml

1. <?xml version="1.0" encoding="UTF-8"?>

2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4. <modelVersion>4.0.0</modelVersion>

6. <parent>

7. <artifactId>garageWS</artifactId>

8. <groupId>com.exemple.garageWS</groupId>

9. <version>1.0.0</version>

10. </parent>

12. <artifactId>garageWS_jaxws</artifactId>

13. <version>1.0.0</version>

14. <packaging>bundle</packaging>

16. <name>garageWS_jaxws Blueprint Bundle</name>

17. <description>garageWS_jaxws OSGi blueprint bundle project.</description>

19. <dependencies>

20. <dependency>

21. <groupId>${project.groupId}</groupId>

22. <artifactId>garageWS_metier</artifactId>

23. <version>[1.0.0,2.0)</version>

24. <type>bundle</type>

25. </dependency>

27. <dependency>

28. <groupId>org.apache.cxf</groupId>

29. <artifactId>cxf-rt-frontend-jaxws</artifactId>

30. </dependency>

32. <dependency>

33. <groupId>org.apache.cxf</groupId>

34. <artifactId>cxf-rt-transports-http</artifactId>

35. </dependency>

37. <dependency>

38. <groupId>org.slf4j</groupId>

39. <artifactId>slf4j-api</artifactId>

40. </dependency>

42. </dependencies>

45. <build>

46. <plugins>

47. <plugin>

48. <groupId>org.apache.felix</groupId>

49. <artifactId>maven-bundle-plugin</artifactId>

50. <version>3.2.0</version>

51. <extensions>true</extensions>

52. <configuration>

53. <instructions>

54. <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>

55. <Bundle-Version>${project.version}</Bundle-Version>

56. <Export-Package>com.exemple.garageWS.jaxws*;version=${project.version}</Export-Package>

57. <Import-Package>*</Import-Package>

58. </instructions>

59. </configuration>

60. </plugin>

61. <plugin>

62. <groupId>org.apache.maven.plugins</groupId>

63. <artifactId>maven-compiler-plugin</artifactId>

64. <configuration>

65. <source>1.8</source>

66. <target>1.8</target>

67. <maxmem>256M</maxmem>

68. </configuration>

69. </plugin>

70. </plugins>

71. </build>

73. </project>

10. Modifiez le fichier « garageWS/pom.xml » en conséquence :

garageWS/pom.xml

1. <?xml version="1.0" encoding="UTF-8"?>

2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

3. <modelVersion>4.0.0</modelVersion>

5. <groupId>com.exemple.garageWS</groupId>

6. <artifactId>garageWS</artifactId>

7. <version>1.0.0</version>

8. <packaging>pom</packaging>

10. <modules>

11. <module>garageWS_modele</module>

12. <module>garageWS_metier</module>

13. <module>garageWS_jaxws</module>

14. </modules>

16. <properties>

17. <cxf.version>3.1.10</cxf.version>

18. <jpa.version>2.1.1</jpa.version>

19. <osgi.log.version>1.3.0</osgi.log.version>

20. <slf4j-version>1.6.1</slf4j-version>

21. <project.build.outputEncoding>UTF-8</project.build.outputEncoding>

22. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

23. </properties>

25. <dependencyManagement>

26. <dependencies>

28. <!-- Module utile au sous-module "garageWS_metier" -->

29. <dependency>

30. <groupId>${project.groupId}</groupId>

31. <artifactId>garageWS_modele</artifactId>

32. </dependency>

34. <!-- Module utile au sous-module "garageWS_jaxws" -->

35. <dependency>

36. <groupId>${project.groupId}</groupId>

37. <artifactId>garageWS_metier</artifactId>

38. </dependency>

40. <!-- JPA -->

41. <dependency>

42. <groupId>org.eclipse.persistence</groupId>

43. <artifactId>javax.persistence</artifactId>

44. <version>${jpa.version}</version>

45. <scope>provided</scope> <!-- Le container ou le JDK devra fournir la dependance lors de l'execution -->

46. </dependency>

48. <dependency>

49. <groupId>org.osgi</groupId>

50. <artifactId>org.osgi.service.log</artifactId>

51. <version>${osgi.log.version}</version>

52. <scope>provided</scope> <!-- Le container ou le JDK devra fournir la dependance lors de l'execution -->

53. </dependency>

55. <dependency>

56. <groupId>org.slf4j</groupId>

57. <artifactId>slf4j-api</artifactId>

58. <version>${slf4j-version}</version>

59. <scope>provided</scope> <!-- Le container ou le JDK devra fournir la dependance lors de l'execution -->

60. </dependency>

62. <dependency>

63. <groupId>org.apache.cxf</groupId>

64. <artifactId>cxf-rt-frontend-jaxws</artifactId>

65. <version>${cxf.version}</version>

66. <scope>provided</scope> <!-- Le container ou le JDK devra fournir la dependance lors de l'execution -->

67. </dependency>

69. <dependency>

70. <groupId>org.apache.cxf</groupId>

71. <artifactId>cxf-rt-transports-http</artifactId>

72. <version>${cxf.version}</version>

73. <scope>provided</scope> <!-- Le container ou le JDK devra fournir la dependance lors de l'execution -->

74. </dependency>

76. </dependencies>

77. </dependencyManagement>

79. </project>

11. Renommez le fichier « my-service.xml » du module « garageWS_jaxws » en « blueprint.xml ». et modifiez le contenu de ce fichier « blueprint.xml » afin qu'il soit :

garageWS_jaxws/blueprint.xml

1. <?xml version="1.0" encoding="UTF-8"?>

3. <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" default-activation="lazy"

4. xmlns:cxf="http://cxf.apache.org/blueprint/core"

5. xmlns:jaxws="http://cxf.apache.org/blueprint/jaxws">

7. <!-- Bus utilise par le endpoint jaxws -->

8. <cxf:bus id="voitureServiceBus">

9. <cxf:features>

10. <cxf:logging/> <!-- On active les logs dans ce bus -->

11. </cxf:features>

13. <!-- Dans le cas ou on veut mettre un interceptor-->

14. <!-- <cxf:features>

15. <bean class="org.apache.cxf.interceptor.security.JAASAuthenticationFeature">

16. <property name="reportFault" value="true"/>

17. </bean>

18. </cxf:features>

19. -->

21. </cxf:bus>

23. <bean id="voitureServiceImpl" class="com.exemple.garageWS.jaxws.VoitureServiceImpl">

24. <property name="voitureMetier" ref="voitureMetier"/>

25. </bean>

27. <!-- Definition des dependances -->

28. <reference id="voitureMetier" interface="com.exemple.garageWS.metier.VoitureMetier"/>

30. <!-- Publication du service SOAP -->

31. <!-- implementor = Nom de la reference du bean sous la forme "#reference_bean"-->

32. <!-- address = Adresse sur laquelle est publie le service web -->

33. <!-- implementorClass = Nom de la classe de l'implementeur -->

34. <jaxws:endpoint

35. implementor="#voitureServiceImpl"

36. address="/voitureService"

37. implementorClass="com.exemple.garageWS.jaxws.VoitureServiceImpl"/>

39. </blueprint>

Notre service web SOAP est désormais terminé. Il ne nous reste plus qu'à le déployer sous forme d'archive « kar », de feature ou de l'inclure dans une distribution personnalisée de Karaf.

V-D - Création du bundle de génération de l'archive Kar

Tout comme dans le tutoriel « Tutoriel pour développer et déployer un service web RESTful OSGI multibundle sous Eclipse et Karaf », nous allons réaliser un fichier kar pour ceux et celles qui veulent avoir une archive à déployer sous Karaf.

Sachez néanmoins qu'il existe la possibilité :

- de déployer notre service web SOAP à partir d'une feature comme le montre le paragraphe « Déploiement à partir d'une feature » dans notre précédent tutoriel ;

- ou de générer une distribution Karaf contenant notre service web sans devoir le déployer. Nous verrons, dans le prochain paragraphe la manière de générer une telle distribution, mais pour l'instant, attardons-nous à la réalisation d'une archive « Kar ».

Pour générer une archive « kar », nous allons créer un nouveau sous-module que nous nommerons « garageWS_kar » qui sera constitué  d'un fichier « feature.xml ». Ce fichier permettra d'indiquer à Karaf les « features » à installer pour que fonctionnent nos « bundle » ainsi que la liste de nos « bundle ».

1. Nous vous invitons à suivre les cinq premiers points du paragraphe « Création du bundle Kar » du tutoriel « Tutoriel pour développer et déployer un service web RESTful OSGI multibundle sous Eclipse et Karaf » en changeant, dans le point 2, le nom du module de « module_kar » à «  garageWS_kar ».
À l'issue de ce travail, vous devriez avoir un sous-module nommé « garageWS_kar » dans l'explorateur de projet comme le présente la capture d'écran ci-dessous.

2. Ouvrez le fichier « garageWS_kar/pom.xml » et modifiez la valeur contenue dans la balise <packaging>de « feature » à « kar ».
Dans le même temps, profitez-en pour « variabiliser » la version de karaf via la balise <properties>. Le contenu du fichier pom.xml devrait désormais être celui-ci :

garageWS_kar/pom.xml

1. <?xml version="1.0" encoding="UTF-8"?>

2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4. <modelVersion>4.0.0</modelVersion>

6. <parent>

7. <artifactId>garageWS</artifactId>

8. <groupId>com.exemple.garageWS</groupId>

9. <version>1.0.0</version>

10. </parent>

12. <artifactId>garageWS_kar</artifactId>

13. <version>1.0.0</version>

14. <packaging>kar</packaging>

16. <name>garageWS_kar-feature</name>

17. <description>garageWS_kar details</description>

19. <properties>

20. <karaf.version>4.1.0</karaf.version>

21. </properties>

23. <build>

24. <pluginManagement>

25. <plugins>

26. <plugin>

27. <groupId>org.apache.karaf.tooling</groupId>

28. <artifactId>karaf-maven-plugin</artifactId>

29. <version>${karaf.version}</version>

30. <extensions>true</extensions>

31. </plugin>

32. </plugins>

33. </pluginManagement>

34. <plugins>

35. <plugin>

36. <groupId>org.apache.karaf.tooling</groupId>

37. <artifactId>karaf-maven-plugin</artifactId>

39. <configuration>

40. <startLevel>50</startLevel>

41. <aggregateFeatures>true</aggregateFeatures>

42. <checkDependencyChange>true</checkDependencyChange>

43. <failOnDependencyChange>false</failOnDependencyChange>

44. <logDependencyChanges>true</logDependencyChanges>

45. <overwriteChangedDependencies>true</overwriteChangedDependencies>

46. </configuration>

47. </plugin>

48. </plugins>

49. </build>

51. </project>

Ne tenez pas compte de l'erreur de compilation concernant ce fichier « pom.xml » dans Eclipse.

3. Modifiez le fichier « garageWS_kar/feature.xml » afin de fournir, dans l'archive « kar », les bibliothèques nécessaires à la bonne exécution de notre service web.
Vous verrez, dans le contenu XML proposé ci-dessous et que vous copierez dans le fichier « garageWS_kar/feature.xml », que certaines bibliothèques embarquées dans Karaf 4.1.0 sont source de bogues ou d'oublis. Pour cette raison, nous remplacerons les bibliothèques incriminées par les versions précédentes qui sont plus stables.
Copiez-collez le contenu ci-dessous dans le fichier « garageWS_kar/feature.xml » :

« garageWS_kar/feature.xml »

1. <?xml version="1.0" encoding="UTF-8"?>

3. <features name="${project.artifactId}-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.3.0">

5. <!-- Ajout de features au repository -->

6. <repository>mvn:org.apache.cxf.karaf/apache-cxf/3.1.10/xml/features</repository>

8. <!-- Concernant PostgreSQL, la version pax-jdbc-features/1.0.0 embarquee dans Karaf 4.1.0 ne propose que la DataSourceFactory dont

9. osgi.jdbc.driver.class = org.postgreSQL.Driver. On ne dispose pas de org.postgreSQL.Driver-pool pour gerer les pools de connexion

10. ni de org.postgreSQL.Driver-pool-xa pour gerer les transactions XA.

11. On ajoute donc la feature pax-jdbc-features/0.9.0 au repository pour l'installer plus loin en lieu et place de pax-jdbc-features/1.0.0 -->

12. <repository>mvn:org.ops4j.pax.jdbc/pax-jdbc-features/0.9.0/xml/features</repository>

14. <!-- hibernate-osgi/5.2.6.Final embarquee dans Karaf 4.1.0 est boguee (https://hibernate.atlassian.net/browse/HHH-11309).

15. On installe donc la version hibernate-osgi/5.2.8.Final dans le repository avant de l'installer-->

16. <repository>mvn:org.hibernate/hibernate-osgi/5.2.8.Final/xml/karaf</repository>

18. <!-- jpa-features/2.5.0 embarquee dans Karaf 4.1.0 entraine l'erreur :

19. java.lang.ClassCastException: org.hibernate.osgi.OsgiPersistenceProvider cannot be cast to javax.persistence.spi.PersistenceProvider:

20. On la remplace donc par la version jpa-features/2.4.0 -->

21. <repository>mvn:org.apache.aries.jpa/jpa-features/2.4.0/xml/features</repository>

23. <feature name='${project.artifactId}' description='${project.name}' version='${project.version}'>

24. <details>${project.description}</details>

26. <!-- Ajout de la capability "osgi.service" car le manifest du bundle pax-logging, qui fournit le service LogService dans Karaf, n'est pas rempli correctement.

27. Ce service n'est pas annonce dans le champ Provide-Capability. Depuis la version 4.0.5 de Karaf, cela pose probleme car Karaf verifie

28. ou il peut recuperer le service LogService. Comme il ne voit aucun bundle capable de fournir ce service, on a une erreur au chargement de notre service dans Karaf.

29. "missing requirement [garageWS_modele/1.0.0] osgi.service; effective:=active; filter:="(objectClass=org.osgi.service.log.LogService)"]"

30. Ceci se produira tant que le bundle pax-login embarque dans Karaf n'est pas mis a jour.

31. Pour tester si la correction a ete faite dans les prochaines versions de pax-logging, il suffit de supprimer la balise "capability" ci-dessous et voir si une erreur est generee dans les logs-->

32. <capability>

33. osgi.service;effective:=active;objectClass=org.osgi.service.log.LogService

34. </capability>

36. <!-- Installation de la feature cxf -->

37. <feature version="3.1.10">cxf</feature>

39. <!-- Installation de pax-jdbc pour creer les DataSourceFactory -->

40. <!-- La creation des DataSources se fait par depot d'un fichier org.ops4j.datasource-*.cfg

41. (ou "*" doit etre remplace par le nom de la DataSource) dans le repertoire "/etc" de Karaf -->

42. <feature version="0.9.0">pax-jdbc</feature>

43. <feature version="0.9.0">pax-jdbc-config</feature>

44. <feature version="0.9.0">pax-jdbc-pool-dbcp2</feature>

45. <feature version="0.9.0">pax-jdbc-postgresql</feature> <!-- Installe le driver PostgreSQL -->

48. <!-- Installation de hibernate-osgi 5.2.8.Final -->

49. <feature version="5.2.8.Final">hibernate-orm</feature>

52. <!-- Installation de la feature jdbc embarquee dans Karaf pour interroger les DataSources depuis l'invite de commande Karaf -->

53. <feature>jdbc</feature>

55. <!-- Installation de la feature jpa embarquee dans Karaf-->

56. <feature version="2.4.0">jpa</feature>

58. <!-- <feature version="1.3.0">transaction</feature>--><!-- Pour Karaf 4.0.4 -->

59. <!-- <feature version="1.3.1">transaction</feature>--> <!-- Pour Karaf 4.0.8 -->

60. <feature version="2.0.0">transaction</feature> <!-- Pour Karaf 4.1.0 -->

62. <!-- <feature version="4.0.4">jndi</feature>--><!-- Pour Karaf 4.0.4 -->

63. <!-- <feature version="4.0.8">jndi</feature>--><!-- Pour Karaf 4.0.8 -->

64. <feature version="4.1.0">jndi</feature><!-- Pour Karaf 4.1.0 -->

66. <!-- Installation de la feature http-whiteboard embarquee dans Karaf pour publier les servlets-->

67. <!-- <feature>http-whiteboard</feature>-->

69. <!-- On place, dans l'archive, les dependances Maven apparaissant dans le fichier garageWS/pom.xml

70. et dont les services ne sont pas encore dans Karaf (commande service:list)-->

71. <bundle>mvn:org.slf4j/slf4j-api/1.6.1</bundle>

72. <bundle>mvn:org.slf4j/slf4j-simple/1.6.1</bundle>

74. <!-- On renseigne les bundles que nous avons developpes et qui seront utiles au service web

75. le chemin de ces bundle est de la forme "mvn:groupId/artifactId/version" ou :

76. - groupeId est la valeur de la balise "/project/parent/groupId" du fichier "pom.xml" de chaque module

77. - artefactId est la valeur de la balise "/project/artefactId" du fichier "pom.xml" de chaque module

78. - version est la valeur de la balise "/project/parent/version" du fichier "pom.xml" de chaque module -->

79. <bundle>mvn:com.exemple.garageWS/garageWS_modele/1.0.0</bundle>

80. <bundle>mvn:com.exemple.garageWS/garageWS_metier/1.0.0</bundle>

81. <bundle>mvn:com.exemple.garageWS/garageWS_jaxws/1.0.0</bundle>

83. </feature>

85. </features>

4. Désormais, cliquez droit sur le projet « garageWS » puis choisissez « Run As... » → « Maven build ». La fenêtre ci-dessous apparaît. Saisissez « clean install » dans le champ « Goals » :

Une archive « kar » a été créée dans le répertoire « target » correspondant au sous-module « garageWS_kar » ;

5. Déployez une distribution de Karaf 4.1.0 puis déposez le fichier « org.ops4j.datasource-garage.cfg » que vous avez créé dans le paragraphe « IV.A Création du bundle DAO » dans le répertoire « etc » de Karaf afin que « pax-jdbc » crée la source de données ;

6. Créez une base de données PostgreSQL nommée « garage » et la table « voiture » à l'aide du script SQL fourni dans le paragraphe « VII.A Création des tables dans la base de données » ;

7. Placez l'archive « kar » dans le répertoire « deploy » puis démarrez Karaf ;

8. Vous pouvez consulter les « logs » de Karaf dans le répertoire « /data/log » de votre distribution de Karaf ;

9. Si tout s'est bien passé, vous pouvez désormais interroger votre service web SOAP par l'intermédiaire d'un client SOAP. Nous utiliserons SOAPUI téléchargeable ici.
Pour cela, sachez que le port de votre serveur Karaf est renseigné dans le fichier « /etc/org.ops4j.pax.web.cfg » et que l'URL de base de la servlet « cxf » est, par défaut, « /cxf ».
Enfin, souvenez-vous que vous avez renseigné le endpoint de votre service web dans le fichier « garageWS_jaxws/blueprint.xml » (il s'agissait du contenu de la balise <adress>) ;
Fort de ces connaissances, ouvrez SOAPUI puis, dans le menu, choisissez « File » → « New SOAP Project » :

10. Dans la fenêtre qui s'affiche, renseignez le champ « Initial WSDL » avec les informations que vous avez recueillies ci-dessus et en complétant par «?wsdl » afin de récupérer le fichier « wsdl » propre à notre service web SOAP. Le champ « Project Name » se remplit dès lors automatiquement. Dans notre cas, la fenêtre est la suivante :

11. Appuyez sur le bouton « OK ». Dans le menu de gauche, notre service web ainsi que les méthodes accessibles apparaissent :

12. Double-cliquez sur une des deux lignes « Request1 » que vous voyez afin d'appeler la requête correspondant à la méthode que vous désirez exécuter.
Si vous choisissez la méthode « getVoiture » et que vous avez ajouté une voiture dans votre table en base de données à l'aide de notre script, alors saisissez la valeur « 1 » dans la requête SOAP tel que décrit ci-dessous :

13. Appuyez désormais sur la flèche verte en haut à gauche (sur la capture d'écran ci-dessus) afin d'envoyer la requête à notre service web. La réponse sera alors la suivante :

Notez que si vous avez choisi d'appeler la méthode « getAllVoiture », vous n'aurez aucun paramètre à fournir.

V-E - Création du bundle de génération d'une distribution personnalisée

Dans le tutoriel « Tutoriel pour développer et déployer un service Web RESTFUL OSGI multibundle sous Eclipse et Karaf » et plus précisément, dans le paragraphe : « VIII-D. Création d'une distribution personnalisée de Karaf », nous avons déjà vu comment créer une distribution personnalisée de Karaf embarquant notre service web en natif.

1. Nous vous invitons donc à suivre les instructions apparaissant dans le paragraphe cité ci-dessus en modifiant le nom du module en « garageWS_custom_distri ».
À l'issue de la création des répertoires, l'explorateur de projet présente votre sous-module de la manière suivante :

2. Remplacez ensuite le contenu du fichier « garageWS_custom_distri/pom.xml » par ceci :

garageWS_custom_distri/pom.xml

1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

2. <modelVersion>4.0.0</modelVersion>

3. <parent>

4. <groupId>com.exemple.garageWS</groupId>

5. <artifactId>garageWS</artifactId>

6. <version>1.0.0</version>

7. </parent>

9. <artifactId>garageWS_custom_distri</artifactId>

10. <packaging>karaf-assembly</packaging>

12. <version>1.0.0</version>

13. <name>Distribution de Karaf personnalisee garageWS</name>

15. <properties>

16. <!-- <karaf.version>4.0.4</karaf.version>-->

17. <!-- <karaf.version>4.0.8</karaf.version>-->

18. <karaf.version>4.1.0</karaf.version>

19. </properties>

21. <!-- Import Karaf POM pour utiliser la version correcte des dependences Karaf -->

22. <dependencyManagement>

23. <dependencies>

24. <dependency>

25. <groupId>org.apache.karaf</groupId>

26. <artifactId>karaf</artifactId>

27. <version>${karaf.version}</version>

28. <type>pom</type>

29. <scope>import</scope>

30. </dependency>

31. </dependencies>

32. </dependencyManagement>

34. <dependencies>

36. <dependency>

37. <!-- La valeur de la balise <scope> est "compile" donc toutes les features sont installees dans le fichier startup.properties

38. et le repository des features n'est pas ajoute dans le fichier etc/org.apache.karaf.features.cfg.

39. Le fichier etc/startup.properties est utilise pour definir les services noyaux et leur ordre de demarrage dans Karaf-->

42. <groupId>org.apache.karaf.features</groupId>

43. <artifactId>framework</artifactId>

44. <version>${karaf.version}</version>

45. <type>kar</type>

46. <scope>compile</scope>

47. </dependency>

49. <dependency>

50. <!--  La valeur de la balise <scope> est "runtime" donc le repository feature est liste dans le fichier etc/org.apache.karaf.features.cfg

51. et les features seront installees dans le repertoire system. Ceci importe les features de Karaf standard décrits dans

52. https://repo1.maven.org/maven2/org/apache/karaf/features/standard/4.0.1/standard-4.0.1-features.xml

53. Ces features sont installees ci-dessous : dans la configuration du plugin karaf-maven-plugin-->

55. <groupId>org.apache.karaf.features</groupId>

56. <artifactId>standard</artifactId>

57. <classifier>features</classifier>

58. <type>xml</type>

59. <version>${karaf.version}</version>

61. <scope>runtime</scope>

62. </dependency>

65. <!-- Les features jdbc et hibernate ne sont pas incluses dans Karaf standard, elles sont dans Karaf enterprise donc on recupere Karaf enterprise

66. qui inclut aussi Karaf standard -->

67. <dependency>

68. <groupId>org.apache.karaf.features</groupId>

69. <artifactId>enterprise</artifactId>

70. <version>${karaf.version}</version>

71. <classifier>features</classifier>

72. <type>xml</type>

73. <scope>runtime</scope>

74. </dependency>

78. <!-- Installation de la feature propre a notre projet. Cette feature a ete generee par le module ""module-kar" -->

79. <dependency>

80. <groupId>${project.groupId}</groupId>

81. <artifactId>garageWS_kar</artifactId>

82. <version>${project.version}</version>

83. <classifier>features</classifier>

84. <type>xml</type>

85. <scope>runtime</scope>

86. </dependency>

88. <!-- Declarations des dependances necessaires a notre projet -->

89. <dependency>

90. <groupId>${project.groupId}</groupId>

91. <artifactId>garageWS_modele</artifactId>

92. <version>${project.version}</version>

93. <scope>runtime</scope>

94. </dependency>

96. <dependency>

97. <groupId>${project.groupId}</groupId>

98. <artifactId>garageWS_metier</artifactId>

99. <version>${project.version}</version>

100. <scope>runtime</scope>

101. </dependency>

103. <dependency>

104. <groupId>${project.groupId}</groupId>

105. <artifactId>garageWS_jaxws</artifactId>

106. <version>${project.version}</version>

107. <scope>runtime</scope>

108. </dependency>

110. </dependencies>

113. <build>

114. <!-- Inclusion des ressources dans la distribution -->

115. <resources>

116. <!-- Le fichier contenant la configuration de notre dataSource sera localise dans le repertoire "src/main/resources"

117. afin d'etre copie dans le repertoire "etc" de Karaf -->

118. <resource>

119. <directory>src/main/resources</directory>

120. <filtering>false</filtering>

121. <includes>

122. <include>**/*</include>

123. </includes>

124. </resource>

126. <!-- Le repertoire "src/main/filtered-resources" contient les fichiers qui incluent des variables denotees par "${...}".

127. Ces variables peuvent venir des proprietes du systeme, des proprietes du projet...

128. Le fichier branding.properties contient de telles variables. Il sera copie, comme les fichiers qui se trouvent dans le repertoire

129. "src/main/resources" dans le repertoire "etc" de Karaf.

130. Voir le lien : http://maven.apache.org/plugins/maven-resources-plugin/examples/filter.html -->

131. <resource>

132. <directory>src/main/filtered-resources</directory>

133. <filtering>true</filtering>

134. <includes>

135. <include>**/*</include>

136. </includes>

137. </resource>

138. </resources>

140. <plugins>

141. <plugin>

142. <groupId>org.apache.karaf.tooling</groupId>

143. <artifactId>karaf-maven-plugin</artifactId>

144. <version>${karaf.version}</version>

145. <extensions>true</extensions>

146. <configuration>

148. <!-- La balise <bootFeatures> permet de demander a Karaf lors de son demarrage de demarrer les features.

149. Le nom des features est obtenu dans le fichier features.xml dans la section <dependencies>.

150. -->

151. <bootFeatures>

152. <!-- Features liees a Karaf standard -->

153. <feature>wrap</feature>

154. <feature>shell</feature>

155. <feature>shell-compat</feature>

156. <feature>feature</feature>

157. <feature>jaas</feature>

158. <feature>ssh</feature>

159. <feature>management</feature>

160. <feature>bundle</feature>

161. <feature>config</feature>

162. <feature>deployer</feature>

163. <feature>diagnostic</feature>

164. <feature>feature</feature>

165. <feature>instance</feature>

166. <feature>kar</feature>

167. <feature>log</feature>

168. <feature>package</feature>

169. <feature>service</feature>

170. <feature>system</feature>

171. <feature>webconsole</feature>

174. <!-- Features liees a Karaf entreprise et necessaires a notre projet -->

175. <feature>jpa</feature>

177. <!-- Demarrage des features propres a notre projet. "pax-jdbc", "apache-cxf" et "hibernate-osgi 5.2.6.final" sont recuperes depuis Maven grace au fichier feature.xml du module "module-kar"-->

178. <feature>pax-jdbc-pool-dbcp2</feature>

180. <!-- Installation la feature de notre projet -->

181. <feature>garageWS_kar</feature>

182. </bootFeatures>

184. <!-- <installedFeatures>

185. installedFeatures installe les features dans le repertoire ${KARAF_HOME}/system directory mais l'utilisateur devra l'installer manuellement via la command feature:install

186. <feature>pax-jdbc</feature>

187. <feature>pax-jdbc-config</feature>

188. <feature>pax-jdbc-pool-dbcp2</feature>

189. </installedFeatures>-->

191. </configuration>

192. </plugin>

193. </plugins>

194. </build>

197. </project>

Remarquez, au passage, que nous avons changé la valeur de la balise <packaging> de « pom » à « karaf-assembly ».

3. Comme indiqué dans le fichier « garageWS_custom_distri/pom.xml » ci-dessus, n'oubliez pas de placer le fichier « org.ops4j.datasource-garage.cfg », que vous avez créé dans le paragraphe « IV.A Création du bundle DAO », dans le répertoire « src/main/resources/etc » ;

4. Si vous le désirez, vous pouvez créer un ASCII art comme nous l'avons fait dans notre précédent tutoriel et le placer dans le répertoire « src/main/filtered-resources/etc » ;

5. Générez votre distribution personnalisée en cliquant droit sur le projet « garageWS » puis « Run as » puis « Maven Build » puis saisissez « clean install » (comme vous le faites jusqu'à présent) ;

6. Récupérez votre distribution de Karaf dans le répertoire « \garageWS\garageWS_custom_distri\target ». Vous pouvez voir que votre distribution est disponible aux formats « tar.gz » et « zip » :

7. Vous n'avez plus qu'à la placer dans le répertoire de votre choix puis à la décompresser avant de l'utiliser comme vous le feriez avec une version classique de Karaf. Si vous avez créé un ASCII Art, vous devriez le voir affiché dans votre invite de commande Karaf comme ceci :

Les personnes qui ont auparavant suivi notre tutoriel « Tutoriel pour développer et déployer un service Web RESTFUL OSGI multibundle sous Eclipse et Karaf » ne doivent pas être perdues jusqu'à présent.

Désormais, nous allons évoquer le moyen d'interroger notre service web SOAP par l'intermédiaire d'une servlet.

VI - Export du service SOAP en service OSGI

Afin de simplifier la mise en place et la maintenance d'interfaces utilisateur permettant l'interrogation de notre service web SOAP, il est intéressant de séparer l'interface utilisateur de la couche CXF et, par conséquent, de rendre ces deux couches totalement indépendantes.

Pour cela, nous allons réaliser un proxy qui déclarera un client du service web SOAP et qui exportera ce client en tant que service OSGI. Il sera, dès lors, très simple de modifier la méthode d'accès au service web SOAP, et même d'ajouter des méthodes d'accès, comme nous le verrons dans le paragraphe suivant.

À la fin de ce paragraphe, notre service web SOAP sera donc interrogeable via un client SOAP ou via une interface utilisateur classique (servlet, JSP, etc.) indépendante de CXF.

Il est donc essentiel de créer un nouveau sous-module qui jouera le rôle d'interface entre l'interface utilisateur et le service web SOAP en consommant le service web SOAP et en l'exportant en tant que service OSGI. Nous nommerons ce sous-module « garageWS_proxy », car il se placera entre les deux couches pour en faciliter les échanges.

La mise en place de ce module sera rapide puisqu'il ne comportera pas une seule ligne de code : il ne s'agira que de configurer les paramètres nécessaires dans le fichier « garageWS_proxy/blueprint.xml ».

Commençons la création de notre sous-module « garageWS_proxy ».

1. Cliquez droit sur le projet « garageWS » → « New » → « Project ». Dans la fenêtre qui apparaît, saisissez « maven » dans le filtre puis choisissez « Maven module ».

2. Cliquez sur le bouton « Next » ;

3. Dans la fenêtre qui apparaît, remplissez le nom du module (par exemple « garageWS_proxy ») et ne cochez pas la case « Create a simple project (skip archetype selection) ».

4. Cliquez sur le bouton « Next » ;

5. Dans le champ « Filter » de la fenêtre qui apparaît, saisissez « blueprint » puis, dans la liste qui s'affiche après quelques instants, choisissez la ligne correspondant à « karaf-blueprint-archetype » puis cliquez sur le bouton « Next » :

6. Renseignez la fenêtre qui apparaît de cette manière :
Group Id : com.exemple.garageWS
Artefact Id : garageWS_proxy
Version : 1.0.0
Package : com.exemple.garageWS.proxy

Dans la section « Properties available from archetype », attribuez la valeur « com.exemple.garageWS.proxy » suivi d'une frappe sur la touche « Entrée » de votre clavier, puis cliquez sur le bouton « Finish » :

Le module nommé « garageWS_proxy » a été créé dans l'explorateur de projet :

7. Dans ce module, il n'est aucunement question de développer une quelconque classe que ce soit. Pour cette raison, supprimez le répertoire « src/main/java » par un clic droit dessus puis choisissez « Delete » et confirmez par l'appui sur le bouton « OK » ;

8. Renommez le fichier « my-service.xml » en « blueprint.xml ». L'architecture de votre sous-module se présente désormais ainsi :

9. Modifiez le contenu du fichier « garageWS_proxy/blueprint.xml » avec le contenu ci-dessous :

garageWS_proxy/blueprint.xml

1. <?xml version="1.0" encoding="UTF-8"?>

3. <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" default-activation="lazy"

4. xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"

5. xmlns:jaxws="http://cxf.apache.org/blueprint/jaxws">

7. <cm:property-placeholder persistent-id="com.exemple.garageWS.proxy"

8. update-strategy="reload">

9. <cm:default-properties>

10. <!-- Adresse d'invocation du service -->

11. <cm:property name="voitureServiceSoap.URL" value="http://localhost:8181/cxf/voitureService" />

12. </cm:default-properties>

13. </cm:property-placeholder>

15. <!-- Export, en tant que service OSGI, du service SOAP consomme par le client.

16. Ceci se fait par encapsulation du client du service SOAP dans un service OSGI.

17. L'interface client web sera ainsi totalement independante de cxf.-->

19. <!-- Definition de l'enregistrement du service dans le registre de services OSGI. Encapsulation du client du service SOAP dans un service OSGI -->

20. <service ref="voitureServiceProxy" interface="com.exemple.garageWS.jaxws.VoitureService" />

23. <!-- Ajout d'un client pour interroger le service web SOAP -->

24. <jaxws:client id="voitureServiceProxy"

25. serviceClass="com.exemple.garageWS.jaxws.VoitureService"

26. address="${voitureServiceSoap.URL}" />

28. </blueprint>

10. Modifiez le fichier « garageWS/pom.xml » afin de placer la balise <module>garageWS_proxy</module> avant la balise <module>garageWS_kar</module>. Ceci aura pour effet de joindre notre nouveau sous-module « garageWS_proxy » à l'archive « kar » puis dans notre distribution personnalisée de Karaf.

11. Du fait que notre sous-module ne comportera aucune classe, simplifiez le contenu du fichier « garageWS_proxy/pom.xml » en supprimant les balises non nécessaires dans la balise <configuration>. Le contenu du fichier « garageWS_proxy/pom.xml » sera celui-ci :

garageWS_proxy/pom.xml

1. <?xml version="1.0" encoding="UTF-8"?>

2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4. <modelVersion>4.0.0</modelVersion>

6. <parent>

7. <artifactId>garageWS</artifactId>

8. <groupId>com.exemple.garageWS</groupId>

9. <version>1.0.0</version>

10. </parent>

12. <artifactId>garageWS_proxy</artifactId>

13. <version>1.0.0</version>

14. <packaging>bundle</packaging>

16. <name>garageWS_proxy Blueprint Bundle</name>

17. <description>garageWS_proxy OSGi blueprint bundle project.</description>

19. <build>

20. <plugins>

21. <plugin>

22. <groupId>org.apache.felix</groupId>

23. <artifactId>maven-bundle-plugin</artifactId>

24. <version>3.2.0</version>

25. <extensions>true</extensions>

26. <configuration>

27. <instructions>

28. <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>

29. <Bundle-Version>${project.version}</Bundle-Version>

30. </instructions>

31. </configuration>

32. </plugin>

33. </plugins>

34. </build>

36. </project>

12. Ajoutez la ligne <bundle>mvn:com.exemple.garageWS/garageWS_proxy/1.0.0</bundle> après la ligne <bundle>mvn:com.exemple.garageWS/garageWS_jaxws/1.0.0</bundle> dans le fichier « garageWS_kar/feature.xml ».

Voilà !!! Notre sous-module ayant le rôle de proxy est déjà terminé ! Il ne reste plus qu'à créer l'interface utilisateur. C'est l'objet du paragraphe suivant.

VII - Création d'une interface utilisateur d'interrogation de notre service web SOAP indépendante de CXF

Dans le but d'interroger notre service web SOAP, nous choisirons de pouvoir utiliser une servlet que nous créerons dans un nouveau sous-module.

Cette servlet peut désormais, par la création du proxy ci-dessus, contenir une propriété qui sera du type de notre service web SOAP (VoitureService). Du fait que notre service web SOAP a été transformé en service OSGI, on pourra, via le fichier « blueprint.xml » du sous-module propre à notre servlet et que nous allons réaliser ci-dessous, procéder par injection de notre service OSGI dans la servlet.

La facilité de mise à jour de la méthode d'accès à notre service web SOAP réside dans ce procédé d'injection.

1. Créez un sous-module blueprint nommé « garageWS_webui ». Étant donné que vous en êtes arrivé là, vous savez faire désormais. Par conséquent, nous vous laissons faire. Afin que vous obteniez le même résultat que nous, voici comment remplir le dernier formulaire :

Vous devriez obtenir ceci dans l'explorateur de projet :

2. Supprimez l'interface « MyService.java » puis renommez la classe « MyServiceImpl.java » en « VoitureServlet.java ». Enfin, renommez le fichier « my-service.xml » en « blueprint.xml » ;

3. Faites hériter la classe « VoitureServlet » de la classe « HttpServlet » ;

4. Copiez le code ci-dessous dans le fichier « VoitureServlet.java » :

VoitureServlet.java

1. package com.exemple.garageWS.webui;

3. import java.util.List;

4. import java.io.IOException;

6. import javax.servlet.ServletException;

7. import javax.servlet.ServletOutputStream;

8. import javax.servlet.http.HttpServlet;

9. import javax.servlet.http.HttpServletRequest;

10. import javax.servlet.http.HttpServletResponse;

12. import com.exemple.garageWS.jaxws.VoitureService;

13. import com.exemple.garageWS.modele.Voiture;

15. public class VoitureServlet extends HttpServlet

16. {

17. private static final long serialVersionUID = -5896342395456213489L;

18. private VoitureService voitureService;

20. @Override

21. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException

22. {

23. ServletOutputStream os = resp.getOutputStream();

24. List<Voiture> voitures = voitureService.getAllVoiture();

25. os.println("<html><body>");

26. os.println("<h2>Voitures</h2>");

27. os.println("<table>");

28. os.println("<tr><th>Id</th><th>Marque</th><th>Modèle</th><th>Catégorie</th><th>Couleur</th><th>Prix</th></tr>");

29. for (Voiture voiture : voitures)

30. {

31. os.println(String.format("<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>", voiture.getIdVoiture(), voiture.getMarque(), voiture.getModele(), voiture.getCategorie(), voiture.getCouleur(), voiture.getPrix()));

32. }

33. os.println("</table>");

34. os.println("<h2>Ajouter voiture</h2>");

35. //Creation d'un formulaire dont l'action est definie dans le fichier blueprint.xml

36. os.println("<form name='input' action='/voitureui' method='post'>");

37. os.println("<table>");

38. os.println("<tr><td>Marque</td><td><input type='text' name='marque'/></td></tr>");

39. os.println("<tr><td>Modèle</td><td><input type='text' name='modele'/></td></tr>");

40. os.println("<tr><td>Catégorie</td><td><input type='text' name='categorie'/></td></tr>");

41. os.println("<tr><td>Couleur</td><td><input type='text' name='couleur'/></td></tr>");

42. os.println("<tr><td>Prix</td><td><input type='text' name='prix'/></td></tr>");

43. os.println("<tr><td colspan='2'><input type='submit' value='Ajouter'/></td></tr>");

44. os.println("</form>");

45. os.println("</table>");

46. os.println("</body></html>");

47. }

50. /**

51. * Methode permettant de recuperer ce qui est envoye par le formulaire (en POST) et d'enregistrer une nouvelle voiture en base

52. * @param req

53. * @param resp

54. * @throws ServletException

55. * @throws IOException

56. */

57. @Override

58. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException

59. {

60. String marque = req.getParameter("marque");

61. String modele = req.getParameter("modele");

62. String categorie = req.getParameter("categorie");

63. String couleur = req.getParameter("couleur");

64. String prix = req.getParameter("prix");

66. voitureService.ajoutVoiture(marque, modele, categorie, couleur, prix);

67. resp.sendRedirect("/voitureui");

68. }

70. public void setVoitureService(VoitureService voitureService)

71. {

72. this.voitureService = voitureService;

73. }

75. }

5. Vous remarquez des erreurs de compilation dans l'éditeur. Afin de les corriger, comme vous le savez désormais, il vous faut signaler les dépendances nécessaires dans le fichier « garageWS_webui/pom.xml ». Modifiez ce fichier pour qu'il contienne ceci :

garageWS_webui/pom.xml

1. <?xml version="1.0" encoding="UTF-8"?>

2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4. <modelVersion>4.0.0</modelVersion>

6. <parent>

7. <artifactId>garageWS</artifactId>

8. <groupId>com.exemple.garageWS</groupId>

9. <version>1.0.0</version>

10. </parent>

12. <artifactId>garageWS_webui</artifactId>

13. <version>1.0.0</version>

14. <packaging>bundle</packaging>

16. <name>garageWS_webui Blueprint Bundle</name>

17. <description>garageWS_webui OSGi blueprint bundle project.</description>

19. <dependencies>

20. <dependency>

21. <groupId>javax.servlet</groupId>

22. <artifactId>servlet-api</artifactId>

23. <version>2.5</version>

24. </dependency>

26. <dependency>

27. <groupId>${project.groupId}</groupId>

28. <artifactId>garageWS_jaxws</artifactId>

29. <version>[1.0.0,2.0)</version>

30. </dependency>

31. </dependencies>

33. <build>

34. <plugins>

35. <plugin>

36. <groupId>org.apache.felix</groupId>

37. <artifactId>maven-bundle-plugin</artifactId>

38. <version>3.2.0</version>

39. <extensions>true</extensions>

40. </plugin>

41. </plugins>

42. </build>

44. </project>

6. Modifiez le fichier « garageWS_webui/blueprint.xml » pour injecter notre service SOAP :

garageWS_webui/blueprint.xml

1. <?xml version="1.0" encoding="UTF-8"?>

3. <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" default-activation="lazy">

4. <!-- Injection d'une implementation de voitureDao dans notre servlet -->

5. <!-- La feature http-whiteboard installee au prealable dans karaf par l'intermediaire de notre installation personnalisée publiera la servlet

6. sur la localisation /voitureui.-->

7. <service interface="javax.servlet.http.HttpServlet" ref="voitureServlet">

8. <service-properties>

9. <entry key="alias" value="/voitureui" />

10. </service-properties>

11. </service>

13. <!-- On utilise le service SOAP entant que service OSGI -->

14. <bean id="voitureServlet" class="com.exemple.garageWS.webui.VoitureServlet">

15. <property name="voitureService" ref="voitureService" />

16. </bean>

18. <reference id="voitureService" interface="com.exemple.garageWS.jaxws.VoitureService"/>

20. </blueprint>

7. Modifiez le fichier « garageWS/pom.xml » afin de placer la balise <module>garageWS_webui</module> après la balise <module>garageWS_proxy</module> et avant la balise <module>garageWS_kar</module>. Ceci afin que notre servlet soit encapsulée dans l'archive « Kar ».

8. Ajoutez la ligne après la ligne dans le fichier « garageWS_kar/feature.xml ».

9. Dans le but que votre servlet fonctionne correctement, il est nécessaire d'installer, dans Karaf, la feature « http-whiteboard ». Pour cela, modifiez le fichier « feature.xml » du sous-module « garageWS_kar » pour y ajouter la balise <feature>http-whiteboard</feature>.

10. Générez votre distribution personnalisée en cliquant droit sur le projet « garageWS » puis « Run as » puis « Maven Build » puis saisissez « clean install » (comme vous le faites jusqu'à présent) ;

11. récupérez votre distribution de Karaf dans le répertoire « \garageWS\garageWS_custom_distri\target ». Vous pouvez voir que votre distribution est disponible aux formats « tar.gz » et « zip » :

12. Vous n'avez plus qu'à la placer dans le répertoire de votre choix, puis à la décompresser avant de l'utiliser comme vous le feriez avec une version classique de Karaf. Si vous avez créé un ASCII Art, vous devriez le voir affiché dans votre invite de commande Karaf comme ceci :

13. Votre service web SOAP est désormais accessible non seulement par un client SOAP comme nous l'avons montré plus haut, mais également par votre navigateur en invoquant la servlet que vous venez de développer. Souvenez-vous que nous avons renseigné l'URL d'invocation de la servlet dans les propriétés du service placées dans le fichier « garageWS_webui/blueprint.xml ». Dans notre cas, ce endpoint est « /voitureui ».
Par conséquent, ouvrez un navigateur et saisissez l'adresse « http://localhost:8181/voitureui » comme le présente la capture ci-dessous :

14. La saisie dans ce formulaire et l'appui sur le bouton « Ajouter » permet d'envoyer les informations au service web SOAP vu en tant que service OSGI depuis que nous avons créé un proxy dans le paragraphe « V Accès au service SOAP en tant que service OSGI ».

De la même manière que nous venons de transformer un service web SOAP en service OSGI, il est également possible de transformer un service REST (comme celui créé dans le tutoriel « création d'un service web RESTful multibundle pour Karaf sous Eclipse ») en service OSGI via l'utilisation de la balise <jaxrs:client> en lieu et place de la balise <jaxws:client> dans le fichier « blueprint.xml » du sous-module proxy.

VIII - Code source de l'exemple

Il est désormais temps pour vous de tester notre exemple et de vous l'approprier dans sa globalité. Aussi, voici le code source et le script à exécuter dans une base de données PostgreSQL.

VIII-A - Création des tables dans la base de données

Afin d'utiliser les sources de ce tutoriel sans modification, il est nécessaire d'installer PostgreSQL en local. Dans la base de données créée, exécutez ensuite les requêtes ci-dessous :

Création du schéma et de la table

1. CREATE SCHEMA garage;

3. CREATE TABLE garage.voiture

4. (

5. id serial  NOT NULL,

6. marque character varying(30) NOT NULL,

7. modele character varying(50) NOT NULL,

8. categorie character varying(50) NOT NULL,

9. colori character varying(50) NOT NULL,

10. prix integer NOT NULL,

12. CONSTRAINT pk_garage PRIMARY KEY (id)

13. );

15. INSERT INTO garage.voiture(id, marque, modele, categorie, colori, prix)

16. VALUES (1, 'RENAULT', 'Scénic', 'Monospace', 'jaune', 30000);

VIII-B - Code JAVA de l'exemple

Le code source de l'exemple dont il est question dans ce tutoriel est disponible à l'adresse suivante : olivier-rozier.developpez.com/tutoriels/soap/soap-karaf-multibundle/fichiers/garageWS.zip

1. Récupérez l'archive à partir du lien ci-dessus.

2. Extrayez les répertoires et les fichiers de telle manière à avoir « garageWS » comme répertoire racine. L'architecture des fichiers doit être celle-ci :

3. Copiez le répertoire « garageWS » dans votre Workspace Eclipse.

4. Pour chaque Module Maven que nous avons évoqué dans ce tutoriel (« garageWS_custom_distri », « garageWS_jaxws », « garageWS_kar », « garageWS_metier », « garageWS_modele », « garageWS_proxy » et « garageWS_webui »), dans le menu d'Eclipse, choisissez « File » → « Import » → « General » → « Existing Projects into Workspace »  puis cliquez sur « Next ».

5. Dans « Select root directory », sélectionnez le chemin du répertoire « garageWS » que vous avez placé dans votre Workspace et choisissez un des modules Maven (répétez cette opération pour tous les autres modules)  puis cliquez sur « Finish ».

6. Après avoir importé tous les modules, n'oubliez pas d'importer le projet qui englobe tous les modules Maven via « Import » puis « Existing project into workspace » :

Cliquez sur Finish.

IX - Conclusion

Au terme de ce tutoriel, et pour ceux qui ont suivi notre précédent tutoriel concernant le développement des services web REST, vous pouvez voir que le développement des services web SOAP est en tout point similaire. Seule la bibliothèque « jaxws » employée en lieu et place de la bibliothèque « jaxrs » permet de distinguer le développement entre les deux types de service web que sont les services web REST et les services web SOAP.

Nous avons néanmoins ajouté un bundle jouant le rôle de proxy et qui permet d'exporter le service web SOAP en service OSGI. Cette méthode, considérée comme étant une bonne pratique OSGI, permet de rendre complètement indépendante l'interface utilisateur de CXF. Ceci a pour effet d'alléger le développement et la maintenance.

Cette pratique peut également être appliquée dans le développement des services web REST.

Je tiens à remercier Mickaël Baron pour la relecture technique de cet article ainsi que Claude Leloup pour sa relecture orthographique.