Content Inclusion in the Book Viewer

   

To include content in the book viewer, we split our monolithic JSF page into four files: the original JSF page, /header.jsp, /menu.jsp, and /content.jsp. We include the header, menu, and content in the original JSF page:

 

 <h:panelGrid columns="2" style     columnClasses="menuColumn, contentColumn">     <f:facet name="header">         <f:subview >             <c:import url="header.jsp"/>         </f:subview>     </f:facet>     <f:subview >         <c:import url="menu.jsp"/>     </f:subview>                <f:subview >          <c:import url="content.jsp"/>     </f:subview> </h:panelGrid> ... 

This code is much cleaner than the original JSF page listed in Listing 8-1 on page 322, so it's easier to understand, maintain, and modify. But more importantly, we are now free to reuse the header, menu, and content for other views. For example, to use the book-viewer with another book, all we have to do is change the book's titleKey, image, and numChapters properties in faces-config.xml.

The directory structure for the book viewer with includes example is shown in Figure 8-5. Listing 8-6 through Listing 8-9 show the JSF pages for the book, its header, menu, and content.

Listing 8-6. book-viewer-include/book.jsp
  1. <html>  2.    <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>  3.    <%@ taglib uri="http://java.sun.com/jsf/core"  prefix="f" %>  4.    <%@ taglib uri="http://java.sun.com/jsf/html"  prefix="h" %>  5.    <f:view>  6.       <head>  7.          <link href="styles.css" rel="stylesheet" type="text/css"/>  8.          <f:loadBundle basename="com.corejsf.messages" var="msgs"/>  9.          <title><h:outputText value="#{msgs.bookWindowTitle}"/></title> 10.       </head> 11.       <body> 12.          <h:form> 13.             <h:panelGrid columns="2" style 14.                    columnClasses="menuColumn, chapterColumn"> 15.                <f:facet name="header"> 16.                   <f:subview > 17.                      <c:import url="/bookHeader.jsp"/> 18.                   </f:subview> 19.                </f:facet> 20. 21.                <f:subview > 22.                   <c:import url="/bookMenu.jsp"/> 23.                </f:subview> 24. 25.                <f:verbatim> 26.                   <c:import url="/bookContent.jsp"/> 27.                </f:verbatim> 28.             </h:panelGrid> 29.          </h:form> 30.       </body> 31.    </f:view> 32. </html> 

Listing 8-7. book-viewer-include/bookHeader.jsp
 1. <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> 2. <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> 3. 4. <h:panelGrid columns="1" style> 5.    <h:graphicImage value="#{book.image}"/> 6.    <h:outputText value="#{msgs[book.titleKey]}" style/> 7.    <f:verbatim><hr></f:verbatim> 8. </h:panelGrid> 

Listing 8-8. book-viewer-include/bookMenu.jsp
  1. <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>  2. <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>  3.  4. <h:dataTable value="#{book.chapterKeys}" var="chapterKey"  5.         style columnClasses="linksColumn">  6.    <h:column>  7.       <h:commandLink>  8.          <h:outputText value="#{msgs[chapterKey]}"/>  9.          <f:param name="chapter" value="#{chapterKey}"/> 10.       </h:commandLink> 11.    </h:column> 12. </h:dataTable> 

Listing 8-9. book-viewer-include/bookContent.jsp
 1. <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %> 2. 3. <c:import url="${param.chapter}.html"/> 

Figure 8-5. The Directory Structure of the Book Viewer with Includes

graphics/08fig05.jpg


Looking at Tiles

We've seen how to encapsulate and include content and how that strategy increases flexibility it's much easier to reuse content if you include it rather than mixing it all in one file. Now that you can create user interfaces with pluggable content, you may be satisfied with that level of flexibility and reuse; but wait, there's more.

In addition to encapsulating content, you can use Tiles to encapsulate layout. For the application shown in Figure 8-2 on page 316, encapsulating layout means making the layout code the h:panelGrid and its contents listed in Listing 8-6 on page 329 available for reuse. As it stands in Listing 8-6, that layout code can only be used by the JSF page shown in Figure 8-2. If you implement JSF pages with identical layouts, you must replicate that layout code for every page. With Tiles, you define a layout that can be reused by multiple tiles, which are nothing more mysterious than imported JSP pages. Tiles lets you implement layout code once and reuse it among many pages.

But reusing layout is just the beginning of the Tiles bag of tricks. You can do more:

  • Nest tiles.

  • Extend tiles.

  • Restrict tiles to users of a particular role.

  • Attach controllers (Java objects) to tiles that are invoked just before their tile is displayed.

Those are the core features that Tiles offers in the pursuit of the ultimate flexibility in crafting web-based user interfaces.

Installing Tiles

Tiles is distributed only with Struts 1.1, but it doesn't depend on Struts at all, so you can use it standalone or with other web application frameworks, such as JSF. Because Tiles comes with Struts, you must download Struts 1.1 from http://jakarta.apache.org/site/binindex.cgi.

Here is how you install Tiles:

  1. Download Struts 1.1 from http://jakarta.apache.org/site/binindex.cgi.

  2. Copy the following JAR files from $STRUTS_HOME/lib to /WEB-INF/lib: struts.jar, commons-beanutils.jar, commons-collections.jar, and commons-digester.jar

  3. Add the Tiles servlet to your deployment descriptor (web.xml).

  4. Set the Tiles configuration file to /WEB-INF/tiles.xml in web.xml.

Your deployment descriptor should look similar to the one listed in Listing 8-10.

Listing 8-10. /WEB-INF/web.xml
  1. <?xml version="1.0"?>  2. <!DOCTYPE web-app PUBLIC  3.    "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"  4.    "http://java.sun.com/dtd/web-app_2_3.dtd">  5.  6. <web-app>  7.    <servlet>  8.       <servlet-name>Tiles Servlet</servlet-name>  9.       <servlet-class>org.apache.struts.tiles.TilesServlet</servlet-class> 10.       <init-param> 11.          <param-name>definitions-config</param-name> 12.          <param-value>/WEB-INF/tiles.xml</param-value> 13.       </init-param> 14.       <load-on-startup>2</load-on-startup> 15.    </servlet> 16. 17.    <servlet> 18.       <servlet-name>Faces Servlet</servlet-name> 19.       <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> 20.       <load-on-startup>1</load-on-startup> 21.    </servlet> 22. 23.    <servlet-mapping> 24.       <servlet-name>Faces Servlet</servlet-name> 25.       <url-pattern>*.faces</url-pattern> 26.    </servlet-mapping> 27. 28.    <welcome-file-list> 29.       <welcome-file>index.html</welcome-file> 30.    </welcome-file-list> 31. </web-app> 

Notice that you must load the Tiles servlet when your application starts. You do that with the load-on-startup element, as we did in the preceding listing.

The definitions-config initialization parameter for the Tiles servlet specifies either a single configuration file as we did in the preceding listing or a comma-separated list of configuration files. Those configuration files contain your tile definitions. You can name those files anything you want as long as they end in .xml. In Listing 8-10 we specified a single file in /WEB-INF named tiles.xml. The following section shows you what to put in your Tiles configuration files.

Using Tiles with the Book Viewer

Using Tiles with JSF is a simple three-step process:

  1. Use tiles:insert to insert a tile definition in a JSF page.

  2. Define the tile in your Tiles configuration file.

  3. Implement the tile's layout.

For the book viewer, we start in book.jsp, where we insert a tile named book:

 

 ... <%@ taglib uri="http://jakarta.apache.org/struts/tags-tiles"     prefix="tiles" %> ... <h:form>     <tiles:insert definition="book" flush="false"/> </h:form> ... 

We define the book tile in /WEB-INF/tiles.xml:

 

 <definition name="book" path="/headerMenuContentLayout.jsp">       <put name="header"  value="/bookHeader.jsp"/>       <put name="menu" value="/bookMenu.jsp"/>       <put name="content" value="/bookContent.jsp"/> </definition> 

The previous snippet of XML defines a tile. The tile's layout is specified with the definition element's path attribute. The tile attributes, specified with put elements, are used by the layout. That layout looks like this:

 

 <%-- this is /headerMenuContentLayout.jsp --%> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%> <%@ taglib uri="http://jakarta.apache.org/struts/tags-tiles" prefix="tiles"%> <h:panelGrid columns="2" styleClass="gridClass"     headerClass="headerClass"     columnClasses="menuClass, contentClass">    <f:facet name="header">       <f:subview >         <tiles:insert attribute="header" flush="false"/>       </f:subview>    </f:facet>    <f:subview >       <tiles:insert attribute="menu" flush="false"/>    </f:subview>    <f:subview >       <tiles:insert attribute="content" flush="false"/>    </f:subview> </h:panelGrid> 

