Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.sun.faces.flow.FlowHandlerImpl Maven / Gradle / Ivy
Go to download
This is the master POM file for Oracle's Implementation of the JSF 2.2 Specification.
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2015 Oracle and/or its affiliates. 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.java.net/public/CDDL+GPL_1_1.html
* or packager/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 packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [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 com.sun.faces.flow;
import com.sun.faces.application.NavigationHandlerImpl;
import com.sun.faces.util.Util;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.el.ELContext;
import javax.el.MethodExpression;
import javax.el.ValueExpression;
import javax.faces.application.ConfigurableNavigationHandler;
import javax.faces.application.NavigationHandler;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.flow.FlowCallNode;
import javax.faces.flow.Flow;
import javax.faces.flow.FlowHandler;
import javax.faces.flow.Parameter;
public class FlowHandlerImpl extends FlowHandler {
public static final String ABANDONED_FLOW = "javax.faces.flow.AbandonedFlow";
public FlowHandlerImpl() {
flowFeatureIsEnabled = false;
flows = new ConcurrentHashMap<>();
flowsByFlowId = new ConcurrentHashMap<>();
}
private boolean flowFeatureIsEnabled;
// key: definingDocumentId, value: Map
private final Map> flows;
// key: flowId, List
private final Map> flowsByFlowId;
@Override
public Map getCurrentFlowScope() {
return FlowCDIContext.getCurrentFlowScopeAndUpdateSession();
}
@Override
public Flow getFlow(FacesContext context, String definingDocumentId, String id) {
Util.notNull("context", context);
Util.notNull("definingDocumentId", definingDocumentId);
Util.notNull("id", id);
Flow result = null;
Map mapsForDefiningDocument = flows.get(definingDocumentId);
if (null != mapsForDefiningDocument) {
result = mapsForDefiningDocument.get(id);
}
return result;
}
@Override
public void addFlow(FacesContext context, Flow toAdd) {
Util.notNull("context", context);
Util.notNull("toAdd", toAdd);
String id = toAdd.getId();
if (null == id || 0 == id.length()) {
throw new IllegalArgumentException("The id of the flow may not be null or zero-length.");
}
String definingDocumentId = toAdd.getDefiningDocumentId();
if (null == definingDocumentId) {
throw new IllegalArgumentException("The definingDocumentId of the flow may not be null.");
}
Map mapsForDefiningDocument = flows.get(definingDocumentId);
if (null == mapsForDefiningDocument) {
mapsForDefiningDocument = new ConcurrentHashMap<>();
flows.put(toAdd.getDefiningDocumentId(), mapsForDefiningDocument);
}
Flow oldFlow = mapsForDefiningDocument.put(id, toAdd);
if (null != oldFlow) {
String message = MessageFormat.format("Flow with id \"{0}\" and definingDocumentId \"{1}\" already exists.",
id, definingDocumentId);
throw new IllegalStateException(message);
}
// Make it possible for the "transition" method to map from view nodes
// to flow instances.
List flowsWithId = flowsByFlowId.get(id);
if (null == flowsWithId) {
flowsWithId = new CopyOnWriteArrayList<>();
flowsByFlowId.put(id, flowsWithId);
}
flowsWithId.add(toAdd);
NavigationHandler navigationHandler = context.getApplication().getNavigationHandler();
if (navigationHandler instanceof ConfigurableNavigationHandler) {
((ConfigurableNavigationHandler)navigationHandler).inspectFlow(context, toAdd);
}
flowFeatureIsEnabled = true;
}
@Override
public boolean isActive(FacesContext context, String definingDocumentId,
String id) {
Util.notNull("context", context);
Util.notNull("definingDocumentId", definingDocumentId);
Util.notNull("id", id);
boolean result = false;
FlowDeque flowStack = getFlowStack(context);
for (Flow cur : flowStack) {
if (id.equals(cur.getId()) &&
definingDocumentId.equals(cur.getDefiningDocumentId())) {
result = true;
break;
}
}
return result;
}
@Override
public Flow getCurrentFlow(FacesContext context) {
Util.notNull("context", context);
if (!flowFeatureIsEnabled) {
return null;
}
Flow result = null;
// If there is no session, there cannot possibly be a flow, so
// don't create one just to check.
if (null == context.getExternalContext().getSession(false)) {
return null;
}
FlowDeque flowStack = getFlowStack(context);
int returnDepth = flowStack.getReturnDepth();
if (flowStack.size() <= returnDepth) {
return null;
}
if (0 < returnDepth) {
Iterator stackIter = flowStack.iterator();
int i = 0;
stackIter.next();
if (stackIter.hasNext()) {
do {
result = stackIter.next();
i++;
} while (i < returnDepth);
}
} else {
result = getFlowStack(context).peekFirst();
}
return result;
}
@Override
public String getLastDisplayedViewId(FacesContext context) {
Util.notNull("context", context);
String result = null;
FlowDeque flowStack = getFlowStack(context);
result = flowStack.peekLastDisplayedViewId();
return result;
}
public static final String FLOW_RETURN_DEPTH_PARAM_NAME = "jffrd";
public int getAndClearReturnModeDepth(FacesContext context) {
int result = 0;
FlowDeque flowStack = getFlowStack(context);
result = flowStack.getAndClearMaxReturnDepth(context);
return result;
}
@Override
public void pushReturnMode(FacesContext context) {
Util.notNull("context", context);
FlowDeque flowStack = getFlowStack(context);
flowStack.pushReturnMode();
}
@Override
public void popReturnMode(FacesContext context) {
Util.notNull("context", context);
FlowDeque flowStack = getFlowStack(context);
flowStack.popReturnMode();
}
// We need a method that takes a view id of a view that is in a flow
// and makes the system "enter" the flow.
@Override
@SuppressWarnings(value="")
public void transition(FacesContext context, Flow sourceFlow,
Flow targetFlow, FlowCallNode outboundCallNode,
String toViewId) {
Util.notNull("context", context);
Util.notNull("toViewId", toViewId);
if (!flowFeatureIsEnabled) {
return;
}
// there has to be a better way to structure this logic
if (!flowsEqual(sourceFlow, targetFlow)) {
// Do we have an outboundCallNode?
Map evaluatedParams = null;
if (null != outboundCallNode) {
Map outboundParameters = outboundCallNode.getOutboundParameters();
Map inboundParameters = targetFlow.getInboundParameters();
// Are we passing parameters?
if (null != outboundParameters && !outboundParameters.isEmpty() &&
null != inboundParameters && !inboundParameters.isEmpty()) {
ELContext elContext = context.getELContext();
String curName;
// for each outbound parameter...
for (Map.Entry curOutbound : outboundParameters.entrySet()) {
curName = curOutbound.getKey();
if (inboundParameters.containsKey(curName)) {
if (null == evaluatedParams) {
evaluatedParams = new HashMap<>();
}
// Evaluate it and put it in the temporary map.
// It is necessary to do this before the flow
// transition because EL expressions may refer to
// things in the current flow scope.
evaluatedParams.put(curName, curOutbound.getValue().getValue().getValue(elContext));
}
}
}
}
performPops(context, sourceFlow, targetFlow);
if (null != targetFlow && !targetFlow.equals(FlowImpl.ABANDONED_FLOW)) {
pushFlow(context, targetFlow, toViewId, evaluatedParams);
} else {
assignInboundParameters(context, targetFlow, evaluatedParams);
}
}
}
private void assignInboundParameters(FacesContext context, Flow calledFlow,
Map evaluatedParams) {
// Now the new flow is active, it's time to evaluate the inbound
// parameters.
if (null != evaluatedParams) {
Map inboundParameters = calledFlow.getInboundParameters();
ELContext elContext = context.getELContext();
String curName;
ValueExpression toSet;
for (Map.Entry curOutbound : evaluatedParams.entrySet()) {
curName = curOutbound.getKey();
assert(inboundParameters.containsKey(curName));
toSet = inboundParameters.get(curName).getValue();
toSet.setValue(elContext, curOutbound.getValue());
}
}
}
@Override
public void clientWindowTransition(FacesContext context) {
Map requestParamMap = context.getExternalContext().getRequestParameterMap();
String toFlowDocumentId = requestParamMap.get(TO_FLOW_DOCUMENT_ID_REQUEST_PARAM_NAME);
String flowId = requestParamMap.get(FLOW_ID_REQUEST_PARAM_NAME);
if (null != toFlowDocumentId) {
// don't use *this*, due to decoration
FlowHandler fh = context.getApplication().getFlowHandler();
Flow sourceFlow = fh.getCurrentFlow(context);
Flow targetFlow = null;
FlowCallNode flowCallNode = null;
// if this is not a return...
if (null != flowId && !FlowHandler.NULL_FLOW.equals(toFlowDocumentId)) {
targetFlow = fh.getFlow(context, toFlowDocumentId, flowId);
if (null != targetFlow && null != sourceFlow) {
flowCallNode = sourceFlow.getFlowCall(targetFlow);
}
} else {
String maxReturnDepthStr = requestParamMap.get(FLOW_RETURN_DEPTH_PARAM_NAME);
int maxReturnDepth = Integer.valueOf(maxReturnDepthStr).intValue();
FlowDeque flowStack = getFlowStack(context);
flowStack.setMaxReturnDepth(context, maxReturnDepth);
}
fh.transition(context, sourceFlow, targetFlow, flowCallNode, context.getViewRoot().getViewId());
}
}
private void performPops(FacesContext context, Flow sourceFlow, Flow targetFlow) {
// case 0: sourceFlow is null. There must be nothing to pop.
if (null == sourceFlow) {
assert(null == peekFlow(context));
return;
}
// case 1: target is null
if (null == targetFlow) {
FlowDeque flowStack = getFlowStack(context);
int maxReturns = flowStack.getAndClearMaxReturnDepth(context);
for (int i=0; i < maxReturns; i++) {
popFlow(context);
}
return;
}
if (FlowImpl.ABANDONED_FLOW.equals(targetFlow)) {
FlowDeque flowStack = getFlowStack(context);
int depth = flowStack.size();
for (int i=0; i
private void pushFlow(FacesContext context, Flow toPush, String lastDisplayedViewId,
Map evaluatedParams) {
FlowDeque flowStack = getFlowStack(context);
flowStack.addFirst(toPush, lastDisplayedViewId);
FlowCDIContext.flowEntered();
assignInboundParameters(context, toPush, evaluatedParams);
MethodExpression me = toPush.getInitializer();
if (null != me) {
me.invoke(context.getELContext(), null);
}
forceSessionUpdateForFlowStack(context, flowStack);
}
private Flow peekFlow(FacesContext context) {
FlowDeque flowStack = getFlowStack(context);
return flowStack.peekFirst();
}
private Flow popFlow(FacesContext context) {
FlowDeque flowStack = getFlowStack(context);
Flow currentFlow = peekFlow(context);
if (null != currentFlow) {
callFinalizer(context, currentFlow, flowStack.size());
}
Flow result = flowStack.pollFirst();
forceSessionUpdateForFlowStack(context, flowStack);
return result;
}
private void callFinalizer(FacesContext context, Flow currentFlow, int depth) {
MethodExpression me = currentFlow.getFinalizer();
if (null != me) {
me.invoke(context.getELContext(), null);
}
FlowCDIContext.flowExited(currentFlow, depth);
}
static FlowDeque getFlowStack(FacesContext context) {
FlowDeque result = null;
ExternalContext extContext = context.getExternalContext();
String sessionKey = extContext.getClientWindow().getId() + "_flowStack";
Map sessionMap = extContext.getSessionMap();
result = (FlowDeque) sessionMap.get(sessionKey);
if (null == result) {
result = new FlowDeque<>(sessionKey);
sessionMap.put(sessionKey, result);
}
return result;
}
private void forceSessionUpdateForFlowStack(FacesContext context, FlowDeque stack) {
ExternalContext extContext = context.getExternalContext();
Map sessionMap = extContext.getSessionMap();
sessionMap.put(stack.getSessionKey(), stack);
}
static class FlowDeque implements Iterable, Serializable {
private static final long serialVersionUID = 7915803727932706270L;
private int returnDepth;
private ArrayDeque data;
private static class RideAlong implements Serializable {
private static final long serialVersionUID = -1899365746835118058L;
String lastDisplayedViewId;
public RideAlong(String lastDisplayedViewId) {
this.lastDisplayedViewId = lastDisplayedViewId;
}
}
private ArrayDeque rideAlong;
private final String sessionKey;
public FlowDeque(final String sessionKey) {
data = new ArrayDeque<>();
rideAlong = new ArrayDeque<>();
this.sessionKey = sessionKey;
}
public String getSessionKey() {
return sessionKey;
}
public int size() {
return data.size();
}
@Override
public Iterator iterator() {
return data.iterator();
}
public void addFirst(E e, String lastDisplayedViewId) {
rideAlong.addFirst(new RideAlong(lastDisplayedViewId));
data.addFirst(e);
}
public E pollFirst() {
rideAlong.pollFirst();
return data.pollFirst();
}
public int getCurrentFlowDepth( ) {
int returnDepth = this.returnDepth;
if (data.size() <= returnDepth) {
return 0;
}
int result = data.size();
if (0 < returnDepth) {
Iterator stackIter = data.iterator();
int i = 0;
while ( stackIter.hasNext() && i < returnDepth ) {
stackIter.next();
result--;
i++;
}
}
return result;
}
public E peekFirst() {
return data.peekFirst();
}
public String peekLastDisplayedViewId() {
String result = null;
RideAlong helper = null;
int myReturnDepth = this.getReturnDepth();
if (0 < myReturnDepth) {
Iterator stackIter = rideAlong.iterator();
stackIter.next();
int i = 0;
if (stackIter.hasNext()) {
do {
helper = stackIter.next();
i++;
} while (i < myReturnDepth);
}
} else {
helper = rideAlong.peekFirst();
}
if (null != helper) {
result = helper.lastDisplayedViewId;
}
return result;
}
public int getReturnDepth() {
return returnDepth;
}
private void setMaxReturnDepth(FacesContext context, int value) {
Map attrs = context.getAttributes();
attrs.put(FLOW_RETURN_DEPTH_PARAM_NAME, value);
}
private int getAndClearMaxReturnDepth(FacesContext context) {
Map attrs = context.getAttributes();
int result = 0;
if (attrs.containsKey(FLOW_RETURN_DEPTH_PARAM_NAME)) {
result = ((Integer)attrs.remove(FLOW_RETURN_DEPTH_PARAM_NAME)).intValue();
}
return result;
}
private void incrementMaxReturnDepth() {
FacesContext context = FacesContext.getCurrentInstance();
Map attrs = context.getAttributes();
if (!attrs.containsKey(FLOW_RETURN_DEPTH_PARAM_NAME)) {
attrs.put(FLOW_RETURN_DEPTH_PARAM_NAME, (Integer) 1);
} else {
Integer cur = (Integer) attrs.get(FLOW_RETURN_DEPTH_PARAM_NAME);
attrs.put(FLOW_RETURN_DEPTH_PARAM_NAME, (Integer) cur + 1);
}
}
private void decrementMaxReturnDepth() {
FacesContext context = FacesContext.getCurrentInstance();
Map attrs = context.getAttributes();
if (attrs.containsKey(FLOW_RETURN_DEPTH_PARAM_NAME)) {
Integer cur = (Integer) attrs.get(FLOW_RETURN_DEPTH_PARAM_NAME);
if (cur > 1) {
attrs.put(FLOW_RETURN_DEPTH_PARAM_NAME, (Integer) cur - 1);
}
else {
attrs.remove(FLOW_RETURN_DEPTH_PARAM_NAME);
}
}
}
public void pushReturnMode() {
this.incrementMaxReturnDepth();
this.returnDepth++;
}
public void popReturnMode() {
// Mojarra #4279 If ConfigureableNavigationHandler is attempting to obtain a NavigationCase outside of
// OutcomeTargetRenderer, then this method must perform exactly the reverse of pushReturnMode() to ensure
// that ConfigureableNavigationHandler's calls are idempotent. For more details, see
// https://github.com/javaserverfaces/mojarra/issues/4279
if (NavigationHandlerImpl.isResetFlowHandlerState(FacesContext.getCurrentInstance())) {
this.decrementMaxReturnDepth();
}
this.returnDepth--;
}
}
//
}