Skip to content
Snippets Groups Projects
Commit 6d2dac6b authored by Emmanuel Bruno's avatar Emmanuel Bruno
Browse files

Merge branch 'feature/jakarta' into develop

parents 8287f4ca 97952604
No related branches found
No related tags found
No related merge requests found
Showing
with 380 additions and 111 deletions
...@@ -21,10 +21,6 @@ gen/ ...@@ -21,10 +21,6 @@ gen/
# Local configuration file (sdk path, etc) # Local configuration file (sdk path, etc)
local.properties local.properties
# Eclipse project files
.classpath
.project
# Proguard folder generated by Eclipse # Proguard folder generated by Eclipse
proguard/ proguard/
......
...@@ -2,6 +2,68 @@ ...@@ -2,6 +2,68 @@
## Usage ## Usage
Compile, package, and run Integration Tests (verify). Launch the REST Server.
```shell ```shell
git clone --branch jakarta \
https://github.com/emmanuelbruno/cours-java-librarymanager-rest.git
mvn clean verify && \
mvn exec:java
```
Get a Hello message
```shell
curl -v http://127.0.0.1:9998/myapp/biblio
```
Init the database with two authors
```shell
curl -v -X PUT "http://localhost:9998/myapp/biblio/init"
```
Get author 1 in JSON
```shell
curl -v -H "Accept: application/json" \
http://127.0.0.1:9998/myapp/biblio/auteurs/1
```
Get author 2 in XML
```shell
curl -v -H "Accept: text/xml" \
http://127.0.0.1:9998/myapp/biblio/auteurs/2
```
Get authors in JSON
```shell
curl -v -H "Accept: application/json" \
http://127.0.0.1:9998/myapp/biblio/auteurs
``` ```
Removes all authors
```shell
curl -v -X DELETE "http://localhost:9998/myapp/biblio/authors"
```
Adds an author
```shell
curl -v -H "Accept: application/json" \
-H "Content-type: application/json" \
-X POST \
-d '{"nom":"John","prenom":"Smith","biographie":"My life"}' \
"http://localhost:9998/myapp/biblio/auteurs/"
```
Fully update an author
```shell
curl -v -H "Accept: application/json" \
-H "Content-type: application/json" \
-X PUT \
-d '{"nom":"Martin","prenom":"Jean","biographie":"ma vie"}' \
"http://localhost:9998/myapp/biblio/auteurs/1"
```
If a resource doesn't exist an exception is raised, and the 404 http status code is returned
```shell
curl -v -H "Accept: application/json" \
http://127.0.0.1:9998/myapp/biblio/auteurs/1000
```
...@@ -48,11 +48,6 @@ ...@@ -48,11 +48,6 @@
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-jaxb</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.glassfish.jersey.media</groupId> <groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId> <artifactId>jersey-media-json-jackson</artifactId>
...@@ -122,11 +117,26 @@ ...@@ -122,11 +117,26 @@
<mainClass>fr.univtln.bruno.samples.jaxrs.server.BiblioServer</mainClass> <mainClass>fr.univtln.bruno.samples.jaxrs.server.BiblioServer</mainClass>
</configuration> </configuration>
</plugin> </plugin>
<!-- Test d'intégration -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.0.0-M4</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>
<properties> <properties>
<jersey.version>3.0.1</jersey.version> <jersey.version>3.0.1</jersey.version>
<jaxb-api.version>2.3.3</jaxb-api.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties> </properties>
......
package fr.univtln.bruno.samples.jaxrs.exceptions;
import jakarta.ws.rs.core.Response;
import lombok.Getter;
@Getter
public class BusinessException extends Exception {
final Response.Status status;
public BusinessException(Response.Status status) {
super(status.getReasonPhrase());
this.status = status;
}
}
package fr.univtln.bruno.samples.jaxrs.exceptions;
import jakarta.ws.rs.core.Response;
public class IllegalArgumentException extends BusinessException {
public IllegalArgumentException() {
super(Response.Status.NOT_ACCEPTABLE);
}
}
package fr.univtln.bruno.samples.jaxrs.exceptions;
import jakarta.ws.rs.core.Response;
public class NotFoundException extends BusinessException {
public NotFoundException() {
super(Response.Status.NOT_FOUND);
}
}
package fr.univtln.bruno.samples.jaxrs.mappers;
import fr.univtln.bruno.samples.jaxrs.exceptions.BusinessException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
import lombok.AccessLevel;
import lombok.experimental.FieldDefaults;
@SuppressWarnings("unused")
@Provider
@FieldDefaults(level = AccessLevel.PRIVATE)
public class BusinessExceptionMapper implements ExceptionMapper<BusinessException> {
public Response toResponse(BusinessException ex) {
return Response.status(ex.getStatus())
.entity(ex.getMessage())
.type(MediaType.APPLICATION_JSON)
.build();
}
}
package fr.univtln.bruno.samples.jaxrs.mappers;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.experimental.FieldDefaults;
@SuppressWarnings("unused")
@Provider
@FieldDefaults(level = AccessLevel.PRIVATE)
public class GenericExceptionMapper implements ExceptionMapper<Exception> {
final Response.Status status = Response.Status.INTERNAL_SERVER_ERROR;
public Response toResponse(Exception ex) {
return Response.status(status)
.entity(ex.getMessage())
.type(MediaType.APPLICATION_JSON)
.build();
}
}
package fr.univtln.bruno.samples.jaxrs.model; package fr.univtln.bruno.samples.jaxrs.model;
import fr.univtln.bruno.samples.jaxrs.exceptions.IllegalArgumentException;
import fr.univtln.bruno.samples.jaxrs.exceptions.NotFoundException;
import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlAttribute; import jakarta.xml.bind.annotation.XmlAttribute;
...@@ -19,21 +21,29 @@ public class BiblioModel { ...@@ -19,21 +21,29 @@ public class BiblioModel {
private static long lastId = 0; private static long lastId = 0;
MutableLongObjectMap<Auteur> auteurs = LongObjectMaps.mutable.empty(); MutableLongObjectMap<Auteur> auteurs = LongObjectMaps.mutable.empty();
public Auteur putAuteur(Auteur auteur) { public Auteur addAuteur(Auteur auteur) throws IllegalArgumentException {
if (auteur.id!=0) throw new IllegalArgumentException();
auteur.id = ++lastId; auteur.id = ++lastId;
return auteurs.put(auteur.id, auteur); auteurs.put(auteur.id, auteur);
return auteur;
} }
public Auteur updateAuteur(long id, Auteur auteur) { public Auteur updateAuteur(long id, Auteur auteur) throws NotFoundException, IllegalArgumentException {
return auteurs.put(id, auteur); if (auteur.id!=0) throw new IllegalArgumentException();
auteur.id = id;
if (!auteurs.containsKey(id)) throw new NotFoundException();
auteurs.put(id, auteur);
return auteur;
} }
public Auteur removeAuteur(long l) { public void removeAuteur(long id) throws NotFoundException {
return auteurs.remove(l); if (!auteurs.containsKey(id)) throw new NotFoundException();
auteurs.remove(id);
} }
public Auteur getAuteur(long l) { public Auteur getAuteur(long id) throws NotFoundException {
return auteurs.get(l); if (!auteurs.containsKey(id)) throw new NotFoundException();
return auteurs.get(id);
} }
public int getAuteurSize() { public int getAuteurSize() {
...@@ -42,6 +52,7 @@ public class BiblioModel { ...@@ -42,6 +52,7 @@ public class BiblioModel {
public void supprimerAuteurs() { public void supprimerAuteurs() {
auteurs.clear(); auteurs.clear();
lastId=0;
} }
@Builder @Builder
......
package fr.univtln.bruno.samples.jaxrs.resources; package fr.univtln.bruno.samples.jaxrs.resources;
import fr.univtln.bruno.samples.jaxrs.exceptions.IllegalArgumentException;
import fr.univtln.bruno.samples.jaxrs.exceptions.NotFoundException;
import fr.univtln.bruno.samples.jaxrs.model.BiblioModel; import fr.univtln.bruno.samples.jaxrs.model.BiblioModel;
import fr.univtln.bruno.samples.jaxrs.model.BiblioModel.Auteur; import fr.univtln.bruno.samples.jaxrs.model.BiblioModel.Auteur;
import jakarta.ws.rs.*; import jakarta.ws.rs.*;
...@@ -12,8 +14,9 @@ import java.util.Collection; ...@@ -12,8 +14,9 @@ import java.util.Collection;
@Path("biblio") @Path("biblio")
@Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_XML}) @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_XML})
public class BiblioResource { public class BiblioResource {
private static BiblioModel modeleBibliotheque = BiblioModel.of(); private static final BiblioModel modeleBibliotheque = BiblioModel.of();
@SuppressWarnings("SameReturnValue")
@GET @GET
@Produces(MediaType.TEXT_PLAIN) @Produces(MediaType.TEXT_PLAIN)
public String sayHello() { public String sayHello() {
...@@ -22,32 +25,41 @@ public class BiblioResource { ...@@ -22,32 +25,41 @@ public class BiblioResource {
@PUT @PUT
@Path("init") @Path("init")
public int init() { public int init() throws IllegalArgumentException {
modeleBibliotheque.putAuteur(Auteur.builder().prenom("Jean").nom("Martin").build()); modeleBibliotheque.addAuteur(Auteur.builder().prenom("Jean").nom("Martin").build());
modeleBibliotheque.putAuteur(Auteur.builder().prenom("Marie").nom("Durand").build()); modeleBibliotheque.addAuteur(Auteur.builder().prenom("Marie").nom("Durand").build());
return modeleBibliotheque.getAuteurSize(); return modeleBibliotheque.getAuteurSize();
} }
@PUT @PUT
@Path("auteurs/{id}")
@Consumes(MediaType.APPLICATION_JSON)
public Auteur updateAuteur(@PathParam("id") long id, Auteur auteur) throws NotFoundException, IllegalArgumentException {
return modeleBibliotheque.updateAuteur(id, auteur);
}
@POST
@Path("auteurs") @Path("auteurs")
public void ajouterAuteur(@QueryParam("prenom") String prenom, @QueryParam("nom") String nom) { @Consumes(MediaType.APPLICATION_JSON)
modeleBibliotheque.putAuteur(Auteur.builder().prenom(prenom).nom(nom).build()); public Auteur ajouterAuteur(Auteur auteur) throws IllegalArgumentException {
return modeleBibliotheque.addAuteur(auteur);
} }
@DELETE @DELETE
@Path("auteurs/{id}") @Path("auteurs/{id}")
public void supprimerAuteur(@PathParam("id") final long id) { public void supprimerAuteur(@PathParam("id") final long id) throws NotFoundException {
modeleBibliotheque.removeAuteur(id); modeleBibliotheque.removeAuteur(id);
} }
@DELETE @DELETE
@Path("auteurs")
public void supprimerAuteurs() { public void supprimerAuteurs() {
modeleBibliotheque.supprimerAuteurs(); modeleBibliotheque.supprimerAuteurs();
} }
@GET @GET
@Path("auteurs/{id}") @Path("auteurs/{id}")
public Auteur getAuteur(@PathParam("id") final long id) { public Auteur getAuteur(@PathParam("id") final long id) throws NotFoundException {
return modeleBibliotheque.getAuteur(id); return modeleBibliotheque.getAuteur(id);
} }
......
...@@ -6,7 +6,6 @@ import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory; ...@@ -6,7 +6,6 @@ import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.logging.LoggingFeature; import org.glassfish.jersey.logging.LoggingFeature;
import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.server.ResourceConfig;
import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
...@@ -42,9 +41,8 @@ public class BiblioServer { ...@@ -42,9 +41,8 @@ public class BiblioServer {
* Main method. * Main method.
* *
* @param args the input arguments * @param args the input arguments
* @throws IOException the io exception
*/ */
public static void main(String[] args) throws IOException, InterruptedException { public static void main(String[] args) throws InterruptedException {
log.info("Rest server starting..." + BASE_URI); log.info("Rest server starting..." + BASE_URI);
final HttpServer server = startServer(); final HttpServer server = startServer();
......
package fr.univtln.bruno.samples.jaxrs;
import fr.univtln.bruno.samples.jaxrs.model.BiblioModel.Auteur;
import fr.univtln.bruno.samples.jaxrs.server.BiblioServer;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.message.internal.MediaTypes;
import org.junit.*;
import java.util.Collection;
import static org.junit.Assert.*;
/**
* A simple junit integration test for A REST service.
*/
public class ServerIT {
private static HttpServer httpServer;
private static WebTarget webTarget;
/**
* Starts the application before the tests.
*
*/
@BeforeClass
public static void setUp() {
//start the Grizzly2 web container
httpServer = BiblioServer.startServer();
// create the client
Client client = ClientBuilder.newClient();
webTarget = client.target(BiblioServer.BASE_URI);
}
/**
* Stops the application at the end of the test.
*
*/
@AfterClass
public static void tearDown() {
httpServer.shutdown();
}
/**
* Adds two authors before each tests.
*/
@Before
public void beforeEach() {
webTarget.path("biblio/init").request().put(Entity.entity("", MediaType.TEXT_PLAIN));
}
/**
* Clears the data after each tests.
*/
@After
public void afterEach() {
webTarget.path("biblio/auteurs").request().delete();
}
@Test
public void testHello() {
String hello = webTarget.path("biblio").request(MediaType.TEXT_PLAIN).get(String.class);
assertEquals("hello", hello);
}
/**
* Tests to get a author by id in JSON.
*/
@Test
public void testGetAuteurJSON() {
Auteur responseAuteur = webTarget.path("biblio/auteurs/1").request(MediaType.APPLICATION_JSON).get(Auteur.class);
assertNotNull(responseAuteur);
assertEquals("Jean", responseAuteur.getPrenom());
assertEquals("Martin", responseAuteur.getNom());
}
/**
* Tests to get a author by id in XML.
*/
@Test
public void testGetAuteurXML() {
Auteur responseAuteur = webTarget.path("biblio/auteurs/1").request(MediaType.TEXT_XML).get(Auteur.class);
assertNotNull(responseAuteur);
assertEquals("Jean", responseAuteur.getPrenom());
assertEquals("Martin", responseAuteur.getNom());
}
/**
* Tests to get a author by id in JSON.
*/
@Test
public void testGetAuteurJSONNotFoundException() {
Response response = webTarget.path("biblio/auteurs/10").request(MediaType.APPLICATION_JSON).get();
assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus());
}
/**
* Tests to get a collection of authors in JSON.
*/
@Test
public void testGetAuteurs() {
@SuppressWarnings("unchecked")
Collection<Auteur> responseAuteurs = webTarget.path("biblio/auteurs").request(MediaType.APPLICATION_JSON).get(Collection.class);
assertEquals(2, responseAuteurs.size());
}
/**
* Tests to clear authors.
*/
@Test
public void deleteAuteurs() {
webTarget.path("biblio/auteurs").request().delete();
@SuppressWarnings("unchecked")
Collection<Auteur> responseAuteurs = webTarget.path("biblio/auteurs").request(MediaType.APPLICATION_JSON).get(Collection.class);
assertEquals(0, responseAuteurs.size());
}
/**
* Tests to add an author in JSON.
*/
@Test
public void addAuteur() {
webTarget.path("biblio/auteurs")
.request(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.post(Entity.entity("{\"nom\":\"Smith\",\"prenom\":\"John\",\"biographie\":\"My life\"}", MediaType.APPLICATION_JSON));
@SuppressWarnings("unchecked")
Collection<Auteur> responseAuteurs = webTarget.path("biblio/auteurs").request(MediaType.APPLICATION_JSON).get(Collection.class);
assertEquals(3, responseAuteurs.size());
Auteur responseAuteur = webTarget.path("biblio/auteurs/3").request(MediaType.APPLICATION_JSON).get(Auteur.class);
assertNotNull(responseAuteur);
assertEquals("John", responseAuteur.getPrenom());
assertEquals("Smith", responseAuteur.getNom());
assertEquals("My life", responseAuteur.getBiographie());
}
/**
* Tests update an author in JSON.
*/
@Test
public void updateAuteur() {
webTarget.path("biblio/auteurs/1")
.request(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.put(Entity.entity("{\"nom\":\"Doe\",\"prenom\":\"Jim\",\"biographie\":\"My weird life\"}", MediaType.APPLICATION_JSON));
Auteur responseAuteur = webTarget.path("biblio/auteurs/1").request(MediaType.APPLICATION_JSON).get(Auteur.class);
assertNotNull(responseAuteur);
assertEquals("Jim", responseAuteur.getPrenom());
assertEquals("Doe", responseAuteur.getNom());
assertEquals("My weird life", responseAuteur.getBiographie());
}
/**
* Tests update an author in JSON.
*/
@Test
public void updateAuteurIllegalArgument() {
Response response = webTarget.path("biblio/auteurs/1")
.request(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.put(Entity.entity("{\"id\":\"1\",\"nom\":\"Doe\",\"prenom\":\"Jim\",\"biographie\":\"My weird life\"}", MediaType.APPLICATION_JSON));
assertEquals(Response.Status.NOT_ACCEPTABLE.getStatusCode(), response.getStatus());
}
/**
* Test if a WADL document is available at the relative path
* "application.wadl".
*/
@Test
public void testApplicationWadl() {
String serviceWadl = webTarget.path("application.wadl")
.request(MediaTypes.WADL_TYPE)
.get(String.class);
assertTrue(serviceWadl.length() > 0);
}
}
package fr.univtln.bruno.samples.jaxrs;
import fr.univtln.bruno.samples.jaxrs.model.BiblioModel.Auteur;
import fr.univtln.bruno.samples.jaxrs.server.BiblioServer;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.MediaType;
import junit.framework.TestCase;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.message.internal.MediaTypes;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
public class ServerTest extends TestCase {
private HttpServer httpServer;
private WebTarget webTarget;
public ServerTest(String testName) {
super(testName);
}
@BeforeClass
protected void setUp() throws Exception {
//start the Grizzly2 web container
httpServer = BiblioServer.startServer();
// create the client
Client client = ClientBuilder.newClient();
webTarget = client.target(BiblioServer.BASE_URI);
}
@AfterClass
protected void tearDown() throws Exception {
httpServer.shutdown();
}
@Before
protected void beforeEach() {
webTarget.path("biblio/init").request(MediaType.TEXT_PLAIN).put(Entity.entity("", MediaType.TEXT_PLAIN));
}
@After
protected void afterEach() {
webTarget.path("biblio").request(MediaType.TEXT_PLAIN).delete();
}
/**
* Test to get the auteur of id 1.
*/
public void testGetAuteurJSON() {
Auteur responseAuteur = webTarget.path("biblio/auteurs/1").request(MediaType.APPLICATION_JSON).get(Auteur.class);
assertNotNull(responseAuteur);
assertEquals("Jean", responseAuteur.getPrenom());
assertEquals("Martin", responseAuteur.getNom());
}
public void testGetAuteurXML() {
Auteur responseAuteur = webTarget.path("biblio/auteurs/1").request(MediaType.TEXT_XML).get(Auteur.class);
assertNotNull(responseAuteur);
assertEquals("Jean", responseAuteur.getPrenom());
assertEquals("Martin", responseAuteur.getNom());
}
/**
* Test if a WADL document is available at the relative path
* "application.wadl".
*/
public void testApplicationWadl() {
String serviceWadl = webTarget.path("application.wadl")
.request(MediaTypes.WADL_TYPE)
.get(String.class);
assertTrue(serviceWadl.length() > 0);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment