lundi, décembre 05, 2011

Etape 5 : Déployer un Service Web et compléter son WSDL


DiggIt! Enregistrer sur Del.icio.us

Il s’agit de la 5ieme étape d’un tutorial sur la création de service web JAX-WS. L’objectif de cette étape est, dans notre approche Contract First, de compléter le WSDL naturellement généré par les annotations JAX-WS. Nous allons en effet constater que les annotations JAX-WS ne permettent pas la génération d’un WSDL contenant toutes les précisions nécessaires (comme la longueur d’une chaine de caractères). Nous allons voir comment surcharger le WSDL généré afin qu’il soit conforme au besoin de notre approche Contract First.

Cette étape s’appuie sur :

Dans cette partie nous allons déployer notre service web sur JBoss 5.1 et analyser le WSDL qui est produit. Ensuite, nous allons compléter le WSDL afin d’ajouter des informations plus précises sur les contraintes des types. Pour cela nous allons enregistrer le WSDL généré et le surcharger en utilisant un schéma XSD complémentaire.

Rappel des étapes du tutorial sur les services avec JAX-WS :

  1. Présentation du tutorial et de l'approche Contract First avec JAX-WS
  2. Etape 2 : Configuration d'un projet Eclipse pour un WebService JAX-WS
  3. Etape 3 : Création des objets métier et d’une exception JAX-WS
  4. Etape 4 : Annotations JAX-WS et JAXB des classes
  5. Etape 5, Ce billet : Déployer un Service Web et compléter son WSDL

Déployer un service web

Je ne vais pas reprendre dans le détail le déploiement d’un service web. Le déploiement est assez spécifique au serveur d’application et j’ai déjà réalisé trois tutoriaux mettant en œuvre le déploiement d’un service web :

Dans notre cas, nous allons créer un WAR puis le déployer sur le serveur d’application.

Génération du War

Assurez vous d’avoir bien sauvegarder l’ensemble des fichiers.

Cliquez droit dans l’explorateur de ressource d’Eclipse sur votre projet JaxWsContractFirst, sélectionnez Export –> WAR File, dans la dialogue « Export » qui s’affiche choisissez :

  • Web Project : JaxWsContractFirst qui est le nom de votre projet
  • Destination : Naviguez pour sélectionner l’emplacement et le nom du WAR File. Pour ma part je l’ai créé sur le bureau. Le nom du fichier est JaxWsContractFirst.war.
  • Décochez l’option “Optimize for a specific server runtime”

Le nom du war n’a aucune importance, il n’est pas utilisé pour obtenir le WSDL et n’a aucun d’impact sur le contenu du WSDL.

ExportWar

Appuyez, ensuite sur Finish. Vous devriez trouver un fichier JaxWsContractFirst.war sur le bureau.

Déployer le war

Cette partie est très spécifique de votre serveur d’application. Je me contente du cas JBoss 5.1.

Pour WebSphere, je vous renvoie à mon tutoriel : http://jl2tho.blogspot.com/2011/11/ce-tutorial-adapte-pour-ibm-websphere-6.html

Avec JBoss, la solution la plus simple est de copier le war dans le répertoire C:\jboss-5.1.0.GA\server\default\deploy.

Vous n’avez pas besoin de redémarrer le serveur s’il est lancé, vous devriez voir apparaitre après quelques secondes dans la console :

12:08:51,352 INFO [DefaultEndpointRegistry] register: jboss.ws:context=JaxWsContractFirst,endpoint=WebContractHelpdesk
12:08:51,778 INFO [TomcatDeployment] deploy, ctxPath=/JaxWsContractFirst
12:08:52,555 INFO [WSDLFilePublisher] WSDL published to: file:/C:/jboss-5.1.0.GA/server/default/data/wsdl/JaxWsContractFirst.war/WebContractHelpdesk7856945428892376495.wsdl

On y retrouve le nom du War que l’on a déployé et le nom du service web (le endpoint).

