Thursday May 07, 2009

JSF Actions on the URL

In a recent entry, I described a mechanism for passing a JSF view ID as a GET parameter to force JSF to a particular view. This entry will improve on that by allowing a JSF action to be specified a a GET parameter.

After writing the previous entry, I started thinking ... JSF already has a mapping from action to view ID, why not make use of that to simplify the URL? Instead of:

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

we have this,

/faces/home/xhtml?jsf.action=success

So, from the current view of /faces/home.xhtml, and action success, go to the view defined in faces-config.xml's navigation rules. For example,

     <navigation-rule>
        <from-view-id>/faces/summary.xhtml
        <navigation-case>
            <from-outcome>success</from-outcome>
            <to-view-id>/admin/facelet/finished.xhtml</to-view-id>
        </navigation-case>
        ...
     ...

If our current view is /faces/summary.xhtml, and the jsf.action=success parameter is present, then move to view ID /facelet/finished.xhtml. This is rather slick as it makes use of existing rules; no additional configuration is required. Anyway, here's the code:

public class ActionPhaseListener implements PhaseListener {

    public RedirectPhaseListener() {
    }

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

    public void beforePhase(PhaseEvent phaseEvent) {
    }

    public void afterPhase(PhaseEvent phaseEvent) {
        if (navigationRules == null) {
            navigationRules = new NavigationRules();
        }

        FacesContext ctx = phaseEvent.getFacesContext();
        HttpServletRequest request =
                (HttpServletRequest) ctx.getExternalContext().getRequest();

        String action = request.getParameter("jsf.action");

        if (action != null) {
            String currentViewId = ctx.getViewRoot().getViewId();
            NavigationRule nr = navigationRules.getNavigationRules().get(currentViewId);
            if (nr == null) {
                nr = navigationRules.getNavigationRules().get(null);
            }
            NavigationCase nc = nr.getNavigationCases().get(action);
            if (nc != null) {
                String newViewId = nc.getToViewId();
                UIViewRoot page = ctx.getApplication().getViewHandler().createView(ctx, newViewId);
                ctx.setViewRoot(page);
                ctx.renderResponse();
            }
        }
    }

    private NavigationRules navigationRules = null;
} 

in the interest of not boring you, NavigationRules.java is linked. It reads faces-config.xml by using ServletContext.getResource() and parses it into bean-like objects using JDOM.

Alain commented on my previous post that the solution there was problematic as it allowed access to arbitrary view which may depend on objects being set up in other views (think of a work flow / wizard). This solution has no such problem, as you can only navigate according to the rules you've already defined in faces-config.xml.



About

jtb

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today