We were building a new web application recently, and being in a position to influence the development, I promptly chose to use the Maven2 web application directory structure. Spring is now our web application framework of choice, so that was a given. We have also had very good results with Tiles in our legacy web application, thanks to the efforts of one of my colleagues, so we also wanted to use Tiles here. I had set up a web application at my previous job to work with Spring and Tiles about 3 years ago, but the details were hazy, and the Tiles integration in our legacy application uses some proprietary components, so I decided to figure this out afresh for this project. Surprisingly, there does not seem to be much documentation about Spring/Tiles integration on the web, but I was able to build an example by piecing together information from various sources, which I describe here. Hopefully, the information will help someone in a similar situation.
We start off with a standard Spring application, with the web.xml containing a reference to the Spring DispatcherServlet, as shown below. The web.xml file lives in src/main/webapp/WEB-INF
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
<servlet>
<servlet-name>myapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myapp</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
</web-app>
|
The myapp-servlet.xml referenced by the web.xml above. The myapp-servlet.xml also lives in src/main/webapp/WEB-INF and is shown below. The myapp-servlet.xml sets up the TilesConfigurer with the location of the tiles configuration file (tiles-def.xml), sets up the Tiles view resolver, and specifies the URL mappings to the respective Spring controllers. It also imports non-web bean definitions from the applicationContext.xml in src/main/resources. This is a personal preference, since I like to be able to unit test the non-web components using JUnit, and breaking this up into a separate configuration file makes this easier.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.0.xsd">
<import resource="classpath:applicationContext.xml" />
<bean id="tilesConfigurer"
class="org.springframework.web.servlet.view.tiles.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/tiles-def.xml</value>
</list>
</property>
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="requestContextAttribute" value="requestContext"/>
<property name="viewClass"
value="org.springframework.web.servlet.view.tiles.TilesView"/>
</bean>
<!-- URL Mappings -->
<bean id="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="alwaysUseFullPath" value="true"/>
<property name="mappings">
<props>
<prop key="/example.html">exampleController</prop>
</props>
</property>
</bean>
</beans>
|
We then define the various tiles. Our example layout contains 4 tiles, one each for static content for the header and footer, one for the left navigation toolbar, and one for the main body of the page. The tiles-def.xml lives in src/main/webapp/WEB-INF and is shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd">
<tiles-definitions>
<!-- Components -->
<definition name="head-tile" path="/example/tiles/head.jsp"/>
<definition name="left-nav-tile" path="/example/tiles/leftnav.jsp"/>
<definition name="body-tile" path="/example/tiles/body.jsp"/>
<definition name="foot-tile" path="/example/tiles/foot.jsp"/>
<!-- Pages -->
<!-- Example -->
<definition name="example" path="/example/example.jsp">
<put name="head-position" value="head-tile"/>
<put name="left-nav-position" value="left-nav-tile"/>
<put name="body-position" value="body-tile"/>
<put name="foot-position" value="foot-tile"/>
</definition>
</tiles-definitions>
|
Currently the only dynamic component is the main body, which uses the "who" parameter to fill out the "Hello ${who}" header. All others are static. The example.jsp page appears below, followed by the different tiles. The locations are in the path attribute in the definitions in the tiles-def.xml file. The root is at src/main/webapp, so /example is actually src/main/webapp/example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <%-- src/main/webapp/example/example.jsp --%>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles" %>
<html>
<head><title>Example Page</title></head>
<body>
<table cellspacing="0" cellpadding="0" border="0">
<tr>
<td colspan="2">
<tiles:insert attribute="head-position"/>
</td>
</tr>
<tr>
<td width="25%">
<tiles:insert attribute="left-nav-position"/>
</td>
<td width="75%">
<tiles:insert attribute="body-position"/>
</td>
</tr>
<tr>
<td colspan="2">
<tiles:insert attribute="foot-position"/>
</td>
</tr>
</table>
</body>
</html>
|
I realize that using table tags to layout pages are kind of frowned upon nowadays, but if you have been reading my posts, you will realize that I am not exactly a UI guru. So please bear with me, and mentally replace the table tags with the appropriate CSS magic that is less offensive. The tiles are quite simple, and they are shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <!-- src/main/webapp/example/tiles/head.jsp -->
<h1>Da Korporate Header go here</h1>
<!-- src/main/webapp/example/tiles/leftnav.jsp -->
<ol>
<li>Foo</li>
<li>Bar</li>
</ol>
<!-- src/main/webapp/example/tiles/body.jsp -->
<h2>Hello ${who}</h2>
... body text filler ...
<!-- src/main/webapp/example/tiles/foot.jsp -->
<h1>Da Korporate Footer go here</h1>
|
So in effect, tiles have given us the ability to reuse JSP snippets on different pages. It is quite likely that the header and footer tiles, and perhaps the left nav tile, will be used across the entire application. So we need to create new tiles for only the body element for different applications.
The controller that backs this is referenced as exampleController in the myapp-servlet.xml and defined more fully in the applicationContext.xml file in src/main/resources. This file currently contains only the controller definition, but could be used to declare beans that the controller(s) depend on as well.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.0.xsd">
<!-- ExampleController -->
<bean id="exampleController" class="com.mycompany.myapp.example.ExampleController">
<property name="viewName" value="example"/>
</bean>
</beans>
|
The actual Java code is quite simple. All it does is pick up the parameter "who" from the URL, and pass it through to the view as a ModelAndView attribute. The java tree is rooted at src/main/java, in case you did not already know.
1 2 3 4 5 6 7 8 9 10 11 12 | public class ExampleController extends ParameterizableViewController {
public ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response) throws Exception {
String who = ServletRequestUtils.getStringParameter(request, "who");
ModelAndView mav = new ModelAndView();
mav.addObject("who", (who == null ? "NULL" : who));
mav.setViewName(getViewName());
return mav;
}
}
|
Start up the web application from the command line with "mvn jetty6:run" and hit the URL: http://localhost:8081/myapp/example.html?who=Sujit to see the following page:
We can also make certain tiles "smarter", in the sense that the Java logic backing these components need not be supplied by the main Spring controller, but can be specified separately. This can be useful when designing widgets for your web pages, which need to do significant processing on the request parameters before rendering the output. We could also refactor the logic out to some kind of service and have the main controller make a single call into it to get the renderable data, but this still means that we have to remember to pull data for each component in every new page controller we write. Specifying a controller for a tile is done in the controllerClass attribute in the tiles definitions.
For our example, we will make the left nav component smart. Depending on the value of the parameter "type" in the URL, it will display different lists. This requires specifying the controllerClass in the tiles definition for left-nav-tile in tiles-def.xml.
1 2 | <definition name="left-nav-tile" path="/example/tiles/leftnav.jsp"
controllerClass="com.mycompany.myapp.example.LeftNavController"/>
|
The controller is not a Spring controller, but a Tiles Controller. The code for that is shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public class LeftNavController extends ControllerSupport {
private String[][] menuItems = {
new String[] {"Foo", "Bar"},
new String[] {"London", "New York", "San Francisco", "Brussels"},
new String[] {"Engineering", "Finance", "Marketing"}
};
public void execute(ComponentContext tileContext,
HttpServletRequest request,
HttpServletResponse response,
ServletContext servletContext) throws Exception {
// decide what kind of menu to show based on parameter "type"
String menuTypeStr = (String) tileContext.getAttribute("type");
int menuType = 0;
try {
menuType = Integer.parseInt(menuTypeStr);
} catch (NumberFormatException e) {}
if (menuType < 0 || menuType > (menuItems.length - 1)) {
menuType = 0;
}
String[] selectedMenuItem = menuItems[menuType];
request.setAttribute("menu", selectedMenuItem);
}
}
|
One small wrinkle. The type parameter has to be injected into the tile context for the left-nav tile. This is done by setting it in the layout example.jsp file from the request using a tiles:put element, like so:
1 2 3 4 5 6 7 8 9 10 11 12 | <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles" %>
<html>
...
<td width="25%">
<tiles:insert attribute="left-nav-position">
<tiles:put name="type" value="${param.type}"/>
</tiles:insert>
</td>
...
</html>
|
The corresponding tile leftnav.jsp in src/main/webapp/example/tiles also has to be modified to show the "menu" object we just pushed into the context using the LeftNavController. Here it is:
1 2 3 4 5 6 7 | <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<ol>
<c:forEach var="menuItem" items="${menu}">
<li>${menuItem}</li>
</c:forEach>
</ol>
|
Now, hitting the application with http://localhost:8081/aetna/example.html?who=Sujit&type=1 will produce the following page, which shows us that the type parameter is being correctly interpreted.
So this is it. The whole thing is not terribly complicated, but requires you to mess with a lot of XML files. The good news is that working with Tiles can significantly speed up your web application development and make it easier, as well as enforce a uniform look and feel to your web application. And once you set it up, and developers get used to the process of adding components and layouts, it will just become second nature and you will wonder how you worked without Tiles.
while i am trying to compail this code on eclipse in class ExampleController it giveing error as "ServletRequestUtils cannot be resolved".....
ReplyDeleteeven i imported org.springframework.web.bind.ServletRequestUtils
but erro remains same...
could u help me out plz...
regards,
gopi
If you have Spring-2.0 jars this should be available. The fact that Eclipse allowed you to resolve the ServletRequestUtils object automatically. Looking at the source for ServletRequestUtils shows me that it needs to import javax.servlet.ServletRequest, so I think you need the servlet API jar and an appropriate impl. I use geronimo-j2ee-1.4_spec which is what is recommended with Maven2.
ReplyDeleteHi,
ReplyDeleteI was trying to implement the example and i was getting the below mentioned error
java.lang.NullPointerException
org.apache.struts.taglib.tiles.InsertTag.processAttribute(InsertTag.java:687)
org.apache.struts.taglib.tiles.InsertTag.createTagHandler(InsertTag.java:478)
org.apache.struts.taglib.tiles.InsertTag.doStartTag(InsertTag.java:438)
org.apache.jsp.WEB_002dINF.jsp.AccDetails.addEmp_jsp._jspx_meth_tiles_005finsert_005f0(addEmp_jsp.java:329)
org.apache.jsp.WEB_002dINF.jsp.AccDetails.addEmp_jsp._jspService(addEmp_jsp.java:147)
org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:98)
javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:328)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:315)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:265)
javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:142)
org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:247)
org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1103)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:840)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:754)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:399)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:364)
javax.servlet.http.HttpServlet.service(HttpServlet.java:710)
javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
Can you please help me out plz.
Sorry Sajad, but I don't think thats very feasible. Also from the stack trace I think the problem is coming from AccDetails.jsp, which is not among the files I have in the example, so its kinda hard for me to figure out the flow as well.
ReplyDeleteIt would be really helpful if you would provide the complete code for this project. Seems really useful but very tedious to set up especially if you are trying to learn these concepts for the first time! Even just figuring out the import statments is tiresome. Otherwise, great article!
ReplyDeleteThanks for the compliments, prumps. Currently, I don't have a way to provide downloadable code, but I try to make up for it by providing all the files that make up the implementations I talk about. I understand your point about missing imports though, I did this originally to save space, but I will start including them in future posts.
ReplyDeleteWhen i run this example, the body.jsp doesn't print out the parameter "who".
ReplyDeletein the jsp i included the jstl taglib and then did c:out value="${who}"
any pointers?
This is an extension to my prev post.
ReplyDeleteNice article!!!
But when i tried the example the body.jsp was not spitting out the parameter "who"
I had to include the JSTL taglib even in body.jsp to make this run. How to remove that limitation?
Hi Seshu, thanks for the comment. I am not a JSP/JSTL expert, but I don't think you can remove the include call in each JSP. A colleague at work has established a convention that we put all our JSTL include statements in one single file and then include that one file in every JSP.
ReplyDeleteThank you! The article was very helpful. Unfortunately there are not many resources on Spring/Tiles integration.
ReplyDeleteHi, thanks for the comment, glad it helped.
ReplyDeleteAn alternative approach to the 1 tile per 1 controller is to have two tile definitions for the left nav, one for the tile and the other to the leftNavController (Spring - extends AbstractController):
ReplyDeletedefinition name="left-nav-tile" path="/example/tiles/leftnav.jsp"
definition name="left-nav-module" path="/leftNav.do"
Then in the main example definition you put:
put name="left-nav-position" value="left-nav-module"
DNM
Hi, thanks for the comment.
ReplyDeleteTo use tiles 2.0.6 with Spring 2.5, use tiles2 package from spring ...
ReplyDeletebean id="tilesConfigurer"
class="org.springframework.web.servlet.view.tiles2.TilesConfigurer"
and ...
bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver
also...
property name="viewClass"
value="org.springframework.web.servlet.view.tiles2.TilesView"
Just simply a great simple but detail enough tutorial about Tiles! One really needs just this to get to know Tiles and further.
ReplyDeleteGreate thanks.
Hi, thank you for your comment, glad it helped you.
ReplyDeletehi,
ReplyDeletei am new to spring tiles concept.
i was typed as u mention belongings file.Once i run the appllication i am getting error like below.
HTTP Status 404 - Servlet myapp is not available
--------------------------------------------------------------------------------
type Status report
message Servlet myapp is not available
description The requested resource (Servlet myapp is not available) is not available.
--------------------------------------------------------------------------------
Apache Tomcat/5.0.28
plz solve my pbm.
regards
Bala
Hi Bala, I am not sure whats wrong, but I would guess that myapp is not declared correctly in web.xml.
ReplyDeleteHTH
Hi Sujit,
ReplyDeleteI am getting problem when i am using two view resolvers,one for my controllers and one for my tiles.
When I tries to call tile from my main .jsp page I always get "javax.jsp.servlet.JspException:TilesContainer not initialized".
Do you have any idea of its cause?
I am using spring2.0 and tiles2.0 with my tiles-def file entry is like
definition name="partner-links" template="/tiles/cstemplates/partnerlinks.jsp"
Hi Mohit, when I've used the approach, it was either a full tiled app or a full untiled app, never a mix of the two. But I haven't been doing webapp development for a while, so I may not be the best person to ask :-). I am hoping that some of the other readers may be able to provide you an answer.
ReplyDeleteHi, there was a question on this blog (which for some strange reason did not show up even though I published it from blogger, maybe its waiting in a publish queue somewhere and will show up sometime later), but anyway, the question was whether the viewName defined for the "exampleController" bean referred to the tile definition "example" or the jsp example.jsp. The answer is that it refers to the tile definition "example".
ReplyDeletethanks for your tutorial.
ReplyDeleteI have question, I have configured tiles for my project which has around 20 JSPs. I am facing two issues,
1. In tiles-def.xml. 'definition' tag needs full path of jsp for name and path attribute
<definition name="/WEB-INF/jsp/project.jsp" path="/WEB-INF/jsp/project.jsp" >
Can i replace this with view or something else ? how to do that? how can you do that with view name?
2. Tiles require me to define all my JSPs in tiles-def.xml, eventhough i am not using tiles in few JSPs (these jsps has different view than rest of the site).
how can i avoid that?
pls advice!
Hi Leegurus, you are welcome. To reply to your questions, for (1), I am not sure if you can use a view name instead of the full path name, and perhaps this may not even be something desirable, since then you will have to keep the mapping someplace else. With the approach described, you can refer to the tiles by their names in tiles-def.xml. (2) To handle tiles and plain JSPs in the same app, you can have a chain of view resolvers, one for tiles, followed by the one for plain JSPs. I've seen it done before, but its been a while, so details are hazy.
ReplyDeleteCould you please put what are the jars we need to build this.
ReplyDeleteEspecially I am struck with tiles jar. which one to use? is it struts tiles or apache tiles.
@Anonymous: In my apps we use struts-1.29 (because it happened to be lying around from another app that needs it), but we use this only for its tiles. I no longer remember which app this post describes, but here is a list of jar files which I pulled from a similar looking app. Hope this helps you: commons-lang-2.1, commons-logging-1.0.4, servlet-api-2.4, log4j-1.2.8, taglibs-standard-1.1.0, taglibs-string-1.1.0, jstl-1.1.2, springframework-2.0, and some other jars for specific functionality (not related to general webapp).
ReplyDeleteHi Sajid,
ReplyDeleteI m getting the following error while executing the code,
SEVERE: Context initialization failed
org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [org.springframework.web.servlet.view.tiles.TilesConfigurer] for bean with name 'tilesConfigurer' defined in ServletContext resource [/WEB-INF/myapp-servlet.xml];
nested exception is java.lang.ClassNotFoundException: org.springframework.web.servlet.view.tiles.TilesConfigurer
Can you please suggest me which libraries are needed to include this class.
Thanks in advance.
@indorama: thanks, glad it helped you.
ReplyDelete@anonymous: it looks like you are missing one of spring jar files. In the old days, they used to have one jar file but post 2.5 I think, they have multiple files. I am guessing you are missing the one that deals with web applications.
Eventhough the article is dated in 2007, this is the first google result for "spring tiles integration samples" well this means ur link is most accessed orrr no body is actualy working on spring with tiles? Anyways a nice article. Download link missing though, but still can manage!
ReplyDelete-- Anoop
Thanks Anoop. I suspect the latter :-).
ReplyDeleteHi,
ReplyDeleteI was traing to implement the example and i was getting the bellow mentioned error:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'tilesConfigurer' defined in ServletContext resource [/WEB-INF/tilesTest-servlet.xml]: Invocation of init method failed; nested exception is java.lang.UnsupportedOperationException: Class org.apache.tiles.web.util.ServletContextAdapter not recognized a TilesApplicationContext
Some Help? Thnks
Hi Anonymous, hard to tell whats going wrong, but I would guess that since it is dying on the init method, it is not able to parse the tiles-def.xml correctly, possibly because the Tiles JAR file is not in the classpath. I believe its now part of the Spring3 distribution, so you just need to add it (it has tiles in the name) to your WEB-INF/lib directory, but if you are using an older version, try the struts.jar file, that contains Tiles classes in it.
ReplyDeletehi, can you please email me the working project please.. i got problem following it so would be helpful if I see the project. jemrusalem07@yahoo.com. thanks!
ReplyDeleteSorry BlogManTree, once I had the proof of concept working (thats the one I wrote about), I handed it off to someone at work, and he built the app around this idea. I don't have the POC app anymore since I did not need it.
ReplyDeleteHey Sujit,
ReplyDeleteNice to read your blog. Was a refresher for Spring-Tile integration.
Thanks Nilay, glad it was helpful.
ReplyDeleteIf anyone is not planning to use maven, then a complimentary article to this one can be seen on my blog post on spring and tiles
ReplyDeleteBut still I would recommend using maven as it makes it simple but just in case...
Thanks for doing this and sharing the link, this will definitely be useful for people who prefer not to use mvn.
ReplyDelete