[Commentaire(s)] [PDF] [Imprimable]

Web services REST avec JAX-RS

Ecrit par : Hyacinthe MENIET
Créé le : 2010-05-08 12:18:50
Dernière maj : 2010-05-08 12:27:38
Profil : Utilisateur avancé

Développer des web services RESTful en Java est possible depuis les débuts de REST, notamment grâce à l'API Servlet. En effet, cette API est suffisamment proche du protocole HTTP pour permettre à chaque développeur de transférer et de récupérer ce qu'il veut dans une réponse HTTP ou depuis une requête HTTP. Dans la documentation Web services REST avec Java 6 (JAXB) j'explique comment faire. Reportez-vous au même article pour une courte introduction sur les architectures REST.

Ceci dit, malgré son expressivité, l'API Servlet reste loin du niveau de commodité atteint par JAX-WS pour les web services SOAP (voir l'article Web services SOAP avec Java 6 (JAX-WS) pour plus de détails). C'est pourquoi, la Java Specification Request 311 a été déposée auprès du Java Community Process (JCP) en Janvier 2007 et approuvée à l'unanimité en Février. A la suite de quoi, un groupe d'experts a entamé des travaux afin de concevoir une API flexible, facile à utiliser et qui encouragerait les développeurs à monter des architectures REST. Le résultat de ce travail a été finalisé en Octobre 2008 sous le nom d'API Java pour les services web RESTful (JAX-RS) dont la version courante est 1.1.

Dans cet article je vais indiquer comment développer un Web service REST en utilisant JAX-RS 1.1. Puis j'expliquerai comment le consommer. Pour le mapping Objet-Relationnel, j'utiliserai la Java Persistence API (JPA 2.0). Enfin, la sérialisation Java vers XML sera assurée par la Java API for XML Binding (JAXB 2.1).

1. Pré-requis

  • Vous êtes familier de Java 6 et de sa syntaxe.
  • Vous êtes familier des Web services REST.
  • Vous disposez du JDK 6 minimum.
  • Vous êtes familier de Tomcat et vous avez installé et configuré sa version 6.
  • Vous êtes familier de MySQL et vous avez installé et configuré sa version 5.
  • Vous êtes familier de Maven et vous avez installé et configuré sa version 2.

2. Vue d'ensemble

2.1 Vue d'ensemble de JAX-RS

JAX-RS est une API récente mais déjà bien fournie : Elle propose notamment des annotations spécifiques pour lier une URI et des verbes HTTP à des méthodes d'une classe Java. JAX-RS dispose également d'annotations capables d'injecter et de récupérer des données dans les entêtes d'une réponse ou depuis celles d'une requête. De même l'API JAX-RS fournit ce qu'il faut pour extraire et écrire ce que vous voulez dans le corps d'une requête/réponse HTTP. Enfin, en cas d'exception Java, JAX-RS est capable de produire une réponse avec le code HTTP le plus pertinent.

Comme mentionné précédemment, les spécifications de JAX-RS 1.0 ont été finalisées en 2008 et depuis cette date, de nombreux Frameworks l'implémentant ont émergé. La plupart de ces implémentations sont open source et libres. Dans ce document, je n'en présenterai qu'une : Jersey. C'est l'implémentation de référence de JAX-RS 1.1.

2.2 Vue d'ensemble de JPA

La Java Persistence API (JPA) est une API légère à base de POJO pour la persistance d'objets Java. Cette API a été développée dans le cadre de la JSR 220. JPA est très souple car elle peut être utilisée dans une webapp, dans une application J2EE et même dans une simple application Java SE.

Comme JAX-RS, JPA dispose de nombreuses implémentations. Pour cet article, j'ai choisi l'une des implémentations les plus populaires de la version 2.0 : Hibernate.

2.3 Vue d'ensemble de JAXB

La Java API for XML Binding (JAXB) est un ensemble d'annotations qui aident à la sérialisation/désérialisation d'objets Java en XML. Cette API est extrêmement utile, car elle permet de se passer des représentations abstraites d'un document XML pour se concentrer sur de simples POJO annotés. Dans l'article Sérialisation et désérialisation XML avec Java 6 (JAXB), j'explique plus en détail comment fonctionne JAXB. L'implémentation de référence de JAXB est intégrée au JDK 6.

2.4 Vue d'ensemble de l'article

Pour rentre cet article le plus didactique possible, je vais parcourir les capacités de JAX-RS à travers le système d'information d'une association de troc de services. Cette association met en relation des membres qui proposent des services en échange d'autres services. Une fois mis en relation et sous réserve d'un accord mutuel, un membre peut, par exemple, recevoir une leçon de cuisine thaïlandaise en échange d'un dépannage de la plomberie.

Dans cet article, je ne modéliserai que la relation entre un membre et ses services. Concrètement, je réaliserai un Web service REST qui expose les 6 actions suivantes :

Le web service REST
URIHTTPDescriptionSortieEntrée
/members/{mId} GET Retourne des informations sur le membre Le membre N/A
/members/{mId}/services GET Retourne les services du membre Une collection de services N/A
/members/{mId}/services POST Ajoute un service au membre l'URI du service Un service
/members/{mId}/services/{sId} GET Retourne des informations sur le service Un service N/A
/members/{mId}/services/{sId} PUT Modifie un service N/A Un service
/members/{mId}/services/{sId} DELETE Supprime un service N/A N/A

Vous pouvez télécharger le code source des projets Maven de web service REST (i.e. serveur et client).

3. Le Web service

3.1 La base de données

La base de données bartering correspond au schéma suivant:

La base de données Bartering

J'ai réalisé ce schéma avec MySQL Workbench. Créez la base de données correspondante ainsi que son propriétaire grâce au script Bartering-grant.sql:

CREATE DATABASE bartering;
GRANT ALL PRIVILEGES ON bartering.* TO bartuser@"%" IDENTIFIED BY 'bartpwd' WITH GRANT OPTION;

Créez les tables member et service à l'aide du script Bartering-schema.sql:

-- -----------------------------------------------------
-- Table `member`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `member` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
  `name` VARCHAR(45) NOT NULL ,
  `firstname` VARCHAR(45) NULL ,
  `location` VARCHAR(45) NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `service`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `service` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
  `member_id` INT UNSIGNED NOT NULL ,
  `description` LONGTEXT NULL ,
  PRIMARY KEY (`id`) ,
  INDEX `fk_service_member` (`member_id` ASC) ,
  CONSTRAINT `fk_service_member`
    FOREIGN KEY (`member_id` )
    REFERENCES `member` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

Insérez les membres et les services en exécutant le script Bartering-data.sql:

INSERT INTO `member` VALUES (1,'Gates','Bill','Quartier Saint Bruno'),(2,'Jobs','Steve','Rue de la pomme');
INSERT INTO `service` VALUES (1,1,'Installation de Windows 7'),(2,1,'Formation d\'1 heure en Economie');

3.2 Projet Maven

Créez un nouveau projet de type webapp avec Maven (commande à taper à la racine de votre workspace):

$ mvn archetype:create -DgroupId=net.dotmyself.bartering -DartifactId=bartering -DarchetypeArtifactId=maven-archetype-webapp

Ecrasez le pom.xml généré, dans le dossier bartering, par celui-ci :

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>net.dotmyself.bartering</groupId>
	<artifactId>bartering</artifactId>
	<packaging>war</packaging>
	<version>1.0</version>
	<name>Bartering Webapp</name>
	<url>http://www.dotmyself.net</url>
	<repositories>
		<repository>
			<id>repository.jboss.org</id>
			<name>JBoss Repository</name>
			<url>http://repository.jboss.org/maven2</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
		<repository>
			<id>maven2-repository.dev.java.net</id>
			<name>Java.net Repository for Maven</name>
			<url>http://download.java.net/maven/2</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>
	<dependencies>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>3.5.1-Final</version>
		</dependency>
		<dependency>
			<groupId>com.sun.jersey</groupId>
			<artifactId>jersey-server</artifactId>
			<version>1.1.5.1</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.12</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.5.6</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.5.6</version>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.13</version>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
		</plugins>
		<finalName>bartering</finalName>
	</build>
</project>

Initialisez votre environnement avec la commande (à taper à la racine de votre projet Maven):

$ mvn clean install

Dans votre projet Maven, créez manuellement le dossier src/main/java/net/dotmyself/bartering. Si vous disposez d'un IDE compatible avec Maven, c'est le moment d'importer votre projet dans ce dernier.

3.3 Les flux XML

Les objets Java que vous sérialiserez en XML ressembleront aux flux XML suivant:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<member id="1">
	<name>Gates</name>
	<firstname>Bill</firstname>
	<location>Quartier Saint Bruno</location>
	<services>
		<service id="1">
			<description>Installation de Windows 7</description>
		</service>
		<service id="2">
			<description>Formation d'1 heure en Economie</description>
		</service>
	</services>
</member>

Ce flux XML pouvant être validé par le schéma XML suivant:

<?xml version="1.0" encoding="utf-8"?>
<xsd:schema targetNamespace="http://dotmyself.net/bartering/model"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
	xmlns="http://dotmyself.net/bartering/model">

	<xsd:element name="member">
		<xsd:complexType>
			<xsd:attribute name="id" type="xsd:int" />
			<xsd:sequence>
				<xsd:element name="name" type="xsd:string" />
				<xsd:element name="firstname" type="xsd:string" />
				<xsd:element name="location" type="xsd:string" />
				<xsd:element name="services">
					<xsd:complexType>
						<xsd:sequence>
							<xsd:element maxOccurs="unbounded" ref="service" />
						</xsd:sequence>
					</xsd:complexType>
				</xsd:element>
			</xsd:sequence>
		</xsd:complexType>
	</xsd:element>

	<xsd:element name="service">
		<xsd:complexType>
			<xsd:attribute name="id" type="xsd:int" />
			<xsd:sequence>
				<xsd:element name="description" type="xsd:string" />
			</xsd:sequence>
		</xsd:complexType>
	</xsd:element>

</xsd:schema>

3.4 La couche persistance

Les classes de la couche persistance appartiennent toutes au package net.dotmyself.bartering.model, et contiennent des annotations :

  • JAXB (i.e. javax.xml.bind.annotation.*): Pour la sérialisation java vers XML (et vice versa).
  • JPA (i.e. javax.persistence.*) : Pour le mapping Objet-Relationnel

Créez la classe Service.java:

/**
 * The persistent class for the service database table.
 */
package net.dotmyself.bartering.model;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;

/**
 * @author Hyacinthe MENIET
 * Created on May 02, 2010
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "description"
})
@XmlRootElement(name = "service")
@Entity
@Table(name="service")
@NamedQuery(name = Service.FIND_BY_MEMBER, 
		query = "FROM Service s WHERE s.id = :serviceId AND s.member.id = :memberId")
public class Service implements Serializable {
	
	private static final long serialVersionUID = 1L;
	public static final String FIND_BY_MEMBER = "Service.finByMember";

	@XmlAttribute
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private int id;

	@XmlElement(required = true)
	@Lob()
    @Column(nullable = false)
	private String description;

	//bi-directional many-to-one association to Member
	@XmlTransient
	@ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "member_id")
	private Member member;

    public Service() {
    }

	public int getId() {
		return this.id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getDescription() {
		return this.description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public Member getMember() {
		return this.member;
	}

	public void setMember(Member member) {
		this.member = member;
	}
	
}

Créez la classe Member.java:

/**
 * The persistent class for the member database table.
 */
package net.dotmyself.bartering.model;

import java.io.Serializable;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

/**
 * @author Hyacinthe MENIET
 * Created on May 02, 2010
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "name",
    "firstname",
    "location",
    "services"
})
@XmlRootElement(name = "member")
@Entity
@Table(name="member")
public class Member implements Serializable {
	
	private static final long serialVersionUID = 1L;
	public static final String PERSISTENCE_UNIT = "BarteringPU";

	@XmlAttribute
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private int id;

	@XmlElement(required = false)
	@Column(nullable = true)
	private String firstname;

	@XmlElement(required = false)
	@Column(nullable = true)
	private String location;

	@XmlElement(required = true)
	@Column(nullable = false)
	private String name;

	// bi-directional many-to-one association to Service
	@XmlElement(name="service", required = true)
    @XmlElementWrapper( name="services", required = true )
    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "member_id", insertable = true, updatable = true)
    @OrderBy("id ASC")
	private List<Service> services;

    public Member() {
    }

	public int getId() {
		return this.id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getFirstname() {
		return this.firstname;
	}

	public void setFirstname(String firstname) {
		this.firstname = firstname;
	}

	public String getLocation() {
		return this.location;
	}

	public void setLocation(String location) {
		this.location = location;
	}

	public String getName() {
		return this.name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public List<Service> getServices() {
		return this.services;
	}

	public void setServices(List<Service> services) {
		this.services = services;
	}
	
}

3.5 La couche service

Les classes de la couche service appartiennent toutes au package net.dotmyself.bartering.service.

Créez la classe ServiceResource.java:

/**
 * The Service resource that returns service's informations.
 */
package net.dotmyself.bartering.service;

import java.net.URI;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;

import net.dotmyself.bartering.model.Member;
import net.dotmyself.bartering.model.Service;

import com.sun.jersey.api.NotFoundException;

/**
 * @author Hyacinthe MENIET 
 * Created on May 02, 2010
 */
public class ServiceResource {

	private int memberId;

	/**
	 * Default constructor
	 * 
	 * @param mid
	 */
	public ServiceResource(int mid) {
		this.memberId = mid;
	}

	@GET
	@Produces("application/xml")
	public List<Service> getServices() {

		EntityManagerFactory emf = Persistence
				.createEntityManagerFactory(Member.PERSISTENCE_UNIT);
		Member m = emf.createEntityManager().find(Member.class, this.memberId);
		if (null == m) {
			emf.close();
			throw new NotFoundException("member with memberId=" + this.memberId
					+ " does not exist!");
		}
		emf.close();
		return m.getServices();
	}

	@GET
	@Path("{id : \\d+}")
	@Produces("application/xml")
	public Service getService(@PathParam("id") int id) {

		Service s = null;
		EntityManagerFactory emf = null;
		try {
			emf = Persistence
					.createEntityManagerFactory(Member.PERSISTENCE_UNIT);
			s = (Service) emf.createEntityManager().createNamedQuery(
					Service.FIND_BY_MEMBER).setParameter("serviceId", id)
					.setParameter("memberId", this.memberId).getSingleResult();
		} catch (Exception e) {
			throw new NotFoundException("service with memberId="
					+ this.memberId + " and serviceId=" + id + " not found");
		} finally {
			emf.close();
		}
		return s;
	}

	@POST
	@Consumes("application/xml")
	public Response createService(Service s) {

		EntityManagerFactory emf = Persistence
				.createEntityManagerFactory(Member.PERSISTENCE_UNIT);
		EntityManager em = emf.createEntityManager();
		EntityTransaction tx = em.getTransaction();
		tx.begin();

		Member m = em.find(Member.class, this.memberId);
		s.setId(0);
		s.setMember(m);
		em.persist(s);

		tx.commit();
		em.close();
		emf.close();

		return Response.created(URI.create("/" + s.getId())).build();
	}

	@PUT
	@Path("{id : \\d+}")
	@Consumes("application/xml")
	public void updateService(@PathParam("id") int id, Service extService) {

		EntityManagerFactory emf = Persistence
				.createEntityManagerFactory(Member.PERSISTENCE_UNIT);
		EntityManager em = emf.createEntityManager();
		EntityTransaction tx = em.getTransaction();
		tx.begin();

		Service intService = em.find(Service.class, id);
		if (null == intService) {
			throw new NotFoundException("service with memberId="
					+ this.memberId + " and serviceId=" + id + " not found");
		}
		intService.setDescription(extService.getDescription());
		em.merge(intService);

		tx.commit();
		em.close();
		emf.close();
	}

	@DELETE
	@Path("{id : \\d+}")
	public void deleteService(@PathParam("id") int id) {

		EntityManagerFactory emf = Persistence
				.createEntityManagerFactory(Member.PERSISTENCE_UNIT);
		EntityManager em = emf.createEntityManager();
		EntityTransaction tx = em.getTransaction();
		tx.begin();

		Service s = em.find(Service.class, id);
		if (null != s) {
			em.remove(s);
		}

		tx.commit();
		em.close();
		emf.close();

	}

}

Créez la classe MemberResource.java:

/**
 * The Member resource that returns member's informations.
 */
package net.dotmyself.bartering.service;

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

import net.dotmyself.bartering.model.Member;

import com.sun.jersey.api.NotFoundException;

/**
 * @author Hyacinthe MENIET 
 * Created on May 02, 2010
 */
@Path("/members")
public class MemberResource {