The tiles:insert tag dynamically includes content. That content is the value of the attribute tag of tiles:insert. For example, the preceding code inserts the header attribute. That attribute's value is /header.jsp, so tiles:insert dynamically includes that file.

Notice that we specified a flush="false" attribute for the tiles:insert tag. That is necessary for most modern servlet containers because those containers disallow buffer flushing inside custom tags. If your servlet container throws an exception stating that you cannot flush from a custom tag, then you know you've forgotten to specify that attribute, which is true by default.

What have we gained by using Tiles in this example? We've encapsulated layout so that we can reuse it in other tiles, instead of replicating that layout code from one JSF page to another. For example, you could reuse the book viewer's layout, implemented in /headerMenuContentLayout.jsp, for other pages in the application that have the same layout.

Parameterizing Tiles

There's one flaw to the layout listed in the previous section: it hardcodes CSS classes, namely gridClass, headerClass, menuClass, and contentClass. That means that every web page using the header-menu-content layout will have the same look and feel. It would be better if we could parameterize the CSS class names. That way, other tiles with a header-menu-content layout could define their own look and feel. Let's see how we can do that. First, we add three attributes to the book tile:

 

 <definition name="book" path="/headerMenuContentLayout.jsp">     <put name="headerClass"  value="headerClass"/>     <put name="menuClass" value="menuClass"/>     <put name="contentClass" value="contentClass"/>     <put name="header" value="/bookHeader.jsp"/>     <put name="menu" value="/bookMenu.jsp"/>     <put name="content" value="/bookContent.jsp"/> </definition> 

Then we use those attributes in the layout:

 

 <%-- this is an excerpt of /headerMenuContentLayout.jsp --%> ... <tiles:importAttribute scope="request"/> <h:panelGrid columns="2" style     header     columnClasses="#{menuClass}, #{contentClass}">    ... </h:panelGrid> 

Tile attributes, such as headerClass, menuClass, etc., in the preceding code, exist in tiles scope, which is inaccessible to JavaServer Faces. To make our attributes accessible to the layout JSF page listed above, we use the tiles:importAttribute tag. That tag imports all tile attributes to the scope you specify with the scope attribute. In the preceding code, we imported them to request scope.

Now we can specify different CSS classes for other tiles:

 

 <definition name="anotherTile" path="/headerMenuContentLayout.jsp">       <put name="headerClass"  value="aDifferentHeaderClass"/>       ... </definition> 

NOTE

graphics/note_icon.gif

The tiles:importAttribute tag also lets you import one attribute at a time; for example: <tiles:importAttribute name="headerClass" scope="..."/>.


Extending Tiles

In "Parameterizing Tiles" on page 334 we defined a tile that looked like this:

 

 <definition name="book" path="/headerMenuContentLayout.jsp">     <put name="headerClass"  value="headerClass"/>     <put name="menuClass" value="menuClass"/>     <put name="contentClass" value="contentClass"/>     <put name="header"  value="/bookHeader.jsp"/>     <put name="menu" value="/bookMenu.jsp"/>     <put name="content" value="/bookContent.jsp"/> </definition> 

There are two distinct types of attributes in that tile: CSS classes and included content. Although the latter is specific to the book tile, the former can be used by tiles that represent something other than books. Because of that generality, we split the book tile into two:

 

 <definition name="header-menu-content" path="/headerMenuContentLayout.jsp">     <put name="headerClass"  value="headerClass"/>     <put name="menuClass" value="menuClass"/>     <put name="contentClass" value="contentClass"/> </definition <definition name="book" extends="header-menu-content">     <put name="header"  value="/bookHeader.jsp"/>     <put name="menu" value="/bookMenu.jsp"/>     <put name="content" value="/bookContent.jsp"/> </definition> 

Now the book tile extends the header-menu-content tile. When you extend a tile, you inherit its attributes, much the same as object-oriented inheritance. Because of that inheritance, both book tile definitions in this section have the same attributes. But now, part of the original book tile the CSS class attributes are available for reuse by other tiles that extend the header-menu-content tile.

NOTE

graphics/note_icon.gif

Here's one more thing to consider about Tiles. Imagine the book viewer has been a huge success and Project Gutenberg has commissioned you to implement a library that can display all 6,000+ of their books. You define more than 6,000 tiles that reuse the same layout one tile for each book and present your finished product to the folks at Gutenberg. They think it's great, but they want you to add a footer to the bottom of the page. Since you've used Tiles, you only need to change the single layout used by all your tiles. Imagine the difficulty you would encounter making that change if you had replicated the layout code more than 6,000 times!


Figure 8-6 shows the directory structure for the "tileized" version of the book viewer. That directory structure is the same as the previous version of the book viewer, except that we've added a layout headerMenuContentLayout.jsp and the tiles definition file, /WEB-INF/tiles.xml.

Figure 8-6. Book Viewer with Extended Tile Directory Structure

graphics/08fig06.jpg


Listing 8-11 through Listing 8-13 show the Tiles definition file, the book layout, and the JSF page that displays Alice in Wonderland. We left out the listings of the other files in the application because they are unchanged from the application discussed in "Content Inclusion in JSP-Based Applications" on page 326.

Listing 8-11. book-viewer-tiles/WEB-INF/tiles.xml
  1. <!DOCTYPE tiles-definitions PUBLIC  2.  "-//Apache Software Foundation//DTD Tiles Configuration//EN"  3.  "http://jakarta.apache.org/struts/dtds/tiles-config.dtd">  4.  5. <tiles-definitions>  6.    <definition name="menu-header-content" path="/headerMenuContentLayout.jsp">  7.       <put name="gridClass"           value="headerMenuContent"/>  8.       <put name="headerClass"         value="header"/>  9.       <put name="menuColumnClass"     value="menuColumn"/> 10.       <put name="contentColumnClass"  value="contentColumn"/> 11.    </definition> 12. 13.    <definition name="book" extends="menu-header-content"> 14.       <put name="header"  value="/bookHeader.jsp"/> 15.       <put name="menu"    value="/bookMenu.jsp"/> 16.       <put name="content" value="/bookContent.jsp"/> 17.    </definition> 18. </tiles-definitions> 

Listing 8-12. book-viewer-tiles/headerMenuContentLayout.jsp
  1. <%@ taglib uri="http://java.sun.com/jsf/core"  prefix="f" %>  2. <%@ taglib uri="http://java.sun.com/jsf/html"  prefix="h" %>  3. <%@ taglib uri="http://jakarta.apache.org/struts/tags-tiles" prefix="tiles" %>  4.  5. <tiles:importAttribute scope="request"/>  6.  7. <h:panelGrid columns="2" style  8.    header  9.       columnClasses="#{menuColumnClass}, #{contentColumnClass}"> 10.    <f:facet name="header"> 11.       <f:subview > 12.          <tiles:insert attribute="header" flush="false"/> 13.       </f:subview> 14.    </f:facet> 15.    <f:subview > 16.       <tiles:insert attribute="menu" flush="false"/> 17.    </f:subview> 18. 19.    <f:verbatim> 20.       <tiles:insert attribute="content" flush="false"/> 21.    </f:verbatim> 22. </h:panelGrid> 

Listing 8-13. book-viewer-tiles/book.jsp
  1. <html>  2.    <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>  3.    <%@ taglib uri="http://java.sun.com/jsf/core"  prefix="f" %>  4.    <%@ taglib uri="http://java.sun.com/jsf/html"  prefix="h" %>  5.    <%@ taglib uri="http://jakarta.apache.org/struts/tags-tiles" prefix="tiles" %>  6.  7.    <f:view>  8.       <head>  9.          <link href="styles.css" rel="stylesheet" type="text/css"/> 10.          <f:loadBundle basename="com.corejsf.messages" var="msgs"/> 11.          <title><h:outputText value="#{msgs.bookWindowTitle}"/></title> 12.       </head> 13.       <body> 14.          <f:subview > 15.             <h:form> 16.                <tiles:insert definition="book" flush="false"/> 17.             </h:form> 18.          </f:subview> 19.       </body> 20.    </f:view> 21. </html> 



core JavaServer Faces
Core JavaServer Faces
ISBN: 0131463055
EAN: 2147483647
Year: 2003
Pages: 121

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