Browse files

Don't try to restore the view during unload when view is already expired

  • Loading branch information...
BalusC committed Sep 11, 2015
1 parent efd9347 commit 62949a336c6e2b7affb09a47836d1c32f1dd57a4
@@ -15,7 +15,7 @@
package org.omnifaces.application;
import static org.omnifaces.util.Faces.getRequestParameter;
import static org.omnifaces.util.Faces.getContext;
import static org.omnifaces.util.Faces.responseComplete;
import javax.faces.component.UIViewRoot;
@@ -51,13 +51,17 @@ public boolean isListenerForSource(Object source) {
* If the event is an instance of {@link PreDestroyViewMapEvent}, which means that the JSF view scope is about to
* be destroyed, then find the current instance of {@link ViewScopeManager} and invoke its
* {@link ViewScopeManager#preDestroyView()} method.
* <p>
* Or, if the event is an instance of {@link PostRestoreStateEvent}, which means that the JSF view scope has
* recently restored, then check if the current request is an unload request and if so, then destroy the JSF view
* scope the same way as described above.
public void processEvent(SystemEvent event) throws AbortProcessingException {
if (event instanceof PreDestroyViewMapEvent) {
else if (event instanceof PostRestoreStateEvent && "unload".equals(getRequestParameter("omnifaces.event"))) {
else if (event instanceof PostRestoreStateEvent && ViewScopeManager.isUnloadRequest(getContext())) {
@@ -33,6 +33,7 @@
import org.omnifaces.cdi.viewscope.ViewScopeContext;
import org.omnifaces.cdi.viewscope.ViewScopeExtension;
import org.omnifaces.cdi.viewscope.ViewScopeManager;
import org.omnifaces.viewhandler.RestorableViewHandler;
* <p>
@@ -95,6 +96,7 @@
* @see ViewScopeContext
* @see ViewScopeManager
* @see ViewScopeEventListener
* @see RestorableViewHandler
* @since 1.6
@@ -21,6 +21,7 @@
import static org.omnifaces.util.Faces.getInitParameter;
import static org.omnifaces.util.Faces.getViewAttribute;
import static org.omnifaces.util.Faces.setViewAttribute;
import static org.omnifaces.util.FacesLocal.getRequestParameter;
import java.util.UUID;
@@ -219,6 +220,16 @@ else if (!Hacks.isScriptResourceRendered(context, new ResourceIdentifier("omnifa
* Returns <code>true</code> if the current request is triggered by an unload request.
* @param context The involved faces context.
* @return <code>true</code> if the current request is triggered by an unload request.
* @since 2.2
public static boolean isUnloadRequest(FacesContext context) {
return "unload".equals(getRequestParameter(context, "omnifaces.event"));
// Nested classes -------------------------------------------------------------------------------------------------
@@ -13,34 +13,41 @@
package org.omnifaces.viewhandler;
import static java.lang.Boolean.TRUE;
import static org.omnifaces.cdi.viewscope.ViewScopeManager.isUnloadRequest;
import static org.omnifaces.util.Faces.normalizeViewId;
import static org.omnifaces.util.Faces.setContext;
import static org.omnifaces.util.FacesLocal.getApplicationAttribute;
import static org.omnifaces.util.FacesLocal.getRenderKit;
import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.application.ViewExpiredException;
import javax.faces.application.ViewHandler;
import javax.faces.application.ViewHandlerWrapper;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.context.FacesContextWrapper;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import org.omnifaces.cdi.ViewScoped;
import org.omnifaces.taghandler.EnableRestorableView;
import org.omnifaces.util.FacesLocal;
* This view handler implementation will recreate the entire view state whenever the view has apparently been expired,
* i.e. whenever {@link #restoreView(FacesContext, String)} returns <code>null</code> and the current request is a
* postback and the view in question has <code>&lt;enableRestorableView&gt;</code> in the metadata. This effectively
* prevents the {@link ViewExpiredException} on the view.
* <p>
* This view handler implementation also detects unload requests coming from {@link ViewScoped} beans and will prevent
* any attempt to restore the view when the view state is already absent. This prevents unnecessary
* {@link ViewExpiredException} during unload on an expired view.
* @author Bauke Scholtz
* @since 1.3
* @see EnableRestorableView
* @see ViewScoped
public class RestorableViewHandler extends ViewHandlerWrapper {
@@ -61,16 +68,24 @@ public RestorableViewHandler(ViewHandler wrapped) {
// Actions --------------------------------------------------------------------------------------------------------
* First try to restore the view. If the <code>&lt;o:enableRestoreView&gt;</code> is used once in the application,
* and the restored view returns null and the current request is a postback, then recreate and build the view.
* If it contains the <code>&lt;o:enableRestoreView&gt;</code>, then return the newly created view, else
* return <code>null</code>.
* First check if this is an unload request from {@link ViewScoped}. If so, and the JSF view state is absent or
* expired, then don't try to restore the view and return a dummy view (to avoid {@link ViewExpiredException}) and
* immediately complete response.
* Then restore the view and check if the <code>&lt;o:enableRestoreView&gt;</code> is used once in the application,
* and the restored view is null and the current request is a postback, then recreate and build the view. If it
* indeed contains the <code>&lt;o:enableRestoreView&gt;</code>, then return the newly created view, else return
* <code>null</code>.
public UIViewRoot restoreView(FacesContext context, String viewId) {
if (isUnloadRequest(context) && getRenderKit(context).getResponseStateManager().getState(context, viewId) == null) {
return new UIViewRoot();
UIViewRoot restoredView = super.restoreView(context, viewId);
if (!(isEnabled(context) && restoredView == null && context.isPostback())) {
if (!(isRestorableViewEnabled(context) && restoredView == null && context.isPostback())) {
return restoredView;
@@ -97,7 +112,7 @@ public UIViewRoot restoreView(FacesContext context, String viewId) {
private boolean isEnabled(FacesContext context) {
private boolean isRestorableViewEnabled(FacesContext context) {
return TRUE.equals(getApplicationAttribute(context, EnableRestorableView.class.getName()));
@@ -133,8 +148,7 @@ public UIViewRoot getViewRoot() {
public RenderKit getRenderKit() {
return ((RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY))
.getRenderKit(this, temporaryView.getRenderKitId());
return FacesLocal.getRenderKit(this);

0 comments on commit 62949a3

Please sign in to comment.