flowexecutionmanager.java

来自「spring的WEB开发插件,支持多状态WEB开发」· Java 代码 · 共 577 行 · 第 1/2 页

JAVA
577
字号
/*
 * Copyright 2002-2005 the original author or authors.
 *
 * Licensed 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 org.springframework.webflow.execution;

import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.binding.convert.ConversionService;
import org.springframework.core.style.StylerUtils;
import org.springframework.util.Assert;
import org.springframework.util.CachingMapDecorator;
import org.springframework.util.StringUtils;
import org.springframework.webflow.Event;
import org.springframework.webflow.Flow;
import org.springframework.webflow.FlowExecutionContext;
import org.springframework.webflow.ViewDescriptor;
import org.springframework.webflow.access.BeanFactoryFlowServiceLocator;
import org.springframework.webflow.access.FlowLocator;
import org.springframework.webflow.convert.FlowConversionService;

/**
 * A manager for the executing flows of the application. This object is responsible for
 * creating new flow executions as requested by the client, as well as signaling events
 * for processing by existing, paused executions (that are waiting to be resumed in response
 * to a user event).
 * <p>
 * The {@link #onEvent(Event)} method implements the following algorithm:
 * <ol>
 * <li>Look for a flow execution id in the event (in a parameter named
 * "_flowExecutionId").</li>
 * <li>If no flow execution id is found, a new flow execution is created.
 * The top-level flow for which the execution is created is determined
 * by first looking for a flow id specified in the event using the "_flowId"
 * parameter. If this parameter is present the specified flow will be
 * used, after lookup using a flow locator. If no "_flowId" parameter is
 * present, the default top-level flow configured for this manager is used.</li>
 * <li>If a flow execution id is found, the previously saved flow execution
 * with that id is loaded from the storage.</li>
 * <li>If a new flow execution was created in the previous steps, it is
 * started.</li>
 * <li>If an existing flow execution was loaded from storage, the current state id
 * ("_currentStateId") and event id ("_eventId") parameter values are
 * extracted from the event. The event is then signaled in that state, and 
 * the executing flow is resumed in that state.</li>
 * <li>If the flow execution is still active after event processing, it
 * is saved in storage. This process generates a unique flow execution
 * id that will be exposed to the caller for reference on subsequent events.
 * The caller will also be given access to the flow execution context and
 * any data placed in request or flow scope.</li>
 * </ol>
 * <p>
 * By default, this class will use the flow execution implementation provided
 * by the <code>FlowExecutionImpl</code> class. If you would like to use a 
 * different implementation, just override the {@link #createFlowExecution(Flow)}
 * method in a subclass.
 * 
 * @see org.springframework.webflow.execution.FlowExecution
 * @see org.springframework.webflow.execution.FlowExecutionStorage
 * 
 * @author Erwin Vervaet
 * @author Keith Donald
 */
public class FlowExecutionManager implements BeanFactoryAware, FlowExecutionListenerLoader {

	/**
	 * Clients can send the id (name) of the flow to be started
	 * using an event parameter with this name ("_flowId").
	 */
	public static final String FLOW_ID_PARAMETER = "_flowId";

	/**
	 * Clients can send the flow execution id using an event
	 * parameter with this name ("_flowExecutionId").
	 */
	public static final String FLOW_EXECUTION_ID_PARAMETER = "_flowExecutionId";

	/**
	 * The id of the flow execution will be exposed to the view in a model
	 * attribute with this name ("flowExecutionId").
	 */
	public static final String FLOW_EXECUTION_ID_ATTRIBUTE = "flowExecutionId";

	/**
	 * The flow context itself will be exposed to the view in a model
	 * attribute with this name ("flowExecutionContext").
	 */
	public static final String FLOW_EXECUTION_CONTEXT_ATTRIBUTE = "flowExecutionContext";

	/**
	 * The current state of the flow execution will be exposed to the view in a
	 * model attribute with this name ("currentStateId").
	 */
	public static final String CURRENT_STATE_ID_ATTRIBUTE = "currentStateId";

	/**
	 * Event id value indicating that the event has not been set ("@NOT_SET@").
	 */
	public static final String NOT_SET_EVENT_ID = "@NOT_SET@";
	
	protected final Log logger = LogFactory.getLog(FlowExecutionManager.class);

	private Flow flow;

	private FlowLocator flowLocator = new BeanFactoryFlowServiceLocator();

	/**
	 * A map of all know flow execution listeners (the key) and their associated
	 * flow execution listener criteria objects (a list -- the value).
	 */
	private CachingMapDecorator flowExecutionListeners = new CachingMapDecorator() {
		protected Object create(Object key) {
			return new LinkedList();
		}
	};

	private FlowExecutionStorage storage;

	private TransactionSynchronizer transactionSynchronizer = new FlowScopeTokenTransactionSynchronizer();
	
	private ConversionService conversionService = new FlowConversionService();

	private BeanFactory beanFactory;

