Differences between revisions 6 and 7
Revision 6 as of 2006-05-05 13:48:30
Size: 7253
Comment:
Revision 7 as of 2009-09-20 23:01:07
Size: 7255
Editor: localhost
Comment: converted to 1.6 markup
Deletions are marked like this. Additions are marked like this.
Line 162: Line 162:
plain text in <f:verbatim> tags, or using the [http://jsftutorials.net/htmLib/ htmlib JSF library] (though that can have performance implications). plain text in <f:verbatim> tags, or using the [[http://jsftutorials.net/htmLib/|htmlib JSF library]] (though that can have performance implications).

How to get started using Tiles and JSF.

Tiles is a framework which allows you to build page templates and easily and declaratively plug in various reusable page components. Tiles reduces the cost of maintaining the look and feel of your application, and at the same time allows authors or developers to focus on the content that really matters. For more information on Tiles, please visit http://www.lifl.fr/~dumoulin/tiles/.

MyFaces Tomahawk provides a series of JSF components that go beyond the JSF specification and contains an implementation of javax.faces.application.ViewHandler, JspTilesViewHandlerImpl which supports Tiles. MyFaces provides an example web app using JspTilesViewHandlerImpl and can be found in the binary distribution of the various webapp examples available at http://myfaces.apache.org/binary.cgi.

The easiest thing to do is to download and install the myfaces-tiles-example.war example. Run it, and look at the configuration files. They will go a long way to explain how things work. If your already familiar with JSF and how things work, then, that may be all you need.

On the other hand, if you stray from standard configurations provided in the example, then you may run into problems (especially if you are new to JSF and come from the Struts world).

My goal was to create a simple example, much like the one provided by myfaces-tiles-example.war, but use path mapping instead of extension mapping, and to place all my pages under a directory called /pages. I also wanted to avoid using the embedded DTD which is specified in the tiles.xml file provided in the example.

The first thing I did was to modify the servlet mapping for the JSF servlet to support path mapping.

<servlet-mapping>
  <servlet-name>Faces Servlet</servlet-name>
  <url-pattern>/app/*</url-pattern>
</servlet-mapping>

I need to tell MyFaces where to find my tiles configurations.

<context-param>
  <param-name>tiles-definitions</param-name>
  <param-value>/WEB-INF/conf/tiles/base-tiles-def.xml</param-value>
</context-param>

I just copied my base-tiles-def.xml file I used in an existing Struts application and used it as a template for my example.

The next thing was to define a welcome file,

<welcome-file-list>
  <welcome-file>index.jsp</welcome-file>
</welcome-file-list>

and place index.jsp directly under the root of my web app context. The file index.jsp simply contains a forward:

<jsp:forward page="/app/launch.jsp"/>

I next updated the faces configuration file to the following:

<?xml version="1.0"?>
<!DOCTYPE faces-config PUBLIC
  "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"
  "http://java.sun.com/dtd/web-facesconfig_1_0.dtd" >

<faces-config>
    <application>
        <view-handler>org.apache.myfaces.tomahawk.application.jsp.JspTilesViewHandlerImpl</view-handler>
    </application>
        
    <navigation-rule>
    </navigation-rule>

</faces-config>

Next make sure I have tile defined. Here's a snippet from base-tiles-def.xml:

<definition name="/launch.tiles" extends="main.layout">
        <put name="content" value="/pages/launch.jsp" />
</definition>

During the view rendering phase, JSF will use the ViewHandler defined, instead of the default, org.apache.myfaces.tomahawk.application.jsp.JspTilesViewHandlerImpl. This ViewHandler will read in the tiles definitions and facilitate rendering the appropriate view.

A few observations of note here

The url in index.jsp is misleading. It would seem to imply that there might be a launch.jsp directly under the web app root context, but there isn't. What happens is that since the path "/app/" is in the url, the JSF servlet intercepts the request and starts the request processing life cycle. During the render view phase, the JspTilesViewHandlerImpl performs the following logical steps:

  1. Identifies the servlet mapping as path mapping.
  2. Looks at viewId (/launch.jsp) and determines that it contains the default suffix, ".jsp".
  3. Creates a tiles definition look up key by replacing the viewId suffix with ".tiles".
    • (/launch.jsp ===> /launch.tiles).

  4. Looks up the tiles definition using the look up key.
  5. Dispatches to the tile
  6. Tiles does the rest as far as rendering the view.

(If your really curious, look at the renderView() method in JspTilesViewHandlerImpl.java.)

In order for this to work, the tiles definition name MUST match the tiles definition look up key created in step 3 above.

The default suffix, ".jsp", is configurable by defining an init context parameter in web.xml file.

<context-param>
  <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
  <param-value>.myTilesSuffix</param-value>
</context-param>

One other interesting note for former Struts users, the faces-config navigation-rule element is empty. This is because the navigation rules never come into play here. There is no such thing as an action mapping which simply forwards to a tile. This abstraction is removed at the cost of coupling the viewId name directly to the tiles definition name.

Out-of-order problems when mixing template text and JSF tags.

There is a known problem with JSF 1.1 and dynamic includes of pages (at least with the Apache Tomcat servlet engine). When a page is included from another page, a buffered response stream is created and passed to the servlet which handles the included page. Only after that page has been completely processed is the generated output appended to the response for the including page. However JSF components within the included page use

  FacesContext.getCurrentInstance().getResponseWriter()

to determine where to write their output as they are rendered, and this mechanism isn't aware of the buffer created by the JSP include mechanism.

The result is that any output generated by JSF tags in the included page is appended to the response before any plain text or output generated by JSP tags in the included page. This usually results in an unusable page.

Unfortunately the whole point of Tiles is to perform dynamic includes to build pages, so this can be a major problem when using Tiles.

Currently the only solution is to ensure that all output from the included page is generated by JSF tags. This means wrapping all plain text in <f:verbatim> tags, or using the htmlib JSF library (though that can have performance implications).

If <f:verbatim> tag doesn't work you can use <h:outputText> tag to generate the plain text (with "escape="false"" or chars like '<' or '>' became &lt and &gt). Example (generating <pre> </pre>):

        <h:outputText value="<pre>" escape="false"/> 
<h:outputText value="#{DocBacker.document}"/>
        <h:outputText value="</pre>" escape="false"/>

Note that this is a different issue than the one which causes out-of-order problems when mixing JSF and non-JSF content within a JSF component which renders its children (eg a Panel).

Tiles_and_JSF (last edited 2009-09-20 23:01:07 by localhost)