In the earlier customer service example you might have noticed that the service is annotated with @Path("/customers"). This annotation plays a key role in routing incoming request urls to appropriate methods. We will build the ProductService and through that we will learn more about @Path.
ProductService.java
Just like we defined the methods to add,update, delete, fetch customers we have added methods for products now. Notice the new value for @produces, @consumes annotation. The value we gave is application/json which means we product service communicates via JSON.
Root Resources
For a moment suppose that the path annotation for ProductService is just @Path("/") then the ProductService is called Root Resource as the string immediately after the root of the web application will be matched with this service. Lets say your webapp is MyWebApp and the url is http://localhost:8090/MyWebApp/xyz then part of the url appearing after http://localhost:8090/MyWebApp is matched with the resource path.
@Path annotations on methods
Methods in service can also be annotated with @Path. Take a look at getProduct() method in ProductService. However it is not mandatory for a method to have @Path annotation to be invoked. Please note that If the method has to be invoked it should have one of the http method annotations @GET,@POST,@PUT,@DELETE.
Once the method has @Path only if the url matches the resource path concatenated with method path the method would be invoked. Lets say we have new method under ProductService called getDiscontinuedProducts with @Path("discontinued").
To call the above method the url should be like
GET http://localhost:8090/MyWebApp/products/discontinued
@Path expressions
We can have templates in the @Path expressions. These templates will be populated based on the incoming url. Take for example the below customer service
If the url is http://localhost:8090/MyWebApp/customers/99 the value for id will be 99.
We can have multiple templates in the @path. Lets say if we have a method like below then the url to match this method would be GET/customers/santosh-srinivas
Similarly you can have regular expressions in @Path. The below method will be matched only for GET /customers/1 and not for GET /customers/xyz
ProductService.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | package com.example.service; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import com.example.Product; /** * @author Santosh * */ @Path("/products") public class ProductService { private Map<Integer, Product> productDb = new ConcurrentHashMap<Integer, Product>(); private AtomicInteger idCounter = new AtomicInteger(); public ProductService() { Product p1 = new Product(); p1.setId(idCounter.getAndIncrement()); p1.setName("Pen"); p1.setCost(10.0); Product p2 = new Product(); p2.setId(idCounter.getAndIncrement()); p2.setName("Book"); p2.setCost(20.0); productDb.put(p1.getId(), p1); productDb.put(p2.getId(), p2); } @GET @Produces("application/json") public List<Product> getAllProducts() throws WebApplicationException { List<Product> products = new ArrayList<>(); for (Product product : productDb.values()) { products.add(product); } return products; } @POST @Produces("application/json") @Consumes("application/json") public Product addProduct(Product product) throws WebApplicationException { if (product == null) { throw new WebApplicationException(Response.Status.BAD_REQUEST); } else { int id = idCounter.getAndIncrement(); product.setId(id); product.setName(product.getName()); product.setCost(product.getCost()); productDb.put(id, product); return productDb.get(id); } } @PUT @Produces("application/json") @Consumes("application/json") public Product updateProduct(Product product) throws WebApplicationException { if (productDb.get(product.getId()) == null) { throw new WebApplicationException(Response.Status.NOT_FOUND); } else { productDb.put(product.getId(), product); return productDb.get(product.getId()); } } @DELETE @Produces("application/json") @Consumes("application/json") public Product cancelProduct(Product product) throws WebApplicationException { if (productDb.get(product.getId()) == null) { throw new WebApplicationException(Response.Status.NOT_FOUND); } else { productDb.remove(product.getId()); return productDb.get(product.getId()); } } @GET @Path("/{id}") public Product getProducts(@PathParam("id") int id) throws WebApplicationException { if (productDb.get(id) == null) { // no such customer exists throw new WebApplicationException(Response.Status.NOT_FOUND); } else { return productDb.get(id); } } } |
Just like we defined the methods to add,update, delete, fetch customers we have added methods for products now. Notice the new value for @produces, @consumes annotation. The value we gave is application/json which means we product service communicates via JSON.
Root Resources
For a moment suppose that the path annotation for ProductService is just @Path("/") then the ProductService is called Root Resource as the string immediately after the root of the web application will be matched with this service. Lets say your webapp is MyWebApp and the url is http://localhost:8090/MyWebApp/xyz then part of the url appearing after http://localhost:8090/MyWebApp is matched with the resource path.
@Path annotations on methods
Methods in service can also be annotated with @Path. Take a look at getProduct() method in ProductService. However it is not mandatory for a method to have @Path annotation to be invoked. Please note that If the method has to be invoked it should have one of the http method annotations @GET,@POST,@PUT,@DELETE.
Once the method has @Path only if the url matches the resource path concatenated with method path the method would be invoked. Lets say we have new method under ProductService called getDiscontinuedProducts with @Path("discontinued").
1 2 3 4 5 | @GET @Produces("application/json") @Path("discontinued") public Product getDiscountinuedProducts() throws WebApplicationException {...} |
To call the above method the url should be like
GET http://localhost:8090/MyWebApp/products/discontinued
@Path expressions
We can have templates in the @Path expressions. These templates will be populated based on the incoming url. Take for example the below customer service
1 2 3 4 5 6 7 8 | @Path("/customers") public class CustomerResource { @GET @Path("{id}") public String getCustomer(@PathParam("id") int id) { ... } |
If the url is http://localhost:8090/MyWebApp/customers/99 the value for id will be 99.
We can have multiple templates in the @path. Lets say if we have a method like below then the url to match this method would be GET/customers/santosh-srinivas
1 2 3 4 5 6 7 8 9 | @Path("/") public class CustomerResource { @GET @Path("customers/{firstname}-{lastname}") public String getCustomer(@PathParam("firstname") String first, @PathParam("lastname") String last) { ... } } |
1 2 3 4 5 6 7 8 | @Path("/customers") public class CustomerResource { @GET @Path("{id : \\d+}") public String getCustomer(@PathParam("id") int id) { ... } } |
Testing the ProductService
We will test the product service we developed almost the same way we tested CustomerService.
Fetching the products
Observe the Accept header value passed as application/json. This is a way to tell the server that the client is expecting json response. The response for this call would be
The body of the response is listing the products in the system in JSON format. Notice the difference between JSON and xml. JSON is more concise.
Adding a product
Let us say we want to add a product Optical Mouse priced at 150 Rs to the system. The http operation would be post and the body should be JSON as the service is annotated with @consumes("application/json"). Sending xml data wont succeed.
{"name":"Optical Mouse","cost":150.0}
The response for this call would be creation of a new product and the product is returned with id field set.
The response is the new product in JSON format.
We are not mentioning the Update and Delete cases as they are exactly similar to POST except that the data passed would have the correct product id populated. You can try that as an exercise
No comments:
Post a Comment