Package org.omnifaces.facesviews

This package contains the classes for the OmniFaces FacesViews feature.

Introduction

FacesViews is a feature where a special dedicated directory (/WEB-INF/faces-views), or optionally one or more user specified directories, can be used to store Facelets source files.

All files found in these directories are automatically mapped as Facelets files and made available using both their original extension as well as without an extension (extensionless). Optionally scanning can be restricted to include only certain extensions.

With FacesViews, there is thus no need to list all Facelets views that should be accessed without an extension in some configuration file. Additionally, it thus automatically maps Facelets files to their original file extension, which prevents exposing the source code of those Facelets that happens with the default Faces mapping.

Scanning is done automatically and thus no further configuration is needed. The feature is compatible with applications that don't have web.xml or faces-config.xml configuration files. As such, it can be used as an alternative to declaring the FacesServlet in web.xml for the .xhtml to .xhtml mapping.

Example 1:

Consider the following file structure and assume no further configuration has been done:

   /WEB-INF/faces-views/index.xhtml
   /WEB-INF/faces-views/users/add.xhtml
   /normal.xhtml
 

This will make the Facelets available via the following URLs (given a root deployment on domain example.com):

   example.com/index
   example.com/users/add
   example.com/index.xhtml (will redirect to /index by default)
   example.com/users/add.xhtml (will redirect to /users/add by default)
   example.com/normal.xhtml
 

Note that although the directory outside /WEB-INF/faces-views is not scanned, the FacesServlet is mapped on all extensions found in /WEB-INF/faces-views, so this will also affect files outside this directory. In the above example normal.xhtml is thus also available via the .xhtml extension, since the whole FacesServlet is mapped on this.

Also note that the extension variants of the scanned views will redirect to the extensionless variants. This behavior can be changed (see below), so that these views are either directly available (no redirect) or are not available at all.

Example 2:

Consider the following web.xml:

   <context-param>
       <param-name>org.omnifaces.FACES_VIEWS_SCAN_PATHS</param-name>
       <param-value>/*.xhtml</param-value>
   </context-param>
 

And this file structure:

   /page1.xhtml
   /foo/page2.xhtml
   /WEB-INF/resources/template.xhtml
   /script.js
 

This will make the Facelets available via the following URLs (given a root deployment on domain example.com):

   example.com/page1
   example.com/foo/page2
   example.com/page1.xhtml (will redirect to /page1 by default)
   example.com/foo/page2.xhtml (will redirect to /foo/page2 by default)
 

Note that in the above example, /WEB-INF was NOT scanned and thus template.xhtml is not made publicly available. Likewise /script.js was also not scanned since it doesn't have the configured extension (.xhtml). Finally, although a web.xml was used, there does not need to be a mapping for the FacesServlet in it.

Example 3:

Consider the following web.xml:

   <context-param>
       <param-name>org.omnifaces.FACES_VIEWS_SCAN_PATHS</param-name>
       <param-value>/*.xhtml/*</param-value>
   </context-param>
 

And this file structure:

   /page1.xhtml
   /foo/page2.xhtml
 

This will make the Facelets available via the following URLs (given a root deployment on domain example.com):

   example.com/page1
   example.com/foo/page2
   example.com/page1/foo (will forward to /page1 with dynamic path parameter "foo")
   example.com/page1/foo/bar (will forward to /page1 with dynamic path parameters "foo" and "bar")
   example.com/foo/page2/bar (will forward to /foo/page2 with dynamic path parameter "bar")
   example.com/foo/page2/bar/baz (will forward to /foo/page2 with dynamic path parameters "bar" and "baz")
   example.com/page1.xhtml (will redirect to /page1 by default)
   example.com/foo/page2.xhtml (will redirect to /foo/page2 by default)
 

The path parameters are injectable via @Param in the managed bean associated with the page. Below example shows how they are injected in the managed bean associated with /page1.xhtml.

   @Inject @Param(pathIndex=0)
   private String foo;

   @Inject @Param(pathIndex=1)
   private String bar;
 

The support for this so-called MultiViews feature which gets triggered when the path pattern is suffixed with /* was added in OmniFaces 2.5 and is not available in older versions.

Example 3:

Consider the following web.xml:

   <context-param>
       <param-name>org.omnifaces.FACES_VIEWS_SCAN_PATHS</param-name>
       <param-value>/*.xhtml, /foo/*</param-value>
   </context-param>
 

And this file structure:

   /page1.xhtml
   /foo/page2.xhtml
 

This will make the Facelets available via the following URLs (given a root deployment on domain example.com):

   example.com/page1
   example.com/foo/page2
   example.com/foo/page2/bar (will forward to /foo/page2 with dynamic path parameter "bar")
   example.com/foo/page2/bar/baz (will forward to /foo/page2 with dynamic path parameters "bar" and "baz")
   example.com/page1.xhtml (will redirect to /page1 by default)
   example.com/foo/page2.xhtml (will redirect to /foo/page2 by default)
 

Note that on contrary to previous example example.com/page1/foo and example.com/page1/foo/bar are not available as the MultiViews feature is only enabled on /foo/*.

Welcome files

If a <welcome-file> is defined in web.xml that's scanned by FacesViews AND REDIRECT_TO_EXTENSIONLESS is used (which is the default, see below), it's necessary to define an extensionless welcome file to prevent a request to / being redirected to /[welcome file]. E.g. without this https://example.com will redirect to say https://example.com/index.

For example:

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

If you're using the MultiViews feature on a site-wide basis and have a welcome file configured for it, then basically any request which doesn't match any physical file will end up in that welcome file. In case this is undesirable, because you're having e.g. a REST API listening on /api/* or a websocket endpoint listening on /push/*, then you can configure them as an exclude pattern as below:

   <context-param>
       <param-name>org.omnifaces.FACES_VIEWS_SCAN_PATHS</param-name>
       <param-value>/*.xhtml/*, !/api, !/push</param-value>
   </context-param>
 

Dispatch methods

Faces normally inspects the request URI to derive a logical view id from it. It assumes the FacesServlet is either mapped on a prefix path or an extension, and will get confused when an extensionless "exactly mapped" request is encountered. To counter this, FacesViews makes use of a filter that intercepts each request and makes it appear to Faces that the request was a normal extension mapped one.

In order to do this dispatching, two methods are provided; forwarding, and wrapping the request and continuing the filter chain. For the last method to work, the FacesServlet is programmatically mapped to every individual resource (page/view) that is encountered. By default the filter is automatically registered and is inserted after all filters that are declared in web.xml.

These internal details are important for users to be aware of, since they greatly influence how extensionless requests interact with other filter based functionality such as security filters, compression filters, file upload filters, etc.

With the forwarding method, filters typically have to be set to dispatch type FORWARD as well. If the FacesViews filter is the first in the chain other filters that are set to dispatch type REQUEST will NOT be invoked at all (the chain is ended). If the FacesViews filter is set to be the last, other filters will be invoked, but they should not modify the response (a forward clears the response buffer till so far if not committed).

No such problems appear to exist when the FacesViews filter simply continues the filtering chain. However, since it wraps the requess there might be unforeseen problems with containers or other filters that get confused when the request URI changes in the middle of the chain. Continuing the chain has been tested with JBoss EAP 6.0.1, GlassFish 3.1.2.2, WebLogic 12.1.1 and TomEE 1.5.2-snapshot and thus with both Mojarra and MyFaces. However, since it's a new method for OmniFaces 1.4 we kept the existing forward as an alternative.

The configuration options below provide more details about the dispatch methods and the filter position which can be used for tweaking FacesViews for interoperability with other filters.

Configuration

The following context parameters are available.

All available context parameters
"org.omnifaces.FACES_VIEWS_ENABLED" Used to completely switch scanning off.
Allowed values: {true,false}
Default value: true
(note that if no /WEB-INF/faces-views directory is present and no explicit paths have been configured, no scanning will be done either)
"org.omnifaces.FACES_VIEWS_SCAN_PATHS" A comma separated list of paths that are to be scanned in addition to /WEB-INF/faces-views.
Allowed values: any path relative to the web root, including the root path (/) and /WEB-INF. A wildcard can be added to the path, which will cause only files with the given extension te be scanned.
Examples:
- Scan all files in both folder1 and folder2: /folder1, /folder2
- Scan only .xhtml files in the root: /*.xhtml
Note that when the root path is given, all its sub paths are also scanned EXCEPT WEB-INF, META-INF and resources. If those have to be scanned as well, they can be added to the list of paths explicitly.
Default value: /WEB-INF/faces-views (note when this value is set, those paths will be in addition to the default /WEB-INF/faces-views)
"org.omnifaces.FACES_VIEWS_SCANNED_VIEWS_ALWAYS_EXTENSIONLESS" Used to set how scanned views should be rendered in Faces controlled links. With this setting set to false, it depends on whether the request URI uses an extension or not. If it doesn't, links are also rendered without one, otherwise they are rendered with an extension. When set to true links are always rendered without an extension.
Default value: true
"org.omnifaces.FACES_VIEWS_EXTENSION_ACTION" Determines the action that is performed whenever a resource is requested WITH extension that's also available without an extension.
Allowed values are enumerated in ExtensionAction, which have the following meaning:
- REDIRECT_TO_EXTENSIONLESS: Send a 301 (permanent) redirect to the same URL, but with the extension removed. E.g. /foo.xhtml redirects to /foo.
- SEND_404: Send a 404 (not found), makes it look like e.g. /foo.xhtml never existed and there's only /foo.
- PROCEED: No special action is taken. Both /foo.xhtml and /foo are processed as-if they were separate views (with same content).
Default value: REDIRECT_TO_EXTENSIONLESS
"org.omnifaces.FACES_VIEWS_PATH_ACTION" Determines the action that is performed whenever a resource is requested in a public path that has been used for scanning views by faces views (e.g. the paths set by "org.omnifaces.FACES_VIEWS_SCAN_PATHS", but excluding the root path /).
Allowed values are enumerated in PathAction, which have the following meaning:
- SEND_404: Send a 404 (not found), makes it look like e.g. /path/foo.xhtml never existed and there's only /foo and optionally /foo.xhtml.
- REDIRECT_TO_SCANNED_EXTENSIONLESS: Send a 301 (permanent) redirect to the resource corresponding with the one that was scanned. E.g. /path/foo.xml redirects to /foo.
- PROCEED: No special action is taken. /path/foo.xml and /foo (and optionally /foo.xhtml) will be accessible.
Default value: SEND_404
"org.omnifaces.FACES_VIEWS_FILTER_AFTER_DECLARED_FILTERS" Used to set whether the FacesViewsForwardingFilter should match before declared filters (false) or after declared filters (true).
Default value: false (the FacesViews forwarding filter is the first in the filter chain)
"org.omnifaces.FACES_VIEWS_LOWERCASED_REQUEST_URI" Used to set whether the request URI should only match the lowercased form of the file name. By default, a scanned view of for example /TitleCasedFileName.xhtml will listen to a request URI of /TitleCasedFileName, but when this setting is set to true, then it will instead listen to a lowercased request URI of /titlecasedfilename.
Default value: false (the request URI must exactly match the letter case of the file name)
Author:
Arjan Tijms