Effective web layer practices with spring-mvc 3 + REST + jsp view

Hi everyone,

For once, I’ll bend the rules, I’ll talk about UI !!!
Hey don’t get me wrong : I’m not a designer. I just don’t have the UI sensibility required to reach csszengarden.com‘s level.
I read a few days ago a tweet saying “If you don’t know anything about UI, don’t pretend, do like me, stay on server side” (@agoncal on tweeter). It really made me laugh but I couldn’t agree more.

I’ll just present a bunch of tips which proved to be effective FOR ME in java web layer design.
But before diving let me set the perimeter and make sure we speak the same language.
The example is a CRUD operation on a domain object : User. The app uses plain old MVC2 architecture.
No fancy new technologies that I’m barely aware of. Yes you heard me you young people scaring me with all your HTML5, JQuery coupled with functional.js which will allow you to curry (to me curry was just a spice), CSS3, COMET and alike.

The good thing about web design is that problems are well identified. One day or another you’ll be concerned with these topics : testing, i18n, logging, data binding, data validation, error handling, servlet mapping.

I wrote a little poc which adresses the above topics through a little webapp.
The user interface has 6 basic use cases (while the server side has 4 functions : CRUD) : register user, display account, edit account, update account, delete account, list users.

I choose to combine classic jsp pages with REST methods. That way, the application is ready for new fancy technologies in case I get skillfull and comfortable enough to address the very same topics :).

As always, the complete source code will be available at the end of the article.

1 – When it comes to multiple languages
Internationalization (i18n) is a very common concern for webapps. It is natively supported by most view technologies. In the example I use JSTL.

Tell JSTL where to find messages

	<context-param>
		<param-name>javax.servlet.jsp.jstl.fmt.localizationContext</param-name>
		<param-value>messages</param-value>
	</context-param>

Use fmt:message to display message

		<title>
		    <fmt:message key="index.title" />
		</title>

Of course you must provide a message.properties resource available in your classpath.
The features I like most (which I didn’t implement here) in spring-mvc i18n features are :
* the ability to change site language at will using AcceptHeaderLocaleResolver
* the ability to use realoadable messages using ReloadableResourceBundleMessageSource

 

2 – Through the logging jungle
Logging can really become a nightmare when it comes to migrating from a widely used solution (JCL) to a better one (SLF4J).
Thanks to this post I now know which combinations are possible for either an existing project, or a brand new one.

import dependencies

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
		</dependency>

logback.xml

	<appender name="log-file" class="ch.qos.logback.core.rolling.RollingFileAppender">
		<file>${LOGS_FOLDER}/webapp-testing.log</file>
		<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
			<level>WARN</level>
		</filter>
		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
			<fileNamePattern>${LOGS_TO_COLLECT_FOLDER}/webapp-testing.%d{yyyyMMdd-HHmm}.log</fileNamePattern>
      </rollingPolicy>
      <encoder>
         <pattern>%d{yyyy/MM/dd HH:mm:ss,SSS} [%thread] %-5level %logger{36} - %m %throwable{0}%n</pattern>
      </encoder>
   </appender>

Note that if you add a file named logback-test.xml in src/test/resources logback will use it automatically.
This article helped me to gather best practices around logging.

 

3 – Web Data binding : the elegant way
You remember when one had to create a mapping between http params (which are Strings) and a backing bean ? In Struts this support was so weak that we often ended designing beans with String fields only. We avoided anything that was not String. And then the work still had to be done : we had to convert String fields to Date, Long, BigDecimal and even more complex types. It was a huge regression in Object design.
With spring-mvc all you need is declare an annotation @ModelAttribute and it will map at best any matching property. Example from UserController

	@RequestMapping(value = "/users/register", method = RequestMethod.PUT)
	public String register(@ModelAttribute("user") User user, BindingResult bindingResult, ModelMap model) {

With this annotation spring will know that it has to bind any request parameter to the user properties.
No plumbing needed except when a property is not supported by spring (typically your domain objects)

 

4 – Data validation : not really an issue
I choose not to validate in web layer. Every business rule stays at server side. The philosophy is : send it as fast as you can to the server. Minimize the transformations. Come as you are at server side. I’ll tell you wether you fit in my system or not.
But if there is a problem I have to report it in the UI.
The following piece of code catches an error from business layer, propagates the error in the response and redisplays the form.

		Long id = null;

		try {

			id = this.userService.add(user);

		} catch (BusinessException ex) {

			if (bindingResult == null) {
				bindingResult = new BindException(user,
						UserController.USER_MODEL_KEY);
			}

			bindingResult.reject(ex.getMessageCode(), ex.getMessageArgs(), ex
					.getDefaultMessage());

			return displayNewFormView();

		}

5 – Error handling : thing are getting a bit rough
I really tend get the maximum of the infrastructures I use.
The servlet specification alows me to associate error pages to HTTP error codes or java Exceptions.
Spring-mvc allows me to handle errors but not to associate HTTP error codes to a view.

I’m used to mix both : use servlet to handle HTTP codes, use spring to handle java exceptions. But lately I’ve been thinking about only using the container mechanism as I find it more complete.

Below the servlet capability (from web.xml) :

    <error-page>
        <error-code>405</error-code>
        <location>/WEB-INF/jsp/errors/405.jsp</location>
    </error-page>
    <error-page>
         <exception-type>java.lang.Throwable</exception-type>
	 <location>/WEB-INF/jsp/errors/container-exceptions.jsp</location>
    </error-page>

Below spring-mvc capability (from dispatcher-servlet.xml) :

	<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
		<property name="exceptionMappings">
			<props>
				<prop key=".lang.Throwable">errors/unhandled-errors</prop>
			</props>
		</property>
	</bean>

6 – Servlet mapping : a real insufficiency there
One of the worst nightmare of servlet specification is servlet mapping.
Nowadays REST-like URI become widely adopted (I personnally was an early-adopter as I find REST so clear and neat).
The support for REST URI is really incomplete in the servlet specification (or I’m not aware of it).
The solution is to map every URI to spring controllers. The real improvement comes with annotations : POJO methods are responsible for handling URI mapping. The flexibility is maximum because, except for common sense reasons, you’re not forced to handle every related URI in the same controller. One could create DefaultController and UserController and still map a user related URI to one of the DefaultController class.
Not being forced to Create a controller per URI is the real improvement (I didn’t like MultiActionController at all).

Controller

@Controller("userController")
public class UserController {
@RequestMapping(value = "/users/load/{id}", method = RequestMethod.GET)
	public String get(@PathVariable("id") Long id, ModelMap model) {

		addUserToModel(id, model);

		User user = (User) model.get(UserController.USER_MODEL_KEY);

		if (user == null)
			throw new IllegalArgumentException("User with ID " + id
					+ "not found");

		return displayAccountView();

	}

	@RequestMapping(value = "/users/edit/{id}", method = RequestMethod.GET)
	public String edit(@PathVariable("id") Long id, ModelMap model) {

		addUserToModel(id, model);

		return displayEditView();

	}
...
}

7 – Handling REST with MVC2 efficiently
To handle REST completely spring-mvc had to hack the Browsers support for HTTP methods as they only support GET and POST. The HiddenHttpMethodFilter is responsible for interpreting the “_method” field which holds the real method name. With this information it can either execute the method or set a 405 (method not allowed) status.
HiddenHttpMethodFilter in web.xml

	<!-- required to enable Spring 3.0 REST support -->
	<filter>
		<filter-name>httpMethodFilter</filter-name>
		<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
	</filter>

Form in jsp

<form:form action="${pageContext.request.contextPath}/users/register" method="PUT" modelAttribute="user">

will result in

<form name="user" action="context/users/register" method="POST" modelAttribute="user">
<input type = "hidden" name="_method" value="PUT"/>
...
</form>

Once you’ve understood this you can dive into spring REST features : multi-content support (xml, json, jsp), content negotiation, etc.

 

8 – The best part : testing
Did I mention that I’m a big fan of tests? Every framework that doesn’t allow me to test will not end in my stack. I mean it.
On web layer you can have 2 kind of tests :
– unit tests : navigation flow, income, outcomes, errors.
– functionnal tests : scenari, simulate user inputs, launch browsers, etc.

The former is easy because web controllers are POJOs. One can test expected outcomes, redirect-after-post pattern, request/session attributes.
While being easy it still is invaluable because you can test your pages flow which is a great part in web sites.

For the functionnal tests you have 2 choices : either you launch a browser and emulates user inputs, either you use a framework which emulates a browser.
The first familly is very complete (run javascript) but heavier. Selenium and Watij are the products I know most. Watij is cleaner, more concise and lighter but it doesn’t integrate in maven when Selenium does …

The solutions that emulates browser are my favorites because they are lighter : HttpUnit and commons-httpclient. The major incovenient with these solutions is that you have to parse the response if you want to test it with a powerful tool like Xpath. It’s a good thing as it forces you to produce well-formed html but parsing a html page can become very very tricky when the page contains html entities like

nbsp; or amp;

. If you add language issues it soon becomes a nightmare.
HttpUnit goes a bit further than commons-httpclient : http sessions and javascript are supported, as well as xhtml. I didn’t manage to make it work (I think my test is not correctly isolated, nothing to do with HttpUnit).

I’am not really fan of functionnal testing because I always feel like business people don’t trust them, so they don’t give us enough time to code exhaustive sets of scenarii (too expansive). And for me the pages flow are more important than user interactions. Moreover there will always be human testers, if we test too perfectly they will have nothing left to do :).

This ends the article. Hope I brought light on classical web layer design to those who still are interested in traditional technologies :).
I would like to thank StSMedia finance app as my pages and style are greatly inspired from its work.

As always the code is available here.

And of course if you like the article feel free to tweet it !

Advertisements

3 thoughts on “Effective web layer practices with spring-mvc 3 + REST + jsp view

  1. Nice post !

    I use JRS 303 to validate with the @Valid annotation and it integrates well with Spring.
    I think I don’t understand well why the support for rest URI is really incomplete. You can do POST, GET, DELETE and PUT as request method. You can also use regexp in the controller to match the URI and the controller method.
    For the functionnal testing, I use the Spring jUnit Class Runner a lot, not a big fan of UI testing 🙂

    1. Thx Mathilde.

      I also use JSR-303 to validate beans but only when I use a database persistence mechanism (which is not the case in the article). That way I can validate “simple” rules. I’m not comfortable with using JSR-303 when it comes to validate complex rules which span on more than one bean. I guess I’ll have to dive a bit more into the bean validation API if I want to get rid of my old tools :).

      When I said “The support for REST URI is really incomplete in the servlet specification” I meant it is not natively supported in the web.xml file. Try to map something like “/users/find/by/*/term/{term}” in the web.xml file and you’ll get my point :). Of course with other technologies like Spring or JERSEY you can achieve that. But before using framework I like to get the max of JEE.

      I think Spring doesn’t have support for functional testing (nothing to run javascript or http requests …), at least non that I’m aware of. It has support for integration testing …

      Hope I was clear enough.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s