Permalink
Browse files

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

  • Loading branch information...
1 parent efd9347 commit 62949a336c6e2b7affb09a47836d1c32f1dd57a4 @BalusC BalusC committed Sep 11, 2015
@@ -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.
*/
@Override
public void processEvent(SystemEvent event) throws AbortProcessingException {
if (event instanceof PreDestroyViewMapEvent) {
processPreDestroyView();
}
- else if (event instanceof PostRestoreStateEvent && "unload".equals(getRequestParameter("omnifaces.event"))) {
+ else if (event instanceof PostRestoreStateEvent && ViewScopeManager.isUnloadRequest(getContext())) {
processPreDestroyView();
responseComplete();
}
@@ -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
*/
@Inherited
@@ -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.io.Serializable;
import java.util.UUID;
@@ -219,6 +220,16 @@ else if (!Hacks.isScriptResourceRendered(context, new ResourceIdentifier("omnifa
addScriptToBody("OmniFaces.Unload.init()");
}
+ /**
+ * 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 java.io.IOException;
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>.
*/
@Override
public UIViewRoot restoreView(FacesContext context, String viewId) {
+ if (isUnloadRequest(context) && getRenderKit(context).getResponseStateManager().getState(context, viewId) == null) {
+ context.responseComplete();
+ 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() {
@Override
public RenderKit getRenderKit() {
- return ((RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY))
- .getRenderKit(this, temporaryView.getRenderKitId());
+ return FacesLocal.getRenderKit(this);
}
@Override

0 comments on commit 62949a3

Please sign in to comment.