├── .travis.yml ├── src ├── main │ └── java │ │ └── rest │ │ └── addressbook │ │ ├── PhoneType.java │ │ ├── PhoneNumber.java │ │ ├── ApplicationConfig.java │ │ ├── AddressBook.java │ │ ├── Person.java │ │ ├── Server.java │ │ └── AddressBookService.java └── test │ └── java │ └── rest │ └── addressbook │ └── AddressBookServiceTest.java ├── README.md └── .gitignore /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | -------------------------------------------------------------------------------- /src/main/java/rest/addressbook/PhoneType.java: -------------------------------------------------------------------------------- 1 | package rest.addressbook; 2 | 3 | public enum PhoneType { 4 | MOBILE, HOME, WORK 5 | } 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Web Engineering 2015-2016 / Restful Web Services 2 | Please, go to the [Wiki](https://github.com/UNIZAR-30246-WebEngineering/Laboratory-3-Restful-Web-Services/wiki) in order to get the instructions for this assignment. 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | 4 | # Ignore Gradle GUI configuration 5 | gradle-app.setting 6 | 7 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 8 | !gradle-wrapper.jar 9 | bin/ 10 | .classpath 11 | .project 12 | .settings 13 | 14 | # Ignore IntelliJ IDEA extra files 15 | .idea/ 16 | *.iml 17 | *.xml 18 | application.properties 19 | -------------------------------------------------------------------------------- /src/main/java/rest/addressbook/PhoneNumber.java: -------------------------------------------------------------------------------- 1 | package rest.addressbook; 2 | 3 | import static rest.addressbook.PhoneType.*; 4 | 5 | /** 6 | * A phone number 7 | * 8 | */ 9 | public class PhoneNumber { 10 | 11 | private String number; 12 | private PhoneType type = HOME; 13 | 14 | public String getNumber() { 15 | return number; 16 | } 17 | public void setNumber(String number) { 18 | this.number = number; 19 | } 20 | public PhoneType getType() { 21 | return type; 22 | } 23 | public void setType(PhoneType type) { 24 | this.type = type; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/rest/addressbook/ApplicationConfig.java: -------------------------------------------------------------------------------- 1 | package rest.addressbook; 2 | 3 | import org.eclipse.persistence.jaxb.rs.MOXyJsonProvider; 4 | import org.glassfish.hk2.utilities.binding.AbstractBinder; 5 | import org.glassfish.jersey.server.ResourceConfig; 6 | 7 | public class ApplicationConfig extends ResourceConfig { 8 | 9 | /** 10 | * Default constructor 11 | */ 12 | public ApplicationConfig() { 13 | this(new AddressBook()); 14 | } 15 | 16 | 17 | /** 18 | * Main constructor 19 | * @param addressBook a provided address book 20 | */ 21 | public ApplicationConfig(final AddressBook addressBook) { 22 | register(AddressBookService.class); 23 | register(MOXyJsonProvider.class); 24 | register(new AbstractBinder() { 25 | 26 | @Override 27 | protected void configure() { 28 | bind(addressBook).to(AddressBook.class); 29 | }}); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/rest/addressbook/AddressBook.java: -------------------------------------------------------------------------------- 1 | package rest.addressbook; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * A really simple Address Book. This class is not thread safe. 8 | * 9 | */ 10 | public class AddressBook { 11 | 12 | private int nextId = 1; 13 | private List personList = new ArrayList(); 14 | 15 | /** 16 | * The value of next unique identifier. 17 | * @return the next unique identifier. 18 | */ 19 | public int getNextId() { 20 | return nextId; 21 | } 22 | 23 | public void setNextId(int nextId) { 24 | this.nextId = nextId; 25 | } 26 | 27 | /** 28 | * The list of persons in this address book. 29 | * @return a person list. 30 | */ 31 | public List getPersonList() { 32 | return personList; 33 | } 34 | 35 | public void setPersonList(List persons) { 36 | this.personList = persons; 37 | } 38 | 39 | /** 40 | * Returns the old next identifier and increases the new value in one. 41 | * @return an identifier. 42 | */ 43 | public int nextId() { 44 | int oldValue = nextId; 45 | nextId++; 46 | return oldValue; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/rest/addressbook/Person.java: -------------------------------------------------------------------------------- 1 | package rest.addressbook; 2 | 3 | import java.net.URI; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | /** 8 | * A person entry in an address book 9 | * 10 | */ 11 | public class Person { 12 | 13 | private String name; 14 | private int id; 15 | private String email; 16 | private URI href; 17 | private List phoneList = new ArrayList(); 18 | 19 | public String getName() { 20 | return name; 21 | } 22 | 23 | public void setName(String name) { 24 | this.name = name; 25 | } 26 | 27 | public int getId() { 28 | return id; 29 | } 30 | 31 | public void setId(int id) { 32 | this.id = id; 33 | } 34 | 35 | public String getEmail() { 36 | return email; 37 | } 38 | 39 | public void setEmail(String email) { 40 | this.email = email; 41 | } 42 | 43 | public List getPhoneList() { 44 | return phoneList; 45 | } 46 | 47 | public void setPhoneList(List phones) { 48 | this.phoneList = phones; 49 | } 50 | 51 | public void addPhone(PhoneNumber phone) { 52 | getPhoneList().add(phone); 53 | } 54 | 55 | public boolean hasEmail() { 56 | return getEmail() != null; 57 | } 58 | 59 | public void setHref(URI href) { 60 | this.href = href; 61 | } 62 | 63 | public URI getHref() { 64 | return href; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/rest/addressbook/Server.java: -------------------------------------------------------------------------------- 1 | package rest.addressbook; 2 | 3 | import java.io.IOException; 4 | import java.net.URI; 5 | import java.util.Scanner; 6 | import java.util.logging.Level; 7 | import java.util.logging.Logger; 8 | 9 | import javax.ws.rs.core.UriBuilder; 10 | 11 | import org.glassfish.grizzly.Grizzly; 12 | import org.glassfish.grizzly.http.server.HttpServer; 13 | import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory; 14 | 15 | public class Server { 16 | private static final Logger LOGGER = Grizzly.logger(Server.class); 17 | 18 | public static void main(String[] args) { 19 | LOGGER.setLevel(Level.FINER); 20 | AddressBook ab = new AddressBook(); 21 | 22 | // Some dummy data 23 | Person salvador = new Person(); 24 | salvador.setName("Salvador"); 25 | salvador.setId(ab.nextId()); 26 | Person juan = new Person(); 27 | juan.setName("Juan"); 28 | juan.setId(ab.getNextId()); 29 | ab.getPersonList().add(salvador); 30 | ab.getPersonList().add(juan); 31 | 32 | URI uri = UriBuilder.fromUri("http://localhost/").port(8080).build(); 33 | HttpServer server = GrizzlyHttpServerFactory.createHttpServer(uri, 34 | new ApplicationConfig(ab)); 35 | try (Scanner scan = new Scanner(System.in)){ 36 | server.start(); 37 | LOGGER.info("Press 's'+'enter' to shutdown now the server..."); 38 | while(!scan.nextLine().equals("s")); 39 | } catch (IOException ioe) { 40 | LOGGER.log(Level.SEVERE, ioe.toString(), ioe); 41 | } finally { 42 | LOGGER.info("Shuting now"); 43 | server.shutdownNow(); 44 | LOGGER.info("Server stopped"); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/rest/addressbook/AddressBookService.java: -------------------------------------------------------------------------------- 1 | package rest.addressbook; 2 | 3 | import javax.inject.Inject; 4 | import javax.ws.rs.Consumes; 5 | import javax.ws.rs.DELETE; 6 | import javax.ws.rs.GET; 7 | import javax.ws.rs.POST; 8 | import javax.ws.rs.PUT; 9 | import javax.ws.rs.Path; 10 | import javax.ws.rs.PathParam; 11 | import javax.ws.rs.Produces; 12 | import javax.ws.rs.core.Context; 13 | import javax.ws.rs.core.MediaType; 14 | import javax.ws.rs.core.Response; 15 | import javax.ws.rs.core.Response.Status; 16 | import javax.ws.rs.core.UriInfo; 17 | 18 | /** 19 | * A service that manipulates contacts in an address book. 20 | * 21 | */ 22 | @Path("/contacts") 23 | public class AddressBookService { 24 | 25 | /** 26 | * The (shared) address book object. 27 | */ 28 | @Inject 29 | AddressBook addressBook; 30 | 31 | /** 32 | * A GET /contacts request should return the address book in JSON. 33 | * @return a JSON representation of the address book. 34 | */ 35 | @GET 36 | @Produces(MediaType.APPLICATION_JSON) 37 | public AddressBook getAddressBook() { 38 | return addressBook; 39 | } 40 | 41 | /** 42 | * A POST /contacts request should add a new entry to the address book. 43 | * @param info the URI information of the request 44 | * @param person the posted entity 45 | * @return a JSON representation of the new entry that should be available at /contacts/person/{id}. 46 | */ 47 | @POST 48 | @Consumes(MediaType.APPLICATION_JSON) 49 | public Response addPerson(@Context UriInfo info, Person person) { 50 | addressBook.getPersonList().add(person); 51 | person.setId(addressBook.nextId()); 52 | person.setHref(info.getAbsolutePathBuilder().path("person/{id}").build(person.getId())); 53 | return Response.created(person.getHref()).entity(person).build(); 54 | } 55 | 56 | /** 57 | * A GET /contacts/person/{id} request should return a entry from the address book 58 | * @param id the unique identifier of a person 59 | * @return a JSON representation of the new entry or 404 60 | */ 61 | @GET 62 | @Path("/person/{id}") 63 | @Produces(MediaType.APPLICATION_JSON) 64 | public Response getPerson(@PathParam("id") int id) { 65 | for (Person p : addressBook.getPersonList()) { 66 | if (p.getId() == id) { 67 | return Response.ok(p).build(); 68 | } 69 | } 70 | return Response.status(Status.NOT_FOUND).build(); 71 | } 72 | 73 | /** 74 | * A PUT /contacts/person/{id} should update a entry if exists 75 | * @param info the URI information of the request 76 | * @param person the posted entity 77 | * @param id the unique identifier of a person 78 | * @return a JSON representation of the new updated entry or 400 if the id is not a key 79 | */ 80 | @PUT 81 | @Path("/person/{id}") 82 | @Produces(MediaType.APPLICATION_JSON) 83 | public Response updatePerson(@Context UriInfo info, 84 | @PathParam("id") int id, Person person) { 85 | for (int i = 0; i < addressBook.getPersonList().size(); i++) { 86 | if (addressBook.getPersonList().get(i).getId() == id) { 87 | person.setId(id); 88 | person.setHref(info.getAbsolutePath()); 89 | addressBook.getPersonList().set(i, person); 90 | return Response.ok(person).build(); 91 | } 92 | } 93 | return Response.status(Status.BAD_REQUEST).build(); 94 | } 95 | 96 | /** 97 | * A DELETE /contacts/person/{id} should delete a entry if exists 98 | * @param id the unique identifier of a person 99 | * @return 204 if the request is successful, 404 if the id is not a key 100 | */ 101 | @DELETE 102 | @Path("/person/{id}") 103 | @Produces(MediaType.APPLICATION_JSON) 104 | public Response updatePerson(@PathParam("id") int id) { 105 | for (int i = 0; i < addressBook.getPersonList().size(); i++) { 106 | if (addressBook.getPersonList().get(i).getId() == id) { 107 | addressBook.getPersonList().remove(i); 108 | return Response.noContent().build(); 109 | } 110 | } 111 | return Response.status(Status.NOT_FOUND).build(); 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /src/test/java/rest/addressbook/AddressBookServiceTest.java: -------------------------------------------------------------------------------- 1 | package rest.addressbook; 2 | 3 | import org.glassfish.grizzly.http.server.HttpServer; 4 | import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory; 5 | import org.junit.After; 6 | import org.junit.Test; 7 | 8 | import javax.ws.rs.client.Client; 9 | import javax.ws.rs.client.ClientBuilder; 10 | import javax.ws.rs.client.Entity; 11 | import javax.ws.rs.core.MediaType; 12 | import javax.ws.rs.core.Response; 13 | import javax.ws.rs.core.UriBuilder; 14 | import java.io.IOException; 15 | import java.net.URI; 16 | 17 | import static org.junit.Assert.assertEquals; 18 | import static org.junit.Assert.assertNotEquals; 19 | 20 | /** 21 | * A simple test suite 22 | * 23 | */ 24 | public class AddressBookServiceTest { 25 | 26 | HttpServer server; 27 | 28 | @Test 29 | public void serviceIsAlive() throws IOException { 30 | // Prepare server 31 | AddressBook ab = new AddressBook(); 32 | launchServer(ab); 33 | 34 | // Request the address book 35 | Client client = ClientBuilder.newClient(); 36 | Response response = client.target("http://localhost:8282/contacts") 37 | .request().get(); 38 | assertEquals(200, response.getStatus()); 39 | assertEquals(0, response.readEntity(AddressBook.class).getPersonList() 40 | .size()); 41 | 42 | ////////////////////////////////////////////////////////////////////// 43 | // Verify that GET /contacts is well implemented by the service, i.e 44 | // test that it is safe and idempotent 45 | ////////////////////////////////////////////////////////////////////// 46 | 47 | // Verifying that GET is safe and idempotent 48 | // Request the address book for second time 49 | // The two sequential requests, need to have the same response (safe and idempotent) 50 | Response response2 = client.target("http://localhost:8282/contacts") 51 | .request().get(); 52 | // It has to return same response code (idempotent) 53 | assertEquals(200, response2.getStatus()); 54 | // It has to return the same person list (as first request) 55 | assertEquals(0, response2.readEntity(AddressBook.class).getPersonList() 56 | .size()); 57 | } 58 | 59 | @Test 60 | public void createUser() throws IOException { 61 | // Prepare server 62 | AddressBook ab = new AddressBook(); 63 | launchServer(ab); 64 | 65 | // Prepare data 66 | Person juan = new Person(); 67 | juan.setName("Juan"); 68 | URI juanURI = URI.create("http://localhost:8282/contacts/person/1"); 69 | 70 | // Create a new user 71 | Client client = ClientBuilder.newClient(); 72 | Response response = client.target("http://localhost:8282/contacts") 73 | .request(MediaType.APPLICATION_JSON) 74 | .post(Entity.entity(juan, MediaType.APPLICATION_JSON)); 75 | 76 | assertEquals(201, response.getStatus()); 77 | assertEquals(juanURI, response.getLocation()); 78 | assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType()); 79 | Person juanUpdated = response.readEntity(Person.class); 80 | assertEquals(juan.getName(), juanUpdated.getName()); 81 | assertEquals(1, juanUpdated.getId()); 82 | assertEquals(juanURI, juanUpdated.getHref()); 83 | 84 | // Check that the new user exists 85 | response = client.target("http://localhost:8282/contacts/person/1") 86 | .request(MediaType.APPLICATION_JSON).get(); 87 | assertEquals(200, response.getStatus()); 88 | assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType()); 89 | juanUpdated = response.readEntity(Person.class); 90 | assertEquals(juan.getName(), juanUpdated.getName()); 91 | assertEquals(1, juanUpdated.getId()); 92 | assertEquals(juanURI, juanUpdated.getHref()); 93 | 94 | ////////////////////////////////////////////////////////////////////// 95 | // Verify that POST /contacts is well implemented by the service, i.e 96 | // test that it is not safe and not idempotent 97 | ////////////////////////////////////////////////////////////////////// 98 | 99 | // Check if POST is safe (it's not) 100 | // Prepare data 101 | Person pedro = new Person(); 102 | pedro.setName("Pedro"); 103 | URI pedroURI = URI.create("http://localhost:8282/contacts/person/2"); 104 | 105 | // Create a new user 106 | Client client2 = ClientBuilder.newClient(); 107 | Response response2 = client2.target("http://localhost:8282/contacts") 108 | .request(MediaType.APPLICATION_JSON) 109 | .post(Entity.entity(pedro, MediaType.APPLICATION_JSON)); 110 | 111 | assertEquals(201, response2.getStatus()); 112 | assertEquals(pedroURI, response2.getLocation()); 113 | assertEquals(MediaType.APPLICATION_JSON_TYPE, response2.getMediaType()); 114 | Person pedroUpdated = response2.readEntity(Person.class); 115 | assertEquals(pedro.getName(), pedroUpdated.getName()); 116 | assertEquals(2, pedroUpdated.getId()); 117 | assertEquals(pedroURI, pedroUpdated.getHref()); 118 | 119 | // Check that the new user exists 120 | response2 = client2.target("http://localhost:8282/contacts/person/2") 121 | .request(MediaType.APPLICATION_JSON).get(); 122 | assertEquals(200, response2.getStatus()); 123 | assertEquals(MediaType.APPLICATION_JSON_TYPE, response2.getMediaType()); 124 | pedroUpdated = response2.readEntity(Person.class); 125 | assertEquals(pedro.getName(), pedroUpdated.getName()); 126 | assertEquals(2, pedroUpdated.getId()); 127 | assertEquals(pedroURI, pedroUpdated.getHref()); 128 | 129 | // Test if AddressBook has changed 130 | response = client.target("http://localhost:8282/contacts") 131 | .request().get(); 132 | assertEquals(2,response.readEntity(AddressBook.class) 133 | .getPersonList().size()); 134 | 135 | /* 136 | At this point, if this test has passed, it means that both POST 137 | request have modified the server resources, therefore, it's not safe. 138 | */ 139 | 140 | // Check if POST is idempotent (it's not) 141 | 142 | // Create existing user (repeat request) 143 | client = ClientBuilder.newClient(); 144 | response = client.target("http://localhost:8282/contacts") 145 | .request(MediaType.APPLICATION_JSON) 146 | .post(Entity.entity(juan, MediaType.APPLICATION_JSON)); 147 | 148 | assertEquals(201, response.getStatus()); 149 | assertNotEquals(juanURI, response.getLocation()); 150 | assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType()); 151 | juanUpdated = response.readEntity(Person.class); 152 | assertEquals(juan.getName(), juanUpdated.getName()); 153 | assertNotEquals(1, juanUpdated.getId()); 154 | assertNotEquals(juanURI, juanUpdated.getHref()); 155 | 156 | /* 157 | At this point, if this test has passed, it means that this repeated POST 158 | request have modified the server resources and have different outcome. 159 | Therefore, it's not idempotent (neither safe). 160 | */ 161 | } 162 | 163 | @Test 164 | public void createUsers() throws IOException { 165 | // Prepare server 166 | AddressBook ab = new AddressBook(); 167 | Person salvador = new Person(); 168 | salvador.setName("Salvador"); 169 | salvador.setId(ab.nextId()); 170 | ab.getPersonList().add(salvador); 171 | launchServer(ab); 172 | 173 | // Prepare data 174 | Person juan = new Person(); 175 | juan.setName("Juan"); 176 | URI juanURI = URI.create("http://localhost:8282/contacts/person/2"); 177 | Person maria = new Person(); 178 | maria.setName("Maria"); 179 | URI mariaURI = URI.create("http://localhost:8282/contacts/person/3"); 180 | 181 | // Create a user 182 | Client client = ClientBuilder.newClient(); 183 | Response response = client.target("http://localhost:8282/contacts") 184 | .request(MediaType.APPLICATION_JSON) 185 | .post(Entity.entity(juan, MediaType.APPLICATION_JSON)); 186 | assertEquals(201, response.getStatus()); 187 | assertEquals(juanURI, response.getLocation()); 188 | 189 | // Create a second user 190 | response = client.target("http://localhost:8282/contacts") 191 | .request(MediaType.APPLICATION_JSON) 192 | .post(Entity.entity(maria, MediaType.APPLICATION_JSON)); 193 | assertEquals(201, response.getStatus()); 194 | assertEquals(mariaURI, response.getLocation()); 195 | assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType()); 196 | Person mariaUpdated = response.readEntity(Person.class); 197 | assertEquals(maria.getName(), mariaUpdated.getName()); 198 | assertEquals(3, mariaUpdated.getId()); 199 | assertEquals(mariaURI, mariaUpdated.getHref()); 200 | 201 | // Check that the new user exists 202 | response = client.target("http://localhost:8282/contacts/person/3") 203 | .request(MediaType.APPLICATION_JSON).get(); 204 | assertEquals(200, response.getStatus()); 205 | assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType()); 206 | mariaUpdated = response.readEntity(Person.class); 207 | assertEquals(maria.getName(), mariaUpdated.getName()); 208 | assertEquals(3, mariaUpdated.getId()); 209 | assertEquals(mariaURI, mariaUpdated.getHref()); 210 | 211 | ////////////////////////////////////////////////////////////////////// 212 | // Verify that GET /contacts/person/3 is well implemented by the service, i.e 213 | // test that it is safe and idempotent 214 | ////////////////////////////////////////////////////////////////////// 215 | 216 | // Verify that GET /contacts/person/3 is well implemented by the service 217 | response = client.target("http://localhost:8282/contacts/person/3") 218 | .request(MediaType.APPLICATION_JSON).get(); 219 | // Response code is equal than first request 220 | assertEquals(200, response.getStatus()); 221 | // MediaType is equal than first request 222 | assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType()); 223 | mariaUpdated = response.readEntity(Person.class); 224 | // Name is equal than original Person Object 225 | assertEquals(maria.getName(), mariaUpdated.getName()); 226 | // ID is the same in both requests 227 | assertEquals(3, mariaUpdated.getId()); 228 | // URI is the same in both requests 229 | assertEquals(mariaURI, mariaUpdated.getHref()); 230 | // Test if AddressBook has changed 231 | response = client.target("http://localhost:8282/contacts") 232 | .request().get(); 233 | assertEquals(3,response.readEntity(AddressBook.class) 234 | .getPersonList().size()); 235 | 236 | /* 237 | At this point, if this test has passed, it means that this repeated GET 238 | request have not modified the server resources and have the same outcome. 239 | Therefore, it's safe and idempotent. 240 | */ 241 | } 242 | 243 | @Test 244 | public void listUsers() throws IOException { 245 | 246 | // Prepare server 247 | AddressBook ab = new AddressBook(); 248 | Person salvador = new Person(); 249 | salvador.setName("Salvador"); 250 | // Added ID for Person 251 | salvador.setId(ab.nextId()); 252 | Person juan = new Person(); 253 | juan.setName("Juan"); 254 | // Added ID for Person 255 | juan.setId(ab.nextId()); 256 | ab.getPersonList().add(salvador); 257 | ab.getPersonList().add(juan); 258 | launchServer(ab); 259 | 260 | // Test list of contacts 261 | Client client = ClientBuilder.newClient(); 262 | Response response = client.target("http://localhost:8282/contacts") 263 | .request(MediaType.APPLICATION_JSON).get(); 264 | assertEquals(200, response.getStatus()); 265 | assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType()); 266 | AddressBook addressBookRetrieved = response 267 | .readEntity(AddressBook.class); 268 | assertEquals(2, addressBookRetrieved.getPersonList().size()); 269 | assertEquals(juan.getName(), addressBookRetrieved.getPersonList() 270 | .get(1).getName()); 271 | 272 | ////////////////////////////////////////////////////////////////////// 273 | // Verify that POST is well implemented by the service, i.e 274 | // test that it is not safe and not idempotent 275 | ////////////////////////////////////////////////////////////////////// 276 | // Check if POST is idempotent and safe (it's not) 277 | 278 | // Create existing users (repeat requests) 279 | URI salvadorURI = URI.create("http://localhost:8282/contacts/person/2"); 280 | URI juanURI = URI.create("http://localhost:8282/contacts/person/1"); 281 | 282 | // Insert "Juan" for second time 283 | juan = new Person(); 284 | juan.setName("Juan"); 285 | response = client.target("http://localhost:8282/contacts") 286 | .request(MediaType.APPLICATION_JSON) 287 | .post(Entity.entity(juan, MediaType.APPLICATION_JSON)); 288 | 289 | assertEquals(201, response.getStatus()); 290 | assertNotEquals(juanURI, response.getLocation()); 291 | assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType()); 292 | Person juanUpdated = response.readEntity(Person.class); 293 | assertEquals(juan.getName(), juanUpdated.getName()); 294 | // Outcomes are not the same (not idempotent) 295 | assertNotEquals(1, juanUpdated.getId()); 296 | assertNotEquals(juanURI, juanUpdated.getHref()); 297 | 298 | // Insert "Salvador" for second time 299 | salvador = new Person(); 300 | salvador.setName("Salvador"); 301 | response = client.target("http://localhost:8282/contacts") 302 | .request(MediaType.APPLICATION_JSON) 303 | .post(Entity.entity(salvador, MediaType.APPLICATION_JSON)); 304 | 305 | assertEquals(201, response.getStatus()); 306 | assertNotEquals(salvadorURI, response.getLocation()); 307 | assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType()); 308 | Person salvadorUpdated = response.readEntity(Person.class); 309 | assertEquals(salvador.getName(), salvadorUpdated.getName()); 310 | // Outcomes are not the same (not idempotent) 311 | assertNotEquals(2, salvadorUpdated.getId()); 312 | assertNotEquals(salvadorURI, salvadorUpdated.getHref()); 313 | 314 | // GET request for testing the POST request has created resources on the server 315 | // This means POST is not safe 316 | 317 | // Checking for the first POST 318 | juanURI = URI.create("http://localhost:8282/contacts/person/3"); 319 | response = client.target("http://localhost:8282/contacts/person/3") 320 | .request(MediaType.APPLICATION_JSON).get(); 321 | // Response code is equal than first request 322 | assertEquals(200, response.getStatus()); 323 | // MediaType is equal than first request 324 | assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType()); 325 | juanUpdated = response.readEntity(Person.class); 326 | // Name is equal than original Person Object 327 | assertEquals(juan.getName(), juan.getName()); 328 | // ID is the same in both requests 329 | assertEquals(3, juanUpdated.getId()); 330 | // URI is the same in both requests 331 | assertEquals(juanURI, juanUpdated.getHref()); 332 | 333 | // Checking for the second POST 334 | salvadorURI = URI.create("http://localhost:8282/contacts/person/4"); 335 | response = client.target("http://localhost:8282/contacts/person/4") 336 | .request(MediaType.APPLICATION_JSON).get(); 337 | // Response code is equal than first request 338 | assertEquals(200, response.getStatus()); 339 | // MediaType is equal than first request 340 | assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType()); 341 | salvadorUpdated = response.readEntity(Person.class); 342 | // Name is equal than original Person Object 343 | assertEquals(salvador.getName(), salvador.getName()); 344 | // ID is the same in both requests 345 | assertEquals(4, salvadorUpdated.getId()); 346 | // URI is the same in both requests 347 | assertEquals(salvadorURI, salvadorUpdated.getHref()); 348 | 349 | /* 350 | At this point, if this test has passed, it means that this repeated POST 351 | request have modified the server resources and have different outcome. 352 | Therefore, it's not idempotent (neither safe). 353 | */ 354 | } 355 | 356 | @Test 357 | public void updateUsers() throws IOException { 358 | // Prepare server 359 | AddressBook ab = new AddressBook(); 360 | Person salvador = new Person(); 361 | salvador.setName("Salvador"); 362 | salvador.setId(ab.nextId()); 363 | Person juan = new Person(); 364 | juan.setName("Juan"); 365 | juan.setId(ab.getNextId()); 366 | URI juanURI = URI.create("http://localhost:8282/contacts/person/2"); 367 | ab.getPersonList().add(salvador); 368 | ab.getPersonList().add(juan); 369 | launchServer(ab); 370 | 371 | // Update Maria 372 | Person maria = new Person(); 373 | maria.setName("Maria"); 374 | Client client = ClientBuilder.newClient(); 375 | Response response = client 376 | .target("http://localhost:8282/contacts/person/2") 377 | .request(MediaType.APPLICATION_JSON) 378 | .put(Entity.entity(maria, MediaType.APPLICATION_JSON)); 379 | assertEquals(200, response.getStatus()); 380 | assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType()); 381 | Person juanUpdated = response.readEntity(Person.class); 382 | assertEquals(maria.getName(), juanUpdated.getName()); 383 | assertEquals(2, juanUpdated.getId()); 384 | assertEquals(juanURI, juanUpdated.getHref()); 385 | 386 | // Verify that the update is real 387 | response = client.target("http://localhost:8282/contacts/person/2") 388 | .request(MediaType.APPLICATION_JSON).get(); 389 | assertEquals(200, response.getStatus()); 390 | assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType()); 391 | Person mariaRetrieved = response.readEntity(Person.class); 392 | assertEquals(maria.getName(), mariaRetrieved.getName()); 393 | assertEquals(2, mariaRetrieved.getId()); 394 | assertEquals(juanURI, mariaRetrieved.getHref()); 395 | 396 | // Verify that only can be updated existing values 397 | response = client.target("http://localhost:8282/contacts/person/3") 398 | .request(MediaType.APPLICATION_JSON) 399 | .put(Entity.entity(maria, MediaType.APPLICATION_JSON)); 400 | assertEquals(400, response.getStatus()); 401 | 402 | ////////////////////////////////////////////////////////////////////// 403 | // Verify that PUT /contacts/person/2 is well implemented by the service, i.e 404 | // test that it is idempotent 405 | ////////////////////////////////////////////////////////////////////// 406 | 407 | // Test if PUT is idempotent (outcomes are the same, resources don't change) 408 | response = client 409 | .target("http://localhost:8282/contacts/person/2") 410 | .request(MediaType.APPLICATION_JSON) 411 | .put(Entity.entity(maria, MediaType.APPLICATION_JSON)); 412 | // Test if response contains the request Person ("Maria") information 413 | assertEquals(200, response.getStatus()); 414 | assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType()); 415 | juanUpdated = response.readEntity(Person.class); 416 | assertEquals(maria.getName(), juanUpdated.getName()); 417 | assertEquals(2, juanUpdated.getId()); 418 | assertEquals(juanURI, juanUpdated.getHref()); 419 | 420 | // Verifying the register for "Maria" has not changed in server 421 | response = client.target("http://localhost:8282/contacts/person/2") 422 | .request(MediaType.APPLICATION_JSON).get(); 423 | assertEquals(200, response.getStatus()); 424 | assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType()); 425 | mariaRetrieved = response.readEntity(Person.class); 426 | assertEquals(maria.getName(), mariaRetrieved.getName()); 427 | assertEquals(2, mariaRetrieved.getId()); 428 | assertEquals(juanURI, mariaRetrieved.getHref()); 429 | 430 | /* 431 | At this point, if this test has passed, it means that this repeated PUT 432 | request have not modified the server resources and have same outcome. 433 | Therefore, it's idempotent and not safe. 434 | */ 435 | } 436 | 437 | @Test 438 | public void deleteUsers() throws IOException { 439 | // Prepare server 440 | AddressBook ab = new AddressBook(); 441 | Person salvador = new Person(); 442 | salvador.setName("Salvador"); 443 | salvador.setId(1); 444 | Person juan = new Person(); 445 | juan.setName("Juan"); 446 | juan.setId(2); 447 | ab.getPersonList().add(salvador); 448 | ab.getPersonList().add(juan); 449 | launchServer(ab); 450 | 451 | // Delete a user 452 | Client client = ClientBuilder.newClient(); 453 | Response response = client 454 | .target("http://localhost:8282/contacts/person/2").request() 455 | .delete(); 456 | assertEquals(204, response.getStatus()); 457 | 458 | // Verify that the user has been deleted 459 | response = client.target("http://localhost:8282/contacts/person/2") 460 | .request().delete(); 461 | assertEquals(404, response.getStatus()); 462 | 463 | ////////////////////////////////////////////////////////////////////// 464 | // Verify that DELETE /contacts/person/2 is well implemented by the service, i.e 465 | // test that it is idempotent 466 | ////////////////////////////////////////////////////////////////////// 467 | 468 | // Delete second user to test if DELETE is idempotent (already deleted) 469 | response = client.target("http://localhost:8282/contacts/person/2") 470 | .request().delete(); 471 | assertEquals(404, response.getStatus()); 472 | 473 | // Confirm the user has been deleted (already deleted) 474 | response = client.target("http://localhost:8282/contacts/person/2") 475 | .request().delete(); 476 | assertEquals(404, response.getStatus()); 477 | 478 | /* 479 | At this point, if this test has passed, it means that this repeated DELETE 480 | request have not modified the server resources and have same outcome (404). 481 | Therefore, it's idempotent and not safe. 482 | */ 483 | } 484 | 485 | @Test 486 | public void findUsers() throws IOException { 487 | // Prepare server 488 | AddressBook ab = new AddressBook(); 489 | Person salvador = new Person(); 490 | salvador.setName("Salvador"); 491 | salvador.setId(1); 492 | Person juan = new Person(); 493 | juan.setName("Juan"); 494 | juan.setId(2); 495 | ab.getPersonList().add(salvador); 496 | ab.getPersonList().add(juan); 497 | launchServer(ab); 498 | 499 | // Test user 1 exists 500 | Client client = ClientBuilder.newClient(); 501 | Response response = client 502 | .target("http://localhost:8282/contacts/person/1") 503 | .request(MediaType.APPLICATION_JSON).get(); 504 | assertEquals(200, response.getStatus()); 505 | assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType()); 506 | Person person = response.readEntity(Person.class); 507 | assertEquals(person.getName(), salvador.getName()); 508 | assertEquals(person.getId(), salvador.getId()); 509 | assertEquals(person.getHref(), salvador.getHref()); 510 | 511 | // Test user 2 exists 512 | response = client.target("http://localhost:8282/contacts/person/2") 513 | .request(MediaType.APPLICATION_JSON).get(); 514 | assertEquals(200, response.getStatus()); 515 | assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getMediaType()); 516 | person = response.readEntity(Person.class); 517 | assertEquals(person.getName(), juan.getName()); 518 | assertEquals(2, juan.getId()); 519 | assertEquals(person.getHref(), juan.getHref()); 520 | 521 | // Test user 3 exists 522 | response = client.target("http://localhost:8282/contacts/person/3") 523 | .request(MediaType.APPLICATION_JSON).get(); 524 | assertEquals(404, response.getStatus()); 525 | } 526 | 527 | private void launchServer(AddressBook ab) throws IOException { 528 | URI uri = UriBuilder.fromUri("http://localhost/").port(8282).build(); 529 | server = GrizzlyHttpServerFactory.createHttpServer(uri, 530 | new ApplicationConfig(ab)); 531 | server.start(); 532 | } 533 | 534 | @After 535 | public void shutdown() { 536 | if (server != null) { 537 | server.shutdownNow(); 538 | } 539 | server = null; 540 | } 541 | 542 | } 543 | --------------------------------------------------------------------------------