Si on tape l’url suivante dans un browser : http://localhost:8080/jbossws/services une page JBossWS/Services s’affiche.

JBossEndpoint

Il doit y a avoir une page avec un Endpoint name jboss.ws:context=JaxWsContractFirst,endpoint=WebContractHelpdesk, un WSDL est accessible sur la ligne suivante : http://127.0.0.1:8080/JaxWsContractFirst?wsdl

Attention ! les URLs dépendent des serveurs d’application. On a par exemple, pour obtenir le WSDL :

 

Analyse du WSDL généré

En cliquant sur le lien précédent : http://127.0.0.1:8080/JaxWsContractFirst?wsdl on affiche le WSDL du service.

Le WSDL dépend du serveur d’application : par exemple WebSphere 6.1 sépare en deux fichiers le WSDL du schema XSD définissant les types XML. JBoss ne fait lui qu’un seul fichier.

Nous allons analyser succinctement son contenu afin de mettre en évidence les correspondances entre les annotations et le contenu du WSDL.

Dans mes précédents tutoriaux sur JAX-WS, j’ai déjà analysé les différentes correspondances, je me limiterai dans cette étape à quelques points nouveaux :

  • l’objet Question : comment un objet Java apparait dans le WSDL
  • l’énumération
  • le FaultBean qui permet de transférer une exception vers le client

L’objet Question

Il devient un ComplexType, son type QuestionType correspond à l’annotation XmlType.

On constate que les name des différents éléments correspondent à l’annotation XmlElement.

<xs:complexType name="QuestionType">
<xs:sequence>
<xs:element minOccurs="0" name="NUMERO" type="xs:string"/>
<xs:element minOccurs="0" name="DESCRIPTION" type="xs:string"/>
<xs:element minOccurs="0" name="SUBMITDATE" type="xs:dateTime"/>
<xs:element name="TITLE" type="xs:string"/>
<xs:element minOccurs="0" name="PRODUCT" type="xs:string"/>
<xs:element minOccurs="0" name="URGENCY" type="tns:urgencyEnum"/>
<xs:element name="PRIORITY" type="xs:string"/>
<xs:element minOccurs="0" name="TYPE" type="xs:string"/>
</xs:sequence>
</xs:complexType>

On remarque qu’on utilise des types simples provenant du schéma xs,  à l’exception de URGENCY qui pointe vers le schéma tns (c’est le WSDL) et plus particulièrement vers notre type énumération.

Si on compare avec le WSDL généré par Axis2 à l’étape : http://jl2tho.blogspot.com/2007/05/etape-5-tutorial-axis2-gnration-des.html dans notre tutorial Services web avec Axis2 : Approche Contract First, on avait pour l’objet Question :

<xsd:complexType name="Question">
<xsd:sequence>
<xsd:element name="Numero" type="tns:QuestionID"></xsd:element>
<xsd:element name="SubmitDate" type="xsd:date"></xsd:element>
<xsd:element name="Product">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:length value="2"></xsd:length>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="Type">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="BUGINPROD"></xsd:enumeration>
<xsd:enumeration value="CONFIG"></xsd:enumeration>
<xsd:enumeration value="USAGE"></xsd:enumeration>
<xsd:enumeration value="BUGINDEV"></xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="Priority" type="tns:Urgency"></xsd:element>
<xsd:element name="Description" type="xsd:string"></xsd:element>
<xsd:element name="Title">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:maxLength value="250"></xsd:maxLength>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>

On diffère sur plusieurs points significatifs (les noms ne le sont pas car ils sont paramétrables par annotation comme on pu le voir à l’étape suivante) :

  • le type de SubmitDate est date au lieu de dateTime
  • l’énumération est directement incluse dans le type, dans notre cas elle est externalisée.
  • avec axis2, il est possible d’être plus précis sur les restrictions de type : notamment on peut indiquer la taille maximale d’un champ.
  • par défaut, il aurait fallu indiquer systématiquement required à true. cela indique que le nœud est toujours présent et fait disparaitre le minOccurs. Attention, cela n’a rien à voir avec la valeur null.
  • avec axis2, on a pu utiliser un type simple externe QuestionID permettant de préciser les contraintes sur le contenu du nœud.

 

Enumération

On avait surchargé les valeurs des énumérations par annotation, on voit qu’on retrouve un SimpleType qui reprend ces valeurs.

Si on compare à la définition ci-dessus qui provient d’Axis2 et qui était incluse directement dans l’objet Question, on retrouve la même structure.

<xs:simpleType name="urgencyEnum">
<xs:restriction base="xs:string">
<xs:enumeration value="URGENCY_BUGINPROD"/>
<xs:enumeration value="URGENCY_CONFIG"/>
<xs:enumeration value="URGENCY_USAGE"/>
<xs:enumeration value="URGENCY_BUGINDEV"/>
</xs:restriction>
</xs:simpleType>

 

Exception et Fault

Nos méthodes pouvaient renvoyer une exception, on retrouve cette possibilité dans la définition de l’opération :

<operation name="creerNouvelleQuestion" parameterOrder="creerNouvelleQuestion">
<input message="tns:ISwHelpDeskPortType_creerNouvelleQuestion"/>
<output message="tns:ISwHelpDeskPortType_creerNouvelleQuestionResponse"/>
<fault message="tns:BusinessFault" name="BusinessFault"/>
</operation>

On a un attribut fault pour nos opérations.

Par rebond, on arrive à la définition du beanFault :

<xs:complexType name="faultBean">
<xs:sequence>
<xs:element minOccurs="0" name="businessMessage" type="xs:string"/>
<xs:element minOccurs="0" name="code" type="xs:string"/>
</xs:sequence>
</xs:complexType>

On voit qu’il contient les deux propriétés qu’on avait définies.

Vous pouvez chercher dans le WSDL, vous ne trouverez aucune référence à la propriété message d’une exception Java. Cette information n’est pas visible dans le WSDL.

Compléter le WSDL

Nous avons deux éléments en écart par rapport à ce qu’on pouvait faire dans l’approche Axis2 :

  • le format d’un champ date
  • la possibilité de définir des contraintes plus fortes sur un type simple

 

Propriété de type Date

La documentation officielle d’Oracle (http://docs.oracle.com/cd/E17802_01/webservices/webservices/reference/tutorials/wsit/doc/DataBinding2.html) considère que le type à utiliser pour une date est en priorité le type XMLGregorianCalendar : si on utilise le type java Date, on ne pourra aller que vers le type XML dateTime. A contrario, le type XMLGregorianCalendar offre une plus grande liberté et peut être envoyé vers un type XML date.

Pour information, les outils qui génère du Java à partir du WSDL vont généralement vers ce type XMLGregorianCalendar.

On va donc modifier doublement la déclaration de notre propriété submitDate dans l’objet Question :

  • on change son type java
  • on lui ajoute une annotation XmlSchemaType afin de préciser qu’on souhaite un type date et non plus dateTime.

On obtient pour sa déclaration le code suivant :

    @XmlElement(name = "SUBMITDATE")
    @XmlSchemaType(name="date")
     public javax.xml.datatype.XMLGregorianCalendar submitDate;

Il faudra également penser à mettre à son jour son getter et son setter.

 

Propriété de type Token ou normalizedString

Au niveau des types de base du XML, il existe trois types simples pour une chaine de caractères : le type string que nous avons utilisé (il s’agit du défaut pour une String Java) et les types token et normalizedString qui peuvent être précisé avec l’utilisation de XmlSchemaType.

Les deux différent du type string par le traitement des caractères retour à la ligne, Entrée, tabulation ainsi que des espaces en début et fin de chaine :

  • token : indique au processeur XML de simplement supprimer ces caractères. Vous obtenez ainsi une chaine sans caractères espaces en début ou fin et sans tabulation ou retour chariot et cela indépendamment de ce qu’envoie le client du service web.
  • normalizedString va un cran plus loin, car il interdit la présence de tel caractère.

Nous allons modifier les propriété product et type afin d’essayer ces deux types. On obtient pour la définition des propriétés de l’objet Question, le code suivant :

On obtient pour sa déclaration le code suivant :

@XmlType(name="QuestionType", propOrder = { "id", "description", "submitDate", "title", "product", "urgency", "priority", "type" })
@XmlAccessorType(XmlAccessType.FIELD)
public class Question {
    @XmlElement(name = "NUMERO")
    private String id;

    @XmlElement(name = "DESCRIPTION")
    private String description;

    @XmlElement(name = "SUBMITDATE")
    @XmlSchemaType(name="date")
     public javax.xml.datatype.XMLGregorianCalendar submitDate;

    @XmlElement(name = "TITLE", required=true)
    private String title;

    @XmlElement(name = "PRODUCT")
    @XmlSchemaType(name="token")
    private String product;

    @XmlElement(name = "URGENCY")
    private UrgencyEnum urgency;

    @XmlElement(name = "PRIORITY", required=true)
    private String priority;

    @XmlElement(name = "TYPE")
    @XmlSchemaType(name="normalizedString")
    private String type;
   

On a mis en gras les modifications.

Il n’est pas nécessaire de modifier les getter et setters.

 

Ajouter des contraintes à un type simple

Cette dernière opération est la plus délicate : on a vu précédemment que les types simples dans axis2 étaient plus précis : il était possible de spécifier la taille maximale d’une chaine de caractères et il était possible de préciser une expression régulière vérifiée par la chaine.

Les annotations JAX-WS ou JAXB ne permettent pas une telle précision, or cette information (surtout d’ailleurs pour la taille d’une chaine de caractères) est particulièrement importante d’un point de vu contractuel. Nous allons aborder ce dernier point dans ce chapitre.

La seule approche possible est l’utilisation d’un WSDL en local : les annotations n’étant pas assez puissante, il faut les aider en reprenant la main. Il y a bien l’annotation XmlSchemaType qui permet d’indiquer un type de données propriétaire : on n’est pas limité aux types du schema standard, il suffit de préciser un autre namespace. Mais il n’existe pas d’annotation permettant d’inclure son propre schéma XSD. Heureusement, nous disposons d’un attribut wsdlLocation dans l’annotation WebService permettant de préciser son propre WSDL. c’est cette approche que nous allons utiliser mais afin d’être moins intrusif, on va également utiliser un schéma XSD complémentaire.

Le principe de base de notre approche est l’utilisation maximale de la génération automatique du WSDL : on partira d’un fichier WSDL généré. Ce fichier WSDL généré que l’on récupère en l’enregistrant va être complété en se limitant au strict nécessaire (ce qui n’aura pas pu être mis en annotation comme l’inclusion d’un schema XSD complémentaire ou bien les annotations length sur les strings).

Fichier XSD pour les contraintes

Tout d’abord nous allons définir un nouveau schéma XSD afin de définir les types nécessitant des contraintes plus fortes comme dans notre exemple le numéro d’une Question. Le numéro est une chaine de caractères de 9 chiffres. Nous allons définir un type simple. Afin de limiter l’impact sur le WSDL généré, nous allons mettre dans un fichier externe : un nouveau fichier XSD.

On crée un répertoire wsdl dans WEB-INF. Il semble impératif qu’il soit dans WEB-INF : il est préférable de mettre ce fichier à coté du fichier WSDL qui doit être sous WEB-INF pour JBoss.

On se place sur le nœud WEB-INF dans WebContent, et on clique droit, choisir New –> Other… Dans la dialogue qui s’ouvre choisir Folder dans le nœud General. Le nom du dossier sera wsdl puis validez.

Dans ce répertoire wsdl, on crée un fichier xsd : HelpDeskBasic.xsd. On se place sur le nœud wsdl que l’on vient de créer, et on clique droit, choisir New –> Other… Dans la dialogue qui s’ouvre choisir XML dans le nœud XML. Tapez HelpDeskBasic.xsd come File name et validez. Le fichier est créé. S’il est ouvert,fermez le afin de le rouvrir en mode Editeur XML.

Ajoutez, le code suivant :

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns:tns="http://basic.demo.jl2tho.fr" xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://basic.demo.jl2tho.fr">
<simpleType name="QuestionId">
<annotation>
<documentation>Type pour un numero de question</documentation>
</annotation>
<restriction base="string">
<length value="9"></length>
<pattern value="\d*"></pattern>
</restriction>
</simpleType>
</schema>

Il est important de noter qu’on utilise pour ce fichier un namespace différent de celui du WSDL en l’occurrence : http://basic.demo.jl2tho.fr. J’en profite pour rappeler qu’un namespace est juste une étiquette : par convention il ressemble à une URL mais il n’y a rien à cette emplacement.

Utilisation d’un type propriétaire avec JAXB

Maintenant que notre nouveau type est défini, nous allons l’utiliser dans notre objet Question. Pour cela, on modifie le champ id de l’objet Java en ajoutant une annotation XmlSchemaType.

cette annotation permet de préciser un type XML (nos l’avons déjà utilisé plus haut). Dans notre cas, comme le type n’appartient au schéma par défaut, nous devons également indiquer le namespace : dans notre cas, c’est celui de notre nouveau XSD.

On obtient pour sa déclaration le code suivant :

@XmlElement(name = "NUMERO")
@XmlSchemaType(name="QuestionId", namespace="http://basic.demo.jl2tho.fr")
private String id;

Il n’est pas nécessaire de mettre à jour son getter et son setter puisqu’on a juste ajouter une annotation.

Création du fichier WSDL local

Pour obtenir la version de départ du WSDL, je souhaite m’appuyer sur le WSDL généré automatiquement. Comme je viens de modifier mes annotations, il faut que je déploie ma nouvelle version sur le serveur JBoss. Je crée un nouveau WAR que je copie sous C:\jboss-5.1.0.GA\server\default\deploy comme au début de ce tutorial. Je rappelle qu’il n’est pas nécessaire de redémarrer le serveur pour prendre en compte une nouvelle version.

On obtient lors du déploiement, le message suivant sur la console :

10:25:46,777 INFO  [TomcatDeployment] undeploy, ctxPath=/JaxWsContractFirst
10:25:46,899 INFO  [DefaultEndpointRegistry] remove: jboss.ws:context=JaxWsContractFirst,endpoint=WebContractHelpdesk
10:25:47,031 INFO  [DefaultEndpointRegistry] register: jboss.ws:context=JaxWsContractFirst,endpoint=WebContractHelpdesk
10:25:47,102 INFO  [TomcatDeployment] deploy, ctxPath=/JaxWsContractFirst
10:25:47,340 ERROR [JBossXSErrorHandler] JBossWS_demo.jl2tho.fr6970218092484449523.xsd[domain:http://www.w3.org/TR/xml-schema-1]::[key=src-resolve.4.2]::Message=src-resolve.4.2: Error resolving component 'ns1:QuestionId'. It was detected that 'ns1:QuestionId' is in namespace 'http://basic.demo.jl2tho.fr', but components from this namespace are not referenceable from schema document 'file:/C:/jboss-5.1.0.GA/server/default/tmp/jbossws/JBossWS_demo.jl2tho.fr6970218092484449523.xsd'. If this is the incorrect namespace, perhaps the prefix of 'ns1:QuestionId' needs to be changed. If this is the correct namespace, then an appropriate 'import' tag should be added to 'file:/C:/jboss-5.1.0.GA/server/default/tmp/jbossws/JBossWS_demo.jl2tho.fr6970218092484449523.xsd'.
10:25:47,434 INFO  [WSDLFilePublisher] WSDL published to: file:/C:/jboss-5.1.0.GA/server/default/data/wsdl/JaxWsContractFirst.war/WebContractHelpdesk6191007714417398541.wsdl


En dépit du message d’erreur qui indique qu’il ne trouve pas la définition du type QuestionId, il est possible d’obtenir un WSDL à jour (il contient bien la référence au nouveau type QuestionId).

Pour le récupérer, on tape l’URL suivante http://127.0.0.1:8080/JaxWsContractFirst?wsdl.

Dans le navigateur, une fois le WSDL affiché, aller dans le menu “Enregistrer Sous…” et enregistrer votre fichier avec le nom JaxWsContractFirst.wsdl. Attention ! l’extension par défaut est XML.

Copier ce fichier dans le répertoire WebContent/WEB-INF/wsdl. Nous avons donc notre fichier WSDL local.

On modifie le fichier en ajoutant au début du fichier un import du xsd qu’on a créé précédemment. Comme ils sont dans le même répertoire, il n’est pas nécessaire de préciser un répertoire.

On obtient pour sa déclaration le code suivant :

<definitions name="WebContractHelpdesk" targetNamespace="http://demo.jl2tho.fr" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://demo.jl2tho.fr" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<types>
  <xs:schema targetNamespace="http://demo.jl2tho.fr" version="1.0" xmlns:tns="http://demo.jl2tho.fr" xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <xsd:import namespace="http://basic.demo.jl2tho.fr" schemaLocation="HelpDeskBasic.xsd"/>
   <xs:element name="BusinessFault" nillable="true" type="tns:faultBean"/>
   <xs:element name="creerNouvelleQuestion" type="tns:creerNouvelleQuestion"/>
   <xs:element name="creerNouvelleQuestionResponse" type="tns:creerNouvelleQuestionResponse"/>

La déclaration va permettre au WSDL de savoir que si on fait référence au namespace http://basic.demo.jl2tho.fr, il doit chercher la définition dans le nouveau schéma XSD. Il faut faire attention à la correspondance avec le nom du fichier xsd.

Bean Webservice HelpdeskServiceBean

Pour finir, on modifie le bean implémentant le Web Service. L’annotation @WebService dispose d’un attribut wsdlLocation qui permet d’indiquer que l’on souhaite utiliser un WSDL local. Attention ! dans le cas de JBoss, il semble indispensable que le fichier WSDL soit sous WEB-INF.

On obtient pour sa déclaration le code suivant :

package fr.j2ltho.webcontracthelpdesk.server;

import javax.ejb.Stateless;
import javax.jws.WebService;

@Stateless
@WebService(endpointInterface="fr.j2ltho.webcontracthelpdesk.server.IWebHelpDesk",
        portName="HelpdeskServicePort",  serviceName="WebContractHelpdesk",
        targetNamespace = "http://demo.jl2tho.fr",
        wsdlLocation="WEB-INF/wsdl/JaxWsContractFirst.wsdl")
public class HelpdeskServiceBean implements IWebHelpDesk {

    public String createQuestion(Question pNewQuestion)
            throws BusinessFaultException {
        if (pNewQuestion==null) {
            FaultBean faultBean = new FaultBean();
            faultBean.setBusinessMessage("Erreur Question nulle");
            faultBean.setCode("01");
            // Le message dans l'exception est disponible dans l'exception recu
            // mais sa déclaration est absente du wsdl
            throw new BusinessFaultException("Internal Message" , faultBean );           
        }
        return null;
    }

    public Question getQuestionWithId(String pNumero)
            throws BusinessFaultException {
        if (pNumero==null) {
            FaultBean faultBean = new FaultBean();
            faultBean.setBusinessMessage("Erreur : Numero de question nul");
            faultBean.setCode("02");
            // Le message dans l'exception est disponible dans l'exception recu
            // mais sa déclaration est absente du wsdl
            throw new BusinessFaultException("Internal Message" , faultBean );           
        }
        return null;
    }

}

On voit que la modification au WSDL généré est minimale : cela me permet d’opter pour régénération du WSDL plutôt que la modification du WSDL local lors d’une modification. Je retire le wsdlLocation, je déploie, je récupère le nouveau WSDL, je l’enregistre et je rajoute ma ligne d’import. Je termine en réactivant le wsdlLocation.

Je régénère mon war et le déploie de nouveau. Le message d’erreur dans la console JBoss a disparu. Si on affiche le WSDL on aura une ligne d’import qui apparait en début de WSDL. Elle diffère de celle que l’on a ajouté :

<definitions name="WebContractHelpdesk" targetNamespace="http://demo.jl2tho.fr">
<types>
<xs:schema targetNamespace="http://demo.jl2tho.fr" version="1.0">
<xsd:import namespace="http://basic.demo.jl2tho.fr" schemaLocation="http://127.0.0.1:8080/JaxWsContractFirst?wsdl&resource=HelpDeskBasic.xsd"/>
<xs:element name="BusinessFault" nillable="true" type="tns:faultBean"/>
<xs:element name="creerNouvelleQuestion" type="tns:creerNouvelleQuestion"/>
<xs:element name="creerNouvelleQuestionResponse" type="tns:creerNouvelleQuestionResponse"/>
<xs:element name="retrouverQuestionApartirId" type="tns:retrouverQuestionApartirId"/>
<xs:element name="retrouverQuestionApartirIdResponse" type="tns:retrouverQuestionApartirIdResponse"/>

Le même war se déploie parfaitement sous WebSphere 6.1. Attention ! l’url du WSDL est http://localhost:9080/JaxWsContractFirst/WebContractHelpdesk?WSDL. Avec ce format d’adresse, Websphere sait afficher le WSDL qu’il faut utiliser. Dans notre cas il le remplace par http://localhost:9080/JaxWsContractFirst/WebContractHelpdesk/WEB-INF/wsdl/JaxWsContractFirst.wsdl alors que quand il s’agit d’un WSDL généré automatiquement, il le redirige vers http://localhost:9080/JaxWsContractFirst/WebContractHelpdesk/webcontracthelpdesk.wsdl (le WEB-INF/wsdl a été supprimé).

On constate que ce n’est pas parce que le WSDL est local, qu’il n’y a pas certaine modification : la définition de l’import change et le nœud soap:address qui contient l’URL du service web sont modifiés à la volée.

Conclusion

Nous avons terminé la partie de création du WSDL avec une approche Contract First. Nous avons obtenu le WSDL de notre choix avec le degré de précision que nous souhaitions.

Nous avons du aider JAX-WS et JAXB car dans la version 2.0, il n’est pas possible en se limitant aux annotations d’être suffisamment précis : il manque des contraintes sur les champs comme la longueur d’une chaine de caractères. La solution retenue repose sur un schéma complémentaire XSD définissant ses contraintes, conjointement utilisé avec un WSDL local afin d’être capable d’ajouter l’import de ce fichier XSD.

Cette approche est beaucoup plus efficace que la solution Axis2 que j’avais décrite en 2007 (voir le tutorial : http://jl2tho.blogspot.com/2007/04/tutorial-services-web-avec-axis2.html).

D’autre part, j’ai pu testé que le même WAR se déploie sur différents serveurs d’application : ce point est important car c’est l’objectif d’une norme comme JAX-WS. Toutefois, souvent, quand on essaie d’aller jusqu’au bout d’une norme censé apporter de la portabilité on se heurte à quelques détails qui cassent sur la fin la portabilité. Cela n’a pas été le cas dans notre exemple qui se veut pourtant complet.

Dans les étapes suivantes, nous allons créer un client puis nous allons coder “véritablement” le service web en introduisant Spring.

Etapes : 1 | 2 | 3 | 4 | 5 – Suivant >


DiggIt! Enregistrer sur Del.icio.us

0 commentaires: