====== TP implémentation de services et de clients via JAXWS ====== === Mises à jour === * **2011-10-15**: ajout d'une indication sur la partie client. * **2011-10-15**: ajout de la date de rendu. * **2011-10-10**: mise à jour du dépôt pour correction de dépendance sur le projet ''modele'' ==== Modalités ==== Ce TP est à rendre pour le **23/10/2011**, à raison d'un rendu par binôme. * Les binômes sont à constituer au sein des groupes de 4-5 pour les étudiants faisant partie d'un tel groupe. Les étudiants sans binôme (dans les groueps de 5) peuvent constituer un binôme entre eux. * Les autres étudiants peuvent constituer un binôme ad-hoc pour ce TP. Il est demandé de rendre le TP sous forme d'un dépôt Mercurial qui aura été initialisé en clonant le dépôt indiqué ci-dessous. [[http://mercurial.selenic.com/|Mercurial]] est un gestionnaire de version, au même titre que par exemple [[http://git-scm.com/|git]]. A la différence de [[http://subversion.tigris.org/|svn]] ou [[http://www.nongnu.org/cvs/|CVS]] c'est système décentralisé, permettant de réaliser des //commit// sans connexion avec un quelconque serveur central. Une introduction rapide à Mercurial est [[http://mercurial.selenic.com/quickstart/|à disposition ici]]. * Si le temps le permet, un dépôt sera mis en place pour chaque binôme sur le serveur [[https://forge.univ-lyon1.fr]]. Il suffira alors de synchroniser votre travail sur ce dernier via la commande hg push url-du-depot-distant * Dans le cas contraire le rendu se fera par envoi via e-mail du dépôt //zippé//. Dans ce cas, il sera demandé de ne pas mettre les fichiers issus de la compilation du projet dans le zip, ce qui peut se faire d'une des manières suivantes: * cloner le projet courant dans un nouveau répertoire: hg clone mon-repertoire-de-dev le-repertoire-a-zipperpuis //zipper// le répertoire ainsi obtenu; * alternativement, exécuter mvn clean dans le projet racine (''projet'') avant la compression, en ayant quitter l'environnement de développement afin d'éviter toute recompilation intempestive. ===== Préliminaires ===== ==== Mise à jour des sources de base ==== Mettre à jour le projet de base: hg pull https://forge.univ-lyon1.fr/hg/tiw5-2011-tp-base Importer le projet ''services-impl'' dans le workspace Eclipse. ==== Logiciels additionnels nécessaires ==== * [[http://www.soapui.org/|soapUI]], soit sous forme de logiciel indépendant, soit sous forme de plugin((pour Eclipse, Netbeans ou IntelliJ)). * Une base PostgreSQL avec un rôle de connexion ''etudiant''/''etudiant'' ayant accès au schéma public de la base ''postgres''. ==== Configuration de Tomcat et Eclipse ==== Ajouter, si ce n'est déjà fait, un serveur tomcat 7.0 à votre configuration Eclipse((onglet servers -> New -> Server)). Créer un fichier ''src/main/webapp/META-INF/context.xml'' dans le projet ''web-interface'': Dans le projet ''web-interface'', ajouter le code suivant au fichier ''web.xml'': jdbc/EtudiantDS javax.sql.DataSource Container et créer un fichier ''src/main/webapp/WEB-INF/classes/META-INF/persistence.xml'': org.hibernate.ejb.HibernatePersistence java:/comp/env/jdbc/EtudiantDS tiw5.modele.Album tiw5.modele.Piste tiw5.modele.Artiste ===== Étapes ===== ==== Un premier service JAX-WS ==== Dans ce TP, on utilisera [[http://cxf.apache.org|Apache CXF]] comme implémentation de JAX-WS. Dans le projet ''services-impl'', créer un répertoire ''src/main/java'' et ajouter ce répertoire au sources dans Eclipse((clic droit -> Build Path -> Use as source folder)). Y créer une classe ''tiw5.services.impl.AlbumDataService'' sur le modèle suivant: package tiw5.services.impl; import javax.jws.Oneway; import javax.jws.WebMethod; import javax.jws.WebService; import javax.persistence.EntityManager; import javax.persistence.Persistence; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import tiw5.modele.Album; import tiw5.modele.Artiste; @WebService(targetNamespace = "http://master-info.univ-lyon1.fr/M2TI/TIW5/services", serviceName = "AlbumDataService", name = "AlbumDataPortType", portName = "AlbumDataPort") public class AlbumDataService { private static final Logger log = LoggerFactory.getLogger(AlbumDataService.class); @WebMethod public Album getAlbumDescription(long albumId) { EntityManager em = Persistence.createEntityManagerFactory("etudiant") .createEntityManager(); Album album = em.find(Album.class, albumId); return album; } @Oneway @WebMethod public void addAlbumDescription(Album album) { EntityManager em = Persistence.createEntityManagerFactory("etudiant") .createEntityManager(); em.getTransaction().begin(); if (em.find(Album.class, album.getId()) != null) { log.info("found {}",album.getId()); em.merge(album); } else { log.info("did not found {}",album.getId()); em.persist(album); } for(Artiste a : album.getArtistes()) { if (em.find(Artiste.class, a.getUri()) != null) { em.merge(a); } else { em.persist(a); } } em.getTransaction().commit(); } } Dans le projet ''web-interface'' ajouter au fichier ''web.xml'' le code suivant((voir [[http://cxf.apache.org/docs/servlet-transport.html|la documentation CXF]])): contextConfigLocation classpath:/services.xml org.springframework.web.context.ContextLoaderListener CXF Servlet CXFServlet org.apache.cxf.transport.servlet.CXFServlet 1 CXFServlet /services/* créer le fichier ''src/main/webapp/WEB-INF/classes/services.xml'': Vérifier que le fichier ''pom.xml'' contient les dépendances suivantes: junit junit 3.8.1 test tiw5 modele 1.0-SNAPSHOT jar compile javax.servlet servlet-api 2.5 jar compile tiw5 services-impl 1.0-SNAPSHOT jar compile org.apache.cxf cxf-rt-frontend-jaxws ${cxf.version} jar runtime org.apache.cxf cxf-rt-transports-http ${cxf.version} jar compile org.hibernate hibernate-entitymanager 3.6.7.Final jar runtime org.slf4j slf4j-simple 1.6.2 jar runtime postgresql postgresql 9.0-801.jdbc4 jar runtime 2.4.2 Exécuter l'application web dans le tomcat configuré dans Eclipse, vérifier le bon déploiement ici: http://localhost:8080/web-interface/services/ et comprendre le fichier WSDL associé au service déployer. Le comparer à la classe ''AlbumDataService'' pour comprendre le rôle des différentes annotations qui y sont utilisées. ==== Test du service ==== Créer un nouveau projet soapUI((ouvrir la perspective SoapUI si vous utilisez le plugin Eclipse)) et indiquer l'adresse du WSDL généré par CXF. Remplir les données des requêtes prédéfinies et les exécuter pour tester le service. ==== Client pour le service ==== - Enregistrer le fichier wsdl généré par CXF dans le fichier ''src/main/resources/album-data.wsdl'' du projet ''client''. - Modifier le ''pom.xml'' de ce projet pour y ajouter le code permettant de générer un client pour ce service via [[http://cxf.apache.org/|apache CXF]], en utilisant [[http://cwiki.apache.org/CXF20DOC/wsdl-to-java.html|la documentation ici]] et [[http://cwiki.apache.org/CXF20DOC/maven-cxf-codegen-plugin-wsdl-to-java.html|là]]. - Utiliser les classes Java générées pour réaliser un client en ligne de commande pour le service précédent. Si l'argument en ligne de commande est un numéro, alors le client récupérera la description XML de l'album correspondant, sinon, et argument sera un nom de fichier xml contenant un descriptif d'album à insérer dans les données. * Afin de faciliter l'usage de JAXB, on s'autorisera à créer une classe tiw5.client.AlbumDescription qui contiendra un album du type généré par CXF pour représenter les albums du service. En particulier, cette classe pourra être annotée via ''@XmlRootElement'' et servir de point de départ à une (dé)sérialisation((marshall/unmarshall)). ==== Nouveau service ==== - Dans le projet services-impl, créer un fichier WSDL src/main/resources/stock.wsdl décrivant des opérations sur un entrepôt de disques, avec les opérations suivantes: * ''disponible'': indique si le stock contient encore des disques correspondant au numéro passé en argument. * ''assureCapacite'': prend une liste de couples (numéro d'album, quantité) et assure que la quantité requise est disponible pour chaque album. * ''commande'': prend une liste de numéros d'albums et retire 1 à la quantité disponible de chaque album listé. - Générer, de manière similaire à la partie précédente, du code pour cette fois-ci implémenter le service. - Implémenter le service en héritant de l'interface Java générée (qui correspond au port type). L'implémentation de ''assureCapacite'' pourra se faire par simple mise à jour du nombre de disques disponibles. - Rendre le service ainsi créé disponible dans ''web-interface''. - Tester avec SOAPUI.