View Javadoc
1   package ejava.examples.ejbwar.inventory.client;
2   
3   import java.net.URI;
4   
5   import javax.ws.rs.client.Client;
6   import javax.ws.rs.client.Entity;
7   import javax.ws.rs.client.Invocation;
8   import javax.ws.rs.client.Invocation.Builder;
9   import javax.ws.rs.client.ResponseProcessingException;
10  import javax.ws.rs.client.WebTarget;
11  import javax.ws.rs.core.Form;
12  import javax.ws.rs.core.MediaType;
13  import javax.ws.rs.core.Response;
14  import javax.ws.rs.core.Response.Status.Family;
15  import javax.ws.rs.core.UriBuilder;
16  
17  import org.slf4j.Logger;
18  import org.slf4j.LoggerFactory;
19  
20  import ejava.examples.ejbwar.inventory.bo.Categories;
21  import ejava.examples.ejbwar.inventory.bo.Category;
22  import ejava.examples.ejbwar.inventory.bo.Product;
23  import ejava.examples.ejbwar.inventory.bo.Products;
24  import ejava.examples.ejbwar.jaxrs.JAXBUtils;
25  import ejava.examples.ejbwar.jaxrs.JSONUtils;
26  
27  /**
28   * This class implements a JAX-RS Client interface to the inventory 
29   * web application. All commands are through HTTP POST, GET, PUT, and DELETE
30   * methods to specific resource URIs for products and categories. However,
31   * this uses an API interface that hides some of the HTTP and marshaling details.
32   */
33  public class InventoryJaxRSClientImpl implements InventoryClient {
34  	private static final Logger logger = LoggerFactory.getLogger(InventoryJaxRSClientImpl.class);
35      public static final String CATEGORIES_PATH = "categories";
36      public static final String CATEGORY_PATH = "categories/{categoryId}";
37      private static final String PRODUCTS_PATH = "products";
38      private static final String PRODUCT_PATH = "products/{productId}";
39  	private Client client;
40  	/**
41  	 * Defines the HTTP URL for the WAR that hosts the JAX-RS resources.
42  	 */
43  	private URI baseUrl;
44  	
45  	/**
46  	 * Defines the protocol between the client and server.
47  	 */
48  	private MediaType mediaType = MediaType.APPLICATION_XML_TYPE;
49  	
50  	public void setClient(Client client) {
51          this.client = client;
52      }
53  
54  	public void setBaseUrl(URI baseUrl) {
55  		this.baseUrl = baseUrl;
56  	}
57  	
58      public void setMediaType(String mediaType) {
59          this.mediaType = MediaType.valueOf(mediaType);
60      }
61  
62      private boolean isSuccessful(Response response) {
63  	    return response.getStatusInfo().getFamily() == Family.SUCCESSFUL;
64  	}
65  
66      /**
67       * Helper class to build the base URI for a client call.
68       * @param resourcePath
69       * @return uri builder ready to accept path parameter values
70       */
71      private UriBuilder getBaseUri(String...resourcePath) {
72          UriBuilder b = UriBuilder.fromUri(baseUrl).path("api");
73          if (resourcePath!=null) {
74              for (String p: resourcePath) {
75                  b = b.path(p);
76              }
77          }
78          return b;
79      }
80  		
81  	@Override
82  	public Categories findCategoryByName(String name, int offset, int limit)  {
83  		//build a URI to the specific method that is hosted within the app
84  		URI uri = getBaseUri(CATEGORIES_PATH).build();
85          //build the overall request 
86  		WebTarget target = client.target(uri)
87                  //marshall @QueryParams into URI
88                  .queryParam("name", name)
89                  .queryParam("offset", offset)
90                  .queryParam("limit", limit);
91  		Builder request = target.request(mediaType);
92  		Invocation get = request.buildGet();
93  
94          //issue request and look for an OK response with entity
95  		try (Response response = get.invoke(Response.class)) {
96  	        logger.debug("GET {}, {} returned {}", uri, mediaType, response.getStatusInfo());
97  	        if (response.getStatusInfo().getFamily() == Family.SUCCESSFUL) {
98  	            return response.readEntity(Categories.class);
99  		    } else {
100 		        String payload = (response.hasEntity()) ? response.readEntity(String.class) 
101 		                : response.getStatusInfo().toString();
102 	            throw new ResponseProcessingException(response, payload);		        
103 		    }
104 		}	
105 	}
106 	
107 	@Override
108 	public Category getCategory(int id) throws ResponseProcessingException {
109 	        //marshal @PathParm into the URI
110 		URI uri = getBaseUri(CATEGORY_PATH).build(id);
111 		
112 		//build the overall request
113 		Builder request = client.target(uri)
114 		                        .request(mediaType);
115 		
116 		//issue request and look for an OK response with entity
117 		try (Response response = request.get()) {
118             logger.debug("GET {}, {} returned {}", uri, mediaType, response.getStatusInfo());
119         		if (isSuccessful(response)) {
120         			return response.readEntity(Category.class);
121         		} else {
122                 String payload = (response.hasEntity()) ? response.readEntity(String.class) 
123                         : response.getStatusInfo().toString();
124                 throw new ResponseProcessingException(response, payload);               
125         		}
126 		}
127 	}
128 	
129 	@Override
130 	public boolean deleteCategory(int id) {
131         //marshal @PathParm into the URI
132 	    URI uri = getBaseUri(CATEGORY_PATH).build(id);
133 		
134 		//build and execute the overall request
135 		 Builder request = client.target(uri)
136 		                        .request();
137 
138 		//issue request and look for an OK response without an entity
139 		try (Response response=request.delete()) {
140 		    logger.debug("DELETE {} returned {}", uri, response.getStatusInfo());
141             if (isSuccessful(response)) {
142     			    return true;
143             } else {
144                 String payload = (response.hasEntity()) ? response.readEntity(String.class) 
145                         : response.getStatusInfo().toString();
146                 throw new ResponseProcessingException(response, payload);               
147             }
148 		}
149 	}
150 	
151 	/**
152 	 * This method uses HTML FORM mechanism to POST a new product in the
153 	 * inventory. 
154 	 */
155 	@Override
156 	public Product createProduct(Product product, String categoryName) {
157 	        //no @PathParams here
158 		URI uri = getBaseUri(PRODUCTS_PATH).build();
159 
160 		//build the form data with the request parameters
161 		Form form = new Form();
162 		form.param("name", product.getName());
163 		form.param("category", categoryName);
164 		if (product.getQuantity()!=null) {
165 			form.param("quantity", product.getQuantity().toString());
166 		}
167 		if (product.getPrice() != null) {
168 			form.param("price", product.getPrice().toString());
169 		}
170 
171 		//create the request
172 		Invocation request = client.target(uri)
173 		        .request(mediaType)
174 		        .buildPost(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
175 			
176 		//issue the request and check the response
177 		try (Response response=request.invoke()) {
178             logger.debug("POST {} returned {}", uri, response.getStatusInfo());
179             if (isSuccessful(response)) {
180     			    return response.readEntity(Product.class, Product.class.getAnnotations());
181             } else {
182                 String payload = (response.hasEntity()) ? response.readEntity(String.class)
183                         : response.getStatusInfo().toString();
184                 throw new ResponseProcessingException(response, payload);               
185             }
186 		}
187 	}
188 	
189 	@Override
190 	public Products findProductsByName(String name, int offset, int limit) {
191 		URI uri = getBaseUri(PRODUCTS_PATH).build();
192 			
193 		//build the overall request
194 		WebTarget target = client.target(uri)
195                 //marshall @QueryParams into URI
196                 .queryParam("name", name)
197                 .queryParam("offset", offset)
198                 .queryParam("limit", limit);
199 		Invocation request = target 
200 		        .request(mediaType)
201 		        .buildGet();
202 		
203 		//issue request and look for OK response with entity
204 		try (Response response=request.invoke()) {
205             logger.debug("GET {} returned {}", uri, response.getStatusInfo());
206             if (isSuccessful(response)) {
207     			    return response.readEntity(Products.class, Products.class.getAnnotations());
208             } else {
209                 String payload = (response.hasEntity()) ? response.readEntity(String.class)
210                         : response.getStatusInfo().toString();
211                 throw new ResponseProcessingException(response, payload);               
212             }
213 		}
214 	}
215 	
216 	@Override
217 	public Product getProduct(int id) {
218 		URI uri = getBaseUri(PRODUCT_PATH)
219 				//marshal @PathParm into the URI
220 				.build(id);
221 			
222 		//build and execute overall request
223 		Invocation request = client.target(uri)
224 		        .request(mediaType)
225 		        .buildGet();
226 		
227 		//issue request look for OK response with entity
228 		try (Response response=request.invoke()) {
229             logger.debug("GET {}, {} returned {}", uri, mediaType, response.getStatusInfo());
230             if (isSuccessful(response)) {
231     			    return response.readEntity(Product.class);
232             } else {
233                 String payload = (response.hasEntity()) ? response.readEntity(String.class)
234                         : response.getStatusInfo().toString();
235                 throw new ResponseProcessingException(response, payload);               
236             }
237 		}
238 	}
239 	
240 	@Override
241 	public Product updateProduct(Product product) {
242 		URI uri = getBaseUri(PRODUCT_PATH)
243 				//marshal @PathParm into the URI
244 				.build(product.getId());
245 			
246         //build overall request
247 		Invocation request = client.target(uri)
248 		        .request(mediaType)
249 		        .buildPut(Entity.entity(product, mediaType, Product.class.getAnnotations()));
250 		
251 		//issue request and look for OK with entity
252 		try (Response response=request.invoke()) {
253             logger.debug("PUT {}, {} returned {}", uri, mediaType, response.getStatusInfo());
254             String requestPayload = MediaType.APPLICATION_JSON_TYPE.equals(mediaType) ? 
255                     JSONUtils.marshal(product) : 
256                     JAXBUtils.marshal(product);  
257             logger.debug("sent=\n{}", requestPayload);
258             if (isSuccessful(response)) {
259                 String payload = response.readEntity(String.class);
260                 logger.debug("rcvd=\n{}", payload);
261                 return MediaType.APPLICATION_JSON_TYPE.equals(mediaType) ? 
262                         JSONUtils.unmarshal(payload, Product.class) : 
263                         JAXBUtils.unmarshal(payload, Product.class);
264     			    //return response.readEntity(Product.class);
265             } else {
266                 String payload = (response.hasEntity()) ? response.readEntity(String.class)
267                         : response.getStatusInfo().toString();
268                 throw new ResponseProcessingException(response, payload);               
269             }
270 		}
271 	}
272 
273 	@Override
274 	public boolean deleteProduct(int id) {
275         URI uri = getBaseUri(PRODUCT_PATH)
276 				//marshal @PathParm into the URI
277 				.build(id);
278 			
279 		//build and execute overall request
280 		Invocation request = client.target(uri)
281 		        .request()
282 		        .buildDelete();
283 
284 		//issue request look for OK response without and entity
285 		try (Response response=request.invoke()) {
286             logger.debug("DELETE {} returned {}", uri, response.getStatusInfo());
287             if (isSuccessful(response)) {
288     			    return true;
289             } else {
290                 String payload = (response.hasEntity()) ? response.readEntity(String.class)
291                         : response.getStatusInfo().toString();
292                 throw new ResponseProcessingException(response, payload);               
293             }
294 		}
295 	}
296 }