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

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

Après notre article traitant de la création d'un service web RESTful multibundle pour Karaf sous Eclipse, nous allons voir comment créer, sous Eclipse, un service web SOAPSimple Object Access Protocol OSGIOpen Services Gateway Initiative multibundle destiné à être déployé sous Karaf.

De la même manière qu'un service web RESTRepresentational State Transfer est accessible par un client RESTRepresentational State Transfer, un service web SOAPSimple Object Access Protocol est accessible par un client SOAPSimple Object Access Protocol. Dans ce tutoriel, nous mettrons néanmoins en place un proxy qui nous permettra d'interroger le service SOAPSimple Object Access Protocol à partir d'une servlet en exportant ce service SOAPSimple Object Access Protocol en tant que service OSGIOpen Services Gateway Initiative.

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

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

Pour réagir au contenu de ce tutoriel, un espace de dialogue vous est proposé sur le forum Commentez Donner une note à l'article (5)

Article lu   fois.

L'auteur

Profil ProSite personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

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 :

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 http://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.

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

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

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

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

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

Cliquez sur « Finish ».

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

pom.xml
Sélectionnez
1.
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 :

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

    Image non disponible
  2. Cliquez sur le bouton « Next » ;

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

    Image non disponible
  4. Cliquez sur le bouton « Next » ;

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

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

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

    Image non disponible

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

    Image non disponible

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

    Image non disponible

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

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

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

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

    Voiture.java
    Sélectionnez
    1.
    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 String getMarque() 
        {
            return marque;
        }
    
        public void setMarque(String marque) 
        {
            this.marque = marque;
        }
        
        public String getModele() 
        {
            return modele;
        }
    
        public void setModele(String modele) 
        {
            this.modele = modele;
        }
        
        public String getCategorie() 
        {
            return categorie;
        }
    
        public void setCategorie(String categorie) 
        {
            this.categorie = categorie;
        }
        
        public String getCouleur() 
        {
            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 String toString() 
        {
          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 ;

  10. Complétez le fichier « garageWS_modele/pom.xml » avec la balise <dependencies> et les balises filles associées en indiquant la dépendance Maven associée à 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.xml
    Sélectionnez
    1.
    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.xml
    Sélectionnez
    1.
    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 ».

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

    Image non disponible

    Cliquez sur le bouton « Finish » ;

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

    Image non disponible

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

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

    Image non disponible

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

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

    VoitureDao.java
    Sélectionnez
    1.
    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 Voiture recupVoiture(final int voitureId);
        public List<Voiture> recupToutesVoitures();
        public Voiture ajouterVoiture(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 » ;

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

    VoitureDaoImpl.java
    Sélectionnez
    1.
    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 VoitureDaoImpl implements 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 Voiture recupVoiture(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 Voiture ajouterVoiture(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 » ;

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

    garageWS_modele/pom.xml
    Sélectionnez
    1.
    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.xml
    Sélectionnez
    1.
    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.

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

    Image non disponible

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

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

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

    garageWS_modele/persistence.xml
    Sélectionnez
    1.
    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 
         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>
    
  20. Dans ce fichier « persistence.xml » nous venons de déclarer une « datasource » (une source de données) que nous avons appelée « garage ». Il ne faut donc pas oublier de créer cette source de données. La création d'une source de données se fait extrêmement rapidement dans Karaf par l'utilisation de « pax-jdbc » qui n'est autre qu'une implémentation de service JDBC 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.cfg
    Sélectionnez
    1.
    2.
    3.
    4.
    5.
    6.
    7.
    8.
    osgi.jdbc.driver.name=PostgreSQL JDBC Driver-pool
    serverName=999.999.999.999
    databaseName=garage
    portNumber=4002
    user=olivier
    password=XXXXXXX
    dataSourceName=garage
    pool.maxTotal=10000
    
  21. Il nous faut renseigner Blueprint sur la configuration de notre bundle. Pour cela, on utilise le fichier « my-service.xml ». Dans un souci de clarté, renommez ce fichier, disponible dans le répertoire « src/main/resources/OSGI-INF/blueprint », en « blueprint.xml » comme le présente la capture d'écran ci-dessous :

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

    garageWS_modele/blueprint.xml
    Sélectionnez
    1.
    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>
    
  23. 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 :
garageWS_modele/pom.xml
Sélectionnez
1.
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.

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

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

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

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

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

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

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

    Image non disponible

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

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

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

    garageWS_metier/VoitureMetier.java
    Sélectionnez
    1.
    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 Voiture getVoitureById(int voitureId);
        public List<Voiture> getAllVoitureRecords();
        public Voiture addVoiture(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 » ;

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

    garageWS_metier/pom.xml
    Sélectionnez
    1.
    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 ?

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

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

    VoitureMetierImpl.java
    Sélectionnez
    1.
    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 VoitureMetierImpl implements VoitureMetier 
    {    
        private VoitureDao voitureDao;
        
        //Recuperation du logger de la classe VoitureDaoImpl
        private static final Logger LOG = LoggerFactory.getLogger(VoitureDao.class);
        
        public Voiture getVoitureById(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 Voiture addVoiture(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.xml
    Sélectionnez
    1.
    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>
    
  9. Dans le même temps, il est nécessaire de préciser la version de la bibliothèque « SLF4J ». Comme nous l'avons fait dans le sous-module précédent, nous allons le signaler dans le fichier « garageWS/pom.xml » ;

    garageWS/pom.xml
    Sélectionnez
    1.
    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.

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

  11. Modifiez le fichier « garageWS_metier/blueprint.xml » de la manière suivante :
garageWS_metier/blueprint.xml
Sélectionnez
1.
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 .

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

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

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

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

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

    VoitureService.java
    Sélectionnez
    1.
    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 Voiture getVoiture(final int voitureId);
        public List<Voiture> getAllVoiture();
        public Voiture ajoutVoiture(String marque, String modele, String categorie, String couleur, String prix);
    }
    

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

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

    garageWS_jaxws/pom.xml
    Sélectionnez
    1.
    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>
    
  7. Il est également nécessaire d'indiquer, dans le fichier « garageWS/pom.xml » cette nouvelle dépendance au sous-module « garageWS_metier ». Le contenu du fichier « garageWS/pom.xml » devient donc celui-ci :

    garageWS/pom.xml
    Sélectionnez
    1.
    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>
    
  8. Modifiez désormais la déclaration de la classe « VoitureServiceImpl » dans le fichier « VoitureServiceImpl.java » en y copiant-collant le code ci-dessous :

    VoitureServiceImpl.java
    Sélectionnez
    1.
    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 VoitureServiceImpl implements VoitureService 
    {
        private VoitureMetier voitureMetier; 
        
        //Recuperation du logger de la classe VoitureMetierImpl
        private static final Logger LOG = LoggerFactory.getLogger(VoitureMetier.class);
        
        public Voiture getVoiture(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 Voiture ajoutVoiture(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;
        }
        
    }
    
  9. Suite aux erreurs de compilation apparaissant, modifiez le fichier « garageWS_jaxws » en ajoutant les dépendances aux bibliothèques « jaxws » et « SLF4J » comme ceci :

    garageWS_jaxws/pom.xml
    Sélectionnez
    1.
    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>
    
  10. Modifiez le fichier « garageWS/pom.xml » en conséquence :

    garageWS/pom.xml
    Sélectionnez
    1.
    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>
    
  11. Renommez le fichier « my-service.xml » du module « garageWS_jaxws » en « blueprint.xml ». et modifiez le contenu de ce fichier « blueprint.xml » afin qu'il soit :
garageWS_jaxws/blueprint.xml
Sélectionnez
1.
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 ».

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

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

    garageWS_kar/pom.xml
    Sélectionnez
    1.
    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.

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

    « garageWS_kar/feature.xml »
    Sélectionnez
    1.
    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>
    
  4. Désormais, cliquez droit sur le projet « garageWS » puis choisissez « Run As... » → « Maven build ». La fenêtre ci-dessous apparaît. Saisissez « clean install » dans le champ « Goals » :

    Image non disponible

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

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

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

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

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

  9. Si tout s'est bien passé, vous pouvez désormais interroger votre service web 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 » :

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

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

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

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

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

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

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

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

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

    garageWS_custom_distri/pom.xml
    Sélectionnez
    1.
    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 ».

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

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

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

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

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

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 ».

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

    Image non disponible
  2. Cliquez sur le bouton « Next » ;

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

    Image non disponible
  4. Cliquez sur le bouton « Next » ;

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

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

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

    Image non disponible

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

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

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

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

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

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

    garageWS_proxy/pom.xml
    Sélectionnez
    1.
    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>
    
  12. Ajoutez la ligne <bundle>mvn:com.exemple.garageWS/garageWS_proxy/1.0.0</bundle> après la ligne <bundle>mvn:com.exemple.garageWS/garageWS_jaxws/1.0.0</bundle> dans le fichier « garageWS_kar/feature.xml ».

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

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

Dans le but d'interroger notre service web 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.

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

    Image non disponible

    Vous devriez obtenir ceci dans l'explorateur de projet :

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

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

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

    VoitureServlet.java
    Sélectionnez
    1.
    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 VoitureServlet extends HttpServlet 
    {
        private static final long serialVersionUID = -5896342395456213489L;
        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;
        }
        
    }
    
  5. Vous remarquez des erreurs de compilation dans l'éditeur. Afin de les corriger, comme vous le savez désormais, il vous faut signaler les dépendances nécessaires dans le fichier « garageWS_webui/pom.xml ». Modifiez ce fichier pour qu'il contienne ceci :

    garageWS_webui/pom.xml
    Sélectionnez
    1.
    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>
    
  6. Modifiez le fichier « garageWS_webui/blueprint.xml » pour injecter notre service SOAPSimple Object Access Protocol :

    garageWS_webui/blueprint.xml
    Sélectionnez
    1.
    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>
    
  7. Modifiez le fichier « garageWS/pom.xml » afin de placer la balise <module>garageWS_webui</module> après la balise <module>garageWS_proxy</module> et avant la balise <module>garageWS_kar</module>. Ceci afin que notre servlet soit encapsulée dans l'archive « Kar ».

  8. Ajoutez la ligne après la ligne dans le fichier « garageWS_kar/feature.xml ».

  9. Dans le but que votre servlet fonctionne correctement, il est nécessaire d'installer, dans Karaf, la feature « http-whiteboard ». Pour cela, modifiez le fichier « feature.xml » du sous-module « garageWS_kar » pour y ajouter la balise <feature>http-whiteboard</feature>.

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

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

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

    Image non disponible
  13. 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 :

    Image non disponible
  14. 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 :

Création du schéma et de la table
Sélectionnez
1.
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

  1. Récupérez l'archive à partir du lien ci-dessus.
  2. Extrayez les répertoires et les fichiers de telle manière à avoir « garageWS » comme répertoire racine. L'architecture des fichiers doit être celle-ci :

    Image non disponible
  3. Copiez le répertoire « garageWS » dans votre Workspace Eclipse.

  4. Pour chaque Module Maven que nous avons évoqué dans ce tutoriel (« garageWS_custom_distri », « garageWS_jaxws », « garageWS_kar », « garageWS_metier », « garageWS_modele », « garageWS_proxy » et « garageWS_webui »), dans le menu d'Eclipse, choisissez « File » → « Import » → « General » → « Existing Projects into Workspace »  puis cliquez sur « Next ».

  5. Dans « Select root directory », sélectionnez le chemin du répertoire « garageWS » que vous avez placé dans votre Workspace et choisissez un des modules Maven (répétez cette opération pour tous les autres modules)  puis cliquez sur « Finish ».

  6. Après avoir importé tous les modules, n'oubliez pas d'importer le projet qui englobe tous les modules Maven via « Import » puis « Existing project into workspace » :
Image non disponible

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.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

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