OCPSoft.com - Simple SolutionsCommunity Documentation

Chapter 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

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.

The URL mapping element is the core configuration element for PrettyFaces most applications. Let's consider, for instance, that we are building a web-store and we want our users to access the store at the URL /store/ - we only need the most basic features of the URL-mapping to achieve this.

<url-mapping id="store"> 
   <pattern value="/store/" /> 
   <view-id value="/faces/shop/store.jsf" />
</url-mapping>

With these two simple lines of configuration, the user sees: pattern="/store/" in the browser URL and in the output HTML, but the server is actually rendering the resource: /faces/shop/store.jsf (the actual location of the page on the server.)

Ignore the id="store" attribute for now, we'll cover that under navigation. Which is useful when redirecting a user between different pages in your application.

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>

Frequently, you may find that several - or all - of your mappings share a common base-URL. It is on these occasions when you should consider using a parent URL-mapping by using the parentId attribute.

<url-mapping parentId="store" id="category"> ... </url-mapping>

Notice that the parentId attribute must refer to a mappingId of another URL-mapping. See examples below:

<url-mapping id="store"> 
   <pattern value="/store/" /> 
   <~-- Result: /store/ -->
   <view-id value="/faces/shop/store.jsf" />
</url-mapping>

<url-mapping parentId="store" id="category"> 
   <pattern value="/#{category}" /> 
   <~-- Result: /store/#{category} -->
   <view-id value="/faces/shop/category.jsf" /> 
</url-mapping>

<url-mapping parentId="category" id="item"> 
   <pattern value="/#{item}" /> 
   <~-- Result: /store/#{category}/#{item} -->
   <view-id value="/faces/shop/item.jsf" /> 
</url-mapping>

<url-mapping parentId="category" id="sales"> 
   <pattern value="/sales" /> 
   <~-- Result: /store/#{category}/sales -->
   <view-id value="/faces/shop/sales.jsf" /> 
</url-mapping>

Child mappings will inherit all properties from the parent mapping. This includes the pattern, validators and query parameters. URL actions won't be inherited per default. If you want an action to be inherited by child mappings, set it's inheritable attribute to true.

<url-mapping id="store"> 
   <pattern value="/store/" /> 
   <view-id value="/faces/shop/store.jsf" />
   <action inheritable="true">#{storeBean.someAction}</action>
</url-mapping>

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

Dynamic view-IDs (referred to as DynaView) allow us to determine (at the time the page is requested) the page our users should see for a given URL-mapping pattern.

<url-mapping id="home">
	<pattern value="/home/" />
	<view-id value="#{bean.getViewPath}" />
</url-mapping>
		

Notice that we've used an EL method-expression where we would normally have specified a view-ID. PrettyFaces will invoke this method-expression, and use the result of the method as a final URL to be displayed by the server. In a sense, we're asking the system to figure out which page to display, and telling it that it can get the answer by calling our method:

@Named("bean")
@RequestScoped
public class HomeBean {
	
	@Inject CurrentUser user;
	
	public String getViewPath() {
		if ( user.isLoggedIn() )
		{
			return "/faces/home/home.jsf";
		}
		
		return "/faces/login.jsf";
	}
}

Here, our method #{bean.getViewPath} is going to check the current user's logged-in status, display the home page if he/she is logged in, or display the login page if they are not.

Caution

Automatic out-bound URL-rewriting will not function on pages with dynamic view-ID's.

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.