I. Introduction▲
La base du service web SOAPSimple Object Access Protocol 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 SOAPSimple Object Access Protocol :
- un bundle constituant la couche d'accès aux données et définissant le modèle (couche DAOData Access Object) ;
- 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 SOAPSimple Object Access Protocol, 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 OSGIOpen Services Gateway Initiative 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 OSGIOpen Services Gateway Initiative 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 OSGIOpen Services Gateway Initiative (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 OSGIOpen Services Gateway Initiative.
En outre, il n'est pas nécessaire de redémarrer le serveur OSGIOpen Services Gateway Initiative pour qu'une nouvelle version de bundle soit prise en compte, car OSGIOpen Services Gateway Initiative est basé sur un annuaire de services qui lui permet de détecter les nouveaux services offerts par chacun des « bundle » qui le composent.
OSGIOpen Services Gateway Initiative 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 SOAPSimple Object Access Protocol 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 OSGIOpen Services Gateway Initiative multibundle.
Comprenez que la couche service « RESTRepresentational State Transfer » utilisant l'API JAX-RS sera remplacée, dans notre service web SOAPSimple Object Access Protocol, par la couche service « SOAPSimple Object Access Protocol » utilisant l'API JAX-WS.
Il s'agira, en fait de la seule différence en notre service RESTRepresentational State Transfer et notre service SOAPSimple Object Access Protocol.
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 RESTRepresentational State Transfer.
Ainsi, les lecteurs qui auront lu notre précédent tutoriel pourront lire en diagonale pour parfaire le développement de services web OSGIOpen Services Gateway Initiative et ceux qui ne l'auront pas lu pourront comprendre le développement OSGIOpen Services Gateway Initiative en créant un service web SOAPSimple Object Access Protocol.
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.
-
Créez le projet Maven par le menu « File » → « New » → « Project » :
-
Dans la fenêtre qui s'affiche, choisissez « General » puis « Project » :
-
Nommez le projet « garageWS » puis cliquez sur « Finish » :
-
Cliquez droit sur le projet « garageWS » puis choisissez « Configure » → « Convert to Maven Project » :
- 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 :
2.
3.
4.
5.
6.
7.
<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"
>
<modelVersion>
4.0.0</modelVersion>
<groupId>
com.exemple.garageWS</groupId>
<artifactId>
garageWS</artifactId>
<version>
1.0.0</version>
<packaging>
pom</packaging>
</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 SOAService Oriented Architecture intra JVM !
Chacun de nos « bundle » constituera une couche applicative particulière de notre service web SOAPSimple Object Access Protocol.
V-A. Création du bundle DAO▲
Le bundle DAOData Access Object 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 JPAJava Persistence API. Pour un exemple de service web utilisant JPAJava Persistence API 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 JPAJava Persistence API. 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 JPAJava Persistence API ; - 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 :
-
Cliquez droit sur le projet « garageWS » → « New » → « Project ». Dans la fenêtre qui apparaît, saisissez « maven » dans le filtre puis choisissez « Maven module ».
-
Cliquez sur le bouton « Next » ;
-
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.
-
Cliquez sur le bouton « Next » ;
-
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 » :
-
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 ;
-
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 » :
-
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 » :
-
Dans l'éditeur, ajoutez les propriétés et les méthodes suivantes :
Voiture.javaSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.package
com.exemple.garageWS.modele;//Packages propres a JAXB
import
javax.xml.bind.annotation.XmlRootElement;import
javax.xml.bind.annotation.XmlAccessorType;import
javax.xml.bind.annotation.XmlAccessType;import
javax.xml.bind.annotation.XmlType;//Packages propres a JPA
import
javax.persistence.Entity;import
javax.persistence.GeneratedValue;import
javax.persistence.GenerationType;import
javax.persistence.Id;import
javax.persistence.Table;import
javax.persistence.Column;/**
* La classe Voiture est un objet JAVA contenant des methodes get et set
*
<
p/
>
* En ajoutant l'annotation @XmlRootElement, nous offrons la possibilite a JAXB de transformer cet objet en document XML et inversement.
*
<
p/
>
* La representation XML d'une voiture ressemblera a ceci :
*
<
Voiture
>
*
<
idVoiture
>
1
<
/idVoiture
>
*
<
marque
>
RENAULT
<
/marque
>
*
<
modele
>
SCENIC
<
/modele
>
*
<
categorie
>
Monospace
<
/categorie
>
*
<
couleur
>
Gris
<
/couleur
>
*
<
prix
>
20000
<
/prix
>
*
<
/Voiture
>
*/
@XmlRootElement
(
name=
"Voiture"
)@XmlAccessorType
(
XmlAccessType.FIELD)@XmlType
(
propOrder={
"idVoiture"
,"marque"
,"modele"
,"categorie"
,"couleur"
,"prix"
}
)//Fixe l'ordre des champs dans le fichier XML
@Entity
@Table
(
name=
"voiture"
,schema=
"garage"
)public
class
Voiture{
@Id
//specifie la cle primaire
@Column
(
name=
"id"
, nullable=
false
)@GeneratedValue
(
strategy=
GenerationType.IDENTITY)private
int
idVoiture;@Column
(
name=
"marque"
, length=
30
)private
String marque;@Column
(
name=
"modele"
, length=
30
)private
String modele;@Column
(
name=
"categorie"
, length=
30
)private
String categorie;@Column
(
name=
"colori"
, length=
30
)private
String couleur;@Column
(
name=
"prix"
)private
int
prix;public
long
getIdVoiture
(
){
return
idVoiture;}
public
void
setIdVoiture
(
int
idVoiture){
this
.idVoiture=
idVoiture;}
public
StringgetMarque
(
){
return
marque;}
public
void
setMarque
(
String marque){
this
.marque=
marque;}
public
StringgetModele
(
){
return
modele;}
public
void
setModele
(
String modele){
this
.modele=
modele;}
public
StringgetCategorie
(
){
return
categorie;}
public
void
setCategorie
(
String categorie){
this
.categorie=
categorie;}
public
StringgetCouleur
(
){
return
couleur;}
public
void
setCouleur
(
String colori){
this
.couleur=
colori;}
public
int
getPrix
(
){
return
prix;}
public
void
setPrix
(
int
prix){
this
.prix=
prix;}
@Override
public
StringtoString
(
){
return
getClass
(
).getSimpleName
(
)+
"[idVoiture="
+
idVoiture+
", marque="
+
marque+
", modele="
+
modele+
", categorie="
+
categorie+
", couleur="
+
couleur+
"]"
;}
}
Dès lors, vous constatez des erreurs de compilation concernant les « packages » propres à JPAJava Persistence API. 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 ; -
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 à JPAJava Persistence API et nécessaire à notre projet. Le fichier « garageWS_modele/pom.xml » doit être celui-ci à l'issue de votre complétion :garageWS_modele/pom.xmlSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.<?xml version="1.0" encoding="UTF-8"?>
<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"
>
<modelVersion>
4.0.0</modelVersion>
<parent>
<artifactId>
garageWS</artifactId>
<groupId>
com.exemple.garageWS</groupId>
<version>
1.0.0</version>
</parent>
<groupId>
com.exemple.garageWS</groupId>
<artifactId>
garageWS_modele</artifactId>
<version>
1.0.0</version>
<packaging>
bundle</packaging>
<name>
garageWS_modele Blueprint Bundle</name>
<description>
garageWS_modele OSGi blueprint bundle project.</description>
<dependencies>
<!-- JPA -->
<dependency>
<groupId>
org.eclipse.persistence</groupId>
<artifactId>
javax.persistence</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>
org.apache.felix</groupId>
<artifactId>
maven-bundle-plugin</artifactId>
<version>
3.2.0</version>
<extensions>
true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>
${project.artifactId}</Bundle-SymbolicName>
<Bundle-Version>
${project.version}</Bundle-Version>
<Export-Package>
com.exemple.garageWS.modele*;version=${project.version}</Export-Package>
<Import-Package>
*</Import-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>
org.apache.maven.plugins</groupId>
<artifactId>
maven-compiler-plugin</artifactId>
<configuration>
<source>
1.8</source>
<target>
1.8</target>
<maxmem>
256M</maxmem>
</configuration>
</plugin>
</plugins>
</build>
</project>
Des erreurs de compilation apparaissent alors dans ce fichier XML. En effet, la version de la dépendance Maven concernant JPAJava Persistence API 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 JPAJava Persistence API 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.xmlSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.<?xml version="1.0" encoding="UTF-8"?>
<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"
>
<modelVersion>
4.0.0</modelVersion>
<groupId>
com.exemple.garageWS</groupId>
<artifactId>
garageWS</artifactId>
<version>
1.0.0</version>
<packaging>
pom</packaging>
<modules>
<module>
garageWS_modele</module>
</modules>
<properties>
<jpa.version>
2.1.1</jpa.version>
<project.build.outputEncoding>
UTF-8</project.build.outputEncoding>
<project.build.sourceEncoding>
UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<!-- JPA -->
<dependency>
<groupId>
org.eclipse.persistence</groupId>
<artifactId>
javax.persistence</artifactId>
<version>
${jpa.version}</version>
<scope>
provided</scope>
<!-- Le container ou le JDK devra fournir la dependance lors de l'execution -->
</dependency>
</dependencies>
</dependencyManagement>
</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 ».
-
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 » ;
-
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 » :
-
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 :
-
Complétez l'interface « VoitureDao » de la manière suivante :
VoitureDao.javaSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.package
com.exemple.garageWS.modele.dao;import
java.util.List;import
com.exemple.garageWS.modele.Voiture;public
interface
VoitureDao{
public
VoiturerecupVoiture
(
final
int
voitureId);public
List<
Voiture>
recupToutesVoitures
(
);public
VoitureajouterVoiture
(
String marque, String modele, String categorie, String couleur, String prix);}
Cette interface définit les méthodes disponibles dans le service web. Ces méthodes doivent être implémentées dans la classe « VoitureDaoImpl » ;
-
Complétez l'interface « VoitureDaoImpl » de la manière suivante :
VoitureDaoImpl.javaSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.package
com.exemple.garageWS.modele.dao;import
javax.persistence.EntityManager;import
javax.persistence.PersistenceContext;import
javax.persistence.criteria.CriteriaBuilder;import
javax.persistence.criteria.CriteriaQuery;import
javax.persistence.criteria.Root;import
javax.persistence.TypedQuery;import
java.util.List;import
org.osgi.service.log.LogService;import
com.exemple.garageWS.modele.Voiture;public
class
VoitureDaoImplimplements
VoitureDao{
//@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">)
//
@PersistenceContext
(
name=
"garage"
, unitName=
"garage"
)protected
EntityManager entityManager;protected
LogService logService;/**
* La methode recupVoiture permet de recuperer une voiture a partir de son identifiant
*/
@Override
public
VoiturerecupVoiture
(
final
int
voitureId){
logService.log
(
LogService.LOG_INFO,getClass
(
).getSimpleName
(
)+
"[recupVoiture("
+
voitureId+
")]"
);if
(
entityManager==
null
){
logService.log
(
LogService.LOG_INFO,getClass
(
).getSimpleName
(
)+
"/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
"
); logService.log
(
LogService.LOG_INFO,getClass
(
).getSimpleName
(
)+
"/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
EntityManager est null/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
"
); logService.log
(
LogService.LOG_INFO,getClass
(
).getSimpleName
(
)+
"/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
/!
\\
"
);}
return
entityManager.find
(
Voiture.class
, voitureId);}
/**
* La methode recupToutesVoitures permet de recuperer toutes les voitures
*/
@Override
public
List<
Voiture>
recupToutesVoitures
(
){
logService.log
(
LogService.LOG_INFO,getClass
(
).getSimpleName
(
)+
"[recupToutesVoitures()]"
); CriteriaBuilder cb=
entityManager.getCriteriaBuilder
(
); CriteriaQuery<
Voiture>
cq=
cb.createQuery
(
Voiture.class
); Root<
Voiture>
rootEntry=
cq.from
(
Voiture.class
); CriteriaQuery<
Voiture>
all=
cq.select
(
rootEntry); TypedQuery<
Voiture>
allQuery=
entityManager.createQuery
(
all);return
allQuery.getResultList
(
);}
/**
* La methode recupToutesVoitures permet de recuperer toutes les voitures
*/
@Override
public
VoitureajouterVoiture
(
String marque, String modele, String categorie, String couleur, String prix){
logService.log
(
LogService.LOG_INFO,getClass
(
).getSimpleName
(
)+
"[ajouterVoiture("
+
marque+
","
+
modele+
","
+
categorie+
","
+
couleur+
","
+
prix+
")]"
); Voiture voiture=
new
Voiture
(
); voiture.setMarque
(
marque); voiture.setModele
(
modele); voiture.setCategorie
(
categorie); voiture.setCouleur
(
couleur); voiture.setPrix
(
Integer.parseInt
(
prix)); entityManager.persist
(
voiture);return
voiture;}
public
void
setLogService
(
final
LogService logService){
this
.logService=
logService;}
}
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 » ;
-
Complétez le fichier « garageWS_modele/pom.xml » de la manière suivante :
garageWS_modele/pom.xmlSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.<?xml version="1.0" encoding="UTF-8"?>
<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"
>
<modelVersion>
4.0.0</modelVersion>
<parent>
<artifactId>
garageWS</artifactId>
<groupId>
com.exemple.garageWS</groupId>
<version>
1.0.0</version>
</parent>
<artifactId>
garageWS_modele</artifactId>
<version>
1.0.0</version>
<packaging>
bundle</packaging>
<name>
garageWS_modele Blueprint Bundle</name>
<description>
garageWS_modele OSGi blueprint bundle project.</description>
<dependencies>
<!-- JPA -->
<dependency>
<groupId>
org.eclipse.persistence</groupId>
<artifactId>
javax.persistence</artifactId>
</dependency>
<!-- Log -->
<dependency>
<groupId>
org.osgi</groupId>
<artifactId>
org.osgi.service.log</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>
org.apache.felix</groupId>
<artifactId>
maven-bundle-plugin</artifactId>
<version>
3.2.0</version>
<extensions>
true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>
${project.artifactId}</Bundle-SymbolicName>
<Bundle-Version>
${project.version}</Bundle-Version>
<Export-Package>
com.exemple.garageWS.modele*;version=${project.version}</Export-Package>
<Import-Package>
*</Import-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>
org.apache.maven.plugins</groupId>
<artifactId>
maven-compiler-plugin</artifactId>
<configuration>
<source>
1.8</source>
<target>
1.8</target>
<maxmem>
256M</maxmem>
</configuration>
</plugin>
</plugins>
</build>
</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.xmlSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.<?xml version="1.0" encoding="UTF-8"?>
<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"
>
<modelVersion>
4.0.0</modelVersion>
<groupId>
com.exemple.garageWS</groupId>
<artifactId>
garageWS</artifactId>
<version>
1.0.0</version>
<packaging>
pom</packaging>
<modules>
<module>
garageWS_modele</module>
</modules>
<properties>
<jpa.version>
2.1.1</jpa.version>
<osgi.log.version>
1.3.0</osgi.log.version>
<project.build.outputEncoding>
UTF-8</project.build.outputEncoding>
<project.build.sourceEncoding>
UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<!-- JPA -->
<dependency>
<groupId>
org.eclipse.persistence</groupId>
<artifactId>
javax.persistence</artifactId>
<version>
${jpa.version}</version>
<scope>
provided</scope>
<!-- Le container ou le JDK devra fournir la dependance lors de l'execution -->
</dependency>
<dependency>
<groupId>
org.osgi</groupId>
<artifactId>
org.osgi.service.log</artifactId>
<version>
${osgi.log.version}</version>
<scope>
provided</scope>
<!-- Le container ou le JDK devra fournir la dependance lors de l'execution -->
</dependency>
</dependencies>
</dependencyManagement>
</project>
Dans la classe « VoitureDaoImpl », nous avons déclaré une propriété de type EntityManager. Il faut savoir que le bundle du container JPAJava Persistence API implémente la spécification du service JPAJava Persistence API OSGIOpen Services Gateway Initiative 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 JPAJava Persistence API (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
. -
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 :
-
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 :
-
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.xmlSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.<?xml version="1.0" encoding="UTF-8"?>
<persistence
xmlns
=
"http://xmlns.jcp.org/xml/ns/persistence"
xmlns
:
xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
xsi
:
schemaLocation
=
"http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
version
=
"2.1"
>
<!-- ******************************************************************* -->
<!-- JTA (Java Transaction API). Elle fournit des interfaces Java standards entre un gestionnaire de transactions et les differentes parties
impliquees dans un systeme de transactions distribuees : le gestionnaire de ressources, le serveur d'application et les applications transactionnelles.
JTA est un protocole de commit a deux phases :
- 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
- 2e phase : chaque partie valide les changements des données. Cette phase est obligatoire, des lors que les parties se sont engagees.
Ce protocole de commit a deux phases fonctionne plutot bien sur les transactions courtes, mais est totalement inefficace en cas de transaction lente
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
qu'apres la deuxieme phase. -->
<!-- ******************************************************************* -->
<persistence-unit
name
=
"garage"
transaction-type
=
"JTA"
>
<provider>
org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!-- On specifie le nom global JNDI de la datasource qui sera utilisee par le container.
Ce nom est la valeur de la propriété "osgi.jndi.service.name" fournie par la commande Karaf "service:list DataSource"-->
<jta-data-source>
osgi:service/javax.sql.DataSource/(osgi.jndi.service.name=garage)</jta-data-source>
<!-- On precise l'ensemble des classes qui sont gerees par l'entityManager dans l'application -->
<class>
com.exemple.garageWS.modele.Voiture</class>
<!-- On exclut les classes qui ne sont pas listees ci-dessus -->
<exclude-unlisted-classes>
true</exclude-unlisted-classes>
<properties>
<!-- On precise le schema par defaut. On peut aussi le faire via l'attribut @Table(name="voiture",schema="garage") dans la classe entite -->
<property
name
=
"hibernate.default_schema"
value
=
"garage"
/>
<!-- La propriete hibernate.dialect definit le dialect SQL de notre base de donnees.Hibernate s'appuiera sur ce dialect
pour optimiser l'execution des requetes en utilisant les proprietes specifiques a la base de donnees.
Dans notre cas, on utilisera le dialect de PostgreSql-->
<property
name
=
"hibernate.dialect"
value
=
"org.hibernate.dialect.PostgreSQLDialect"
/>
<!-- La propriete hibernate.hbm2ddl.auto=none specifie que le schema de la table n'est pas automatiquement cree quand l'application est deployee.
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.
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.
En production, un utilisateur n'a que tres rarement le privilege de creer ou supprimer des tables.-->
<property
name
=
"hibernate.hbm2ddl.auto"
value
=
"none"
/>
<!-- On log les requetes executees par hibernate pour faciliter le debogage -->
<property
name
=
"hibernate.show_sql"
value
=
"true"
/>
<!-- On met des commentaires dans toutes les requetes SQL generees pour faciliter la comprehension des requetes-->
<property
name
=
"use_sql_comments"
value
=
"true"
/>
</properties>
</persistence-unit>
</persistence>
-
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 OSGIOpen Services Gateway Initiative.
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.cfgSélectionnez1.
2.
3.
4.
5.
6.
7.
8.osgi.jdbc.driver.name
=PostgreSQL JDBC Driver-poolserverName
=999
.999
.999
.999
databaseName
=garageportNumber
=4002
user
=olivierpassword
=XXXXXXXdataSourceName
=garagepool.maxTotal
=10000
-
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 :
-
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.xmlSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.<?xml version="1.0" encoding="UTF-8"?>
<blueprint
xmlns
=
"http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns
:
jpa
=
"http://aries.apache.org/xmlns/jpa/v2.0.0"
xmlns
:
tx
=
"http://aries.apache.org/xmlns/transactions/v1.2.0"
default-activation
=
"lazy"
>
<!-- On active JPA -->
<
jpa
:
enable />
<!-- Definition de l'enregistrement du service dans le registre de services OSGI. On renseigne le bean qui fournit le service via l'attribut ref -->
<service
ref
=
"voitureDao"
interface
=
"com.exemple.garageWS.modele.dao.VoitureDao"
>
<service-properties>
<entry
key
=
"service.exported.interfaces"
value
=
"*"
/>
</service-properties>
</service>
<!-- 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
(passage par reference local versus passage par valeur distant). Pour eviter qu'une application appelle accidentellement un service exporte qui est uniquement designe
par un appel par "passage de valeur", il faut cacher le lookup local.
La solution est d'exporter le meme bean deux fois : une fois pour les appels distants et une autre pour les appels locaux.
En d'autres termes, on ajoute un autre element avec la meme configuration mais sans la propriete "service.exported.interfaces"-->
<service
ref
=
"voitureDao"
interface
=
"com.exemple.garageWS.modele.dao.VoitureDao"
/>
<!-- Publication du bean en tant que service OSGI-->
<!-- Ce bean possede des proprietes a injecter. L'injection de ces proprietes (ici la propriete logService) est faite
immediatement apres la creation du bean -->
<bean
id
=
"voitureDao"
class
=
"com.exemple.garageWS.modele.dao.VoitureDaoImpl"
>
<property
name
=
"logService"
ref
=
"logService"
/>
<!-- Etalissement des transactions pour toutes les methodes DAO. L'application n'a pas besoin de creer l'entity manager que nous avons declare.
La OSGI runtime creera cet entity manager et l'injectera partout ou il est requis. La balise "<tx:transaction>" permet de demander au runtime
d'envelopper les methodes dans une transaction.-->
<
tx
:
transaction
method
=
"*"
/>
</bean>
<!-- Definition des dependances -->
<reference
id
=
"logService"
interface
=
"org.osgi.service.log.LogService"
/>
</blueprint>
- Afin que JPAJava Persistence API 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 :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
<?xml version="1.0" encoding="UTF-8"?>
<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"
>
<modelVersion>
4.0.0</modelVersion>
<parent>
<artifactId>
garageWS</artifactId>
<groupId>
com.exemple.garageWS</groupId>
<version>
1.0.0</version>
</parent>
<artifactId>
garageWS_modele</artifactId>
<version>
1.0.0</version>
<packaging>
bundle</packaging>
<name>
garageWS_modele Blueprint Bundle</name>
<description>
garageWS_modele OSGi blueprint bundle project.</description>
<dependencies>
<!-- JPA -->
<dependency>
<groupId>
org.eclipse.persistence</groupId>
<artifactId>
javax.persistence</artifactId>
</dependency>
<!-- Log -->
<dependency>
<groupId>
org.osgi</groupId>
<artifactId>
org.osgi.service.log</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>
org.apache.felix</groupId>
<artifactId>
maven-bundle-plugin</artifactId>
<version>
3.2.0</version>
<extensions>
true</extensions>
<configuration>
<instructions>
<JPA-PersistenceUnits>
garage</JPA-PersistenceUnits>
<!-- La balise <Meta-Persistence> pointe sur le fichier "persistence.xml" afin que JPA cree une EntityManagerFactory
pour ce bundle-->
<Meta-Persistence>
META-INF/persistence.xml</Meta-Persistence>
<Bundle-SymbolicName>
${project.artifactId}</Bundle-SymbolicName>
<Bundle-Version>
${project.version}</Bundle-Version>
<Export-Package>
com.exemple.garageWS.modele*;version=${project.version}</Export-Package>
<Import-Package>
<!-- Necessaire pour le proxy de Javassist sous peine d'avoir le message "java.lang.RuntimeException: by java.lang.NoClassDefFoundError: org/hibernate/proxy/HibernateProxy" -->
org.hibernate.proxy,javassist.util.proxy,*
</Import-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>
org.apache.maven.plugins</groupId>
<artifactId>
maven-compiler-plugin</artifactId>
<configuration>
<source>
1.8</source>
<target>
1.8</target>
<maxmem>
256M</maxmem>
</configuration>
</plugin>
</plugins>
</build>
</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 DAOData Access Object et la couche service.
Tout comme le module DAOData Access Object 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 DAOData Access Object 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.
-
Cliquez droit sur le projet « garageWS » puis choisissez « New » → « Other » comme le présente l'image ci-dessous :
-
Dans la zone de saisie, tapez « maven » puis, dans les lignes qui apparaissent, double-cliquez sur « Maven Module » :
-
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 :
-
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 » :
-
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 » : -
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.javaSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.package
com.exemple.garageWS.metier;import
java.util.List;import
com.exemple.garageWS.modele.Voiture;public
interface
VoitureMetier{
public
VoituregetVoitureById
(
int
voitureId);public
List<
Voiture>
getAllVoitureRecords
(
);public
VoitureaddVoiture
(
String marque, String modele, String categorie, String couleur, String prix);}
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 DAOData Access Object dans le fichier « garageWS_metier/pom.xml » ;
-
Complétez le fichier « garageWS_metier/pom.xml » de la manière suivante :
garageWS_metier/pom.xmlSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.<?xml version="1.0" encoding="UTF-8"?>
<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"
>
<modelVersion>
4.0.0</modelVersion>
<parent>
<artifactId>
garageWS</artifactId>
<groupId>
com.exemple.garageWS</groupId>
<version>
1.0.0</version>
</parent>
<artifactId>
garageWS_metier</artifactId>
<version>
1.0.0</version>
<packaging>
bundle</packaging>
<name>
garageWS_metier Blueprint Bundle</name>
<description>
garageWS_metier OSGi blueprint bundle project.</description>
<dependencies>
<dependency>
<groupId>
${project.groupId}</groupId>
<artifactId>
garageWS_modele</artifactId>
<version>
[1.0.0,2.0)</version>
<type>
bundle</type>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>
org.apache.felix</groupId>
<artifactId>
maven-bundle-plugin</artifactId>
<version>
3.2.0</version>
<extensions>
true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>
${project.artifactId}</Bundle-SymbolicName>
<Bundle-Version>
${project.version}</Bundle-Version>
<Export-Package>
com.exemple.garageWS.metier*;version=${project.version}</Export-Package>
<Import-Package>
*</Import-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>
org.apache.maven.plugins</groupId>
<artifactId>
maven-compiler-plugin</artifactId>
<configuration>
<source>
1.8</source>
<target>
1.8</target>
<maxmem>
256M</maxmem>
</configuration>
</plugin>
</plugins>
</build>
</project>
Faut-il encore vous faire remarquer que nous nous sommes contentés d'ajouter la balise
<dependencies>
ainsi que ses balises filles ? -
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 DAOData Access Object dans cette propriété.
Copiez-collez le code de la classe « VoitureMetierImpl » ci-dessous dans le fichier « VoitureMetierImpl.java » :
VoitureMetierImpl.javaSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.package
com.exemple.garageWS.metier;import
java.util.List;import
org.slf4j.Logger;import
org.slf4j.LoggerFactory;import
com.exemple.garageWS.modele.Voiture;import
com.exemple.garageWS.modele.dao.VoitureDao;public
class
VoitureMetierImplimplements
VoitureMetier{
private
VoitureDao voitureDao;//Recuperation du logger de la classe VoitureDaoImpl
private
static
final
Logger LOG=
LoggerFactory.getLogger
(
VoitureDao.class
);public
VoituregetVoitureById
(
int
voitureId){
LOG.info
(
getClass
(
).getSimpleName
(
)+
"[getVoitureById({})]"
, voitureId);return
voitureDao.recupVoiture
(
voitureId);}
public
List<
Voiture>
getAllVoitureRecords
(
){
LOG.info
(
getClass
(
).getSimpleName
(
)+
"[getAllVoitureRecords()]"
);return
voitureDao.recupToutesVoitures
(
);}
public
VoitureaddVoiture
(
String marque, String modele, String categorie, String couleur, String prix){
LOG.info
(
getClass
(
).getSimpleName
(
)+
"[addVoiture("
+
marque+
","
+
modele+
","
+
categorie+
","
+
couleur+
","
+
prix+
")]"
);return
voitureDao.ajouterVoiture
(
marque, modele, categorie, couleur, prix);}
public
void
setVoitureDao
(
final
VoitureDao voitureDao){
this
.voitureDao=
voitureDao;}
}
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.xmlSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.<?xml version="1.0" encoding="UTF-8"?>
<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"
>
<modelVersion>
4.0.0</modelVersion>
<parent>
<artifactId>
garageWS</artifactId>
<groupId>
com.exemple.garageWS</groupId>
<version>
1.0.0</version>
</parent>
<artifactId>
garageWS_metier</artifactId>
<version>
1.0.0</version>
<packaging>
bundle</packaging>
<name>
garageWS_metier Blueprint Bundle</name>
<description>
garageWS_metier OSGi blueprint bundle project.</description>
<dependencies>
<dependency>
<groupId>
${project.groupId}</groupId>
<artifactId>
garageWS_modele</artifactId>
<version>
[1.0.0,2.0)</version>
<type>
bundle</type>
</dependency>
<dependency>
<groupId>
org.slf4j</groupId>
<artifactId>
slf4j-api</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>
org.apache.felix</groupId>
<artifactId>
maven-bundle-plugin</artifactId>
<version>
3.2.0</version>
<extensions>
true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>
${project.artifactId}</Bundle-SymbolicName>
<Bundle-Version>
${project.version}</Bundle-Version>
<Export-Package>
com.exemple.garageWS.metier*;version=${project.version}</Export-Package>
<Import-Package>
*</Import-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>
org.apache.maven.plugins</groupId>
<artifactId>
maven-compiler-plugin</artifactId>
<configuration>
<source>
1.8</source>
<target>
1.8</target>
<maxmem>
256M</maxmem>
</configuration>
</plugin>
</plugins>
</build>
</project>
-
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.xmlSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.<?xml version="1.0" encoding="UTF-8"?>
<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"
>
<modelVersion>
4.0.0</modelVersion>
<groupId>
com.exemple.garageWS</groupId>
<artifactId>
garageWS</artifactId>
<version>
1.0.0</version>
<packaging>
pom</packaging>
<modules>
<module>
garageWS_modele</module>
<module>
garageWS_metier</module>
<module>
garageWS_jaxws</module>
</modules>
<properties>
<jpa.version>
2.1.1</jpa.version>
<osgi.log.version>
1.3.0</osgi.log.version>
<slf4j-version>
1.6.1</slf4j-version>
<project.build.outputEncoding>
UTF-8</project.build.outputEncoding>
<project.build.sourceEncoding>
UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<!-- Module utile au sous-module "garageWS_metier" -->
<dependency>
<groupId>
${project.groupId}</groupId>
<artifactId>
garageWS_modele</artifactId>
</dependency>
<!-- JPA -->
<dependency>
<groupId>
org.eclipse.persistence</groupId>
<artifactId>
javax.persistence</artifactId>
<version>
${jpa.version}</version>
<scope>
provided</scope>
<!-- Le container ou le JDK devra fournir la dependance lors de l'execution -->
</dependency>
<dependency>
<groupId>
org.osgi</groupId>
<artifactId>
org.osgi.service.log</artifactId>
<version>
${osgi.log.version}</version>
<scope>
provided</scope>
<!-- Le container ou le JDK devra fournir la dependance lors de l'execution -->
</dependency>
<dependency>
<groupId>
org.slf4j</groupId>
<artifactId>
slf4j-api</artifactId>
<version>
${slf4j-version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</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.
-
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 » ; - Modifiez le fichier « garageWS_metier/blueprint.xml » de la manière suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
<?xml version="1.0" encoding="UTF-8"?>
<blueprint
xmlns
=
"http://www.osgi.org/xmlns/blueprint/v1.0.0"
default-activation
=
"lazy"
>
<!-- Definition de l'enregistrement du service dans le registre de services OSGI. On renseigne le bean qui fournit le service via l'attribut ref -->
<service
ref
=
"voitureMetier"
interface
=
"com.exemple.garageWS.metier.VoitureMetier"
>
<service-properties>
<entry
key
=
"service.exported.interfaces"
value
=
"*"
/>
</service-properties>
</service>
<!-- 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
(passage par reference local versus passage par valeur distant). Pour eviter qu'une application appelle accidentellement un service exporte qui est uniquement designe
par un appel par "passage de valeur", il faut cacher le lookup local.
La solution est d'exporter le meme bean deux fois : une fois pour les appels distants et une autre pour les appels locaux.
En d'autres termes, on ajoute un autre element avec la meme configuration mais sans la propriete "service.exported.interfaces"-->
<service
ref
=
"voitureMetier"
interface
=
"com.exemple.garageWS.metier.VoitureMetier"
/>
<bean
id
=
"voitureMetier"
class
=
"com.exemple.garageWS.metier.VoitureMetierImpl"
>
<property
name
=
"voitureDao"
ref
=
"voitureDao"
/>
</bean>
<!-- Definition des dependances -->
<reference
id
=
"voitureDao"
interface
=
"com.exemple.garageWS.modele.dao.VoitureDao"
/>
</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 SOAPSimple Object Access Protocol.
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 OSGIOpen Services Gateway Initiative : il ne s'agira que d'un bundle qui déclarera notre service web SOAPSimple Object Access Protocol. Nous verrons, dans le paragraphe « V Export du service SOAP en service OSGI » comment utiliser ce service comme un service OSGIOpen Services Gateway Initiative.
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 SOAPSimple Object Access Protocol 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 .
-
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 » :
-
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 -
Cliquez sur « Finish ».
Le nouveau sous-module est désormais créé dans l'explorateur de projet : -
Renommez les fichiers « MyService.java » et « MyServiceImpl.java » respectivement en « VoitureService.java » et « VoitureServiceImpl.java » ;
-
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.javaSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.package
com.exemple.garageWS.jaxws;import
java.util.List;import
javax.jws.WebService;import
com.exemple.garageWS.modele.Voiture;@WebService
public
interface
VoitureService{
public
VoituregetVoiture
(
final
int
voitureId);public
List<
Voiture>
getAllVoiture
(
);public
VoitureajoutVoiture
(
String marque, String modele, String categorie, String couleur, String prix);}
Remarquez la présence de l'annotation
@WebService
pour 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. -
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.xmlSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.<?xml version="1.0" encoding="UTF-8"?>
<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"
>
<modelVersion>
4.0.0</modelVersion>
<parent>
<artifactId>
garageWS</artifactId>
<groupId>
com.exemple.garageWS</groupId>
<version>
1.0.0</version>
</parent>
<artifactId>
garageWS_jaxws</artifactId>
<version>
1.0.0</version>
<packaging>
bundle</packaging>
<name>
garageWS_jaxws Blueprint Bundle</name>
<description>
garageWS_jaxws OSGi blueprint bundle project.</description>
<dependencies>
<dependency>
<groupId>
${project.groupId}</groupId>
<artifactId>
garageWS_metier</artifactId>
<version>
[1.0.0,2.0)</version>
<type>
bundle</type>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>
org.apache.felix</groupId>
<artifactId>
maven-bundle-plugin</artifactId>
<version>
3.2.0</version>
<extensions>
true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>
${project.artifactId}</Bundle-SymbolicName>
<Bundle-Version>
${project.version}</Bundle-Version>
<Export-Package>
com.exemple.garageWS.jaxws*;version=${project.version}</Export-Package>
<Import-Package>
*</Import-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>
org.apache.maven.plugins</groupId>
<artifactId>
maven-compiler-plugin</artifactId>
<configuration>
<source>
1.8</source>
<target>
1.8</target>
<maxmem>
256M</maxmem>
</configuration>
</plugin>
</plugins>
</build>
</project>
-
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.xmlSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.<
?xml version=
"1.0"
encoding=
"UTF-8"
?>
<
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"
>
<
modelVersion>
4.0.0
</
modelVersion>
<
groupId>
com.exemple.garageWS</
groupId>
<
artifactId>
garageWS</
artifactId>
<
version>
1.0.0
</
version>
<
packaging>
pom</
packaging>
<
modules>
<
module
>
garageWS_modele</
module
>
<
module
>
garageWS_metier</
module
>
<
module
>
garageWS_jaxws</
module
>
</
modules>
<
properties>
<
jpa.version>
2.1.1
</
jpa.version>
<
osgi.log.version>
1.3.0
</
osgi.log.version>
<
slf4j-
version>
1.6.1
</
slf4j-
version>
<
project.build.outputEncoding>
UTF-
8
</
project.build.outputEncoding>
<
project.build.sourceEncoding>
UTF-
8
</
project.build.sourceEncoding>
</
properties>
<
dependencyManagement>
<
dependencies>
<!--
Module utile au sous-
module
"garageWS_metier"
-->
<
dependency>
<
groupId>
${
project.groupId}</
groupId>
<
artifactId>
garageWS_modele</
artifactId>
</
dependency>
<!--
Module utile au sous-
module
"garageWS_jaxws"
-->
<
dependency>
<
groupId>
${
project.groupId}</
groupId>
<
artifactId>
garageWS_metier</
artifactId>
</
dependency>
<!--
JPA-->
<
dependency>
<
groupId>
org.eclipse.persistence</
groupId>
<
artifactId>
javax.persistence</
artifactId>
<
version>
${
jpa.version}</
version>
<
scope>
provided</
scope>
<!--
Le container ou le JDK devra fournir la dependance lors de l'execution -->
</
dependency>
<
dependency>
<
groupId>
org.osgi</
groupId>
<
artifactId>
org.osgi.service.log</
artifactId>
<
version>
${
osgi.log.version}</
version>
<
scope>
provided</
scope>
<!--
Le container ou le JDK devra fournir la dependance lors de l'execution -->
</
dependency>
<
dependency>
<
groupId>
org.slf4j</
groupId>
<
artifactId>
slf4j-
api</
artifactId>
<
version>
${
slf4j-
version}</
version>
</
dependency>
</
dependencies>
</
dependencyManagement>
</
project>
-
Modifiez désormais la déclaration de la classe « VoitureServiceImpl » dans le fichier « VoitureServiceImpl.java » en y copiant-collant le code ci-dessous :
VoitureServiceImpl.javaSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.package
com.exemple.garageWS.jaxws;import
java.util.List;import
com.exemple.garageWS.metier.VoitureMetier;import
com.exemple.garageWS.modele.Voiture;import
org.slf4j.Logger;import
org.slf4j.LoggerFactory;public
class
VoitureServiceImplimplements
VoitureService{
private
VoitureMetier voitureMetier;//Recuperation du logger de la classe VoitureMetierImpl
private
static
final
Logger LOG=
LoggerFactory.getLogger
(
VoitureMetier.class
);public
VoituregetVoiture
(
int
voitureId){
LOG.info
(
getClass
(
).getSimpleName
(
)+
"[getVoiture({})]"
, voitureId);return
voitureMetier.getVoitureById
(
voitureId);}
public
List<
Voiture>
getAllVoiture
(
){
LOG.info
(
getClass
(
).getSimpleName
(
)+
"[getAllVoiture()]"
);return
voitureMetier.getAllVoitureRecords
(
);}
public
VoitureajoutVoiture
(
String marque, String modele, String categorie, String couleur, String prix){
LOG.info
(
getClass
(
).getSimpleName
(
)+
"[ajoutVoiture("
+
marque+
","
+
modele+
","
+
categorie+
","
+
couleur+
","
+
prix+
")]"
);return
voitureMetier.addVoiture
(
marque, modele, categorie, couleur, prix);}
public
void
setVoitureMetier
(
final
VoitureMetier voitureMetier){
this
.voitureMetier=
voitureMetier;}
}
-
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.xmlSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.<?xml version="1.0" encoding="UTF-8"?>
<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"
>
<modelVersion>
4.0.0</modelVersion>
<parent>
<artifactId>
garageWS</artifactId>
<groupId>
com.exemple.garageWS</groupId>
<version>
1.0.0</version>
</parent>
<artifactId>
garageWS_jaxws</artifactId>
<version>
1.0.0</version>
<packaging>
bundle</packaging>
<name>
garageWS_jaxws Blueprint Bundle</name>
<description>
garageWS_jaxws OSGi blueprint bundle project.</description>
<dependencies>
<dependency>
<groupId>
${project.groupId}</groupId>
<artifactId>
garageWS_metier</artifactId>
<version>
[1.0.0,2.0)</version>
<type>
bundle</type>
</dependency>
<dependency>
<groupId>
org.apache.cxf</groupId>
<artifactId>
cxf-rt-frontend-jaxws</artifactId>
</dependency>
<dependency>
<groupId>
org.apache.cxf</groupId>
<artifactId>
cxf-rt-transports-http</artifactId>
</dependency>
<dependency>
<groupId>
org.slf4j</groupId>
<artifactId>
slf4j-api</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>
org.apache.felix</groupId>
<artifactId>
maven-bundle-plugin</artifactId>
<version>
3.2.0</version>
<extensions>
true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>
${project.artifactId}</Bundle-SymbolicName>
<Bundle-Version>
${project.version}</Bundle-Version>
<Export-Package>
com.exemple.garageWS.jaxws*;version=${project.version}</Export-Package>
<Import-Package>
*</Import-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>
org.apache.maven.plugins</groupId>
<artifactId>
maven-compiler-plugin</artifactId>
<configuration>
<source>
1.8</source>
<target>
1.8</target>
<maxmem>
256M</maxmem>
</configuration>
</plugin>
</plugins>
</build>
</project>
-
Modifiez le fichier « garageWS/pom.xml » en conséquence :
garageWS/pom.xmlSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.<?xml version="1.0" encoding="UTF-8"?>
<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"
>
<modelVersion>
4.0.0</modelVersion>
<groupId>
com.exemple.garageWS</groupId>
<artifactId>
garageWS</artifactId>
<version>
1.0.0</version>
<packaging>
pom</packaging>
<modules>
<module>
garageWS_modele</module>
<module>
garageWS_metier</module>
<module>
garageWS_jaxws</module>
</modules>
<properties>
<cxf.version>
3.1.10</cxf.version>
<jpa.version>
2.1.1</jpa.version>
<osgi.log.version>
1.3.0</osgi.log.version>
<slf4j-version>
1.6.1</slf4j-version>
<project.build.outputEncoding>
UTF-8</project.build.outputEncoding>
<project.build.sourceEncoding>
UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<!-- Module utile au sous-module "garageWS_metier" -->
<dependency>
<groupId>
${project.groupId}</groupId>
<artifactId>
garageWS_modele</artifactId>
</dependency>
<!-- Module utile au sous-module "garageWS_jaxws" -->
<dependency>
<groupId>
${project.groupId}</groupId>
<artifactId>
garageWS_metier</artifactId>
</dependency>
<!-- JPA -->
<dependency>
<groupId>
org.eclipse.persistence</groupId>
<artifactId>
javax.persistence</artifactId>
<version>
${jpa.version}</version>
<scope>
provided</scope>
<!-- Le container ou le JDK devra fournir la dependance lors de l'execution -->
</dependency>
<dependency>
<groupId>
org.osgi</groupId>
<artifactId>
org.osgi.service.log</artifactId>
<version>
${osgi.log.version}</version>
<scope>
provided</scope>
<!-- Le container ou le JDK devra fournir la dependance lors de l'execution -->
</dependency>
<dependency>
<groupId>
org.slf4j</groupId>
<artifactId>
slf4j-api</artifactId>
<version>
${slf4j-version}</version>
<scope>
provided</scope>
<!-- Le container ou le JDK devra fournir la dependance lors de l'execution -->
</dependency>
<dependency>
<groupId>
org.apache.cxf</groupId>
<artifactId>
cxf-rt-frontend-jaxws</artifactId>
<version>
${cxf.version}</version>
<scope>
provided</scope>
<!-- Le container ou le JDK devra fournir la dependance lors de l'execution -->
</dependency>
<dependency>
<groupId>
org.apache.cxf</groupId>
<artifactId>
cxf-rt-transports-http</artifactId>
<version>
${cxf.version}</version>
<scope>
provided</scope>
<!-- Le container ou le JDK devra fournir la dependance lors de l'execution -->
</dependency>
</dependencies>
</dependencyManagement>
</project>
- 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 :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
<?xml version="1.0" encoding="UTF-8"?>
<blueprint
xmlns
=
"http://www.osgi.org/xmlns/blueprint/v1.0.0"
default-activation
=
"lazy"
xmlns
:
cxf
=
"http://cxf.apache.org/blueprint/core"
xmlns
:
jaxws
=
"http://cxf.apache.org/blueprint/jaxws"
>
<!-- Bus utilise par le endpoint jaxws -->
<
cxf
:
bus
id
=
"voitureServiceBus"
>
<
cxf
:
features>
<
cxf
:
logging/>
<!-- On active les logs dans ce bus -->
</
cxf
:
features>
<!-- Dans le cas ou on veut mettre un interceptor-->
<!-- <cxf:features>
<bean class="org.apache.cxf.interceptor.security.JAASAuthenticationFeature">
<property name="reportFault" value="true"/>
</bean>
</cxf:features>
-->
</
cxf
:
bus>
<bean
id
=
"voitureServiceImpl"
class
=
"com.exemple.garageWS.jaxws.VoitureServiceImpl"
>
<property
name
=
"voitureMetier"
ref
=
"voitureMetier"
/>
</bean>
<!-- Definition des dependances -->
<reference
id
=
"voitureMetier"
interface
=
"com.exemple.garageWS.metier.VoitureMetier"
/>
<!-- Publication du service SOAP -->
<!-- implementor = Nom de la reference du bean sous la forme "#reference_bean"-->
<!-- address = Adresse sur laquelle est publie le service web -->
<!-- implementorClass = Nom de la classe de l'implementeur -->
<
jaxws
:
endpoint
implementor
=
"#voitureServiceImpl"
address
=
"/voitureService"
implementorClass
=
"com.exemple.garageWS.jaxws.VoitureServiceImpl"
/>
</blueprint>
Notre service web SOAPSimple Object Access Protocol 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 ».
-
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. -
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.xmlSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.<?xml version="1.0" encoding="UTF-8"?>
<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"
>
<modelVersion>
4.0.0</modelVersion>
<parent>
<artifactId>
garageWS</artifactId>
<groupId>
com.exemple.garageWS</groupId>
<version>
1.0.0</version>
</parent>
<artifactId>
garageWS_kar</artifactId>
<version>
1.0.0</version>
<packaging>
kar</packaging>
<name>
garageWS_kar-feature</name>
<description>
garageWS_kar details</description>
<properties>
<karaf.version>
4.1.0</karaf.version>
</properties>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>
org.apache.karaf.tooling</groupId>
<artifactId>
karaf-maven-plugin</artifactId>
<version>
${karaf.version}</version>
<extensions>
true</extensions>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>
org.apache.karaf.tooling</groupId>
<artifactId>
karaf-maven-plugin</artifactId>
<configuration>
<startLevel>
50</startLevel>
<aggregateFeatures>
true</aggregateFeatures>
<checkDependencyChange>
true</checkDependencyChange>
<failOnDependencyChange>
false</failOnDependencyChange>
<logDependencyChanges>
true</logDependencyChanges>
<overwriteChangedDependencies>
true</overwriteChangedDependencies>
</configuration>
</plugin>
</plugins>
</build>
</project>
Ne tenez pas compte de l'erreur de compilation concernant ce fichier « pom.xml » dans Eclipse.
-
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 »Sélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.<?xml version="1.0" encoding="UTF-8"?>
<features
name
=
"${project.artifactId}-${project.version}"
xmlns
=
"http://karaf.apache.org/xmlns/features/v1.3.0"
>
<!-- Ajout de features au repository -->
<repository>
mvn:org.apache.cxf.karaf/apache-cxf/3.1.10/xml/features</repository>
<!-- Concernant PostgreSQL, la version pax-jdbc-features/1.0.0 embarquee dans Karaf 4.1.0 ne propose que la DataSourceFactory dont
osgi.jdbc.driver.class = org.postgreSQL.Driver. On ne dispose pas de org.postgreSQL.Driver-pool pour gerer les pools de connexion
ni de org.postgreSQL.Driver-pool-xa pour gerer les transactions XA.
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 -->
<repository>
mvn:org.ops4j.pax.jdbc/pax-jdbc-features/0.9.0/xml/features</repository>
<!-- hibernate-osgi/5.2.6.Final embarquee dans Karaf 4.1.0 est boguee (https://hibernate.atlassian.net/browse/HHH-11309).
On installe donc la version hibernate-osgi/5.2.8.Final dans le repository avant de l'installer-->
<repository>
mvn:org.hibernate/hibernate-osgi/5.2.8.Final/xml/karaf</repository>
<!-- jpa-features/2.5.0 embarquee dans Karaf 4.1.0 entraine l'erreur :
java.lang.ClassCastException: org.hibernate.osgi.OsgiPersistenceProvider cannot be cast to javax.persistence.spi.PersistenceProvider:
On la remplace donc par la version jpa-features/2.4.0 -->
<repository>
mvn:org.apache.aries.jpa/jpa-features/2.4.0/xml/features</repository>
<feature
name
=
'${project.artifactId}'
description
=
'${project.name}'
version
=
'${project.version}'
>
<details>
${project.description}</details>
<!-- 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.
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
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.
"missing requirement [garageWS_modele/1.0.0] osgi.service; effective:=active; filter:="(objectClass=org.osgi.service.log.LogService)"]"
Ceci se produira tant que le bundle pax-login embarque dans Karaf n'est pas mis a jour.
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-->
<capability>
osgi.service;effective:=active;objectClass=org.osgi.service.log.LogService</capability>
<!-- Installation de la feature cxf -->
<feature
version
=
"3.1.10"
>
cxf</feature>
<!-- Installation de pax-jdbc pour creer les DataSourceFactory -->
<!-- La creation des DataSources se fait par depot d'un fichier org.ops4j.datasource-*.cfg
(ou "*" doit etre remplace par le nom de la DataSource) dans le repertoire "/etc" de Karaf -->
<feature
version
=
"0.9.0"
>
pax-jdbc</feature>
<feature
version
=
"0.9.0"
>
pax-jdbc-config</feature>
<feature
version
=
"0.9.0"
>
pax-jdbc-pool-dbcp2</feature>
<feature
version
=
"0.9.0"
>
pax-jdbc-postgresql</feature>
<!-- Installe le driver PostgreSQL -->
<!-- Installation de hibernate-osgi 5.2.8.Final -->
<feature
version
=
"5.2.8.Final"
>
hibernate-orm</feature>
<!-- Installation de la feature jdbc embarquee dans Karaf pour interroger les DataSources depuis l'invite de commande Karaf -->
<feature>
jdbc</feature>
<!-- Installation de la feature jpa embarquee dans Karaf-->
<feature
version
=
"2.4.0"
>
jpa</feature>
<!-- <feature version="1.3.0">transaction</feature>--><!-- Pour Karaf 4.0.4 -->
<!-- <feature version="1.3.1">transaction</feature>-->
<!-- Pour Karaf 4.0.8 -->
<feature
version
=
"2.0.0"
>
transaction</feature>
<!-- Pour Karaf 4.1.0 -->
<!-- <feature version="4.0.4">jndi</feature>--><!-- Pour Karaf 4.0.4 -->
<!-- <feature version="4.0.8">jndi</feature>--><!-- Pour Karaf 4.0.8 -->
<feature
version
=
"4.1.0"
>
jndi</feature>
<!-- Pour Karaf 4.1.0 -->
<!-- Installation de la feature http-whiteboard embarquee dans Karaf pour publier les servlets-->
<!-- <feature>http-whiteboard</feature>-->
<!-- On place, dans l'archive, les dependances Maven apparaissant dans le fichier garageWS/pom.xml
et dont les services ne sont pas encore dans Karaf (commande service:list)-->
<bundle>
mvn:org.slf4j/slf4j-api/1.6.1</bundle>
<bundle>
mvn:org.slf4j/slf4j-simple/1.6.1</bundle>
<!-- On renseigne les bundles que nous avons developpes et qui seront utiles au service web
le chemin de ces bundle est de la forme "mvn:groupId/artifactId/version" ou :
- groupeId est la valeur de la balise "/project/parent/groupId" du fichier "pom.xml" de chaque module
- artefactId est la valeur de la balise "/project/artefactId" du fichier "pom.xml" de chaque module
- version est la valeur de la balise "/project/parent/version" du fichier "pom.xml" de chaque module -->
<bundle>
mvn:com.exemple.garageWS/garageWS_modele/1.0.0</bundle>
<bundle>
mvn:com.exemple.garageWS/garageWS_metier/1.0.0</bundle>
<bundle>
mvn:com.exemple.garageWS/garageWS_jaxws/1.0.0</bundle>
</feature>
</features>
-
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 » ;
-
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 ;
-
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 » ;
-
Placez l'archive « kar » dans le répertoire « deploy » puis démarrez Karaf ;
-
Vous pouvez consulter les « logs » de Karaf dans le répertoire « /data/log » de votre distribution de Karaf ;
-
Si tout s'est bien passé, vous pouvez désormais interroger votre service web SOAPSimple Object Access Protocol par l'intermédiaire d'un client SOAPSimple Object Access Protocol. 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 » : -
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 SOAPSimple Object Access Protocol. Le champ « Project Name » se remplit dès lors automatiquement. Dans notre cas, la fenêtre est la suivante :
-
Appuyez sur le bouton « OK ». Dans le menu de gauche, notre service web ainsi que les méthodes accessibles apparaissent :
-
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 SOAPSimple Object Access Protocol tel que décrit ci-dessous : - 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.
-
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 : -
Remplacez ensuite le contenu du fichier « garageWS_custom_distri/pom.xml » par ceci :
garageWS_custom_distri/pom.xmlSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.<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"
>
<modelVersion>
4.0.0</modelVersion>
<parent>
<groupId>
com.exemple.garageWS</groupId>
<artifactId>
garageWS</artifactId>
<version>
1.0.0</version>
</parent>
<artifactId>
garageWS_custom_distri</artifactId>
<packaging>
karaf-assembly</packaging>
<version>
1.0.0</version>
<name>
Distribution de Karaf personnalisee garageWS</name>
<properties>
<!-- <karaf.version>4.0.4</karaf.version>-->
<!-- <karaf.version>4.0.8</karaf.version>-->
<karaf.version>
4.1.0</karaf.version>
</properties>
<!-- Import Karaf POM pour utiliser la version correcte des dependences Karaf -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>
org.apache.karaf</groupId>
<artifactId>
karaf</artifactId>
<version>
${karaf.version}</version>
<type>
pom</type>
<scope>
import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<!-- La valeur de la balise <scope> est "compile" donc toutes les features sont installees dans le fichier startup.properties
et le repository des features n'est pas ajoute dans le fichier etc/org.apache.karaf.features.cfg.
Le fichier etc/startup.properties est utilise pour definir les services noyaux et leur ordre de demarrage dans Karaf-->
<groupId>
org.apache.karaf.features</groupId>
<artifactId>
framework</artifactId>
<version>
${karaf.version}</version>
<type>
kar</type>
<scope>
compile</scope>
</dependency>
<dependency>
<!-- La valeur de la balise <scope> est "runtime" donc le repository feature est liste dans le fichier etc/org.apache.karaf.features.cfg
et les features seront installees dans le repertoire system. Ceci importe les features de Karaf standard décrits dans
https://repo1.maven.org/maven2/org/apache/karaf/features/standard/4.0.1/standard-4.0.1-features.xml
Ces features sont installees ci-dessous : dans la configuration du plugin karaf-maven-plugin-->
<groupId>
org.apache.karaf.features</groupId>
<artifactId>
standard</artifactId>
<classifier>
features</classifier>
<type>
xml</type>
<version>
${karaf.version}</version>
<scope>
runtime</scope>
</dependency>
<!-- Les features jdbc et hibernate ne sont pas incluses dans Karaf standard, elles sont dans Karaf enterprise donc on recupere Karaf enterprise
qui inclut aussi Karaf standard -->
<dependency>
<groupId>
org.apache.karaf.features</groupId>
<artifactId>
enterprise</artifactId>
<version>
${karaf.version}</version>
<classifier>
features</classifier>
<type>
xml</type>
<scope>
runtime</scope>
</dependency>
<!-- Installation de la feature propre a notre projet. Cette feature a ete generee par le module ""module-kar" -->
<dependency>
<groupId>
${project.groupId}</groupId>
<artifactId>
garageWS_kar</artifactId>
<version>
${project.version}</version>
<classifier>
features</classifier>
<type>
xml</type>
<scope>
runtime</scope>
</dependency>
<!-- Declarations des dependances necessaires a notre projet -->
<dependency>
<groupId>
${project.groupId}</groupId>
<artifactId>
garageWS_modele</artifactId>
<version>
${project.version}</version>
<scope>
runtime</scope>
</dependency>
<dependency>
<groupId>
${project.groupId}</groupId>
<artifactId>
garageWS_metier</artifactId>
<version>
${project.version}</version>
<scope>
runtime</scope>
</dependency>
<dependency>
<groupId>
${project.groupId}</groupId>
<artifactId>
garageWS_jaxws</artifactId>
<version>
${project.version}</version>
<scope>
runtime</scope>
</dependency>
</dependencies>
<build>
<!-- Inclusion des ressources dans la distribution -->
<resources>
<!-- Le fichier contenant la configuration de notre dataSource sera localise dans le repertoire "src/main/resources"
afin d'etre copie dans le repertoire "etc" de Karaf -->
<resource>
<directory>
src/main/resources</directory>
<filtering>
false</filtering>
<includes>
<include>
**/*</include>
</includes>
</resource>
<!-- Le repertoire "src/main/filtered-resources" contient les fichiers qui incluent des variables denotees par "${...}".
Ces variables peuvent venir des proprietes du systeme, des proprietes du projet...
Le fichier branding.properties contient de telles variables. Il sera copie, comme les fichiers qui se trouvent dans le repertoire
"src/main/resources" dans le repertoire "etc" de Karaf.
Voir le lien : http://maven.apache.org/plugins/maven-resources-plugin/examples/filter.html -->
<resource>
<directory>
src/main/filtered-resources</directory>
<filtering>
true</filtering>
<includes>
<include>
**/*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>
org.apache.karaf.tooling</groupId>
<artifactId>
karaf-maven-plugin</artifactId>
<version>
${karaf.version}</version>
<extensions>
true</extensions>
<configuration>
<!-- La balise <bootFeatures> permet de demander a Karaf lors de son demarrage de demarrer les features.
Le nom des features est obtenu dans le fichier features.xml dans la section <dependencies>.
-->
<bootFeatures>
<!-- Features liees a Karaf standard -->
<feature>
wrap</feature>
<feature>
shell</feature>
<feature>
shell-compat</feature>
<feature>
feature</feature>
<feature>
jaas</feature>
<feature>
ssh</feature>
<feature>
management</feature>
<feature>
bundle</feature>
<feature>
config</feature>
<feature>
deployer</feature>
<feature>
diagnostic</feature>
<feature>
feature</feature>
<feature>
instance</feature>
<feature>
kar</feature>
<feature>
log</feature>
<feature>
package</feature>
<feature>
service</feature>
<feature>
system</feature>
<feature>
webconsole</feature>
<!-- Features liees a Karaf entreprise et necessaires a notre projet -->
<feature>
jpa</feature>
<!-- 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"-->
<feature>
pax-jdbc-pool-dbcp2</feature>
<!-- Installation la feature de notre projet -->
<feature>
garageWS_kar</feature>
</bootFeatures>
<!-- <installedFeatures>
installedFeatures installe les features dans le repertoire ${KARAF_HOME}/system directory mais l'utilisateur devra l'installer manuellement via la command feature:install
<feature>pax-jdbc</feature>
<feature>pax-jdbc-config</feature>
<feature>pax-jdbc-pool-dbcp2</feature>
</installedFeatures>-->
</configuration>
</plugin>
</plugins>
</build>
</project>
Remarquez, au passage, que nous avons changé la valeur de la balise
<packaging>
de « pom » à « karaf-assembly ». -
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 » ;
-
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 » ;
-
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) ;
-
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 » :
- 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 SOAPSimple Object Access Protocol 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 SOAPSimple Object Access Protocol, 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 SOAPSimple Object Access Protocol et qui exportera ce client en tant que service OSGIOpen Services Gateway Initiative. Il sera, dès lors, très simple de modifier la méthode d'accès au service web SOAPSimple Object Access Protocol, 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 SOAPSimple Object Access Protocol sera donc interrogeable via un client SOAPSimple Object Access Protocol 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 SOAPSimple Object Access Protocol en consommant le service web SOAPSimple Object Access Protocol et en l'exportant en tant que service OSGIOpen Services Gateway Initiative. 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 ».
-
Cliquez droit sur le projet « garageWS » → « New » → « Project ». Dans la fenêtre qui apparaît, saisissez « maven » dans le filtre puis choisissez « Maven module ».
-
Cliquez sur le bouton « Next » ;
-
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) ».
-
Cliquez sur le bouton « Next » ;
-
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 » :
-
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 :
-
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 » ;
-
Renommez le fichier « my-service.xml » en « blueprint.xml ». L'architecture de votre sous-module se présente désormais ainsi :
-
Modifiez le contenu du fichier « garageWS_proxy/blueprint.xml » avec le contenu ci-dessous :
garageWS_proxy/blueprint.xmlSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.<?xml version="1.0" encoding="UTF-8"?>
<blueprint
xmlns
=
"http://www.osgi.org/xmlns/blueprint/v1.0.0"
default-activation
=
"lazy"
xmlns
:
cm
=
"http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
xmlns
:
jaxws
=
"http://cxf.apache.org/blueprint/jaxws"
>
<
cm
:
property-placeholder
persistent-id
=
"com.exemple.garageWS.proxy"
update-strategy
=
"reload"
>
<
cm
:
default-properties>
<!-- Adresse d'invocation du service -->
<
cm
:
property
name
=
"voitureServiceSoap.URL"
value
=
"http://localhost:8181/cxf/voitureService"
/>
</
cm
:
default-properties>
</
cm
:
property-placeholder>
<!-- Export, en tant que service OSGI, du service SOAP consomme par le client.
Ceci se fait par encapsulation du client du service SOAP dans un service OSGI.
L'interface client web sera ainsi totalement independante de cxf.-->
<!-- Definition de l'enregistrement du service dans le registre de services OSGI. Encapsulation du client du service SOAP dans un service OSGI -->
<service
ref
=
"voitureServiceProxy"
interface
=
"com.exemple.garageWS.jaxws.VoitureService"
/>
<!-- Ajout d'un client pour interroger le service web SOAP -->
<
jaxws
:
client
id
=
"voitureServiceProxy"
serviceClass
=
"com.exemple.garageWS.jaxws.VoitureService"
address
=
"${voitureServiceSoap.URL}"
/>
</blueprint>
-
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. -
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.xmlSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.<?xml version="1.0" encoding="UTF-8"?>
<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"
>
<modelVersion>
4.0.0</modelVersion>
<parent>
<artifactId>
garageWS</artifactId>
<groupId>
com.exemple.garageWS</groupId>
<version>
1.0.0</version>
</parent>
<artifactId>
garageWS_proxy</artifactId>
<version>
1.0.0</version>
<packaging>
bundle</packaging>
<name>
garageWS_proxy Blueprint Bundle</name>
<description>
garageWS_proxy OSGi blueprint bundle project.</description>
<build>
<plugins>
<plugin>
<groupId>
org.apache.felix</groupId>
<artifactId>
maven-bundle-plugin</artifactId>
<version>
3.2.0</version>
<extensions>
true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>
${project.artifactId}</Bundle-SymbolicName>
<Bundle-Version>
${project.version}</Bundle-Version>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
- 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 SOAPSimple Object Access Protocol, 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 SOAPSimple Object Access Protocol (VoitureService). Du fait que notre service web SOAPSimple Object Access Protocol a été transformé en service OSGIOpen Services Gateway Initiative, 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 OSGIOpen Services Gateway Initiative dans la servlet.
La facilité de mise à jour de la méthode d'accès à notre service web SOAPSimple Object Access Protocol réside dans ce procédé d'injection.
-
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 :
-
Supprimez l'interface « MyService.java » puis renommez la classe « MyServiceImpl.java » en « VoitureServlet.java ». Enfin, renommez le fichier « my-service.xml » en « blueprint.xml » ;
-
Faites hériter la classe « VoitureServlet » de la classe « HttpServlet » ;
-
Copiez le code ci-dessous dans le fichier « VoitureServlet.java » :
VoitureServlet.javaSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.package
com.exemple.garageWS.webui;import
java.util.List;import
java.io.IOException;import
javax.servlet.ServletException;import
javax.servlet.ServletOutputStream;import
javax.servlet.http.HttpServlet;import
javax.servlet.http.HttpServletRequest;import
javax.servlet.http.HttpServletResponse;import
com.exemple.garageWS.jaxws.VoitureService;import
com.exemple.garageWS.modele.Voiture;public
class
VoitureServletextends
HttpServlet{
private
static
final
long
serialVersionUID=
-
5896342395456213489
L;private
VoitureService voitureService;@Override
protected
void
doGet
(
HttpServletRequest req, HttpServletResponse resp)throws
ServletException, IOException{
ServletOutputStream os=
resp.getOutputStream
(
); List<
Voiture>
voitures=
voitureService.getAllVoiture
(
); os.println
(
"<html><body>"
); os.println
(
"<h2>Voitures</h2>"
); os.println
(
"<table>"
); os.println
(
"<tr><th>Id</th><th>Marque</th><th>Modèle</th><th>Catégorie</th><th>Couleur</th><th>Prix</th></tr>"
);for
(
Voiture voiture : voitures){
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
(
)));}
os.println
(
"</table>"
); os.println
(
"<h2>Ajouter voiture</h2>"
);//Creation d'un formulaire dont l'action est definie dans le fichier blueprint.xml
os.println
(
"<form name='input' action='/voitureui' method='post'>"
); os.println
(
"<table>"
); os.println
(
"<tr><td>Marque</td><td><input type='text' name='marque'/></td></tr>"
); os.println
(
"<tr><td>Modèle</td><td><input type='text' name='modele'/></td></tr>"
); os.println
(
"<tr><td>Catégorie</td><td><input type='text' name='categorie'/></td></tr>"
); os.println
(
"<tr><td>Couleur</td><td><input type='text' name='couleur'/></td></tr>"
); os.println
(
"<tr><td>Prix</td><td><input type='text' name='prix'/></td></tr>"
); os.println
(
"<tr><td colspan='2'><input type='submit' value='Ajouter'/></td></tr>"
); os.println
(
"</form>"
); os.println
(
"</table>"
); os.println
(
"</body></html>"
);}
/**
* Methode permettant de recuperer ce qui est envoye par le formulaire (en POST) et d'enregistrer une nouvelle voiture en base
*
@param
req
*
@param
resp
*
@throws
ServletException
*
@throws
IOException
*/
@Override
protected
void
doPost
(
HttpServletRequest req, HttpServletResponse resp)throws
ServletException, IOException{
String marque=
req.getParameter
(
"marque"
); String modele=
req.getParameter
(
"modele"
); String categorie=
req.getParameter
(
"categorie"
); String couleur=
req.getParameter
(
"couleur"
); String prix=
req.getParameter
(
"prix"
); voitureService.ajoutVoiture
(
marque, modele, categorie, couleur, prix); resp.sendRedirect
(
"/voitureui"
);}
public
void
setVoitureService
(
VoitureService voitureService){
this
.voitureService=
voitureService;}
}
-
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.xmlSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.<?xml version="1.0" encoding="UTF-8"?>
<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"
>
<modelVersion>
4.0.0</modelVersion>
<parent>
<artifactId>
garageWS</artifactId>
<groupId>
com.exemple.garageWS</groupId>
<version>
1.0.0</version>
</parent>
<artifactId>
garageWS_webui</artifactId>
<version>
1.0.0</version>
<packaging>
bundle</packaging>
<name>
garageWS_webui Blueprint Bundle</name>
<description>
garageWS_webui OSGi blueprint bundle project.</description>
<dependencies>
<dependency>
<groupId>
javax.servlet</groupId>
<artifactId>
servlet-api</artifactId>
<version>
2.5</version>
</dependency>
<dependency>
<groupId>
${project.groupId}</groupId>
<artifactId>
garageWS_jaxws</artifactId>
<version>
[1.0.0,2.0)</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>
org.apache.felix</groupId>
<artifactId>
maven-bundle-plugin</artifactId>
<version>
3.2.0</version>
<extensions>
true</extensions>
</plugin>
</plugins>
</build>
</project>
-
Modifiez le fichier « garageWS_webui/blueprint.xml » pour injecter notre service SOAPSimple Object Access Protocol :
garageWS_webui/blueprint.xmlSélectionnez1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.<?xml version="1.0" encoding="UTF-8"?>
<blueprint
xmlns
=
"http://www.osgi.org/xmlns/blueprint/v1.0.0"
default-activation
=
"lazy"
>
<!-- Injection d'une implementation de voitureDao dans notre servlet -->
<!-- La feature http-whiteboard installee au prealable dans karaf par l'intermediaire de notre installation personnalisée publiera la servlet
sur la localisation /voitureui.-->
<service
interface
=
"javax.servlet.http.HttpServlet"
ref
=
"voitureServlet"
>
<service-properties>
<entry
key
=
"alias"
value
=
"/voitureui"
/>
</service-properties>
</service>
<!-- On utilise le service SOAP entant que service OSGI -->
<bean
id
=
"voitureServlet"
class
=
"com.exemple.garageWS.webui.VoitureServlet"
>
<property
name
=
"voitureService"
ref
=
"voitureService"
/>
</bean>
<reference
id
=
"voitureService"
interface
=
"com.exemple.garageWS.jaxws.VoitureService"
/>
</blueprint>
-
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 ». -
Ajoutez la ligne après la ligne dans le fichier « garageWS_kar/feature.xml ».
-
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>
. -
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) ;
-
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 » :
-
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 :
-
Votre service web SOAPSimple Object Access Protocol est désormais accessible non seulement par un client SOAPSimple Object Access Protocol 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 : - La saisie dans ce formulaire et l'appui sur le bouton « Ajouter » permet d'envoyer les informations au service web SOAPSimple Object Access Protocol vu en tant que service OSGIOpen Services Gateway Initiative 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 SOAPSimple Object Access Protocol en service OSGIOpen Services Gateway Initiative, il est également possible de transformer un service RESTRepresentational State Transfer (comme celui créé dans le tutoriel « création d'un service web RESTful multibundle pour Karaf sous Eclipse ») en service OSGIOpen Services Gateway Initiative 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 :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
CREATE
SCHEMA
garage;
CREATE
TABLE
garage.voiture
(
id serial
NOT
NULL
,
marque character
varying
(
30
)
NOT
NULL
,
modele character
varying
(
50
)
NOT
NULL
,
categorie character
varying
(
50
)
NOT
NULL
,
colori character
varying
(
50
)
NOT
NULL
,
prix integer
NOT
NULL
,
CONSTRAINT
pk_garage PRIMARY
KEY
(
id)
)
;
INSERT
INTO
garage.voiture(
id, marque, modele, categorie, colori, prix)
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
- Récupérez l'archive à partir du lien ci-dessus.
-
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 :
-
Copiez le répertoire « garageWS » dans votre Workspace Eclipse.
-
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 ».
-
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 ».
- 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.