Simple, effective, bookmark, JSF
PrettyFaces is an OpenSource JSF extension which enables creation of bookmark-able, pretty URLs made easy. Our goal was to solve this problem as simply as possible, while still enabling a useful set of functions such as: page-load actions, integration with faces navigation, dynamic view-id assignment, and managed parameter parsing. All of this without introducing unnecessary coupling.
Downloads: ↓Dependencies:J2EE5 and JSF Coming soon:Updated demos. |
PrettyFaces requires separate downloads of dependencies. Also, we encourage feedback for all of our products, so please, if you have questions, concerns, suggestions, or other ramblings - email the user-group, or open an issue.
|
Features:
- Replace jsf views (/faces/page.jsf) with pretty URLS (/mapped/page): The client browser never sees your JSF View ID
- De-coupling, separation of URL and View ID. Now with dynamic view ID mapping (through El method expressions) you can show different pages based on runtime conditions.
- Page load actions call one or more actions before rendering a view.
- Managed parameter parsing: HTTP parameters parsed from URLs are stored in managed beans for easy access from other Java classes, simply get a reference to your parameter bean and you have full access to your HTTP parameters.
- Navigation by ID: Define your view IDs in one place, reference them by assigned IDs
- Easy to configure. Requires only one consolidated XML configuration file.
What makes PrettyFaces different:
Read the full article, here.
Configuration
Setting up PrettyFaces is simple.
- 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
0. Extract necessary JAR files into your /WEB-INF/lib directory
This step is pretty straight-forward, right?
1. Add the <filter> and <filter-mapping> to /WEB-INF/web.xml
<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> </filter-mapping>
2. Create the /WEB-INF/pretty-config.xml file
<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> <view-id> /faces/story/viewStory.jsf </view-id> <action>#{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>
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)
- <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)
- <view-id>#{someBean.methodName}<view-id> - Specify the JSF view ID 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 using a literal value. This element is required, and has a multiplicity of 1 (only one)
- <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)
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 called before RESTORE_VIEW phase, unless the optional phaseId attribute is specified.
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.
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.
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 can 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 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"; } }
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 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}--> <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 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.
Finished. Run your application!
You should now have a fully functional PrettyFaces configuration.
