For a project we were looking for an appropriate technology stack to create a back end application supporting a REST like architecture together with some screens to test and maintain the application. The application uses REST like urls and JSON as response objects. Some data is persisted in a database and we want to use Java as pure as possible. Another requirements was not to use xml. We are very used to using spring framework for dependency injection. We chose another framework though. Most important reason was to lose the xml configuration (yes I know Spring can do that as well), Guice comes with a wel thought out strategy for configuring dependency injection using annotations. Than we needed a front end (MVC framework) without xml configurtion as well. Since there is a pretty easy configuration of Stripes and Guice, we decided to use Stripes. With this blog post I am going to walk you through a the stripes part of the mentioned application. If you want to learn more about Guice and the JPA part, check my other blog item One liter of Guice during spring break.
Read on to find out more about stripes.
- mvc framework
- No xml
- Extendability using Interceptors
- Test framework of stripes
What am I going to create?
Configuring stripes
Within the web.xml (the only xml you really need) we configure a filter and a servlet. You initialize the filter with the starting package for ActionBeans. The servet is a typical mvc dispatcher servlet that receives all requests. One of my requirements to use REST like urls made me change the serlvet mapping a little bit from the more common configuration. I map /rest/* to the servlet. That way all static resources like javascript files and stylesheets are not directed through the dispatcher servlet. Check the following url for an example web.xml. My important change is after the url
http://www.stripesframework.org/display/stripes/Quick+Start+Guide
<servlet-mapping> <servlet-name>StripesDispatcher</servlet-name> <url-pattern>/rest/*</url-pattern> </servlet-mapping>
Stripes is an MVC framework, this means we have to do something with a model, controllers and of course view. The dispatcher servlet maps incoming requests to controllers or ActionBeans. This is the first time you have almost no xml configuration to tell Stripes where to find these ActionBeans and how to map them to requests. Within the web.xml you configure the package(s) where it can find ActionBeans. The ActionBeans themselves implement the ActionBean interface. Using a url mapper you can define the url a certain ActionBean responds to. The following code gives an example of one of these mappings.
@UrlBinding("/rest/parcel/{$event}/{parcelId}") public class ParcelsActionBean extends BaseActionBean implements ActionBean { private int parcelId; @HandlesEvent("all") @DefaultHandler public Resolution obtainParcels() { ... } @HandlesEvent("view") public Resolution viewReadonly() { ... } @HandlesEvent("edit") public Resolution edit() { ... } @HandlesEvent("drop") public Resolution dropWithoutConfirm() { ... } public void setParcelId(int parcelId) { this.parcelId = parcelId; } }
The header for this class shows the @UrlBinding tag, this tags configures the url that this bean responds to. The good part of this url is that you can use parameter binding. The last part {parcelId} is used by stripes to call the setter method for parcelId when clients call the appropriate url. Another thing is the {$event}. That value is used to determine which method to call. Check out the following examples
- http://…/rest/parcel – returns a collection of Parcel objects by calling the obtainParcels method.
- http://…/rest/parcel/view/1 – return the parcel with id 1 to view by calling the viewReadOnly method
- http://…/rest/parcel/edit/1 – updates the parcel with id 1 by calling the edit method.
Stripes also supports a nice mechanism for default mapping of urls to ActionBeans. We did not use this however.
One important thing to know about Stripes is that the ActionBeans are not reused. They are recreated for every request to it. Therefore it is no problem to add instance variables to an ActionBean. To make it easier for a jsp developer, the ActionBean is stored in the request under the name actionBean. This makes it very easy to use the standard jstl library to read items from the ActionBean. The following example would format the property squareMeters using the getSquareMeters() method.
Our project made use of a JSon interface, therefore there was no need for a lot of jsp’s or other front end technology. We did have one form, but we used JQuery to submit it to the server. JQuery was also used to receive the result and present it in a normal textare element. The script looks as follows
function submitForm() { $('#solidSpaceFilter').ajaxSubmit(function(data) { $("#reponseText").val(data); }); }
The data that is returned was in JSon format, well, actually in a long JSon string. To be ble to read it, we used the page from Chris Nielsen. Stripes uses the Resolution implementations to create the response. It is a very easy interface with just an execute method. There are a number of Resolutions that come with stripes out of the box. Some of them are: ForwardResolution, StreamingResolution. For the project we have created a JSon resolution. The following code shows the code of this bean.
public class JSONResolution implements Resolution { private static final Logger logger = LoggerFactory.getLogger(JSONResolution.class); private static final String CONTENT_TYPE = "application/json"; private String toPrint; public JSONResolution(String toPrint) { this.toPrint = toPrint; } public void execute(HttpServletRequest request, HttpServletResponse response) throws Exception { response.setContentType(CONTENT_TYPE); doExecute(request, response); response.getOutputStream().print(toPrint); } protected void doExecute(HttpServletRequest request, HttpServletResponse response) throws IOException { } protected String getToPrint() { return toPrint; } protected void setToPrint(String toPrint) { this.toPrint = toPrint; } }
As you can see, this is a very easy class that sets the content type and just writes the content to the outputstream. The class is easy to extend by just overwriting the doExecute(…) method.
The next thing I want to mention is the test framework that comes with stripes. There are a lot of mock objects available that you can use. There are mocks for HttpServletRequest and HttpServletResponse. That way you can test your action beans. Due to the nature of the action beans it is very easy to call a method of an action bean that you want to test. The following code block shows a test we have created.
@Test public void testView() throws Exception { long id = 1L; MyService service = createMock(MyService.class); expect(service.obtainItemById(id)).andReturn(preparedItem); replay(service); MyActionBean bean = new MyActionBean(); bean.setService(service); bean.setId(id); Resolution resolution = bean.view(); MockHttpServletResponse mockResponse = new MockHttpServletResponse(); resolution.execute(new MockHttpServletRequest("", ""), mockResponse); JSONObject json = JSONObject(mockResponse.getOutputString()); assertEquals("Parcel with id '" + nonExistingParcelId + "' does not exist", json.get("error")); }
Using the mocks you can prepare your test so it looks like your real application. You can add filters to the MockServletContext. That way you can also test the mapping of requests to your action beans.
The final topic I want to mention is the use of Interceptor classes. Stripes has provided a mechanism to extend the framework by using interceptors. I already discussed this technique in my previous post on integrating stripes and guice. I configured the stripes filter with a special GuiceInterceptor class. Check out the other post for details
That’s it for now. I think Stripes is a very nice framework. I am not sure if I will be using it a lot. I am so used to spring framework and the spring-mvc project. I do like the way you can do url mapping. This will be introduced in spring 3 as well. But for everybody that thinks spring is to much, or that just want to try out something different, stripes is a very nice product. If you want to learn more about stripes, I recommend the following book.
[amtap book:isbn=1934356212]