	/**
	 * Create a new flow execution manager. Before use, the manager should
	 * be appropriately configured using setter methods. At least the flow
	 * execution storage strategy should be set!
	 * 
	 * @see #setFlow(Flow)
	 * @see #setFlowLocator(FlowLocator)
	 * @see #setListener(FlowExecutionListener)
	 * @see #setListener(FlowExecutionListener, FlowExecutionListenerCriteria)
	 * @see #setListenerMap(Map)
	 * @see #setListeners(Collection)
	 * @see #setListeners(Collection, FlowExecutionListenerCriteria)
	 * @see #setStorage(FlowExecutionStorage) 
	 * @see #setTransactionSynchronizer(TransactionSynchronizer)
	 * @see #setConversionService(ConversionService)
	 */
	public FlowExecutionManager() {
	}

	/**
	 * Returns the flow whose executions are managed by this manager.
	 * Could be <code>null</code> if there is no preconfigured flow and
	 * the id of the flow for which executions will be managed is sent
	 * in an event parameter "_flowId".
	 */
	protected Flow getFlow() {
		return flow;
	}

	/**
	 * Set the flow whose executions will be managed if there is no alternate
	 * flow id specified in a "_flowId" event parameter.
	 */
	public void setFlow(Flow flow) {
		this.flow = flow;
	}

	/**
	 * Returns the flow locator to use for lookup of flows specified using the
	 * "_flowId" event parameter.
	 */
	protected FlowLocator getFlowLocator() {
		return flowLocator;
	}

	/**
	 * Set the flow locator to use for lookup of flows specified using the
	 * "_flowId" event parameter.
	 */
	public void setFlowLocator(FlowLocator flowLocator) {
		this.flowLocator = flowLocator;
	}

	/**
	 * Returns the array of flow execution listeners for specified flow.
	 * @param flow the flow definition associated with the execution to be listened to
	 * @return the flow execution listeners
	 */
	public FlowExecutionListener[] getListeners(Flow flow) {
		Assert.notNull(flow, "The Flow to load listeners for cannot be null");
		List listeners = new LinkedList();
		for (Iterator entryIt = flowExecutionListeners.entrySet().iterator(); entryIt.hasNext(); ) {
			Map.Entry entry = (Map.Entry)entryIt.next();
			for (Iterator criteriaIt = ((List)entry.getValue()).iterator(); criteriaIt.hasNext(); ) {
				FlowExecutionListenerCriteria criteria = (FlowExecutionListenerCriteria)criteriaIt.next();
				if (criteria.applies(flow)) {
					// the criteria 'guarding' this flow execution listener is
					// telling us that the listener applies to the flow
					listeners.add((FlowExecutionListener)entry.getKey());
					break;
				}
			}
		}
		if (logger.isDebugEnabled()) {
			logger.debug("Loaded " + listeners.size() + " of possible " + flowExecutionListeners.size() + " listeners to this execution request for flow: '" + flow.getId() 
					+ "', the listeners to attach are: " + StylerUtils.style(listeners));
		}
		return (FlowExecutionListener[])listeners.toArray(new FlowExecutionListener[listeners.size()]);
	}

	/**
	 * Set the flow execution listener that will be notified of managed
	 * flow executions.
	 */
	public void setListener(FlowExecutionListener listener) {
		setListeners(Collections.singleton(listener));
	}

	/**
	 * Set the flow execution listener that will be notified of managed
	 * flow executions for the flows that match given criteria.
	 */
	public void setListener(FlowExecutionListener listener, FlowExecutionListenerCriteria criteria) {
		setListeners(Collections.singleton(listener), criteria);
	}

	/**
	 * Sets the flow execution listeners that will be notified of managed
	 * flow executions.
	 */
	public void setListeners(Collection listeners) {
		setListeners(listeners, FlowExecutionListenerCriteriaFactory.allFlows());
	}
	
	/**
	 * Sets the flow execution listeners that will be notified of managed
	 * flow executions for flows that match given criteria.
	 */
	public void setListeners(Collection listeners, FlowExecutionListenerCriteria criteria) {
		for (Iterator it = listeners.iterator(); it.hasNext(); ) {
			FlowExecutionListener listener = (FlowExecutionListener)it.next();
			List registeredCriteria = (List)flowExecutionListeners.get(listener);
			registeredCriteria.add(criteria);
		}
	}

	/**
	 * Sets the flow execution listeners that will be notified of managed
	 * flow executions. The map keys may be individual flow execution listener instances or 
	 * collections of execution listener instances. The map values can either
	 * be string encoded flow execution listener criteria or direct
	 * <code>FlowExecutionListenerCriteria</code> objects.
	 */
	public void setListenerMap(Map listenerCriteriaMap) {
		Iterator it = listenerCriteriaMap.entrySet().iterator();
		while (it.hasNext()) {
			Map.Entry entry = (Map.Entry)it.next();
			FlowExecutionListenerCriteria criteria;
			if (entry.getValue() instanceof FlowExecutionListenerCriteria) {
				criteria = (FlowExecutionListenerCriteria)entry.getValue();
			}
			else {
				// string encoded
				criteria = 
					(FlowExecutionListenerCriteria)getConversionService().
						getConversionExecutor(String.class, FlowExecutionListenerCriteria.class).execute(entry.getValue());
			}
			if (entry.getKey() instanceof Collection) {
				setListeners((Collection)entry.getKey(), criteria);
			}
			else {
				setListener((FlowExecutionListener)entry.getKey(), criteria);
			}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?