Tutoriel sur le développement de services web étendus avec JAX-WS, Maven et Eclipse

Mickael Baron

L'objectif de cette deuxième série d'exercices est d'apprendre à manipuler l'API JAX-WS pour le développement de services web étendus à partir de la plateforme de développement Java.

Chaque exercice est fourni avec un projet Java Maven contenant des classes et des fichiers de configuration qu'il faudra compléter au fur et à mesure des questions. À la fin de chaque exercice, une solution du projet Java sera donnée.

Buts pédagogiques : transformation d'un POJO Java en service web, génération des artifacts à partir d'une description WSDL, utilisation de l'outillage fourni depuis JavaSE 6, mise en place d'un intercepteur (handler), clients service web en mode synchrone et asynchrone, outils wsgen et wsimport.

Les codes pour les exercices sont disponibles sur le dépôt Git suivant : https://github.com/mickaelbaron/jaxws-tutorial (pour récupérer le code faire : $ git clone https://github.com/mickaelbaron/jaxws-tutorial).

La solution de tous les exercices est disponible sur la branche solution : $ git checkout solution

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

- Titre : Tutoriel sur le développement de services web étendus avec JAX-WS, Maven et Eclipse

- Auteur : Mickael Baron

- Parution : 25 janvier 2016

- Mise à jour : 1 février 2019

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

Sommaire

- Prérequis logiciels

- I. Exercice 1 : Développer un service web suivant une approche montante « Bottom/Up »

- I-A. But

- I-B. Description

- I-C. Étapes à suivre

- II. Exercice 2 : Développer un service web suivant une approche descendante « Top/Down »

- II-A. But

- II-B. Description

- II-C. Étapes à suivre

- III. Exercice 3 : Développer un client de service web en mode synchrone

- III-A. But

- III-B. Description

- III-C. Étapes à suivre

- IV. Exercice 4 : développer un client de service web en mode asynchrone

- IV-A. But

- IV-B. Description

- IV-C. Étapes à suivre

- V. Exercice 5 : Ajouter un intercepteur (handler) à un service web

- V-A. But

- V-B. Description

- V-C. Étapes à suivre

- VI. Exercice 6 : déployer des services web étendus

- VI-A. But

- VI-B. Description

- VI-C. Étapes à suivre pour effectuer un déploiement pour des tests (utilisation de la classe Endpoint)

- VIII-D. Étapes à suivre pour effectuer un déploiement sur le serveur d'applications Tomcat

- VII. Conclusion et remerciements

Prérequis logiciels

Avant de démarrer cette série d'exercices sur l'utilisation de l'API JAX-WS, veuillez préparer votre environnement de développement en installant les outils suivants

- Java via OpenJDK

- Maven

- Eclipse (ou tout autre EDI)

- SOAP-UI

- Docker (exercice 6)

I - Exercice 1 : Développer un service web suivant une approche montante « Bottom/Up »

I-A - But

- Décrire un service web à partir d'une interface Java.

- Implémenter le service web.

- Publier en local et tester le service web via SOAP-UI.

I-B - Description

Le service web de ce premier exercice consiste à fournir des opérations pour la gestion d'un carnet d'adresses simplifié. Une opération pour ajouter une personne, une autre pour récupérer la liste complète et enfin une dernière opération pour récupérer une personne par un nom. Une personne est décrite par un nom (String) et une adresse (String).

I-C - Étapes à suivre

1. package fr.mickaelbaron.jaxwstutorialexercice1;

3. public class Person {

5. private String name;

7. private String address;

9. public Person() {

10. }

12. public Person(String name, String address) {

13. this.name = name;

14. this.address = address;

15. }

17. public String getName() {

18. return name;

19. }

21. public void setName(String name) {

22. this.name = name;

23. }

25. public String getAddress() {

26. return address;

27. }

29. public void setAddress(String address) {

30. this.address = address;

31. }

32. }

- Ajouter les méthodes suivantes dans l'interface NotebookService :

- boolean addPerson(Person p) : ajouter une nouvelle personne ;

- List<Person> getPersons() : récupérer toutes les personnes ;

- Person getPersonAt(String name) : récupérer une personne par son nom.

1. package fr.mickaelbaron.jaxwstutorialexercice1;

3. import java.util.List;

4. import javax.jws.WebService;

6. // TODO: définir l'interface en service web SOAP

7. // en initialisant les attributs `name` et `targetNamespace`.

8. public interface NotebookService {

10. boolean addPerson(Person p);

12. List<Person> getPersons();

14. Person getPersonAt(String name);

15. }

1. package fr.mickaelbaron.jaxwstutorialexercice1;

3. import java.util.List;

4. import javax.jws.WebService;

6. @WebService(name = "NotebookService", targetNamespace="http://jaxwstutorialexercice1.mickaelbaron.fr")

7. public interface NotebookService {

9. boolean addPerson(Person p);

11. List<Person> getPersons();

13. Person getPersonAt(String name);

14. }

- Ajouter une annotation @WebService au niveau de la classe, puis modifier les attributs de l'annotation comme décrit ci-dessous :

- endpointInterface = fr.mickaelbaron.jaxwstutorialexercice1.NotebookService;

- serviceName = NotebookService ;

- portName = NotebookPort.

1. package fr.mickaelbaron.jaxwstutorialexercice1;

3. import java.util.ArrayList;

4. import java.util.List;

6. import javax.jws.WebService;

8. // TODO: définir la classe en service web SOAP

9. // en initialisant les attributs `endpointInterface`, `serviceName` et `portName`.

10. public class NotebookServiceImpl implements NotebookService {

12. private List<Person> persons = new ArrayList<Person>();

14. public NotebookServiceImpl() {

15. persons.add(new Person("Mickael Baron", "Migné-Auxances"));

16. persons.add(new Person("Sébastien Loeb", "France"));

17. }

19. @Override

20. public boolean addPerson(Person p) {

21. System.out.println("addPerson method has been invoked:" + p);

23. if (p == null) throw new NullPointerException("Person is null");

25. try {

26. Thread.sleep(5000);

27. } catch (InterruptedException e) {

28. e.printStackTrace();

29. }

31. return persons.add(p);

32. }

34. @Override

35. public List<Person> getPersons() {

36. System.out.println("getPersons method has been invoked.");

38. return persons;

39. }

41. @Override

42. public Person getPersonAt(String name) {

43. System.out.println("getPersonAt method has been invoked: " + name);

45. if (name == null || name.isEmpty()) throw new NullPointerException("Name is null.");

47. for (Person person : persons) {

48. if (name.equals(person.getName())) return person;

49. }

50. return null;

51. }

52. }

1. package fr.mickaelbaron.jaxwstutorialexercice1;

3. import java.util.ArrayList;

4. import java.util.List;

6. import javax.jws.WebService;

8. @WebService(endpointInterface="fr.mickaelbaron.jaxwstutorialexercice1.NotebookService", serviceName="NotebookService", portName="NotebookPort")

9. public class NotebookServiceImpl implements NotebookService {

11. private List<Person> persons = new ArrayList<Person>();

13. public NotebookServiceImpl() {

14. persons.add(new Person("Mickael Baron", "Migné-Auxances"));

15. persons.add(new Person("Sébastien Loeb", "France"));

16. }

18. @Override

19. public boolean addPerson(Person p) {

20. System.out.println("addPerson method has been invoked:" + p);

22. if (p == null) throw new NullPointerException("Person is null");

24. try {

25. Thread.sleep(5000);

26. } catch (InterruptedException e) {

27. e.printStackTrace();

28. }

30. return persons.add(p);

31. }

33. @Override

34. public List<Person> getPersons() {

35. System.out.println("getPersons method has been invoked.");

37. return persons;

38. }

40. @Override

41. public Person getPersonAt(String name) {

42. System.out.println("getPersonAt method has been invoked: " + name);

44. if (name == null || name.isEmpty()) throw new NullPointerException("Name is null.");

46. for (Person person : persons) {

47. if (name.equals(person.getName())) return person;

48. }

49. return null;

50. }

51. }

- Afin de tester localement ce service web, définir une classe appelée NotebookWebServicePublisher à déposer dans le package déjà créé et saisir le code ci-dessous.

1. public class NotebookWebServicePublisher {

2. public static void main(String[] args) throws ClassNotFoundException {

3. Endpoint.publish("http://localhost:9991/ws/notebookservice", new NotebookServiceImpl());

4. }

5. }

- Exécuter la classe NotebookWebServicePublisher pour démarrer votre service web. Selon la version de Java que vous utilisez, différents messages d'avertissement seront affichés.

- Afficher la description WSDL de votre service web http://localhost:9991/ws/notebookservice?wsdl et comparer le résultat par rapport à ce qui a été défini dans l'interface Java.

Pour tester le comportement du service web, vous utiliserez l'outil SOAP-UI (voir atelier 1).

Nous allons nous intéresser à générer le document WSDL à partir de Maven.

mvn clean package

II - Exercice 2 : Développer un service web suivant une approche descendante « Top/Down »

II-A - But

- Générer les artifacts d'un service web à partir d'une description WSDL.

- Implémenter le service web.

- Publier en local.

- Tester le service web.

- Utilisation de wsimport.

II-B - Description

Dans cet exercice, nous développons un service web à partir de sa description WSDL. Pour cela, nous nous basons sur la description WSDL du service web obtenue à la fin de l'exercice 1. L'intérêt est de montrer que 1) la génération des classes (artifacts) ne donne pas exactement les mêmes classes que celles construites dans l'exercice 1 et 2) la génération est obligatoire.

II-C - Étapes à suivre

1. <plugin>

2. <groupId>com.sun.xml.ws</groupId>

3. <artifactId>jaxws-maven-plugin</artifactId>

4. <version>${jaxws.version}</version>

5. <executions>

6. <execution>

7. <goals>

8. <goal>wsimport</goal>

9. </goals>

10. <configuration>

11. <packageName>fr.mickaelbaron.jaxwstutorialexercice2</packageName>

12. <sourceDestDir>${project.build.directory}/generated-sources/jaxws-wsimport</sourceDestDir>

13. <keep>true</keep>

14. <wsdlUrls>

15. <wsdlUrl>http://localhost:9991/ws/notebookservice?wsdl</wsdlUrl>

16. </wsdlUrls>

17. </configuration>

18. </execution>

19. </executions>

20. </plugin>

- Depuis Eclipse, exécuter la configuration d'exécution appelée wsimport2 (clean and generate-sources). Si vous rencontrez des soucis avec l'intégration Maven sous Eclipse, exécuter la commande shell suivante à la racine de votre projet $ mvn clean generate-sources. Un ensemble de classes sera généré par l'outil wsimport et localisé dans le répertoire ./target/generated-sources/jaxws-wsimport. Ces classes ne doivent pas être modifiées puisqu'elles sont générées automatiquement.

Il reste maintenant à implémenter la classe NotebookServiceImpl. Elle correspond à la classe implémentant le comportement du service web.

- Réutiliser et adapter le code de l'exercice 1 pour compléter cette classe.

III - Exercice 3 : Développer un client de service web en mode synchrone

III-A - But

- Développer un client d'un service web.

- Appel synchrone de service web.

- Outil wsimport et test d'intégration.

III-B - Description

Cet exercice consiste à appeler le service web défini depuis l'exercice 1. Des tests unitaires ont été définis pour invoquer les différentes méthodes.

III-C - Étapes à suivre

1. <<plugin>

2. <groupId>com.sun.xml.ws</groupId>

3. <artifactId>jaxws-maven-plugin</artifactId>

4. <version>${jaxws.version}</version>

5. <executions>

6. <execution>

7. <goals>

8. <goal>wsimport</goal>

9. </goals>

10. <configuration>

11. <packageName>fr.mickaelbaron.jaxwstutorialexercice3</packageName>

12. <sourceDestDir>${project.build.directory}/generated-sources/jaxws-wsimport</sourceDestDir>

13. <keep>true</keep>

14. <wsdlUrls>

15. <wsdlUrl>http://localhost:9991/ws/notebookservice?wsdl</wsdlUrl>

16. </wsdlUrls>

17. </configuration>

18. </execution>

19. </executions>

20. </plugin>

1. package fr.mickaelbaron.jaxwstutorialexercice3;

3. import org.junit.Assert;

4. import org.junit.Before;

5. import org.junit.Test;

7. public class NotebookWebServiceTest {

9. protected NotebookService notebookPort;

11. @Before

12. public void setup() {

13. NotebookService_Service notebookService = new NotebookService_Service();

14. notebookPort = notebookService.getNotebookPort();

15. }

17. @Test

18. public void addPersonTest() {

19. // Given

20. Person myPerson = new Person();

21. myPerson.setName("NewPerson");

22. myPerson.setAddress("NewAddress");

24. // When

25. // TODO: appeler l'opération `addPerson`.

27. // Then

28. Assert.assertTrue(addPerson);

29. }

31. @Test

32. public void getPersonsTest() {

33. // Given

34. // When

35. // TODO: appeler l'opération `getPersons`.

37. // Then

38. Assert.assertTrue(persons.size() >= 2);

39. }

41. @Test

42. public void getPersonAtTest() {

43. // Given

44. // TODO: initialiser une variable à 'Mickael Baron'.

46. // When

47. // TODO: appeler l'opération `getPersonAt` avec la variable précédente.

49. // Then

50. Assert.assertNotNull(personAt);

51. Assert.assertEquals(personAt.getAddress(), "Migné-Auxances");

52. }

53. }

1. package fr.mickaelbaron.jaxwstutorialexercice3;

3. import org.junit.Assert;

4. import org.junit.Before;

5. import org.junit.Test;

7. public class NotebookWebServiceTest {

9. protected NotebookService notebookPort;

11. @Before

12. public void setup() {

13. NotebookService_Service notebookService = new NotebookService_Service();

14. notebookPort = notebookService.getNotebookPort();

15. }

17. @Test

18. public void addPersonTest() {

19. // Given

20. Person myPerson = new Person();

21. myPerson.setName("NewPerson");

22. myPerson.setAddress("NewAddress");

24. // When

25. boolean addPerson = notebookPort.addPerson(myPerson);

27. // Then

28. Assert.assertTrue(addPerson);

29. }

31. @Test

32. public void getPersonsTest() {

33. // Given

34. // When

35. List<Person> persons = notebookPort.getPersons();

37. // Then

38. Assert.assertTrue(persons.size() >= 2);

39. }

41. @Test

42. public void getPersonAtTest() {

43. // Given

44. String name = "Mickael Baron";

46. // When

47. Person personAt = notebookPort.getPersonAt(name);

49. // Then

50. Assert.assertNotNull(personAt);

51. Assert.assertEquals(personAt.getAddress(), "Migné-Auxances");

52. }

53. }

IV - Exercice 4 : développer un client de service web en mode asynchrone

IV-A - But

- Développer un client d'un service web.

- Appel asynchrone de service web.

- Outil wsimport.

- Client lourd via une interface graphique SWING.

IV-B - Description

Cet exercice consiste à appeler le service web défini dans l'exercice 1. Un client lourd défini via une interface graphique SWING est utilisé pour invoquer l'opération addPerson(...) en mode asynchrone. L'intérêt de cet exercice est de montrer comment paramétrer la génération des artifacts via wsimport pour le mode asynchrone.

IV-C - Étapes à suivre

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

2. <bindings

3. wsdlLocation="http://localhost:9991/ws/NotebookService?wsdl"

4. xmlns="http://java.sun.com/xml/ns/jaxws">

5. <enableAsyncMapping>true</enableAsyncMapping>

6. </bindings>

