You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lu...@apache.org on 2013/01/15 21:22:32 UTC
svn commit: r1433615 - in /myfaces/core/branches/2.2.x:
api/src/main/java/javax/faces/component/ api/src/main/java/javax/faces/event/
impl/src/main/java/org/apache/myfaces/application/
impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/core/
Author: lu4242
Date: Tue Jan 15 20:22:32 2013
New Revision: 1433615
URL: http://svn.apache.org/viewvc?rev=1433615&view=rev
Log:
MYFACES-3674 Implement f:viewAction
Added:
myfaces/core/branches/2.2.x/api/src/main/java/javax/faces/component/UIViewAction.java (with props)
Modified:
myfaces/core/branches/2.2.x/api/src/main/java/javax/faces/event/PhaseId.java
myfaces/core/branches/2.2.x/impl/src/main/java/org/apache/myfaces/application/NavigationHandlerImpl.java
myfaces/core/branches/2.2.x/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/core/CoreLibrary.java
Added: myfaces/core/branches/2.2.x/api/src/main/java/javax/faces/component/UIViewAction.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.2.x/api/src/main/java/javax/faces/component/UIViewAction.java?rev=1433615&view=auto
==============================================================================
--- myfaces/core/branches/2.2.x/api/src/main/java/javax/faces/component/UIViewAction.java (added)
+++ myfaces/core/branches/2.2.x/api/src/main/java/javax/faces/component/UIViewAction.java Tue Jan 15 20:22:32 2013
@@ -0,0 +1,401 @@
+/*
+ * 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 javax.faces.component;
+
+import javax.el.MethodExpression;
+import javax.faces.context.FacesContext;
+import javax.faces.context.FacesContextWrapper;
+import javax.faces.el.MethodBinding;
+import javax.faces.event.AbortProcessingException;
+import javax.faces.event.ActionEvent;
+import javax.faces.event.ActionListener;
+import javax.faces.event.FacesEvent;
+import javax.faces.event.PhaseId;
+import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
+import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFListener;
+import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
+
+/**
+ *
+ * @author Leonardo Uribe
+ * @since 2.2
+ */
+@JSFComponent(name = "f:viewAction")
+public class UIViewAction extends UIComponentBase implements ActionSource2
+{
+ //private static final Logger log = Logger.getLogger(UIViewAction.class.getName());
+ public static final String COMPONENT_FAMILY = "javax.faces.ViewAction";
+ public static final String COMPONENT_TYPE = "javax.faces.ViewAction";
+
+ /**
+ * Key in facesContext attribute map to check if a viewAction broadcast is
+ * being processed. This is used to check when a JSF lifecycle restart is required
+ * by the NavigationHandler implementation.
+ */
+ private static final String BROADCAST_PROCESSING_KEY = "oam.viewAction.broadcast";
+
+ /**
+ * Key in facesContext attribute map to count the number of viewAction events that
+ * remains to be processed.
+ */
+ private static final String EVENT_COUNT_KEY = "oam.viewAction.eventCount";
+
+ public UIViewAction()
+ {
+ setRendererType(null);
+ }
+
+ @Override
+ public void broadcast(FacesEvent event) throws AbortProcessingException
+ {
+ super.broadcast(event);
+
+ FacesContext context = getFacesContext();
+
+ if (context.getResponseComplete())
+ {
+ return;
+ }
+
+ UIComponent c = event.getComponent();
+ UIViewRoot sourceViewRoot = null;
+ do
+ {
+ if (c instanceof UIViewRoot)
+ {
+ sourceViewRoot = (UIViewRoot) c;
+ break;
+ }
+ else
+ {
+ c = c.getParent();
+ }
+ } while (c != null);
+
+ if (!context.getViewRoot().equals(sourceViewRoot))
+ {
+ return;
+ }
+
+ if (event instanceof ActionEvent)
+ {
+ ActionListener defaultActionListener = context.getApplication().getActionListener();
+ if (defaultActionListener != null)
+ {
+ String viewIdBeforeAction = context.getViewRoot().getViewId();
+ Boolean oldBroadcastProcessing = (Boolean) context.getAttributes().
+ get(BROADCAST_PROCESSING_KEY);
+ try
+ {
+ context.getAttributes().put(BROADCAST_PROCESSING_KEY, Boolean.TRUE);
+
+ ViewActionFacesContextWrapper wrappedFacesContext = new ViewActionFacesContextWrapper(context);
+
+ try
+ {
+ wrappedFacesContext.setWrapperAsCurrentFacesContext();
+
+ /* Note f:viewAction does not have actionListener property defined.
+ MethodBinding mb = getActionListener();
+ if (mb != null)
+ {
+ mb.invoke(context, new Object[]
+ { event });
+ }*/
+
+ if (defaultActionListener != null)
+ {
+ defaultActionListener.processAction((ActionEvent) event);
+ }
+
+ // Decrement count
+ Integer count = (Integer) context.getAttributes().get(EVENT_COUNT_KEY);
+ count = (count == null) ? 0 : count - 1;
+ context.getAttributes().put(EVENT_COUNT_KEY, count);
+ }
+ finally
+ {
+ wrappedFacesContext.restoreCurrentFacesContext();
+ }
+ }
+ finally
+ {
+ context.getAttributes().put(BROADCAST_PROCESSING_KEY,
+ oldBroadcastProcessing == null ? Boolean.FALSE : oldBroadcastProcessing);
+ }
+
+ if (context.getResponseComplete())
+ {
+ return;
+ }
+ else
+ {
+ Integer count = (Integer) context.getAttributes().get(EVENT_COUNT_KEY);
+ count = (count == null) ? 0 : count;
+ String viewIdAfterAction = context.getViewRoot().getViewId();
+
+ if (viewIdBeforeAction.equals(viewIdAfterAction) && count == 0)
+ {
+ context.renderResponse();
+ }
+ // "... Otherwise, execute the lifecycle on the new UIViewRoot ..."
+ // Note these words are implemented in the NavigationHandler, but
+ // the original proposal from seam s:viewAction did a trick here
+ // to restart the JSF lifecycle.
+ }
+ }
+ }
+ }
+
+ @Override
+ public void decode(FacesContext context)
+ {
+ super.decode(context);
+
+ if (context.isPostback() && !isOnPostback())
+ {
+ return;
+ }
+
+ if (!isRendered())
+ {
+ return;
+ }
+
+ ActionEvent evt = new ActionEvent(this);
+ String phase = getPhase();
+ PhaseId phaseId = (phase != null) ? PhaseId.phaseIdValueOf(phase) :
+ isImmediate() ? PhaseId.APPLY_REQUEST_VALUES : PhaseId.INVOKE_APPLICATION;
+ evt.setPhaseId(phaseId);
+ this.queueEvent(evt);
+
+ // "... Keep track of the number of events that are queued in this way
+ // on this run through the lifecycle. ...". The are two options:
+ // 1. Use an attribute over FacesContext attribute map
+ // 2. Use an attribute over the component
+ // If the view is recreated again with the same viewId, the component
+ // state get lost, so the option 1 is preferred.
+ Integer count = (Integer) context.getAttributes().get(EVENT_COUNT_KEY);
+ count = (count == null) ? 1 : count + 1;
+ context.getAttributes().put(EVENT_COUNT_KEY, count);
+ }
+
+ @Override
+ public void queueEvent(FacesEvent event)
+ {
+ if (event != null && event instanceof ActionEvent)
+ {
+ UIComponent component = event.getComponent();
+ if (component instanceof ActionSource)
+ {
+ if (((ActionSource)component).isImmediate())
+ {
+ event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
+ }
+ else
+ {
+ event.setPhaseId(PhaseId.INVOKE_APPLICATION);
+ }
+ }
+ }
+ super.queueEvent(event);
+ }
+
+ public MethodBinding getAction()
+ {
+ MethodExpression actionExpression = getActionExpression();
+ if (actionExpression instanceof _MethodBindingToMethodExpression)
+ {
+ return ((_MethodBindingToMethodExpression) actionExpression)
+ .getMethodBinding();
+ }
+ if (actionExpression != null)
+ {
+ return new _MethodExpressionToMethodBinding(actionExpression);
+ }
+ return null;
+ }
+
+ /**
+ * @deprecated Use setActionExpression instead.
+ */
+ public void setAction(MethodBinding action)
+ {
+ if (action != null)
+ {
+ setActionExpression(new _MethodBindingToMethodExpression(action));
+ }
+ else
+ {
+ setActionExpression(null);
+ }
+ }
+
+ @JSFProperty
+ public boolean isImmediate()
+ {
+ return (Boolean) getStateHelper().eval(PropertyKeys.immediate, Boolean.FALSE);
+ }
+
+ public void setImmediate(boolean immediate)
+ {
+ getStateHelper().put(PropertyKeys.immediate, immediate );
+ }
+
+ @JSFProperty
+ public Object getValue()
+ {
+ return getStateHelper().eval(PropertyKeys.value);
+ }
+
+ public void setValue(Object value)
+ {
+ getStateHelper().put(PropertyKeys.value, value );
+ }
+
+ @JSFProperty(stateHolder=true, returnSignature = "java.lang.Object", jspName = "action", clientEvent="action")
+ public MethodExpression getActionExpression()
+ {
+ return (MethodExpression) getStateHelper().eval(PropertyKeys.actionExpression);
+ }
+
+ public void setActionExpression(MethodExpression actionExpression)
+ {
+ getStateHelper().put(PropertyKeys.actionExpression, actionExpression);
+ }
+
+ //@JSFProperty(stateHolder=true, returnSignature = "void", methodSignature = "javax.faces.event.ActionEvent")
+ public MethodBinding getActionListener()
+ {
+ return (MethodBinding) getStateHelper().eval(PropertyKeys.actionListener);
+ // Note f:viewAction does not have actionListener property defined.
+ //throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @deprecated
+ */
+ //@JSFProperty(returnSignature="void",methodSignature="javax.faces.event.ActionEvent")
+ public void setActionListener(MethodBinding actionListener)
+ {
+ getStateHelper().put(PropertyKeys.actionListener, actionListener);
+ // Note f:viewAction does not have actionListener property defined.
+ //throw new UnsupportedOperationException();
+ }
+
+ public void addActionListener(ActionListener listener)
+ {
+ addFacesListener(listener);
+ }
+
+ public void removeActionListener(ActionListener listener)
+ {
+ removeFacesListener(listener);
+ }
+
+ @JSFListener(event="javax.faces.event.ActionEvent",
+ phases="Invoke Application, Apply Request Values")
+ public ActionListener[] getActionListeners()
+ {
+ return (ActionListener[]) getFacesListeners(ActionListener.class);
+ }
+
+ @JSFProperty
+ public String getPhase()
+ {
+ return (String) getStateHelper().get(PropertyKeys.phase);
+ }
+
+ public void setPhase(String phase)
+ {
+ getStateHelper().put(PropertyKeys.phase, phase);
+ }
+
+ @JSFProperty
+ public boolean isOnPostback()
+ {
+ return (Boolean) getStateHelper().eval(PropertyKeys.onPostback, Boolean.FALSE);
+ }
+
+ public void setOnPostback(boolean onPostback)
+ {
+ getStateHelper().put(PropertyKeys.onPostback, onPostback );
+ }
+
+ public static boolean isProcessingBroadcast(FacesContext context)
+ {
+ return Boolean.TRUE.equals(context.getAttributes().get(BROADCAST_PROCESSING_KEY));
+ }
+
+ enum PropertyKeys
+ {
+ immediate
+ , value
+ , actionExpression
+ , actionListener
+ , phase
+ , onPostback
+ }
+
+ @Override
+ public String getFamily()
+ {
+ return COMPONENT_FAMILY;
+ }
+
+ private static class ViewActionFacesContextWrapper extends FacesContextWrapper
+ {
+ private FacesContext delegate;
+ private boolean renderResponseCalled;
+
+ public ViewActionFacesContextWrapper(FacesContext delegate)
+ {
+ this.delegate = delegate;
+ this.renderResponseCalled = false;
+ }
+
+ @Override
+ public void renderResponse()
+ {
+ //Take no action
+ renderResponseCalled = true;
+ }
+
+ public boolean isRenderResponseCalled()
+ {
+ return renderResponseCalled;
+ }
+
+ @Override
+ public FacesContext getWrapped()
+ {
+ return delegate;
+ }
+
+ void setWrapperAsCurrentFacesContext()
+ {
+ setCurrentInstance(this);
+ }
+
+ void restoreCurrentFacesContext()
+ {
+ setCurrentInstance(delegate);
+ }
+ }
+}
Propchange: myfaces/core/branches/2.2.x/api/src/main/java/javax/faces/component/UIViewAction.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: myfaces/core/branches/2.2.x/api/src/main/java/javax/faces/event/PhaseId.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.2.x/api/src/main/java/javax/faces/event/PhaseId.java?rev=1433615&r1=1433614&r2=1433615&view=diff
==============================================================================
--- myfaces/core/branches/2.2.x/api/src/main/java/javax/faces/event/PhaseId.java (original)
+++ myfaces/core/branches/2.2.x/api/src/main/java/javax/faces/event/PhaseId.java Tue Jan 15 20:22:32 2013
@@ -21,6 +21,7 @@ package javax.faces.event;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import javax.faces.FacesException;
/**
* see Javadoc of <a href="http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/index.html">JSF Specification</a>
@@ -88,5 +89,29 @@ public class PhaseId implements Comparab
{
return _name + "(" + _ordinal + ")";
}
+
+ /*
+ * @since 2.2
+ */
+ public String getName()
+ {
+ return this._name;
+ }
+ public static PhaseId phaseIdValueOf(String phase)
+ {
+ if (phase == null)
+ {
+ throw new NullPointerException("phase");
+ }
+ for (int i = 0; i < VALUES.size(); i++)
+ {
+ PhaseId phaseId = VALUES.get(i);
+ if (phaseId.getName().equals(phase))
+ {
+ return phaseId;
+ }
+ }
+ throw new FacesException("Phase "+phase+" is invalid");
+ }
}
Modified: myfaces/core/branches/2.2.x/impl/src/main/java/org/apache/myfaces/application/NavigationHandlerImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.2.x/impl/src/main/java/org/apache/myfaces/application/NavigationHandlerImpl.java?rev=1433615&r1=1433614&r2=1433615&view=diff
==============================================================================
--- myfaces/core/branches/2.2.x/impl/src/main/java/org/apache/myfaces/application/NavigationHandlerImpl.java (original)
+++ myfaces/core/branches/2.2.x/impl/src/main/java/org/apache/myfaces/application/NavigationHandlerImpl.java Tue Jan 15 20:22:32 2013
@@ -39,6 +39,7 @@ import javax.faces.application.Navigatio
import javax.faces.application.ProjectStage;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIComponent;
+import javax.faces.component.UIViewAction;
import javax.faces.component.UIViewRoot;
import javax.faces.component.visit.VisitCallback;
import javax.faces.component.visit.VisitContext;
@@ -103,7 +104,30 @@ public class NavigationHandlerImpl
" toViewId =" + navigationCase.getToViewId(facesContext) +
" redirect=" + navigationCase.isRedirect());
}
- if (navigationCase.isRedirect())
+ boolean isViewActionProcessingBroadcastAndRequiresRedirect = false;
+ if (UIViewAction.isProcessingBroadcast(facesContext))
+ {
+ // f:viewAction tag always triggers a redirect to enforce execution of
+ // the lifecycle again. Note this requires enables flash scope
+ // keepMessages automatically, because a view action can add messages
+ // and these ones requires to be renderer afterwards.
+ facesContext.getExternalContext().getFlash().setKeepMessages(true);
+ String fromViewId = (facesContext.getViewRoot() == null) ? null :
+ facesContext.getViewRoot().getViewId();
+ String toViewId = navigationCase.getToViewId(facesContext);
+ // A redirect is required only if the viewId changes. If the viewId
+ // does not change, section 7.4.2 says that a redirect/restart JSF
+ // lifecycle is not necessary.
+ if (fromViewId == null && toViewId != null)
+ {
+ isViewActionProcessingBroadcastAndRequiresRedirect = true;
+ }
+ else if (fromViewId != null && !fromViewId.equals(toViewId))
+ {
+ isViewActionProcessingBroadcastAndRequiresRedirect = true;
+ }
+ }
+ if (navigationCase.isRedirect() || isViewActionProcessingBroadcastAndRequiresRedirect)
{
//&& (!PortletUtil.isPortletRequest(facesContext)))
// Spec section 7.4.2 says "redirects not possible" in this case for portlets
Modified: myfaces/core/branches/2.2.x/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/core/CoreLibrary.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.2.x/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/core/CoreLibrary.java?rev=1433615&r1=1433614&r2=1433615&view=diff
==============================================================================
--- myfaces/core/branches/2.2.x/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/core/CoreLibrary.java (original)
+++ myfaces/core/branches/2.2.x/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/core/CoreLibrary.java Tue Jan 15 20:22:32 2013
@@ -21,6 +21,7 @@ package org.apache.myfaces.view.facelets
import javax.faces.component.UIParameter;
import javax.faces.component.UISelectItem;
import javax.faces.component.UISelectItems;
+import javax.faces.component.UIViewAction;
import javax.faces.component.UIViewParameter;
import javax.faces.convert.DateTimeConverter;
import javax.faces.convert.NumberConverter;
@@ -101,6 +102,8 @@ public final class CoreLibrary extends A
this.addTagHandler("view", ViewHandler.class);
+ this.addComponent("viewAction", UIViewAction.COMPONENT_TYPE, null);
+
this.addComponent("viewParam", UIViewParameter.COMPONENT_TYPE, null);
this.addComponent("verbatim", "javax.faces.HtmlOutputText", "javax.faces.Text", VerbatimHandler.class);