Search This Blog

Showing posts with label JSR 311. Show all posts
Showing posts with label JSR 311. Show all posts

Friday, August 8, 2008

JAXRS, JBoss RestEasy, Maven, Spring...rock on!

I'm drowning my audience with Rest related code. What can I do, the hunger is biting me bad! JSR 311/JAXRS is an API. Different providers provide their own implementations of the same. I have already explored RESTLET's version of the implementation and how it applies to my SpringRestlet example. Note the common theme running here..I need a framework to be able to provide a Spring Hook otherwise, I turn my head the other way :-). Yeah, a Spring fan (those who remember the movie "Swim fan" feel my obsession)!

That said, we move on to the example. RestEasy is a JBoss project that allows one to build RESTful webservices. I like their philosophy where they say, the goal is to build an easy way to speak REST.

As of this blog, Rest Easy JAXRS is at : 1.0 BETA 5. Some of the things I liked about the implementation right away:

1. Support for a Client API to talk to Restful Webservice
2. Easy Spring Integration
3. Maven Support
4. Decent Documentation
5. Easy to get going with a Starter Webservice
6. JBoss Backing..we have Hibernate after all ;-)
7. Last but not the least led by a member of the JSR

In my previous blog with the RESTLET framework support for JAXRS and Spring I had developed my own custom Servlet. With Rest Easy, I did not have to do the same. My webapp's web.xml has exactly the same configuration as mentioned in the RestEasy documentation. One major issue that I encountered is that I could not get auto discovery of Spring managed Resources and Providers (as advertised by the framework) without annotating the corresponding classes with the Spring @Component annotation. Maybe there is something I am missing. Regardless, I have filed a bug report with the RestEasy jira, lets see what surfaces from the same.

With the Resteasy version of my Spring/Restlet example , the webapp module has only 3 classes! An OrderNotFoundProvider class that translates all OrderNotFoundExceptions to meaningful REST responses, an OrderResource and a ProductsResource. I do not have any other JAVA artifacts in this module. A provider class for the Products that managed the marshalling specifics (
JSON marshalling/unmarshalling) has been moved to the common maven module to be shared among client and web modules. Regardless, look at my webapp module now, all we have is Resource and Exception management classes :-). Gone are the Application, Servlet, ApplicationConfig ...etc etc classes.

Unlike in the previous example of JAXRS that I had provided, in this example, I have changed the Client to use RestEasy's JAXRS Client support. I must admit, I am rather impressed by their effort with the same. So what are the changed in the client, the OrderClient has changed to:
@ConsumeMime("application/xml") 
public interface OrderClient {

/**
* Create an Order.
*
* @param orderDTO Order DTO
* @return OrderDTO with created id
* @throws IOException If an error occurs
*/

@POST
@Path("/order")
@ProduceMime("application/xml")
@ConsumeMime("application/xml")
public OrderDTO createOrder(OrderDTO orderDTO) throws IOException;

/**
* Updates an Order.
*
* @param orderDTO Order DTO
*/

@PUT
@Path("/order/{id}")
@ProduceMime("application/xml")
public void updateOrder(OrderDTO orderDTO, @PathParam ("id") Long id);

/**
* Retrieves an Order with the specified <code>orderId</code>.
*
* @param orderId Order Id
* @return OrderDTO
* @throws OrderNotFoundException if order is not found
* @throws IOException if an error occurs
*/

@GET
@Path("/order/{id}")
@ProduceMime("application/xml")
public OrderDTO getOrder(@PathParam("id") Long orderId) throws OrderNotFoundEx
ception, IOException;

/**
* Deletes an Order with the specified <code>orderId</code>
*
* @param orderId order Id
* @throws OrderException If an error occurs
*/

@DELETE
@Path("/order/{id}")
@ProduceMime("application/xml")
public void deleteOrder(@PathParam("id") Long orderId) throws OrderException;
}


Note the use of JAXRS annotations in the Order Client. Code is being shared, always a good thing :-). It is sufficient with the OrderClient defintion to talk to the webservice using Reseasy code. The same can be accomplished with the following lines from a consumer:

ResteasyProviderFactory.initializeInstance(); 
RegisterBuiltin.register(ResteasyProviderFactory.getInstance());

OrderClient client = ProxyFactory.create(OrderClient.class, "http://localhost:9090/IntegrationTest");

The first two lines initialize the Resteasy client code. The third line is where a Proxy is created for the OrderClient. In the spirit of my rest/spring example, I have decided to provide an abstraction where I delegate to the proxy mentioned above as shown below ;-) :
public class OrderClientImpl implements OrderClient { 
private final OrderClient delegate;

/**
* @param uri Server Uri
*/

public OrderClientImpl(String uri) {
delegate = ProxyFactory.create(OrderClient.class, uri);
}

public OrderDTO createOrder(OrderDTO orderDTO) throws IOException {
return delegate.createOrder(orderDTO);
}

public void deleteOrder(Long orderId) throws OrderException {
delegate.deleteOrder(orderId);
}

public OrderDTO getOrder(Long orderId) throws OrderNotFoundException, IOExcept
ion {
return delegate.getOrder(orderId);
}

public void updateOrder(OrderDTO orderDTO, Long id) {
delegate.updateOrder(orderDTO, id);
}

}

I choose to depend on my Integration test and/or consumer of the service to initialize the Resteasy framework via plugins/listener what have you.

Above said, the code just works nice. I feel with the Resteasy JAXRS implementation, I have reduced the amount of coding required to build REST web service that integrates with my favorite framework Spring. Onward and upward you Resteasy folks!

The Resteasy version of the JAXRS Spring/RestEasy/Maven/Dozer project can be downloaded from HERE!
The example was run using JDK 1.6.X and Apache maven 2.0.9 on Linux environment.
Enjoy! I am off to look at CXF from the apache foundation next..:-) ...

Monday, August 4, 2008

Restlet, JAXRS, JSR 311, Maven, Spring and more.

I have been wanting to alter my Restlet example that utilizes Spring, Restlet and Maven to use JAXRS or JSR 311 API. The Restlet project supports JAXRS.

For the sake of simplicity, I did not change the client code in anyway, i.e., preferring to use the Restlet API for invocations. In addition JAXRS does not provide for any Client API specifications ;-)

I changed the OrderResource as follows:
@Component @Path("/order")

public class OrderResource {
private static final Logger log = Logger.getLogger(OrderResource.class);

@Autowired private OrderService orderService;
@Autowired private MapperIF beanMapper;

public OrderResource() {
super();
}

private OrderDTO persistOrder(OrderDTO orderDTO) {
if (log.isDebugEnabled()) {
log.debug("Persisting order:" + orderDTO);
}

Order order = (Order) beanMapper.map(orderDTO, Order.class);

if (log.isDebugEnabled()) {
log.debug("Mapped Order" + order);
}

orderService.persist(order);

if (log.isDebugEnabled()) {
log.debug("Mapping persisted order to OrderDTO:" + order);
}
orderDTO = (OrderDTO) beanMapper.map(order, OrderDTO.class);

if (log.isDebugEnabled()) {
log.debug("Returning mapped order:" + orderDTO);
}

return orderDTO;
}

@Path("/{id}") @ConsumeMime("application/xml") @PUT public void updateOrder(
@PathParam("id") String id, OrderDTO orderDTO) {
if (log.isDebugEnabled()) {
log.debug("Enter Update Order, Id=" + id + ", Order DTO:" + orderDTO);
}
Long idLong = new Long(id);
orderDTO.setOrderId(idLong);
orderDTO = persistOrder(orderDTO);

if (log.isDebugEnabled()) {
log.debug("Order Persisted:" + orderDTO);
}
}

@ProduceMime("application/xml") @ConsumeMime("application/xml") @POST public OrderDTO storeOrder(
OrderDTO orderDTO) {
orderDTO = persistOrder(orderDTO);

return orderDTO;
}

@GET @Path("/{id}") @ProduceMime("application/xml") public OrderDTO getOrder(
@PathParam("id") String id) throws OrderNotFoundException {
Long orderId = new Long(id);

Order order = null;

try {
order = orderService.getOrder(orderId);
}
catch (OrderNotFoundException nfe) {
log.error("Order Not Found", nfe);
throw nfe;
}

log.info("Order found..");

OrderDTO orderDTO = (OrderDTO) beanMapper.map(order, OrderDTO.class);

return orderDTO;
}

@DELETE @Path("/{id}") public void deleteOrder(@PathParam("id") String id) {
Long orderId = new Long(id);
orderService.delete(orderId);
}

public void validate() {
Assert.notNull(orderService);
}
}


Notable Changes to the OrderResource:


  1. The @Path annotation on the OrderResource class tells the container that the OrderResource will handle calls of the context /order.
  2. The @Path annotation on some of the methods of the OrderResource denote specifics of the path.
  3. @ConsumeMime and @ProduceMime annotations indicate the mime types that will be consumed or produced by the method respectively.
  4. @POST, @GET, @PUT, @DELETE denote the different HTTP methods and a method annotated with one of these annotations will handle the request of the specific type.
  5. @PathParam denotes a parameter that will be available for the method.

In the above example, we have eliminated code that extends a Restlet Resource class. We have used annotations to specify what HTTP methods the Resource supports. We have also eliminated the Representation concept from the methods in favor of @ProduceMine and @ConsumeMine which help define what mime types can be produced and consumed by the method respectively. JAXRS introduces the concept of Providers that help in marshalling/unmarshalling different mime types. Providers are annotated with the @Provider annotation. In addition, in the case of Exceptions, Exception Providers also can be developed that determine the response to be provided to a client.

If a method is annotated with @ConsumeMime or @ProduceMime of type "application/xml" and the object part of the method argument or return type is a JAXB object, i.e., an object that has the annotation @XmlRootElement, automatic JAXB marshalling is accomplished. The OrderDTO is one such object.


From the example, we also had a ProductResource. The ProductResource from the earlier example only supported the MIME type of "application/jspon". The updated ProductResource is shown below:

@Component @Path("/products") public class ProductsResource {

private static final Logger log = Logger.getLogger(ProductsResource.class);

@Autowired private ProductService productService;
@Autowired private MapperIF beanMapper;

private Set map(Set products) {
Set productDTOs = new HashSet();

for (Product product : products) {
ProductDTO productDTO = (ProductDTO) beanMapper.map(product, ProductDTO.class);
productDTOs.add(productDTO);
}

return productDTOs;
}

/**
* Gets a {@link ProductListDTO} of Products that are supported.
*
* @return a List of Products
*/
@GET @ProduceMime( { "application/json" }) public ProductListDTO getProducts() {
log.debug("Enter getProducts()");

Set products = productService.getProducts();
Set productDTOs = map(products);

if (log.isDebugEnabled()) {
log.debug("Returning Products:" + productDTOs);
}

return new ProductListDTO(productDTOs);
}
}


Unlike in JAXB where the annotations determine the marshalling sematics, for the JSON marshalling, I had to do some customizationwhere we specifically detail how the marshalling should occur.

I have been discussing the operation with Jerome on the Restlet Discussion forum and maybe it will become easier to just specify the mime type and not have to worry about the conversion.

Until then, we can accomplish the conversion to JSON using a custom Provider as shown below:
@ProduceMime("application/json")

@Provider
public class ProductProvider implements
MessageBodyWriter {
private static final Logger log = Logger.getLogger(ProductProvider.class);

public long getSize(ProductListDTO t) {
return -1;
}

public boolean isWriteable(Class type, Type genericType, Annotation[] annotations) {
return true;
}

public void writeTo(ProductListDTO t, Class type, Type genericType, Annotation[] annotations,
MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException,
WebApplicationException {

log.debug("Write To of ProductProvider invoked...");

JSONArray jsonArray = new JSONArray();

for (ProductDTO product : t.getProducts()) {
jsonArray.put(product.getProductId()).put(product.getName()).put(product.getDescription());
}

OutputStreamWriter writer = new OutputStreamWriter(entityStream);

try {
writer.write(jsonArray.toString());
writer.flush();
}
catch (IOException e) {
log.error("Error Writing JSON Array:", e);
throw e;
}

log.debug("Exit Write To of ProductProvider");
}
}



When an Order is not found, an OrderNotFoundException is thrown. The same is translated to a Response of HTTP code 404 to the consumer via the following Provider:





@Provider public class OrderNotFoundProvider implements ExceptionMapper {

public Response toResponse(OrderNotFoundException exception) {
return Response.status(Response.Status.NOT_FOUND).build();
}
}




So how do all there components tie together. I have largely based the glue code on a very nice example from the Restlet WIKI about JAXRS Support. I have an OrderConfig class as shown below that indicates the supported MediaType mappings, the Resource Classes and the Custom Provider classes:





public class OrderConfig extends ApplicationConfig {

public Set> getResourceClasses() {
Set> rrcs = new HashSet>();

rrcs.add(OrderResource.class);
rrcs.add(ProductsResource.class);

return rrcs;
}

@Override public Map getMediaTypeMappings() {
Map map = new HashMap();

map.put("html", MediaType.TEXT_HTML_TYPE);
map.put("xml", MediaType.APPLICATION_XML_TYPE);
map.put("json", MediaType.APPLICATION_JSON_TYPE);

return map;
}

public Set>getProviderClasses() {
Set> rrcs = new HashSet>();
rrcs.add(ProductProvider.class);
rrcs.add(OrderNotFoundProvider.class);

return rrcs;
}
}


The Restlet OrderApplication class from my earlier example has now transformed to an instance of JaxRsApplication to which it attaches the above mentioned OrderConfig class:

public class OrderApplication extends JaxRsApplication {

/**
* Class Constructor. Attaches the {@link OrderConfig} class.
*
* @param context Restlet Context
*/
public OrderApplication(Context context) {
super(context);
attach(new OrderConfig());
}
}


One issue we need to address is how will resource classes, Mapper Beans, Services etc get Autowired and injected, i.e., where is the Spring Hook? The JaxRsApplication class supports the concept of Custom Resource creation factories. This hook is utilized by creating a Custom Spring ObjectFactory that instantiates and provides Spring Managed bean. Setting the hook into the JaxRsApplication is achieved using a Custom Restlet ServerServlet as shown below:


public class SpringServlet extends ServerServlet {


public Application createApplication(Context context) {
JaxRsApplication application = (JaxRsApplication) super.createApplication(context);

// Set the Object Factory to Spring Object Factory
application.setObjectFactory(new SpringObjectFactory(getWebApplicationContext()
.getAutowireCapableBeanFactory()));

return application;
}

private static class SpringObjectFactory implements ObjectFactory {
private final AutowireCapableBeanFactory beanFactory;

public SpringObjectFactory(AutowireCapableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}

public T getInstance(Class jaxRsClass) throws InstantiateException {

@SuppressWarnings("unchecked")
T object = (T) beanFactory.createBean(jaxRsClass,
AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT, false);

return object;
}
}

public WebApplicationContext getWebApplicationContext() {
return WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
}
}

I could not use the Restlet's SpringServerServlet for the implementation as it expects a RestletResource and Router etc.

Thoughts on JSR 311 and JAXRS and forward:

I quite like the JSR 311 method of creating the Web Service. I found the clean separation of Providers and Resources via annotations really helpful. The use of annotation makes reading the Resource code very simple and the Resource code itself is not working with Representation's like before. I would like to introduce WADL and WADL2JAVA into the example at some point. I also would like to see better JSON support. In addition, I am curious as how other implementations of JAXRS work and in particular provide for easy integration with my favorite framework Spring. One thing is the lack of a Client API from the specification that I regret.

Enviorment on which example was run:

OS - Windows Vista, JDK-1.6.X, Maven 2.0.8.

The JAXRS, Spring, Maven, Dozer example can be downloaded from HERE.

If you are unable to run the example, as always Ping me and I will be glad to help if I can :-)