Tutoriel sur le développement de services Web avec JAX-WS et Netbeans : Leçon 2

Mickael BARON

Le but de cette deuxième leçon est d'apprendre à manipuler l'API JAX-WS pour le développement de services Web étendus à partir de la plateforme de développement Java. La leçon insiste sur le développement d'un service Web suivant les approches Bottom/Up et Top/Down puis sur le développement de la partie cliente d'un service Web et enfin sur la manipulation de Handler.

Buts pédagogiques : transformation d'un POJO Java en service Web, génération des artefacts à 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.

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 avec JAX-WS et Netbeans : Leçon 2

- Auteur : Mickael BARON

- Parution : 20 février 2011

- Mise à jour : 2 décembre 2013

- 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 ® 2013 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

- Exercice 1 - Développer un service Web suivant une approche montante Bottom/Up

- But

- Description

- Étapes à suivre

- Exercice 2 - Développer un service Web suivant une approche descendante Up/Down

- But

- Description

- Étapes à suivre

- Exercice 3 - Développer un client de service Web en mode synchrone

- But

- Description

- Étapes à suivre

- Exercice 4 - Développer un client de service Web en mode asynchrone

- But

- Description

- Étapes à suivre

- Exercice 5 - Développer un service Web en utilisant l'outillage de JavaSE

- But

- Description

- Étapes à suivre

- Exercice 6 - Ajouter un intercepteur (handler) à un service Web

- But

- Description

- Étapes à suivre

- Remerciements

Exercice 1 - Développer un service Web suivant une approche montante Bottom/Up

But

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

- Implémenter le service Web.

- Déployer sous Glassfish ou Tomcat.

- Tester le service Web via SOAP-UI.

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

Étapes à suivre

Démarrer l'environnement de développement NetBeans.

Créer un nouveau projet File -> New Project… puis Java Web et choisir Web Application, faire Next.

Dans le nom du projet, choisir le nom NotebookWebServiceExercice1.

Comme type de serveur d'application, choisir Tomcat ou GlassFish et comme version Java EE choisir Java EE 7 puis faire Next.

Ne rien choisir dans les Frameworks proposés, puis faire Finish. L'initialisation du projet est en cours.

Depuis le projet NotebookWebServiceExercice1 s'assurer que les opérations Clean and Build et Deploy fonctionnent correctement. L'URL pour accéder à l'application Web est donnée depuis la console de Netbeans (par défaut : http://localhost:8080/NotebookWebServiceExercice1).

Créer une classe qui représentera une personne (File -> New File… puis choisir Java et enfin Java Class), puis faire Next. Appeler la classe Person et la définir dans le package soa.jaxwslabs.notebookwebserviceexercice1, puis faire Finish.

Dans la nouvelle classe créée, ajouter un attribut name de type String et un attribut address de type String. Définir un constructeur par défaut (important pour JAXB) et un constructeur avec deux paramètres correspondant respectivement aux deux attributs. Générer les modifieurs et accesseurs. Pour la génération, exploiter les outils fournis par NetBeans (Insert Code… via le menu contextuel de la classe Java).

1. public class Person {

3. protected String name;

5. protected String address;

7. public Person() {

8. }

10. public Person(String pName, String pAddress) {

11. this.name = pName;

12. this.address = pAddress;

13. }

15. public String getName() {

16. return name;

17. }

19. public void setName(String name) {

20. this.name = name;

21. }

23. public String getAddress() {

24. return address;

25. }

27. public void setAddress(String address) {

28. this.address = address;

29. }

30. }

Définir une interface Java représentant la description du service Web (File -> New File… puis choisir Java et enfin Java Interface) puis faire Next. Définir comme nom de l'interface NotebookService et utiliser le précédent package. Faire Finish.

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

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

- List<Person> getPersons() : récupérer l'ensemble des personnes ;

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

Ajouter une annotation @WebService au niveau de l'interface puis initialiser ces attributs name à NotebookService et targetNamespace à http://soa.jaxwslabs.notebookwebserviceexercice1.

@WebService(name = "NotebookService", targetNamespace = "http://soa.jaxwslabs.notebookwebserviceexercice1")
public interface NotebookService {
void addPerson(Person p);

List<Person> getPersons();

Person getPersonAt(String name);
}

Construire une nouvelle classe appelée NotebookServiceImpl qui implémente l'interface NotebookService.

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

- endpointInterface = soa.jaxwslabs.notbookwebserviceexercice1.NotebookService;

- serviceName = NotebookService;

- portName = NoteBookPort.

Ci-dessous est donné un exemple d'implémentation. Saisir le code de la classe NotebookServiceImpl.

1. @WebService(

2. endpointInterface = "soa.jaxwslabs.notebookwebserviceexercice1.NotebookService",

3. serviceName = "NotebookService",

4. portName = "NoteBookPort")

5. public class NotebookServiceImpl implements NotebookService {

7. @Override

8. public void addPerson(Person p) {

9. if (p == null) {

10. throw new NullPointerException("Person is null");

11. }

13. try {

14. Thread.sleep(5000);

15. } catch (InterruptedException e) {

16. e.printStackTrace();

17. }

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

20. }

22. @Override

23. public List<Person> getPersons() {

24. List<Person> myPerson = new ArrayList<Person>();

26. myPerson.add(new Person("Mickael BARON", "Migné-Auxances"));

27. myPerson.add(new Person("Sébastien LOEB", "France"));

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

30. return myPerson;

31. }

33. @Override

34. public Person getPersonAt(String name) {

35. if (name == null || name.equals("")) {

36. throw new NullPointerException("Name is null");

37. }

39. System.out.println("getPersonAt method has been invoked");

40. return new Person("Mickael BARON", "Migné-Auxances");

41. }

42. }

Faire un Clean and Build à partir du projet NotebookWebServiceExercice1 et s'assurer que le projet se construit correctement.

Faire un Deploy et Run à partir du projet. Le serveur Tomcat ou Glassfish doit démarrer. Depuis la console de Glassfish, un service Web doit avoir été découvert, un message similaire doit être présent :

Afficher sa description WSDL et examiner le résultat par rapport à ce qui a été défini dans l'interface Java.

Pour tester le comportement du service Web vous pouvez soit utiliser l'outil SOAP-UI (voir leçon 1) soit utiliser l'outil fourni par JAX-WS (à partir du projet sous NetBeans, afficher le menu contextuel du sous-nœud de services Web qui est NotebookServiceImpl).

Exercice 2 - Développer un service Web suivant une approche descendante Up/Down

But

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

- Implémenter le service Web.

- Déployer sous Glassfish ou Tomcat.

- Tester le service Web.

- wsimport.

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 la génération des classes (artefacts) ne donne pas exactement les mêmes classes que celles construites et utilisées dans l'exercice 1.

Étapes à suivre

Créer un nouveau projet de type Web Application nommé NotebookWebServiceExercice2.

Ajouter un nouveau fichier File -> New File… puis choisir Web Services et enfin Web Service from WSDL. Faire Next.

Dans le champ Web Service Name saisir la valeur NotebookServiceImpl, dans le champ package soa.jaxwslabs.notebookwebserviceexercice2, dans le champ Select Local WSDL File or Enter WSDL URL saisir l'URL du WSDL de l'exercice 1. L'outil analyse le WSDL et propose de choisir le port, sélectionner celui par défaut. Faire Finish.

Un ensemble de classes est généré par l'outil wsimport. Ces classes ne doivent pas être modifiées puisqu'elles sont générées automatiquement. Seule la classe NotebookServiceImpl peut l'être. Elle correspond à la classe implémentant le comportement du service Web. Réutiliser le code de l'exercice 1 pour compléter cette classe en l'adaptant en fonction du code généré.

1. @WebService(serviceName = "NotebookService", portName = "NoteBookPort", endpointInterface = "soa.jaxwslabs.notebookwebserviceexercice1.NotebookService", targetNamespace = "http://notebookwebserviceexercice1.jaxwslabs.soa/", wsdlLocation = "WEB-INF/wsdl/NotebookServiceImpl/lias-baron_8080/NotebookWebServiceExercice1/NotebookService.wsdl")

2. public class NotebookServiceImpl {

4. public void addPerson(Person p) {

5. if (p == null) {

6. throw new NullPointerException("Person is null");

7. }

9. try {

10. Thread.sleep(5000);

11. } catch (InterruptedException e) {

12. e.printStackTrace();

13. }

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

16. }

18. public List<Person> getPersons() {

19. List<Person> myPersons = new ArrayList<Person>();

21. Person myPerson = new Person();

22. myPerson.setName("Mickael BARON");

23. myPerson.setAddress("Migné-Auxances");

24. myPersons.add(myPerson);

26. myPerson = new Person();

27. myPerson.setName("Sébastien LOEB");

28. myPerson.setAddress("France");

29. myPersons.add(myPerson);

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

32. return myPersons;

33. }

35. public Person getPersonAt(String name) {

36. if (name == null || name.equals("")) {

37. throw new NullPointerException("Name is null");

38. }

40. System.out.println("getPersonAt method has been invoked");

42. Person myPerson = new Person();

43. myPerson.setName("Mickael BARON");

44. myPerson.setAddress("Migné-Auxances");

46. return myPerson;

47. }

48. }

Faire un Clean and Build (s'assurer qu'il n'y a aucune erreur) et faire un Deploy.

En comparant le WSDL obtenu par cet exercice avec celui de l'exercice 1 vous remarquerez que syntaxiquement, il existe des différences, mais que sémantiquement c'est la même chose.

Exercice 3 - Développer un client de service Web en mode synchrone

But

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

- Appel synchrone.

- Outil wsimport.

- Client Web via une JSP.

Description

Cet exercice consiste à appeler le service Web défini dans l'exercice 1. Un client léger via une JSP est utilisé pour appeler l'opération getPersons() définie par le service Web. Cette opération est invoquée en mode synchrone, c'est-à-dire que le client est en attente de la réponse pour continuer son traitement.

Étapes à suivre

Créer un nouveau projet de type Web Application nommé NotebookWebServiceClientExercice3.

Ajouter un nouveau fichier File -> New File… puis choisir Web Services et enfin Web Service Client. Faire Next.

Dans le champ WSDL URL, saisir l'URL http://localhost:8080/NotebookWebServiceExercice1/NotebookService?wsdl et vérifier que les options sont paramétrées comme montrées sur la figure ci-dessous.

Un ensemble de classe est généré par l'outil wsimport. Elles représentent les classes liées au mapping JAXB (voir répertoire NotebookWebServiceClientExercice3\build\generated-sources\).

Éditer la page index.jsp de manière à invoquer l'opération getPersons() en mode synchrone. Saisir le code comme décrit ci-dessous :

1. <html>

2. <head>

3. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

4. <title>Web Service Client of NotebookWebService</title>

5. </head>

6. <body>

7. <h1>Web Service Client : NotebookWebServiceExercice 1</h1>

9. <%

10. soa.jaxwslabs.notebookwebserviceexercice1.NotebookService_Service myService = new soa.jaxwslabs.notebookwebserviceexercice1.NotebookService_Service();

11. soa.jaxwslabs.notebookwebserviceexercice1.NotebookService notebookPort = myService.getNoteBookPort();

12. java.util.List<notebookwebserviceexercice1.jaxwslabs.soa.Person> persons = notebookPort.getPersons();

14. for (notebookwebserviceexercice1.jaxwslabs.soa.Person current : persons) {

15. out.print(current.getName() + " - " + current.getAddress() + "<br>");

16. }

17. %>

18. </body>

19. </html>

L'opération getPersons() est invoquée via l'utilisation de la classe NotebookService. Le retour de l'opération est traité dans une boucle qui affiche pour chaque personne les informations concernant le nom et l'adresse. Déployer et tester l'application.

Exercice 4 - Développer un client de service Web en mode asynchrone

But

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

- Appel asynchrone.

- Outil wsimport.

- Client lourd via une interface graphique SWING.

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 artefacts via wsimport pour le mode asynchrone.

Étapes à suivre

Créer un nouveau projet File -> New Project… puis Java choisir Java Application, faire Next.

Choisir comme nom de projet NotebookWebServiceClientExercice4 et comme nom de la classe principale (Main) soa.jaxwslabs.notebookwebserviceclientexercice4.WebServiceClient puis faire Finish.

Ajouter un nouveau fichier File -> New File… puis choisir Web Services et enfin Web Service Client. Faire Next.

Dans le champ WSDL URL, saisir l'URL http://localhost:8080/NotebookWebServiceExercice1/NotebookService?wsdl et configurer de la même manière que dans l'exercice 3 (étape 3).

Au niveau de la gestion des services Web du projet (nœud Web Service References), accéder aux propriétés du service NotebookService (menu Edit Web Service Attributes). Ouvrir l'onglet WSDL Customization, déplier l'option Global Customization et cocher l'option Enable Asynchronous Client (voir figure ci-dessous pour plus de précisions).

Il vous sera demandé de régénérer les artefacts liés au client service Web. Choisir le service Web NotebookService et faire Refresh Client.

Dans la classe WebServiceClient, recopier le code présenté ci-dessous.

1. public class WebServiceClient extends JFrame {

3. public WebServiceClient() {

4. super("WebService Client from SWING application");

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

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

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

10. final JTextArea textArea = new JTextArea();

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

13. startButton.addActionListener(new ActionListener() {

14. public void actionPerformed(ActionEvent e) {

15. startButton.setEnabled(false);

16. Person newPerson = new Person();

17. newPerson.setName("Mickael BARON");

18. newPerson.setAddress("Poitiers");

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

22. NotebookService_Service service = new NotebookService_Service();

23. NotebookService port = service.getNoteBookPort();

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

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

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

28. textArea.append("New Person added");

29. startButton.setEnabled(true);

30. }

31. }

32. });

33. }

34. });

