Knowledge brings fear ... Prosthetic Conscious

Tuesday May 05, 2009

Every wanted to access a JSF view by directly entering the view onto URL? This is a common request. In my case, I needed to link to specific places in my JSF app from a legacy non-JSF application. This is the type of thing you'd think would be straightforward, but JSF falls flat.

I scoured the web for solutions to this, and this is the simplest: implement a phase listener. In the interest of getting right to it, here it is:

public class RedirectPhaseListener implements PhaseListener {

    public RedirectPhaseListener() {
    }

    public PhaseId getPhaseId() {
        return PhaseId.RESTORE_VIEW;
    }

    public void afterPhase(PhaseEvent phaseEvent) {
    }

    public void beforePhase(PhaseEvent phaseEvent) {
        FacesContext ctx = phaseEvent.getFacesContext();
        HttpServletRequest request =
                (HttpServletRequest) ctx.getExternalContext().getRequest();

        String viewId = request.getParameter("viewId");

        if (viewId != null) {
            UIViewRoot page = ctx.getApplication().getViewHandler().createView(ctx, viewId);
            ctx.setViewRoot(page);
            ctx.renderResponse();

        }
    }
} 

The phase listener gets the request, and checks for a parameter. The parameter is the path to the view ID you want to visit. For example: /faces/someview.xhtml. With Facelets, these goto URLs end up looking funny, because the real view ID is in the URL also,

 /faces/home.xhtml?viewId=/faces/other.xhtml

Not nice, but it works. Other solutions I've seen try to get fancy by keeping a mapping a view+action result=new view. That's cleaner, and it's easy to do once you understand what's going on above.

Comments:

Or you could use seam if your project allows it. Much easier and much more expressive.

Posted by Fadzlan on May 06, 2009 at 08:08 AM PDT #

You can also check primefaces. it has xml-less navigation

http://www.rehberharitam.com/prime-showcase/optimus/source.jsf

Yigit.

Posted by yigit darcin on May 07, 2009 at 02:23 AM PDT #

Cool solution. It shows how relatively cleanly URL-based navigation can be added to JSF. What you are talking about here is addessability which is a fundamental aspect of HTTP's original design. It's not just legacy applications that benefit, but human users too. If done right this approach enables bookmarking and back/forward buttons to work as expected. The big limitation here is that many views in a non-trivial JSF application rely on session state from previous requests in the expected click path to that view. I do like this solution and I hope it encourages JSF developers to rely a lot less on session state.

Posted by Alain O'Des on May 07, 2009 at 03:44 AM PDT #

JSF also lets you create your own NavigationHandler and plug it in through faces-config.xml. This way, you could make your action methods return a string that is prefixed with url: ("url:MyPage.jsf") and have it navigate to the url. If there is no "url:" prefix, just delegate to the base navigation handler which will look up the navigation rule.

Posted by Geoffrey Longo on May 07, 2009 at 04:57 AM PDT #

Ignore my previous comment, I missed the part about coming from a non-JSF application.

Posted by Geoffrey Longo on May 07, 2009 at 05:04 AM PDT #

Perhaps I'm missing something, but I don't see what this accomplishes. Can you provide an example of the use case?

Posted by Kito D. Mann on May 13, 2009 at 03:15 PM PDT #

kito,

in JSF, the view ID is not determined by the URL. you can't hit http://foo.bar/../a.xhtml and go to view a.xhtml. it will go to whatever view JSF has internally set to be the current view.

it is common however to have a non-JSF app that needs to link to view a.xhtml directly. that's where this type of solution comes in. i understand there are JSF extension and JSF-like frameworks that allow this, but pure JSF does not.

Posted by jeff on May 13, 2009 at 03:22 PM PDT #

Post a Comment:
  • HTML Syntax: NOT allowed