scope support

This commit is contained in:
Keith Donald
2007-10-30 18:19:23 +00:00
parent ee3158b388
commit c6a5f937c7
16 changed files with 409 additions and 1 deletions

View File

@@ -33,6 +33,8 @@
<dependency org="org.springframework" name="spring-web" rev="2.5-rc1" />
<!-- testing support only dependencies -->
<dependency org="aopalliance" name="aopalliance" rev="1.0" conf="test->default"/>
<dependency org="org.springframework" name="spring-aop" rev="2.5-rc1" conf="test->default" />
<dependency org="junit" name="junit" rev="3.8.2" conf="buildtime, testing->default" />
<!-- spring mvc only dependencies -->
@@ -45,7 +47,7 @@
<dependency org="struts" name="struts" rev="1.2.9" conf="buildtime, struts->default" />
<dependency org="org.springframework" name="spring-webmvc-struts" rev="2.5-rc1" conf="buildtime, struts->default" />
<!-- build time only dependencies -->
<!-- build time only dependencies -->
<dependency org="javax.servlet" name="servlet-api" rev="2.4" conf="buildtime->default" />
<dependency org="javax.portlet" name="portlet-api" rev="1.0" conf="buildtime->default" />
<dependency org="javax.el" name="el-api" rev="1.0" conf="buildtime->default" />

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2004-2007 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.config;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.webflow.config.scope.ScopeRegistrar;
import org.w3c.dom.Element;
/**
* {@link BeanDefinitionParser} for the <code>&lt;enable-flow-scopes&gt;</code> tag.
* @author Ben Hale
*/
class EnableFlowScopesBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
protected Class getBeanClass(Element element) {
return ScopeRegistrar.class;
}
protected boolean shouldGenerateId() {
return true;
}
}

View File

@@ -28,5 +28,6 @@ public class WebFlowConfigNamespaceHandler extends NamespaceHandlerSupport {
registerBeanDefinitionParser("flow-executor", new FlowExecutorBeanDefinitionParser());
registerBeanDefinitionParser("flow-execution-listeners", new FlowExecutionListenerLoaderBeanDefinitionParser());
registerBeanDefinitionParser("flow-registry", new FlowRegistryBeanDefinitionParser());
registerBeanDefinitionParser("enable-flow-scopes", new EnableFlowScopesBeanDefinitionParser());
}
}

View File

@@ -0,0 +1,78 @@
package org.springframework.webflow.config.scope;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
import org.springframework.webflow.core.collection.MutableAttributeMap;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.execution.RequestContextHolder;
/**
* Base class for {@link Scope} implementations that access a Web Flow scope from the current request.
*
* @author Keith Donald
*/
public abstract class AbstractWebFlowScope implements Scope {
/**
* Logger, usable by subclasses.
*/
protected final Log logger = LogFactory.getLog(getClass());
public Object get(String name, ObjectFactory objectFactory) {
MutableAttributeMap scope = getScope();
Object scopedObject = scope.get(name);
if (scopedObject == null) {
if (logger.isDebugEnabled()) {
logger.debug("No scoped instance '" + name + "' found; creating new instance");
}
scopedObject = objectFactory.getObject();
scope.put(name, scopedObject);
} else {
if (logger.isDebugEnabled()) {
logger.debug("Returning scoped instance '" + name + "'");
}
}
return scopedObject;
}
public Object remove(String name) {
return getScope().remove(name);
}
/**
* Template method that returns the target scope map.
* @throws IllegalStateException if the scope could not be accessed
*/
protected abstract MutableAttributeMap getScope() throws IllegalStateException;
/**
* Always returns <code>null</code> as most Spring Web Flow scopes do not have obvious conversation ids.
* Subclasses should override this method where conversation ids can be intelligently returned.
* @return always returns <code>null</code>
*/
public String getConversationId() {
return null;
}
/**
* Will not register a destruction callback as Spring Web Flow does not support destruction of scoped beans.
* Subclasses should override this method where where destruction can adequately be accomplished.
* @param name the name of the bean to register the callback for
* @param callback the callback to execute
*/
public void registerDestructionCallback(String name, Runnable callback) {
logger.warn("Destruction callback for '" + name + "' was not registered. Spring Web Flow does not "
+ "support destruction of scoped beans.");
}
protected RequestContext getRequiredRequestContext() {
RequestContext context = RequestContextHolder.getRequestContext();
if (context == null) {
throw new IllegalStateException(
"No request context bound to this thread; to access flow-scoped beans you must be running in a flow execution request");
}
return context;
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2004-2007 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.config.scope;
import org.springframework.beans.factory.config.Scope;
import org.springframework.webflow.core.collection.MutableAttributeMap;
/**
* Conversation {@link Scope scope} implementation.
* @author Ben Hale
*/
class ConversationScope extends AbstractWebFlowScope {
protected MutableAttributeMap getScope() {
return getRequiredRequestContext().getConversationScope();
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2004-2007 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.config.scope;
import org.springframework.beans.factory.config.Scope;
import org.springframework.webflow.core.collection.MutableAttributeMap;
/**
* Flash {@link Scope scope} implementation.
* @author Ben Hale
*/
class FlashScope extends AbstractWebFlowScope {
protected MutableAttributeMap getScope() {
return getRequiredRequestContext().getFlashScope();
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2004-2007 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.config.scope;
import org.springframework.beans.factory.config.Scope;
import org.springframework.webflow.core.collection.MutableAttributeMap;
/**
* Flow {@link Scope scope} implementation.
* @author Ben Hale
*/
class FlowScope extends AbstractWebFlowScope {
protected MutableAttributeMap getScope() {
return getRequiredRequestContext().getFlowScope();
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2004-2007 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.config.scope;
import org.springframework.beans.factory.config.Scope;
import org.springframework.webflow.core.collection.MutableAttributeMap;
/**
* Request {@link Scope scope} implementation.
* @author Ben Hale
*/
class RequestScope extends AbstractWebFlowScope {
protected MutableAttributeMap getScope() {
return getRequiredRequestContext().getRequestScope();
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2004-2007 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.config.scope;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.Scope;
import org.springframework.core.Ordered;
import org.springframework.webflow.execution.ScopeType;
/**
* Registers the Spring Web Flow bean scopes with a
* @{link ConfigurableListableBeanFactory}.
*
* @author Ben Hale
* @see Scope
*/
public class ScopeRegistrar implements BeanFactoryPostProcessor, Ordered {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.registerScope(ScopeType.REQUEST.getLabel().toLowerCase(), new RequestScope());
beanFactory.registerScope(ScopeType.FLASH.getLabel().toLowerCase(), new FlashScope());
beanFactory.registerScope(ScopeType.FLOW.getLabel().toLowerCase(), new FlowScope());
beanFactory.registerScope(ScopeType.CONVERSATION.getLabel().toLowerCase(), new ConversationScope());
}
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}

View File

@@ -0,0 +1,5 @@
<html>
<body>
Support code to allow access to the Spring Web Flow scopes (request, flash, flow conversation) from a Spring ApplicationContext.
</body>
</html>

View File

@@ -208,6 +208,16 @@ The idref to the registry this executor will use to locate flow definitions for
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="enable-flow-scopes">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Allows access to the Spring Web Flow scopes (request, flash, flow, conversation) from a Spring ApplicationContext.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:complexType name="flowExecutionRepositoryType">

View File

@@ -0,0 +1,36 @@
package org.springframework.webflow.config;
import junit.framework.TestCase;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.webflow.core.collection.AttributeMap;
import org.springframework.webflow.execution.FlowExecutionListenerAdapter;
import org.springframework.webflow.execution.FlowSession;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.executor.FlowExecutor;
import org.springframework.webflow.test.MockExternalContext;
public class EnableScopesBeanDefinitionParserTests extends TestCase {
private ClassPathXmlApplicationContext context;
private FlowExecutor executor;
public void setUp() {
context = new ClassPathXmlApplicationContext("org/springframework/webflow/config/enable-flow-scopes.xml");
executor = (FlowExecutor) context.getBean("flowExecutor");
}
public void testExecute() {
MockExternalContext context = new MockExternalContext();
context.setFlowId("flow");
executor.execute(context);
}
public static class ConfigurationListener extends FlowExecutionListenerAdapter {
public void sessionEnded(RequestContext context, FlowSession session, AttributeMap output) {
assertNotNull(session.getScope().get("user"));
}
}
}

View File

@@ -0,0 +1,15 @@
package org.springframework.webflow.config;
import org.springframework.util.Assert;
public class EnableScopesService {
private EnableScopesUser user;
public void setUser(EnableScopesUser user) {
this.user = user;
}
public void execute() {
Assert.isTrue("foo".equals(user.getName()));
}
}

View File

@@ -0,0 +1,9 @@
package org.springframework.webflow.config;
public class EnableScopesUser {
private String name = "foo";
public String getName() {
return name;
}
}

View File

@@ -0,0 +1,14 @@
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
<start-state idref="end"/>
<end-state id="end">
<entry-actions>
<bean-action bean="service" method="execute"/>
</entry-actions>
</end-state>
</flow>

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:web="http://www.springframework.org/schema/webflow-config"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/webflow-config
http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd">
<web:enable-flow-scopes/>
<web:flow-executor id="flowExecutor" flow-registry="flowRegistry">
<web:flow-execution-repository type="continuation" max-conversations="1" max-continuations="2"/>
<web:flow-execution-attributes>
<web:alwaysRedirectOnPause value="false"/>
<web:attribute name="foo" value="bar"/>
<web:attribute name="bar" value="2" type="integer"/>
</web:flow-execution-attributes>
<web:flow-execution-listeners>
<web:listener ref="listener" criteria="*"/>
</web:flow-execution-listeners>
</web:flow-executor>
<bean id="listener" class="org.springframework.webflow.config.EnableScopesBeanDefinitionParserTests$ConfigurationListener" />
<web:flow-registry id="flowRegistry">
<web:flow-location path="org/springframework/webflow/config/enable-flow-scopes-flowdef.xml" />
</web:flow-registry>
<bean id="user" class="org.springframework.webflow.config.EnableScopesUser" scope="flow">
<aop:scoped-proxy/>
</bean>
<bean id="service" class="org.springframework.webflow.config.EnableScopesService">
<property name="user" ref="user"/>
</bean>
</beans>