May 17, 2015

RESTful JSF with POST-redirect-GET (part 5 of 5)


Pages: 1 2 3 4 5

What about optimistic locking?

As I briefly mentioned earlier, our application is implicitly ready for optimistic locking. There’s really not much left to do:

In the edit view, make sure to include an <h:inputHidden> field which is backed by the entity’s @Version annotated property, and handle OptimisticLockException accordingly.

Because the example implementation uses a mocked backend instead, I neglected this topic in the implementation.

True RESTful URLs with PrettyFaces

Our application now supports RESTful URLs, but they are still not truly RESTful as they work with GET request parameters rather than nested resource paths. For example, a URL such as myapp/faces/pages/customers/edit.xhtml?id=1 should really be written as myapp/customers/1. Good news is that with the PrettyFaces library in your path, you get this almost for free.

As the climax of our work, we will thus use PrettyFaces to get true RESTful URLs according to the Microformats definition. This will change the application’s URLs as follows:

Customers Payments
List /customers/ /customers/1/payments/


Edit /customers/1 /customers/1/payments/1


Create /customers/new /customers/1/payments/new



Here’s a little walkthough, based on the official documentation.

First of all, add PrettyFaces dependencies to your pom.xml.

Then, create a WEB-INF/pretty-config.xml page.

For the demo application, this is its complete content:
<pretty-config xmlns="http://ocpsoft.org/schema/rewrite-config-prettyfaces" 
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
               xsi:schemaLocation="http://ocpsoft.org/schema/rewrite-config-prettyfaces
                      http://ocpsoft.org/xml/ns/prettyfaces/rewrite-config-prettyfaces.xsd">
    
    <url-mapping>
        <pattern value="/customers/" />
        <view-id value="/faces/pages/customers/list.xhtml" />
    </url-mapping>
    <url-mapping>
        <pattern value="/customers/#{/[0-9]+/ id}" />
        <view-id value="/faces/pages/customers/edit.xhtml" />
    </url-mapping>
    <url-mapping>
        <pattern value="/customers/new" />
        <view-id value="/faces/pages/customers/edit.xhtml" />
    </url-mapping>
    
    <url-mapping>
        <pattern value="/customers/#{customer}/payments/" />
        <view-id value="/faces/pages/payments/list.xhtml" />
    </url-mapping>
    <url-mapping>
        <pattern value="/customers/#{customer}/payments/#{/[0-9]+/ id}" />
        <view-id value="/faces/pages/payments/edit.xhtml" />
    </url-mapping>
    <url-mapping>
        <pattern value="/customers/#{customer}/payments/new" />
        <view-id value="/faces/pages/payments/edit.xhtml" />
    </url-mapping>

</pretty-config>
After redeployment, really everything will work. In fact, not only will you be able to enter the new URL and still access the expected site, but thanks to what PrettyFaces calls “outbound-rewrite”, even link URLs generated by the application (<a href> values) will be rewritten (sort of reverse-engineered) to match the new URL patterns.

Here are a few lessons learnt:
  • The <url-mapping>s are checked in order and the first match wins. You should thus start with the most specialized pattern and then get more general.
  • However, you may find unexpected behavior if the first match is too specific. For example, if the two <url-mapping>s pointing to /faces/pages/customers/edit.xhtml were swapped, a link which should map to /customers/1 would map to /customers/new?id=1. I do not fully understand this behavior, but by swapping the definition, the problem disappeared. Note that instead I had to use regex matching for the parameter in order to overcome ambiguity.
  • Note that even trailing slashes in the URL matter! For example, /customers/1 works fine, but /customers/1/ would lead to a 404 Not Found error. This is actually in accord to the RESTful URL definitions mentioned earlier.
  • Rewriting <h:button> and <h:link> outcomes only works if every pattern path-parameter is matched by a non-disabled <f:param>. In the example application, this is especially true for the link in the Id column of the payments dataTable in /customers/list.xhtml and for the Reset button in payments/edit.xhtml. Note how I used the rendered attribute to create two versions of that button just to make sure that its params match the respective mapping:
<h:button value="Reset" outcome="edit.xhtml" rendered="#{empty paymentController.currentEntity.id}">
    <f:param name="customer" value="#{paymentController.currentEntity.customer.id}"/>
</h:button>
<h:button value="Reset" outcome="edit.xhtml" rendered="#{not empty paymentController.currentEntity.id}">
    <f:param name="id" value="#{paymentController.currentEntity.id}"/>
    <f:param name="customer" value="#{paymentController.currentEntity.customer.id}"/>
</h:button>
  • Finally, I found out that PretttyFaces does not support <a> anchor navigation (“#”). While this is perfectly valid for a true REST framework, it may nevertheless be a nice feature for integration with JavaScript actions or the like. Luckily, there’s a workaround:
In the example application, I’ve made it so that /customers/1/payments/ should redirect to /customers/1#payments. Currently, #payments is just an anchor on the page, but you could imagine it triggering e.g. a tab change on a JavaScript-controlled tab element.

The trick is to create a separate XHTML page (/payments/list.xhtml) which is then used in the respective <url-mapping> just like any other page URL. The page, however, consists of a single JavaScript function call which redirects to /customers/1#payments:
<h:head>
    <title>Payments</title>
    <h:outputScript library="js" name="purl.js" />
    <script type="text/javascript">
        var Navigation = {
            navigate : function() {
                var customer = purl().param("customer");
                if (typeof customer === 'undefined') {
                    customer = purl().segment(3);
                }
                window.location = '/' + purl().segment(1) + '/customers/' + customer + '#payments';
            }
        };
    </script>
</h:head>
<h:body>
    <o:onloadScript>Navigation.navigate();</o:onloadScript>
</h:body>
Here, I’m using the Purl helper library to parse the request URL. Note that I have to parse it for either the request parameter (that’s the case for an outbound-rewritten URL) or for a segment in the URL (that’s the case for a manually typed URL) to find the id of the customer to the edit.xhtml of which I have to redirect.

Conclusion

Building a true RESTful application in JSF is not trivial. Yes, there are a few pitfalls, and there’s no complete out-of-the-box recipe out there. But once the base has been built, your application will stand on a solid best-practices foundation. I think the most important part really is to think about the concepts behind REST, and then reason which parts of it will be useful for the application’s requirements. Adhering to the concepts of REST helps building a web application in a robust and clean way, and you should have very good reasons not to do so. Lack of JSF knowledge should not be one of those reasons.

I encourage you to check out the example application and adapt the techniques I presented here for your own application, as needed. If you run the application on your own server, remember that as the mocked backend services are just @SessionScoped CDI beans, you just have to invalidate the session in order to reset the entities. Please let me know whether this article has been helpful for you or if it lacks any important information. Also feel free to share your experience with “JSF on REST” in the comments section below. Than you for your interest in this blog post!

Updated June 21, 2015: part 3 / 4 restructured (“Restoring invalid null input”); included a link to the @ViewScoped source code on GitHub.


Pages: 1 2 3 4 5

6 comments:

  1. Congratulations for your article.
    It is very helpful and is the only one describing clearly the proper way to implement the navigation in a non-trivial jsf application.
    Could you please publish the projet after the part 3 (without the requestscoped evolutions) ?
    The two last parts are very interesting about the working of jsf but i think i prefer to stay with my viewscoped.
    I have another question : how to implement in a proper way the back navigation in a jsf application if a page can be called from different pages ?

    ReplyDelete
    Replies
    1. Thank you for your kind comment. I’m glad you found this article helpful. As per your request, I uploaded the @ViewScoped” version of the example to GitHub as well. You’ll find it here: https://github.com/codebulb/JsfRestfulPostRedirectGetViewScoped. It still includes PrettyFaces. If you want to get rid of that, you’ll have to delete pretty-config.xml plus the pom.xml dependencies and change the “anchor redirect” in payments/list to window.location = '/' + purl().segment(1) + '/faces/pages/customers/edit.xhtml?id=' + customer + '#payments';

      I’ll try to answer your question about back-navigation with multiple navigation paths here as well: This problem really comes from a disadvantage of RESTful navigation: As REST is stateless, there is no memory of a “previous” page. In practice, I see two solutions: one is to encourage the user to use the browser's back button; because navigation is stateless, it will always work. If you really want to navigate back using a link / button, you have to introduce a view param which identifies the previous page.

      Delete
  2. Thank you for your answer and the ViewScoped version, it is very helpful.

    Use the browser s back button seems to be the right way to return to the previous page.
    Is it possible to have a jsf component which simulates the browser back button (may be it already exists ...) ?

    ReplyDelete
    Replies
    1. You’re welcome. To invoke the browser’s back button, I would use plain stupid JavaScript ;-) <h:button value="Back" onclick="window.history.go(-1); return false;"/>

      Delete
  3. Ok thanks a lot. You are absolutely right it is really simple ;-)
    What about the save button ? This button invokes a method on the server and then redirect to the previous page : is it possible to use a similar way ?

    ReplyDelete
    Replies
    1. You’ll be pleased to find that this is quite simple as well. You can register an ajax listener for that button (<f:ajax>). The listener will execute the JavaScript code provided as soon as the method invoked which ajax returns. There should be some examples of its application on stackoverflow.com.

      PS: If you struggle to find answers to your JSF questions, you may want to check out my „Top links for JSF developers“ post (http://www.codebulb.ch/2015/03/top-tips-and-links-for-jsf-developers.html, shameless self-promo). OK, maybe the link list at ZEEF (https://jsf.zeef.com/bauke.scholtz) is even better.

      Delete