35. this.pack();

36. this.setVisible(true);

37. }

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

40. new WebServiceClient();

41. }

42. }

Vous aurez peut être besoin d'ajouter la bibliothèque JAX-WS dans le classpath du projet. Dans la partie Libraries du projet, ajouter la dépendance vers la bibliothèque JAX-WS 2.1 (menu Add Library… et choisir JAX-WS 2.1).

Faire un Clean and Build à partir du projet puis exécuter le programme. Le résultat attendu est celui montré sur la figure ci-dessous.

Exercice 5 - Développer un service Web en utilisant l'outillage de JavaSE

But

- Développer un service Web avec JavaSE.

- EndPoint.

- Méthode publish.

- Serveur Web intégré.

Description

L'objectif de cet exercice est de développer et publier un service Web en utilisant les fonctionnalités offertes par JavaSE. L'exemple de l'exercice 1 (Notebook) est utilisé.

Étapes à suivre

Créer un nouveau projet File -> New Project… puis Java choisir Java Application, faire Next.

Choisir comme nom de projet NotebookWebServiceJavaSE6Exercice5 et comme nom de la classe principale (Main) soa.jaxwslabs.notebookwebservicejavase6exercice5.JavaSE6WebService puis faire Finish.

Recopier l'interface NotebookService et les classes NotebookServiceImpl et Person issues de l'exercice 1 dans le projet NotebookWebServiceJavaSE6Exercice5 (respecter le nouveau nom de package).

Dans la classe JavaSE6WebService recopier le code présenté ci-dessous.

1. public class JavaSE6WebService extends JFrame {

3. private Endpoint publish;

5. public JavaSE6WebService() {

6. this.getContentPane().setLayout(new GridLayout(2, 1));

7. final JButton startPublish = new JButton();

8. this.getContentPane().add(startPublish);

9. startPublish.setText("Start Publish");

11. final JButton stopPublish = new JButton();

12. stopPublish.addActionListener(new ActionListener() {

13. @Override

14. public void actionPerformed(ActionEvent e) {

15. publish.stop();

16. stopPublish.setEnabled(false);

17. startPublish.setEnabled(true);

18. }

19. });

20. stopPublish.setText("Stop Publish");

22. startPublish.addActionListener(new ActionListener() {

23. @Override

24. public void actionPerformed(ActionEvent e) {

25. NotebookService current = new NotebookServiceImpl();

26. publish = Endpoint.publish("http://localhost:8080/notebook", current);

27. startPublish.setEnabled(false);

28. stopPublish.setEnabled(true);

29. }

30. });

31. stopPublish.setEnabled(false);

32. startPublish.setEnabled(true);

33. this.getContentPane().add(stopPublish);

34. this.setTitle("Start / Stop Web Service Publication");

35. this.pack();

36. this.setVisible(true);

37. }

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

40. new JavaSE6WebService();

41. }

42. }

Vous aurez peut être encore besoin d'ajouter la bibliothèque JAX-WS dans le classpath du projet. Dans la partie Libraries du projet, ajouter la dépendance vers la bibliothèque JAX-WS 2.1 (menu Add Library… et choisir JAX-WS 2.1).

Faire un Clean and Build à partir du projet puis exécuter le programme.

Ouvrir une page de votre navigateur préféré et vérifier que le WSDL s'affiche correctement (URL : http://localhost:8080/notebook).

Exercice 6 - Ajouter un intercepteur (handler) à un service Web

But

- Implémentation handler.

- Configuration du service Web.

- Filtrage par opération.

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

Étapes à suivre

Créer un nouveau projet de type Web Application nommé NotebookWebServiceExercice6.

Créer un package intitulé soa.jaxwslabs.notebookwebserviceexercice6.

Recopier l'interface NotebookService et les classes NotebookServiceImpl et Person issues de l'exercice 1 dans le projet NotebookWebServiceExercice6 (respecter le nouveau nom de package).

Dans la classe NotebookServiceImpl, ajouter l'annotation @HandlerChain(file = « handler.xml ») au niveau de la description de la classe.

Ajouter un fichier handler.xml, au niveau du répertoire WEB-INF de l'application Web.

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>soa.jaxwslabs.notebookwebserviceexercice6.SOAPLoggingHandler</handler-name>

6. <handler-class>soa.jaxwslabs.notebookwebserviceexercice6.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. public class SOAPLoggingHandler implements SOAPHandler<SOAPMessageContext> {

2. @Override

3. public Set<QName> getHeaders() { return null; }

5. @Override

6. public void close(MessageContext context) { }

8. public boolean handleMessage(SOAPMessageContext smc) {

9. Boolean outboundProperty = (Boolean) smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);

11. if (outboundProperty.booleanValue()) { return false; }

12. else {

13. return (!"getPersons".equals(getMethodName(smc, true)));

14. }

15. }

17. public boolean handleFault(SOAPMessageContext smc) { return true; }

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

20. try {

21. Field field = context.getClass().getSuperclass().getDeclaredField("packet");

22. field.setAccessible(true);

23. Packet packet = (Packet) field.get(context);

25. if (isRequest) {

26. return ((StreamMessage) packet.getMessage()).getPayloadLocalPart();

27. }

29. return ((JAXBMessage) packet.getMessage()).getPayloadLocalPart();

30. } catch (Exception e) {

31. e.printStackTrace();

32. }

33. return null;

34. }

35. }

Noter que la méthode getMethodName(…) s'occupe d'extraire du contenu du message SOAP le nom de l'opération.

Faire un Clean and Build à partir du projet de déployer l'application.

Remerciements

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