Using Tiles

Unlike all the previous view technologies, Tiles is not actually used to generate the output. Instead, it can combine the output from various views into a master view. Each individual tile can consist of a collection of tiles, a JSP page, or output directly written to the HttpResponse object in the handleRequest() method of a Spring controller.

Tiles is a part of the Jakarta Struts project (http://struts.apache.org/). At the time of writing, the latest version of Struts was 1.2.4. Do not forget to include the Struts library (struts.jar) in your Spring project.

Integrating Tiles and Spring

Tiles requires some initialization to be performed. In a Struts application, the initialization is performed when Tiles is loaded as a plugin; in Spring, we need to create a TilesConfigurer bean that loads the tile definitions XML file and configures the Tiles framework, as shown in Listing 18-25.

Listing 18-25: TilesConfigurer Definition in Application Context

image from book
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" ¿ "http://www.springframework.org/dtd/spring-beans.dtd">      <beans>     <bean bold">tilesConfigurer"          bold">org.springframework.web.servlet.view.tiles.TilesConfigurer">         <property name="definitions">             <list>                 <value>/WEB-INF/tiles-layout.xml</value>             </list>         </property>     </bean> </beans>
image from book

This bean creates the Tiles DefinitionsFactory instance using the definition files listed in the definitions property of the TilesConfigurer bean.

Before we can use Tiles in our application, we must create the tiles-layout.xml file. We start with a very simple file (see Listing 18-26) that simply verifies that the Tiles support is properly configured in our Spring application.

Listing 18-26: Simple Tiles Definition File

image from book
<?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>     <definition name=".dummy"/> </tiles-definitions>
image from book

When we build and deploy the application, the application server's console prints log messages like the following from the TilesConfigurer class to indicate that the Tiles support has been correctly configured.

21:07:58,001 INFO  [TilesConfigurer] TilesConfigurer: initializion started 21:07:58,011 INFO  [TilesConfigurer] TilesConfigurer: adding definitions ¿ [/WEB-INF/tiles-layout.xml] 21:07:58,211 INFO  [TilesConfigurer] TilesConfigurer: initialization completed

Now that we have verified that the Tiles support is working, we need to think about the tiles we want to use in our application. A typical layout of a web page might look like the one shown in Figure 18-5. We are going to call this page a root page. It is a JSP page that uses the Tiles tag library to insert the appropriate tiles according to their definitions in the tiles configuration files.

image from book
Figure 18-5: Tiles layout

Looking at this layout, you might think that we are going to create five tiles—one for each section of the final page layout. In fact, we are going to add another tile, which is going to output the content of the <meta> tag in the root layout.

Let's begin by creating the root.jsp page that places all the tiles into an HTML table, as shown in Listing 18-27.

Listing 18-27: The root.jsp Page

image from book
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%> <%@taglib prefix="tiles" uri="http://jakarta.apache.org/struts/tags-tiles"%> <%@taglib prefix="c" uri="http://java.sun.com/jstl/core"%>      <html> <head>     <c:set var="css"><spring:theme code="css"/></c:set>        <c:if test="${not empty css}">             <link rel="stylesheet" href="<c:url value="${css}"/>"                  type="text/css" />         </c:if>     <tiles:insert attribute="meta"/>     <title><tiles:getAsString name="title"/></title> </head>      <table cellspacing="0" cellpadding="0" width="700px" align="center"      bgcolor="#ffffff">     <tr>         <td colspan="2"><tiles:insert attribute="header"/></td>     </tr>     <tr>         <td colspan="2"><tiles:insert attribute="toolbar"/></td>     </tr>     <tr height="400px">         <td width="150px" valign="top">  <tiles:insert attribute="menu"/></td>         <td width="550px" valign="top">  <tiles:insert attribute="body"/></td>     </tr>     <tr>         <td colspan="2"><tiles:insert attribute="footer"/></td>     </tr> </table>      </body> </html>
image from book

The root layout page is quite straightforward: we define the layout of the page, and we use Tiles tags to specify where Tiles should insert the appropriate pages. However, the Tiles framework still does not know what content to insert for all <tiles:insert> and <tiles:getAsString> tags. To solve this problem, we need to create the Tiles definition file, as shown in Listing 18-28.

Listing 18-28: Tiles Definition File

image from book
<?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>     <!-- Abstract root definition -->     <definition name=".root" path="/WEB-INF/views/tiles/root.jsp">         <put name="title" value="CHANGE-ME"/>         <put name="meta" value="/WEB-INF/views/tiles/meta.jsp"/>         <put name="header" value="/WEB-INF/views/tiles/header.jsp"/>         <put name="menu" value="/WEB-INF/views/tiles/menu.jsp"/>         <put name="toolbar" value="/WEB-INF/views/tiles/toolbar.jsp"/>         <put name="footer" value="/WEB-INF/views/tiles/footer.jsp"/>     </definition>          <!-- Index -->     <definition name=".index" extends=".root">         <put name="title" value="Main Page"/>         <put name="body" value="/WEB-INF/views/index.jsp"/>     </definition> </tiles-definitions>
image from book

This definition file introduces a number of Tiles concepts. Let's go through the features used here, line by line. The first definition element's attribute name is set to .root, and the element also includes the path attribute. This instructs Tiles to use the JSP page specified in the path attribute and to use the values specified in the put elements to display their content. However, the .root definition is missing the body attribute we are using in the root.jsp page. The definition we created is an abstract definition because it does not define all the attributes used. We must extend this abstract definition to ensure that all the necessary attributes are defined, as shown in the second definition element. Its name attribute is set to .index and its extends attribute specifies that it should inherit all the values in the put elements from the definition element whose name is .root. It overrides the title value and adds the body value— just as you would expect in Java code.

Before we can move ahead and configure the Spring views to use Tiles, we need to stress that the individual pages that are used as tiles must not include the standard HTML headers. However, if you are using JSP pages, they must include all taglib references. The requirement for no document HTML tags is obvious because the document tags are already included in the root page. The taglibs must be included in the JSP pages because Tiles requests each tile individually—the tiles are not actually aware that the output they are rendering is being collected by another layer and formatted using the root layout.

Now that we know how what the requirements are for individual tiles, we must configure the Spring views to use the Tiles framework. To do this, we are going to modify the views.properties file, as shown in Listing 18-29.

Listing 18-29: views.properties Definition

image from book
#index index.class=org.springframework.web.servlet.view.tiles.TilesJstlView index.url=.index
image from book

This file defines that the view name index is going to be created as an instance of TilesJstlView and its url is going to be .index.

Note 

You may be wondering why we are using dots (.) in the Tiles definition names and no dots in the view names. This is simply a practice we find useful when managing applications with a large number of views and Tiles definitions. By looking at the names, you can immediately identify which one is a tile and which one is a Spring view.

The last step we need to do before we can test our application (shown in Listing 18-30) is to make sure that the IndexController's handleRequestInternal() method is returning the correct view.

Listing 18-30: IndexController Class

image from book
package com.apress.prospring.ch18.web;      public class IndexController extends AbstractController {          protected ModelAndView handleRequestInternal(HttpServletRequest request,          HttpServletResponse response) throws Exception {                  setCacheSeconds(10);         Map<String, Object> model = new HashMap<String, Object>();         model.put("Greeting", "Hello World");         model.put("Server time", new Date());                  return new ModelAndView("index", model);     }      }
image from book

When we now deploy the application and make a request to the /index.html page, the TilesConfigurer bean parses the Tiles configuration, loads the .root and .index definitions, calls each JSP page, renders their output, and then finally takes the JSP output and outputs it into the appropriate places in the root.jsp page, whose output is returned to the client, as shown in Figure 18-6.

image from book
Figure 18-6: The .root Tiles view

Advanced Tiles Concepts

In the previous section, we showed you how to use Tiles in your Spring application in a way that is not too different from the @include JSP directive. The true power of Tiles comes from the fact that Tiles can take any output and paste it into the appropriate place. A tile can consist of other tiles, JSP pages, or even simple output from a controller. We start with a tile whose content is the output written to the response stream by a simple controller.

First, we create a tile that prints out the memory usage information directly to the HttpServletResponse's Writer object. We create another servlet mapping in web.xml to map all *.tile requests to the ch18 servlet. The only reason for this is to keep the request namespace clean of any ambiguous request URLs. The modified web.xml file is shown in Listing 18-31.

Listing 18-31: web.xml Descriptor

image from book
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC      "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"      "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app>     <!-- omitted for clarity -->     <servlet-mapping>         <servlet-name>ch18</servlet-name>         <url-pattern>*.tile</url-pattern>     </servlet-mapping> </web-app>
image from book

Next, we create a TileController that is a subclass of MultiActionController. We implement only one method in the TileController, the handleStatus(), which prints out the memory information. Listing 18-32 shows that this implementation is quite trivial.

Listing 18-32: TileController.handleStatus Implementation

image from book
package com.apress.prospring.ch18.web.tiles;      import java.io.PrintWriter; import java.lang.management.ManagementFactory; import java.lang.management.MemoryPoolMXBean; import java.util.List;      import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;      import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.multiaction.MultiActionController;      public class TileController extends MultiActionController {          private void writeMemoryPoolMXBean(MemoryPoolMXBean bean,          PrintWriter writer) {         writer.append("<pre><tt>");                  writer.append("Name: "); writer.append(bean.getName());          writer.append("\n");         writer.append("Type: "); writer.append(bean.getType().name());          writer.append("\n");         writer.append("Usage: "); writer.append(bean.getUsage().toString());          writer.append("\n");                  writer.append("</pre></tt>");     }               public ModelAndView handleStatus(HttpServletRequest request,         HttpServletResponse response) throws Exception {         List<MemoryPoolMXBean> beans =                  ManagementFactory.getMemoryPoolMXBeans();         PrintWriter writer = response.getWriter();         for (MemoryPoolMXBean bean : beans) {             writeMemoryPoolMXBean(bean, writer);         }         return null;     }      }
image from book

Notice that the handleStatus() method returns null, which means that Spring does not attempt to perform any view processing.

Next, we declare the tileController bean in the application context file together with a tileMethodNameResolver bean and an entry in the publicUrlMapping bean, as shown in Listing 18-33.

Listing 18-33: The tileController and tileMethodNameResolver Beans

image from book
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" ¿ "http://www.springframework.org/dtd/spring-beans.dtd">      <beans>     <bean          >         <property name="mappings">             <props>                 <prop key="/index.html">indexController</prop>                 <prop key="/product/index.html">productController</prop>                 <prop key="/product/view.html">productController</prop>                 <prop key="/product/edit.html">productFormController</prop>                 <prop key="/product/image.html">productImageFormController</prop>                                  <prop key="/tile/*.tile">tileController</prop>             </props>         </property>     </bean>          <!-- Tile -->     <bean bold">tileController"          >         <property name="methodNameResolver">             <ref local="tileMethodNameResolver"/>         </property>     </bean>     <bean bold">tileMethodNameResolver"          symbol">¿ PropertiesMethodNameResolver">         <property name="mappings">             <props>                 <prop key="/tile/status.tile">handleStatus</prop>             </props>         </property>     </bean> </beans>
image from book

We test that our tileController works by making a request to /ch18/tile/status.tile. This prints the JVM memory status information.

Finally, we create a StatusController as a subclass of AbstractController, add it to the application context file, and add an entry to the publicUrlMapping bean to map /status.html to the StatusController, as shown in Listing 18-34.

Listing 18-34: The statusController Bean Definition and a New Entry in the publicUrlMapping Bean

image from book
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" ¿ "http://www.springframework.org/dtd/spring-beans.dtd">      <beans>     <bean          >         <property name="mappings">             <props>                 <prop key="/index.html">indexController</prop>                 <prop key="/status.html">statusController</prop>                 <prop key="/product/index.html">productController</prop>                 <prop key="/product/view.html">productController</prop>                 <prop key="/product/edit.html">productFormController</prop>                 <prop key="/product/image.html">productImageFormController</prop>                                  <prop key="/tile/*.tile">tileController</prop>             </props>         </property>     </bean>     <bean bold">statusController"          /> </beans>
image from book

The code in the StatusController.handleRequestInternal() method simply returns an instance of ModelAndView("status"). This means that we need to add an entry to the tiles-layout.xml file and views.properties, as shown in Listing 18-35.

Listing 18-35: Additions to tiles-layout.xml and views.properties

image from book
 tiles-layout.xml: <?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>     <!-- other definitions omitted -->          <definition name=".status" extends=".root">         <put name="title" value="Status"/>         <put name="body" value="/tile/status.tile"/>     </definition> </tiles-definitions>      views.properties: status.class=org.springframework.web.servlet.view.tiles.TilesJstlView status.url=.status
image from book

When we make a request to /ch18/status.html, Spring instantiates the status view defined in views.properties. This view points to the .status tile definition, which specifies that the value for the body element should be taken from the output generated by /ch18/tile/status.tile. The final result may look like what appears in Figure 18-7.

image from book
Figure 18-7: Request to /ch18/status.html

You would rarely use a controller that prints out the output directly to the output stream; in most cases, a controller returns a ModelAndView that identifies a view and data to be rendered by the view. You can use this approach in a Tiles application. To demonstrate this, we add the handleMenu() method to the TileController, which is going to read the links from the configuration file and render them using a JSP page.

First, we modify the TileController, as shown in Listing 18-36.

Listing 18-36: Modified TileController

image from book
public class TileController extends MultiActionController {          public ModelAndView handleMenu(HttpServletRequest request,          HttpServletResponse response) throws Exception {         return new ModelAndView("tile-menu", "menu", menu);     }          public void setMenu(Map menu) {         this.menu = menu;     } }
image from book

The handleMenu() method takes the menu property set in the application context file and forwards it to the tile-menu view, which is defined as JstlView in views.properties, as shown in Listing 18-37.

Listing 18-37: views.properties Contents

image from book
#index index.class=org.springframework.web.servlet.view.tiles.TilesJstlView index.url=.index      #status status.class=org.springframework.web.servlet.view.tiles.TilesJstlView status.url=.status      #menu tile tile-menu.class=org.springframework.web.servlet.view.JstlView tile-menu.url=/WEB-INF/views/tiles/menu2.jsp
image from book

Listing 18-38 shows that the JSP page that displays the menu is quite trivial; it uses the core JSTL tags to iterate over all items in a map.

Listing 18-38: The tile/menu2.jsp Page

image from book
<%@taglib prefix="c" uri="http://java.sun.com/jstl/core"%>      <c:forEach items="${menu}" var="item">     <a href="<c:out value="${item.value}"/>"><c:out value="${item.key}"/></a><br> </c:forEach> 
image from book

Finally, in Listing 18-39, we modify the tiles-layout.xml and the ch18-servlet.xml files to use the newly created menu.

Listing 18-39: The tiles-layout.xml and ch18-servlet.xml Files

image from book
 tiles-layout.xml: <tiles-definitions>     <!-- Abstract root definition -->     <definition name=".root" path="/WEB-INF/views/tiles/root.jsp">         <put name="title" value="CHANGE-ME"/>         <put name="meta" value="/WEB-INF/views/tiles/meta.jsp"/>         <put name="header" value="/WEB-INF/views/tiles/header.jsp"/>         <put name="menu" value="/tile/menu.tile"/>         <put name="toolbar" value="/WEB-INF/views/tiles/toolbar.jsp"/>         <put name="footer" value="/WEB-INF/views/tiles/footer.jsp"/>     </definition>     <!-- other definitions omitted --> </tiles-definitions>      ch18-servlet.xml: <beans>     <bean           >         <property name="methodNameResolver">             <ref local="tileMethodNameResolver"/></property>         <property name="menu">             <map>                 <entry key="Apress">                     <value>http://www.apress.com</value></entry>                 <entry key="Spring">                     <value>http://www.springframework.org</value></entry>                 <entry key="Cake Solutions">                     <value>http://www.cakesolutions.net</value></entry>             </map>         </property>     </bean>     <bean           symbol">¿ PropertiesMethodNameResolver">         <property name="mappings">             <props>                 <prop key="/tile/status.tile">handleStatus</prop>                 <prop key="/tile/menu.tile">handleMenu</prop>             </props>         </property>     </bean> </beans> 
image from book

Because we modified the .root tile definition, we can use our new menu in all the definitions that extend the .root definition. You can see the newly displayed menu in Figure 18-8.

image from book
Figure 18-8: New menu tile

Tiles Best Practices

Tiles is a very powerful framework that can greatly simplify the development of web applications. With its power comes the complexity of configuration files and different request paths. Because of this power, it is important to make sure you use and enforce a logical naming convention in the entire project.

We feel that the best way to organize the requests URLs is to use *.html for the final pages returned to the client and *.tile for requests whose output is to be rendered as a tile. The view naming conventions follow the directory structure of the source files, so a Spring JstlView definition for a JSP page located in tile/menu.jsp is named tile-menu. And finally, a tile definition that you might use to render the output of the /product/index.html page is best named .product.index.

At the time of writing, there was only experimental support for Tiles Velocity views, so for the time being your only option is to use JSP pages as the source for individual tiles.



Pro Spring
Pro Spring
ISBN: 1590594614
EAN: 2147483647
Year: 2006
Pages: 189

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net