InventoryJaxRSClientImpl.java

package ejava.examples.ejbwar.inventory.client;

import java.net.URI;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.Invocation.Builder;
import javax.ws.rs.client.ResponseProcessingException;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status.Family;
import javax.ws.rs.core.UriBuilder;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ejava.examples.ejbwar.inventory.bo.Categories;
import ejava.examples.ejbwar.inventory.bo.Category;
import ejava.examples.ejbwar.inventory.bo.Product;
import ejava.examples.ejbwar.inventory.bo.Products;
import ejava.examples.ejbwar.jaxrs.JAXBUtils;
import ejava.examples.ejbwar.jaxrs.JSONUtils;

/**
 * This class implements a JAX-RS Client interface to the inventory 
 * web application. All commands are through HTTP POST, GET, PUT, and DELETE
 * methods to specific resource URIs for products and categories. However,
 * this uses an API interface that hides some of the HTTP and marshaling details.
 */
public class InventoryJaxRSClientImpl implements InventoryClient {
	private static final Logger logger = LoggerFactory.getLogger(InventoryJaxRSClientImpl.class);
    public static final String CATEGORIES_PATH = "categories";
    public static final String CATEGORY_PATH = "categories/{categoryId}";
    private static final String PRODUCTS_PATH = "products";
    private static final String PRODUCT_PATH = "products/{productId}";
	private Client client;
	/**
	 * Defines the HTTP URL for the WAR that hosts the JAX-RS resources.
	 */
	private URI baseUrl;
	
	/**
	 * Defines the protocol between the client and server.
	 */
	private MediaType mediaType = MediaType.APPLICATION_XML_TYPE;
	
	public void setClient(Client client) {
        this.client = client;
    }

	public void setBaseUrl(URI baseUrl) {
		this.baseUrl = baseUrl;
	}
	
    public void setMediaType(String mediaType) {
        this.mediaType = MediaType.valueOf(mediaType);
    }

    private boolean isSuccessful(Response response) {
	    return response.getStatusInfo().getFamily() == Family.SUCCESSFUL;
	}

    /**
     * Helper class to build the base URI for a client call.
     * @param resourcePath
     * @return uri builder ready to accept path parameter values
     */
    private UriBuilder getBaseUri(String...resourcePath) {
        UriBuilder b = UriBuilder.fromUri(baseUrl).path("api");
        if (resourcePath!=null) {
            for (String p: resourcePath) {
                b = b.path(p);
            }
        }
        return b;
    }
		
	@Override
	public Categories findCategoryByName(String name, int offset, int limit)  {
		//build a URI to the specific method that is hosted within the app
		URI uri = getBaseUri(CATEGORIES_PATH).build();
        //build the overall request 
		WebTarget target = client.target(uri)
                //marshall @QueryParams into URI
                .queryParam("name", name)
                .queryParam("offset", offset)
                .queryParam("limit", limit);
		Builder request = target.request(mediaType);
		Invocation get = request.buildGet();

        //issue request and look for an OK response with entity
		try (Response response = get.invoke(Response.class)) {
	        logger.debug("GET {}, {} returned {}", uri, mediaType, response.getStatusInfo());
	        if (response.getStatusInfo().getFamily() == Family.SUCCESSFUL) {
	            return response.readEntity(Categories.class);
		    } else {
		        String payload = (response.hasEntity()) ? response.readEntity(String.class) 
		                : response.getStatusInfo().toString();
	            throw new ResponseProcessingException(response, payload);		        
		    }
		}	
	}
	
	@Override
	public Category getCategory(int id) throws ResponseProcessingException {
	        //marshal @PathParm into the URI
		URI uri = getBaseUri(CATEGORY_PATH).build(id);
		
		//build the overall request
		Builder request = client.target(uri)
		                        .request(mediaType);
		
		//issue request and look for an OK response with entity
		try (Response response = request.get()) {
            logger.debug("GET {}, {} returned {}", uri, mediaType, response.getStatusInfo());
        		if (isSuccessful(response)) {
        			return response.readEntity(Category.class);
        		} else {
                String payload = (response.hasEntity()) ? response.readEntity(String.class) 
                        : response.getStatusInfo().toString();
                throw new ResponseProcessingException(response, payload);               
        		}
		}
	}
	
	@Override
	public boolean deleteCategory(int id) {
        //marshal @PathParm into the URI
	    URI uri = getBaseUri(CATEGORY_PATH).build(id);
		
		//build and execute the overall request
		 Builder request = client.target(uri)
		                        .request();

		//issue request and look for an OK response without an entity
		try (Response response=request.delete()) {
		    logger.debug("DELETE {} returned {}", uri, response.getStatusInfo());
            if (isSuccessful(response)) {
    			    return true;
            } else {
                String payload = (response.hasEntity()) ? response.readEntity(String.class) 
                        : response.getStatusInfo().toString();
                throw new ResponseProcessingException(response, payload);               
            }
		}
	}
	
