Skip to content
Snippets Groups Projects
Commit 5f01dd4e authored by Yvan Peter's avatar Yvan Peter
Browse files

Passage à Jakarta et maj README

parent b3f5ebe9
No related branches found
No related tags found
No related merge requests found
...@@ -206,5 +206,7 @@ local.properties ...@@ -206,5 +206,7 @@ local.properties
### VisualStudioCode Patch ### ### VisualStudioCode Patch ###
# Ignore all local history of files # Ignore all local history of files
.history .history
.project
.settings
.classpath
# End of https://www.gitignore.io/api/eclipse,visualstudiocode # End of https://www.gitignore.io/api/eclipse,visualstudiocode
# Développement REST avec Jersey # Développement REST avec Jersey
## Mise en place de l'environnement ## Mise en place de l'environnement
Ce tutoriel utilise [Apache Maven](http://maven.apache.org/) pour
l'automatisation des tâches de développement (compilation, tests,
déploiement...).
Si votre machine se trouve derrière un
proxy, vous devrez mettre la configuration suivante dans le fichier
`~/.m2/settings.xml` (à adapter à votre environnement) :
<settings>
<proxies>
<proxy>
<id>lille1-proxy</id>
<active>true</active>
<protocol>http</protocol>
<host>cache.univ-lille.fr</host>
<port>3128</port>
</proxy>
<proxy>
<id>lille1-proxy-sec</id>
<active>true</active>
<protocol>https</protocol>
<host>cache.univ-lille.fr</host>
<port>3128</port>
</proxy>
</proxies>
</settings>
En local, vous pouvez également recopier le fichier `/home/public/peter/maven/settings.xml`.
## Récupération du projet initial ## Récupération du projet initial
Pour récupérer le projet vous pouvez utiliser la commande `git clone Pour récupérer le projet vous pouvez utiliser la commande `git clone
...@@ -61,8 +33,6 @@ les paquetages Java qui contiennent les ressources. ...@@ -61,8 +33,6 @@ les paquetages Java qui contiennent les ressources.
│ ├── resources │ ├── resources
│ │ ├── ingredients.json │ │ ├── ingredients.json
│ │ └── logback.xml │ │ └── logback.xml
│ └── webapp
│ └── WEB-INF
└── test └── test
├── java ├── java
│ └── fr │ └── fr
...@@ -97,12 +67,12 @@ Un ingrédient comporte uniquement un identifiant et un nom. Sa ...@@ -97,12 +67,12 @@ Un ingrédient comporte uniquement un identifiant et un nom. Sa
représentation JSON prendra donc la forme suivante : représentation JSON prendra donc la forme suivante :
{ {
"id": 1, "id": "f38806a8-7c85-49ef-980c-149dcd81d306",
"name": "mozzarella" "name": "mozzarella"
} }
Lors de la création, l'identifiant n'est pas connu car il sera fourni Lors de la création, l'identifiant n'est pas connu car il sera fourni
par la base de données. Aussi on aura une par le JavaBean qui représente un ingrédient. Aussi on aura une
représentation JSON qui comporte uniquement le nom : représentation JSON qui comporte uniquement le nom :
{ "name": "mozzarella" } { "name": "mozzarella" }
...@@ -169,6 +139,7 @@ Le code suivant qui se trouve dans la classe `IngredientResourceTest` ...@@ -169,6 +139,7 @@ Le code suivant qui se trouve dans la classe `IngredientResourceTest`
montre la mise en place de l'environnement (`configure()`) et l'amorce montre la mise en place de l'environnement (`configure()`) et l'amorce
d'un premier test. d'un premier test.
~~~java
public class IngredientResourceTest extends JerseyTest { public class IngredientResourceTest extends JerseyTest {
@Override @Override
...@@ -189,10 +160,11 @@ d'un premier test. ...@@ -189,10 +160,11 @@ d'un premier test.
assertEquals(0, ingredients.size()); assertEquals(0, ingredients.size());
} }
~~~
En héritant de JerseyTest, votre classe de test se comporte comme un En héritant de JerseyTest, votre classe de test se comporte comme un
[`Client` [`Client`
JAX-RS](https://docs.oracle.com/javaee/7/api/javax/ws/rs/client/Client.html). La JAX-RS](https://docs.oracle.com/javaee/7/api/jakarta/ws/rs/client/Client.html). La
méthode `target()` notamment permet de préparer une requête sur une méthode `target()` notamment permet de préparer une requête sur une
URI particulière. URI particulière.
...@@ -207,6 +179,7 @@ DTO `IngredientDto`. ...@@ -207,6 +179,7 @@ DTO `IngredientDto`.
Pour commencer, on se contentera de l'implémentation minimale Pour commencer, on se contentera de l'implémentation minimale
suivante : suivante :
~~~java
package fr.ulille.iut.pizzaland.dto; package fr.ulille.iut.pizzaland.dto;
public class IngredientDto { public class IngredientDto {
...@@ -215,23 +188,27 @@ suivante : ...@@ -215,23 +188,27 @@ suivante :
} }
} }
~~~
A ce stade, vous pouvez lancer un premier test au moyen de la commande A ce stade, vous pouvez lancer un premier test au moyen de la commande
`mvn test`. Évidemment, ce test va échouer. `mvn test`. Évidemment, ce test va échouer.
~~~
$ mvn test $ mvn test
Results : Results :
Failed tests: testGetEmptyList(fr.ulille.iut.pizzaland.IngredientResourceTest): expected:<200> but was:<404> Failed tests: testGetEmptyList(fr.ulille.iut.pizzaland.IngredientResourceTest): expected:<200> but was:<404>
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0 Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
~~~
Pour réussir, ce premier test, nous allons mettre en place la Pour réussir, ce premier test, nous allons mettre en place la
ressource `IngredientResource`. ressource `IngredientResource`.
Une première implémentation de la ressource pourra avoir la forme Une première implémentation de la ressource pourra avoir la forme
suivante : suivante :
~~~java
@Path("/ingredients") @Path("/ingredients")
public class IngredientResource { public class IngredientResource {
...@@ -247,27 +224,30 @@ suivante : ...@@ -247,27 +224,30 @@ suivante :
return new ArrayList<IngredientDto>(); return new ArrayList<IngredientDto>();
} }
} }
~~~
Avec cette première implémentation, on va pouvoir tester notre Avec cette première implémentation, on va pouvoir tester notre
ressource : ressource :
~~~
$ mvn test $ mvn test
Results : Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
~~~
### Récupérer un ingrédient existant ### Récupérer un ingrédient existant
Nous allons continuer en ajoutant la possibilité de récupérer un Nous allons continuer en ajoutant la possibilité de récupérer un
ingrédient particulier à partir de son identifiant. ingrédient particulier à partir de son identifiant.
Pour cela voici un premier test qui permettra de vérifier cela : Pour cela voici un premier test qui permettra de vérifier cela :
~~~java
import fr.ulille.iut.pizzaland.beans.Ingredient; import fr.ulille.iut.pizzaland.beans.Ingredient;
@Test @Test
public void testGetExistingIngredient() { public void testGetExistingIngredient() {
Ingredient ingredient = new Ingredient(); Ingredient ingredient = new Ingredient();
ingredient.setId(1);
ingredient.setName("mozzarella"); ingredient.setName("mozzarella");
Response response = target("/ingredients/1").request().get(); Response response = target("/ingredients/1").request().get();
...@@ -276,12 +256,14 @@ Pour cela voici un premier test qui permettra de vérifier cela : ...@@ -276,12 +256,14 @@ Pour cela voici un premier test qui permettra de vérifier cela :
Ingredient result = Ingredient.fromDto(response.readEntity(IngredientDto.class)); Ingredient result = Ingredient.fromDto(response.readEntity(IngredientDto.class));
assertEquals(ingredient, result); assertEquals(ingredient, result);
} }
~~~
Vous pourrez vérifier que le test échoue au moyen de la commande `mvn test` Vous pourrez vérifier que le test échoue au moyen de la commande `mvn test`
Afin de réussir ce test, nous devons compléter la classe IngredientDto Afin de réussir ce test, nous devons compléter la classe IngredientDto
avec les getter/setter correspondant aux propriétés de l'object JSON. avec les getter/setter correspondant aux propriétés de l'object JSON.
~~~java
public class IngredientDto { public class IngredientDto {
private long id; private long id;
private String name; private String name;
...@@ -304,10 +286,12 @@ avec les getter/setter correspondant aux propriétés de l'object JSON. ...@@ -304,10 +286,12 @@ avec les getter/setter correspondant aux propriétés de l'object JSON.
return name; return name;
} }
} }
~~~
Du côté de la ressource, on peut fournir une première implémentation : Du côté de la ressource, on peut fournir une première implémentation :
import javax.ws.rs.PathParam; ~~~java
import jakarta.ws.rs.PathParam;
import fr.ulille.iut.pizzaland.beans.Ingredient; import fr.ulille.iut.pizzaland.beans.Ingredient;
@GET @GET
...@@ -319,11 +303,13 @@ Du côté de la ressource, on peut fournir une première implémentation : ...@@ -319,11 +303,13 @@ Du côté de la ressource, on peut fournir une première implémentation :
return Ingredient.toDto(ingredient); return Ingredient.toDto(ingredient);
} }
~~~
Pour cette méthode, nous avons introduit la classe `Ingredient`. Ce Pour cette méthode, nous avons introduit la classe `Ingredient`. Ce
JavaBean représente un ingrédient manipulé par la ressource. JavaBean représente un ingrédient manipulé par la ressource.
Voici une implémentation pour cette classe : Voici une implémentation pour cette classe :
~~~java
package fr.ulille.iut.pizzaland.beans; package fr.ulille.iut.pizzaland.beans;
import fr.ulille.iut.pizzaland.dto.IngredientDto; import fr.ulille.iut.pizzaland.dto.IngredientDto;
...@@ -396,11 +382,14 @@ Voici une implémentation pour cette classe : ...@@ -396,11 +382,14 @@ Voici une implémentation pour cette classe :
return "Ingredient [id=" + id + ", name=" + name + "]"; return "Ingredient [id=" + id + ", name=" + name + "]";
} }
} }
~~~
Le test devrait maintenant réussir : Le test devrait maintenant réussir :
~~~
$ mvn test $ mvn test
~~~
## Introduction de la persistence ## Introduction de la persistence
Pour aller plus loin et mettre en place la création des ingrédients il Pour aller plus loin et mettre en place la création des ingrédients il
va falloir introduire la persistence. Pour cela, nous allons utiliser va falloir introduire la persistence. Pour cela, nous allons utiliser
...@@ -409,6 +398,7 @@ base de données. ...@@ -409,6 +398,7 @@ base de données.
Pour cela nous allons devoir implémenter le DAO (Data Access Object) `IngredientDao` : Pour cela nous allons devoir implémenter le DAO (Data Access Object) `IngredientDao` :
~~~java
package fr.ulille.iut.pizzaland.dao; package fr.ulille.iut.pizzaland.dao;
import java.util.List; import java.util.List;
...@@ -440,6 +430,7 @@ Pour cela nous allons devoir implémenter le DAO (Data Access Object) `Ingredien ...@@ -440,6 +430,7 @@ Pour cela nous allons devoir implémenter le DAO (Data Access Object) `Ingredien
@RegisterBeanMapper(Ingredient.class) @RegisterBeanMapper(Ingredient.class)
Ingredient findById(long id); Ingredient findById(long id);
} }
~~~
JDBI fonctionne par annotations : JDBI fonctionne par annotations :
- Les annotations `sqlUpdate` et `SqlQuery` correspondent à des - Les annotations `sqlUpdate` et `SqlQuery` correspondent à des
...@@ -459,7 +450,8 @@ tests qui est définie via la classe `BDDFactory`. ...@@ -459,7 +450,8 @@ tests qui est définie via la classe `BDDFactory`.
Les méthodes `setEnvUp` et `tearEnvDown` permettent de créer et Les méthodes `setEnvUp` et `tearEnvDown` permettent de créer et
détruire la base de données entre chaque test. détruire la base de données entre chaque test.
~~~java
import fr.ulille.iut.pizzaland.dao.IngredientDao; import fr.ulille.iut.pizzaland.dao.IngredientDao;
public class IngredientResourceTest extends JerseyTest { public class IngredientResourceTest extends JerseyTest {
...@@ -499,15 +491,17 @@ détruire la base de données entre chaque test. ...@@ -499,15 +491,17 @@ détruire la base de données entre chaque test.
Ingredient result = Ingredient.fromDto(response.readEntity(IngredientDto.class)); Ingredient result = Ingredient.fromDto(response.readEntity(IngredientDto.class));
assertEquals(ingredient, result); assertEquals(ingredient, result);
} }
~~~
### La ressource avec la base de données ### La ressource avec la base de données
~~~java
import fr.ulille.iut.pizzaland.BDDFactory; import fr.ulille.iut.pizzaland.BDDFactory;
import fr.ulille.iut.pizzaland.dao.IngredientDao; import fr.ulille.iut.pizzaland.dao.IngredientDao;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.ws.rs.WebApplicationException; import jakarta.ws.rs.WebApplicationException;
public class IngredientResource { public class IngredientResource {
private IngredientDao ingredients; private IngredientDao ingredients;
...@@ -538,42 +532,48 @@ détruire la base de données entre chaque test. ...@@ -538,42 +532,48 @@ détruire la base de données entre chaque test.
throw new WebApplicationException(Response.Status.NOT_FOUND); throw new WebApplicationException(Response.Status.NOT_FOUND);
} }
} }
} }
~~~
### Les tests fonctionnent avec la base de données ### Les tests fonctionnent avec la base de données
Nous pouvons maintenant vérifier que la base fonctionne avec la base Nous pouvons maintenant vérifier que la base fonctionne avec la base
de données : de données :
~~~
$ mvn test $ mvn test
Results : Results :
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0 Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
~~~
## Complétons maintenant les différents tests ## Complétons maintenant les différents tests
L'implémentation de la classe devrait fonctionner avec le test suivant L'implémentation de la classe devrait fonctionner avec le test suivant
: :
~~~java
@Test @Test
public void testGetNotExistingIngredient() { public void testGetNotExistingIngredient() {
Response response = target("/ingredients/125").request().get(); Response response = target("/ingredients/125").request().get();
assertEquals(Response.Status.NOT_FOUND.getStatusCode(),response.getStatus()); assertEquals(Response.Status.NOT_FOUND.getStatusCode(),response.getStatus());
} }
~~~
~~~
$ mvn test $ mvn test
Results : Results :
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0 Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
~~~
### Implementation de la méthode POST ### Implementation de la méthode POST
Il va falloir implémenter la méthode POST pour la création des Il va falloir implémenter la méthode POST pour la création des
ingrédients. Commençons par les différents tests : création, création ingrédients. Commençons par les différents tests : création, création
de deux ingrédients identiques et création d'ingrédient sans nom. de deux ingrédients identiques et création d'ingrédient sans nom.
~~~java
import fr.ulille.iut.pizzaland.dto.IngredientCreateDto; import fr.ulille.iut.pizzaland.dto.IngredientCreateDto;
@Test @Test
...@@ -622,17 +622,19 @@ de deux ingrédients identiques et création d'ingrédient sans nom. ...@@ -622,17 +622,19 @@ de deux ingrédients identiques et création d'ingrédient sans nom.
assertEquals(Response.Status.NOT_ACCEPTABLE.getStatusCode(), response.getStatus()); assertEquals(Response.Status.NOT_ACCEPTABLE.getStatusCode(), response.getStatus());
} }
~~~
Nous utiliserons un DTO spécifique `IngredientCreateDto` dans la Nous utiliserons un DTO spécifique `IngredientCreateDto` dans la
mesure où nous n'aurons que le nom de l'ingrédient pour la création. mesure où nous n'aurons que le nom de l'ingrédient pour la création.
La classe [`javax.ws.rs.client.Entity<T>`](https://docs.oracle.com/javaee/7/api/javax/ws/rs/client/Entity.html) permet de définir le corps de La classe [`jakarta.ws.rs.client.Entity<T>`](https://docs.oracle.com/javaee/7/api/jakarta/ws/rs/client/Entity.html) permet de définir le corps de
la requête POST et le type de données associée (ici `application/json`). la requête POST et le type de données associée (ici `application/json`).
Nous devons également fournir une implémentation de Nous devons également fournir une implémentation de
`IngredientCreateDto` pour pouvoir compiler notre code : `IngredientCreateDto` pour pouvoir compiler notre code :
package fr.ulille.iut.pizzaland.dto; ~~~java
package fr.ulille.iut.pizzaland.dto;
public class IngredientCreateDto { public class IngredientCreateDto {
private String name; private String name;
...@@ -647,10 +649,12 @@ Nous devons également fournir une implémentation de ...@@ -647,10 +649,12 @@ Nous devons également fournir une implémentation de
return name; return name;
} }
} }
~~~
Nous pouvons maintenant compiler notre code de test et constater que Nous pouvons maintenant compiler notre code de test et constater que
ceux-ci échouent. ceux-ci échouent.
~~~
$ mvn test $ mvn test
Results : Results :
...@@ -660,11 +664,13 @@ ceux-ci échouent. ...@@ -660,11 +664,13 @@ ceux-ci échouent.
testCreateIngredient(fr.ulille.iut.pizzaland.IngredientResourceTest): expected:<201> but was:<405> testCreateIngredient(fr.ulille.iut.pizzaland.IngredientResourceTest): expected:<201> but was:<405>
Tests run: 6, Failures: 3, Errors: 0, Skipped: 0 Tests run: 6, Failures: 3, Errors: 0, Skipped: 0
~~~
Nous pouvons maintenant implémenter notre méthode POST dans la Nous pouvons maintenant implémenter notre méthode POST dans la
ressource : ressource :
import javax.ws.rs.POST; ~~~java
import jakarta.ws.rs.POST;
import fr.ulille.iut.pizzaland.dto.IngredientCreateDto; import fr.ulille.iut.pizzaland.dto.IngredientCreateDto;
...@@ -690,17 +696,21 @@ Nous pouvons maintenant implémenter notre méthode POST dans la ...@@ -690,17 +696,21 @@ Nous pouvons maintenant implémenter notre méthode POST dans la
throw new WebApplicationException(Response.Status.NOT_ACCEPTABLE); throw new WebApplicationException(Response.Status.NOT_ACCEPTABLE);
} }
} }
~~~
Comme nous vérifions qu'il n'y a pas déjà un ingrédient avec le nom Comme nous vérifions qu'il n'y a pas déjà un ingrédient avec le nom
fourni, nous devont ajouter une méthode `findbyName` à notre DAO fourni, nous devont ajouter une méthode `findbyName` à notre DAO
~~~java
@SqlQuery("SELECT * FROM ingredients WHERE name = :name") @SqlQuery("SELECT * FROM ingredients WHERE name = :name")
@RegisterBeanMapper(Ingredient.class) @RegisterBeanMapper(Ingredient.class)
Ingredient findByName(String name); Ingredient findByName(String name);
~~~
Nous avons également besoin de rajouter les méthodes de conversion Nous avons également besoin de rajouter les méthodes de conversion
pour ce DTO à notre bean `Ingredient` : pour ce DTO à notre bean `Ingredient` :
~~~java
import fr.ulille.iut.pizzaland.dto.IngredientCreateDto; import fr.ulille.iut.pizzaland.dto.IngredientCreateDto;
public static IngredientCreateDto toCreateDto(Ingredient ingredient) { public static IngredientCreateDto toCreateDto(Ingredient ingredient) {
...@@ -716,14 +726,17 @@ Nous avons également besoin de rajouter les méthodes de conversion ...@@ -716,14 +726,17 @@ Nous avons également besoin de rajouter les méthodes de conversion
return ingredient; return ingredient;
} }
~~~
Nous pouvons maintenant vérifier nos tests : Nous pouvons maintenant vérifier nos tests :
~~~
$ mvn test $ mvn test
Results : Results :
Tests run: 6, Failures: 0, Errors: 0, Skipped: 0 Tests run: 6, Failures: 0, Errors: 0, Skipped: 0
~~~
Vous aurez peut-être un affichage d'exception liée au test de création Vous aurez peut-être un affichage d'exception liée au test de création
de doublon, toutefois le test est réussi puisqu'il a levé une de doublon, toutefois le test est réussi puisqu'il a levé une
...@@ -732,6 +745,7 @@ exception qui a été traduite par un code d'erreur HTTP 406. ...@@ -732,6 +745,7 @@ exception qui a été traduite par un code d'erreur HTTP 406.
### Implémentation de la méthode DELETE ### Implémentation de la méthode DELETE
Les tests liés à la méthode DELETE sont les suivants : Les tests liés à la méthode DELETE sont les suivants :
~~~
@Test @Test
public void testDeleteExistingIngredient() { public void testDeleteExistingIngredient() {
Ingredient ingredient = new Ingredient(); Ingredient ingredient = new Ingredient();
...@@ -753,11 +767,13 @@ Les tests liés à la méthode DELETE sont les suivants : ...@@ -753,11 +767,13 @@ Les tests liés à la méthode DELETE sont les suivants :
assertEquals(Response.Status.NOT_FOUND.getStatusCode(), assertEquals(Response.Status.NOT_FOUND.getStatusCode(),
response.getStatus()); response.getStatus());
} }
~~~
Après avoir constaté que ces tests échouent, nous pouvons fournir une Après avoir constaté que ces tests échouent, nous pouvons fournir une
implémentation pour la méthode DELETE : implémentation pour la méthode DELETE :
import javax.ws.rs.DELETE; ~~~java
import jakarta.ws.rs.DELETE;
@DELETE @DELETE
@Path("{id}") @Path("{id}")
...@@ -770,12 +786,15 @@ implémentation pour la méthode DELETE : ...@@ -770,12 +786,15 @@ implémentation pour la méthode DELETE :
return Response.status(Response.Status.ACCEPTED).build(); return Response.status(Response.Status.ACCEPTED).build();
} }
~~~
Nous devons également implémenter la méthode remove dans Nous devons également implémenter la méthode remove dans
`IngredientDao` : `IngredientDao` :
~~~java
@SqlUpdate("DELETE FROM ingredients WHERE id = :id") @SqlUpdate("DELETE FROM ingredients WHERE id = :id")
void remove(long id); void remove(long id);
~~~
Avec cette implémentation, nos tests réussissent. Avec cette implémentation, nos tests réussissent.
...@@ -783,6 +802,7 @@ Avec cette implémentation, nos tests réussissent. ...@@ -783,6 +802,7 @@ Avec cette implémentation, nos tests réussissent.
Commençons par les tests correspondant à cette URI (GET Commençons par les tests correspondant à cette URI (GET
/ingredients/{id}/name) /ingredients/{id}/name)
~~~java
@Test @Test
public void testGetIngredientName() { public void testGetIngredientName() {
Ingredient ingredient = new Ingredient(); Ingredient ingredient = new Ingredient();
...@@ -802,9 +822,11 @@ Commençons par les tests correspondant à cette URI (GET ...@@ -802,9 +822,11 @@ Commençons par les tests correspondant à cette URI (GET
assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus()); assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus());
} }
~~~
L'implémentation correspondant à ce test est simple : L'implémentation correspondant à ce test est simple :
~~~java
@GET @GET
@Path("{id}/name") @Path("{id}/name")
public String getIngredientName(@PathParam("id") long id) { public String getIngredientName(@PathParam("id") long id) {
...@@ -815,6 +837,7 @@ L'implémentation correspondant à ce test est simple : ...@@ -815,6 +837,7 @@ L'implémentation correspondant à ce test est simple :
return ingredient.getName(); return ingredient.getName();
} }
~~~
### Implémentation d'une méthode de création avec des données de formulaire ### Implémentation d'une méthode de création avec des données de formulaire
La création d'un ingrédient pourrait également se faire via un La création d'un ingrédient pourrait également se faire via un
...@@ -823,8 +846,9 @@ formulaire Web. Dans ce cas, le type de représentation sera ...@@ -823,8 +846,9 @@ formulaire Web. Dans ce cas, le type de représentation sera
On peut déjà préparer un test pour cette méthode de création : On peut déjà préparer un test pour cette méthode de création :
import javax.ws.rs.core.MediaType; ~~~java
import javax.ws.rs.core.Form; import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Form;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
...@@ -843,9 +867,11 @@ On peut déjà préparer un test pour cette méthode de création : ...@@ -843,9 +867,11 @@ On peut déjà préparer un test pour cette méthode de création :
assertNotNull(result); assertNotNull(result);
} }
~~~
On peut maintenant fournir une implémentation pour cette méthode : On peut maintenant fournir une implémentation pour cette méthode :
~~~java
@POST @POST
@Consumes("application/x-www-form-urlencoded") @Consumes("application/x-www-form-urlencoded")
public Response createIngredient(@FormParam("name") String name) { public Response createIngredient(@FormParam("name") String name) {
...@@ -871,7 +897,7 @@ On peut maintenant fournir une implémentation pour cette méthode : ...@@ -871,7 +897,7 @@ On peut maintenant fournir une implémentation pour cette méthode :
throw new WebApplicationException(Response.Status.NOT_ACCEPTABLE); throw new WebApplicationException(Response.Status.NOT_ACCEPTABLE);
} }
} }
~~~
# Créer une base de données de test # Créer une base de données de test
Nous avons maintenant implémenté et testé toutes les méthodes prévues Nous avons maintenant implémenté et testé toutes les méthodes prévues
...@@ -883,6 +909,7 @@ d'une variable d'environnement : `PIZZAENV`. ...@@ -883,6 +909,7 @@ d'une variable d'environnement : `PIZZAENV`.
Quand cette variable aura la valeur `withdb`, nous allons remplir la Quand cette variable aura la valeur `withdb`, nous allons remplir la
base au démarrage avec le code suivant : base au démarrage avec le code suivant :
~~~java
import fr.ulille.iut.pizzaland.beans.Ingredient; import fr.ulille.iut.pizzaland.beans.Ingredient;
import fr.ulille.iut.pizzaland.dao.IngredientDao; import fr.ulille.iut.pizzaland.dao.IngredientDao;
...@@ -892,8 +919,8 @@ base au démarrage avec le code suivant : ...@@ -892,8 +919,8 @@ base au démarrage avec le code suivant :
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.json.bind.Jsonb; import jakarta.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder; import jakarta.json.bind.JsonbBuilder;
@ApplicationPath("api/v1/") @ApplicationPath("api/v1/")
public class ApiV1 extends ResourceConfig { public class ApiV1 extends ResourceConfig {
...@@ -922,16 +949,20 @@ base au démarrage avec le code suivant : ...@@ -922,16 +949,20 @@ base au démarrage avec le code suivant :
} }
} }
} }
~~~
Dans un terminal, nous pouvons maintenant fixer la variable Dans un terminal, nous pouvons maintenant fixer la variable
d'environnemnet et démarrer notre serveur REST au moyen de la d'environnemnet et démarrer notre serveur REST au moyen de la
commande `mvn jetty:run` : commande `mvn jetty:run` :
~~~
$ export PIZZAENV=withdb $ export PIZZAENV=withdb
$ mvn jetty:run $ mvn exec:java
~~~
Dans un autre terminal, nous pouvons utiliser `curl` pour tester nos Dans un autre terminal, nous pouvons utiliser `curl` pour tester nos
différentes méthodes : différentes méthodes :
~~~
$ curl -i localhost:8080/api/v1/ingredients $ curl -i localhost:8080/api/v1/ingredients
HTTP/1.1 200 OK HTTP/1.1 200 OK
...@@ -941,6 +972,7 @@ différentes méthodes : ...@@ -941,6 +972,7 @@ différentes méthodes :
Server: Jetty(9.4.26.v20200117) Server: Jetty(9.4.26.v20200117)
[{"id":1,"name":"mozzarella"},{"id":2,"name":"jambon"},{"id":3,"name":"champignons"},{"id":4,"name":"olives"},{"id":5,"name":"tomate"},{"id":6,"name":"merguez"},{"id":7,"name":"lardons"},{"id":8,"name":"fromage"},{"id":9,"name":"oeuf"},{"id":10,"name":"poivrons"},{"id":11,"name":"ananas"},{"id":12,"name":"reblochon"}] [{"id":1,"name":"mozzarella"},{"id":2,"name":"jambon"},{"id":3,"name":"champignons"},{"id":4,"name":"olives"},{"id":5,"name":"tomate"},{"id":6,"name":"merguez"},{"id":7,"name":"lardons"},{"id":8,"name":"fromage"},{"id":9,"name":"oeuf"},{"id":10,"name":"poivrons"},{"id":11,"name":"ananas"},{"id":12,"name":"reblochon"}]
~~~
# Implémentation de la ressource Pizza # Implémentation de la ressource Pizza
Une pizza comprend des ingrédients. Pour développer cette ressource, Une pizza comprend des ingrédients. Pour développer cette ressource,
...@@ -949,6 +981,7 @@ de données. Cela pourra être géré au niveau du DAO grâce à ...@@ -949,6 +981,7 @@ de données. Cela pourra être géré au niveau du DAO grâce à
[JDBI](https://jdbi.org/#_default_methods). Cet extrait de code montre [JDBI](https://jdbi.org/#_default_methods). Cet extrait de code montre
comment faire : comment faire :
~~~java
import org.jdbi.v3.sqlobject.transaction.Transaction; import org.jdbi.v3.sqlobject.transaction.Transaction;
public interface PizzaDao { public interface PizzaDao {
...@@ -964,6 +997,7 @@ comment faire : ...@@ -964,6 +997,7 @@ comment faire :
createAssociationTable(); createAssociationTable();
createPizzaTable(); createPizzaTable();
} }
~~~
Vous écrivez les différentes méthodes annotées avec `@SqlUpdate` ou Vous écrivez les différentes méthodes annotées avec `@SqlUpdate` ou
`@SqlQuery`. Vous utilisez ensuite ces méthodes au sein d'une méthode `@SqlQuery`. Vous utilisez ensuite ces méthodes au sein d'une méthode
......
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 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> <modelVersion>4.0.0</modelVersion>
<groupId>fr.ulille.iut.pizzaland</groupId> <groupId>fr.ulille.iut.pizzaland</groupId>
<artifactId>pizzaland_jdbi</artifactId> <artifactId>pizzaland_jdbi</artifactId>
<packaging>war</packaging> <packaging>jar</packaging>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
<name>pizzaland</name> <name>pizzaland_jdbi</name>
<build> <dependencyManagement>
<finalName>pizzaland</finalName> <dependencies>
<plugins> <dependency>
<plugin> <groupId>org.glassfish.jersey</groupId>
<groupId>org.apache.maven.plugins</groupId> <artifactId>jersey-bom</artifactId>
<artifactId>maven-compiler-plugin</artifactId> <version>${jersey.version}</version>
<version>3.8.1</version> <type>pom</type>
<inherited>true</inherited> <scope>import</scope>
<configuration> </dependency>
<source>1.8</source> <dependency>
<target>1.8</target> <groupId>org.jdbi</groupId>
<compilerArgs> <artifactId>jdbi3-bom</artifactId>
<arg>-parameters</arg> <type>pom</type>
</compilerArgs> <version>${jdbi.version}</version>
</configuration> <scope>import</scope>
</plugin> </dependency>
<plugin> </dependencies>
<groupId>org.apache.maven.plugins</groupId> </dependencyManagement>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.3</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>${jetty-version}</version>
<configuration>
<scanIntervalSeconds>10</scanIntervalSeconds>
</configuration>
</plugin>
</plugins>
</build>
<dependencyManagement> <dependencies>
<dependencies> <dependency>
<dependency> <groupId>org.glassfish.jersey.containers</groupId>
<groupId>org.glassfish.jersey</groupId> <artifactId>jersey-container-grizzly2-http</artifactId>
<artifactId>jersey-bom</artifactId> </dependency>
<version>${jersey.version}</version> <dependency>
<type>pom</type> <groupId>org.glassfish.jersey.inject</groupId>
<scope>import</scope> <artifactId>jersey-hk2</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.jdbi</groupId>
<artifactId>jdbi3-bom</artifactId>
<type>pom</type>
<version>3.12.0</version>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies> <!-- uncomment this to get JSON support: -->
<dependency> <dependency>
<groupId>org.glassfish.jersey.containers</groupId> <groupId>org.glassfish.jersey.media</groupId>
<!-- use the following artifactId if you don't need servlet 2.x compatibility --> <artifactId>jersey-media-json-binding</artifactId>
<artifactId>jersey-container-servlet</artifactId> </dependency>
</dependency> <!-- -->
<dependency> <dependency>
<groupId>org.glassfish.jersey.inject</groupId> <groupId>junit</groupId>
<artifactId>jersey-hk2</artifactId> <artifactId>junit</artifactId>
</dependency> <version>4.12</version>
<!-- uncomment this to get JSON support --> <scope>test</scope>
<dependency> </dependency>
<groupId>org.glassfish.jersey.media</groupId> <dependency>
<artifactId>jersey-media-json-binding</artifactId> <groupId>org.glassfish.jersey.test-framework.providers</groupId>
</dependency> <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<!-- --> <version>${jersey.version}</version>
<dependency> </dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId> <!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc -->
<artifactId>jersey-test-framework-provider-jetty</artifactId> <dependency>
<version>2.30</version> <groupId>org.xerial</groupId>
</dependency> <artifactId>sqlite-jdbc</artifactId>
<!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc --> <version>${sqlite-jdbc.version}</version>
<dependency> </dependency>
<groupId>org.xerial</groupId> <!-- https://mvnrepository.com/artifact/org.jdbi/jdbi3-core -->
<artifactId>sqlite-jdbc</artifactId> <dependency>
<version>3.30.1</version> <groupId>org.jdbi</groupId>
</dependency> <artifactId>jdbi3-core</artifactId>
<!-- https://mvnrepository.com/artifact/org.jdbi/jdbi3-core --> </dependency>
<dependency> <dependency>
<groupId>org.jdbi</groupId> <groupId>org.jdbi</groupId>
<artifactId>jdbi3-core</artifactId> <artifactId>jdbi3-sqlobject</artifactId>
<version>3.12.0</version> </dependency>
</dependency> <dependency>
<dependency> <groupId>org.jdbi</groupId>
<groupId>org.jdbi</groupId> <artifactId>jdbi3-sqlite</artifactId>
<artifactId>jdbi3-sqlobject</artifactId> </dependency>
<version>3.12.0</version> <dependency>
</dependency> <groupId>org.jdbi</groupId>
<dependency> <artifactId>jdbi3-testing</artifactId>
<groupId>org.jdbi</groupId> <version>3.12.0</version>
<artifactId>jdbi3-sqlite</artifactId> <scope>test</scope>
<version>3.12.0</version> </dependency>
</dependency> <dependency>
<dependency> <groupId>jakarta.xml.bind</groupId>
<groupId>org.jdbi</groupId> <artifactId>jakarta.xml.bind-api</artifactId>
<artifactId>jdbi3-testing</artifactId> <version>3.0.0</version>
<version>3.12.0</version> </dependency>
<scope>test</scope> <dependency>
</dependency> <groupId>com.sun.xml.bind</groupId>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic --> <artifactId>jaxb-impl</artifactId>
<dependency> <version>3.0.0</version>
<groupId>ch.qos.logback</groupId> <scope>runtime</scope>
<artifactId>logback-classic</artifactId> </dependency>
<version>1.2.3</version> <!-- dependency>
</dependency> <groupId></groupId>
</dependencies> <artifactId></artifactId>
<version></version>
<properties> </dependency-->
<jersey.version>2.30</jersey.version> </dependencies>
<jetty-version>9.4.26.v20200117</jetty-version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <build>
</properties> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<inherited>true</inherited>
<configuration>
<release>11</release>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>fr.ulille.iut.pizzaland.Main</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>${shade.version}</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>fr.ulille.iut.pizzaland.Main</mainClass>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<jersey.version>3.0.0</jersey.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<shade.version>3.2.4</shade.version>
<lombok.version>1.18.16</lombok.version>
<jdbi.version>3.16.0</jdbi.version>
<sqlite-jdbc.version>3.34.0</sqlite-jdbc.version>
<jaxb.version>2.4.0-b180830.0359</jaxb.version>
</properties>
</project> </project>
...@@ -10,26 +10,21 @@ import org.jdbi.v3.sqlobject.SqlObjectPlugin; ...@@ -10,26 +10,21 @@ import org.jdbi.v3.sqlobject.SqlObjectPlugin;
public class BDDFactory { public class BDDFactory {
private static Jdbi jdbi = null; private static Jdbi jdbi = null;
private static String dbPath = "jdbc:sqlite:" private static String dbPath = "jdbc:sqlite:" + System.getProperty("java.io.tmpdir")
+ System.getProperty("java.io.tmpdir") + System.getProperty("file.separator") + System.getProperty("user.name") + "_";
+ System.getProperty("file.separator")
+ System.getProperty("user.name")
+ "_";
public static Jdbi getJdbi() { public static Jdbi getJdbi() {
if ( jdbi == null ) { if (jdbi == null) {
jdbi = Jdbi.create(dbPath + "pizza.db") jdbi = Jdbi.create(dbPath + "pizza.db").installPlugin(new SQLitePlugin())
.installPlugin(new SQLitePlugin()) .installPlugin(new SqlObjectPlugin());
.installPlugin(new SqlObjectPlugin());
} }
return jdbi; return jdbi;
} }
public static void setJdbiForTests() { public static void setJdbiForTests() {
if ( jdbi == null ) { if (jdbi == null) {
jdbi = Jdbi.create(dbPath + "pizza_test.db") jdbi = Jdbi.create(dbPath + "pizza_test.db").installPlugin(new SQLitePlugin())
.installPlugin(new SQLitePlugin()) .installPlugin(new SqlObjectPlugin());
.installPlugin(new SqlObjectPlugin());
} }
} }
...@@ -43,5 +38,5 @@ public class BDDFactory { ...@@ -43,5 +38,5 @@ public class BDDFactory {
public static <T> T buildDao(Class<T> daoClass) { public static <T> T buildDao(Class<T> daoClass) {
return getJdbi().onDemand(daoClass); return getJdbi().onDemand(daoClass);
} }
} }
package fr.ulille.iut.pizzaland;
import java.io.IOException;
import java.net.URI;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.logging.LoggingFeature;
import org.glassfish.jersey.server.ResourceConfig;
/**
* Main class.
*
*/
public class Main {
// Base URI the Grizzly HTTP server will listen on
public static final String BASE_URI = "http://localhost:8080/api/v1/";
private static final LogManager logManager = LogManager.getLogManager();
static {
try {
logManager.readConfiguration(Main.class.getClassLoader().getResourceAsStream("logging.properties"));
}
catch ( Exception ex ) {
ex.printStackTrace();
}
}
/**
* Starts Grizzly HTTP server exposing JAX-RS resources defined in this
* application.
*
* @return Grizzly HTTP server.
*/
public static HttpServer startServer() {
// create a resource config that scans for JAX-RS resources and providers
// in fr.ulille.iut.todo package
/* ResourceConfig rc = new ResourceConfig().packages("fr.ulille.iut.pizzaland");
// Activation des log des requêtes et réponses
String logging = System.getenv("LOG_MESSAGES");
if ( logging != null && logging.equalsIgnoreCase("true") ) {
rc.register(new LoggingFeature(Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME), Level.INFO,
LoggingFeature.Verbosity.PAYLOAD_ANY, 10000));
}
String debug = System.getenv("DEBUG_INTERNAL");
if ( debug != null && debug.equalsIgnoreCase("true") ) {
rc.register(DebugMapper.class);
}*/
// create and start a new instance of grizzly http server
// exposing the Jersey application at BASE_URI
return GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), new ApiV1());
}
/**
* Main method.
*
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
final HttpServer server = startServer();
System.out.println(String.format(
"Jersey app started with WADL available at " + "%sapplication.wadl\nexport LOG_MESSAGES=true pour voir les requêtes et réponses\nexport DEBUG_INTERNAL=true pour voir les erreurs 500\nHit enter to stop it...",
BASE_URI));
System.in.read();
server.stop();
}
}
package fr.ulille.iut.pizzaland.resources;
import java.sql.SQLException;
import fr.ulille.iut.pizzaland.BDDFactory;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
/**
* BDDClearRessource
*/
@Path("clearDatabase")
public class BDDClearRessource {
@GET
public void clearDatabase() throws SQLException {
BDDFactory.dropTables();
}
}
[ [
{ "name": "mozzarella"}, { "id": "f38806a8-7c85-49ef-980c-149dcd81d306", "name": "mozzarella"},
{ "name": "jambon"}, { "id": "d36903e1-0cc0-4bd6-a0ed-e0e9bf7b4037", "name": "jambon"},
{ "name": "champignons"}, { "id": "bc5b315f-442f-4ee4-96de-486d48f20c2f", "name": "champignons"},
{ "name": "olives"}, { "id": "6a04320c-3a4f-4570-96d3-61faf3f898b0", "name": "olives"},
{ "name": "tomate"}, { "id": "c77deeee-d50d-49d5-9695-c98ec811f762", "name": "tomate"},
{ "name": "merguez"}, { "id": "c9375542-8142-43f6-b54d-0d63597cf614", "name": "merguez"},
{ "name": "lardons"}, { "id": "dee27dd6-f9b6-4d03-ac4b-216b5c9c8bd7", "name": "lardons"},
{ "name": "fromage"}, { "id": "657f8dd4-6bc1-4622-9af7-37d248846a23", "name": "fromage"},
{ "name": "oeuf"}, { "id": "070d8077-a713-49a0-af37-3936b63d5ff2", "name": "oeuf"},
{ "name": "poivrons"}, { "id": "5d9ca5c4-517f-40fd-aac3-5a823d680c1d", "name": "poivrons"},
{ "name": "ananas"}, { "id": "52f68024-24ec-46c0-8e77-c499dba1e27e", "name": "ananas"},
{ "name": "reblochon"} { "id": "dfdf6fae-f1b2-45fa-8c39-54e522c1933f", "name": "reblochon"}
] ]
<configuration scan="true">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
</appender>
<root level="info">
<appender-ref ref="STDOUT"/>
</root>
<logger name="fr.ulille.iut.pizzaland" level="debug"/>
</configuration>
handlers= java.util.logging.ConsoleHandler
.level= INFO
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment