URL based Navigation into a JSF application – on PhaseListener and ViewHandler

Lucas Jellema 2
0 0
Read Time:5 Minute, 39 Second

Although most navigation takes place within the Java Server Faces application we are currently developing, some is from the outside. From an existing web application that handles the workflow for the end users, we will have to navigate to the JSF application – and not just to the homepage or start page, but deeplinking directly into specific pages more deep down in the JSF app. And before those pages can be displayed, data has to be prepared based on parameters passed from the external Web Application. This article will briefly show what we did to turn URL based navigation – GET or POST  with parameters – into JSF navigation.

In brief the solution is based on the following ideas....
:

  1. all navigation from outside the JSF application takes place to the same url, that will pass through the Faces Servlet. The url contains a parameter targetPage that contains a logical identifier for the page that should be navigated to; it may also contain additional parameters that help describe the context that should be set up for the target page. Note: the navigation can be initiated using a plain URL (http://url/faces/controller?targetPage=…&param1=….) and also through a POST request, by submitting an HTML form.
  2. a new PhaseListener is configured. This PhaseListener intercepts the requests to the controller url and redirects them. Before it actually executes the redirection, it may first set up the context by initializing business services and performing preliminary queries or derivations.

In a concrete example:

We can use a URL such as

http://host:8988/DirectNavigationTrial/faces/controller?targetPage=real3&param1=SomeParameterValue 

and type it directly in the browser’s location bar:

This will take us to whatever JSF page the PhaseListener associates with the label real3 that we set for the targetPage parameter. Note: there is no page, servlet or other resource that is called controller – it simply is a label that triggers the PhaseListener.

Alternatively – to show a more programmatic approach to navigation – we can create a simple HTML page that allows navigation to one out of four JSF pages, passing parameters into them:

 

The navigation is handled by the PhaseListener and takes to whatever JSF page is associated with the target page selected:

 

 

The Implementation

 

Let us check out the code behind this navigation handler. First of all, the HTML form:

    <form name="jsfNavigator" action="http://host:8988/DirectNavigationTrial/faces/controller" method="post">
      <table cellspacing="2" cellpadding="3" border="0" width="100%">
        <tr>
          <td>Target Page&nbsp;</td>
          <td>
            <select size="4" name="targetPage">
              <option value="real">Manager</option>
              <option value="real2">Inventory</option>
              <option value="real3">Order Trail</option>
              <option value="real4">MyAgenda</option>
            </select>
          </td>
        </tr>
        <tr>
          <td>Parameter 1&nbsp;</td>
          <td><input type="text" name="param1"/></td>
        </tr>
        <tr>
          <td>Parameter 2</td>
          <td><input type="text" name="param2"/></td>
        </tr>
      </table>
      <input type="submit" value="Go"/>
    </form>

The action attribute of the form refers to the same URL as we used before in the browser’s location bar. 

Then the configuration of the PhaseListener in the faces-config.xml file:

  <lifecycle>
    <phase-listener>directnavigation.RedirectPhaseListener</phase-listener>
  </lifecycle>
 

The code of the RedirectPhaseListener class:

package directnavigation;

import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

import javax.servlet.http.HttpServletRequest;

import nl.amis.jsf.navigation.NavigationBean;


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();
        ValueBinding vb =
            ctx.getApplication().createValueBinding("#{navigationBean}");
        NavigationBean nb = (NavigationBean)vb.getValue(ctx);
        String path = request.getPathInfo();
        if ("/controller".equals(path) ) {
            try {

                String newPageViewId=null;
                if ("real".equalsIgnoreCase(nb.getTarget()))
                  newPageViewId="/realpage.jspx";
                if ("real2".equalsIgnoreCase(nb.getTarget()))
                  newPageViewId="/realpage2.jspx";
                if ("real3".equalsIgnoreCase(nb.getTarget()))
                  newPageViewId="/otherrealpage3.jspx";

                UIViewRoot newPage =
                    ctx.getApplication().getViewHandler().createView(ctx,
                                                                     newPageViewId);
                ctx.setViewRoot(newPage);
                ctx.renderResponse();

            } catch (Exception e) {
                // TODO
            }
        }
    }

}

The RedirectPhaseListener class checks whether the URL ended with /controller. If so it is interpreted as a navigation request from the outside that will have passed in the targetPage parameter. This parameter is then read from the navigationBean’s target property. Its value is inspected and mapped to a real JSF page’s ViewId. This ViewId is then set as the new ViewRoot and the response is rendered. 

This class leverages one important managed bean: the navigationBean. This bean has three properties that contain the values of the targetPage, param1 and param2 request parameters that are either set in the URL or in the HTML Form. The NavigationBean is configured as managed bean like this:

  <managed-bean>
    <managed-bean-name>navigationBean</managed-bean-name>
    <managed-bean-class>nl.amis.jsf.navigation.NavigationBean</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
    <managed-property>
        <property-name>target</property-name>
        <value>#{param.targetPage}</value>
    </managed-property>
    <managed-property>
        <property-name>param1</property-name>
        <value>#{param.param1}</value>
    </managed-property>
    <managed-property>
        <property-name>param2</property-name>
        <value>#{param.param2}</value>
    </managed-property>
  </managed-bean>
 

The bean is in request scope – so it gets refreshed/created with each new request that is interested in it. It retrieves HTTP Request Parameters targetPage, param1 and param2 in its properties using EL expressions such as #{param.targetPage}.

Resources

Very useful was the information provided in the blog article POST-Redirect-GET pattern by BalusC (http://balusc.blogspot.com/2007/03/post-redirect-get-pattern.html ). Another useful source was this forum thread on the SUN Java Forums: http://forum.java.sun.com/thread.jspa?threadID=5273047 .

 

About Post Author

Lucas Jellema

Lucas Jellema, active in IT (and with Oracle) since 1994. Oracle ACE Director and Oracle Developer Champion. Solution architect and developer on diverse areas including SQL, JavaScript, Kubernetes & Docker, Machine Learning, Java, SOA and microservices, events in various shapes and forms and many other things. Author of the Oracle Press book Oracle SOA Suite 12c Handbook. Frequent presenter on user groups and community events and conferences such as JavaOne, Oracle Code, CodeOne, NLJUG JFall and Oracle OpenWorld.
Happy
Happy
0 %
Sad
Sad
0 %
Excited
Excited
0 %
Sleepy
Sleepy
0 %
Angry
Angry
0 %
Surprise
Surprise
0 %

Average Rating

5 Star
0%
4 Star
0%
3 Star
0%
2 Star
0%
1 Star
0%

2 thoughts on “URL based Navigation into a JSF application – on PhaseListener and ViewHandler

  1. Hi Rene,

    In this case, all requests come to one url: …./faces/controller?param1=…. – the request is intercepted and redirected. For mapping different URLs I probably would have used a Filter, just like you suggest.

    best regards,
    Lucas

  2. Hello Lucas,
    very nice article.
    I’m using the same approach but i was wondering how you are mapping the URLs without extensions to the FacesServlet? Are you using a Filter?

    Regards,
    René

Comments are closed.

Next Post

AMIS Query 20/3: Toon Koppelaars, Gerwin Timmermans & RuleGen

Nog drie dagen te gaan, voor je laatste kans in een verfrissende, ouderwetse, gezellige en enerverende kennisavond met Gerwin Timmermans (AMIS) en Toon Koppelaars (RuleGen). Meld je aan, click hier: "Business rules met Toon Koppelaars en Gerwin Timmerman".     Over de sprekers Toon Koppelaars is de ontwikkelaar van Rulegen, […]
%d bloggers like this: