OCPSoft.com - Simple SolutionsCommunity Documentation

PrettyFaces Reference Guide

URL-rewriting, dynamic parameters, bookmarks, and navigation for Servlet, Java EE, and JSF


1. Introduction
1.1. Using this guide
1.2. What PrettyFaces provides
1.3. Business value of URL-rewriting
2. Get Started
2.1. Get PrettyFaces
2.2. Configure PrettyFaces in web.xml
2.3. Create /WEB-INF/pretty-config.xml
3. Configure PrettyFaces
3.1. Create your first bookmarkable URL (mapping a URL)
3.2. Add Path-parameters to your mapped URL (REST)
3.2.1. Named path parameters
3.2.2. * EL-injected path parameters
3.2.3. Restrict what Path Parameters will accept (Custom regex patterns)
3.3. Manage Query-parameters in mapped URLs
3.4. Inherit from a parent URL-mapping
3.5. * Dynamic view-IDs (DynaView)
3.6. * Load data when accessing mapped URLs (Page-action methods)
3.6.1. Invoking navigation from a page-action
3.6.2. Invoking during specific JSF Phases
3.6.3. Toggling invocation on postback
3.7. * Parameter validation
3.7.1. Query parameters
3.7.2. Path parameters
3.8. Annotation based configuration
3.8.1. Basic setup
3.8.2. Simple URL mappings
3.8.3. Page actions
3.8.4. Query Parameters
3.8.5. Parameter validation
3.8.6. Bean name resolving
4. Order of Events
4.1. Request processing order
4.2. Configuration processing order
5. Simplify Navigation
5.1. * Integrating with JSF action methods
5.2. * Integrating with JSF commandLink and commandButton
5.3. Redirecting from non-JSF application Java code
5.4. * Preserving FacesMessages across redirects
6. Inbound URL Rewriting
6.1. Common rewriting features
6.2. Rewriting URLs that match a specific pattern
6.3. Rewrite options
7. Outbound URL-rewriting
7.1. Rewriting URLs in Java
7.2. * Rewriting URLs in JSF views
7.3. Disabling outbound-rewriting for a URL-mapping
8. * Rendering HTML links and URLs
8.1. The <pretty:link> component
8.2. The <pretty:urlbuffer> component
8.3. Using JSF standard components
9. Extend PrettyFaces
9.1. com.ocpsoft.pretty.faces.spi.ConfigurationProvider
9.2. com.ocpsoft.pretty.faces.spi.ConfigurationPostProcessor
9.3. com.ocpsoft.pretty.faces.spi.ELBeanNameResolver
10. Feedback & Contribution
11. Updating Notes
11.1. Updating to 3.x.y from earlier versions
12. FAQ

It should not be difficult to install PrettyFaces into any new or existing Servlet-based application. Follow the next few steps and you should have a working configuration in only a few minutes.

This step is pretty straight-forward, right? Copy necessary JAR files into your /WEB-INF/lib directory, or include a Maven dependency in your pom.xml (recommended):

Non-Maven users may download JAR files manually from one of the following repositories: (Central, OcpSoft). Be sure to select the correct package for your version of JSF.

Note

Non-Maven Users must also include the following required JAR dependencies (downloaded separately) in addition to the PrettyFaces JAR file:

List of Maven Dependencies:

<!-- for JSF 2.x -->
<dependency>
   <groupId>com.ocpsoft</groupId>
   <artifactId>prettyfaces-jsf2</artifactId>
   <version>${latest.prettyfaces.version}</version>
</dependency>

<!-- for JSF 1.2.x -->
<dependency>
   <groupId>com.ocpsoft</groupId>
   <artifactId>prettyfaces-jsf12</artifactId>
   <version>${latest.prettyfaces.version}</version>
</dependency>

<!-- for JSF 1.1.x (UNSUPPORTED) -->
<dependency>
   <groupId>com.ocpsoft</groupId>
   <artifactId>prettyfaces-jsf11</artifactId>
   <version>${latest.prettyfaces.version}</version>
</dependency>

This is where you'll tell PrettyFaces what to do, which URLs to rewrite. Each URL-mapping contained in the configuration must specify a pattern and a viewId. The pattern specifies which inbound URL to match, and the viewId specifies the location that URL should resolve -be redirected- to.


Congratulations! That's all you should have to do in order to use PrettyFaces. The rest of this reference guide covers detailed configuration options, framework integration with JavaServer Faces, and special use-cases.

PrettyFaces offers in-depth configuration options for completely customizable use. We'll start with the most commonly used features, then move on to the advanced goodies.

Suppose we want to create a URL mapping that allows users to access items in a specific category of a web-store, such as: /store/category/. In other words, we want to take a piece of the URL string itself - for example: /store/[category]/ and use that value: [category] in our application's logic.

This is commonly known as creating a REST-ful URL, but here we'll use the term "path-parameter" to represent each individual data element created in our URL path.

<url-mapping id="viewCategory">
  <pattern value="/store/#{ cat }/" />
  <view-id value="/faces/shop/store.jsf" /> 
</url-mapping>

Here we've used PrettyFaces path-parameter syntax to capture values stored in part of inbound URLs that match our pattern. Take, for instance, the following URL:

/store/shoes/

Part of this URL will be matched by the path-parameter expression: '#{ cat }', making its value, 'shoes', available in the application logic, the rest of the URL will be matched against the pattern, but ignored.

<pattern value="/store/#{ cat }/" />

The application can gain access to these path-parameters in a few ways, via request parameter naming, or EL bean injection; both techniques will be covered below.

Another method of accessing path-parameter values is via EL value injection, where PrettyFaces can inject the value of the URL parameter directly into a managed-bean. This requires a syntax similar to specifying a named parameter, but PrettyFaces looks for '.' characters, an easy way to distinguish a name from an EL value-injection expression:

<pattern value="/store/#{ bean.category }/" />

In this case, we must have a managed bean defined in our application; these beans can be registered either in JSF, Spring, Google Guice, or as shown here using CDI/Weld. Based on the configuration in this particular example, the bean must be named "bean", and must have an accessible field named "category".

Any value matched by the path-parameter will be injected directly into the bean.

@Named("bean")
@RequestScoped
public class CategoryBean {
	private String category;
	
	/* Getters & Setters */
}

Please note that PrettyFaces will automatically use the JSF converter registered for the type of the referenced bean property to convert the path parameter. This means that PrettyFaces supports all JSF standard converters and converters that have been manually registered to be used for a specific type using the converter-for-class element in the faces-config.xml (or the forClass attribute of the @FacesConverter annotation).

In PrettyFaces, each url-pattern compiles into a regular expression that is used to match incoming URLs. By default, any path-parameter expressions (#{...}) found in the URL pattern will be compiled into the regular expression: [^/]+, meaning that path-parameters do not match over the '/' character.

The time will come when you need to make a URL match more selectively, for instance, when you have two URLs that share the same parameter order. Or perhaps you need to take a very complex URL and parse it more selectively (matching across multiple '/' characters, for instance.) It is in scenarios like these when you should consider using a custom regular expression within your url-mapping pattern.

Let's consider, for instance, that you are building a blog application, where the URLs: /blog/2010/12/ and /blog/lincoln/23/ have the same number of parameters, but mean different things. One is going to display a list of articles from December 2010, and the other is going to display Lincoln's 23rd post. There has to be a way for the system to tell the two apart, and the answer is custum regex patterns.

<url-mapping id="archives">
  <pattern value="/#{ /\\d{4}/ year }/#{ /\\d{2}/ month }/" />
  <view-id value="/faces/blog/archives.jsf" /> 
</url-mapping>

This pattern specifies custom regular expressions for each path-parameter, the former must match exactly four digits (numbers 0-9), while the latter must match exactly two digits. In order to understand what this means, it might make sense to think of what the pattern would look like once it is compiled into a regular expression:

/(\\d{4})/(\\d{2})/

Below we see how to map the second of our two example URLs.

<url-mapping id="viewAuthorPost">
  <pattern value="/#{ /[a-z]+/ blogger }/#{ /\\d+/ postId }/" />
  <view-id value="/faces/blog/viewPost.jsf"/> 
</url-mapping>

Notice that this compiled pattern looks somewhat different from the first example URL:

/([a-z]+)/(\\d+)/

This is how you can completely customize and parse complex URLs (even capture values that include the '/' character.) Any path-parameter expression can accept a /custom-regex/, but the custom regex must be placed before any name or EL injections.

Sometimes you might want PrettyFaces to inject path parameters only on GET requests. This could be the case if you inject path parameters into a view-scoped bean and want to change these values at a later time.

You can use the onPostback attribute of url-mapping to specifiy if values should be injected on postbacks. Please note that the default value of the attribute is true.

<url-mapping id="viewCategory" onPostback="false">
  <pattern value="/store/#{ cat }/" />
  <view-id value="/faces/shop/store.jsf"/> 
</url-mapping>

Most people are already familiar with URL query-parameters. They come in key=value pairs, and begin where the '?' character is found in a URL. For example:

http://example.com/path?query=data

Here, 'query' is the name of the parameter, and 'data' is the value of the parameter; order of parameters does not matter, and if duplicates parameter names are encountered, an array of multiple values will be stored for that parameter.

While query-parameters are automatically stored in the HttpServletRequest parameter map, it may sometimes be convenient to also inject those values directly into JSF managed beans.

<query-param name="language"> #{bean.language} </query-param>

In this case, we must have a managed bean defined in our application; these beans can be registered either in JSF, Spring, Google Guice, or as shown here using CDI/Weld. Based on the configuration in this particular example, the bean must be named "bean", and must have an accessible field named "language".

<url-mapping id="store"> 
   <pattern value="/store/" /> 
   <view-id value="/faces/shop/store.jsf" />
   <query-param name="language"> #{bean.language} </query-param>
</url-mapping>

Any value matched by the query-parameter will be injected directly into the bean.

@Named("bean")
@RequestScoped
public class LanguageBean {
	private String language;
	
	/* Getters + Setters */
}

Please note that PrettyFaces will automatically use the JSF converter registered for the type of the referenced bean property to convert the query parameter. This means that PrettyFaces supports all JSF standard converters and converters that have been manually registered to be used for a specific type using the converter-for-class element in the faces-config.xml (or the forClass attribute of the @FacesConverter annotation).

In some situations you might want that PrettyFaces doesn't inject the value of a query parameter on JSF postbacks. A typical usecase for this would be a query parameter that is used to initially populate a bean property holding the value bound to an input component. In this case you will want the query parameter value to be injected only on the initial GET request but not on postbacks, because the postback will contain the submitted value of the input component.

You can use the onPostback attribute to tell PrettyFaces whether you want the query parameter to be injected on postbacks. Please note that the default value of the attribute is true.

<query-param name="query" onPostback="false">#{searchBean.query}</query-param>

Most of the time, when creating bookmarkable URLs, it is not enough to simply display a page to the user; we also need to load data to be shown on that page - allowing for pages to appear completely stateless to the end-user. This would typically be difficult in JSF, but PrettyFaces has another option to satisfy this requirement that breaks the coupling typically associated with other solutions such as using @SessionScoped data beans to save data across pages, or passing values across views using: <f:setPropertyActionListener/>.

Consider, for a moment, that we have a web-store, and would like to map a URL to display one specific item in that store:


Once we have defined our action method, it is very likely that situations will arise where we do not want to continue displaying the current page, say, when data could not be loaded, or if the user does not have sufficient permissions to perform the action; instead, we want to redirect the user to another page in our site.

This can be done by returning a JSF navigation-string, just like we would do from a normal JSF action-method. PrettyFaces integrated navigation can also be used to perform dynamic redirection.


The validation of path and query parameters is very important as they are directly modifiable by the user. Therefore PrettyFaces offers the possibility to attach validation code to each of your parameters.

Recently PrettyFaces added support to configure URL mappings via annotations. This feature is primarily intended for people who don't want to maintain a separate XML configuration file for the mappings and instead prefer to declare them directly on the affected classes.

To create a simple URL mapping, you must annotate one of your beans with a @URLMapping annotation. You will typically want to place this annotation on a class that is primarily responsible for the page.

@Named("bean")
@RequestScoped
@URLMapping(id = "store", pattern = "/store/", viewId = "/faces/shop/store.jsf")
public class StoreBean {
  /* your code */
}

You can see that the annotation attributes are very similar to the attributes of the url-mapping element in the PrettyFaces XML configuration file. Refer to Mapping a simple URL for details on the configuration of URL mappings.

If you want to use path parameters in the URL pattern, you can add these the same way as you would in pretty-config.xml.

@Named("bean")
@RequestScoped
@URLMapping(id = "categoryBean", pattern = "/store/#{ bean.category }/", viewId = "/faces/shop/store.jsf")
public class CategoryBean {
  
  private String category;
  
  /* Getters & Setters */ 
}

Sometimes you may want to declare multiple URL mappings on a single class. Unfortunately Java does not allow to add the same annotation to a class more than once. PrettyFaces offers a simple container annotation called @URLMapping that can be used in this case.

@Named("bean")
@RequestScoped
@URLMappings(mappings={
  @URLMapping(id = "categoryBean", pattern = "/store/#{ bean.category }/", viewId = "/faces/shop/store.jsf"),
  @URLMapping(id = "categoryBean2", pattern = "/shop/#{ bean.category }/", viewId = "/faces/shop/store.jsf")
})
public class CategoryBean {

  private String category;

  /* Getters & Setters */
}

PrettyFaces offers a very intuitive way to specify page actions with annotations. All you have to do is add a @URLAction annotation to the method you want to be executed.

@Named("bean")
@RequestScoped
@URLMapping(id = "viewItem", pattern = "/store/item/#{ bean.itemId }/", viewId = "/faces/shop/item.jsf")
public class CategoryBean {

  private String itemId;

  private Item item;

  @Inject 
  StoreItems items;

  @URLAction
  public String loadItem() {
    if ( itemId != null ) {
      this.item = items.findById(itemId);
      return null;
    }

    // Add a message here, "The item {..} could not be found."
    return "pretty:store";
  }

  /* Getters & Setters */
}

Note

If the class the annotated method belongs to declares multiple URL mappings using the @URLMappings annotation, the action will be used for each of the mappings.

The annotation supports all attributes that are available in the XML configuration file.

@URLAction(phaseId=PhaseId.RENDER_RESPONSE, onPostback=false)
public String loadItem() {
  // do something
}

Sometimes you might want to call methods on other beans than the bean annotated with the @URLMapping. In this case just refer to the foreign mapping using the mappingId attribute.

@Named("bean")
@RequestScoped
@URLMapping(id = "viewItem", pattern = "/store/item/#{ bean.itemId }/", viewId = "/faces/shop/item.jsf")
public class CategoryBean {
  /* some code */
}

@Named("otherBean")
@RequestScoped
public class OtherBean {

  @URLAction(mappingId = "viewItem")
  public void myAction() {
    /* your code */
  }

}

Note

If the class the annotated field belongs to declares multiple URL mappings using the @URLMappings annotation, the query parameter will be used for each of the mappings.

Validation is of major importance when processing any kind of user input. This also applies to path and query parameters as they are directly modifiable by the user.

The declaration of validation rules is very simple when using PrettyFaces annotations. To validate a query parameter with a standard JSF validator, you'll just have to add a @URLValidator annotation to the field.

@Named("bean")
@RequestScoped
public class LanguageBean {
 
  @URLQueryParameter("language")
  @URLValidator(validatorIds="com.example.LanguageValidator")
  private String language;
  
  /* Getters + Setters */
}

This example shows how to attach the com.example.LanguageValidator validator to the query parameter language. You can also specify a mapping to redirect to if the validation fails or attach multiple validators to the same query parameter.

@Named("bean")
@RequestScoped
public class LanguageBean {
 
  @URLQueryParameter("language")
  @URLValidator(onError="pretty:error",
      validatorIds= { "com.example.LanguageValidator", "com.example.OtherValidator" })
  private String language;
  
  /* Getters + Setters */
}

To validate path parameters, you have to add the @URLValidator to the @URLMapping and specify the index of the path parameter you are referring to.

@Named("bean")
@RequestScoped
@URLMapping(id = "viewItem", pattern = "/store/item/#{ bean.itemId }/", viewId = "/faces/shop/item.jsf",
  validation=@URLValidator(index=0, validatorIds="com.example.ItemIdValidator"))
public class CategoryBean {
  /* some code */
}

This will tell PrettyFaces to validate the first path parameter #{bean.itemId} with the validator com.example.ItemIdValidator.

PrettyFaces is required to access your managed beans in multiple ways. If you declare a page action to be executed for a specific URL, the framework will create a method expression and execute it. If you want to inject a query parameter into your bean, a value expression is created to write the value into the field.

All these actions require PrettyFaces to know the name of your beans in the EL context. In most cases this can be done by an auto-detection mechanism that supports the most common environments for defining managed beans. Currently PrettyFaces supports:

If you are using a non-standard way of defining managed beans within your application, the auto-detection will not work. In this case you'll have two options.

The first option is to use a @URLBeanName annotation on the class to explicitly tell PrettyFaces the name of the bean. The framework will then use this name to build EL expressions for this bean.

@URLBeanName("bean")
public class LanguageBean {
 
  @URLQueryParameter("language")
  private String language;
  
  /* Getters + Setters */
}

In this example PrettyFaces will create the EL expression #{bean.language} to access the language field.

The other option to tell PrettyFaces about your beans names is to implement a custom BeanNameResolver. PrettyFaces already ships with resolver implementations for the most common environments. If your environment is not supported, you can easily create your own resolver.

PrettyFaces follows a set order of events when processing each request. If a request is not mapped, or does not match a rewrite-rule, then the request will not be processed by PrettyFaces, and will continue normally.

  1. URL-matching, path-parameter parsing, query-parameter handling, and value injection into managed beans.

  2. DynaView calculation (if a view Id is dynamic, the EL method will be called.)

  3. PrettyFaces relinquishes control of the current request via RequestDispatcher.forward(“/context/faces/viewId.jsf”).

  4. If the view-ID is a JSF view, page-action methods are called after RESTORE_VIEW phase, unless the optional phaseId attribute is specified.

  5. The server continues to process the request as normal.

Navigation is a critical part of any web-application, and PrettyFaces provides simple integrated navigation - both with JSF, and in non-JSF environments (such as Servlets) that only have access to the HttpRequest object. PrettyFaces navigation is non-intrusive, and may be used optionally at your discretion.

Typically when navigating in JSF, one must define navigation cases (in JSF 1.x) or return the path to a view (in JSF 2.x). PrettyFaces, however, lets us simply reference the URL-mapping ID in these scenarios. Since the URL-mapping pattern already contains the locations of all required values (path-parameter EL expressions), these values are extracted from managed beans and used in URL generation.

Simply return a URL-mapping ID from any JSF action-method just as you would when using an <h:commandLink action="..." />. This outcome string should be prefixed by "pretty:" followed e.g.: "pretty:store", where "store" is our URL-mapping ID.

<url-mapping id="viewItem">
  <pattern value="/store/item/#{ iid : bean.itemId }/" />
  <view-id value="/faces/shop/item.jsf" />
  <action>#{bean.loadItem}</action>
</url-mapping>

Now look at our action-method; notice that if the item is found, we return null, signaling that no navigation should occur, but if the item cannot be loaded, we return "pretty:store" which is a PrettyFaces navigation outcome; PrettyFaces will intercept this outcome string, and redirect the user to the URL-mapping identified by "store" in pretty-config.xml.

public String loadItem() {
	if(itemId != null) {
		this.item = items.findById(itemId);
		return null;
	}

	// Add a message here, "The item {..} could not be found."
	return "pretty:store";
}

If any action method (JSF action-method or PrettyFaces page-actions) returns a mapping-ID that specifies path-parameters in the pattern, PrettyFaces will automatically extract values from bean locations specified in the pattern. The client browser will be redirected to the URL built using these values injected back into the pattern. For example:

<pattern value="/store/#{ cat : bean.category }/" />

Let's assume for a moment that #{ bean.category } contains the value, "shoes". if we return "pretty:viewCategory" from our action method, where "viewCategory" is the id of a URL-mapping in pretty-config.xml, PrettyFaces will extract the current value from the bean #{ bean.category } and use that value to generate a new URL:

/store/shoes/

This means that we can control the generated URLs directly from Java code. Simply set the value of the bean field (the field used in the URL pattern) to control the outcome:

public String loadItem() {
	...
	// Add a message here, "The item {..} could not be found."
	this.setCategory("shoes");
	return "pretty:viewCategory";
}

Tip

It is generally good practice to dedicate a separate bean to store page-parameters, this way, the same bean can be used throughout the application when setting values for PrettyFaces navigation.

PrettyFaces inbound URL rewriting provides seamless URL rewriting to all Servlets within the Context. This is the capability of intercepting and changing the location of the client's browser URL, modifying or replacing that URL entirely, and displaying a resource.

The table below outlines each of the individual rewrite-rule options:

OptionAllowed valuesUsage
inboundtrue/false (Default: true) Enable or disable inbound URL rewriting for this rule. Setting this value to false means that this rule will be ignored on incoming requests.
matcha regex (Optional) Describes, via a regular expression pattern, when this 'rewrite' rule should trigger on an inbound or outbound URL. If empty, this rule will match all URLs.
outboundtrue/false (Default: true) Enable or disable outbound URL rewriting for this rule. If enabled, any matching links encoded using HttpServletResponse.encodeURL() will be rewritten according to the rules specified.
processorqualified class name (Optional.) Specify a custom processor class to perform more complex, custom URL-rewriting. This class must implement the interface: 'com.ocpsoft.pretty.faces.rewrite.Processor'
public class CustomClassProcessor implements Processor {
	public static final String RESULT = "I PROCESSED!";

	public String process(final RewriteRule rewrite, final String url) {
		return RESULT;
	}
}
redirect301, 302, chain (Default: 301) Specifies which type of redirect should be issued when this rule triggers. If 'chain' is specified, a Servlet forward will be issued to the new URL instead of a redirect.
substitutelifecycle (Optional.) The regular expression substitution value of the "match" attribute. This effectively enables a "search and replace" functionality. Regular expression back-references to the match="..." attribute are supported in the URL, so using '$' and '/' characters may change the value of the result. See Rewriting URLs that match a specific pattern, for more details.
toCaseuppercase, lowercase, ignore (Default: ignore) Change the entire URL (excluding context-path and query- parameters) to 'UPPERCASE' or 'lowercase'.
trailingSlashappend, remove, ignore (Default: ignore) Control whether trailing slashes on a URL should be appended if missing, or removed if present.
urla well-formed URL (Optional.) Specify an well-formed URL to replace the current URL. This will overwrite the context-path and query-parameters. This attribute should usually be combined with redirect="301" (default), which is recommended to prevent adverse SEO effects, loss of page- rank.) Note: You must provide a fully qualified URL, including scheme (such as 'http://", 'ftp://', 'mailto:'). Regular expression back-references to the match="..." attribute are supported in the URL, so using '$' and '/' characters may change the value of the result. See Rewriting URLs that match a specific pattern, for more details.

Outbound URL-rewriting in provides natural integration with most existing URL components (including all of those from the JavaServer Faces framework.) When PrettyFaces is installed, any URL passed into HttpServletRequest.encodeRedirectURL(String url) will be processed by PrettyFaces outbound URL-rewriting.

Given the following URL-mapping, some of our JSF URLs will automatically be rewritten:

<url-mapping id="viewCategory">
	<pattern value="/store/#{ cat : bean.category }/" />
	<view-id value="/faces/shop/store.jsf" />
</url-mapping>

For example:

<h:link outcome="/faces/shop/store.jsf" value="View category: Shoes>
	<f:param name="cat" value="shoes" />
	<f:param name="lang" value="en_US" />
</h:link>

And:

<h:link outcome="pretty:viewCategory" value="View category: Shoes>
	<f:param name="cat" value="shoes" />
	<f:param name="lang" value="en_US" />
</h:link>

Will both produce the same output URL:

/store/shoes/?lang=en_US

Notice that even though we did not define a managed query-parameter, the resulting URL still contains the 'lang' parameter. This is because PrettyFaces only rewrites the named path-parameters defined in the URL-pattern; all other query-parameters are simply left unchanged.

Tip

Notice that all <f:param name="" value=""> elements contain both a name and a value attribute. These are required, since (unlike the <pretty:link> component,) <h:link> does not accept un-named parameters, even when passing a "pretty:mappingId" as the outcome.

This is due to the fact that PrettyFaces integration occurs **after** the original URL has already been rendered by the <h:link> component, intercepting the URL at the externalContext.encodeRedirectURL step, explained above (in the section "Rewriting URLs in Java".)

PrettyFaces provides several methods of generating HTML links via a set of components, and when operating in a JSF 2.0 environment, standard JSF 'h:link' components may be used instead. 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 path-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.

Caution

Mappings using DynaView functionality will not function with JSF link components.

Because PrettyFaces provides out-bound URL-rewriting, one can actually use standard JSF components such as <h:outputLink> in JSF 1.x, or <h:link> in JSF 2.x.


In JSF 2.x, you can achieve an even greater level of abstraction by using the mapping-ID in combination with <h:link>


As a modern web-framework, it is important to provide extension and integration points, enabling complex functionality that goes beyond the scope of the core framework itself. In order to do this, PrettyFaces provides several extension points, described below:

It may sometimes be necessary to provide custom configuration options in PrettyFaces. Loading URL-mappings from a database, for example, or generating mappings based on the state of the file-system. Due to this requirement, PrettyFaces offers an extension point for configuration providers to supply their own additional configuration to be merged with the master configuration at the time when PrettyFaces boots up. This is when you would implement a custom ConfigurationProvider.

public class MyConfigurationProvider implements ConfigurationProvider {
   
   public PrettyConfig loadConfiguration(ServletContext servletContext)
   {
      // add new configuration elements here
      return new PrettyConfig(); 
   }
}

To let PrettyFaces know about your provider, you'll have to register your implementation by creating a file named META-INF/services/com.ocpsoft.pretty.faces.spi.ConfigurationProvider in your classpath and add the fully-qualified class name of your implementation class there.

         META-INF/services/com.ocpsoft.pretty.faces.spi.ConfigurationProvider
         com.example.MyConfigurationProvider
       

After all configurations have been loaded and merged from any built-in or registered ConfigurationProvider sources, there is an additional opportunity to post-process the entire configuration, for instance, if you wanted to programmatically add security constraints to mappings based on various patterns. This is when you would implement a custom ConfigurationPostProcessor.

public class MyPostProcessor implements ConfigurationPostProcessor {
   
   public PrettyConfig processConfiguration(ServletContext servletContext, PrettyConfig config)
   {
      // make changes to the configuration here
      return config; 
   }
}

To let PrettyFaces know about your post-processor, you'll have to register your implementation by creating a file named META-INF/services/com.ocpsoft.pretty.faces.spi.ConfigurationPostProcessor in your classpath and add the fully-qualified class name of your implementation class there.

         META-INF/services/com.ocpsoft.pretty.faces.spi.ConfigurationPostProcessor
         com.example.MyPostProcessor
       

As part of the Annotations scanning (one of PrettyFaces' configuration methods,) it may sometimes be necessary to integrate with a custom bean-container that is not one of the built in containers supported natively. It is in these cases when you should implement a custom ELBeanNameResolver. The following example shows a resolver that will resolve the bean name by searching for a @Named annotation.

public class MyBeanNameResolver implements ELBeanNameResolver {
   
      public boolean init(ServletContext servletContext, ClassLoader classLoader) {
         // tell PrettyFaces that initialization was successful
         return true;
      }
   
      public String getBeanName(Class<?> clazz) {
         
         // try to find @Named annotation
         Named annotation = clazz.getAnnotation(Named.class);
         
         // return name attribute if annotation has been found
         if(annotation != null) {
            return annotation.value();
         }
         
         // we don't know the name
         return null;
         
      }
      
   }

To let PrettyFaces know about your resolver, you'll have to register your implementation by creating a file named META-INF/services/com.ocpsoft.pretty.faces.spi.ELBeanNameResolver in your classpath and add the fully-qualified class name of your implementation class there.

         META-INF/services/com.ocpsoft.pretty.faces.spi.ELBeanNameResolver
         com.example.MyBeanNameResolver
       

Contribute ideas to PrettyFaces by submitting a feature request or bug report. Join the team; check out the source code - submit a patch! Ask a question on the forums or the mailing list.

  1. Q. Can I use PrettyFaces to handle UrlRewriting for other (non-JSF) resources on my server?

    A. Yes. PrettyFaces still requires a configured JSF instance to function, but it can be used to map a URL to any resource in the Servlet Container – without invoking FacesServlet. Values will be injected into JSF beans as usual, but PrettyFaces Action methods will not trigger (since no JSF lifecycle executes for non-Faces requests.)


  2. Q. Why do my Tomahawk / MyFaces components, or other 3rd party add-ons, break when I use PrettyFaces?

    A. Since PrettyFaces intercepts mapped HttpRequests then forwards those requests to JSF, it is necessary to enable any additional filters between PrettyFaces and JSF to listen to Servlet Forwards. This is done in the web.xml deployment descriptor by adding the following dispatcher elements to any needed Filters:


  3. Q. Why, when using MyFaces, am I getting a NullPointerException when I try to use normal faces-navigation?

    A. Some MyFaces versions do not completely comply with the JSF specification, thus the ViewRoot is null when the request is processed. There is a patch/workaround, which can be added to fix this issue. You must add this ViewHandler to your faces-config.xml.

  4. Q. Can I configure PrettyFaces via Annotations?

    A. Yes – please refer to Annotation based configuration for details.

  5. Q. How do I enable logging, so that I can tell what the heck is really going on?

    A. Create or update your log4j.properties file with the following values:


  6. Q. Can I map and process URLs that span a dynamic number of ‘/’ characters?

    A. Yes, please read about custom path-parameter patterns.

  7. Q. How do I save FacesMessage objects after performing a redirect or pretty:redirect?

    A. You need to configure the optional MultiPageMessagesSupport PhaseListener (or something like it.) JBoss Seam provides a Messaging API that goes above and beyond JSF, providing this feature automatically.

    See Preserving FacesMessages across redirects or this article for a full explanation of how this works.

  8. Q. Does PrettyFaces work on IBM’s WebSphere?

    A. Yes, but websphere requires a custom setting in order to behave like a sane server.


  9. Q. Why are non-ASCII characters distorted when using mod_jk?

    A. This can happen because mod_jk partially reencodes the request URL after processing them inside Apache httpd and before forwarding it to Tomcat. You should set the following option to let mod_jk forward the unparsed URL.