PrettyFaces Documentation
Setting up PrettyFaces is simple.
CELEBRATE! PrettyFaces version 2.0.4 is Released!
<dependency> <groupId>com.ocpsoft</groupId> <artifactId>ocpsoft-pretty-faces</artifactId> <version>2.0.4</version> </dependency>
Read the release notes, or reference documentation
(PDF only until we convert them to HTML.)
NOTICE: The documentation below is for version 2.0.3.
Configure PrettyFaces Filters in WEB-INF/web.xml
Create the WEB-INF/pretty-config.xml file
(Steps 1 & 2 are required. Advanced features are optional.)
Advanced Features
Components
Configuring Logging (log4j)
This version of the documentation is for PrettyFaces 2.0.3, but you can view older versions of the PrettyFaces docs (1.1.0, 1.2.x, and 2.0.2_GA).
0. Get PrettyFaces.
This step is pretty straight-forward, right? Extract necessary JAR files into your /WEB-INF/lib directory, or include a maven dependency in your pom.xml
<!-- For JSF 2.0 --> <dependency> <groupId>com.ocpsoft</groupId> <artifactId>ocpsoft-pretty-faces</artifactId> <version>2.0.3</version> </dependency>
1. Add the <filter> and <filter-mapping> to /WEB-INF/web.xml
PrettyFilter does most of the work. Without it, not much would happen.
<filter> <filter-name>Pretty Filter</filter-name> <filter-class>com.ocpsoft.pretty.PrettyFilter</filter-class> </filter> <filter-mapping> <filter-name>Pretty Filter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping>
2. Create the /WEB-INF/pretty-config.xml file
This is where you will map URLs to Faces Views. Read on for details.
<pretty-config xmlns="http://ocpsoft.com/prettyfaces-xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://ocpsoft.com/prettyfaces-xsd http://ocpsoft.com/xml/ns/prettyfaces/pretty-1.0.xsd"> <url-mapping id="login"> <pattern> /login </pattern> <view-id> /faces/login.jsf </view-id> </url-mapping> <url-mapping id="home"> <pattern> /home </pattern> <query-param name="displayWelcomeMessage">#{homeBean.displayWelcomeMessage}</query-param> <view-id> #{homeBean.getViewPath} </view-id> </url-mapping> <url-mapping id="viewStory"> <pattern> /story/#{myBean.currentStoryId}/ </pattern> <query-param name="commentText" decode="false">#{myBean.commentText}</query-param> <view-id> /faces/story/viewStory.jsf </view-id> <action onPostback="false">#{myBean.loadStory}</action> </url-mapping> <url-mapping id="viewComment"> <pattern> /story/#{myBean.currentStoryId}/#{myBean.commentId} </pattern> <view-id>/faces/story/comment.jsf</view-id> <action>#{myBean.loadComment}</action> </url-mapping> </pretty-config>
Url Mappings
Each <url-mapping id=”"> must specify a unique id. And contains the following attributes and elements, in order:
<pattern>/blog/article/#{someBean.paramName}</pattern>
Specify the pattern for which this URL will be matched. This element is required, and has a multiplicity of 1 (only one)
Any EL expressions #{someBean.paramName} found within the pattern will be processed as value injections. The URL will be parsed and the value found at the location of the EL expression will be injected into the location specified in that EL expression. Note: EL expressions will not match over the ‘/’ character.
The pattern itself is compiled parsed as a regular expression, meaning that the actual URL matching can be as simple or as complex as desired.
If further custom URL parsing is required, it is recommended to do this manually via an Action Method: See custom parsing.
<query-param name=”key”>#{someBean.queryParamValue}</query-param>
Defines a managed query parameter of the form http://site.com/url?key=somevalue, where if the parameter exists, the value will be injected into the specified managed bean. This also handles JSF commandLink and AJAX <f:param> values. This element is optional, and has a multiplicity of 0…N (zero or more)
Attributes:
- name — String, required. This is the request value key
- decode — boolean, optional (default true), if set to false, this query-param will not be URLDecoded (see java.net.URLDecoder)
<view-id>#{someBean.methodName}<view-id>
Specify the JSF ViewId displayed by this mapping, by either calling an el Method (must return an object for which the toString() method will return the view Id) or by returning a literal String value. This element is required, and has a multiplicity of 1 (only one)
The ViewId may be any resource located within the current Servlet Context: E.g. PrettyFaces can also forward to a non-Faces servlet.
<action>#{someBean.methodName}</action>
Specify an action method to be called after URL parameters have been parsed and assigned into beans. This element has a multiplicity of 0…N (zero or more)
Attributes:
phaseId — String, optional (default after RESTORE_VIEW) if set to a valid JSF PhaseId, the action will occur immediately before the specified Phase (or immediately after RESTORE_VIEW). (see javax.faces.event.PhaseId)
Valid values for this attribute are: RESTORE_VIEW, APPLY_REQUEST_VALUES, PROCESS_VALIDATIONS, UPDATE_MODEL_VALUES, INVOKE_APPLICATION, RENDER_RESPONSE, ANY_PHASE.
Note however, that if the phase does not occur, neither will your action method.
If ANY_PHASE is specified, the action method will fire on EVERY phase.
onPostback — boolean, optional (default true), if set to false, this action method will not occur on form postback. (see ResponseStateManager.isPostback())
Order of processing:
- URL pattern parsing, query-parameter handling, and value injection into JSF managed beans.
- View-Id calculation (if a view Id is dynamic, the el method will be called.)
- JSF gains control of the request via RequestDispatcher.forward(“/context/faces/viewId.jsf”).
- Action methods are called after RESTORE_VIEW phase, unless the optional phaseId attribute is specified.
Config Loading:
At application startup time, before any requests are processed, Pretty Faces processes zero or more configuration resources, located according to the following algorithm:
- Search for classpath resources named META-INF/pretty-config.xml in the ServletContext resource paths for this web application, and load each as a configuration resource.
- Check for the existence of a context initialization parameter named com.ocpsoft.pretty.CONFIG_FILES. If it exists, treat it as a comma-delimited list of context relative resource paths (starting with a /), and load each of the specfied resources.
- Check for the existence of a web application configuration resource named /WEB-INF/pretty-config.xml, and load it if the resource exists.
3. Advanced Features
3.1 Using Dynamic View ID Capabilities
Dynamic view IDs allow a mapped URL to display content from any JSF view. This is prevents doing redirects which would otherwise destroy information stored in the URL, and also provides some extra functionality for application design.
This pretty-config mapping uses a dynamic view-id:
<pretty-config> <url-mapping id="home"> <pattern> /home </pattern> <query-param name="displayWelcomeMessage">#{homeBean.displayWelcomeMessage}</query-param> <view-id> #{homeBean.getViewPath} </view-id> </url-mapping> </pretty-config>
The corresponding backing-bean method must return a JSF ViewID, which will proceed with the faces lifecycle (leaving the URL /home), or a pretty:mappingId. In the case when a pretty:mappingId is returned, PrettyFaces will “switch mappings” and instead treat the request as if it had been sent to the new mappingId, without issuing a browser redirect.
public class ManagedBean { public String getViewPath() { // This method returns the path of the JSF view to display // when the URL /home is accessed. if(user.isLoggedIn()) { // Note: the extension '.jsf' is the mapped faces extension return "/faces/home.jsf"; } // The home page can instead display a different view; return // the pretty:mappingId of the view you wish to display. // Note that this will not cause a redirect, and will not // change the client browser URL. // If you wish to issue a redirect, you should use a page // load action instead of a dynamic view Id function. return "pretty:login"; } }
3.2 Using the Managed Query Parameter facility
Managed query parameters allow automatic assignment of values into JSF managed bean fields, instead of parsing and URL Decoding the value manually out of the request object. Examining this sample mapping, we see that the developer has specified two managed query-parameters. The ‘sortBy‘ and ‘itemId‘ parameters.
<pretty-config> <url-mapping id="itemList"> <pattern> /items/list </pattern> <query-param name="sortBy">#{itemBean.sortByField}</query-param> <query-param name="itemId">#{itemBean.currentItemId}</query-param> <view-id> /faces/items/list.jsf </view-id> <action>#{itemBean.loadItems}</action> </url-mapping> </pretty-config>
The managed bean that accompanies this mapping:
public class ItemBean { private List<Item> items; private Integer currentItemId; private String sortByField; public String deleteItem() { // currentItemId will be automatically populated by // PrettyFaces if the parameter was passed in the request // (see example JSF page below) ItemManager.deleteById(currentItemId); // Redisplay the current page via redirect. return "pretty:" } public void loadItems() { // The sortByField member will be null if the sortBy // query-parameter is not found in the request this.items = ItemManager.getSortedItems(sortByField); } //... getters and setters... }
Example JSF page: Notice the <f:param> tag.
This will generate a link that provides the ‘itemId’ parameter to the request for PrettyFaces to parse.
<c:forEach var="item" items="${itemBean.items}"> <h:commandLink> Delete this item. <f:param name="itemId" value="${item.id}" /> </h:commandLink> </c:forEach>
3.3 Validating URL Parameters
One of the important factors to consider when dealing with any type of user input, is validation. PrettyFaces offers hooks into JSF validation, allowing validators to be attached to individual parameters in each dynamic URL. (See example…)
<url-mapping id="validate"> <pattern value="/validate/#{validationBean.pathInput}"> <validate param="0" validatorIds="validator1 validator2" onError="#{validationBean.handle}" /> </pattern> <query-param name="param1" validatorIds="validator2" onError="pretty:demo"> #{validationBean.queryInput} </query-param> <view-id>/faces/validation/test.jsf</view-id> </url-mapping>
The “validatorIds” attribute is a space-separated list of all JSF validators you wish to attach to a given parameter. These validator IDs must have been defined in faces-config.xml or via the JSF2 @FacesValidator annotation.

PrettyFaces Validation Flow
3.4 Wiring navigation into JSF action methods
public class PageBean { public String goHome() { // this will tell pretty to redirect the client to the home-page // no parameters are mapped, so this is pretty simple return "pretty:home"; } public String goHomeAndWelcome() { // this will tell pretty to redirect the client to the home-page // since there is a managed query-parameter defined in the mapping, // PrettyFaces will generate the URL, and append the mapped param // eg: /home?displayWelcome=true homeBean.displayWelcomeMessage(true); return "pretty:home"; } public String goViewStory() { // this will tell pretty to redirect the client to the viewStory page // PrettyFaces will generate the URL by extracting any values from // the mapping beans and using them to inject back into the pattern // therefore, navigation can be controlled by placing a value into // the mapped field before PrettyFaces extracts it and generates the URL // so... /story/#{myBean.currentStoryId}/ ...becomes... /story/12/ viewStoryBean.setCurrentStoryId(12); return "pretty:viewStory"; } public String doRefreshByRedirect() { // using the "pretty:" prefix without a mapping-id will cause a // redirect to the current page return "pretty:"; } public String doNormalJSFRender() { // returning an value without the "pretty:" prefix will fall back to // the default JSF navigation handlers return "someNavigationCase"; } }
3.5 Parsing complex / dynamic-length URLs
Consider the following example configuration and bean: The URL contains many dynamic layers of mysite.com/value/value2/value3/…/valueN, which cannot be specified using a static URL pattern.
Since patterns are regexes, we can use a catch-all pattern, and process the values ourselves. Note, however, that <pretty:link>, and other URL PrettyFaces URL generation tools, will NOT be able to generate dynamic links to URLs mapped in this fashion; you will need to do that yourself as well.
<url-mapping id="dynamicUrl"> <pattern> /blog/categories/.* </pattern> <view-id> #{urlParsingBean.parseComplexUrl} </view-id> </url-mapping>
Example of a complex URL parser. Remember to URLDecode before using any values from the URL:
public class UrlParsingBean { public String parseComplexUrl() throws UnsupportedEncodingException { String uri = PrettyContext.getCurrentInstance().getOriginalUri(); List<String> categoryChain = new ArrayList<String>(); while(uri.length() > 0) { int index = uri.indexOf('/'); String value = uri.substring(0, index); categoryChain.add(URLDecoder.decode(value, "UTF-8")); uri = uri.substring(index); } //now load the data... return "/blag/viewArticle.jsf"; } }
3.6 Accessing PrettyContext through EL
Since a new PrettyContext is generated on each request, and stored into the requestMap, it is possible to access the context object through EL in a JSP, or Facelet.
#{prettyContext} <!-- returns the current PrettyContext -->
#{prettyContext.currentMapping.id} <!-- returns the current UrlMapping Id -->
<!-- And so on... -->4. Rendering HTML Links and URLs
4.1 The pretty:link component
PrettyFaces provides a JSF component to output an HTML link to the page. The link tag requires a mapping-id (specified in the pretty-config.xml,) identifying which link to render.
If the provided mappingId requires any url-pattern-parameters or managed-query-parameters, they can be passed in via the <f:param> tag.
Url pattern parameters can be passed individually, as a java.util.List, or as an Array. In the latter two cases, toString() will be called on each of the objects in the list/array. If an empty or null list/array is passed, it will be ignored.
Url pattern parameters do NOT have a name attribute, and are parsed in the order they are passed into the tag. Managed-query-parameters DO have a name attribute, and order is irrelevant.
For Example: The viewComment pattern requires:
<!-- From pretty-config.xml, viewComment mapping-id, above: /story/#{myBean.currentStoryId}/#{myBean.commentId}--> <%@ taglib prefix="pretty" uri="http://ocpsoft.com/prettyfaces" %> <pretty:link mappingId="comment"> <f:param value="#{myBean.currentStoryId}" /> <f:param value="#{myBean.commentId}" /> Go to Comment. (This is Link Text) </pretty:link>
4.2 The pretty:urlbuffer component
PrettyFaces provides a JSF component to generate a URL for use as a page scoped variable through El. This tag requires a mapping-id (specified in the pretty-config.xml)
If the provided mappingId requires any url-pattern-parameters or managed-query-parameters, they can be passed in via the <f:param> tag.
Url pattern parameters can be passed individually, as a java.util.List, or as an Array. In the latter two cases, toString() will be called on each of the objects in the list/array. If an empty or null list/array is passed, it will be ignored.
Url pattern parameters do NOT have a name attribute, and are parsed in the order they are passed into the tag. Managed-query-parameters DO have a name attribute, and order is irrelevant.
For Example: The viewItem pattern requires:
<!-- From the managed-query-parameter section above, itemList mapping-id.--> <pretty:urlbuffer var="itemListUrl" mappingId="itemList"> <f:param name="itemId" value="22" /> <f:param name="sortBy" value="price" /> </pretty:urlbuffer> <h:outputText value="Generated Url Is: #{requestScope.itemListUrl}" /> <!-- /items/list?itemId=22&sortBy=price --> <br />
This design is intended to reduce complexity and prevent manual manipulation of URLs.
5. Configuring Logging (log4j)
example log4j.properties
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=warn, stdout
#this will set PrettyFaces logging level to 'info'
log4j.logger.com.ocpsoft.pretty=infoFinished. Run your application!
You should now have a fully functional PrettyFaces configuration.