	@GET
	@Path("{id : \\d+}")
	@Produces("application/xml")
	public Member getMember(@PathParam("id") int id) {
		EntityManagerFactory emf = Persistence
				.createEntityManagerFactory(Member.PERSISTENCE_UNIT);
		Member m = emf.createEntityManager().find(Member.class, id);
		if (null == m) {
			emf.close();
			throw new NotFoundException("member with memberId=" + id
					+ " does not exist!");
		}
		emf.close();
		return m;
	}

	@Path("{id : \\d+}/services")
	public ServiceResource getResources(@PathParam("id") int id) {
		return new ServiceResource(id);
	}
}

Terminez par la classe BarteringApplication.java qui indique à la Servlet de Jersey quelles classes resources charger :

/**
 * Used to tell server container which Restful web 
 * services (@Path annotated classes) we want deployed.
 */
package net.dotmyself.bartering.service;

import java.util.HashSet;
import java.util.Set;

import javax.ws.rs.core.Application;

/**
 * @author Hyacinthe MENIET
 * Created on May 02, 2010
 */
public class BarteringApplication extends Application {

	@Override
	public Set<Class<?>> getClasses() {
		Set<Class<?>> classes = new HashSet<Class<?>>();
        classes.add(net.dotmyself.bartering.service.MemberResource.class);
        return classes;
	}

}

3.6 Fichiers de configuration

Ecrasez le fichier src/main/webapp/WEB-INF/web.xml avec celui-ci:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="WebApp_ID" version="2.5">
	<display-name>Bartering web application</display-name>
	<servlet>
		<servlet-name>bartering</servlet-name>
		<servlet-class>com.sun.jersey.server.impl.container.servlet.ServletAdaptor</servlet-class>
		<init-param>
			<param-name>com.sun.jersey.config.feature.Redirect</param-name>
			<param-value>true</param-value>
		</init-param>
		<init-param>
			<param-name>unit:BarteringPU</param-name>
			<param-value>persistence/bartering</param-value>
		</init-param>
		<init-param>
			<param-name>javax.ws.rs.Application</param-name>
			<param-value>net.dotmyself.bartering.service.BarteringApplication</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>bartering</servlet-name>
		<url-pattern>/*</url-pattern>
	</servlet-mapping>
	<session-config>
		<session-timeout>30</session-timeout>
	</session-config>
	<welcome-file-list>
		<welcome-file>
            index.jsp
        </welcome-file>
	</welcome-file-list>
	<persistence-unit-ref>
		<persistence-unit-ref-name>persistence/bartering</persistence-unit-ref-name>
		<persistence-unit-name>BarteringPU</persistence-unit-name>
	</persistence-unit-ref>
	<resource-ref>
		<res-ref-name>UserTransaction</res-ref-name>
		<res-type>javax.transaction.UserTransaction</res-type>
		<res-auth>Container</res-auth>
	</resource-ref>
</web-app>

Créez le fichier src/main/resources/META-INF/persistence.xml suivant:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
	http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
	version="2.0">
	<persistence-unit name="BarteringPU" transaction-type="RESOURCE_LOCAL">
		<class>net.dotmyself.bartering.model.Member</class>
		<class>net.dotmyself.bartering.model.Service</class>
		<properties>
			<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
			<property name="javax.persistence.jdbc.user" value="bartuser" />
			<property name="javax.persistence.jdbc.password" value="bartpwd" />
			<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/bartering" />
			<property name="hibernate.hbm2ddl.auto" value="update" />
			<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
			<property name="hibernate.use_sql_comments" value="true" />
			<property name="hibernate.show_sql" value="true" />
			<property name="hibernate.query.substitutions" value="true 'Y', false 'N'" />
		</properties>
	</persistence-unit>
</persistence>

4. Déploiement et test du Web service

4.1 Configuration et déploiement

Compilez et générez votre war (commande à taper à la racine de votre projet Maven):

$ mvn clean install

Maintenant copiez le fichier target/bartering.war dans le dossier CATALINA_HOME/webapps/. Démarrez votre Tomcat s'il ne l'est pas. Testez que vous pouvez accéder à l'URL suivante : http://localhost:8080/bartering/members/1.

5. Le client

5.1 Projet Maven

Créez un nouveau projet Maven (commande à taper à la racine de votre workspace) :

$ mvn archetype:create -DgroupId=net.dotmyself.bartering -DartifactId=bartering-client

Ecrasez votre le pom.xml généré par celui-ci :

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>net.dotmyself.bartering</groupId>
	<artifactId>bartering-client</artifactId>
	<version>1.0</version>
	<packaging>jar</packaging>

	<name>Bartering Client</name>
	<url>http://www.dotmyself.net</url>

	<repositories>
		<repository>
			<id>maven2-repository.dev.java.net</id>
			<name>Java.net Repository for Maven</name>
			<url>http://download.java.net/maven/2</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>com.sun.jersey</groupId>
			<artifactId>jersey-client</artifactId>
			<version>1.1.5.1</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

Supprimez le dossier test à la racine du projet nouvellement créez. Initialisez votre environnement avec la commande suivante (à taper à la racine de votre projet Maven):

$ mvn clean install

Si vous disposez d'un IDE compatible avec Maven, c'est le moment d'importer votre projet dans ce dernier.

5.2 Programme client

Ecrasez la classe net.dotmyself.bartering.App par celle-ci :

/**
 * Java client for the Bartering's restful web service
 */
package net.dotmyself.bartering;

import java.net.URI;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;

/**
 * @author hyacinthe MENIET 
 * Created on May 02, 2010
 * 
 */
public class App {
	
	private static final String REST_URL = "http://localhost:8080/bartering/members";

	public static void main(String[] args) {
		Client client = Client.create();

		System.out.println("Reading the member Bill Gates ...");

		WebResource webResource = client.resource(REST_URL + "/1");
		ClientResponse response = webResource.get(ClientResponse.class);
		int status = response.getStatus();
		String textEntity = response.getEntity(String.class);
		System.out.println("Status = " + status);
		System.out.println("Body = " + textEntity);

		System.out.println("Reading the member Steve Jobs ...");

		webResource = client.resource(REST_URL + "/2");
		response = webResource.get(ClientResponse.class);
		status = response.getStatus();
		textEntity = response.getEntity(String.class);
		System.out.println("Status = " + status);
		System.out.println("Body = " + textEntity);

		System.out.println("Adding a Service to the member Steve Jobs ...");

		webResource = client.resource(REST_URL + "/2/services");
		String serv = "<service><description>Téléchargement de 3 morceaux sur l'Apple Music Store</description></service>";
		response = webResource.type("application/xml").post(ClientResponse.class, serv);
		status = response.getStatus();
		URI uri = response.getLocation();
		System.out.println("Status = " + status);
		System.out.println("Location = " + uri);

		System.out
				.println("Reading the list of services from the member Steve Jobs ...");

		webResource = client.resource(REST_URL + "/2/services");
		response = webResource.get(ClientResponse.class);
		status = response.getStatus();
		textEntity = response.getEntity(String.class);
		System.out.println("Status = " + status);
		System.out.println("Body = " + textEntity);

		System.out
				.println("Reading one service from the member Steve Jobs ...");

		webResource = client.resource(uri);
		response = webResource.get(ClientResponse.class);
		status = response.getStatus();
		textEntity = response.getEntity(String.class);
		System.out.println("Status = " + status);
		System.out.println("Body = " + textEntity);

		System.out
				.println("Updating one service from the member Steve Jobs ...");

		webResource = client.resource(uri);
		serv = "<service><description>1 Entrée pour le concert : Flash Haters</description></service>";
		response = webResource.type("application/xml").put(ClientResponse.class, serv);
		status = response.getStatus();
		System.out.println("Status = " + status);

		System.out
				.println("Re-reading one service from the member Steve Jobs ...");

		webResource = client.resource(uri);
		response = webResource.get(ClientResponse.class);
		status = response.getStatus();
		textEntity = response.getEntity(String.class);
		System.out.println("Status = " + status);
		System.out.println("Body = " + textEntity);

		System.out
				.println("Deleting one service from the member Steve Jobs ...");
		
		webResource = client.resource(uri);
		response = webResource.delete(ClientResponse.class);
		status = response.getStatus();
		System.out.println("Status = " + status);
		
		System.out.println("Re-reading the member Steve Jobs ...");

		webResource = client.resource(REST_URL + "/2");
		response = webResource.get(ClientResponse.class);
		status = response.getStatus();
		textEntity = response.getEntity(String.class);
		System.out.println("Status = " + status);
		System.out.println("Body = " + textEntity);

	}
}

Compilez et exécutez cette classe.

[Commentaire(s)] [PDF] [Imprimable]