	/**
	 * This method uses HTML FORM mechanism to POST a new product in the
	 * inventory. 
	 */
	@Override
	public Product createProduct(Product product, String categoryName) {
	        //no @PathParams here
		URI uri = getBaseUri(PRODUCTS_PATH).build();

		//build the form data with the request parameters
		Form form = new Form();
		form.param("name", product.getName());
		form.param("category", categoryName);
		if (product.getQuantity()!=null) {
			form.param("quantity", product.getQuantity().toString());
		}
		if (product.getPrice() != null) {
			form.param("price", product.getPrice().toString());
		}

		//create the request
		Invocation request = client.target(uri)
		        .request(mediaType)
		        .buildPost(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
			
		//issue the request and check the response
		try (Response response=request.invoke()) {
            logger.debug("POST {} returned {}", uri, response.getStatusInfo());
            if (isSuccessful(response)) {
    			    return response.readEntity(Product.class, Product.class.getAnnotations());
            } else {
                String payload = (response.hasEntity()) ? response.readEntity(String.class)
                        : response.getStatusInfo().toString();
                throw new ResponseProcessingException(response, payload);               
            }
		}
	}
	
	@Override
	public Products findProductsByName(String name, int offset, int limit) {
		URI uri = getBaseUri(PRODUCTS_PATH).build();
			
		//build the overall request
		WebTarget target = client.target(uri)
                //marshall @QueryParams into URI
                .queryParam("name", name)
                .queryParam("offset", offset)
                .queryParam("limit", limit);
		Invocation request = target 
		        .request(mediaType)
		        .buildGet();
		
		//issue request and look for OK response with entity
		try (Response response=request.invoke()) {
            logger.debug("GET {} returned {}", uri, response.getStatusInfo());
            if (isSuccessful(response)) {
    			    return response.readEntity(Products.class, Products.class.getAnnotations());
            } else {
                String payload = (response.hasEntity()) ? response.readEntity(String.class)
                        : response.getStatusInfo().toString();
                throw new ResponseProcessingException(response, payload);               
            }
		}
	}
	
	@Override
	public Product getProduct(int id) {
		URI uri = getBaseUri(PRODUCT_PATH)
				//marshal @PathParm into the URI
				.build(id);
			
		//build and execute overall request
		Invocation request = client.target(uri)
		        .request(mediaType)
		        .buildGet();
		
		//issue request look for OK response with entity
		try (Response response=request.invoke()) {
            logger.debug("GET {}, {} returned {}", uri, mediaType, response.getStatusInfo());
            if (isSuccessful(response)) {
    			    return response.readEntity(Product.class);
            } else {
                String payload = (response.hasEntity()) ? response.readEntity(String.class)
                        : response.getStatusInfo().toString();
                throw new ResponseProcessingException(response, payload);               
            }
		}
	}
	
	@Override
	public Product updateProduct(Product product) {
		URI uri = getBaseUri(PRODUCT_PATH)
				//marshal @PathParm into the URI
				.build(product.getId());
			
        //build overall request
		Invocation request = client.target(uri)
		        .request(mediaType)
		        .buildPut(Entity.entity(product, mediaType, Product.class.getAnnotations()));
		
		//issue request and look for OK with entity
		try (Response response=request.invoke()) {
            logger.debug("PUT {}, {} returned {}", uri, mediaType, response.getStatusInfo());
            String requestPayload = MediaType.APPLICATION_JSON_TYPE.equals(mediaType) ? 
                    JSONUtils.marshal(product) : 
                    JAXBUtils.marshal(product);  
            logger.debug("sent=\n{}", requestPayload);
            if (isSuccessful(response)) {
                String payload = response.readEntity(String.class);
                logger.debug("rcvd=\n{}", payload);
                return MediaType.APPLICATION_JSON_TYPE.equals(mediaType) ? 
                        JSONUtils.unmarshal(payload, Product.class) : 
                        JAXBUtils.unmarshal(payload, Product.class);
    			    //return response.readEntity(Product.class);
            } else {
                String payload = (response.hasEntity()) ? response.readEntity(String.class)
                        : response.getStatusInfo().toString();
                throw new ResponseProcessingException(response, payload);               
            }
		}
	}

	@Override
	public boolean deleteProduct(int id) {
        URI uri = getBaseUri(PRODUCT_PATH)
				//marshal @PathParm into the URI
				.build(id);
			
		//build and execute overall request
		Invocation request = client.target(uri)
		        .request()
		        .buildDelete();

		//issue request look for OK response without and entity
		try (Response response=request.invoke()) {
            logger.debug("DELETE {} returned {}", uri, response.getStatusInfo());
            if (isSuccessful(response)) {
    			    return true;
            } else {
                String payload = (response.hasEntity()) ? response.readEntity(String.class)
                        : response.getStatusInfo().toString();
                throw new ResponseProcessingException(response, payload);               
            }
		}
	}
}