- Compléter le fichier de description Maven pom.xml existant afin de générer les classes artifacts (identique à l'exercice 2 et 3). Pour la prise en compte du fichier binding.xml vous ajouterez ces paramètres dans le contenu de la balise configuration.

1. <configuration>

2. ...

3. <bindingFiles>

4. <bindingFile>

5. ${basedir}/src/main/resources/binding.xml

6. </bindingFile>

7. </bindingFiles>

8. </configuration>

1. package fr.mickaelbaron.jaxwstutorialexercice4;

3. import java.awt.BorderLayout;

4. import java.awt.event.ActionEvent;

5. import java.awt.event.ActionListener;

7. import javax.swing.JButton;

8. import javax.swing.JFrame;

9. import javax.swing.JTextArea;

10. import javax.xml.ws.AsyncHandler;

11. import javax.xml.ws.Response;

13. public class NotebookWebServiceAsynchronousClient extends JFrame {

15. private static final long serialVersionUID = -8095150953761740313L;

17. public NotebookWebServiceAsynchronousClient() {

18. super("NotebookWebServiceAsynchronous Client");

20. this.setLayout(new BorderLayout());

21. final JButton startButton = new JButton("Call addPerson operation");

22. this.getContentPane().add(BorderLayout.NORTH, startButton);

24. final JTextArea textArea = new JTextArea();

25. textArea.setEditable(false);

26. this.getContentPane().add(BorderLayout.CENTER, textArea);

28. startButton.addActionListener(new ActionListener() {

29. @Override

30. public void actionPerformed(ActionEvent e) {

31. startButton.setEnabled(false);

32. Person newPerson = new Person();

33. newPerson.setName("New Person");

34. newPerson.setAddress("Poitiers");

36. textArea.append("addPerson operation has been invoked.\n");

38. // TODO: initialiser un service et un port.

40. // TODO: invoquer de manière asynchrone l'opération `addPerson` à partir du port précédent

41. // et faire appel au code suivant.

42. //     if (!res.isCancelled() && res.isDone()) {

43. //         textArea.append("New Person added.\n");

44. //         startButton.setEnabled(true);

45. //     }

46. }

47. });

49. this.pack();

50. this.setVisible(true);

51. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

52. }

54. public static void main(String[] args) {

55. new NotebookWebServiceAsynchronousClient();

56. }

57. }

1. package fr.mickaelbaron.jaxwstutorialexercice4;

3. import java.awt.BorderLayout;

4. import java.awt.event.ActionEvent;

5. import java.awt.event.ActionListener;

7. import javax.swing.JButton;

8. import javax.swing.JFrame;

9. import javax.swing.JTextArea;

10. import javax.xml.ws.AsyncHandler;

11. import javax.xml.ws.Response;

13. public class NotebookWebServiceAsynchronousClient extends JFrame {

15. private static final long serialVersionUID = -8095150953761740313L;

17. public NotebookWebServiceAsynchronousClient() {

18. super("NotebookWebServiceAsynchronous Client");

20. this.setLayout(new BorderLayout());

21. final JButton startButton = new JButton("Call addPerson operation");

22. this.getContentPane().add(BorderLayout.NORTH, startButton);

24. final JTextArea textArea = new JTextArea();

25. textArea.setEditable(false);

26. this.getContentPane().add(BorderLayout.CENTER, textArea);

28. startButton.addActionListener(new ActionListener() {

29. @Override

30. public void actionPerformed(ActionEvent e) {

31. startButton.setEnabled(false);

32. Person newPerson = new Person();

33. newPerson.setName("New Person");

34. newPerson.setAddress("Poitiers");

36. textArea.append("addPerson operation has been invoked.\n");

38. NotebookService_Service service = new NotebookService_Service();

39. NotebookService port = service.getNoteBookPort();

41. port.addPersonAsync(newPerson, new AsyncHandler<AddPersonResponse>() {

42. @Override

43. public void handleResponse(Response<AddPersonResponse> res) {

44. if (!res.isCancelled() && res.isDone()) {

45. textArea.append("New Person added.\n");

46. startButton.setEnabled(true);

47. }

48. }

49. });

50. }

51. });

53. this.pack();

54. this.setVisible(true);

55. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

56. }

58. public static void main(String[] args) {

59. new NotebookWebServiceAsynchronousClient();

60. }

61. }

- Exécuter le programme, le résultat attendu est celui montré sur la figure ci-dessous.

V - Exercice 5 : Ajouter un intercepteur (handler) à un service web

V-A - But

- Implémentation handler côté serveur.

- Configuration du service web.

- Filtrage par opération.

- Développer un client d'un service web.

V-B - Description

Dans cet exercice un intercepteur est ajouté au service web décrivant le carnet d'adresses. L'intercepteur a pour fonction de filtrer les messages SOAP de telle sorte que le traitement de l'opération getPersons() ne soit pas réalisé.

V-C - Étapes à suivre

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

2. <handler-chains xmlns="http://java.sun.com/xml/ns/javaee">

3. <handler-chain>

4. <handler>

5. <handler-name>fr.mickaelbaron.jaxwstutorialexercice5.SOAPLoggingHandler</handler-name>

6. <handler-class>fr.mickaelbaron.jaxwstutorialexercice5.SOAPLoggingHandler</handler-class>

7. </handler>

8. </handler-chain>

9. </handler-chains>

- Ajouter une nouvelle classe intitulée SOAPLoggingHandler dont le code est défini de la manière suivante.

1. package fr.mickaelbaron.jaxwstutorialexercice5;

3. import java.util.Set;

5. import javax.xml.namespace.QName;

6. import javax.xml.soap.SOAPBody;

7. import javax.xml.soap.SOAPEnvelope;

8. import javax.xml.soap.SOAPException;

9. import javax.xml.ws.handler.MessageContext;

10. import javax.xml.ws.handler.soap.SOAPHandler;

11. import javax.xml.ws.handler.soap.SOAPMessageContext;

13. public class SOAPLoggingHandler implements SOAPHandler<SOAPMessageContext> {

15. @Override

16. public Set<QName> getHeaders() {

17. return null;

18. }

20. @Override

21. public void close(MessageContext context) {

22. }

24. @Override

25. public boolean handleMessage(SOAPMessageContext smc) {

26. Boolean outboundProperty = (Boolean) smc

27. .get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);

29. if (outboundProperty.booleanValue()) {

30. return false;

31. } else {

32. String methodName = getMethodName(smc, true);

33. if (!"getPersons".equals(methodName)) {

34. System.out.println(methodName + ": authorized");

35. return true;

36. } else {

37. System.out.println(methodName + ": not authorized");

38. return false;

39. }

40. }

41. }

43. @Override

44. public boolean handleFault(SOAPMessageContext smc) {

45. return true;

46. }

48. private String getMethodName(SOAPMessageContext context, boolean isRequest) {

49. try {

50. SOAPEnvelope envelope = context.getMessage().getSOAPPart().getEnvelope();

51. SOAPBody body = envelope.getBody();

52. return body.getChildNodes().item(0).getLocalName();

53. } catch (SOAPException e) {

54. e.printStackTrace();

56. return null;

57. }

58. }

59. }

VI - Exercice 6 : déployer des services web étendus

VI-A - But

- Déployer pour des tests.

- Packager un service web dans une archive War.

- Déployer un service web sur le serveur d'applications Java Tomcat.

- Gérer les problèmes de dépendances.

VI-B - Description

Dans cet exercice nous allons réaliser deux types de déploiement d'un service web étendu : un déploiement pour des tests (exécuter depuis un jar) et un déploiement sur le serveur d'applications Tomcat (déployer un fichier war). Nous ferons abstraction de l'environnement de développement Eclipse afin d'être le plus proche de l'environnement de production.

VI-C - Étapes à suivre pour effectuer un déploiement pour des tests (utilisation de la classe Endpoint)

Remarquez que le contenu des fichiers Java est identique au projet jaxws-tutorial-exercice1.

$ java -cp "target/ws.jar" fr.mickaelbaron.jaxwstutorialexercice6.NotebookWebServicePublisher
Exception in thread "main" java.lang.NoClassDefFoundError: javax/xml/ws/Endpoint
at fr.mickaelbaron.jaxwstutorialexercice6.NotebookWebServicePublisher.main(NotebookWebServicePublisher.java:10)
Caused by: java.lang.ClassNotFoundException: javax.xml.ws.Endpoint
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:190)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:499)
... 1 more

Vous remarquerez que le projet de démarre pas du fait de l'absence de certaines dépendances. Il est donc nécessaire de fournir lors de l'exécution (dans le classpath) les dépendances nécessaires.

- Modifier le fichier pom.xml afin d'ajouter le plugin maven-dependency-plugin qui permettra de lister toutes les bibliothèques nécessaires.

1. <plugin>

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

3. <artifactId>maven-dependency-plugin</artifactId>

4. <version>${maven.dependency.version}</version>

5. <executions>

6. <execution>

7. <id>copy-dependencies</id>

8. <phase>package</phase>

9. <goals>

10. <goal>copy-dependencies</goal>

11. </goals>

12. </execution>

13. </executions>

14. </plugin>

- Saisir les lignes de commande suivantes pour compiler, construire et démarrer le projet.

$ mvn clean package
...
$ java -cp "target/classes:target/dependency/*" fr.mickaelbaron.jaxwstutorialexercice6.NotebookWebServicePublisher

VIII-D - Étapes à suivre pour effectuer un déploiement sur le serveur d'applications Tomcat

Le fichier web.xml est utilisé pour configurer le déploiement de l'application web. Ainsi pour accéder à l'application vous devrez utiliser le contexte suivant /notebookservice.

- Compléter le fichier web.xml à partir de la configuration donnée ci-dessous.

1. <web-app>

2. <display-name>jaxws-tutorial-exercice6</display-name>

3. <listener>

4. <listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>

5. </listener>

6. <servlet>

7. <display-name>notebookservice</display-name>

8. <servlet-name>notebookservice</servlet-name>

9. <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>

10. <load-on-startup>1</load-on-startup>

11. </servlet>

12. <servlet-mapping>

13. <servlet-name>notebookservice</servlet-name>

14. <url-pattern>/notebookservice</url-pattern>

15. </servlet-mapping>

16. <session-config>

17. <session-timeout>60</session-timeout>

18. </session-config>

19. </web-app>

Le fichier sun-jaxws.xml est utilisé pour configurer l'accès au service web SOAP. Il permet de mapper le contexte /notebookservice avec la classe qui implémente le service web étendue NotebookServiceImpl.

- Compléter le fichier à partir de la configuration donnée ci-dessous.

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

3. <endpoints xmlns='http://java.sun.com/xml/ns/jax-ws/ri/runtime' version='2.0'>

4. <endpoint

5. name='notebookservice'

6. implementation='fr.mickaelbaron.jaxwstutorialexercice6.NotebookServiceImpl'

7. url-pattern='/notebookservice'/>

8. </endpoints>

- Saisir la ligne de commande suivante pour compiler et construire le projet vers un fichier war.

mvn clean package -P war

Le fichier web.xml est utilisé pour configurer l'application web. Ainsi pour accéder à l'application vous devrez utiliser le contexte suivant /notebookwebservice.

- Compléter le fichier à partir de la configuration donnée ci-dessous.

1. ...

2. <web-app>

3. <display-name>NotebookWebService6</display-name>

4. <listener>

5. <listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>

6. </listener>

7. <servlet>

8. <display-name>notebookwebservice6</display-name>

9. <servlet-name>notebookwebservice6</servlet-name>

10. <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>

11. <load-on-startup>1</load-on-startup>

12. </servlet>

13. <servlet-mapping>

14. <servlet-name>notebookwebservice6</servlet-name>

15. <url-pattern>/notebookwebservice6</url-pattern>

16. </servlet-mapping>

17. <session-config>

18. <session-timeout>60</session-timeout>

19. </session-config>

20. </web-app>

Le fichier sun-jaxws.xml est utilisé pour configurer l'accès au service web SOAP. Ainsi pour accéder au service web le chemin relatif à utiliser sera /notebookwebservice6/notebookwebservice.

- Compléter le fichier à partir de la configuration donnée ci-dessous.

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

3. <endpoints xmlns='http://java.sun.com/xml/ns/jax-ws/ri/runtime' version='2.0'>

4. <endpoint

5. name='notebookservice'

6. implementation='fr.mickaelbaron.jaxwstutorialexercice6.NotebookServiceImpl'

7. url-pattern='/notebookservice'/>

8. </endpoints>

mvn clean package -P war

L'option -P war permet d'utiliser le profile Maven appelé war. Depuis le fichier pom.xml examiner la balise <profiles>. Cette astuce permet de générer un fichier jar ou un fichier war depuis un même fichier pom.xml.

1. ...

2. <packaging>${project.packaging}</packaging>

3. ...

4. <profiles>

5. <profile>

6. <id>jar</id>

7. <activation>

8. <activeByDefault>true</activeByDefault>

9. </activation>

10. <properties>

11. <project.packaging>jar</project.packaging>

12. </properties>

13. </profile>

14. <profile>

15. <id>war</id>

16. <properties>

17. <project.packaging>war</project.packaging>

18. </properties>

19. </profile>

20. </profiles>

docker pull tomcat:9.0.12-jre10-slim

docker run --rm --name helloworldservice-tomcat -v $(pwd)/target/ws.war:/usr/local/tomcat/webapps/ws.war -it -p 8080:8080 tomcat:9.0.12-jre10-slim

VII - Conclusion et remerciements

Nous avons vu au travers de six exercices comment utiliser l'API JAX-WS. Nous avons notamment insisté sur le développement de services web et de clients utilisés pour appeler les services web de type étendu.

Pour aller plus loin, vous pouvez consulter les ressources suivantes :

- Support de cours SOA

- Support de cours WSDL

- Support de cours SOAP

- Support de cours JAX-WS

- Exercices sur le test fonctionnel de services web avec SOAP-UI

Vous pouvez retrouver cette série d'exercices sur mon compte Github : https://github.com/mickaelbaron/jaxws-tutorial.

Je tiens à remercier Claude Leloup pour sa relecture orthographique de cet article.