Wednesday, April 8, 2009

Struts 2, Apache Tiles 2 - View Preparer - Dynamic Menu Creation

Let us develop as page using Struts 2, Apache Tile and Spring. This page has the following tiles – Header, Menu, Body and Footer. Let us have our Menu which is dynamic in nature which gets all menu items from database. So, we will use View Preparer to prepare the Menu before rendering.

Our tile definition file (tiles.xml) would look like

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN"
"http://jakarta.apache.org/struts/dtds/tiles-config_2_0.dtd">

<tiles-definitions>
<definition name="test.homepage" template="/htdocs/layouts/homepage.jsp">
<put-attribute name="logo" value="/htdocs/tiles/logo.jsp" />
<put-attribute name="menu" value="test.menu" />
<put-attribute name="body" value="/htdocs/tiles/body.jsp" />
<put-attribute name="footer" value="/htdocs/tiles/footer.jsp" />
</definition>

<definition name="test.menu" preparer="menuPreparer" template="/htdocs/tiles/menu.jsp" />

</tiles-definitions>

I have associated a preparer “menuPreparer’ to the tile definition for the menu. “menuPreparer” is the bean defined in applicationContext.xml below. Our bean definition file (applicationContext.xml) looks like

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">

<context:annotation-config />

<bean id="menuService" class="test.MenuService"
/>

<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/tiles.xml</value>
</list>
</property>
<property name="preparerFactoryClass" value="org.springframework.web.servlet.view.tiles2.SpringBeanPreparerFactory"/>
</bean>

<bean id="menuPreparer" class="test.MenuViewPreparer"
/>
</beans>

Here, we have defined a bean test.MenuViewPreparer whose id “menuPreparer” is defined in tiles.xm above.

test.MenuViewPreparer implements ViewPreparer interface whose execute method is called before tile is rendered. In this execute method we will get the list of all the menus to be displayed. We will use the service test.MenuService to get the list of all menu items. This service can get all the menus from database. This service (bean) is defined in the applicationContext above.

Now we have to inject menuService to test.MenuViewPreparer. So, how do I do this?

I will use annotations to configure Spring’s dependency injection. So, I have <context:annotation-config /> in my applicationContext.xml.

This is my test.MenuViewPreparer

package test;

import java.util.List;

import org.apache.tiles.Attribute;
import org.apache.tiles.AttributeContext;
import org.apache.tiles.context.TilesRequestContext;
import org.apache.tiles.preparer.PreparerException;
import org.apache.tiles.preparer.ViewPreparer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class MenuViewPreparer implements ViewPreparer {
@Autowired
private IMenuService menuService;

private List<Menu> menuList;

public IMenuService getMenuService() {
return menuService;
}

public void setMenuService(IMenuService menuService) {
this.menuService = menuService;
}

public List<Menu> getMenuList() {
return menuList;
}

public void setMenuList(List<Menu> menuList) {
this.menuList = menuList;
}

public void execute(TilesRequestContext tilesContext,
AttributeContext attributeContext) throws PreparerException {
menuList = menuService.getMenuList();
attributeContext.putAttribute("menuItems", new Attribute(menuList));
System.out.println(menuList);

}

}

Here I have used @Service and @Autowired. @Autowired is used to autowire the dependency of the MenuViewPreparer on the IMenuService. Here is the IMenuService interface.

package test;

import java.util.List;

public interface IMenuService {

List<Menu> getMenuList();

}

Here is test.Menu

package test;

public class Menu {
private String menuName;

private String menuURL;

public Menu(String menuName, String menuURL) {
super();
this.menuName = menuName;
this.menuURL = menuURL;
}

public String getMenuName() {
return menuName;
}

public void setMenuName(String menuName) {
this.menuName = menuName;
}

public String getMenuURL() {
return menuURL;
}

public void setMenuURL(String menuURL) {
this.menuURL = menuURL;
}
}

At last let us have a look at homepage.jsp and menu.jsp. This is my homepage.jsp

<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<html>
<head>
<title>Test</title>
</head>

<body>
<div id="main">
<tiles:insertAttribute name="logo"/>
<tiles:insertAttribute name="menu"/>
<tiles:insertAttribute name="body"/>
<tiles:insertAttribute name="footer"/>
</div>
</body>
</html>

menu.jsp looks like –

<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>

<tiles:importAttribute name="menuItems" />
<c:forEach var="item" items="${menuItems}">
<a href="${item.menuURL}"> ${item.menuName}</a>

</c:forEach>

<br><br>

Here I have used JSTL to display the menu items. Test.MenuViewPreparer’s execute method puts the attribute menuItems in attributeContext. So, on JSP I get this attribute and display the menu (name and URL) on the page.

Libraries used –

Apart from Struts2-core, xwork, tiles-api, tiles-core these jars are required

spring.jar (Spring 2.5 and above)
JSTL jars - standard.jar, jstl.jar

Note – You do not have to initialize “StrutsTilesListener” in your web.xm. As we are using Spring to load the tile definitions.

11 comments:

Anuj said...

which Tiles version are you using 2.0 or 2.1?

Anuj said...

what tiles2 version are you using 2.0 or 2.1

Pranav Kumar Varshney said...

Tiles 2.0.5

beku said...

man this is awesome, However I can't get it working :( I understood what's really going on with your code and followed it but I got javax.servlet.jsp.JspException: Error - tag importAttribute : no tiles context found.
I defined my viewPreparor implementing bean in my servlet.xml do you think it's the problem. Anything else is almost same. Can you upload this project somewhere so you can be my(and many others') hero?

beku said...

Oh and by the way, I am using spring 3.0 and tile 2.2 there might be a compatibility issue

beku said...

ok I got it working. I really did stupid mistake.instead of <%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %> I used struts tag lib. My bad. You rock man tnx

TripleDrops said...

I am using Spring 3 and Tiles 2.1. ViewPreparer was instantiated by Spring correctly, but its execute method is not being invoked. Any ideas?

TripleDrops said...

Forgot to mention, I am using Struts 2 also.

Henry said...

I have the same problem, I'm using Struts 2.1.8, Spring 3, Tiles 2.2.2 and the execute method of the ViewPreparer is never executed, it seems that Tiles can't see the viewPreparer bean created by Spring... Any ideas about this problem?

chandrasekhar said...

Hi praveen its wonderful blog.Its help me alot.

Unknown said...

great article. would be nice to use the "code" tag though !!!