Sunday 17 March 2013

Spring MVC with Apache Tiles 3

There is a dearth of tutorials which examine Spring MVC with Apache Tiles 3. Most of them allude to this one, which is insufficient. Having recently integrated Tiles 3 into my Spring MVC project, I have decided to make a short tutorial on how to get everything running.

What you need : Apache Tiles 3, Spring Framework 3.2.2 (earlier versions may not support Tiles 3 by default), Spring MVC

Steps :

Add your Tiles dependency to pom.xml (or download the Tiles 3 jar and add to your classpath if you're not using Maven) :
<dependency>
    <groupId>org.apache.tiles</groupId>
    <artifactId>tiles-extras</artifactId>
    <version>3.0.0</version>
</dependency>
Make sure you're using Spring 3.2.2 :
<properties>
    <java-version>1.6</java-version>
    <org.springframework-version>3.2.2.RELEASE</org.springframework-version>
    <org.aspectj-version>1.6.9</org.aspectj-version>
    <org.slf4j-version>1.5.10</org.slf4j-version>
</properties>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>3.2.2.RELEASE</version>
</dependency>
In your servlet-context.xml (or whatever you named yours) :
<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">
    <!--Don't add suffix or prefix like you do with .jsp files-->
    <property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView"/>
</bean>

<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer" >
    <property name="definitions">
        <value>/WEB-INF/tiles.xml</value>
    </property>
</bean>
If you were using the traditional Spring MVC view resolution method with jsp files, your servlet-context.xml might have had something like this :
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/" />
    <property name="suffix" value=".jsp" />
</bean>
Please remove that, since you're using a new mechanism to resolve views.

Create a tiles.xml file under your WEB-INF directory :
<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN" "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">

<tiles-definitions>

    <definition name="*/*" template="/WEB-INF/tiles/template.jsp">
        <put-attribute name="meta" value="/WEB-INF/tiles/{2}/meta.jsp" />
        <put-attribute name="header" value="/WEB-INF/tiles/{2}/header.jsp" />
        <put-attribute name="body" value="/WEB-INF/tiles/{2}/body.jsp" />
        <put-attribute name="footer" value="/WEB-INF/tiles/{2}/footer.jsp" />

    </definition>

</tiles-definitions>
Here's the directory structure :

Create a template.jsp under WEB-INF/tiles. Here's an example :
<!DOCTYPE HTML>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>

<html>
    <head>
        <tiles:insertAttribute name="meta" />
    </head>
    <body>
        <div id="header">
            <tiles:insertAttribute name="header" />
        </div>
        <div id="body">
            <tiles:insertAttribute name="body" />
        </div>
        <div id="footer">
            <tiles:insertAttribute name="footer" />
        </div>
    </body>
</html>
Whatever java controller you're using, make sure that the view you're returning matches the wildcard pattern in tiles.xml e.g., "/index" will match "*/*" in tiles.xml and you can use {2} to get the "index" string and tiles will render the tiles under the tiles/index folder :
@Controller
public class IndexController {
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public ModelAndView indexHandler() {
        ...
        return new ModelAndView("/index", "user", new SomeObject());
    }
}
Lastly, you can add your views under the WEB-INF/tiles directory. Remember that you'll need the same jsp files which are described in your tiles.xml file.

Extra :

If you require list attribute inheritance (which was implemented as a "fallback" here, and did not work for me at all), then change your tiles.xml to :
<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN" "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">

<tiles-definitions>

    <definition name="base" template="/WEB-INF/tiles/template.jsp">
        <put-attribute name="meta" value="/WEB-INF/tiles/common/meta.jsp" />
        <put-attribute name="footer" value="/WEB-INF/tiles/common/footer.jsp" />
    </definition>

    <definition name="*/*" extends="base">
        <put-attribute name="header" value="/WEB-INF/tiles/{2}/header.jsp" />
        <put-attribute name="body" value="/WEB-INF/tiles/{2}/body.jsp" />
    </definition>

</tiles-definitions>
What this essentially does is use the jsp files from the common folder, so you can avoid copying and pasting the same jsp file over and over again in all your subfolders. My directory structure now looks like this :
Notice that I don't need to have the footer.jsp and meta.jsp in all the WEB-INF/tiles subfolders.

22 comments:

  1. Hello Dhruv..Thank you very much for the example.
    I followed same steps but I am getting below error when start my application..can you please help me with htis?

    java.lang.ClassNotFoundException: org.apache.tiles.ArrayStack

    ReplyDelete
    Replies
    1. That class is not included with Tiles 3. I think you're using Tiles 2.

      Delete
    2. Thank you for your reply. I double checked all files, nowhere I found tiles2. I have tiles3 jars in lib too.

      I hope you can understand below code.

      dispatcher-servlet.xml
      -------------------------




      /WEB-INF/tiles.xml







      Tiles.xml
      ------------
      !DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN" "http://tiles.apache.org/dtds/tiles-config_3_0.dtd"

      Here is the error I am getting ( couldn’t paste all)
      -------------------------------
      org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.apache.tiles.request.render.CannotRenderException: ServletException including path '/WEB-INF/jsp/layout.jsp'.
      org.apache.tiles.request.render.CannotRenderException: ServletException including path '/WEB-INF/jsp/layout.jsp'.
      root cause
      javax.servlet.ServletException: java.lang.NoClassDefFoundError: org/apache/tiles/ArrayStack

      Delete
    3. Sorry..dispatcher-servlet text didn't show

      Tiles Inegration -->
      bean id="tilesConfigurer"
      class="org.springframework.web.servlet.view.tiles3.TilesConfigurer"


      /WEB-INF/tiles.xml
      /list>
      </property
      /bean
      bean id="viewResolver"
      class="org.springframework.web.servlet.view.UrlBasedViewResolver"
      property name="viewClass"
      value="org.springframework.web.servlet.view.tiles3.TilesView"

      Delete
  2. Maybe you should make a new stackoverflow question on it. I think your tiles.xml is probably configured wrongly. Can you try removing "/" from "/WEB-INF/jsp/layout.jsp" so that it looks like "WEB-INF/jsp/layout.jsp" inside your tiles.xml. Again, I would recommend that you create a new stackoverflow question.

    ReplyDelete
    Replies
    1. Thank you Dhruv. Sure I will try that and see, else I will create new one.

      Delete
  3. Could you send use the source ? doesn't work for me :(

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

    ReplyDelete
  5. Ever get the fallback working? I've tried all day and keep getting this exception from the OptionsRenderer:

    java.lang.IllegalStateException: A matching list-attribute name="my_opts" must be defined.

    I put the list attribute in the definitions and referenced it according to the docs, but it doesn't work. Any luck? Here's a snippet of my code in tiles.xml within a definition element:

    put-attribute name="meta" value="/WEB-INF/tiles/${options[my_opts]}/meta.jsp

    followed by a put-list-attribute name="my_opts" within the same definitions element.

    Anyone get this working?

    Thanks in advance
    Mike

    ReplyDelete
  6. btw you can simply use



    instead of UrlBasedViewResolver


    In fact this is the same implementation as you defined in xml but looks more clear

    ReplyDelete
  7. I mean you can use

    org.springframework.web.servlet.view.tiles3.TilesViewResolver

    ReplyDelete
  8. I used the same eventually today. Thanks for the comment. In the fallback method, there's also a SpringApplicationContext class that no longer exists, so I simply took it out. But I'm guessing that might be the issue. I also cracked open the OptionsRenderer to see where that exception was being thrown, and it appears to be trying to get the list attribute via a class named TilesAccess. Anywho, if no one see's a way around this maybe I can find something on the tiles mail lists or post the question there too. The fallback approach looks pretty neat, but I have yet to find a complete working example using a Spring and Tiles version I'm using, which is Spring 3.2.3.RELEASE and Tiles 3.0.1 if I recall (I'm away from my work computer, so I can't see my pom.xml at the moment).

    ReplyDelete
  9. Wonder if http://tiles.apache.org/tiles-request/apidocs/org/apache/tiles/request/ApplicationContextWrapper.html
    could replace SpringApplicationContext because it was deleted in Nov 2012 (See https://fisheye.springsource.org/browse/spring-framework-issues/SPR-8825/src/main/java/org/springframework/web/servlet/view/tiles3).
    I'm too tired to think anymore....Time to watch some A-Team reruns...

    ReplyDelete
  10. Hi Dhruv, thanks for your post. Do you know how to be able using tiles to reload only the body everytime a option of the menu is clicked, leaving the header and menu static?

    Thanks in advance

    ReplyDelete
  11. Dhruv, this was very helpful, and it worked the first time. :-)

    Thanks for posting it.

    ReplyDelete
  12. The servlet-context.xml stuff is out of date.

    It's correct on the original blog post
    http://tech.finn.no/2012/07/25/the-ultimate-view-tiles-3/4/

    Hopefully you find that the information you were missing originally on that article (that lead you to write this entry) has since been addressed.

    ReplyDelete
    Replies
    1. The article link was correct, but the information on tiles3 to spring-3.2 integration is on the second page:
           http://tech.finn.no/2012/07/25/the-ultimate-view-tiles-3/2/

      Delete
  13. why dont you put the source so atleast one can see and debug himself.

    ReplyDelete
  14. i deleted the last one, because it removed the code,.. here again :D

    Hi!!

    this worked awesome for me !!!!!!!

    thanks!!!

    i did some changes:
    1-I only needed the body, the header was on common too in my case.
    2- i added a third parameter in the tiles config */*/*:

    < definition name="*/*/*" extends="base" >
    < put-attribute name="body" value="/WEB-INF/views/{2}/{3}.jsp" / >
    < / definition >

    So i can use many jsp files in the same folder =D

    then the controller would return the jsp name too in this way:

    return new ModelAndView("/dreamer/registered");//for tiles
    >>"registered" is the name of the jsp =D (registered.jsp) under the folder "dreamer"

    4- i change the path of jsp files, where i had them at first ("views") this was the part that was removed from the servlet context... so i re-added it from the tiles.xml (i only change the path :D )

    ... /WEB-INF/tiles/template.jsp to /WEB-INF/views/template.jsp

    in this way (we can notice the header.jsp here):

    < definition name="base" template="/WEB-INF/views/template.jsp" >
    < put-attribute name="meta" value="/WEB-INF/views/common/meta.jsp" / >
    < put-attribute name="footer" value="/WEB-INF/views/common/footer.jsp" / >
    < put-attribute name="header" value="/WEB-INF/views/common/header.jsp" / >
    < / definition >



    thanks!!!!!!1

    ReplyDelete
  15. Excellent post!!! Java is most popular and efficient programming language available in the market today. It helps developers to create stunning desktop/web applications loaded with stunning functionalities. JAVA J2EE Training in Chennai | Best JAVA Training institute in Chennai

    ReplyDelete
  16. This technical post helps me to improve my skills set, thanks for this wonder article I expect your upcoming blog, so keep sharing...
    Regards,
    ccna training in Chennai|ccna courses in Chennai|ccna training center in Chennai

    ReplyDelete
  17. Cloud has beome the common word that is being used by most of the professional these day. The reason for relying on this technology is security. Your content too lecture the same. Thanks for sharing this worth able information in here. Keep blogging article like this.

    Hadoop Training Chennai | Big Data Training in Chennai
    | Best Hadoop Training in Chennai

    ReplyDelete