Insights into Java Server Faces

2009/07/03

Using Custom factories or howto wrap FacesContext and ExternalContext in JSF 1.2

Filed under: JSF — Tags: — Hanspeter @ 22:48

Sometimes you need to wrap FacesContext or even ExternalContext. For our component library we have several needs to do so – e.g. add custom logic to RequestParameterMap or SessionMap. In JSF 1.2 ExternalContext cannot be wrapped directly – one needs to wrap FacesContext to hook in a wrapped ExternalContext.

With this post I want to give some hints how this can be done and what needs to be considered.

To wrap FacesContext we need to replace the standard FacesContextFactory with a custom one. In JSF the factories are initialized by FactoryFinder, which recognizes if your custom factory has a delegating constructor (a one argument constructor for the type of the factory). That way it is very easy to wrap a standard factory from JSF, because FactoryFinder will pass in the previously known factory, usually the standard one.

Example wrapping FacesContextFactory to returns a CustomFacesContext wrapping the standard FacesContext:

  package ch.dueni.jsf12.context;

  import javax.faces.FacesException;
  import javax.faces.context.FacesContext;
  import javax.faces.context.FacesContextFactory;
  import javax.faces.lifecycle.Lifecycle;

  public class CustomFacesContextFactory extends FacesContextFactory {

    private FacesContextFactory delegate;

    public CustomFacesContextFactory(FacesContextFactory facesContextFactory) {
      delegate = facesContextFactory;
    }

    public FacesContext getFacesContext(Object context, Object request, Object response,
      Lifecycle lifecycle) throws FacesException {
      return new CustomFacesContext(delegate.getFacesContext(context, request, response, lifecycle));
    }

  }

To activate the CustomFacesFactory simply add the following configuration snippet to your faces-config.xml:

   <factory>
    <faces-context-factory>ch.dueni.jsf12.context.CustomFacesContextFactory</faces-context-factory>
   </factory>

Below you see how the CustomFacesContext could look like. Please notice some importand points in the constructor:

  1. call setCurrentInstance(this) to assign the current CustomFacesContext instance to the ThreadLocal so FacesContext.getCurrentInstance() will return the CustomFacesContext.
  2. also assign CustomFacesContext (this) to ELContext, otherwise CustomFacesContext will not be picked up during most expression evaluations, especially by the JSF bean management facility (I discoverd that while I had to tweak SessionMap to let portal resource requests be executed with access to the portlet private session – without that I always got new managed bean instances).
  3. within the CustomFacesContext we can also warp ExternalContext with a custom implementation.
  package ch.dueni.jsf12.context;

  import java.util.Iterator;
  import javax.faces.application.Application;
  import javax.faces.application.FacesMessage;
  import javax.faces.application.FacesMessage.Severity;
  import javax.faces.component.UIViewRoot;
  import javax.faces.context.ExternalContext;
  import javax.faces.context.FacesContext;
  import javax.faces.context.ResponseStream;
  import javax.faces.context.ResponseWriter;
  import javax.faces.render.RenderKit;

  public class CustomFacesContext extends FacesContext {

    private FacesContext delegate;

    private ExternalContext externalContext;

    public CustomFacesContext(FacesContext context) {
      delegate = context;
      // 1. assign the new FacesContext instance to the ThreadLocal
      setCurrentInstance(this);

      // 2. put CustomFacesContext as FacesContext to ELContext
      getELContext().putContext(FacesContext.class, this);

      // 3. preset wrapping CustomExternalContext
      externalContext = new CustomExternalContext(delegate.getExternalContext());
    }

    public void addMessage(String clientId, FacesMessage message) {
      delegate.addMessage(clientId, message);
    }

    /* ... other delegating methods */

  }

And finally this could be the CustomExternalContext:

  package ch.dueni.jsf12.context;

  import javax.faces.context.ExternalContext;
  ...

  public class CustomExternalContext extends ExternalContext {

    private ExternalContext delegate;

    public CustomExternalContext(ExternalContext externalContext) {
      delegate = externalContext;
    }

    /* ... other delegating methods and wrappings */

  }

The sources for these examples are available on https://github.com/dueni/faces-ext/tree/master/jsf12-context-wrapper.

Advertisements

6 Comments »

  1. […] For step 1 to 3, please follow the steps mentioned in teh previous post titled ‘Using Custom factories or howto wrap FacesContext’. […]

    Pingback by Using File Upload in JSF Portlets with Weblogic Portal « Insights into Java Server Faces — 2010/04/06 @ 13:59

  2. Hi,
    Thanks for the article.
    This is exactly I wanted to do. I’ve made all the changes as suggested here. However, there is one error that I am facing.
    On application startup, the configuration fails.

    com.ibm.ws.webcontainer.webapp.WebApp notifyServletContextCreated SRVE0283E: Exception caught while initializing context: {0}
    com.sun.faces.config.ConfigurationException: CONFIGURATION FAILED! myapp.faces.improved.ICliperFacesContextFactory

    Caused by: java.lang.InstantiationException: myapp.faces.improved.CustomFacesContextFactory

    Have been trying to find a fix, but to no avail.

    Comment by abhi — 2012/01/05 @ 20:29

    • Hmm, are you using JSF 1.2 or JSF 2.0? Without sources and stacktrace I can only guess. If you use JSF 2.0 or higher you should make sure to implement the FacesWrapper interface also within the factory – e.g. implement the getWrapped() method to return the wrapped (delegate).

      cheers
      Hanspeter

      Comment by Hanspeter — 2012/01/18 @ 21:29

  3. Thx for this post. It finally helped me to reference XHTML pages contained in a JAR file.
    I encountered a little bug in CustomFacesContext. It needs to be:

    // 2. put CustomFacesContext as FacesContext to ELContext
    delegate.getELContext().putContext(FacesContext.class, this);

    Otherwise it results in a NullPointerException.

    To override the external context you also need to override the corresponding method:

    @Override
    public ExternalContext getExternalContext() {
    return this.externalContext;
    }

    Comment by Steffen — 2012/01/06 @ 08:19

    • Sorry for the delayed answer. To load Facelets resources (XHTML pages) from jar files I would recommend to look into javax.faces.view.facelets.ResourceResolver. It’s pretty simple to implement an additional Facelets ResourceResolver loading XHTML files from jars. We did that and it works great. See also http://www.ninthavenue.com.au/blog/packaging-your-facelets-in-jars

      Overriding FacesContext.getExternalContext() is only needed before JSF 2.0 – since JSF 2.0 there is an ExternalContextWrapper that can be extended to add/modify ExternalContext -of course that need a custom ExternalContextFactory impl. Within custom ExternalContextFactory and custom ExternalContextWrapper you cannot access FacesContext.getCurrentInstance() since ExternalContext is constructed before FacesContext.

      Cheers
      Hanspeter

      Comment by Hanspeter — 2012/01/18 @ 21:18

  4. […] Obtaining those instances was already possible before JSF 2.2 via ExternalContext#getFlash and this will remain the default way for ‘normal’ user code. The fact that there’s now a standard factory underneath means that there’s a hook available for overriding and/or wrapping the default implementation. This mechanism is explained here. […]

    Pingback by What’s new in JSF 2.2? | J-Development — 2012/02/15 @ 23:39


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: