Tutorial Service Web avec JBosss 5.1
DiggIt!
Enregistrer sur Del.icio.us
Ce tutorial actualise la version que j’avais écrite en 2007 avec le serveur JBoss 4.2 : elle est disponible à l’url suivante : Tutorial Service Web avec JBoss 4.2. Ce nouveau tutorial présente la création d’un Service Web très simple avec JBoss 5.1.
Une rapide présentation des Services Web avec JBoss
JBoss s’appuie sur les EJB 3.0 pour définir et implémenter les Services Web. La version 5.1 de JBoss inclut à la fois le conteneur d’EJB et un module Service Web.Aucun rappel ne sera fait sur les EJB dans ce post : je vous renvoie donc au post que j’avais écris à l’époque : Tutorial EJB 3.0 de session avec JBoss 4.2
Le module service web fait partie du package default. Vous devez trouver dans le répertoire deploy de la configuration default, un sous répertoire jbossws.sar.
JBoss respecte les spécifications de JAX-WS pour la définition des services web. Cette norme définit un ensemble d’annotations qui permettent la transformation d’un EJB 3.0 en service web. Les annotations sont interprétées par le serveur d’application afin de transformer les classes Java en service web.
La définition des différentes annotations peuvent être trouvées sur le site de Sun à l’adresse suivante : http://java.sun.com/javaee/5/docs/api/javax/jws/package-summary.html
Les annotations simplifient grandement la réalisation de services web. Elles permettent de définir :
- toutes les informations pertinentes pour construire un WSDL à partir de la classe Java
- les correspondances entre les messages SOAP du service web et la méthode de la classe Java responsable du traitement.
Par exemple, il est possible de définir le nom du service web, des opérations et des paramètres.
Les services web nécessite la présence d’un serveur d’application capable d’être serveur de service web : dans notre tutorial nous allons utiliser JBoss dans sa version 5.1.
Organisation du tutorial
Ce tutorial a pour objectif de présenter une mise en œuvre basique d’un Service Web avec JBoss 5.1.
Nous ne reprendrons pas l’installation et la configuration de JBoss 5.1 décrite dans le tutorial : Installation de JBoss 5.1 sur Windows 7 64 bits
Un Service Web est avant tout un EJB, il est possible de reprendre n’importe quel EJB et de le compléter afin de le transformer en Service Web.
Ce tutorial repose sur l’utilisation d’Eclipse 3.5 et sa distribution JEE.
Le tutorial comprendra les étapes suivantes :
- La réalisation du service web dans Eclipse
- La construction du client pour ce service web réalisé dans un autre projet Eclipse.
- L’analyse du code et du WSDL
Configuration
Le tutorial a été réalisé sur un poste Windows 7.
Le runtime et le jdk Java sont à la version 1.5.0_11.
Pour JBoss, j’ai utilisé la version 5.1 : l’installation est décrite dans le billet : Installation de JBoss 5.1 sur Windows 7 64 bits
Création d’un projet Eclipse pour le service web
Dans Eclipse, on crée un projet Java simple que l’on nommera MaCalculetteSW.
Dans Eclipse, à partir de la barre de menu : File -> New -> Project.
Dans la dialogue New Project, choisir le dossier Java puis le nœud Java Project. Appuyez sur le bouton Next.
Pour Project name, tapez : MaCalculetteSW et laissez les autres options par défaut.
Appuyez sur Finish. Acceptez le choix de la perspective Java si la dialogue « Open Associated Perspective » s’affiche.
Il faut importer des jar JBoss : cliquez droit sur le projet dans l’explorateur et choisissez Properties. Dans la dialogue qui s’ouvre choisir « Java Build Path ».
Appuyer sur « Add External Jars.. », et utiliser l’explorateur pour aller dans le répertoire C:\jboss-5.1.0.GA\common\lib et sélectionnez les jar suivant :
- Jboss-javaee.jar qui définit les annotations @Stateless et @Remote
- Jbossws-native-jaxws.jar qui définit les annotations @WebService, @SOAPBinding et @SOAPBinding
Validez en appuyant sur Ok.
Notre poste est maintenant correctement configuré pour construire un Service Web.
Pour information, avec JBoss 6.0 Final, les jars ont de nouveau changé : il s’agit de :
- jboss-ejb-api_3.1_spec.jar
- jsr181-api.jar
qui se trouvent tous les deux dans c:\jboss-6.0.0\common\lib
Création de l’interface
Nous commençons par créer l’interface du service. L’interface définit les méthodes qui seront disponibles dans l’EJB et le service web.
Nous allons créer l’interface fr.j2ltho.calculette.ejb. MaCalculette. Par rapport à la définition d’un EJB, il faut ajouter deux autres informations au niveau de la classe :
- Le nom du service web qui sera dans notre cas MaCalculetteSw
- Le format et le style des messages échangés entre le client et le serveur. En d’autre terme, on choisit entre RPC/encoded et Document/literal, tout comme on choisit pour un Document/Literal s’il est wrapped ou unwrapped. Le format le plus répandu et portable étant Document/Literal/Wrapped c’est ce dernier que l’on retiendra.
Ensuite pour chaque méthode, il faut indiquer qu’elle est accessible comme opération du service web, il est possible de définir son nom pour le service web, le nom du paramètre de sortie et le nom des paramètres dans le fichier WSDL (ce point est important car par défaut, leur nom est arg0, arg1…).
Création de l’interface
Dans Eclipse, à partir de la barre de menu : File -> New -> Interface.
Dans la dialogue New Java Interface:
- Pour Package, tapez : fr.j2ltho.calculette.ejb
- Pour Name tapez : MaCalculette
Laissez les autres options par défaut et appuyez sur Finish.
On modifie le code pour définir les deux méthodes add et substract du service.
On ajoute l’annotation @Remote pour spécifier au conteneur d’EJB qu’il devra créé un lien de type Remote avec cette interface pour l’EJB.
Ajout des annotations Service Web
On ajoute l’annotation @WebService qui déclare la classe comme un service web. Le paramètre name, permet de définir un nom différent de celui de la classe. C’est le nom du portType du WSDL. Ce sera le nom également de la classe d’interface cliente si on utilise l’outil JBoss wsconsume pour créer les classes clientes à partir du WSDL.
On ajoute ensuite l’annotation @SOAPBinding qui permet de définir le format des messages :
- Le paramètre style permet de choisir entre DOCUMENT et RPC,
- le paramètre use permet de choisir entre LITERAL et ENCODED
- le paramètre parameterStyle entre WRAPPED ou BARE (unwrapped).
Il faut ensuite pour chaque méthode ajouter le tag @WebMethod pour indiquer qu’il s’agira d’une opération du service web. Le paramètre operationName permet de définir un nom dans le WSDL différent de celui de la méthode Java. Il est également possible grâce au tag @WebResult de définir le nom du paramètre de sortie de la méthode tel qu’il apparait dans le WSDL.
Pour chaque paramètre, il est possible de définir grâce au tag @WebParam son nom (par défaut ce sera arg0, arg…) mais également grâce à la propriété mode si un paramètre est IN, OUT ou IN/OUT.
On obtient le code suivant (en gras le code ajouté par rapport à l’EJB):
package fr.j2ltho.calculette.ejb;
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 = "MaCalculetteSw")
@SOAPBinding(style=Style.DOCUMENT, use=SOAPBinding.Use.LITERAL, parameterStyle= SOAPBinding.ParameterStyle.WRAPPED)
public interface MaCalculette {
@WebMethod(operationName="ajouter")
@WebResult(name = "resultat")
public int add(int x, int y);
@WebMethod
public int subtract(
@WebParam(name = "x")
int x,
@WebParam(name = "y")
int y);
}
Création du Bean
Pour la classe qui implémente le service web défini par l’interface précédente, il est possible d’utiliser l’implémentation de l’EJB : Il suffira d’y ajouter l’annotation @Webservice en indiquant la propriété endpointInterface : cette propriété doit pointer vers l’interface du service. C’est grâce aux informations contenue dans l’interface que le conteneur saura quel WSDL généré.
Nous commençons par créer la classe qui implémente le service.
Dans Eclipse, à partir de la barre de menu : File -> New -> Class.
Dans la dialogue New Java Class :
- Pour Package, tapez : fr.j2ltho.calculette.ejb
- Pour Name tapez : MaCalculetteBean
Appuyez, sur le bouton Add.. afin de définir l’interface. Dans la dialogue « Implemented Interfaces Selection » tapez MaCalculette puis sélectionnez l’interface MaCalculette – fr.j2ltho.calculette.ejb de votre projet. Validez en appuyant sur Ok. La dialogue se referme et l’interface apparait dans la liste Interfaces de la dialogue « New java Class ».
Laissez les autres options par défaut et appuyez sur Finish. Le code de la classe apparait avec les deux méthodes créées, il faut maintenant compléter le code.
Il faut également ajouter :
- l’annotation @Stateless en début de classe. Cette annotation précise que cet EJB sera sans état : il ne conserve pas une mémoire de ce qu’il a fait précédemment.
- l’annotation @WebService pour indiquer la classe d’interface qui contient les annotations décrivant le service web. Il permet également de définir le nom du service web tel qu’il apparait dans le WSDL.
On obtient le code suivant :
package fr.j2ltho.calculette.ejb;
import javax.ejb.Stateless;
import javax.jws.WebService;
@Stateless
@WebService(endpointInterface="fr.j2ltho.calculette.ejb.MaCalculette", serviceName="MaSwCalculette")
public class MaCalculetteBean implements MaCalculette {
public int add(int x, int y) {
return x + y ;
}
public int subtract(int x, int y) {
return x - y;
}
}
Génération du jar
Assurez vous d’avoir bien sauvegarder la classe et son interface.
Cliquez droit dans l’explorateur de ressource d’Eclipse sur votre projet MaCalculetteSW, sélectionnez Export…, dans la dialogue « Export » qui s’affiche choisissez, dans Java, JAR file. Puis Next. Sélectionner l’emplacement et le nom du JAR File. Pour ma part je l’ai créé directement dans le répertoire C:\jboss-5.1.0.GA\server\default\deploy qui est le répertoire de déploiement pour la configuration par default. Le nom du fichier est MaCalculette.jar.
Le nom du jar n’a aucune importance, il n’est pas utilisé pour obtenir le WSDL et n’a aucun d’impact sur le contenu du WSDL.
Appuyez, ensuite sur Finish. Vous devriez trouver un fichier MaCalculette.jar dans le répertoire C:\jboss-5.1.0.GA\server\default\deploy.
On lance JBoss, si on tape l’url suivante dans un browser : http://localhost:8080/jbossws/services une page JBossWS/Services s’affiche. Il doit y a avoir une page avec un ServiceEndID avec MaCalculetteBeanService, un WSDL est accessible sur la ligne suivante : http://127.0.0.1:8080/MaCalculette/MaCalculetteBean?wsdl
Analyse du WSDL
En cliquant sur le lien précédent : http://127.0.0.1:8080/MaCalculette/MaCalculetteBean?wsdl on affiche le WSDL du service.
Nous allons analyser succinctement son contenu afin de mettre en évidence les correspondances entre les annotations et le contenu du WSDL.
ServiceName
Tout d’abord le premier tag, le tag definition :
<definitions name="MaSwCalculette" targetNamespace="http://ejb.calculette.j2ltho.fr/">
Il contient le nom du Service Web tel qu’il est définit par la propriété serviceName dans l’annotation @WebService du bean d’implémentation.
On retrouve ce nom de service dans le tag service à la fin du WSDL :
<service name="MaSwCalculette">
Le service web est la collection de protocoles qu’il est possible d’utiliser pour déclencher un traitement donné. Dans certains cas plusieurs protocoles peuvent être mis à disposition comme SOAP 1.1, SOAP 1.2 et http.
Choix du protocole et du format des messages
Le protocole est de type SOAP 1.1. Un seul mécanisme est retenu dans cette implémentation. Il n’y a qu’un seul tag port dans le tag service, de même il n’y a qu’un seul tag binding. C’est la présence de l’annotation @SOAPBinding qui est responsable du choix du protocole SOAP.
Cette annotation permet également de définir le format du message :
Le paramètre style permet de choisir entre DOCUMENT et RPC : cela est visible dans le tag soap:binding la propriété style indique le style.
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
Le paramètre use permet de choisir entre LITERAL et ENCODED : cela est visible dans les tags soap:body avec la propriété use qui indique le mécanisme d’encodage :
<soap:body use="literal" />
Pour finir, le paramètre parameterStyle permettait de choisir entre WRAPPED ou BARE (unwrapped). Le mode WRAPPED implique que dans le message appelant, les paramètres sont encapsulés dans un type portant le nom de l’opération. On peut le vérifier dans la partie message ou operation du WSDL où l’on voit la définition de l’opération ajouter indiquer un input message :
<operation name="ajouter" parameterOrder="ajouter">
<input message="tns:MaCalculetteSw_ajouter" />
<output message="tns:MaCalculetteSw_ajouterResponse" />
</operation>
Cet input message fait référence à un message composé d’un unique élément qui porte comme nom ajouter (le nom de l’opération) :
<message name="MaCalculetteSw_ajouter">
<part element="tns:ajouter" name="ajouter" />
</message>
Les annotations sur les méthodes
Les annotations @WebMethod, @WebResult et @WebParam utilisées dans l’interface modifie les noms par défaut des opérations dans le WSDL.
On a pu constater que la propriété operationName de @WebMethod a permis pour la méthode add que celle-ci apparaisse sous le nom de ajouter dans le WSDL. Le nom par défaut aurez été add (on peut le vérifier avec la méthode substract).
Avec l’opération ajouter, on constate que les noms des arguments de la méthode sont peu significatifs :
<xs:complexType name="ajouter">
<xs:sequence>
<xs:element name="arg0" type="xs:int" />
<xs:element name="arg1" type="xs:int" />
</xs:sequence>
</xs:complexType>
Le WSDL étant le contrat entre le client et le serveur, il est également la première source de documentation pour utiliser un service web, il est donc souhaitable que les noms d’éléments soient plus significatifs. On a utiliser pour cela l’annotation @WebParam pour la méthode substract afin d’imposer la valeur de la propriété name. Il faut également savoir que c’est cette valeur qui sera prise par l’outil wsconsume de JBoss afin de nommer les paramètres lors de la génération du stub client (comme on le verra plus loin). En conséquence des noms correctes et significatifs vont grandement simplifier l’utilisation du service.
Pour finir, on constate que l’annotation @WebResult a permis de renommer le nom de l’élément de sortie (la valeur renvoyée par la méthode) :
<xs:complexType name="ajouterResponse">
<xs:sequence>
<xs:element name="resultat" type="xs:int" />
</xs:sequence>
</xs:complexType>
On obtient une valeur resultat au lieu de return : cela n’a priori pas d’intérêt particulier dans notre cas.
PortType ou name de @WebService
On constate que la propriété name du tag portType du WSDL reprend la valeur de la propriété name de l’annotation @Webservice de l’interface.
<portType name="MaCalculetteSw">
Ce nom est utilisé lors de la génération des classes Java cliente avec l’outil wsconsume comme nom pour la classe d’interface qui sera utilisée. Je vous conseille donc d’utiliser le même nom que celui de l’interface afin de simplifier votre développement.
Types des données
Les données pour les types simples qu’on a utilisés utilisent des types simples du XMLSchema :
<xs:element name="arg0" type="xs:int" />
<xs:element name="arg1" type="xs:int" />
Ces types simples sont indépendants des langages de programmation. Cela est un point positif.
Création d’un projet Eclipse pour le client
Qu’on est récupéré le projet EJB ou créé un projet de rien, il faut dans tous les cas créé un nouveau client. Dans Eclipse, on crée un second projet Java simple que l’on nommera MonClientSW.
Dans Eclipse, à partir de la barre de menu : File -> New -> Project.
Dans la dialogue New Project, choisir le dossier Java puis le nœud Java Project. Appuyez sur le bouton Next.
Pour Project name, tapez : MonClientSW et laissez les autres options par défaut.
Appuyez sur Finish. Acceptez le choix de la perspective Java si la dialogue « Open Associated Perspective » s’affiche.
Il faut importer des jar JBoss nécessaire à l’appel d’un Service Web : cliquez droit sur le projet dans l’explorateur et choisissez Properties. Dans la dialogue qui s’ouvre choisir « Java Build Path ».
Appuyer sur « Add External Jars.. », et utiliser l’explorateur pour aller dans le répertoire C:\jboss-5.1.0.GA\client et sélectionnez les jar :
- activation.jar
- javassist.jar
- jaxb-api.jar
- jaxb-impl.jar
- jaxb-xjc.jar
- jboss-serialization.jar
- jboss-system-client.jar
- mail.jar
- policy.jar
- stax-api.jar
- wsdl4j.jar
- wstx.jar
- jbossws-native-core.jar
- jbossws-native-jaxrpc.jar
- jbossws-native-jaxws.jar
- jbossws-native-jaxws-ext.jar
- jbossws-native-saaj.jar
- jboss-xml-binding.jar
- ejb3-persistence.jar
- jbossws-spi.jar
- jbossws-common.jar
- jboss-logging-spi.jar
- jboss-common-core.jar
- commons-logging.jar
- jboss-remoting.jar
- serializer.jar
- xalan
- xercesImpl
Validez en appuyant sur Ok.
Génération des stubs
Nous allons utiliser l’utilitaire wsconsume.bat qui est livré avec JBoss. Il se trouve dans le répertoire C:\jboss-5.1.0.GA\bin. Cet utilitaire permet à partir d’un WSDL de générer un ensemble de classes qui facilitent l’appel du Service Web.
On ouvre une fenêtre invite de commandes DOS. On se positionne sur le répertoire, il faut positionner la variable JAVA_HOME vers l’installation Java de votre poste.
On lance la commande en indiquant à la fois le WSDL et le répertoire du projet Eclipse :
Cd C:\jboss-5.1.0.GA\bin
SET JAVA_HOME=C:\Program Files\Java\jdk1.6.0_18
wsconsume.bat -k -p fr.j2ltho.client.jboss.ejb http://127.0.0.1:8080/MaCalculette/MaCalculetteBean?wsdl -o "C:\Documents and Settings\jltho\Mes documents\PrjJava\MonClientSW\src"
Où C:\Documents and Settings\j2ltho\Mes documents\PrjJava est le nom de mon répertoire contenant mes projets Java.
Si vous avez l’erreur “Le chemin d'accès spécifié est introuvable.”, c’est que probablement la variable JAVA_HOME a été incorrectement spécifiée.
Les lignes suivantes s’affichent :
fr\j2ltho\client\jboss\ejb\Ajouter.java
fr\j2ltho\client\jboss\ejb\AjouterResponse.java
fr\j2ltho\client\jboss\ejb\MaCalculetteSw.java
fr\j2ltho\client\jboss\ejb\MaSwCalculette.java
fr\j2ltho\client\jboss\ejb\ObjectFactory.java
fr\j2ltho\client\jboss\ejb\Subtract.java
fr\j2ltho\client\jboss\ejb\SubtractResponse.java
fr\j2ltho\client\jboss\ejb\package-info.java
Dans Eclipse si on fait F5 sur le projet, un nouveau package fr.j2ltho.client.jboss.ejb apparait.
Création de la classe cliente
Dans Eclipse. Nous allons créer une classe de type Main afin d’appeler ce service web.
Dans Eclipse, à partir de la barre de menu : File -> New -> Class.
Dans la dialogue New Java Class :
- Pour Package, tapez : fr.j2ltho.client.test.sw
- Pour Name tapez : TestMaCalculetteMain
- Cochez la checkbox public static void main(String[] args)
et laissez les autres options par défaut.
Appuyez sur Finish.
Le code à ajouter est très simple :
On récupère d’abord un objet sur le service web que l’on souhaite appeler. Le nom de la classe correspond au nom du service. Il s’agit à ce niveau d’une collection de protocole de communication qu’il est possible d’appeler.
Ensuite on récupère une interface sur ce service : c’est cette interface que l’on va pouvoir utiliser comme une simple classe. Vous pouvez constater que sa définition ressemble à celle de l’interface client. Le nom de la classe est celui du tag portType du WSDL. Comme on a forcé son nom à MaCalculetteSw, c’est ce nom qu’il prend. Dans la pratique, il est probablement plus intéressant de conserver le même nom que l’interface.
Pour finir, on appelle les deux services.
package fr.j2ltho.client.test.sw;
import java.util.Date;
import fr.j2ltho.client.jboss.ejb.MaCalculetteSw;
import fr.j2ltho.client.jboss.ejb.MaSwCalculette;
public class TestMaCalculetteMain {
/**
* @param args
*/
public static void main(String[] args) {
System.out.println("debut:"+new Date());
MaSwCalculette service = new MaSwCalculette();
MaCalculetteSw calculator =service.getMaCalculetteBeanPort();
System.out.println("apres initialisation:"+new Date());
System.out.println(calculator.ajouter(2, 4));
System.out.println("fin du add:"+new Date());
System.out.println(calculator.subtract(8, 3));
System.out.println("fin du substract:"+new Date());
}
}
Création du fichier META-INF/standard-jaxws-client-config.xml
Dans le répertoire src, on créé un nouveau Folder META-INF : Cliquez droit sur le répertoire src et sélectionnez New –> Other. La dialogue “New – Select a wizard” apparait. Choisir dans le noeud General, l’élément Folder et appuyez sur Next. Saisir le nom du folder qui doit impérativement être META-INF et validez.
Le répertoire META-INF a été créé dans le répertoire src de votre projet.
Ensuite, on copie depuis C:\jboss-5.1.0.GA\server\default\deployers\jbossws.deployer\META-INF, le fichier satandard-jaxws-client-config.xml dans le nouveau répertoire META-INF : il suffit de faire un glisser/déposer de l’explorateur Windows vers le répertoire META-INF dans le Project explorer d’Eclipse.
Aucune adaptation n’est nécessaire à ce fichier. Il s’agit d’une nouveauté nécessaire avec JBoss 5.1.
Sans ce fichier vous auriez eu à l’exécution l’erreur suivante :
exception in thread "main" org.jboss.ws.WSException: Cannot find configFile: META-INF/standard-jaxws-client-config.xml
at org.jboss.ws.metadata.config.JBossWSConfigFactory.filenameToURL(JBossWSConfigFactory.java:196)
at org.jboss.ws.metadata.config.JBossWSConfigFactory.getConfig(JBossWSConfigFactory.java:150)
at org.jboss.ws.metadata.umdm.EndpointMetaData.initEndpointConfigMetaData(EndpointMetaData.java:874)
at org.jboss.ws.metadata.umdm.EndpointMetaData.initEndpointConfig(EndpointMetaData.java:851)
at org.jboss.ws.metadata.builder.jaxws.JAXWSClientMetaDataBuilder.rebuildEndpointMetaData(JAXWSClientMetaDataBuilder.java:292)
at org.jboss.ws.core.jaxws.spi.ServiceDelegateImpl.getPortInternal(ServiceDelegateImpl.java:269)
at org.jboss.ws.core.jaxws.spi.ServiceDelegateImpl.getPort(ServiceDelegateImpl.java:200)
at javax.xml.ws.Service.getPort(Service.java:141)
at fr.j2ltho.client.jboss.ejb.MaSwCalculette.getMaCalculetteBeanPort(MaSwCalculette.java:55)
at fr.j2ltho.client.test.sw.TestMaCalculetteMain.main(TestMaCalculetteMain.java:18)
Tester l’application
Pour tester, on sauve, puis on clique droit sur la classe TestMaCalculetteMain dans le Package Explorer d’Eclipse. Choisissez le menu Run As, puis le sous menu Java Application.
La console affichée en bas est rafraichie et affiche deux lignes :
debut:Tue May 22 10:59:32 CEST 2007
apres initialisation:Tue May 22 10:59:33 CEST 2007
6
fin du add:Tue May 22 10:59:33 CEST 2007
5
fin du substract:Tue May 22 10:59:33 CEST 2007
On constate que le temps d’initialisation de l’appel est long (de l’ordre de 2s sur mon poste, un core duo 2 avec 2 Go). La documentation de JBoss indique que cette opération est longue et ne doit être faite qu’une seule fois et recommande de conserver cette classe en mémoire. Le test met l’accent sur l’importance de suivre cette recommandation.
Quelques exceptions et leur solution
Lorsqu’on lance l’exécution de l’application de test, on est susceptible d’obtenir des exceptions s’il manque un ou plusieurs jar. J’indique ci dessous certaines des erreurs que j’ai rencontré et le jar que j’ai du ajouté pour résoudre le problème :
L’exception suivante est résolue par l’ajout du jar : jbossws-spi.jar
Exception in thread "main" java.lang.NoClassDefFoundError: org/jboss/wsf/spi/deployment/UnifiedVirtualFile
at org.jboss.ws.core.jaxws.spi.ProviderImpl.createServiceDelegate(ProviderImpl.java:63)
L’exception suivante est résolue par l’ajout du jar : jbossws-common.jar
Exception in thread "main" java.lang.NoClassDefFoundError: org/jboss/wsf/common/ResourceLoaderAdapter
at org.jboss.ws.core.jaxws.spi.ServiceDelegateImpl.<init>(ServiceDelegateImpl.java:118)
L’exception suivante est résolue par l’ajout du jar : policy.jar
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/ws/policy/Policy
at org.jboss.ws.metadata.builder.jaxws.JAXWSClientMetaDataBuilder.buildMetaData(JAXWSClientMetaDataBuilder.java:91)
at org.jboss.ws.core.jaxws.spi.ServiceDelegateImpl.<init>(ServiceDelegateImpl.java:138)
L’exception suivante est résolue par l’ajout du jar : jboss-remoting.jar
Exception in thread "main" javax.xml.ws.WebServiceException: java.lang.IllegalStateException: Failed to load org.jboss.ws.core.client.RemoteConnection.http: org.jboss.ws.core.client.SOAPProtocolConnectionHTTP
at org.jboss.ws.core.jaxws.client.ClientImpl.handleRemoteException(ClientImpl.java:396)
at org.jboss.ws.core.jaxws.client.ClientImpl.invoke(ClientImpl.java:302)
at org.jboss.ws.core.jaxws.client.ClientProxy.invoke(ClientProxy.java:170)
at org.jboss.ws.core.jaxws.client.ClientProxy.invoke(ClientProxy.java:150)
at $Proxy15.ajouter(Unknown Source)
at fr.j2ltho.client.test.sw.TestMaCalculette3Main.main(TestMaCalculette3Main.java:22)
Caused by: java.lang.IllegalStateException: Failed to load org.jboss.ws.core.client.RemoteConnection.http: org.jboss.ws.core.client.SOAPProtocolConnectionHTTP
at org.jboss.wsf.spi.util.ServiceLoader.loadFromServices(ServiceLoader.java:97)
at org.jboss.wsf.spi.util.ServiceLoader.loadService(ServiceLoader.java:59)
at org.jboss.ws.core.client.RemoteConnectionFactory.getRemoteConnection(RemoteConnectionFactory.java:61)
at org.jboss.ws.core.CommonClient.invoke(CommonClient.java:339)
at org.jboss.ws.core.jaxws.client.ClientImpl.invoke(ClientImpl.java:290)
... 4 more
Caused by: java.lang.NoClassDefFoundError: org/jboss/remoting/marshal/Marshaller
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2357)
at java.lang.Class.getConstructor0(Class.java:2671)
at java.lang.Class.newInstance0(Class.java:321)
at java.lang.Class.newInstance(Class.java:303)
at org.jboss.wsf.spi.util.ServiceLoader.loadFromServices(ServiceLoader.java:92)
... 8 more
L’exception suivante est résolue par l’ajout du jar : commons-logging.jar
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
at org.apache.ws.policy.util.Loader.<clinit>(Loader.java:32)
at org.apache.ws.policy.util.PolicyFactory.getPolicyReader(PolicyFactory.java:56)
at org.jboss.ws.extensions.policy.metadata.PolicyMetaDataBuilder.processPolicyExtensions(PolicyMetaDataBuilder.java:155)
at org.jboss.ws.metadata.builder.jaxws.JAXWSClientMetaDataBuilder.buildMetaData(JAXWSClientMetaDataBuilder.java:92)
at org.jboss.ws.core.jaxws.spi.ServiceDelegateImpl.<init>(ServiceDelegateImpl.java:138)
at org.jboss.ws.core.jaxws.spi.ProviderImpl.createServiceDelegate(ProviderImpl.java:63)
at javax.xml.ws.Service.<init>(Service.java:79)
L’exception suivante est résolue par l’ajout du jar : jboss-common-core.jar
Exception in thread "main" java.lang.NoClassDefFoundError: org/jboss/util/NotImplementedException
at org.jboss.ws.metadata.umdm.ServiceMetaData.<init>(ServiceMetaData.java:85)
at org.jboss.ws.metadata.builder.jaxws.JAXWSClientMetaDataBuilder.buildMetaData(JAXWSClientMetaDataBuilder.java:80)
at org.jboss.ws.core.jaxws.spi.ServiceDelegateImpl.<init>(ServiceDelegateImpl.java:138)
at org.jboss.ws.core.jaxws.spi.ProviderImpl.createServiceDelegate(ProviderImpl.java:63)
at javax.xml.ws.Service.<init>(Service.java:79)
Conclusion
L’exemple est volontairement simple et certainement pas très significatif vis-à-vis de la problématique d’un véritable projet. Néanmoins, il me parait intéressant de mettre l’accent sur quelques points qui devront être confirmé avant de s’enthousiasmer plus :
- Le WSDL généré est standard. Pour cet exemple simple il ne contient aucune référence au langage Java. Sur un exemple un peu plus compliqué en utilisant l’approche POJO de Axis2 cela n’avait pas été du tout le cas.
- Les diverses annotations ont permis de générer un WSDL qui peut être extrêmement précis et indépendant de l’implémentation Java. On obtient ainsi à la fois des classes Java proche du métier mais également le WSDL que l’on souhaite. Toujours sur un exemple plus compliqué, j’ai eu beaucoup plus de mal avec Axis2 (voir le tutorial http://jl2tho.blogspot.com/2007/04/tutorial-services-web-avec-axis2.html)
- A priori, le code est indépendant de la solution retenu : tout serveur d’application respectant JAX-WS doit être capable d’héberger le service web. Le Jar ServiceWeb généré avec un runtime JBoss 4.2 fonctionne sur JBoss 5.1. Seule l’URL du WSDL a changé. Le code du service et sa définition (annotation) ne change pas.
- Le code client généré par l’utilitaire wsconsume.bat de JBoss est plus simple et plus portable que celui généré par wsdl2java d’Axis2 en utilisant le binding ADB.
- L’EJB est accessible : pour ceux qui ont réalisé le tutorial sur les EJB, ils peuvent relancer l’application cliente et constater qu’elle continue de s’exécuter correctement.
- Les modèles de programmations pour le client que l’on utilise un EJB, un service web ou bien que l’on appelle directement la classe d’implémentation restent très proches. La seule différence réside dans l’obtention de la classe d’interface, le reste du code peut être identique. Cela peut être un gros avantage pour la mise en œuvre de la solution lors du développement, en supprimant les étages.
Comme on le voit tout cela semble très positif. Le seul point qui parait négatif mais pour lequel je n’ai pas de comparaison c’est l’aspect performance : la création d’un bean permettant l’appel d’un service web est long.
DiggIt!
Enregistrer sur Del.icio.us
3 commentaires:
Bonjour.
Il est très agréable en tant que novice d'avoir un tutoriel pas à pas! bravo.
Lors de l’exécution, j'ai eu l'erreur suivante :
Exception in thread "main" javax.xml.ws.WebServiceException: java.lang.UnsupportedOperationException: setProperty must be overridden by all subclasses of SOAPMessage
En relisant le tuto, la seule différence vient de la version de mon jdk et d'eclipse.
Est-il possible d'avoir le même genre de tuto avec une version jdk plus récente (jdk 7 ou 6)?
Attention ! au moment où j'écris ces lignes, la maturité du JDK 7 n'est pas complète et ni Jboss ni Tomcat ne supportent officiellement ce dernier. Je conseille pour le moment de rester avec le 1.6. cela peut poser un problème pour toute personne travaillant sur Eclipse Indigo qui est par défaut en 7.
Précision pour Anonyme, ce tutorial a été fait avec un JDK 6.
Pour le message d'erreur il semble concerner l’exécution du client (référence à Main) il n'est pas impossible que cela concerne un problème de compatibilité si le test est fait en JDK 7.
Enregistrer un commentaire