Etape 4 : Annotations JAX-WS et JAXB des classes
DiggIt!
Enregistrer sur Del.icio.us
Il s’agit de la 4ieme étape d’un tutorial sur la création de service web JAX-WS. Cette étape s’appuie sur :
- le projet Eclipse créé précédemment (http://jl2tho.blogspot.com/2011/11/etape-2-du-tutorial-configuration-dun.html) qui référencent les jars contenant les annotations
- les objets Java (classe métier, interface et bean de service) créés à l’étape précédente Etape 3 : Création des objets métier et d’une exception JAX-WS .
Dans cette partie nous allons annoter les objets métiers créés précédemment afin de les transformer en Service Web. L’objectif est d’utiliser les annotations afin d’obtenir une complète maitrise du WSDL généré. Dans cette étape on se concentre sur les possibilités des annotations. On ne complètera pas le fonctionnelle de notre service web. Le code restera identique.
Ce billet peut vous être utile si vous rechercher comment maitriser un aspect du WSDL en utilisant JAX-WS 2.0.
Nous allons annoter les éléments suivants :
- l’interface et le bean afin de définir le service
- l’objet métier Question et l’énumération Java afin de préciser la structure du XML permettant le stockage d’un objet Java.
Rappel des étapes du tutorial sur les services avec JAX-WS :
- Présentation du tutorial et de l'approche Contract First avec JAX-WS
- Etape 2 : Configuration d'un projet Eclipse pour un WebService JAX-WS
- Etape 3 : Création des objets métier et d’une exception JAX-WS
- Etape 4, Ce billet : Annotations JAX-WS et JAXB des classes
Les annotations JAX-WS
Les annotations JAX-WS se place sur l’interface et son implémentation : elles permettent de définir le service web. Le serveur d’application va les utiliser pour déployer le service web, générer le WSDL et assurer la correspondance entre les messages XML qui arrivent en entrée et l’appel de la méthode Java que l’on aura défini pour le traitement.
Pour une documentation de référence sur les annotations JAX-WS 2.0 vous pouvez vous reporter aux pages suivantes :
- http://publib.boulder.ibm.com/infocenter/wasinfo/v6r1/index.jsp?topic=%2Fcom.ibm.websphere.wsfep.multiplatform.doc%2Finfo%2Fae%2Fae%2Frwbs_jaxwsannotations.html
- http://jax-ws.java.net/jax-ws-ea3/docs/annotations.html
Interface IWebHelpDesk
C’est au niveau de l’interface qu’on a la plus grande maitrise sur la génération du WSDL :
- on y définit le protocole utilisé
- on y définit le nom de la majorité des nœuds XML
Au niveau de l’interface, nous définissons deux types d’annotations :
- celles qui annotent l’interface directement : @Remote, @WebService et @SOAPBinding
- celles qui annotent une méthode de l’interface : @WebMethod, @WebResult et @WebParam
Les premières permettent de définir le protocole des messages, comment on assemble les messages, le nom du service…
- @WebService permet de définir le nom du PortType. Je surcharge systématiquement le targetNameSpace afin de donner une URL neutre (détachée du nom de mes classes). je réutilise la même URL partout. Attention ! une uRL a déjà été utilisée au niveau de la BusinessFaultException.
- @SOAPBinding : j’utilise systématiquement Document, Literal et Wrapped qui sont les standards de fait (les plus utilisés). Cela m’assure que le client est capable d’accéder correctement au service indépendamment de la qualité de son implémentation.
Les secondes permettent de définir les éléments de base d’une opération : le nom des nœuds ou attributs XML. Ceux sont les annotations les plus déterminantes pour maitriser la forme du message XML et donc de la partie sensible du WSDL.
- @WebMethod : indique le nom de l’opération du WSDL. Ce nom est également utilisé dans le mode WRAPPED comme nom du message en entrée du service et comme nom du message de réponse (dans ce dernier cas, on ajoute Response au nom). Dans l’exemple, j’ai volontairement pris un nom différent du nom de la méthode, mais il est préférable, dans la mesure du possible, de conserver un nom identique afin de faciliter la phase de test.
- @WebResult : nom de l’objet XML encapsulant la valeur retournée.
- @WebParam : permet d’indiquer le nom du paramètre (par défaut on a arg0…). Il s’agit d’un des éléments les plus importants pour maitriser le WSDL et pour le rendre compréhensible.
On modifie le code initiale avec les annotations suivantes, afin d’obtenir le code suivant :
package fr.j2ltho.webcontracthelpdesk.server;
import javax.ejb.Remote;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;
@Remote
@WebService(name = "ISwHelpDeskPortType", targetNamespace = "http://demo.jl2tho.fr" )
@SOAPBinding(style=Style.DOCUMENT, use=SOAPBinding.Use.LITERAL, parameterStyle= SOAPBinding.ParameterStyle.WRAPPED)
public interface IWebHelpDesk {
@WebMethod(operationName="retrouverQuestionApartirId")
@WebResult(name = "QUESTION")
public Question getQuestionWithId(
@WebParam(name = "NUMERO")String pNumero
) throws BusinessFaultException;
@WebMethod(operationName="creerNouvelleQuestion")
@WebResult(name = "NUMERO")
public String createQuestion(
@WebParam(name = "INPUT_QUESTION") Question pNewQuestion
) throws BusinessFaultException;
}
Attention ! Pour que les annotations soit utilisables, il est indispensable d’avoir correctement définis les External Jars de votre projet Eclipse (voir Etape 2 : Configuration d'un projet Eclipse pour un WebService JAX-WS de ce tutorial).
Attention ! WebSphere 6.1 ne supporte pas bien les noms de paramètres avec un tiret (-). Le signe souligné (_) est préférable comme séparateur si vous décidez d’utiliser des noms en majuscule pour les nœuds XML.
Bean WebService
Au niveau du bean implémentant le Webservice, il n’y a pas grand chose à préciser si ce n’est le nom du WebService lui même.
Attention ! Dans une approche avec un WAR, il faut assurer la correspondance du nom avec celui indiqué dans le servlet-name de web.xml.
Pour les différents attributs de l’annotation WebService d’un bean nous avons :
- endpointInterface : elle indique le nom de l’interface Java décrivant le service Web (dans notre cas IWebHelpDesk).
- portName : la valeur est utilisée comme Port et Binding dans le WSDL. Il s’agit d’un élément de contrôle supplémentaire sur le WSDL mais je doute de l’intérêt de le surcharger.
- serviceName : ce sera le nom du service web. Il correspond aux nœuds Definition et Service du WSDL. Il doit correspondre au servlet-name du web.xml : dans notre cas : WebContractHelpdesk
- targetNamespace : je reprends le même que celui définit dans l’interface. Il s’agit plus pour moi d’éviter de voir apparaitre dans le WSDL des informations sur la structure du développement.
On modifie le code initial afin d’obtenir 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")
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 constate que l’impact sur l’implémentation du service Web est bien plus faible que sur l’interface.
Avec l’annotation de l’interface et de la classe implémentant le service web, on a définit une grande partie du WSDL. En fait si on travaille sur des types simples on a tout définis.
Annotation JAXB sur la classe Question
Néanmoins, dans le cadre d’un vrai service, il est fort probable qu’il vous faille échanger des données un peu plus structurées comme un objet.
Dans notre cas, nous avons un objet métier Question qui va nous permettre d’aborder ce point. En développement Java, il va de soi, qu’il s’agit d’une classe.
JAX-WS 2.0 travaille de pair avec JAXB 2.0 pour plier les objets Java. JAXB a également recours aux annotations pour préciser le pliage sous forme d’objet XML. Nous allons en utiliser 3 :
- @XmlType : il s’agit du point départ. elle porte sur l’objet et elle indique que l’on souhaite surcharger le comportement par défaut de JAXB. Elle est un prérequis pur l’utilisation des deux autres. Je l’utilise pour surcharger le nom du type XML correspondant à l’objet. Attention ! Son nom est présent dans la partie Schema du WSDL, mais son nom n’intervient pas dans le balisage XML du message SOAP. C’est également lui qui permet de surcharger le comportement par défaut du targetNameSpace. Il permet pour finir de définir l’ordre d’apparition des propriétés, mais j’avoue ne pas très bien comprendre son utilité.
- @XmlAccessorType : il permet de choisir entre deux façons de plier un objet Java en XML : XmlAccessType.FIELD dans ce cas, chaque propriété correspond à un nœud XML, XmlAccessType.ATTRIBUTE dans ce cas, il n’y a qu’un seul nœud XML pour l’objet et les propriétés correspondent à des attributs du nœud XML. Je pense que le plus robuste et standard est XmlAccessType.FIELD, c’est pour cela que je préfère son utilisation.
- @XmlElement : Il se place au niveau de chaque propriété de la classe. Son utilisation nécessite l’annotation XmlType au niveau de la classe. Il permet de préciser le nom du nœud XML correspondant à la propriété. L’attribut required à true retire le minOccurs à 0 du WSDL.
Pour plus d’information sur JAXB, je vous renvoie au très bon article : http://blog.paumard.org/cours/jaxb-rest/chap01-jaxb-annotations.html.
On édite la classe Java Question pour modifier sa première partie. Les constructeurs et getter/setter ne sont pas modifiés.
Le début du code (jusqu’au constructeur) est modifié afin d’obtenir le code suivant :
package fr.j2ltho.webcontracthelpdesk.server;
import java.util.Date;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
@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")
private Date submitDate;
@XmlElement(name = "TITLE", required=true)
private String title;
@XmlElement(name = "PRODUCT")
private String product;
@XmlElement(name = "URGENCY")
private UrgencyEnum urgency;
@XmlElement(name = "PRIORITY", required=true)
private String priority;
@XmlElement(name = "TYPE")
private String type;
public Question() {
super();
}
Certaines annotations JAXB comme le nillable ne sont pas traitées par JAX-WS. Elles n’auront alors aucun effet sur le WSDL.
Attention ! Les annotations JAXB peuvent nécessiter un jar différent que celles de JAX-WS. C’est notamment le cas de JBoss 5.1 qui nécessite jaxb-api.jar.
Si vous avez suivi à la lettre mon Etape 2 : Configuration d'un projet Eclipse pour un WebService JAX-WS de ce tutorial et que vous utilisez JBoss 5.1, vous ne devriez pas avoir de problème.
XmlEnum et XmlEnumValue
Nous avions définis une énumération comme propriété, nous allons également annoter cette enum java afin de maitriser également sa transformation XML.
L’annotation XmlEnumValue permet de définir la chaine de caractères qui correspond à la valeur de l’énumération dans le message.
Editez UrgencyEnum et modifiez la afin d’obtenir le code suivant :
package fr.j2ltho.webcontracthelpdesk.server;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlEnumValue;
import javax.xml.bind.annotation.XmlType;
@XmlType
@XmlEnum(String.class)
public enum UrgencyEnum {
@XmlEnumValue("URGENCY_BUGINPROD") BUGINPROD,
@XmlEnumValue("URGENCY_CONFIG") CONFIG,
@XmlEnumValue("URGENCY_USAGE") USAGE,
@XmlEnumValue("URGENCY_BUGINDEV") BUGINDEV
}
Conclusion
Nous avons finis d’annoter nos classes, interface et énumérations Java. Vous allez voir dans l’étape suivante à quoi correspond le WSDL généré automatiquement à partir de ces annotations.
Vous constaterez alors que nous avons une très grande maitrise grâce aux différentes annotations utilisées à cette étape. Nous n’avons pas progressé sur l’implémentation du service. Nous sommes bien encore dans une approche Contract First : jusqu’à présent tous nos efforts ont consisté à définir l’interface du service web.
Etapes : 1 | 2 | 3 | 4| 5 – Suivant >
DiggIt!
Enregistrer sur Del.icio.us
0 commentaires:
Enregistrer un commentaire