Created
March 5, 2010 15:25
-
-
Save mojavelinux/322811 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<faces-config xmlns="http://java.sun.com/xml/ns/javaee" | |
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xsi:schemaLocation=" | |
http://java.sun.com/xml/ns/javaee | |
http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd" | |
version="2.0"> | |
<!-- Not required if the @FacesComponent is used on the component class! --> | |
<component> | |
<component-type>javax.faces.ViewAction</component-type> | |
<component-class>javax.faces.component.UIViewAction</component-class> | |
</component> | |
</faces-config> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. | |
* | |
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. | |
* | |
* The contents of this file are subject to the terms of either the GNU | |
* General Public License Version 2 only ("GPL") or the Common Development | |
* and Distribution License("CDDL") (collectively, the "License"). You | |
* may not use this file except in compliance with the License. You can obtain | |
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html | |
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific | |
* language governing permissions and limitations under the License. | |
* | |
* When distributing the software, include this License Header Notice in each | |
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt. | |
* Sun designates this particular file as subject to the "Classpath" exception | |
* as provided by Sun in the GPL Version 2 section of the License file that | |
* accompanied this code. If applicable, add the following below the License | |
* Header, with the fields enclosed by brackets [] replaced by your own | |
* identifying information: "Portions Copyrighted [year] | |
* [name of copyright owner]" | |
* | |
* Contributor(s): | |
* | |
* If you wish your version of this file to be governed by only the CDDL or | |
* only the GPL Version 2, indicate your decision by adding "[Contributor] | |
* elects to include this software in this distribution under the [CDDL or GPL | |
* Version 2] license." If you don't indicate a single choice of license, a | |
* recipient has the option to distribute your version of this file under | |
* either the CDDL, the GPL Version 2 or to extend the choice of license to | |
* its licensees as provided above. However, if you add GPL Version 2 code | |
* and therefore, elected the GPL Version 2 license, then the option applies | |
* only if the new code is made subject to such option by the copyright | |
* holder. | |
*/ | |
package javax.faces.component; | |
import java.io.Serializable; | |
import javax.faces.component.StateHolder; | |
import javax.faces.context.FacesContext; | |
import javax.faces.el.EvaluationException; | |
import javax.faces.el.MethodBinding; | |
import javax.el.MethodExpression; | |
import javax.el.MethodInfo; | |
import javax.el.ELException; | |
import javax.el.ELContext; | |
import javax.el.ExpressionFactory; | |
import javax.el.ValueExpression; | |
import java.util.Arrays; | |
import java.lang.reflect.Method; | |
/** | |
* <p>Wrap a MethodExpression instance and expose it as a MethodBinding</p> | |
* | |
*/ | |
class MethodBindingMethodExpressionAdapter extends MethodBinding implements StateHolder, | |
Serializable { | |
private static final long serialVersionUID = 7334926223014401689L; | |
private MethodExpression methodExpression = null; | |
private boolean tranzient; | |
public MethodBindingMethodExpressionAdapter() { | |
} // for StateHolder | |
MethodBindingMethodExpressionAdapter(MethodExpression methodExpression) { | |
this.methodExpression = methodExpression; | |
} | |
@Override | |
public Object invoke(FacesContext context, Object params[]) | |
throws javax.faces.el.EvaluationException, javax.faces.el.MethodNotFoundException { | |
assert (null != methodExpression); | |
if (context == null) { | |
throw new NullPointerException("FacesConext -> null"); | |
} | |
Object result = null; | |
try { | |
result = methodExpression.invoke(context.getELContext(), | |
params); | |
} catch (javax.el.MethodNotFoundException e) { | |
throw new javax.faces.el.MethodNotFoundException(e); | |
} catch (javax.el.PropertyNotFoundException e) { | |
throw new EvaluationException(e); | |
} catch (ELException e) { | |
Throwable cause = e.getCause(); | |
if (cause == null) { | |
cause = e; | |
} | |
throw new EvaluationException(cause); | |
} catch (NullPointerException e) { | |
throw new javax.faces.el.MethodNotFoundException(e); | |
} | |
return result; | |
} | |
@Override | |
public Class getType(FacesContext context) throws javax.faces.el.MethodNotFoundException { | |
assert (null != methodExpression); | |
if (context == null) { | |
throw new NullPointerException("FacesConext -> null"); | |
} | |
Class result = null; | |
if (context == null) { | |
throw new NullPointerException(); | |
} | |
try { | |
MethodInfo mi = | |
methodExpression.getMethodInfo(context.getELContext()); | |
result = mi.getReturnType(); | |
} catch (javax.el.PropertyNotFoundException e) { | |
throw new javax.faces.el.MethodNotFoundException(e); | |
} catch (javax.el.MethodNotFoundException e) { | |
throw new javax.faces.el.MethodNotFoundException(e); | |
} catch (ELException e) { | |
throw new javax.faces.el.MethodNotFoundException(e); | |
} | |
return result; | |
} | |
@Override | |
public String getExpressionString() { | |
assert (null != methodExpression); | |
return methodExpression.getExpressionString(); | |
} | |
@Override | |
public boolean equals(Object other) { | |
if (this == other) { | |
return true; | |
} | |
if (other instanceof MethodBindingMethodExpressionAdapter) { | |
return methodExpression.equals(((MethodBindingMethodExpressionAdapter) other).getWrapped()); | |
} else if (other instanceof MethodBinding) { | |
MethodBinding binding = (MethodBinding) other; | |
// We'll need to do a little leg work to determine | |
// if the MethodBinding is equivalent to the | |
// wrapped MethodExpression | |
String expr = binding.getExpressionString(); | |
int idx = expr.indexOf('.'); | |
String target = expr.substring(0, idx).substring(2); | |
String t = expr.substring(idx + 1); | |
String method = t.substring(0, (t.length() - 1)); | |
FacesContext context = FacesContext.getCurrentInstance(); | |
ELContext elContext = context.getELContext(); | |
MethodInfo controlInfo = methodExpression.getMethodInfo(elContext); | |
// ensure the method names are the same | |
if (!controlInfo.getName().equals(method)) { | |
return false; | |
} | |
// Using the target, create an expression and evaluate | |
// it. | |
ExpressionFactory factory = context.getApplication().getExpressionFactory(); | |
ValueExpression ve = factory.createValueExpression(elContext, | |
"#{" + target + '}', | |
Object.class); | |
if (ve == null) { | |
return false; | |
} | |
Object result = ve.getValue(elContext); | |
if (result == null) { | |
return false; | |
} | |
// Get all of the methods with the matching name and try | |
// to find a match based on controlInfo's return and parameter | |
// types | |
Class type = binding.getType(context); | |
Method[] methods = result.getClass().getMethods(); | |
for (Method meth : methods) { | |
if (meth.getName().equals(method) | |
&& type.equals(controlInfo.getReturnType()) | |
&& Arrays.equals(meth.getParameterTypes(), | |
controlInfo.getParamTypes())) { | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
@Override | |
public int hashCode() { | |
assert (null != methodExpression); | |
return methodExpression.hashCode(); | |
} | |
@Override | |
public boolean isTransient() { | |
return this.tranzient; | |
} | |
@Override | |
public void setTransient(boolean tranzient) { | |
this.tranzient = tranzient; | |
} | |
@Override | |
public Object saveState(FacesContext context) { | |
if (context == null) { | |
throw new NullPointerException(); | |
} | |
Object result = null; | |
if (!tranzient) { | |
if (methodExpression instanceof StateHolder) { | |
Object[] stateStruct = new Object[2]; | |
// save the actual state of our wrapped methodExpression | |
stateStruct[0] = ((StateHolder) methodExpression).saveState(context); | |
// save the class name of the methodExpression impl | |
stateStruct[1] = methodExpression.getClass().getName(); | |
result = stateStruct; | |
} else { | |
result = methodExpression; | |
} | |
} | |
return result; | |
} | |
@Override | |
public void restoreState(FacesContext context, Object state) { | |
if (context == null) { | |
throw new NullPointerException(); | |
} | |
// if we have state | |
if (null == state) { | |
return; | |
} | |
if (!(state instanceof MethodExpression)) { | |
Object[] stateStruct = (Object[]) state; | |
Object savedState = stateStruct[0]; | |
String className = stateStruct[1].toString(); | |
MethodExpression result = null; | |
Class toRestoreClass = null; | |
if (null != className) { | |
try { | |
toRestoreClass = loadClass(className, this); | |
} catch (ClassNotFoundException e) { | |
throw new IllegalStateException(e.getMessage()); | |
} | |
if (null != toRestoreClass) { | |
try { | |
result = | |
(MethodExpression) toRestoreClass.newInstance(); | |
} catch (InstantiationException e) { | |
throw new IllegalStateException(e.getMessage()); | |
} catch (IllegalAccessException a) { | |
throw new IllegalStateException(a.getMessage()); | |
} | |
} | |
if (null != result && null != savedState) { | |
// don't need to check transient, since that was | |
// done on the saving side. | |
((StateHolder) result).restoreState(context, savedState); | |
} | |
methodExpression = result; | |
} | |
} else { | |
methodExpression = (MethodExpression) state; | |
} | |
} | |
public MethodExpression getWrapped() { | |
return methodExpression; | |
} | |
// | |
// Helper methods for StateHolder | |
// | |
private static Class loadClass(String name, | |
Object fallbackClass) throws ClassNotFoundException { | |
ClassLoader loader = | |
Thread.currentThread().getContextClassLoader(); | |
if (loader == null) { | |
loader = fallbackClass.getClass().getClassLoader(); | |
} | |
return Class.forName(name, true, loader); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<facelet-taglib xmlns="http://java.sun.com/xml/ns/javaee" | |
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xsi:schemaLocation=" | |
http://java.sun.com/xml/ns/javaee | |
http://java.sun.com/xml/ns/javaee/web-facelettaglibary_2_0.xsd" | |
version="2.0"> | |
<namespace>http://java.sun.com/jsf/core</namespace> | |
<tag> | |
<tag-name>viewAction</tag-name> | |
<component> | |
<component-type>javax.faces.ViewAction</component-type> | |
</component> | |
</tag> | |
</facelet-taglib> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package javax.faces.component; | |
import javax.el.MethodExpression; | |
import javax.faces.FacesException; | |
import javax.faces.FactoryFinder; | |
import javax.faces.application.NavigationHandler; | |
import javax.faces.component.ActionSource2; | |
import javax.faces.component.FacesComponent; | |
import javax.faces.component.UICommand; | |
import javax.faces.component.UIComponent; | |
import javax.faces.component.UIComponentBase; | |
import javax.faces.component.UIViewParameter; | |
import javax.faces.component.UIViewRoot; | |
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 javax.faces.event.PreRenderViewEvent; | |
import javax.faces.lifecycle.Lifecycle; | |
import javax.faces.lifecycle.LifecycleFactory; | |
import javax.faces.view.ViewMetadata; | |
import javax.faces.webapp.FacesServlet; | |
/** | |
* <p><strong>UIViewAction</strong> is an {@link ActionSource2} {@link | |
* UIComponent} that specifies an application-specific command (or | |
* action)--defined as an EL method expression--to be invoked during one of the | |
* JSF lifecycle phases that proceeds view rendering. This component must be | |
* declared as a child of the {@link ViewMetadata} facet of the {@link | |
* UIViewRoot} so that it gets incorporated into the JSF lifecycle on both | |
* non-faces (initial) requests and faces (postback) requests.</p> | |
* | |
* <p>The purpose of this component is to provide a light-weight | |
* front-controller solution for executing code upon the loading of a JSF view | |
* to support the integration of system services, content retrieval, view | |
* management, and navigation. This functionality is especially useful for | |
* non-faces (initial) requests.</p> | |
* | |
* <p>The {@link UIViewAction} component is closely tied to the {@link | |
* UIViewParameter} component. The {@link UIViewParameter} component binds a | |
* request parameter to a model property. Most of the time, this binding is used | |
* to populate the model with data that supports the method being invoked by a | |
* {@link UIViewAction} component, much like form inputs populate the model with | |
* data to support the method being invoked by a {@link UICommand} | |
* component.</p> | |
* | |
* <p>When the <literal>decode()</literal> method of the {@link UIViewAction} is | |
* invoked, it will queue an {@link ActionEvent} to be broadcast to all | |
* interested listeners when the <literal>broadcast()</literal> method is | |
* invoked.</p> | |
* | |
* <p>If the value of the component's <literal>immediate</literal> attribute is | |
* <literal>true</literal>, the action will be invoked during the Apply Request | |
* Values JSF lifecycle phase. Otherwise, the action will be invoked during the | |
* Invoke Application phase, the default behavior. The phase cannot be | |
* set explicitly in the <literal>phase</literal> attribute, which takes | |
* precedence over the <literal>immediate</literal> attribute.</p> | |
* | |
* <p>The invocation of the action is normally suppressed (meaning the {@link | |
* ActionEvent} is not queued) on a faces request. It can be enabled by setting | |
* the component's <literal>onPostback</literal> attribute to <literal>true</literal>. | |
* Execution of the method can be subject to a required condition for all requests by | |
* assigning an EL value expression of expected type boolean to the component's | |
* <literal>if</literal> attribute, which must evaluate to | |
* <literal>true</literal> for the action to be invoked.</p> | |
* | |
* <p>The {@link NavigationHandler} is consulted after the action is invoked to | |
* carry out the navigation case that matches the action signature and outcome. | |
* If a navigation case is matched, or the response is marked complete by the | |
* action, subsequent {@link UIViewAction} components associated with the | |
* current view are short-circuited. The lifecycle then advances | |
* appropriately.</p> | |
* | |
* <p>It's important to note that the full component tree is not built before | |
* the UIViewAction components are processed on an non-faces (initial) request. | |
* Rather, the component tree only contains the {@link ViewMetadata}, an | |
* important part of the optimization of this component and what sets it apart | |
* from a {@link PreRenderViewEvent} listener.</p> | |
* | |
* @author Dan Allen | |
* @author Andy Schwartz | |
* | |
* @see UIViewParameter | |
*/ | |
@FacesComponent( | |
// tagName = "viewAction", | |
// namespace = "http://java.sun.com/jsf/core", | |
// (see https://javaserverfaces-spec-public.dev.java.net/issues/show_bug.cgi?id=594) | |
value = UIViewAction.COMPONENT_TYPE) | |
public class UIViewAction extends UIComponentBase implements ActionSource2 { | |
// ------------------------------------------------------ Manifest Constants | |
/** | |
* <p> | |
* The standard component type for this component. | |
* </p> | |
*/ | |
public static final String COMPONENT_TYPE = "javax.faces.ViewAction"; | |
/** | |
* <p> | |
* The standard component family for this component. | |
* </p> | |
*/ | |
public static final String COMPONENT_FAMILY = "javax.faces.ViewAction"; | |
/** | |
* Properties that are tracked by state saving. | |
*/ | |
enum PropertyKeys | |
{ | |
onPostback, actionExpression, immediate, phase, ifAttr("if"); | |
private String name; | |
PropertyKeys() | |
{ | |
} | |
PropertyKeys(String name) | |
{ | |
this.name = name; | |
} | |
@Override | |
public String toString() | |
{ | |
return name != null ? name : super.toString(); | |
} | |
} | |
// ------------------------------------------------------------ Constructors | |
/** | |
* <p> | |
* Create a new {@link UIViewAction} instance with default property values. | |
* </p> | |
*/ | |
public UIViewAction() | |
{ | |
super(); | |
setRendererType(null); | |
} | |
// -------------------------------------------------------------- Properties | |
@Override | |
public String getFamily() | |
{ | |
return COMPONENT_FAMILY; | |
} | |
/** | |
* {@inheritDoc} | |
* | |
* @deprecated This has been replaced by {@link #getActionExpression}. | |
*/ | |
@Override | |
public MethodBinding getAction() { | |
MethodBinding result = null; | |
MethodExpression me; | |
if (null != (me = getActionExpression())) { | |
result = new MethodBindingMethodExpressionAdapter(me); | |
} | |
return result; | |
} | |
/** | |
* {@inheritDoc} | |
* | |
* @deprecated This has been replaced by {@link #setActionExpression(javax.el.MethodExpression)}. | |
* @throws UnsupportedOperationException if called | |
*/ | |
@Override | |
public void setAction(MethodBinding action) { | |
throw new UnsupportedOperationException("Not supported."); | |
} | |
/** | |
* Action listeners are not supported by the {@link UIViewAction} component. | |
* | |
* @throws UnsupportedOperationException if called | |
*/ | |
@Override | |
public MethodBinding getActionListener() { | |
throw new UnsupportedOperationException("Not supported."); | |
} | |
/** | |
* Action listeners are not supported by the {@link UIViewAction} component. | |
* | |
* @throws UnsupportedOperationException if called | |
*/ | |
@Override | |
public void setActionListener(MethodBinding actionListener) { | |
throw new UnsupportedOperationException("Not supported."); | |
} | |
/** | |
* Returns the value which dictates the JSF lifecycle phase in which the | |
* action is invoked. If the value of this attribute is | |
* <literal>true</literal>, the action will be invoked in the Apply Request | |
* Values phase. If the value of this attribute is <literal>true</literal>, | |
* the default, the action will be invoked in the Invoke Application Phase. | |
*/ | |
@Override | |
public boolean isImmediate() { | |
return (Boolean) getStateHelper().eval(PropertyKeys.immediate, false); | |
} | |
/** | |
* Sets the immediate flag, which controls the JSF lifecycle in which | |
* the action is invoked. | |
*/ | |
@Override | |
public void setImmediate(boolean immediate) { | |
getStateHelper().put(PropertyKeys.immediate, immediate); | |
} | |
/** | |
* <p>Returns the name of the phase in which the action is to be queued. Only | |
* the following phases are supported (case does not matter):</p> | |
* <ul> | |
* <li>APPLY_REQUEST_VALUES</li> | |
* <li>PROCESS_VALIDATIONS</li> | |
* <li>UPDATE_MODEL_VALUES</li> | |
* <li>INVOKE_APPLICATION</li> | |
* </ul> | |
* <p>If the phase is set, it takes precedence over the | |
* immediate flag.</p> | |
*/ | |
public String getPhase() { | |
String phase = (String) getStateHelper().eval(PropertyKeys.phase); | |
if (phase != null) { | |
phase = phase.toUpperCase(); | |
} | |
return phase; | |
} | |
/** | |
* Set the name of the phase in which the action is to be queued. | |
*/ | |
public void setPhase(String phase) { | |
getStateHelper().put(PropertyKeys.phase, phase); | |
} | |
public PhaseId getPhaseId() { | |
String phase = getPhase(); | |
if (phase == null) { | |
return null; | |
} | |
if ("APPLY_REQUEST_VALUES".equals(phase)) { | |
return PhaseId.APPLY_REQUEST_VALUES; | |
} else if ("PROCESS_VALIDATIONS".equals(phase)) { | |
return PhaseId.PROCESS_VALIDATIONS; | |
} else if ("UPDATE_MODEL_VALUES".equals(phase)) { | |
return PhaseId.UPDATE_MODEL_VALUES; | |
} else if ("INVOKE_APPLICATION".equals(phase)) { | |
return PhaseId.INVOKE_APPLICATION; | |
} else if ("ANY_PHASE".equals(phase) || "RESTORE_VIEW".equals(phase) || "RENDER_REPONSE".equals(phase)) { | |
throw new FacesException("View actions cannot be executed in specified phase: [" + phase + "]"); | |
} else { | |
throw new FacesException("Not a valid phase [" + phase + "]"); | |
} | |
} | |
/** | |
* Action listeners are not supported by the {@link UIViewAction} component. | |
* | |
* @throws UnsupportedOperationException if called | |
*/ | |
@Override | |
public void addActionListener(ActionListener listener) { | |
throw new UnsupportedOperationException("Not supported."); | |
} | |
/** | |
* Action listeners are not supported by the {@link UIViewAction} component. | |
*/ | |
@Override | |
public ActionListener[] getActionListeners() { | |
return new ActionListener[0]; | |
} | |
/** | |
* Action listeners are not supported by the {@link UIViewAction} component. | |
* | |
* @throws UnsupportedOperationException if called | |
*/ | |
@Override | |
public void removeActionListener(ActionListener listener) { | |
throw new UnsupportedOperationException("Not supported."); | |
} | |
/** | |
* Returns the action, represented as an EL method expression, to invoke. | |
*/ | |
@Override | |
public MethodExpression getActionExpression() | |
{ | |
return (MethodExpression) getStateHelper().get(PropertyKeys.actionExpression); | |
} | |
/** | |
* Sets the action, represented as an EL method expression, to invoke. | |
*/ | |
@Override | |
public void setActionExpression(MethodExpression actionExpression) | |
{ | |
getStateHelper().put(PropertyKeys.actionExpression, actionExpression); | |
} | |
/** | |
* Returns a boolean value that controls whether the action is invoked during | |
* faces (postback) request. | |
*/ | |
public boolean isOnPostback() | |
{ | |
return (Boolean) getStateHelper().eval(PropertyKeys.onPostback, false); | |
} | |
/** | |
* Set the bookean flag that controls whether the action is invoked during | |
* a faces (postback) request. | |
*/ | |
public void setOnPostback(boolean onPostback) | |
{ | |
getStateHelper().put(PropertyKeys.onPostback, onPostback); | |
} | |
/** | |
* Returns a condition, represented as an EL value expression, that must | |
* evaluate to true for the action to be invoked. | |
*/ | |
public boolean isIf() | |
{ | |
return (Boolean) getStateHelper().eval(PropertyKeys.ifAttr, true); | |
} | |
/** | |
* Sets the condition, represented as an EL value expression, that must | |
* evaluate to true for the action to be invoked. | |
*/ | |
public void setIf(boolean condition) | |
{ | |
getStateHelper().put(PropertyKeys.ifAttr, condition); | |
} | |
// ----------------------------------------------------- UIComponent Methods | |
/** | |
* <p>In addition to to the default {@link UIComponent#broadcast} processing, | |
* pass the {@link ActionEvent} being broadcast to the default {@link | |
* ActionListener} registered on the {@link | |
* javax.faces.application.Application}.</p> | |
* | |
* @param event {@link FacesEvent} to be broadcast | |
* | |
* @throws AbortProcessingException Signal the JavaServer Faces | |
* implementation that no further processing on the current event | |
* should be performed | |
* @throws IllegalArgumentException if the implementation class | |
* of this {@link FacesEvent} is not supported by this component | |
* @throws NullPointerException if <code>event</code> is | |
* <code>null</code> | |
*/ | |
@Override | |
public void broadcast(FacesEvent event) throws AbortProcessingException { | |
super.broadcast(event); | |
FacesContext context = getFacesContext(); | |
// only proceed if the response has not been marked complete and navigation to another view has not occurred | |
if (event instanceof ActionEvent && !context.getResponseComplete() && | |
context.getViewRoot() == getViewRootOf(event)) { | |
ActionListener listener = context.getApplication().getActionListener(); | |
if (listener != null) { | |
UIViewRoot viewRootBefore = context.getViewRoot(); | |
InstrumentedFacesContext instrumentedContext = new InstrumentedFacesContext(context); | |
// defer the call to renderResponse() that happens in ActionListener#processAction(ActionEvent) | |
instrumentedContext.disableRenderResponseControl().set(); | |
listener.processAction((ActionEvent) event); | |
instrumentedContext.restore(); | |
// if the response is marked complete, the story is over | |
if (!context.getResponseComplete()) { | |
UIViewRoot viewRootAfter = context.getViewRoot(); | |
// if the view id changed as a result of navigation, then execute the JSF lifecycle for the new view id | |
if (viewRootBefore != viewRootAfter) { | |
/* | |
// execute the JSF lifecycle by dispatching a forward request | |
// this approach is problematic because it throws a wrench in the event broadcasting | |
try { | |
context.getExternalContext().dispatch(context.getApplication() | |
.getViewHandler().getActionURL(context, viewRootAfter.getViewId()) | |
.substring(context.getExternalContext().getRequestContextPath().length())); | |
// kill this lifecycle execution | |
context.responseComplete(); | |
} catch (IOException e) { | |
throw new FacesException("Dispatch to viewId failed: " + viewRootAfter.getViewId(), e); | |
} | |
*/ | |
// manually execute the JSF lifecycle on the new view id | |
// certain tweaks have to be made to the FacesContext to allow us to reset the lifecycle | |
Lifecycle lifecycle = getLifecycle(context); | |
instrumentedContext = new InstrumentedFacesContext(context); | |
instrumentedContext.pushViewIntoRequestMap().clearViewRoot().clearPostback().set(); | |
lifecycle.execute(instrumentedContext); | |
instrumentedContext.restore(); | |
} | |
else { | |
// apply the deferred call (relevant when immediate is true) | |
context.renderResponse(); | |
} | |
} | |
} | |
} | |
} | |
/** | |
* First, determine if the action should be invoked by evaluating this | |
* components pre-conditions. If this is a faces (postback) request and the | |
* evaluated value of the postback attribute is false, take no action. If the | |
* evaluated value of the if attribute is false, take no action. If both | |
* conditions pass, proceed with creating an {@link ActionEvent}. | |
* | |
* Set the phaseId in which the queued {@link ActionEvent} should be | |
* broadcast by assigning the appropriate value to the phaseId property of | |
* the {@link ActionEvent} according to the evaluated value of the immediate | |
* attribute. If the value is <literal>true</literal>, set the phaseId to | |
* {@link PhaseId#APPLY_REQUEST_VALUES}. Otherwise, set the phaseId to to | |
* {@link PhaseId#INVOKE_APPLICATION}. | |
* | |
* Finally, queue the event by calling <literal>queueEvent()</literal> and | |
* passing the {@link ActionEvent} just created. | |
*/ | |
@Override | |
public void decode(FacesContext context) { | |
if (context == null) { | |
throw new NullPointerException(); | |
} | |
if ((context.isPostback() && !isOnPostback()) || !isIf()) { | |
return; | |
} | |
ActionEvent e = new ActionEvent(this); | |
PhaseId phaseId = getPhaseId(); | |
if (phaseId != null) { | |
e.setPhaseId(phaseId); | |
} | |
else if (isImmediate()) { | |
e.setPhaseId(PhaseId.APPLY_REQUEST_VALUES); | |
} | |
else { | |
e.setPhaseId(PhaseId.INVOKE_APPLICATION); | |
} | |
queueEvent(e); | |
} | |
private UIViewRoot getViewRootOf(FacesEvent e) { | |
UIComponent c = e.getComponent(); | |
do { | |
if (c instanceof UIViewRoot) { | |
return (UIViewRoot) c; | |
} | |
c = c.getParent(); | |
} while (c != null); | |
return null; | |
} | |
private Lifecycle getLifecycle(FacesContext context) { | |
LifecycleFactory lifecycleFactory = (LifecycleFactory) | |
FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY); | |
String lifecycleId = context.getExternalContext() | |
.getInitParameter(FacesServlet.LIFECYCLE_ID_ATTR); | |
if (lifecycleId == null) { | |
lifecycleId = LifecycleFactory.DEFAULT_LIFECYCLE; | |
} | |
return lifecycleFactory.getLifecycle(lifecycleId); | |
} | |
/** | |
* A FacesContext delegator that gives us the necessary controls over the FacesContext | |
* to allow the execution of the lifecycle to accomodate the UIViewAction sequence. | |
*/ | |
private class InstrumentedFacesContext extends FacesContextWrapper { | |
private FacesContext wrapped; | |
private boolean viewRootCleared = false; | |
private boolean renderedResponseControlDisabled = false; | |
private Boolean postback = null; | |
public InstrumentedFacesContext(FacesContext wrapped) { | |
this.wrapped = wrapped; | |
} | |
@Override | |
public FacesContext getWrapped() { | |
return wrapped; | |
} | |
@Override | |
public UIViewRoot getViewRoot() { | |
if (viewRootCleared) { | |
return null; | |
} | |
return wrapped.getViewRoot(); | |
} | |
@Override | |
public void setViewRoot(UIViewRoot viewRoot) { | |
viewRootCleared = false; | |
wrapped.setViewRoot(viewRoot); | |
} | |
@Override | |
public boolean isPostback() { | |
return postback == null ? wrapped.isPostback() : postback; | |
} | |
@Override | |
public void renderResponse() { | |
if (!renderedResponseControlDisabled) { | |
wrapped.renderResponse(); | |
} | |
} | |
/** | |
* Make it look like we have dispatched a request using the include method. | |
*/ | |
public InstrumentedFacesContext pushViewIntoRequestMap() { | |
getExternalContext().getRequestMap() | |
.put("javax.servlet.include.servlet_path", wrapped.getViewRoot().getViewId()); | |
return this; | |
} | |
public InstrumentedFacesContext clearPostback() { | |
postback = false; | |
return this; | |
} | |
public InstrumentedFacesContext clearViewRoot() { | |
viewRootCleared = true; | |
return this; | |
} | |
public InstrumentedFacesContext disableRenderResponseControl() { | |
renderedResponseControlDisabled = true; | |
return this; | |
} | |
public void set() { | |
setCurrentInstance(this); | |
} | |
public void restore() { | |
setCurrentInstance(wrapped); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment