Develop Restful application with RESTeasy
Web service is complicated, to understand web service you must know SOAP, WSDL, XML schema etc. CORBA is even more completed than Web service. Our team have many years experience of developing web services by using various libraries like Axis, XFire and Cxf .We spent nearly month to understand and practice each type of SOAP message, parts of WSDL and tricks to keep the interoperability follows WS-I guidelines. However, we only need several days to learn REST and practice it in our project. Recently, we have a task of providing the API allows third-party application could get data from our application, though we have many experience in web service but we would like to investigate REST option. After several days for evaluating, REST is our choice. We are very pleased with the simplicity of REST - a lightweight web service, simple and powerful. In java world, RESTeasy (http://www.jboss.org/resteasy/) provides frameworks to help you build Restful Web Services. With RESTeasy, we could deploy Restful solution without impacting to existing code-base, ease to write unit test and ease to extend its functionalities, and the last RESTeasy documentation is comprehensive - you can learn to use RESTeasy in several hours only.
Our sample project is an existing application that uses Spring as IOC container, we would like to expose service method to third-party applications. We have the interface and its implementation already.
public interface FileSystemService { SimpleFile getFileByPath(String filePath); ... }
Enable Restful service
The request /filesystem/getFile/<path of file> will return the file information if it is existed. To make it happen, we add java REST annotation to our service:
@Path("/filesystem") public interface FileSystemService { @GET @Path("/getFile/{filePath:.*}") @Produces("application/xml") SimpleFile getFileByPath(@PathParam("filePath") String filePath); ... }
Because the path of file may contains the '/' character, '/' is also the special characters of Http request, thus in line 4 we must use regular expression * to tell RESTeasy that all requests starts with /getFille is mapped to method getFileByPath (for example: /getFile/content/A or /getFile/content/A/B/C is valid path). @Produces("application/xml") annotation tells RESTeasy that the output must be in XML form.
Content marshalling/unmarshalling
Class SimpleFile is the complex type, to force RESTeasy produces XML data to client you must instruct RESTeasy how to marshal SimpleFile class.
@XmlRootElement(namespace="http://www.esofthead.com/engroup") public class SimpleFile extends Content { private long size; ... }
Installation
It is easy to install RESTeasy in java application, read the Installation section of RESTeasy documentation. Read more Spring integration section if you want to integrate RESTeasy with Spring. And we finish enable RESTful support in our Java application! Like its name, it is easy to deploy Restful solution by using RESTeasy.
Unit test
After all above tasks, it is time to look unit test solution. RESTeasy provides two approaches to write unit test:
- Embedded Container:RESTeasy packages TJWS embeddable servlet container with JAX-RS. You can start the embedded server in your unit test and use Apache HttpClient to test your code. See the sample in attachment files for detail.
- Server side - Mock framework:If you are not comfortable with starting the embedded server and would like a simpler solution, RESTeasy Mock framework would be a good option.
Create Dispatcher mock object and integrate RESTeasy with Spring
dispatcher = MockDispatcherFactory.createDispatcher(); SpringBeanProcessor processor = new SpringBeanProcessor(dispatcher, null, null); factory = new ClassPathXmlApplicationContext("ecm-context-test.xml"); factory.addBeanFactoryPostProcessor(processor); SpringResourceFactory noDefaults = new SpringResourceFactory( "fileSystemService", factory, FileSystemService.class); dispatcher.getRegistry().addResourceFactory(noDefaults);
Send the request and test expected output
MockHttpRequest request = MockHttpRequest .get("/filesystem/getFile/content/A"); MockHttpResponse response = new MockHttpResponse(); dispatcher.invoke(request, response); Assert.assertEquals(HttpServletResponse.SC_OK, response.getStatus()); String responseBodyAsString = response.getContentAsString(); JAXBContext jaxbContext = JAXBContext.newInstance(SimpleFile.class); Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); SimpleFile resultFile = (SimpleFile) unmarshaller .unmarshal(new StringReader(responseBodyAsString)); Assert.assertEquals(ContentConstants.FILE_ROOT_PATH + "/A", resultFile .getPath());The result of request method is a XML string, the result of marshaling SimpleFile object to XML. To avoid testing base on XML data, we use JAXB to unmarshal XML data to SimpleFile object and validate data on this POJO.
Related posts:
- Develop application runs on both Desktop and Web environments by Flex and AIR A case study of developing RIA for both web and...
- Testing Spring beans against database with DbUnit This is another post of testing series we wrote so...
- Integration of Jackrabbit OCM and Spring (updated version) In the previous post Jackrabbit OCM and Spring, I have...
- Engroup 1.5.07082009 released eSoftHead is very pleased to announce that Engroup 1.5.07082009 is...
- Engroup 1.5 Milestone 1 released eSoftHead is pleased to inform that Engroup 1.5 M1 of...
I wrote the SpringMVC integration and the version of the DispatcherServlet add on to the TJWS Embedded container.
This does seem like a compelling approach. I’ll try to add on some utilities to make this kind of testing even easier.
something like
in your test file
and in your test:
@Autowired
ClientRequestFactory requestFactory;
@Test
public void test(){
SimpleFile file = requestFactory.get(“/filesystem/getFile/content/A”, SimpleFile.class);
…
}
Does that sound good? Do you have any other suggestions?
}
Solomon, it sounds good. You should support options of unmarshalling (if you want to write a generic purpose utility) for JSON, XML, Atom etc
I am willing if you need further discussion.
I already added that in the RESTEasy SVN.
SimpleFile sf = ReaderUtility.read(SimpleFile.class, “application/xml”, responseBodyAsString)
With a corresponding WriterUtility…
It is good for developers, they do not need to know the raw data is created. Thanks Solomon
Hi
Am using JAXB to marshall and unmarshall both the request and response objects. Its pretty easy and performance is very good compared to SOAP.
Yeah, it is pretty easy to JAXB to marshall and unmarshall java objects. One more option is using JSON serializer/de-serializer, I prefer the second option if it is possible because it is simple, ease of reading and it is ease to make interoperability with other language.