Quand nous avons développé le web service LifeLeft, vous avez remarqué que nous avions soigneusement évité de prendre en compte les cas où des erreurs peuvent apparaître. Je pense par exemple au cas ou le service distant fournit à l'opération anneesRestantesAVivre une date de naissance dans le futur par exemple.
Comment sont indiquées les erreurs dans un WSDL ?
Il y a une balise spéciale pour renvoyer une erreur : < fault >. Quand votre service rencontre une exception, il renvoie donc une réponse spéciale de type fault. Fault est un complexType qui se compose éventuellement d'un code d'erreur et d'un message d'erreur.
Nous allons voir comment il est déclaré en détail plus tard quand on aura généré notre WSDL.
Générer une exception
On pourrait croire qu'il suffit de lancer une exception comme on ferait normalement, et que JAX-WS générera la balise fault correspondante. Cela peut marcher mais cela génère beaucoup de problèmes du côté client.
Pour gérer les exceptions correctement, il nous faudra le faire comme le préconisent les spécifications de JAX-WS, c'est-à-dire avec :
Une Class POJO dans laquelle nous allons stocker les messages d'erreur. Si vous ne savez pas ce que une Class POJO, c'est une classe basique, qui contient quelques attributs avec leurs getters et setters pour les manipuler, littéralement : Plain Old Java Class. Rien de compliqué.
Une Class qui représente notre exception et qui hérite de Exception.
Créez un nouveau package sous le dossier java et appelez-le exceptions.
Commençons par la POJO, créez une Class et nommez-la LifeLeftFault.
package exceptions;
public class LifeLeftFault {
// code d'erreur
private String faultCode;
//message d'erreur
private String faultString;
//Tous les getters et setters des 2 attributs précédentes
public String getFaultCode() {
return faultCode;
}
public void setFaultCode(String faultCode) {
this.faultCode = faultCode;
}
public String getFaultString() {
return faultString;
}
public void setFaultString(String faultString) {
this.faultString = faultString;
}
}
C'est une classe toute simple pour contenir les détails de l'erreur que nous allons retourner.
Créez une Class et nommez-la LeftLifeException, celle-ci contiendra le code de notre exception proprement dite. Cette Class doit répondre à des critères précis :
Être annotée avec @WebFault
Hériter de Exception
Avoir 2 constructeurs minimum
Avoir la méthode getFaultInfo qui retourne la POJO déjà créée
On obtient donc ceci :
package exceptions;
import javax.xml.ws.WebFault;
@WebFault(name = "LifeLeftException")
public class LifeLeftException extends Exception {
/*
on déclare une instance de LifeLeftFault qu'on créée précédemment
afin de récupérer ensuite les messages et codes d'erreurs à renvoyer dans cette exception
*/
private LifeLeftFault fault;
/*
On crée un constructeur qui prend en paramètre un message d'erreur et un objet LifeLeftFault
avec plus de détails sur l'erreur
*/
public LifeLeftException(String message, LifeLeftFault fault) {
super(message);
this.fault = fault;
}
public LifeLeftException(String message, Throwable cause, LifeLeftFault fault) {
super(message, cause);
this.fault = fault;
}
public LifeLeftFault getFaultInfo() {
return fault;
}
}
Le rôle du deuxième constructeur est de nous permettre de générer des exceptions sans le POJO en passant un Throwable à la place, afin de pouvoir utiliser les exceptions classiques de Java.
Vous devriez avoir l'arborescence suivante :
Il ne reste plus qu'à utiliser cette classe pour générer une exception quand le paramètre naissance est supérieur à 2017 par exemple.
@WebMethod
public String anneesRestantesAVivre (String prenom, String genre, Integer anneeNaissance) throws LifeLeftException {
//vaut mieux remplacer 2017 par Year.now().getValue(), mais pour simplifier on laisse 2017
if(anneeNaissance > 2017) {
//On créer une nouvelle instance de notre POJO
LifeLeftFault fault = new LifeLeftFault();
//On y ajoute le code d'erreur et le détail de l'erreur en question
fault.setFaultCode("1234");
fault.setFaultString("L'année reçu est supérieur l'année actuelle");
//on lance l'exception avec comme premier argument un message général sur l'erreur.
throw new LifeLeftException("Année invalide", fault);
}
if(genre.equals(homme)) evDeReference = ESPERANCE_VIE_HOMMES;
else evDeReference = ESPERANCE_VIE_FEMMES;
//Remarque, en cas de problème, vous pouvez changer Year.now().getValue() par Calendar.getInstance().get(Calendar.YEAR)
Integer anneeRestantes = evDeReference -(Year.now().getValue() - anneeNaissance );
return "Bonjour " + prenom + ", il vous reste " + anneeRestantes + " ans, à vivre, Profitez-en au maximum !";
}
Comme je l'ai indiqué dans les commentaires, on alimente le POJO avec les messages appropriés puis on le passe en argument à l'exception à retourner.
C'est fini ! Faites dans le panneau latéral de Maven un Clean puis un Install et uploadez le War sur Glassfish.
Tester
Retournez à SoapUI dans le projet LifeLeft. Allez cette fois dans TestSuite 2 que nous avons créé précédemment et double cliquez sur anneesRestantesAVivre. Remplissez les balises avec les arguments nécessaires en veillent à mettre une année supérieure à 2017 puis cliquez sur play.
Vous devriez alors avoir une balise fault en réponse avec faultstring contenant le message général qu'on a défini en premier argument de notre exception et les détails de l'erreur sous la balise detail.
Le WSDL
Rendez-vous à l'URL du WSDL de LifeLeft http://localhost:8080/lifeleft/LifeLeft?wsdl. Vous constaterez que pour gérer l'exception, les éléments suivants ont été ajoutés :
Sous < types > si vous allez au lien de schemaLocation qui contient les types complexes, vous verrez alors qu'un nouveau type complexe a été ajouté :
<xs:complexType name="lifeLeftFault"> <xs:sequence> <xs:element name="faultCode" type="xs:string" minOccurs="0"/> <xs:element name="faultString" type="xs:string" minOccurs="0"/> </xs:sequence> </xs:complexType>
Sous portType l'opération anneesRestantesAVivre a maintenant, en plus des input et output classiques, une autre possibilité de réponse : fault.
Cette dernière réponse possible est répercutée comme toutes les autres sous binding.