I had a need to dynamically determine what page to return to in a JSF application. There has to be an easier way, but here's how I wanted to do it. I wanted to put an EL expression in my <to-view-id> nav rules in the JSF config file (e.g. faces-config.xml). This isn't allowed. So I built my own custom ViewHandler to do it.
1) Define the ViewHandler in faces-config.xml:
<application>
...
<view-handler>com.msd.tts.pluggable.MyDynamicViewHandler</view-handler>
</application>
2) Create the class:
package com.msd.tts.pluggable;
import java.io.IOException;
import java.util.Locale;
import javax.faces.*;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.*;
import javax.faces.el.ValueBinding;
import com.sun.faces.util.Util;
/**
* Adds ability to put EL expressions in the <to-view-id> Navigation Rules in JSF
* configuration file (faces-config.xml).
*
* @author Craig Peters
* @version 1.0
*
*/
public class MyDynamicViewHandler extends ViewHandler {
/**
* The original handler we are customizing.
*/
private ViewHandler prevHandler = null;
/** Creates a new instance of MappingViewHandler. By including
* a parameter of the same type, we encourage the JSF framework
* to pass a reference of the previously used ViewHandler. This way
* we can use all the previous functionality and override only the
* method we are interested in (in this case, the getActionURL() method).
*/
public MyDynamicViewHandler(ViewHandler prevHandler) {
this.prevHandler = prevHandler;
}
@Override
public String getActionURL(FacesContext context, String viewId) {
return prevHandler.getActionURL(context, viewId);
}
@Override
public Locale calculateLocale(FacesContext context) {
return prevHandler.calculateLocale(context);
}
@Override
public String calculateRenderKitId(FacesContext context) {
return prevHandler.calculateRenderKitId(context);
}
@Override
public UIViewRoot createView(FacesContext context, String viewId) {
return prevHandler.createView(context, viewId);
}
@Override
public String getResourceURL(FacesContext context, String path) {
return prevHandler.getResourceURL(context, path);
}
@Override
public void renderView(FacesContext context, UIViewRoot viewToRender)
throws IOException, FacesException {
String result = viewToRender.getViewId();
if (Util.isVBExpression(viewToRender.getViewId())) {
ValueBinding vb = context.getApplication().createValueBinding(viewToRender.getViewId());
result = vb.getValue(context).toString();
}
if (result.charAt(0) != '/')
throw new IllegalArgumentException("Illegal view ID " + result + ". The ID must begin with '/'");
viewToRender.setViewId(result);
prevHandler.renderView(context, viewToRender);
}
@Override
public UIViewRoot restoreView(FacesContext context, String viewId) {
return prevHandler.restoreView(context, viewId);
}
@Override
public void writeState(FacesContext context) throws IOException {
prevHandler.writeState(context);
}
}
3) Use EL in navigation rules:
<navigation-rule>
<from-view-id>/CommonForm.jsp</from-view-id>
<navigation-case>
<to-view-id>#{bean.caller}</to-view-id>
</navigation-case>
</navigation-rule>
The "bean.caller" method will have saved the page to go back to.
Comments are welcome. I just didn't see any other way to dynamically go back to an arbitrary page.
Thanks.