JSF is a common used and widely spread Web-Framework with a lot of powerful features and a great community. But JSF also follows a concept which is not comparable to Action-Based or Request-Based Frameworks like MVC 1.0 or Spring MVC. The concept behind JSF is a so called Event- or Component-Based Framework (alternative therm is MVC-Pull). This approach gives you a lot of flexibility in developing web applications, but it also includes some problems. What you often see in JSF applications is, that URLs are not bookmarkable and you can not use the browsers History-Back button. This makes the behavior of JSF a little bit clumsy to the end-users. I will explain in this post how to solve this ‘problem’ in an elegant way without abusing JSF.
The Problem
The problem with the non bookmarkable URLs and the ugly situation that we can not use the Browser History-Back Button in a JSF application is founded in the so called Postback mechanism. Each time you use a JSF Command-Action like a <h:commandButton> or a <h:commandLink>, JSF generates a Form Post, computes the resulting web site internally and posts back the markup to the browser. This is the natural behavior of the HTTP POST method and very efficient because the browser is not forced to load a new page. But if you want to change the page content/url the user is faced with a usability problem. The following two pages illustrating the problem:
page1.xhtml:
... <h:body> <h:form> <h1>Page1</h1> <h:commandLink action="/page2">Go to Page2</h:commandLink> </h:form> </h:body>
page2.xhtml:
... <h:body> <h:form> <h1>Page2</h1> <h:commandLink action="/page1">Go to Page1</h:commandLink> </h:form> </h:body>
If you test this page example you will see that the browser URL did not correspond with the page you see. Command-Actions are very powerful and we need them in situations where we want to submit the user input. But for simple navigation there is another tag introduced in JSF 2.0 which should be used instead of a command action. The <h:link> and <h:button>. In the next example you can see how the two pages work when we use this new JSF component:
page1.xhtml:
... <h:body> <h1>Page1</h1> <h:link outcome="/page2">Link to Page2</h:link> </h:body>
page2.xhtml:
... <h:body> <h1>Page2</h1> <h:link outcome="/page1">Link to Page1</h:link> </h:body>
Now when the user clicks on one of the page links the browser url is updated correctly because a HTTP GET request is initiated. This is the correct way to implement a page navigation in JSF.
Rule No. 1: Never use a command-action to navigate between pages
The Action Controller
In most situations it is not sufficient to simply navigate between to pages. What we need is business logic to be called when the user clicks on the navigation link to open a new page. Command Actions providing a lot of functionality to solve this problem. And this is also the reason why command actions are often used in JSF. To control the outcome of a page after the user clicks on a action link is called an Action Controller. A Action Controller is in most cases a request-scoped CDI bean. So what we can do here is to bind the outcome attribute of the <h:link> component to a CDI Bean method like seen in the following example:
page1.xhtml:
... <h:body> <h1>Page1</h1> <h:link outcome="#{myActionController.action1()">Link to Page2</h:link> </h:body>
MyActionController.java:
@Named @RequestScoped public class MyActionController implements Serializable { private static final long serialVersionUID = 1L; public String action1() { // your code goes here..... return "/page2"; } }
The ugly part of this solution is that the action controller have to know the page name. So the action controller is tightly coupled to our JSF pages. (By the way, we see exactly the same coupling in MVC 1.0 examples.) In addition in this solution we need to make sure hat every link navigating to page2 is calling our action method. But a more complicating problem is that the action controller is not called if the user opens the page2 form a bookmarked URL!
So lets look on a better solution: We can place the ActionController directly into the page view. This can be done by the new JSF 2.0 component <f:event> inside a the JSF component <h:view>. With the f:event type we can specify the jsf life-cycle phase where the action controller should be called. See the next example:
page1.xhtml:
... <h:body> <f:view> <f:event type="preRenderView" listener="#{myActionController.init()}" /> <h1>Page1</h1> <h:link outcome="/page2">Link to Page2</h:link> </f:view> </h:body>
MyActionController.java:
@Named @RequestScoped public class MyActionController implements Serializable { private static final long serialVersionUID = 1L; public void init() { System.out.println("...initializing action controller...."); } }
This solution ensures that your ActionController is always called before the page is rendered.
Rule No. 2: Avoid binding controller methods to a navigation link
If you do some tests with the ActionController example you will notice that the init() method of the ActionController is not called if the user clicks the Browser History-Back Button to enter the page. This again is not a problem of JSF but of the caching behavior of Web Browsers. See a good blog post about this topic here.
The JSF Command-Action and Postbacks
Now lets take a look on the JSF command action. As I mentioned earlier the JSF command actions are useful if you want to submit the users input from a input form.
<h:form> <h1>Form1</h1> <h:inputText value="#{myActionController.orderDate}" > <f:convertDateTime pattern="dd.MM.yyyy" timeZone="CET"/> </h:inputText> <h:commandButton action="#{myActionController.submitOrder}" value="submit"/> </h:form>
In this example the submit action button triggers our action controller method ‘submitOrder()’. Thus a method typically implements our business logic to persist or update data. JSF exepcts a public method returning a String which points to the resulting page outcome. See the following example:
public String submitOrder() { // your code goes here.... return "/page2"; }
If you do some tests, you will see that a click on the submit button will produce a HTTP POST method call and the page content will result in the markup of page2. As this was a postback the browser URL has not updated. Another problem seen here is that we now have again the situation where our Action Controller have to now the page name.
To avoid this problems simple make use of another JSF command action attribute called ‘actionListener’. With EL 2.2 we can call any method of a CDI bean with or without parameter. The action attribute itself can now be used for the navigation part. In case we want to navigate to another page we still have the Postback problem. But JSF 2.0 allows to force a redirect by adding the query parameter ‘?faces-redirect=true’ at the end of the navigation path.
<h:commandButton actionListener="#{myActionController.submitOrderByListener()}" action="page2?faces-redirect=true" value="submit"/>
ActionListener method:
public void submitOrderByListener() { // your code goes here.... System.out.println("ActionListener called..."); }
The result of the faces-redirect=true is also known as the Postback/Redirect/Get (PRG) pattern. The browser will receive a HTTP result 302 with the new URL to be redirected to.
Rule No. 3: Use faces-redirect=true to force Posteback/Redirect/Get pattern
Conclusion
As you can see, JSF 2.0 is a powerful framework which can be used also to implement web application with a request-based behavior as it is typically for modern web applications. Take care of the following rules:
Rule No. 1: Never use a command-action to navigate between pages
Rule No. 2: Avoid binding controller methods to a navigation link
Rule No. 3: Use faces-redirect=true to force Posteback/Redirect/Get pattern
I hope this blog post will help someone to get out the most of JSF. If you have any ideas or questions post your comments.
Excellent! You are the first to explain the difference between h:link and commandLink. Thank you!
Now I know why the URL address did not update.
Q What is the best way to speed up page loads, should I use ajax in this example:
Hi, ajax is of course always the better (faster) way. But it can force you to use session scoped beans in the backend.
In my own application I mostly force a redirect when I use a managed bean to process business logic, with a call like this :
actionResult = defaultAction+ “?id=” + uniqueID + “&faces-redirect=true”;
This is when I need a h:command. This guaranties that my result url is always bookmarkable.
In all other cases I use h:link whenever it is possible.
And take a look at : @ConversationScoped
This can solve a lot of problems when users work with multiple browser tabs.