25
UPDATE: For a missing piece of code, please see this entry.
In one of our applications at work, we needed to be able to deep link to certain pages to allow external applications to get at specific pieces of data, product and order information to be specific. Since JSF 1.x does not support HTTP GET requests, this poses a problem. In order to get (no pun intended) information to the backing bean for processing, the data would have to be POSTed. This obviously makes bookmarking the resulting page useless. Our initial solution was to write a servlet filter which would get a reference to the current FacesContext, then get a reference to the appropriate JSF managed bean, and pump data into it. This actually worked rather well, but, at Ed Burns’ suggestion, I decided to reimplement this a JSF PhaseListener (many thanks to Ryan Lubke for his help!). A PhaseListener is “an interface implemented by objects that wish to be notified at the beginning and ending of processing for each standard phase of the request processing lifecycle.”
At any rate, what I was able to do is register a PhaseListener to execute on the RESTORE VIEW phase (for a GET request, the only two phases that run are the RESTORE VIEW and RENDER RESPONSE phases). In a nut shell, what the beforePhase() method does is this:
- Get the Request URI and break it into parts. The URI is expected to be in the format /Context/application/parm1/parm2/parm3/…
- The “application” is extracted from the URI, and the rest is passed off to the appropriate handler
- Inside the handler, a reference to the managed bean for the “application” is retrieved from the FacesContext
- As appropriate for each handler, data is pulled from the URI and injected, via setters, into the managed bean
- The ViewRoot is set for the appropriate output page. If this is not done, the server will return a 404, as /Context/application does not exist on the filesystem.
- The method returns, as does beforePhase(), and the lifecycle is completed.
What comes out the other end is the expected pages, just as if someone had filled out a form and clicked submit. It’s really quite nifty. Of course, all of that is pretty tough to follow with out some code, so here we go. First, let’s register the PhaseListener:
Next, let’s look at the PhaseListener itself:
There’s one more step. Currently, our application is only configured to map *.jsf to the FacesServlet, so we’ll need to add a couple more mappings to make our “virtual” URLs work. This goes in web.xml:
If you had the rest of our code, you should now be able to deploy the web page and point your browser at /Context/product/ABC123/tab and learn all about one of our products.
Since you don’t have the rest of the app, you obviously can’t do that, but hopefully I’ve provided enough information for you to implement a similar solution.
As always, any comments and enhancements are much appreciated.
Popularity: 25% [?]
Why not use the param implicit object to inject values in your backing bean? Maybe I’m missing something…
May 9th, 2006 | #
We didn’t use that for a couple of reasons. The first being we weren’t aware of that functionality.
It wouldn’t have mattered if were, though, for the second reason: pretty URLs. “Pretty URLs” were a requirement, so we had to find a way to do it, and that led us to the solution above.
May 10th, 2006 | #
Thanks Jason for the very nice and easy to read post on implementing a phase listener. We have incorporated this into our new dashboard application. This will allow our users to have a “Pretty” bookmarkable url for their favorite link within the site. Thanks again!
March 27th, 2007 | #
What about Type-Convertion?
This is normally done while in Validation-Phase.
The
https://javaserverfaces.dev.java.net/sandbox/components/prettyUrl.html
just sets Strings:
11:25:01,815 ERROR [STDERR] javax.faces.el.EvaluationException: Exception setting property companyGroupNumber of base with class com.retail_sc.style.marketsharecode.Style
11:25:01,815 ERROR [STDERR] at org.apache.myfaces.el.PropertyResolverImpl.setValue(PropertyResolverImpl.java:188)
11:25:01,815 ERROR [STDERR] at org.apache.myfaces.el.ValueBindingImpl.setValue(ValueBindingImpl.java:278)
11:25:01,815 ERROR [STDERR] at com.sun.faces.sandbox.util.PrettyUrlPhaseListener.beforePhase(PrettyUrlPhaseListener.java:97)
Is there any chance to set the Value the bean expect (here: Short)?
May 14th, 2007 | #
Stefan, I had noticed that shortcoming too. Let me see what I can do about that.
May 14th, 2007 | #