March 07, 2011

JBoss & Apache: JSF, RichFaces and ViewHandler

There is another solution to the problem described in my previous post (JSF, RichFaces and mod_proxy_html): modify the generated links at JBoss level and not at Apache level.

The solution below has been implemented before finding mod_proxy_uml - but replaced by this one for three reasons:
  • mod_proxy_html is far simpler to use, just one or two lines of Apache configuration;
  • The application at jboss level runs unmodified, without needing to build a different ear for testing or production.
  • It transfers part of the processing to the apache serve.r

Anyway, we publish the solution because it might be useful in some situations.

JSF pages are generated by a "view handler" and it is possible to write a custom implementation. Based on [1], we implemented a view handler that replaces the context path by an arbitrary value.

As we use Facelets, we extend the Facelet ViewHandler instead of the basical JSF one and just override the two relevant methods.

import javax.faces.application.ViewHandler;
import javax.faces.context.FacesContext;
import com.sun.facelets.FaceletViewHandler;

/**
 * Replaces the context path in the links generated
 * by JSF by an arbitrary value
 * configured as context parameter.
 * 
 * The alternative context path should be configured in
 * web.xml with name "br.com.net2tel.PROXY_CONTEXT_PATH"
 *  
 * (this is needed to use the app behind a reverse proxy,
 * with a different context path).
 */
public class ReverseProxyViewHandler extends FaceletViewHandler {
 
 private ViewHandler defaultHandler;
 
 public ReverseProxyViewHandler(ViewHandler defaultHandler) {
  super(defaultHandler);
  this.defaultHandler = defaultHandler;
 }


 @Override
 public String getActionURL(FacesContext context, String viewId) {
  
  String actionURL = defaultHandler.getActionURL(context, viewId);

  return getProxyiedURL(context, actionURL);
 }
 
 @Override
 public String getResourceURL(FacesContext context, String path) {
  return getProxyiedURL(context, path);
 }
 

 /**
  * Actual string replacement. Simply replaces the context path
  * in the passed URL. For the root context, (/) just removes
  * the context path.
  * 
  * @param context Faces context (used to retrieve application context path
  *    and init parameter)
  * @param path Action or resource path to be modified
  * @return URL with changed context path
  */
 private String getProxyiedURL(FacesContext context, String path) {
  
  String contextPath = context.getExternalContext().getRequestContextPath();
  String proxyPath = context.getExternalContext().getInitParameter(
   "br.com.net2tel.PROXY_CONTEXT_PATH");  
  
  // For root proxy context, simply remove context path
  if ("/".equals(proxyPath))
   return path.replaceFirst(contextPath, ""); 
  else
   return path.replaceFirst(contextPath, proxyPath); 
 }

}

Two additional steps are necessary: configure the init parameter in web.xml and register the view handler in

faces-config.xml

    
  br.com.net2tel.portability.ui.viewhandlers.ReverseProxyViewHandler       
 

web.xml

 
  br.com.net2tel.PROXY_CONTEXT_PATH/


References:
[1] "Publishing JSF application from behind a proxy"

Ok, now we should have everything ok. We faced one more problem, solved in two lines of configuration (already included in "Basical Apache Setup"). This is the covered by the next post: Form-based Authentication

0 comments:

Post a Comment