For the past few months, I have been mucking around with an idea for a RESTful Web Services framework I call RESTEasy. The project was inspired by the JAX-WS API and some of the work done by the WADL project. Yesterday, I noticed this post on Marc Hadley’s Blog regarding JSR-311. I thought I’d share what I’ve been up to, since some of my ideas in RESTEasy are quite similar.
My driver for RESTEasy was to make it very transparent to expose a SessionBean or a POJO as a RESTful service and using JAXB annoated Entity Beans as value objects. I had looked into the RESTful capabilities of JAX-WS, but it is not quite a complete solution yet. I wanted a RESTful framework to do the following:
- Seamless integration with EJB3 SessionBeans, Message Driven Beans, POJOs, JPA/Hibernate, and JAXB
- Annotation based
- Automatically discover class marked with an
@WebReource
annotation - Route HTTP method to the proper Java method for the specified URI
A simple RESTful SessionBean service in RESTEasy look like this:
@Stateless @WebResource("/contacts/{contactId}") public class ContactServiceBean implements ContactService { @PersistenceContext private EntityManager entityManager; @HttpMethod("GET") public Contact getContactById(@URLParam("contactId") Long contactId) { Â Â Â Â Contact contact = entityManager.find(Contact.class, contactId); Â Â Â Â return contact; Â Â } @HttpMethod("POST") public Contact updateContact(@Representation("contact") Contact contact) { Â Â Â Â return entityManager.merge(contact); Â Â } }
By default, RESTEasy uses JAXB to convert XML documents to Java Objects and back. However, RESTEasy is not limited to just XML-based reources and can work with most media types through the RepresentationHandler
interface. In the example above, the Contact
class is managed by Hibernate and JAXB. When a GET request is issued for the following URL:
http://somehost/contacts/12345
RESTEasy will extract the contactId
from the URL and converted to a Long
and the getContactById()
methdod invoked. The return value is automagically marshalled to XML and returned to the client.
A POST operation to the same URL will invoke the updateContact()
method. In this case, the parameter marked with the @Representation
is unmarshalled to our Contact
class by JAXB and the updateContact()
method is invoked.
If you want to combine multiple URIs to a single class or EJB, you can use the @WebResources
annotation to group multipe @WebReource
annotations:
@Stateless @WebResources( Â Â resources={@WebResource(id=ContactService.CONTACT_BASE,value="/contacts"), Â Â Â Â Â Â Â Â Â Â Â Â Â @WebResource(id=ContactService.INDIVIDUAL,value="/contacts/{contactId}")} ) public class ContactServiceBean implements ContactService {
When using the the @WebResources
annotation, each @WebReource
must define an unique ID. This ID is then referenced by each @HttpMethod
annotation using the resourceId
attribute:
@HttpMethod(value=HttpMethod.POST, resourceId=ContactService.INDIVIDUAL)
Since some HTTP clients such as Apple’s Safari and Adobe Flash don’t support the DELETE and PUT http methods, you’ll be limited to using only GET and POST operations. If it’s important for you to be able to use PUT where the client supports put but also be able to handle a client that does not support PUT, you can do the following:
@HttpMethods(methods={ Â Â Â Â @HttpMethod(resourceId=ContactService.CONTACT_BASE,value=HttpMethod.PUT), Â Â Â Â @HttpMethod(resourceId=ContactService.CONTACT_BASE,value=HttpMethod.POST,discriminator="create") }) public void createContact(@Representation("contact") Contact contact);
The discriminator value is used to distinguish multiple POST operations for same URL. If you define the discriminator name to be “Action”, RESTEasy would write the create URL for a contact as:
http://somehost/contacts?Action=create
And a DELETE operation using POST could be:
http://somehost/contacts/12345?Action=delete
Right now, I have RESTEasy working very well with the RESTEasy client. I am also using to delevlop an Adobe FLEX based application which is working better than expected. There’s still a bit of work left to do on RESTEasy before the code get released into the wild. Here’s what I need to do at the moment:
- Decide on a place to host the project (java.net, Source Forge, or Google Code)
- Need to do some more thinking about how to handle some HTTP headers such as created, last modified, Content-Length, etc. As of now, these values aren’t supported
- JPA/Hibernate integration is a big part of RESTEasy. One of the challenges of using RESTEasy with a SessionBean is that the
ResourceInvoker
, the class that routes and HTTP method to a Java Method, resides on teh web tier. In order to avoidLazyInitializationException
, theEJBResourceInvoker
can start aUserTransaction
before the EJB is invoked. This ensures that everything goes as planned when marshalling the JAXB annotated Entity to XML. JBoss Seam appears handle this rather well for JSF applications. I’m looking to see if there is a way to utilize Seam’s remoting package to achcive what I am doing in the dispatch servlet. Or prehaps there is a better way to manage this all together. - Implement conditional gets to minimize load on the server.
- Finish the documentation
- Service stub generation from a WADL definition
There is also a RESTEasy client that I’ll cover in another post
Have you guys implemented support for including public key signature in http header for RestEasy, same way as WSSE inserts on SOAP header?
LikeLike
Have you guys implemented support for including public key signature in http header for RestEasy, same way as WSSE inserts on SOAP header?
LikeLike
Looks like the JBoss implementation of the JAX-RS specification adopted the RESTEasy moniker. If you really have seamless integration you do not have to specify what it seamlessly integrates with. JAX-RS binds URIs to classes and methods with plug-in providers to handle mime types. Once in the JVM world of classes, parameters and methods, you automatically have “seamless” integration with ANY Java API.
LikeLike
Rick, this post is at least a few years old. Since then, I worked with Bill Burke on rewriting RESTEasy and making it a JBoss project.
LikeLike
Looks like the JBoss implementation of the JAX-RS specification adopted the RESTEasy moniker. If you really have seamless integration you do not have to specify what it seamlessly integrates with. JAX-RS binds URIs to classes and methods with plug-in providers to handle mime types. Once in the JVM world of classes, parameters and methods, you automatically have “seamless” integration with ANY Java API.
LikeLike
Rick, this post is at least a few years old. Since then, I worked with Bill Burke on rewriting RESTEasy and making it a JBoss project.
LikeLike
Where can I get my hands on your code? :-). I’ve built my own REST-ful web service framework based on Stripes (www.stripesframework.org), but that was a workaround.. your stuff looks much cooler, except I’m not seeing any validation, which I think I can work around if the framework is flexible enough.
Are there callbacks during different lifecycle stages like pre- and post- binding, etc.?
I’d be more than happy to help out with the project, too, as I’ve worked heavily with frameworks of this ilk..
LikeLike
Where can I get my hands on your code? :-). I’ve built my own REST-ful web service framework based on Stripes (www.stripesframework.org), but that was a workaround.. your stuff looks much cooler, except I’m not seeing any validation, which I think I can work around if the framework is flexible enough.
Are there callbacks during different lifecycle stages like pre- and post- binding, etc.?
I’d be more than happy to help out with the project, too, as I’ve worked heavily with frameworks of this ilk..
LikeLike