Friday, December 31, 2010

Spring MVC 3 - Tiles 2 Integration Tutorial

In this tutorial we will build a simple Spring MVC 3 application that utilizes templates using the Apache Tile 2 framework. We will create a template version of our pages, and compare it with non-template versions of the same pages. We will split the content, style, and template of these pages logically.

What is Apache Tiles 2?
Apache Tiles is a templating framework built to simplify the development of web application user interfaces.

Tiles allows authors to define page fragments which can be assembled into a complete page at runtime. These fragments, or tiles, can be used as simple includes in order to reduce the duplication of common page elements or embedded within other tiles to develop a series of reusable templates. These templates streamline the development of a consistent look and feel across an entire application.

Source: Apache Tiles 2

Our MVC application is about pet information. The main page describes pets in general. The other two pages are speficic for dogs and cats . We will develop our application using standard JSP. Then later, we'll create a Tiles version.

Let's start with the Pets page.

pets.jsp

There are four major div elements here. Each one has been assigned an id so that we can style them using CSS.

Here's the actual screenshot of the Pets page:

Here's the Dogs page.

dogs.jsp

There are five major div elements here. Each one has been assigned an id so that we can style them using CSS.

Here's the actual screenshot of the Dogs page

Here's the Cats page.

cats.jsp

Here's the actual screenshot of the Cats page

We also have the same five major div elements here.

In order for these JSP pages to be served in Spring MVC, we need to declare the corresponding controller.

NoTilesController

This controller has three mappings:
/notiles/pets - for displaying the pets page
/notiles/dogs - for displaying the dogs page
/notiles/cats - for displaying the cats page
To load the pets page, enter the following url:
http://localhost:8080/spring-tiles/krams/notiles/pets
To load the dogs page, enter the following url:
http://localhost:8080/spring-tiles/krams/notiles/dogs
To load the cats page, enter the following url:
http://localhost:8080/spring-tiles/krams/notiles/cats
That's it. We've developed a simple Spring MVC 3 application using standard JSPs.

However, we have a problem

We've declared three JSP pages. Imagine if we have 100 JSP pages to managed with. We need to modify the title and the footer. So we open all 100 JSP pages, edit them one by one. To make it faster, we copy and paste the same code. But we made a typo. So we edit again those 100 pages. And the cycle goes on. What if we have 500 pages? 1000? How do we make our lives less difficult?

Enter Apache Tiles 2

Let's refactor our JSPs. Remember in each of these pages there are major div elements. We'll use those as markers for our templates.

The Pets page uses the following structure:

Our first template is based on this structure.

main.jsp

Each div contains a tiles:insertAttribute element. These are placeholders for the actual content.

Our second template will be based on the cats.jsp and dogs.jsp. They share the same structure so that means they will share the same template.

details.jsp

So far we have created 5 JSPs:
cats.jsp - a standard JSP
dogs.jsp - a standard JSP 
pets.jsp - a standard JSP 

detail.jsp - a Tiles JSP 
main.jsp - a Tiles JSP 
To use these templates we need to activate Apache Tiles by declaring the required XML configurations. To do that we need to declare the required Spring configurations as well.

We'll start with the web.xml.

web.xml

Take note of the URL pattern. When accessing any pages in our MVC application, the host name must be appended with
/krams
In the web.xml we declared a servlet-name spring. By convention, we must declare a spring-servlet.xml as well.

spring-servlet.xml

This XML config declares a view resolver. All references to a JSP name in the controllers will map to a corresponding JSP in the /WEB-INF/jsp location.

By convention, we must declare an applicationContext.xml

applicationContext.xml

This XML config declares three beans to activate the Spring 3 MVC programming model. W've also imported a resource tiles-context.xml. This contains the Tiles configuration.

tiles-context.xml

This configuration declares required beans to activate Tiles. The TilesConfigurer relies on an external definitions file tiles-definitions.xml for the actual declaration of the templates.

tiles-definitions.xml

We've declared two templates: template-main and template-detail.

These attributes must match the attributes you declared in template: main.jsp

We've declared three concrete pages: pet-tiles, dog-tiles, cat-tiles

Each concrete page extends a template name. For example, cat-tiles extends from template-detail. In Java this is comparable to inheritance.

Do you understand now how Tiles can help us reduce repetitive code and lessen errors? By reusing the same JSP and template, we improve our development time. Our JSPs became manageable. If you want to switch content, you just do it in the tiles-definitions.xml. For example, if you need to update the footer.jsp, you just open the file. Edit then save. You don't need to edit 1000 JSP pages anymore.

Of course, if you're just dealing with three pages. The initial configuration might be too much. But in real-life applications, it's a time saver because you're dealing with lots of JSPs.

Because we're still dealing with a MVC application, we need to declare another controller to handle these Tiles pages.

TilesController

This controller has three mappings:
/tiles/pets - for displaying the pets page
/tiles/dogs - for displaying the dogs page
/tiles/cats - for displaying the cats page
To load the pets page, enter the following url:
http://localhost:8080/spring-tiles/krams/tiles/pets
To load the dogs page, enter the following url:
http://localhost:8080/spring-tiles/krams/tiles/dogs
To load the cats page, enter the following url:
http://localhost:8080/spring-tiles/krams/tiles/cats

Let's compare side by side our JSP pages with using the standard JSPs and the ones with Tiles support.

Pets Page (before and after)

Dogs Page (before and after)

Cats Page (before and after)

That's it. We've managed to refactor our code and use the Tiles framework. We've also leveraged on Spring programming model to develop our MVC application.

The best way to learn further is to try the actual application.

Download the project
You can access the project site at Google's Project Hosting at http://code.google.com/p/spring-mvc-tiles/

You can download the project as a Maven build. Look for the spring-tiles.zip in the Download sections.

You can run the project directly using an embedded server via Maven.
For Tomcat: mvn tomcat:run
For Jetty: mvn jetty:run
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: Spring MVC 3 - Tiles 2 Integration Tutorial ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

29 comments:

  1. after strugling for a day with tiles and spring mvc3 , i got this article which solved my problem within an hour.

    Thanks
    -- Santosh

    ReplyDelete
  2. great job , many explanations many comments as should be!
    best Spring 3 MVC + Tiles example I have found!

    ReplyDelete
  3. The only thing missing here is a image of the project structure + what JARs to use

    ReplyDelete
  4. JARs :

    from Tiles download file:
    commons-beanutils-1.8.0
    commons-digester-2.0
    tiles-api-2.2.2
    tiles-core-2.2.2
    tiles-jsp-2.2.2
    tiles-servlet-2.2.2
    tiles-template-2.2.2
    slf4j-api-1.5.8
    slf4j-simple-1.5.6

    Other
    log4j-1.2.16
    Spring jars

    ReplyDelete
  5. Dude, I owe you my life... twice. It is the second time I struggle with Java EE for a time until I find the answer here in your blog. :)

    \\//

    ReplyDelete
  6. Hello, I am trying to adapt your suggestions in my Spring3.0.6 Tiles project, and have tried for a full day now, hence am asking for help. When I try to load a page, I get an error, and portions of stacktrace are paseted....can you point me towards what may I be doing wrong?
    -----------------------------------
    2011-11-05 17:12:12,440 [http-bio-8080-exec-2] ERROR org.springframework.web.servlet.tags.form.LabelTag - Neither BindingResult nor plain target object for bean name 'command' available as request attribute
    java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute
    at org.springframework.web.servlet.support.BindStatus.(BindStatus.java:141)
    at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getBindStatus(AbstractDataBoundFormElementTag.java:174)
    at org.springframework.web.servlet.tags.form.AbstractHtmlElementTag.resolveCssClass(AbstractHtmlElementTag.java:448)
    ......
    Nov 5, 2011 5:12:12 PM org.apache.catalina.core.ApplicationDispatcher invoke
    SEVERE: Servlet.service() for servlet jsp threw exception
    java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute
    at org.springframework.web.servlet.support.BindStatus.(BindStatus.java:141)
    at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getBindStatus(AbstractDataBoundFormElementTag.java:174)
    at org.springframework.web.servlet.tags.form.AbstractHtmlElementTag.resolveCssClass(AbstractHtmlElementTag.java:448)
    at org.springframework.web.servlet.tags.form.AbstractHtmlElementTag.writeOptionalAttributes(AbstractHtmlElementTag.java:418)
    .................
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)
    Caused by: org.apache.tiles.util.TilesIOException: ServletException including path '/WEB-INF/jsp/layout.jsp'.
    at org.apache.tiles.servlet.context.ServletUtil.wrapServletException(ServletUtil.java:241)
    at org.apache.tiles.servlet.context.ServletTilesRequestContext.forward(ServletTilesRequestContext.java:243)
    at org.apache.tiles.servlet.context.ServletTilesRequestContext.dispatch(ServletTilesRequestContext.java:222)
    at org.apache.tiles.renderer.impl.TemplateAttributeRenderer.write(TemplateAttributeRenderer.java:44)
    at org.apache.tiles.renderer.impl.AbstractBaseAttributeRenderer.render(AbstractBaseAttributeRenderer.java:106)
    at org.apache.tiles.impl.BasicTilesContainer.render(BasicTilesContainer.java:670)
    at org.apache.tiles.impl.BasicTilesContainer.render(BasicTilesContainer.java:690)
    ... 28 more
    Caused by: org.apache.tiles.util.TilesIOException: JSPException including path '/WEB-INF/jsp/user.jsp'.
    at org.apache.tiles.servlet.context.ServletUtil.wrapServletException(ServletUtil.java:241)
    at org.apache.tiles.jsp.context.JspTilesRequestContext.include(JspTilesRequestContext.java:105)
    at org.apache.tiles.jsp.context.JspTilesRequestContext.dispatch(JspTilesRequestContext.java:96)
    at org.apache.tiles.renderer.impl.TemplateAttributeRenderer.write(TemplateAttributeRenderer.java:44)
    ........................

    ReplyDelete
  7. Thanks for post just a good tutorial on spring mvc and tiles 2 integration. I have a question if anyone know the answer.

    How sitemesh comparing with tiles 2? Is it sitemesh no longer end of life? I try a search on the web to make comparing this two frameworks, most of comparison are few years back.


    Dave.

    ReplyDelete
  8. @Dave, I haven't used SiteMesh but a quick Google shows that Sitemesh is either dead (or half-dead) or its development has been slowed down drastically. See http://comments.gmane.org/gmane.comp.web.sitemesh.sitemesh3.general/58

    ReplyDelete
  9. Hi Krams, thanks for the post - it was really useful.Quick question about using controllers with Tiles.I have an app with a similar layout to yours.However my header has a selection box that is dynamically filled from a DB.I have a controller that adds the items for the selection box to a list like this:

    @RequestMapping("/mySubscriptions")
    public String listTopics(Map map){
    map.put("topic", new Topic());
    map.put("topicList", topicService.listTopic());

    List topics = topicService.listTopic();
    return "mySubscriptions";
    }

    But when the header is rendered the selection box remains empty even though I am using the correct JSTL libraries in my JSP like so:

    ...
    &ltselect name="topic" id="topicSelect" style="width:375px">

    &ltoption&gt value="${topic.id}">${topic.name}&lt/option&gt

    &lt/select&gt
    ...

    Before I started using tiles I was using a simple include tag to include my headers like so:

    <%@ include file="/WEB-INF/jsp/header.jsp" %>

    This worked fine and my slection box was filled in my header. With tiles it has stopped working! Have you any idea why?

    Thanks,

    Seán

    ReplyDelete
  10. Hi Krams,

    Great post.

    I'm currently using Spring 3.1, Spring MVC, Tiles 2.2.x. I've got a jspx page that contains a form that is put together with Tiles. Is it possible to make a JQuery ajax call to load the form into a content area? I would assume it is, but I've not been able to find something that is using just Spring MVC. I'm not sure if I need to use Spring Web Flow, or even if I need to render the form as a partial at all.

    If there is a better way to do this, or if you or someone else has some insight as to how I can do this, it would be great and I would really appreciate it.

    Thanks,
    John

    ReplyDelete
    Replies
    1. John, you can create a jQuery plugin (or a JavaScript function) that will create you a dynamic form. The jQuery plugin is a more elegant solution though. You can attach it to any content area (i.e div). Basically your form is generated on the fly instead of hard-coding it in a JSP form.

      That means to populate or interact with this form, you will have to rely on AJAX calls instead of Spring's JSP tag support. And I believe this is a more flexible solution since you can reuse the same HTML (JSP in this case) on a non-Java backend.

      Delete
  11. Thanks krams, I'll give that approach a try. Sounds like a nice solution.

    Thanks again!
    John

    ReplyDelete
  12. Hi Thanks for the wonderful tutorial.

    I need a help over here. I want to use Ajax in tiles. I have changed the resolver and view class in dispatcher to "AjaxUrlBasedViewResolver" and "AjaxTilesView" But still ajax is not working. Any reasons?

    ReplyDelete
  13. Thanks...this helped me a lot.

    ReplyDelete
  14. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
  15. Hi,
    you have created three concrete pages: pet-tiles, dog-tiles, cat-tiles.

    can we somehow make dynamic page, for example like:

    <definition name="*" extends="template-detail">
    <put-attribute name="subtitle-content" value="{1}" />
    <put-attribute name="primary-content" value="/WEB-INF/jsp/contents/{1}.jsp" />
    </definition>

    so if we have hundreds of pages to show in main content, not to create for each concrete page?

    ReplyDelete
  16. with spring 3 and tiles, one can configure page structure in every possible way. thanks for putting in the screenshots for page structure. But does page re-size and resolution have any effect on pages created using tiles plugin.

    ReplyDelete
    Replies
    1. I think the effects of page resize and resolution will matter on the CSS rules (and browser implementation). If this is your scenario, I would suggest employing "Responsive Web Design" principles, so that you can accommodate various resolutions i.e mobile, desktop, and tablets

      Delete
  17. I´m looking for a tutorial to create a search form and, after sendding it, create a jqgrid with the search results. Tecnologies needed are Spring MVC 3, Tiles 2, JqGrid, JQuery UI.
    Do you know where can I find something similar to this?
    thanks in advance
    After I will need securize only one column but this is another business now :)

    ReplyDelete
  18. Hi,

    I am using PHP as view in Spring MVC 3 using QuercusView as view class with UrlBasedViewResolver. Is it possible to implement tiles there using TilesViewResolver?

    Thanks in advance.

    ReplyDelete
  19. This is quite comprehensive tutorial covering various details on spring and tiles. highly recommended. the only thing one can add is that if you are using maven, the dependencies can be downloaded automatically by using the pom.xml. I have explained the same in a blog post on STS, tiles and maven

    ReplyDelete
  20. Hi Krams, thank you for posting this. My particular problem, and the reason I read your post (!) still has not been answered unfortunately...

    Where you say
    " // Do your work here. Whatever you like
    // i.e call a custom service to do your business
    // Prepare a model to be used by the JSP page"
    Please can you give an example how to do this. See my SOF question:
    http://stackoverflow.com/questions/16396266/spring-3-mvc-model-not-displaying-in-jsp-page

    Thanks !!! Would appreciate some feedback.

    ReplyDelete
  21. How i can open a definition in a new tab. Please anyone help me.

    ReplyDelete