Split off AutowiringPhaseListener.

This commit is contained in:
Erwin Vervaet
2008-06-23 12:21:38 +00:00
parent ec9d5a5e83
commit a3d28aa48c
4 changed files with 251 additions and 95 deletions

View File

@@ -14,7 +14,7 @@ Package org.springframework.webflow.action
Package org.springframework.webflow.executor
* Statics on FlowFacesUtils are now public: they're supposed to be well known names.
* FlowPhaseListener.cleanupResources() is now protected.
* Added AutowiringFlowExecutionListener (SWF-746).
* Added AutowiringSupport, AutowiringFlowExecutionListener and AutowiringPhaseListener (SWF-746).
* In a JSF 1.2 environment, SWF will now correctly maintain the "java.faces.ViewState" parameter accross flow
execution redirects (SWF-445).
* FlowPhaseListener now maintains FacesMessages accross a flow execution redirect by temporarily storing them in

View File

@@ -0,0 +1,68 @@
package org.springframework.webflow.executor.jsf;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.servlet.ServletContext;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.webflow.execution.ScopeType;
import org.springframework.webflow.executor.support.AutowiringSupport;
/**
* JSF phase listener that that can be combined with the {@link FlowPhaseListener} to automatically autowire objects in
* the flow execution scopes every time a flow execution is restored. In faces-config.xml it should be declared <i>after</i>
* the FlowPhaseListener to make sure the FlowPhaseListener first restores the flow execution before this listener tries
* to autowire objects inside the flow execution.
* <p>
* This listener is particularly useful when you are storing stateful objects inside the flow execution that have
* dependencies on other objects, typically singleton service objects managed by Spring in an application context. Since
* Web Flow serializes the contents of flash and flow scope inbetween requests, those dependencies should be marked as
* transient to avoid serializing the referenced objects (services). As a consequence however, those dependencies are
* lost when the flow execution is restored. This listener makes it possible to rewire any required depencencies every
* time the flow execution is restored.
* <p>
* By default, the listener will use autowiring by name. This behaviour can be changed by setting the "autowireMode"
* property to the appropriate value.
* <p>
* Dependency checking is turned off by default: if no matching service layer bean can be found, the setter in question
* will simply not get invoked. To enforce matching service layer beans, set the "dependencyCheck" property to true.
* <p>
* Autowiring will be attempted for all objects stored in flash, flow and conversation scope. Override the
* {@link #shouldBeAutowired(ScopeType, String, Object)} method if you want to limit autowiring to specific objects.
* <p>
* Autowiring is done using {@link AutowireCapableBeanFactory#autowireBeanProperties(Object, int, boolean)}. If you
* require special autowiring behaviour, consider overriding the {@link #autowire(ScopeType, String, Object)} method.
* <p>
* This phase listener will use the root web application context as {@link AutowireCapableBeanFactory}.
*
* @see FlowPhaseListener
* @see WebApplicationContextUtils#getWebApplicationContext(ServletContext)
*
* @author Erwin Vervaet
*/
public class AutowiringPhaseListener extends AutowiringSupport implements PhaseListener {
protected AutowireCapableBeanFactory getBeanFactory() {
return WebApplicationContextUtils.getWebApplicationContext(
(ServletContext) FacesContext.getCurrentInstance().getExternalContext().getContext())
.getAutowireCapableBeanFactory();
}
public PhaseId getPhaseId() {
return PhaseId.RESTORE_VIEW;
}
public void beforePhase(PhaseEvent event) {
FacesContext context = event.getFacesContext();
if (FlowExecutionHolderUtils.isFlowExecutionRestored(context)) {
autowire(FlowExecutionHolderUtils.getCurrentFlowExecution(context));
}
}
public void afterPhase(PhaseEvent event) {
// nothing to do after the phase
}
}

View File

@@ -1,17 +1,21 @@
package org.springframework.webflow.executor.support;
import java.util.Iterator;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.webflow.core.collection.AttributeMap;
import org.springframework.webflow.core.collection.MutableAttributeMap;
import org.springframework.webflow.definition.FlowDefinition;
import org.springframework.webflow.definition.StateDefinition;
import org.springframework.webflow.execution.EnterStateVetoException;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.FlowExecutionException;
import org.springframework.webflow.execution.FlowExecutionListener;
import org.springframework.webflow.execution.FlowExecutionListenerAdapter;
import org.springframework.webflow.execution.FlowSession;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.execution.ScopeType;
import org.springframework.webflow.execution.ViewSelection;
/**
* Flow execution listener that will autowire objects found in the flow execution scopes when a flow execution
@@ -25,12 +29,12 @@ import org.springframework.webflow.execution.ScopeType;
* time the flow execution resumes.
* <p>
* By default, the listener will use autowiring by name. This behaviour can be changed by setting the "autowireMode"
* property to the approriate value.
* property to the appropriate value.
* <p>
* Dependency checking is turned off by default: If no matching service layer bean can be found, the setter in question
* Dependency checking is turned off by default: if no matching service layer bean can be found, the setter in question
* will simply not get invoked. To enforce matching service layer beans, set the "dependencyCheck" property to true.
* <p>
* Autowiring will be attempted for all objects stored in any of the flow execution scopes. Override the
* Autowiring will be attempted for all objects stored in flash, flow and conversation scope. Override the
* {@link #shouldBeAutowired(ScopeType, String, Object)} method if you want to limit autowiring to specific objects.
* <p>
* Autowiring is done using {@link AutowireCapableBeanFactory#autowireBeanProperties(Object, int, boolean)}. If you
@@ -45,112 +49,61 @@ import org.springframework.webflow.execution.ScopeType;
*
* @author Erwin Vervaet
*/
public class AutowiringFlowExecutionListener extends FlowExecutionListenerAdapter implements ApplicationContextAware {
/**
* The autowiring mode to use.
*/
private int autowireMode = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
/**
* The dependency check setting to use.
*/
private boolean dependencyCheck = false;
public class AutowiringFlowExecutionListener extends AutowiringSupport implements ApplicationContextAware,
FlowExecutionListener {
/**
* The bean factory we will delegate autowiring to.
*/
private AutowireCapableBeanFactory beanFactory;
/**
* Returns the configured autowiring mode. The default is autowiring by name.
*/
public int getAutowireMode() {
return autowireMode;
}
/**
* Set the autowiring mode to use, either by name or by type. Defaults to autowiring by name.
* @see AutowireCapableBeanFactory#AUTOWIRE_BY_NAME
* @see AutowireCapableBeanFactory#AUTOWIRE_BY_TYPE
* @see AutowireCapableBeanFactory#AUTOWIRE_NO
*/
public void setAutowireMode(int autowireMode) {
this.autowireMode = autowireMode;
}
/**
* Returns the configured dependency check setting. Defaults to false.
*/
public boolean getDependencyCheck() {
return dependencyCheck;
}
/**
* Set whether to perform a dependency check for object references in the instance being autowired. Defaults to
* false.
* @see AutowireCapableBeanFactory#autowireBeanProperties(Object, int, boolean)
*/
public void setDependencyCheck(boolean dependencyCheck) {
this.dependencyCheck = dependencyCheck;
protected AutowireCapableBeanFactory getBeanFactory() {
return beanFactory;
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.beanFactory = applicationContext.getAutowireCapableBeanFactory();
}
// implementing FlowExecutionListener
public void requestSubmitted(RequestContext context) {
}
public void requestProcessed(RequestContext context) {
}
public void sessionStarting(RequestContext context, FlowDefinition definition, MutableAttributeMap input) {
}
public void sessionCreated(RequestContext context, FlowSession session) {
}
public void sessionStarted(RequestContext context, FlowSession session) {
}
public void eventSignaled(RequestContext context, Event event) {
}
public void stateEntering(RequestContext context, StateDefinition state) throws EnterStateVetoException {
}
public void stateEntered(RequestContext context, StateDefinition previousState, StateDefinition newState) {
}
public void resumed(RequestContext context) {
// autowire request scope, which is not tied to the flow session
autowire(ScopeType.REQUEST, context.getRequestScope());
// autowire flash and flow scope for all flow sessions in the flow execution
FlowSession flowSession = context.getFlowExecutionContext().getActiveSession();
while (flowSession != null) {
autowire(ScopeType.FLASH, flowSession.getFlashMap());
autowire(ScopeType.FLOW, flowSession.getScope());
flowSession = flowSession.getParent();
}
// autowire conversation scope, which is not tied to the flow session
autowire(ScopeType.CONVERSATION, context.getConversationScope());
autowire(context.getFlowExecutionContext());
}
/**
* Autowire all objects in given flow execution scope.
*/
private void autowire(ScopeType scopeType, MutableAttributeMap scope) {
for (Iterator keys = scope.asMap().keySet().iterator(); keys.hasNext();) {
String key = (String) keys.next();
Object value = scope.get(key);
if (shouldBeAutowired(scopeType, key, value)) {
autowire(scopeType, key, value);
}
}
public void paused(RequestContext context, ViewSelection selectedView) {
}
/**
* Determine whether or not given value, indexed using given key in specified flow execution scope, should be
* autowired. The default implementation simply returns true, i.e. it tries to autowire all objects found in the
* flow execution scopes.
* <p>
* Subclasses can override this method if they want to limit autowiring to a particular scope or to particular
* objects, for instance objects implementing a paritcular marker interface, having a certain marker annotation or
* objects whose class name follows some naming convention.
*/
protected boolean shouldBeAutowired(ScopeType scopeType, String key, Object value) {
return true;
public void sessionEnding(RequestContext context, FlowSession session, MutableAttributeMap output) {
}
/**
* Autowire given value indexed using given key in specified flow execution scope. By default, this method will
* simply call {@link AutowireCapableBeanFactory#autowireBeanProperties(Object, int, boolean)} using the configured
* {@link #setAutowireMode(int) autowiring mode} and {@link #setDependencyCheck(boolean) dependency check} settings.
* <p>
* Subclasses can override this method when special autowiring is required, for instance using any of the other
* method offered by {@link AutowireCapableBeanFactory}.
*/
protected void autowire(ScopeType scopeType, String key, Object value) {
beanFactory.autowireBeanProperties(value, autowireMode, dependencyCheck);
public void sessionEnded(RequestContext context, FlowSession session, AttributeMap output) {
}
public void exceptionThrown(RequestContext context, FlowExecutionException exception) {
}
}

View File

@@ -0,0 +1,135 @@
package org.springframework.webflow.executor.support;
import java.util.Iterator;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.webflow.core.collection.MutableAttributeMap;
import org.springframework.webflow.execution.FlowExecutionContext;
import org.springframework.webflow.execution.FlowSession;
import org.springframework.webflow.execution.ScopeType;
/**
* Abstract support class for classes that can autowire objects found in one of flow execution scopes.
* <p>
* Subclasses are required to implement the {@link #getBeanFactory()} hook method, returning the
* {@link AutowireCapableBeanFactory} to use for autowiring.
*
* @see AutowireCapableBeanFactory
*
* @since 1.0.6
*
* @author Erwin Vervaet
*/
public abstract class AutowiringSupport {
/**
* The autowiring mode to use.
*/
private int autowireMode = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
/**
* The dependency check setting to use.
*/
private boolean dependencyCheck = false;
/**
* Returns the configured autowiring mode. The default is autowiring by name.
*/
public int getAutowireMode() {
return autowireMode;
}
/**
* Set the autowiring mode to use, either by name or by type. Defaults to autowiring by name.
* @see AutowireCapableBeanFactory#AUTOWIRE_BY_NAME
* @see AutowireCapableBeanFactory#AUTOWIRE_BY_TYPE
* @see AutowireCapableBeanFactory#AUTOWIRE_NO
*/
public void setAutowireMode(int autowireMode) {
this.autowireMode = autowireMode;
}
/**
* Returns the configured dependency check setting. Defaults to false.
*/
public boolean getDependencyCheck() {
return dependencyCheck;
}
/**
* Set whether to perform a dependency check for object references in the instance being autowired. Defaults to
* false.
* @see AutowireCapableBeanFactory#autowireBeanProperties(Object, int, boolean)
*/
public void setDependencyCheck(boolean dependencyCheck) {
this.dependencyCheck = dependencyCheck;
}
/**
* Autowire all objects in flash, flow and conversation scope for given flow execution.
* @see #autowire(ScopeType, MutableAttributeMap)
*/
public void autowire(FlowExecutionContext flowExecutionContext) {
// no need to autowire request scope since that will be setup from scratch in this request
// autowire flash and flow scope for all flow sessions in the flow execution
FlowSession flowSession = flowExecutionContext.getActiveSession();
while (flowSession != null) {
autowire(ScopeType.FLASH, flowSession.getFlashMap());
autowire(ScopeType.FLOW, flowSession.getScope());
flowSession = flowSession.getParent();
}
// autowire conversation scope, which is not tied to the flow session
autowire(ScopeType.CONVERSATION, flowExecutionContext.getConversationScope());
}
/**
* Autowire all objects in given flow execution scope.
* <p>
* Will invoke {@link #shouldBeAutowired(ScopeType, String, Object)} to determine whether or not an object should be
* autowirded and {@link #autowire(ScopeType, String, Object)} to actually autowire it.
*/
protected void autowire(ScopeType scopeType, MutableAttributeMap scope) {
for (Iterator keys = scope.asMap().keySet().iterator(); keys.hasNext();) {
String key = (String) keys.next();
Object value = scope.get(key);
if (shouldBeAutowired(scopeType, key, value)) {
autowire(scopeType, key, value);
}
}
}
/**
* Determine whether or not given value, indexed using given key in specified flow execution scope, should be
* autowired. The default implementation simply returns true, i.e. it tries to autowire all objects found in the
* flow execution scopes.
* <p>
* Subclasses can override this method if they want to limit autowiring to a particular scope or to particular
* objects, for instance objects implementing a paritcular marker interface, having a certain marker annotation or
* objects whose class name follows some naming convention.
*/
protected boolean shouldBeAutowired(ScopeType scopeType, String key, Object value) {
return true;
}
/**
* Autowire given value indexed using given key in specified flow execution scope. By default, this method will
* simply call {@link AutowireCapableBeanFactory#autowireBeanProperties(Object, int, boolean)} using the configured
* {@link #setAutowireMode(int) autowiring mode} and {@link #setDependencyCheck(boolean) dependency check} settings.
* <p>
* Subclasses can override this method when special autowiring is required, for instance using any of the other
* method offered by {@link AutowireCapableBeanFactory}.
*/
protected void autowire(ScopeType scopeType, String key, Object value) {
getBeanFactory().autowireBeanProperties(value, autowireMode, dependencyCheck);
}
/**
* Returns the {@link AutowireCapableBeanFactory} that will be used for autowiring.
* <p>
* Subclasses are required to implement this hook method.
*/
protected abstract AutowireCapableBeanFactory getBeanFactory();
}