Turning the app into a webapp
After creating a small persistence layer for our JPokerStats application, it is now time to finally create a web application out of our sources. If you already have the source files from the previous parts, you may have noticed the src/main/webapp directory that was created by the Maven archetype plugin. Let's start here.
Getting the sources
As always, you can either go the project website or checkout the associated tag from the source control system:
svn checkout http://jpokerstats.googlecode.com/svn/tags/tutorial_part_05
Integrating Faces and Facelets
A web.xml is already present, and for a JSF application we will need to declare the JSF servlet here. We will also add some additional configuration options for debugging purposes and will map all requests to the /faces/ context to the servlet. This results in the following basic web.xml:
-
<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">
-
<display-name>JPokerStats</display-name>
-
-
<!--===========================================================-->
-
<!-- Facelet Configuration -->
-
<!--===========================================================-->
-
-
<!-- Use Documents Saved as *.xhtml -->
-
<context-param>
-
<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
-
<param-value>.xhtml</param-value>
-
</context-param>
-
-
<!-- Special Debug Output for Development -->
-
<context-param>
-
<param-name>facelets.DEVELOPMENT</param-name>
-
<param-value>true</param-value>
-
</context-param>
-
-
<!-- Facelet Servlet -->
-
<servlet>
-
<servlet-name>Faces Servlet</servlet-name>
-
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
-
<load-on-startup>1</load-on-startup>
-
</servlet>
-
-
<!-- Facelet Servlet Mapping -->
-
<servlet-mapping>
-
<servlet-name>Faces Servlet</servlet-name>
-
<url-pattern>/faces/*</url-pattern>
-
</servlet-mapping>
-
-
</web-app>
The next step is adding a faces-config.xml to configure the use of Facelets for templating purposes. This is also quite simple, Facelets is just a special view handler for JSF. For further information on how to configure Facelets please refer to the Facelets developer documentation.
-
<?xml version="1.0"?>
-
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
-
http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
-
version="1.2">
-
<application>
-
<view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
-
</application>
-
</faces-config>
Now we're set with our basic JSP/Facelets application, but now we'll need a template for the application.
Creating a template set
Writing nice HTML pages isn't one of my favourite tasks and this tutorial is not about webdesign, so I will just use a pre-made template set and adapt it for use with Facelets. The template is called Multiflex 4 and you can download this template (and 4 other versions) from 1234.info. Basically, templating with Facelets is just placing ui:insert tags in the template as named placeholders and replacing them with ui:define tags in the concrete pages later on. The templating process cannot be given here in full detail, so I will only include excerpts and you may refer to the code revisions in the source repository.
Adding namespaces
All templates must be valid XHTML pages. As XHTML pages are de-facto also valid XML documents, we can extend the set of tags available for use by adding some namespaces to the document. We declare namespaces for Facelets itself (namespace ui) and for JSF core and HTML (namespaces f and h) so we can use any of these tags within the templates. The declaration looks as follows:
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-
<html xmlns="http://www.w3.org/1999/xhtml" lang="en"
-
xmlns:ui="http://java.sun.com/jsf/facelets"
-
xmlns:h="http://java.sun.com/jsf/html"
-
xmlns:f="http://java.sun.com/jsf/core">
Getting the stylesheets and graphics right
The first problem you will encounter when using a template mechanism is with links to stylesheets and graphics. Since our main template exists only once in our project but is addressed from different locations, normal paths to stylesheets will not work, the need to be recalculated for every page. This can be achieved by using the <base> tag together with some predefined variables in Faces.
-
<base href="#{facesContext.externalContext.request.scheme}://#{facesContext.externalContext.request.serverName}:#{facesContext.externalContext.request.serverPort}#{facesContext.externalContext.requestContextPath}/"/>
This will place a base link in the final HTML that is valid for all possible paths and all paths contained in <link> tags will be relative to this base URL.
Adding placeholders
Now it's time to add some placeholders to the template which can later be replaced by the actual pages. For example, the page title should be replaceable, so we put a ui:insert there, the basic placeholder of Facelets. It has a name attribute that must be unique across the template and any subtemplate (or weird things will happen). This name can later be used to address this placeholder. You may also define contents for the placeholder in the template, this snippet will be used as default when you don't explicitly replace the placeholder. You can use any valid JSF snippet for the content, just as you would in a normal page.
-
<head>
-
...
-
<title>
-
<ui:insert name="page_head_title">JPokerStats</ui:insert>
-
</title>
-
</head>
We also add placeholders for the heading and the subheading of the page, for the breadcrumb trail and the main content of the page.
-
<!-- Breadcrumb -->
-
<ui:insert name="breadcrumb">
-
<ul>
-
<li class="nobullet">You are here: </li>
-
<li><a href="#{application.contextPath}/" title="Home">Home</a></li>
-
</ul>
-
</ui:insert>
You could also add a placeholder for the navigation and realise it via either a subtemplate or some dynamic mechanism, but I'm no friend of dynamic menus so you will have to figure out how to do it yourselves. To see the complete source code with all placeholders, please refer to the projects source repository.
Using the template for our first page
Finally the web based poker result tracking software is getting its first web page. It will be the index page of the whole application and is hence located directly in the webapp directory. We specified in the faces-config.xml that the default suffix for JSF pages is .xhtml, so we name our page start.xhtml and fill it with as little content as follows:
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
<html xmlns="http://www.w3.org/1999/xhtml"
-
xmlns:ui="http://java.sun.com/jsf/facelets"
-
xmlns:h="http://java.sun.com/jsf/html"
-
xmlns:f="http://java.sun.com/jsf/core">
-
<head>
-
<title>JPokerStats start page</title>
-
</head>
-
<body>
-
<div>
-
<h1>This heading will not appear in the page</h1>
-
<ui:composition template="_multiflex/template.xhtml">
-
</ui:composition>
-
</div>
-
</body>
-
</html>
Again, we have a valid XHTML page extended by additional namespaces for Facelets and JSF (admittedly, it may not be quite legal to declare a doctype based on a DTD and extend that by using Schema namespaces). You will notice the ui:composition tag. This tag tells Facelets, that this page is to be processed by Facelets and will use the specified template. You can also see the heading, claiming to not appear in the final page. And I promise, it won't. For any page wishing to use a template, we must define a ui:composition block to tell Facelets which template to use. (OK, this is only partially true as there is a tag called ui:decorate that works nearly the same way. This tag will be discussed in later parts of this tutorial). When processing the page, Facelets automatically discards any code outside this block. This serves two purposes: first, it allows the page itself to be a complete and valid XML/XHTML document, thus making it easier for IDEs to perform code completion. Second, it allows the user to edit the page with design tools such as Dreamweaver, without having to handle only snippets of pages, provided the tool handles namespaces properly.
To be able to use this page as our welcome page, that will be shown when only http://somehost.tld/jpokerstats/ is called, we need a little trick as we cannot define any JSF page as welcome page in the web.xml. This is one of the spots where the not-so-tight integration between th web application itself and Faces produces some difficulties. We will stumble across this integration problem again later on. But for now we create a new page called index.jsp and put a redirect to the JSF page in it and it will do the trick.
-
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
-
<% response.sendRedirect("faces/start.xhtml"); %>
You can now build and run the application by executing mvn clean package jetty:run. Then point your browser to http://localhost:8080/jpokerstats and see the "magic". Don't be discouraged by an exception with a message such as
Parse Error at line 6 column 17: Document root element "faces-config", must match DOCTYPE root "null".
org.xml.sax.SAXParseException: Document root element "faces-config", must match DOCTYPE root "null".
This error occurs on some machines when the XML parser used does not understand Schema declarations and insists on having a DOCTYPE definition (which isn't available in our faces-config.xml, hence the "DOCTYPE root null"). The application will still run.
Coming up next
In the next part of this tutorial, which I will try to publish a little faster than this part, we are going to breathe some life on the application by letting it actually do something.
Tags: developing java web applications, facelets, jpa, jsf, shale, spring, templating, web









#1 von Kim am 21. Dezember 2008 - 15:44
Well,
when are you going to continue this project?
br
kim
#2 von Hendrik Busch am 23. Dezember 2008 - 21:00
I’m very sorry! I’ve been quite busy the past few month and couldn’t find the time to continue this tutorial. But now that I have two weeks off for christmas, chances are quite good that you’ll se a new part coming soon. This is the best I can offer for the time being, sorry…