flowexecutionimpl.java

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

JAVA
536
字号
	}

	public FlowSession getActiveSession() {
		return getActiveSessionInternal();
	}
	
	/**
	 * Check that this flow execution is active and throw an exception if it's not.
	 */
	protected void assertActive() throws IllegalStateException {
		if (!isActive()) {
			throw new IllegalStateException(
					"No active flow sessions executing - this flow execution has ended (or has never been started)");
		}
	}

	// methods implementing FlowExecution

	public synchronized ViewDescriptor start(Event sourceEvent) throws IllegalStateException {
		Assert.state(!isActive(), "This flow is already executing -- you cannot call start more than once");
		updateLastRequestTimestamp();
		if (logger.isDebugEnabled()) {
			logger.debug("Start event signaled: " + sourceEvent);
		}
		StateContext context = createStateContext(sourceEvent);
		getListeners().fireRequestSubmitted(context);
		try {
			ViewDescriptor viewDescriptor = context.spawn(rootFlow.getStartState(), new HashMap());
			if (isActive()) {
				getActiveSessionInternal().setStatus(FlowSessionStatus.PAUSED);
				getListeners().firePaused(context);
			}
			return viewDescriptor;
		}
		finally {
			getListeners().fireRequestProcessed(context);
		}
	}

	public synchronized ViewDescriptor signalEvent(Event sourceEvent) throws FlowNavigationException, IllegalStateException {
		assertActive();
		updateLastRequestTimestamp();
		if (logger.isDebugEnabled()) {
			logger.debug("Resume event signaled: " + sourceEvent);
		}
		String stateId = sourceEvent.getStateId();
		if (!StringUtils.hasText(stateId)) {
			if (logger.isDebugEnabled()) {
				logger.debug("Current state id was not provided in request to signal event '"
						+ sourceEvent.getId()
						+ "' in flow "
						+ getCaption()
						+ "' -- pulling current state id from session -- "
						+ "note: if the user has been using the browser back/forward buttons, the currentState could be incorrect.");
			}
			stateId = getCurrentState().getId();
		}
		TransitionableState state = getActiveFlow().getRequiredTransitionableState(stateId);
		if (!state.equals(getCurrentState())) {
			if (logger.isDebugEnabled()) {
				logger.debug("Event '" + sourceEvent.getId() + "' in state '" + state.getId()
						+ "' was signaled by client; however the current flow execution state is '"
						+ getCurrentState().getId() + "'; updating current state to '" + state.getId() + "'");
			}
			setCurrentState(state);
		}
		// execute the event
		StateContextImpl context = createStateContext(sourceEvent);
		getListeners().fireRequestSubmitted(context);
		getActiveSessionInternal().setStatus(FlowSessionStatus.ACTIVE);
		getListeners().fireResumed(context);
		try {
			ViewDescriptor viewDescriptor = state.onEvent(sourceEvent, context);
			if (isActive()) {
				getActiveSessionInternal().setStatus(FlowSessionStatus.PAUSED);
				getListeners().firePaused(context);
			}
			return viewDescriptor;
		}
		finally {
			getListeners().fireRequestProcessed(context);
		}
	}
	
	public FlowExecutionListenerList getListeners() {
		return listenerList;
	}

	// flow session management helpers
	
	/**
	 * Create a flow execution state context for given event.
	 * <p>
	 * The default implementation uses the <code>StateContextImpl</code>
	 * class. Subclasses can override this to use a custom class.
	 * @param sourceEvent the event at the origin of this request
	 */
	protected StateContextImpl createStateContext(Event sourceEvent) {
		return new StateContextImpl(sourceEvent, this);
	}
	
	/**
	 * Returns the currently active flow session.
	 * @throws IllegalStateException this execution is not active
	 */
	protected FlowSessionImpl getActiveSessionInternal() throws IllegalStateException {
		assertActive();
		return (FlowSessionImpl)executingFlowSessions.peek();
	}

	/**
	 * Returns the parent flow session of the currently active flow session.
	 * @return the parent flow session
	 * @throws IllegalArgumentException when this execution is not active or
	 *         when the current flow session has no parent (e.g. is the root
	 *         flow session)
	 */
	protected FlowSession getParentSession() throws IllegalArgumentException {
		assertActive();
		Assert.state(!getActiveSession().isRoot(), "There is no parent flow session for the currently active flow session");
		return (FlowSession)executingFlowSessions.get(executingFlowSessions.size() - 2);
	}

	/**
	 * Returns the flow session associated with the root flow.
	 * @throws IllegalStateException this execution is not active
	 */
	protected FlowSession getRootSession() throws IllegalStateException {
		assertActive();
		return (FlowSession)executingFlowSessions.get(0);
	}

	/**
	 * Set the state that is currently active in this flow execution.
	 * @param newState the new current state
	 */
	protected void setCurrentState(State newState) {
		getActiveSessionInternal().setCurrentState(newState);
	}

	/**
	 * Create a new flow session and activate it in this flow execution. This
	 * will push the flow session onto the stack and mark it as the active flow
	 * session.
	 * @param context the flow execution request context
	 * @param subflow the flow that should be associated with the flow session
	 * @param input the input parameters used to populate the flow session
	 * @return the created and activated flow session
	 */
	protected FlowSession activateSession(RequestContext context, Flow subflow, Map input) {
		FlowSessionImpl session;
		if (!executingFlowSessions.isEmpty()) {
			FlowSessionImpl parent = getActiveSessionInternal();
			parent.setStatus(FlowSessionStatus.SUSPENDED);
			session = createFlowSession(subflow, input, parent);
		}
		else {
			session = createFlowSession(subflow, input, null);
		}
		executingFlowSessions.push(session);
		session.setStatus(FlowSessionStatus.ACTIVE);
		if (logger.isDebugEnabled()) {
			logger.debug("Activated: " + session);
		}
		return session;
	}

	/**
	 * Create a new flow session object. Subclasses can override this to return
	 * a special implementation if required.
	 * @param flow the flow that should be associated with the flow session
	 * @param input the input parameters used to populate the flow session
	 * @param parent the flow session that should be the parent of the newly
	 *        created flow session
	 * @return the newly created flow session
	 */
	protected FlowSessionImpl createFlowSession(Flow flow, Map input, FlowSessionImpl parent) {
		return new FlowSessionImpl(flow, input, parent);
	}

	/**
	 * End the active flow session of this flow execution. This will pop the top
	 * element from the stack and activate the new top flow session.
	 * @return the flow session that ended
	 */
	protected FlowSession endActiveFlowSession() {
		FlowSessionImpl endingSession = (FlowSessionImpl)executingFlowSessions.pop();
		endingSession.setStatus(FlowSessionStatus.ENDED);
		if (logger.isDebugEnabled()) {
			logger.debug("Ended: " + endingSession);
		}		
		if (!executingFlowSessions.isEmpty()) {
			getActiveSessionInternal().setStatus(FlowSessionStatus.ACTIVE);
			if (logger.isDebugEnabled()) {
				logger.debug("Resumed: " + getActiveSessionInternal());
			}		
		}
		return endingSession;
	}

	// custom serialization

	private void writeObject(ObjectOutputStream out) throws IOException {
		out.writeObject(this.key);
		out.writeLong(this.creationTimestamp);
		if (this.getRootFlow() != null) {
			// avoid bogus NullPointerExceptions
			out.writeObject(this.getRootFlow().getId());
		}
		else {
			out.writeObject(null);
		}
		out.writeObject(this.lastEventId);
		out.writeLong(this.lastRequestTimestamp);
		out.writeObject(this.executingFlowSessions);
	}

	private void readObject(ObjectInputStream in) throws OptionalDataException, ClassNotFoundException, IOException {
		this.key = (String)in.readObject();
		this.creationTimestamp = in.readLong();
		this.rootFlowId = (String)in.readObject();
		this.lastEventId = (String)in.readObject();
		this.lastRequestTimestamp = in.readLong();
		this.executingFlowSessions = (Stack)in.readObject();
	}

	public synchronized void rehydrate(FlowLocator flowLocator, FlowExecutionListenerLoader listenerLoader,
			TransactionSynchronizer transactionSynchronizer) {
		// implementation note: we cannot integrate this code into the
		// readObject() method since we need the flow locator, listener list and tx synchronizer!
		if (this.rootFlow != null) {
			// nothing to do, we're already hydrated
			return;
		}
		Assert.notNull(rootFlowId, "The root flow id was not set during deserialization: cannot restore"
				+ " -- was this flow execution deserialized properly?");
		this.rootFlow = flowLocator.getFlow(rootFlowId);
		this.rootFlowId = null;
		// rehydrate all flow sessions
		Iterator it = this.executingFlowSessions.iterator();
		while (it.hasNext()) {
			FlowSessionImpl session = (FlowSessionImpl)it.next();
			session.rehydrate(flowLocator);
		}
		if (isActive()) {
			// sanity check
			Assert.isTrue(getRootFlow() == getRootSession().getFlow(),
					"The root flow of the execution should be the same as the flow in the root flow session");
		}
		this.listenerList = new FlowExecutionListenerList();
		this.listenerList.add(listenerLoader.getListeners(this.rootFlow));
		this.transactionSynchronizer = transactionSynchronizer;
	}

	public String toString() {
		if (!isActive()) {
			return "[Empty FlowExecutionStack with key '" + getKey() + "'; no flows are active]";
		}
		else {
			return new ToStringCreator(this)
					.append("key", getKey())
					.append("activeFlow", getActiveSession().getFlow().getId())
					.append("currentState",	getCurrentState().getId())
					.append("rootFlow", getRootFlow().getId())
					.append("executingFlowSessions", executingFlowSessions).toString();
		}
	}
}

⌨️ 快捷键说明

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