You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 8 Next »

In JSF 2.0 or later it is better to create a custom ResourceHandler wrapper and use FacesServlet instead. In that way, JSF will create FacesContext instance.

Nested FacesContext

If you create a custom FacesContext e.g. like described here Access FacesContext From Servlet you might face a problem that e.g. messages added to your custom FacesContext won't show up in h:messages.

The FacesServlet creates its own new FacesContext which will be used for all subsequent JSF operations.

One reason why this is good is, that the FacesServlet can't be sure that your custom FacesContext use the correct ServletRequest/ServletResponse objects.

An example when your custom FacesContext is not good is when you use requestDispatcher.forward.
Like my setup:

  • A custom filter which creates a custom FacesContext
  • A custom servlet which forwards to the FacesServlet (getServletContext().getRequestDispatcher(jsfPath).forward(req, res)(wink)
  • The FacesServlet which creates a FacesContext too

If the FacesServlet uses our own custom FacesContext you will see that JSF did not use the jsfPath for further operations, but the original url - which is false.

So, I found to let the FacesServlet create a new FacesContext is fine and the easiest solution, but I still wanted it to show any messages added to the custom FacesContext.

I ended with the following FacesContextFactory (adjust the package names as needed):

Configuration in faces-config.xml:

	<factory>
		<faces-context-factory>org.apache....StateCopyFacesContextFactory</faces-context-factory>
	</factory>

Source:

StateCopyFacesContextFactory.java
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
*/
package org.apache.....;

import javax.faces.context.FacesContextFactory;
import javax.faces.context.FacesContext;
import javax.faces.lifecycle.Lifecycle;
import javax.faces.FacesException;
import javax.faces.application.FacesMessage;
import java.util.Iterator;

/**
 * FacesContextFactory which first checks if a FacesContext is already there
 */
public class StateCopyFacesContextFactory extends FacesContextFactory
{
	private final FacesContextFactory original;

	public StateCopyFacesContextFactory(FacesContextFactory original)
	{
		this.original = original;
	}

	@Override
	public FacesContext getFacesContext(Object context, Object request, Object response, Lifecycle lifecycle) throws FacesException
	{
		FacesContext previousContext = FacesContext.getCurrentInstance();

		FacesContext newContext = original.getFacesContext(context, request, response, lifecycle);

		if (previousContext != null)
		{
			// transfer the default locale (might have been changed)
			newContext.getApplication().setDefaultLocale(previousContext.getApplication().getDefaultLocale());

			// transfer all already collected messages
			Iterator iterClientIds = previousContext.getClientIdsWithMessages();
			while (iterClientIds.hasNext())
			{
				String clientId = (String) iterClientIds.next();
				Iterator iterMessages = previousContext.getMessages(clientId);
				while (iterMessages.hasNext())
				{
					FacesMessage message = (FacesMessage) iterMessages.next();

					newContext.addMessage(clientId, message);
				}
			}
		}

		return newContext;
	}
}

Notice: This solution do not deal with resetting to the previousContext on newContext.release(). One can enhance it. For me it was sufficient that way as after the JSF request there is nothing further I will do with the FacesContext ... and the problem might be, that then one might copy some state back ... too complicated.

  • No labels