This commit is contained in:
Keith Donald
2008-04-04 22:14:06 +00:00
parent ec2c4ba105
commit ee597b7995
63 changed files with 866 additions and 542 deletions

View File

@@ -23,6 +23,8 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.style.ToStringCreator;
@@ -36,6 +38,8 @@ import org.springframework.util.CachingMapDecorator;
*/
class DefaultMessageContext implements StateManageableMessageContext {
private static final Log logger = LogFactory.getLog(DefaultMessageContext.class);
private MessageSource messageSource;
private Map objectMessages = new CachingMapDecorator() {
@@ -95,8 +99,14 @@ class DefaultMessageContext implements StateManageableMessageContext {
public void addMessage(MessageResolver messageResolver) {
Locale currentLocale = LocaleContextHolder.getLocale();
if (logger.isDebugEnabled()) {
logger.debug("Resolving message using " + messageResolver);
}
Message message = messageResolver.resolveMessage(messageSource, currentLocale);
List messages = (List) objectMessages.get(message.getSource());
if (logger.isDebugEnabled()) {
logger.debug("Adding resolved message " + message);
}
messages.add(message);
}

View File

@@ -1,8 +1,10 @@
package org.springframework.binding.message;
import java.text.MessageFormat;
import java.util.Locale;
import org.springframework.context.MessageSource;
import org.springframework.util.Assert;
import org.springframework.context.support.AbstractMessageSource;
/**
* Default message context factory that simply stores messages indexed in a map by their source. Suitable for use in
@@ -20,11 +22,19 @@ public class DefaultMessageContextFactory implements MessageContextFactory {
* @param messageSource
*/
public DefaultMessageContextFactory(MessageSource messageSource) {
Assert.notNull(messageSource, "The message source is required");
if (messageSource == null) {
messageSource = new DefaultTextFallbackMessageSource();
}
this.messageSource = messageSource;
}
public StateManageableMessageContext createMessageContext() {
return new DefaultMessageContext(messageSource);
}
private class DefaultTextFallbackMessageSource extends AbstractMessageSource {
protected MessageFormat resolveCode(String code, Locale locale) {
return null;
}
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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.binding.message;
import java.util.Locale;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.core.style.ToStringCreator;
class DefaultMessageResolver implements MessageResolver, MessageSourceResolvable {
private Object source;
private String[] codes;
private Severity severity;
private Object[] args;
private String defaultText;
public DefaultMessageResolver(Object source, String[] codes, Severity severity, Object[] args,
String defaultText) {
this.source = source;
this.codes = codes;
this.severity = severity;
this.args = args;
this.defaultText = defaultText;
}
public Message resolveMessage(MessageSource messageSource, Locale locale) {
return new Message(source, messageSource.getMessage(this, locale), severity);
}
// implementing MessageSourceResolver
public String[] getCodes() {
return codes;
}
public Object[] getArguments() {
return args;
}
public String getDefaultMessage() {
return defaultText;
}
public String toString() {
return new ToStringCreator(this).append("source", source).append("severity", severity).append("codes",
codes).append("args", args).append("defaultText", defaultText).toString();
}
}

View File

@@ -4,11 +4,11 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.core.style.ToStringCreator;
/**
* A convenient builder for building {@link MessageResolver} objects programmatically. Often used by model code such as
@@ -170,41 +170,7 @@ public class MessageBuilder {
}
String[] codesArray = (String[]) codes.toArray(new String[codes.size()]);
Object[] argsArray = args.toArray(new Object[args.size()]);
return new BuiltMessageResolver(source, codesArray, severity, argsArray, defaultText);
}
private static class BuiltMessageResolver implements MessageResolver, MessageSourceResolvable {
private Object source;
private String[] codes;
private Severity severity;
private Object[] args;
private String defaultText;
public BuiltMessageResolver(Object source, String[] codes, Severity severity, Object[] args, String defaultText) {
this.source = source;
this.codes = codes;
this.severity = severity;
this.args = args;
this.defaultText = defaultText;
}
public Message resolveMessage(MessageSource messageSource, Locale locale) {
return new Message(source, messageSource.getMessage(this, locale), severity);
}
// implementing MessageSourceResolver
public String[] getCodes() {
return codes;
}
public Object[] getArguments() {
return args;
}
public String getDefaultMessage() {
return defaultText;
}
return new DefaultMessageResolver(source, codesArray, severity, argsArray, defaultText);
}
private static class ResolvableArgument implements MessageSourceResolvable {
@@ -227,6 +193,10 @@ public class MessageBuilder {
return arg.toString();
}
public String toString() {
return new ToStringCreator(this).append("arg", arg).toString();
}
}
}

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- Appenders -->
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p: %c - %m%n" />
</layout>
</appender>
<logger name="org.springframework.beans">
<level value="warn" />
</logger>
<logger name="org.springframework.binding">
<level value="debug" />
</logger>
<!-- Root Logger -->
<root>
<priority value="warn" />
<appender-ref ref="console" />
</root>
</log4j:configuration>

View File

@@ -32,6 +32,14 @@ public class ManySelectionTrackingListDataModel extends SerializableListDataMode
private List selections = new ArrayList();
public ManySelectionTrackingListDataModel() {
super();
}
public ManySelectionTrackingListDataModel(List list) {
super(list);
}
public List getSelections() {
return selections;
}

View File

@@ -31,6 +31,14 @@ public class OneSelectionTrackingListDataModel extends SerializableListDataModel
private List selections = new ArrayList();
public OneSelectionTrackingListDataModel() {
super();
}
public OneSelectionTrackingListDataModel(List list) {
super(list);
}
public List getSelections() {
return selections;
}

View File

@@ -31,8 +31,9 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.binding.expression.Expression;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.ContextResource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.faces.ui.AjaxViewRoot;
import org.springframework.webflow.context.ExternalContext;
import org.springframework.webflow.execution.RequestContext;
@@ -56,13 +57,10 @@ public class JsfViewFactory implements ViewFactory {
private final Expression viewIdExpression;
private final ResourceLoader resourceLoader;
private final Lifecycle lifecycle;
public JsfViewFactory(Expression viewIdExpression, ResourceLoader resourceLoader, Lifecycle lifecycle) {
public JsfViewFactory(Expression viewIdExpression, Lifecycle lifecycle) {
this.viewIdExpression = viewIdExpression;
this.resourceLoader = resourceLoader;
this.lifecycle = lifecycle;
}
@@ -141,8 +139,16 @@ public class JsfViewFactory implements ViewFactory {
if (viewId.startsWith("/")) {
return viewId;
} else {
ContextResource viewResource = (ContextResource) resourceLoader.getResource(viewId);
return viewResource.getPathWithinContext();
ApplicationContext flowContext = context.getActiveFlow().getApplicationContext();
if (flowContext == null) {
throw new IllegalStateException("A Flow ApplicationContext is required to resolve Flow View Resources");
}
Resource viewResource = flowContext.getResource(viewId);
if (!(viewResource instanceof ContextResource)) {
throw new IllegalStateException(
"A ContextResource is required to get relative view paths within this context");
}
return ((ContextResource) viewResource).getPathWithinContext();
}
}

View File

@@ -20,7 +20,6 @@ import javax.faces.lifecycle.Lifecycle;
import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ExpressionParser;
import org.springframework.binding.format.FormatterRegistry;
import org.springframework.core.io.ResourceLoader;
import org.springframework.webflow.engine.builder.ViewFactoryCreator;
import org.springframework.webflow.execution.ViewFactory;
@@ -36,8 +35,8 @@ public class JsfViewFactoryCreator implements ViewFactoryCreator {
private Lifecycle lifecycle;
public ViewFactory createViewFactory(Expression viewIdExpression, ExpressionParser expressionParser,
FormatterRegistry formatterRegistry, ResourceLoader resourceLoader) {
return new JsfViewFactory(viewIdExpression, resourceLoader, getLifecycle());
FormatterRegistry formatterRegistry) {
return new JsfViewFactory(viewIdExpression, getLifecycle());
}
public String getViewIdByConvention(String viewStateId) {

View File

@@ -70,8 +70,8 @@ public class JsfFinalResponseActionTests extends TestCase {
jsfMock.facesContext().setViewRoot(null);
jsfMock.facesContext().getApplication().setViewHandler(viewHandler);
lifecycle = new TestLifecycle(jsfMock.lifecycle());
factory = new JsfViewFactory(parser.parseExpression("#{'" + VIEW_ID + "'}", new FluentParserContext().template()
.evaluate(RequestContext.class).expectResult(String.class)), null, lifecycle);
factory = new JsfViewFactory(parser.parseExpression("#{'" + VIEW_ID + "'}", new FluentParserContext()
.template().evaluate(RequestContext.class).expectResult(String.class)), lifecycle);
RequestContextHolder.setRequestContext(context);
MockExternalContext ext = new MockExternalContext();
ext.setNativeContext(new MockServletContext());

View File

@@ -94,7 +94,7 @@ public class JsfViewFactoryTests extends TestCase {
lifecycle = new NoExecutionLifecycle(jsfMock.lifecycle());
factory = new JsfViewFactory(parser.parseExpression(VIEW_ID, new FluentParserContext().template().evaluate(
RequestContext.class).expectResult(String.class)), null, lifecycle);
RequestContext.class).expectResult(String.class)), lifecycle);
UIViewRoot newRoot = new UIViewRoot();
newRoot.setViewId(VIEW_ID);
@@ -117,7 +117,7 @@ public class JsfViewFactoryTests extends TestCase {
lifecycle = new NoExecutionLifecycle(jsfMock.lifecycle());
factory = new JsfViewFactory(parser.parseExpression(VIEW_ID, new FluentParserContext().template().evaluate(
RequestContext.class).expectResult(String.class)), null, lifecycle);
RequestContext.class).expectResult(String.class)), lifecycle);
UIViewRoot existingRoot = new UIViewRoot();
existingRoot.setViewId(VIEW_ID);
@@ -141,7 +141,7 @@ public class JsfViewFactoryTests extends TestCase {
lifecycle = new NoExecutionLifecycle(jsfMock.lifecycle());
factory = new JsfViewFactory(parser.parseExpression(VIEW_ID, new FluentParserContext().template().evaluate(
RequestContext.class).expectResult(String.class)), null, lifecycle);
RequestContext.class).expectResult(String.class)), lifecycle);
UIViewRoot existingRoot = new UIViewRoot();
existingRoot.setViewId(VIEW_ID);
@@ -169,7 +169,7 @@ public class JsfViewFactoryTests extends TestCase {
lifecycle = new NoExecutionLifecycle(jsfMock.lifecycle());
factory = new JsfViewFactory(parser.parseExpression(VIEW_ID, new FluentParserContext().template().evaluate(
RequestContext.class).expectResult(String.class)), null, lifecycle);
RequestContext.class).expectResult(String.class)), lifecycle);
UIViewRoot newRoot = new UIViewRoot();
newRoot.setViewId(VIEW_ID);

View File

@@ -7,5 +7,6 @@
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.web.container"/>
<classpathentry kind="var" path="IVY_CACHE/org.easymock/easymock/easymock-2.3.jar" sourcepath="/IVY_CACHE/org.easymock/easymock/easymock-sources-2.3.jar"/>
<classpathentry kind="var" path="IVY_CACHE/javax.el/el-api/el-api-1.0.jar" sourcepath="IVVY_CACHE/javax.el/el-api/el-api-sources-1.0.jar"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -6,9 +6,15 @@ import java.util.List;
import javax.faces.model.DataModel;
import org.easymock.EasyMock;
import org.springframework.binding.mapping.Mapper;
import org.springframework.binding.mapping.MappingResults;
import org.springframework.faces.model.OneSelectionTrackingListDataModel;
import org.springframework.faces.model.converter.FacesConversionService;
import org.springframework.webflow.config.FlowDefinitionResource;
import org.springframework.webflow.config.FlowDefinitionResourceFactory;
import org.springframework.webflow.core.collection.AttributeMap;
import org.springframework.webflow.engine.EndState;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.test.MockExternalContext;
import org.springframework.webflow.test.MockFlowBuilderContext;
import org.springframework.webflow.test.execution.AbstractXmlFlowExecutionTests;
@@ -32,7 +38,7 @@ public class MainFlowExecutionTests extends AbstractXmlFlowExecutionTests {
builderContext.getFlowBuilderServices().setConversionService(new FacesConversionService());
}
public void testStart() {
public void testStartMainFlow() {
List<Booking> bookings = new ArrayList<Booking>();
bookings.add(new Booking(new Hotel(), new User("keith", "password", "Keith Donald")));
EasyMock.expect(bookingService.findBookings("keith")).andReturn(bookings);
@@ -42,31 +48,80 @@ public class MainFlowExecutionTests extends AbstractXmlFlowExecutionTests {
context.setCurrentUser("keith");
startFlow(context);
assertCurrentStateEquals("enterSearchCriteria");
assertResponseWrittenEquals("enterSearchCriteria", context);
assertTrue(getRequiredFlowAttribute("searchCriteria") instanceof SearchCriteria);
assertTrue(getRequiredViewAttribute("bookings") instanceof DataModel);
EasyMock.verify(bookingService);
}
// public void testSearch() {
// setCurrentState("enterSearchCriteria");
// SearchCriteria criteria = new SearchCriteria();
// criteria.setSearchString("Jameson");
// getFlowScope().put("searchCriteria", criteria);
//
// List<Hotel> hotels = new ArrayList<Hotel>();
// hotels.add(new Hotel());
// EasyMock.expect(bookingService.findHotels(criteria)).andReturn(hotels);
// EasyMock.replay(bookingService);
//
// MockExternalContext context = new MockExternalContext();
// context.setEventId("search");
// resumeFlow(context);
//
// EasyMock.verify(bookingService);
//
// assertCurrentStateEquals("reviewHotels");
// assertTrue(getRequiredViewAttribute("hotels") instanceof DataModel);
// }
public void testSearchHotels() {
setCurrentState("enterSearchCriteria");
SearchCriteria criteria = new SearchCriteria();
criteria.setSearchString("Jameson");
getFlowScope().put("searchCriteria", criteria);
List<Hotel> hotels = new ArrayList<Hotel>();
hotels.add(new Hotel());
EasyMock.expect(bookingService.findHotels(criteria)).andReturn(hotels);
EasyMock.replay(bookingService);
MockExternalContext context = new MockExternalContext();
context.setEventId("search");
resumeFlow(context);
EasyMock.verify(bookingService);
assertCurrentStateEquals("reviewHotels");
assertResponseWrittenEquals("reviewHotels", context);
assertTrue(getRequiredViewAttribute("hotels") instanceof DataModel);
}
public void testSelectHotel() {
setCurrentState("reviewHotels");
List<Hotel> hotels = new ArrayList<Hotel>();
Hotel hotel = new Hotel();
hotel.setId(1L);
hotel.setName("Jameson Inn");
hotels.add(hotel);
OneSelectionTrackingListDataModel dataModel = new OneSelectionTrackingListDataModel(hotels);
dataModel.select(hotel);
getViewScope().put("hotels", dataModel);
MockExternalContext context = new MockExternalContext();
context.setEventId("select");
resumeFlow(context);
assertCurrentStateEquals("reviewHotel");
assertNull(getFlowAttribute("hotels"));
assertSame(hotel, getFlowAttribute("hotel"));
}
public void testBookHotel() {
setCurrentState("reviewHotel");
Hotel hotel = new Hotel();
hotel.setId(1L);
hotel.setName("Jameson Inn");
getFlowScope().put("hotel", hotel);
Flow mockBookingFlow = new Flow("booking");
mockBookingFlow.setInputMapper(new Mapper() {
public MappingResults map(Object source, Object target) {
assertEquals(new Long(1), ((AttributeMap) source).get("hotelId"));
return null;
}
});
new EndState(mockBookingFlow, "bookingConfirmed");
getFlowDefinitionRegistry().registerFlowDefinition(mockBookingFlow);
MockExternalContext context = new MockExternalContext();
context.setEventId("book");
resumeFlow(context);
assertFlowExecutionEnded();
assertFlowExecutionOutcomeEquals("finish");
}
}

View File

@@ -45,7 +45,6 @@
<label for="checkinDate">Check In Date:</label>
</div>
<div class="input">
<form:errors path="checkinDate" cssClass="errors"/>
<form:input path="checkinDate"/>
</div>
</div>
@@ -54,7 +53,6 @@
<label for="checkoutDate">Check Out Date:</label>
</div>
<div class="input">
<form:errors path="checkoutDate" cssClass="errors"/>
<form:input path="checkoutDate"/>
</div>
</div>
@@ -84,7 +82,6 @@
<label for="creditCard">Credit Card #:</label>
</div>
<div class="input">
<form:errors path="creditCard" cssClass="errors"/>
<form:input id="creditCard" path="creditCard"/>
</div>
</div>
@@ -93,7 +90,6 @@
<label for="creditCardName">Credit Card Name:</label>
</div>
<div class="input">
<form:errors path="creditCardName" cssClass="errors"/>
<form:input id="creditCardName" path="creditCardName"/>
</div>
</div>

View File

@@ -0,0 +1,2 @@
booking.checkinDate.typeMismatch=The Check In Date must be in the format dd/mm/yy
booking.checkoutDate.typeMismatch=The Check Out Date must be in the format dd/mm/yy

View File

@@ -1,7 +1,7 @@
<?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:web="http://www.springframework.org/schema/webflow-config"
xmlns:webflow="http://www.springframework.org/schema/webflow-config"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="
@@ -48,18 +48,18 @@
</bean>
<!-- Executes flows: the central entry point into the Spring Web Flow system -->
<web:flow-executor id="flowExecutor" flow-registry="flowRegistry">
<web:flow-execution-listeners>
<web:listener ref="jpaFlowExecutionListener" criteria="*" />
<web:listener ref="securityFlowExecutionListener" criteria="*" />
</web:flow-execution-listeners>
</web:flow-executor>
<webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry">
<webflow:flow-execution-listeners>
<webflow:listener ref="jpaFlowExecutionListener" criteria="*" />
<webflow:listener ref="securityFlowExecutionListener" criteria="*" />
</webflow:flow-execution-listeners>
</webflow:flow-executor>
<!-- The registry of executable flow definitions -->
<web:flow-registry id="flowRegistry">
<web:flow-location path="WEB-INF/hotels/booking/booking.xml" />
</web:flow-registry>
<webflow:flow-registry id="flowRegistry">
<webflow:flow-location path="/WEB-INF/hotels/booking/booking.xml" />
</webflow:flow-registry>
<!-- Installs a listener that manages JPA persistence contexts for flows that require them -->
<bean id="jpaFlowExecutionListener" class="org.springframework.webflow.persistence.JpaFlowExecutionListener">
<constructor-arg ref="entityManagerFactory" />

View File

@@ -1,9 +0,0 @@
log4j.rootCategory=WARN, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
# Enable web flow logging
log4j.category.org.springframework.webflow=DEBUG
log4j.category.org.springframework.binding=DEBUG

View File

@@ -36,15 +36,10 @@ public class FlowExecutionRepositoryType extends StaticLabeledEnum {
*/
public static final FlowExecutionRepositoryType CONTINUATION = new FlowExecutionRepositoryType(1, "Continuation");
/**
* The 'client' (continuation) flow execution repository type.
*/
public static final FlowExecutionRepositoryType CLIENT = new FlowExecutionRepositoryType(2, "Client");
/**
* The 'singleKey' flow execution repository type.
*/
public static final FlowExecutionRepositoryType SINGLEKEY = new FlowExecutionRepositoryType(3, "Single Key");
public static final FlowExecutionRepositoryType SINGLEKEY = new FlowExecutionRepositoryType(2, "Single Key");
/**
* Private constructor because this is a typesafe enum!

View File

@@ -261,36 +261,29 @@ class FlowExecutorFactoryBean implements FactoryBean, InitializingBean {
*/
protected FlowExecutionRepository createFlowExecutionRepository(FlowExecutionRepositoryType repositoryType,
FlowExecutionStateRestorer executionStateRestorer, ConversationManager conversationManager) {
if (repositoryType == FlowExecutionRepositoryType.CLIENT) {
throw new UnsupportedOperationException(
"The 'client' flow execution repository is not supported in this 2.0 Milestone; support is planned for a future release");
if (conversationManager == null) {
conversationManager = createDefaultConversationManager();
}
if (repositoryType == FlowExecutionRepositoryType.CONTINUATION) {
DefaultFlowExecutionRepository repository = new DefaultFlowExecutionRepository(conversationManager,
executionStateRestorer);
if (maxContinuations != null) {
repository.setMaxContinuations(maxContinuations.intValue());
}
return repository;
} else if (repositoryType == FlowExecutionRepositoryType.SIMPLE) {
DefaultFlowExecutionRepository repository = new DefaultFlowExecutionRepository(conversationManager,
executionStateRestorer);
repository.setMaxContinuations(1);
return repository;
} else if (repositoryType == FlowExecutionRepositoryType.SINGLEKEY) {
DefaultFlowExecutionRepository repository = new DefaultFlowExecutionRepository(conversationManager,
executionStateRestorer);
repository.setAlwaysGenerateNewNextKey(false);
return repository;
} else {
// determine the conversation manager to use
ConversationManager conversationManagerToUse = conversationManager;
if (conversationManagerToUse == null) {
conversationManagerToUse = createDefaultConversationManager();
}
if (repositoryType == FlowExecutionRepositoryType.SIMPLE) {
DefaultFlowExecutionRepository repository = new DefaultFlowExecutionRepository(
conversationManagerToUse, executionStateRestorer);
repository.setMaxContinuations(1);
return repository;
} else if (repositoryType == FlowExecutionRepositoryType.CONTINUATION) {
DefaultFlowExecutionRepository repository = new DefaultFlowExecutionRepository(
conversationManagerToUse, executionStateRestorer);
if (maxContinuations != null) {
repository.setMaxContinuations(maxContinuations.intValue());
}
return repository;
} else if (repositoryType == FlowExecutionRepositoryType.SINGLEKEY) {
DefaultFlowExecutionRepository repository = new DefaultFlowExecutionRepository(
conversationManagerToUse, executionStateRestorer);
repository.setAlwaysGenerateNewNextKey(false);
return repository;
} else {
throw new IllegalStateException("Cannot create execution repository - unsupported repository type "
+ repositoryType);
}
throw new IllegalStateException("Cannot create execution repository - unsupported repository type "
+ repositoryType);
}
}

View File

@@ -313,16 +313,6 @@ of the browser back button. This is the default if not specified.
<xsd:documentation>
<![CDATA[
The simple repository type. Use of this strategy incurs minimal storage overhead but explicitly prevents resubmits using the browser back button.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="client">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
The client continuation repository type.
Use this to snapshot flow execution state client-side to support use of the browser back button in a stateless server environment.
]]>
</xsd:documentation>
</xsd:annotation>

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.webflow.definition.registry;
import org.springframework.webflow.definition.FlowDefinition;
/**
* A container of flow definitions. Extends {@link FlowDefinitionLocator} for accessing registered Flow definitions for
* execution at runtime.
@@ -41,4 +43,10 @@ public interface FlowDefinitionRegistry extends FlowDefinitionLocator {
*/
public void registerFlowDefinition(FlowDefinitionHolder definitionHolder);
/**
* Register a flow definition in this registry.
* @param definition the actual flow definition
*/
public void registerFlowDefinition(FlowDefinition definition);
}

View File

@@ -33,12 +33,6 @@ public class FlowVariable extends AnnotatedObject {
*/
private String name;
/**
* Is this flow variable local or global? Local variables go into flow scope. Global variables go into conversation
* scope.
*/
private Boolean local;
/**
* The value factory that provides this variable's value.
*/
@@ -47,14 +41,12 @@ public class FlowVariable extends AnnotatedObject {
/**
* Creates a new flow variable.
* @param name the variable name
* @param local the local variable
*/
public FlowVariable(String name, VariableValueFactory valueFactory, boolean local) {
public FlowVariable(String name, VariableValueFactory valueFactory) {
Assert.hasText(name, "The variable name is required");
Assert.notNull(valueFactory, "The variable value factory is required");
this.name = name;
this.valueFactory = valueFactory;
this.local = Boolean.valueOf(local);
}
/**
@@ -64,13 +56,6 @@ public class FlowVariable extends AnnotatedObject {
return name;
}
/**
* Is this a local flow variable or a conversation-scoped flow variable?
*/
public boolean isLocal() {
return local.booleanValue();
}
// name and scope based equality
public boolean equals(Object o) {
@@ -78,11 +63,11 @@ public class FlowVariable extends AnnotatedObject {
return false;
}
FlowVariable other = (FlowVariable) o;
return name.equals(other.name) && valueFactory.equals(other.valueFactory) && local.equals(other.local);
return name.equals(other.name) && valueFactory.equals(other.valueFactory);
}
public int hashCode() {
return name.hashCode() + valueFactory.hashCode() + local.hashCode();
return name.hashCode() + valueFactory.hashCode();
}
/**
@@ -91,11 +76,7 @@ public class FlowVariable extends AnnotatedObject {
*/
public void create(RequestContext context) {
Object value = valueFactory.createInitialValue(context);
if (local == Boolean.TRUE) {
context.getFlowScope().put(name, value);
} else {
context.getConversationScope().put(name, value);
}
context.getFlowScope().put(name, value);
}
/**
@@ -104,12 +85,7 @@ public class FlowVariable extends AnnotatedObject {
* @param context the executing flow
*/
public void restore(RequestContext context) {
Object value;
if (local == Boolean.TRUE) {
value = context.getFlowScope().get(name);
} else {
value = context.getConversationScope().get(name);
}
Object value = context.getFlowScope().get(name);
valueFactory.restoreReferences(value, context);
}
@@ -118,15 +94,10 @@ public class FlowVariable extends AnnotatedObject {
* @param context the executing flow
*/
public Object destroy(RequestContext context) {
if (local == Boolean.TRUE) {
return context.getFlowScope().remove(name);
} else {
return context.getConversationScope().remove(name);
}
return context.getFlowScope().remove(name);
}
public String toString() {
return new ToStringCreator(this).append("name", name).append("valueFactory", valueFactory).append("local",
local).toString();
return new ToStringCreator(this).append("name", name).append("valueFactory", valueFactory).toString();
}
}

View File

@@ -0,0 +1,27 @@
package org.springframework.webflow.engine;
import org.springframework.core.enums.StaticLabeledEnum;
public class History extends StaticLabeledEnum {
/**
* The history of the view state should be preserved when the view state exits to support back-tracking.
*/
public static final History PRESERVE = new History(0, "preserve");
/**
* The history of the view state should be discarded when the view state exits to prevent back-tracking.
*/
public static final History DISCARD = new History(1, "discard");
/**
* The history of the view state and all previous view state should be invalidated to completely restrict back
* tracking.
*/
public static final History INVALIDATE = new History(2, "invalidate");
public History(int code, String label) {
super(code, label);
}
}

View File

@@ -53,6 +53,47 @@ public interface RequestControlContext extends RequestContext {
*/
public void setCurrentState(State state);
/**
* Assign the ongoing flow execution its flow execution key. This method will be called before a state is about to
* render a view and pause the flow execution.
*/
public FlowExecutionKey assignFlowExecutionKey();
/**
* Update the current flow execution snapshot to save the current state.
*/
public void updateCurrentFlowExecutionSnapshot();
/**
* Remove the current flow execution snapshot to invalidate the current state.
*/
public void removeCurrentFlowExecutionSnapshot();
/**
* Remove all flow execution snapshots associated with the ongoing conversation. Invalidates previous states.
*/
public void removeAllFlowExecutionSnapshots();
/**
* Signals the occurrence of an event in the current state of this flow execution request context. This method
* should be called by clients that report internal event occurrences, such as action states. The
* <code>onEvent()</code> method of the flow involved in the flow execution will be called.
* @param event the event that occurred
* @return a boolean indicating if handling this event caused the current state to exit and a new state to enter
* @throws FlowExecutionException if an exception was thrown within a state of the flow during execution of this
* signalEvent operation
* @see Flow#handleEvent(RequestControlContext)
*/
public boolean handleEvent(Event event) throws FlowExecutionException;
/**
* Execute this transition out of the current source state. Allows for privileged execution of an arbitrary
* transition.
* @param transition the transition
* @see Transition#execute(State, RequestControlContext)
*/
public boolean execute(Transition transition);
/**
* Record the transition executing in the flow. This method will be called as part of executing a transition from
* one state to another.
@@ -61,12 +102,6 @@ public interface RequestControlContext extends RequestContext {
*/
public void setCurrentTransition(Transition transition);
/**
* Assign the ongoing flow execution its flow execution key. This method will be called before a state is about to
* render a view and pause the flow execution.
*/
public FlowExecutionKey assignFlowExecutionKey();
/**
* Spawn a new flow session and activate it in the currently executing flow. Also transitions the spawned flow to
* its start state. This method should be called by clients that wish to spawn new flows, such as subflow states.
@@ -80,18 +115,6 @@ public interface RequestControlContext extends RequestContext {
*/
public void start(Flow flow, MutableAttributeMap input) throws FlowExecutionException;
/**
* Signals the occurrence of an event in the current state of this flow execution request context. This method
* should be called by clients that report internal event occurrences, such as action states. The
* <code>onEvent()</code> method of the flow involved in the flow execution will be called.
* @param event the event that occurred
* @return a boolean indicating if handling this event caused the current state to exit and a new state to enter
* @throws FlowExecutionException if an exception was thrown within a state of the flow during execution of this
* signalEvent operation
* @see Flow#handleEvent(RequestControlContext)
*/
public boolean handleEvent(Event event) throws FlowExecutionException;
/**
* End the active flow session of the current flow execution. This method should be called by clients that terminate
* flows, such as end states. The <code>end()</code> method of the flow involved in the flow execution will be
@@ -103,14 +126,6 @@ public interface RequestControlContext extends RequestContext {
*/
public FlowSession endActiveFlowSession(MutableAttributeMap output) throws IllegalStateException;
/**
* Execute this transition out of the current source state. Allows for privileged execution of an arbitrary
* transition.
* @param transition the transition
* @see Transition#execute(State, RequestControlContext)
*/
public boolean execute(Transition transition);
/**
* Returns true if the 'always redirect pause' flow execution attribute is set to true, false otherwise.
* @return true or false

View File

@@ -40,7 +40,7 @@ import org.springframework.webflow.execution.ViewFactory;
public class ViewState extends TransitionableState {
/**
* The list of actions to be executed when this state is entered.
* The list of actions to be executed before the view is rendered.
*/
private ActionList renderActionList = new ActionList();
@@ -59,6 +59,12 @@ public class ViewState extends TransitionableState {
*/
private Boolean redirect;
/**
* An enum indicating the history behavior of this view-state. Used to configure back-tracking policies. Default is
* {@link History#PRESERVE}.
*/
private History history = History.PRESERVE;
/**
* Whether or not the view should render as a popup.
*/
@@ -146,6 +152,24 @@ public class ViewState extends TransitionableState {
this.popup = popup;
}
/**
* Returns the history behavior of this view-state. Used to configure back-tracking policies. Default is
* {@link History#PRESERVE}.
* @return the history
*/
public History getHistory() {
return history;
}
/**
* Sets the history behavior of this view state. Used to configure back-tracking policies. Default is
* {@link History#PRESERVE}.
* @param history the history
*/
public void setHistory(History history) {
this.history = history;
}
/**
* Returns the view factory.
*/
@@ -201,8 +225,15 @@ public class ViewState extends TransitionableState {
}
public void exit(RequestControlContext context) {
destroyVariables(context);
super.exit(context);
destroyVariables(context);
if (history == History.PRESERVE) {
context.updateCurrentFlowExecutionSnapshot();
} else if (history == History.DISCARD) {
context.removeCurrentFlowExecutionSnapshot();
} else if (history == History.INVALIDATE) {
context.removeAllFlowExecutionSnapshots();
}
}
// internal helpers
@@ -269,7 +300,7 @@ public class ViewState extends TransitionableState {
protected void appendToString(ToStringCreator creator) {
super.appendToString(creator);
creator.append("viewFactory", viewFactory).append("variables", variables).append("redirect", redirect).append(
"popup", popup);
"popup", popup).append("history", history);
}
}

View File

@@ -23,6 +23,7 @@ import org.springframework.webflow.engine.DecisionState;
import org.springframework.webflow.engine.EndState;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.engine.FlowExecutionExceptionHandler;
import org.springframework.webflow.engine.History;
import org.springframework.webflow.engine.State;
import org.springframework.webflow.engine.SubflowAttributeMapper;
import org.springframework.webflow.engine.SubflowState;
@@ -81,12 +82,14 @@ public class FlowArtifactFactory {
* @return the fully initialized view state instance
*/
public State createViewState(String id, Flow flow, ViewVariable[] variables, Action[] entryActions,
ViewFactory viewFactory, Boolean redirect, boolean popup, Action[] renderActions, Transition[] transitions,
FlowExecutionExceptionHandler[] exceptionHandlers, Action[] exitActions, AttributeMap attributes) {
ViewFactory viewFactory, Boolean redirect, boolean popup, History history, Action[] renderActions,
Transition[] transitions, FlowExecutionExceptionHandler[] exceptionHandlers, Action[] exitActions,
AttributeMap attributes) {
ViewState viewState = new ViewState(flow, id, viewFactory);
viewState.addVariables(variables);
viewState.setRedirect(redirect);
viewState.setPopup(popup);
viewState.setHistory(history);
viewState.getRenderActionList().addAll(renderActions);
configureCommonProperties(viewState, entryActions, transitions, exceptionHandlers, exitActions, attributes);
return viewState;

View File

@@ -18,7 +18,6 @@ package org.springframework.webflow.engine.builder;
import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ExpressionParser;
import org.springframework.binding.format.FormatterRegistry;
import org.springframework.core.io.ResourceLoader;
import org.springframework.webflow.execution.View;
import org.springframework.webflow.execution.ViewFactory;
@@ -37,7 +36,7 @@ public interface ViewFactoryCreator {
* @return the view factory
*/
public ViewFactory createViewFactory(Expression viewIdExpression, ExpressionParser expressionParser,
FormatterRegistry formatterRegistry, ResourceLoader resourceLoader);
FormatterRegistry formatterRegistry);
/**
* Get the default id of the view to render in the provided view state by convention.

View File

@@ -7,6 +7,7 @@ import java.util.Iterator;
import java.util.List;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.binding.convert.ConversionException;
import org.springframework.binding.convert.ConversionExecutor;
@@ -21,7 +22,9 @@ import org.springframework.binding.mapping.impl.DefaultMapper;
import org.springframework.binding.mapping.impl.DefaultMapping;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.core.JdkVersion;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
@@ -44,9 +47,11 @@ import org.springframework.webflow.core.collection.AttributeMap;
import org.springframework.webflow.core.collection.LocalAttributeMap;
import org.springframework.webflow.core.collection.MutableAttributeMap;
import org.springframework.webflow.definition.registry.FlowDefinitionLocator;
import org.springframework.webflow.engine.AnnotatedAction;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.engine.FlowExecutionExceptionHandler;
import org.springframework.webflow.engine.FlowVariable;
import org.springframework.webflow.engine.History;
import org.springframework.webflow.engine.SubflowAttributeMapper;
import org.springframework.webflow.engine.TargetStateResolver;
import org.springframework.webflow.engine.Transition;
@@ -87,7 +92,6 @@ import org.springframework.webflow.engine.support.TransitionCriteriaChain;
import org.springframework.webflow.engine.support.TransitionExecutingFlowExecutionExceptionHandler;
import org.springframework.webflow.execution.Action;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.execution.ScopeType;
import org.springframework.webflow.execution.ViewFactory;
import org.springframework.webflow.security.SecurityRule;
@@ -104,6 +108,10 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
private LocalFlowBuilderContext localFlowBuilderContext;
/**
* Creates a flow builder that can build a {@link Flow} from a {@link FlowModel}.
* @param flowModelHolder the flow model holder
*/
public FlowModelFlowBuilder(FlowModelHolder flowModelHolder) {
this.flowModelHolder = flowModelHolder;
}
@@ -311,10 +319,31 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
}
new XmlBeanDefinitionReader(flowContext).loadBeanDefinitions(resources);
registerFlowBeans(flowContext.getBeanFactory());
registerMessageSource(flowContext, flowResource);
flowContext.refresh();
return flowContext;
}
private void registerMessageSource(GenericApplicationContext flowContext, Resource flowResource) {
boolean localMessageSourcePresent = flowContext
.containsLocalBean(AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME);
if (flowResource != null && !localMessageSourcePresent) {
Resource messageBundle;
try {
messageBundle = flowResource.createRelative("messages.properties");
} catch (IOException e) {
messageBundle = null;
}
if (messageBundle != null && messageBundle.exists()) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.rootBeanDefinition(ReloadableResourceBundleMessageSource.class);
builder.addPropertyValue("basename", "messages");
flowContext.registerBeanDefinition(AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME, builder
.getBeanDefinition());
}
}
}
private Flow parseFlow(FlowModel flow) {
String flowId = getLocalContext().getFlowId();
AttributeMap externallyAssignedAttributes = getLocalContext().getFlowAttributes();
@@ -329,8 +358,7 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
Class clazz = (Class) fromStringTo(Class.class).execute(var.getClassName());
VariableValueFactory valueFactory = new BeanFactoryVariableValueFactory(clazz, getFlow()
.getApplicationContext().getAutowireCapableBeanFactory());
ScopeType scope = ScopeType.CONVERSATION;
return new FlowVariable(var.getName(), valueFactory, scope == ScopeType.FLOW ? true : false);
return new FlowVariable(var.getName(), valueFactory);
}
private Mapper parseFlowInputMapper(List inputs) {
@@ -476,6 +504,10 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
if (StringUtils.hasText(state.getPopup())) {
popup = ((Boolean) fromStringTo(Boolean.class).execute(state.getPopup())).booleanValue();
}
History history = History.PRESERVE;
if (StringUtils.hasText(state.getHistory())) {
history = (History) fromStringTo(History.class).execute(state.getHistory());
}
MutableAttributeMap attributes = parseMetaAttributes(state.getAttributes());
if (state.getModel() != null) {
attributes.put("model", getLocalContext().getExpressionParser().parseExpression(state.getModel(),
@@ -484,7 +516,7 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
parseAndPutSecured(state.getSecured(), attributes);
getLocalContext().getFlowArtifactFactory().createViewState(state.getId(), flow,
parseViewVariables(state.getVars()), parseActions(state.getOnEntryActions()), viewFactory, redirect,
popup, parseActions(state.getOnRenderActions()), parseTransitions(state.getTransitions()),
popup, history, parseActions(state.getOnRenderActions()), parseTransitions(state.getTransitions()),
parseExceptionHandlers(state.getExceptionHandlers(), state.getTransitions()),
parseActions(state.getOnExitActions()), attributes);
}
@@ -560,8 +592,7 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
private ViewFactory createViewFactory(Expression viewId) {
return getLocalContext().getViewFactoryCreator().createViewFactory(viewId,
getLocalContext().getExpressionParser(), getLocalContext().getFormatterRegistry(),
getLocalContext().getApplicationContext());
getLocalContext().getExpressionParser(), getLocalContext().getFormatterRegistry());
}
private ViewVariable[] parseViewVariables(List vars) {
@@ -734,13 +765,21 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
if (actionModels != null && !actionModels.isEmpty()) {
List actions = new ArrayList(actionModels.size());
for (Iterator it = actionModels.iterator(); it.hasNext();) {
AbstractActionModel action = (AbstractActionModel) it.next();
if (action instanceof EvaluateModel) {
actions.add(parseEvaluateAction((EvaluateModel) action));
} else if (action instanceof RenderModel) {
actions.add(parseRenderAction((RenderModel) action));
} else if (action instanceof SetModel) {
actions.add(parseSetAction((SetModel) action));
AbstractActionModel actionModel = (AbstractActionModel) it.next();
Action action;
if (actionModel instanceof EvaluateModel) {
action = parseEvaluateAction((EvaluateModel) actionModel);
} else if (actionModel instanceof RenderModel) {
action = parseRenderAction((RenderModel) actionModel);
} else if (actionModel instanceof SetModel) {
action = parseSetAction((SetModel) actionModel);
} else {
action = null;
}
if (action != null) {
AnnotatedAction annotatedAction = new AnnotatedAction(action);
annotatedAction.getAttributes().putAll(parseMetaAttributes(actionModel.getAttributes()));
actions.add(annotatedAction);
}
}
return (Action[]) actions.toArray(new Action[actions.size()]);
@@ -847,14 +886,6 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
}
}
private ScopeType parseScopeType(String scope, ScopeType defaultScope) {
if (StringUtils.hasText(scope)) {
return (ScopeType) fromStringTo(ScopeType.class).execute(scope);
} else {
return defaultScope;
}
}
private ConversionExecutor fromStringTo(Class targetType) throws ConversionException {
return getLocalContext().getConversionService().getConversionExecutor(String.class, targetType);
}

View File

@@ -264,6 +264,18 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
}
}
public void setCurrentState(String stateId) {
State state = flow.getStateInstance(stateId);
FlowSessionImpl session;
if (started) {
session = getActiveSessionInternal();
} else {
session = activateSession(flow);
started = true;
}
session.setCurrentState(state);
}
public void resume(ExternalContext externalContext) throws FlowExecutionException, IllegalStateException {
if (!isActive()) {
if (started) {
@@ -386,6 +398,18 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
return key;
}
void updateCurrentFlowExecutionSnapshot() {
keyFactory.updateFlowExecutionSnapshot(this);
}
void removeCurrentFlowExecutionSnapshot() {
keyFactory.removeFlowExecutionSnapshot(this);
}
void removeAllFlowExecutionSnapshots() {
keyFactory.removeAllFlowExecutionSnapshots(this);
}
// package private setters for restoring transient state used by FlowExecutionImplServicesConfigurer
FlowExecutionListener[] getListeners() {
@@ -516,7 +540,7 @@ public class FlowExecutionImpl implements FlowExecution, Externalizable {
* @param flow the flow definition
* @return the new flow session
*/
private FlowSession activateSession(Flow flow) {
private FlowSessionImpl activateSession(Flow flow) {
FlowSessionImpl session;
if (!flowSessions.isEmpty()) {
FlowSessionImpl parent = getActiveSessionInternal();

View File

@@ -69,6 +69,15 @@ public class FlowExecutionImplFactory extends FlowExecutionImplServicesConfigure
return new SimpleFlowExecutionKey(idGenerator.generateUid());
}
public void removeAllFlowExecutionSnapshots(FlowExecution execution) {
}
public void removeFlowExecutionSnapshot(FlowExecution execution) {
}
public void updateFlowExecutionSnapshot(FlowExecution execution) {
}
private static class SimpleFlowExecutionKey extends FlowExecutionKey {
private Serializable value;

View File

@@ -16,8 +16,6 @@
package org.springframework.webflow.engine.impl;
import org.springframework.binding.message.DefaultMessageContextFactory;
import org.springframework.binding.message.MessageContextFactory;
import org.springframework.context.support.StaticMessageSource;
import org.springframework.util.Assert;
import org.springframework.webflow.core.collection.AttributeMap;
import org.springframework.webflow.core.collection.CollectionUtils;
@@ -37,11 +35,6 @@ abstract class FlowExecutionImplServicesConfigurer {
*/
private FlowExecutionListenerLoader executionListenerLoader = StaticFlowExecutionListenerLoader.EMPTY_INSTANCE;
/**
* The factory for message contexts for tracking flow execution messages.
*/
private MessageContextFactory messageContextFactory = new DefaultMessageContextFactory(new StaticMessageSource());
/**
* Sets the attributes to apply to flow executions created by this factory. Execution attributes may affect flow
* execution behavior.
@@ -61,13 +54,6 @@ abstract class FlowExecutionImplServicesConfigurer {
this.executionListenerLoader = executionListenerLoader;
}
/**
* Sets the strategy for creating message contexts that track flow execution messages.
*/
public void setMessageContextFactory(MessageContextFactory messageContextFactory) {
this.messageContextFactory = messageContextFactory;
}
/**
* Called by subclasses to apply the configured set of standard services to the flow execution.
* @param execution the flow execution
@@ -75,6 +61,7 @@ abstract class FlowExecutionImplServicesConfigurer {
protected void configureServices(FlowExecutionImpl execution) {
execution.setAttributes(executionAttributes);
execution.setListeners(executionListenerLoader.getListeners(execution.getDefinition()));
execution.setMessageContextFactory(messageContextFactory);
execution.setMessageContextFactory(new DefaultMessageContextFactory(execution.getDefinition()
.getApplicationContext()));
}
}

View File

@@ -195,15 +195,23 @@ class RequestControlContextImpl implements RequestControlContext {
return flowExecution.assignKey();
}
public void updateCurrentFlowExecutionSnapshot() {
flowExecution.updateCurrentFlowExecutionSnapshot();
}
public void removeCurrentFlowExecutionSnapshot() {
flowExecution.removeCurrentFlowExecutionSnapshot();
}
public void removeAllFlowExecutionSnapshots() {
flowExecution.removeAllFlowExecutionSnapshots();
}
public boolean getAlwaysRedirectOnPause() {
Boolean redirectOnPause = flowExecution.getAttributes().getBoolean("alwaysRedirectOnPause");
return redirectOnPause != null ? redirectOnPause.booleanValue() : false;
}
public void start(Flow flow, MutableAttributeMap input) throws FlowExecutionException {
flowExecution.start(flow, input, this);
}
public boolean handleEvent(Event event) throws FlowExecutionException {
this.currentEvent = event;
return flowExecution.handleEvent(event, this);
@@ -213,6 +221,10 @@ class RequestControlContextImpl implements RequestControlContext {
return flowExecution.execute(transition, this);
}
public void start(Flow flow, MutableAttributeMap input) throws FlowExecutionException {
flowExecution.start(flow, input, this);
}
public FlowSession endActiveFlowSession(MutableAttributeMap output) throws IllegalStateException {
return flowExecution.endActiveFlowSession(output, this);
}

View File

@@ -16,9 +16,7 @@
package org.springframework.webflow.engine.model.builder.xml;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -57,8 +55,6 @@ import org.springframework.webflow.engine.model.registry.FlowModelLocator;
import org.springframework.webflow.engine.model.registry.NoSuchFlowModelException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
@@ -287,8 +283,7 @@ public class XmlFlowModelBuilder implements FlowModelBuilder {
}
private LinkedList parseActions(Element element) {
List actionElements = getChildElementsByTagNames(element, Arrays.asList(new String[] { "evaluate", "render",
"set" }));
List actionElements = DomUtils.getChildElementsByTagName(element, new String[] { "evaluate", "render", "set" });
if (actionElements.isEmpty()) {
return null;
}
@@ -300,8 +295,8 @@ public class XmlFlowModelBuilder implements FlowModelBuilder {
}
private LinkedList parseStates(Element element) {
List stateElements = getChildElementsByTagNames(element, Arrays.asList(new String[] { "view-state",
"action-state", "decision-state", "subflow-state", "end-state" }));
List stateElements = DomUtils.getChildElementsByTagName(element, new String[] { "view-state", "action-state",
"decision-state", "subflow-state", "end-state" });
if (stateElements.isEmpty()) {
return null;
}
@@ -618,25 +613,6 @@ public class XmlFlowModelBuilder implements FlowModelBuilder {
return state;
}
// TODO: submited to DomUtils in spring-core will be available in 2.5.3
public static List getChildElementsByTagNames(Element ele, Collection childEleNames) {
Assert.notNull(ele, "Element must not be null");
Assert.notNull(childEleNames, "Element names collection must not be null");
NodeList nl = ele.getChildNodes();
List childEles = new ArrayList();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element && nodeNameMatch(node, childEleNames)) {
childEles.add(node);
}
}
return childEles;
}
private static boolean nodeNameMatch(Node node, Collection desiredNames) {
return (desiredNames.contains(node.getNodeName()) || desiredNames.contains(node.getLocalName()));
}
public String toString() {
return new ToStringCreator(this).append("resource", resource).toString();
}

View File

@@ -53,7 +53,9 @@ public class BeanFactoryVariableValueFactory implements VariableValueFactory {
}
public void restoreReferences(Object value, RequestContext context) {
beanFactory.autowireBean(value);
if (value != null) {
beanFactory.autowireBean(value);
}
}
public String toString() {

View File

@@ -30,4 +30,11 @@ public interface FlowExecutionKeyFactory {
* @return the key to assign to the flow execution
*/
public FlowExecutionKey getKey(FlowExecution execution);
public void updateFlowExecutionSnapshot(FlowExecution execution);
public void removeFlowExecutionSnapshot(FlowExecution execution);
public void removeAllFlowExecutionSnapshots(FlowExecution execution);
}

View File

@@ -126,6 +126,22 @@ public class DefaultFlowExecutionRepository extends AbstractFlowExecutionContinu
putConversationScope(flowExecution);
}
// implementing flow execution key factory
public void removeAllFlowExecutionSnapshots(FlowExecution execution) {
getContinuationGroup(execution.getKey()).removeAllContinuations();
}
public void removeFlowExecutionSnapshot(FlowExecution execution) {
FlowExecutionKey key = execution.getKey();
getContinuationGroup(key).removeContinuation(getContinuationId(key));
}
public void updateFlowExecutionSnapshot(FlowExecution execution) {
FlowExecutionKey key = execution.getKey();
getContinuationGroup(key).updateContinuation(getContinuationId(key), snapshot(execution));
}
// hooks for subclassing
protected FlowExecutionContinuationGroup createFlowExecutionContinuationGroup() {

View File

@@ -66,23 +66,20 @@ class FlowExecutionContinuationGroup implements Serializable {
/**
* Returns the continuation with the provided <code>id</code>, or <code>null</code> if no such continuation
* exists with that id.
* @param id the continuation id
* @param continuationId the continuation id
* @return the continuation
* @throws ContinuationNotFoundException if the id does not match a continuation in this group
*/
public FlowExecutionContinuation get(Serializable id) throws ContinuationNotFoundException {
FlowExecutionContinuation continuation = (FlowExecutionContinuation) continuations.get(id);
public FlowExecutionContinuation get(Serializable continuationId) throws ContinuationNotFoundException {
FlowExecutionContinuation continuation = (FlowExecutionContinuation) continuations.get(continuationId);
if (continuation == null) {
throw new ContinuationNotFoundException(id);
throw new ContinuationNotFoundException(continuationId);
}
return continuation;
}
/**
* Add a flow execution continuation with given id to this group.
*
* TODO add listener methods 1. continuationAdded(ConversationId conversationId, FlowExecutionContinuation
* continuation) 2. maxContinuationsReached(ConversationId conversationId)
* @param continuationId the continuation id
* @param continuation the continuation
*/
@@ -99,6 +96,37 @@ class FlowExecutionContinuationGroup implements Serializable {
}
}
/**
* Update the continuation with the given id.
* @param continuationId the continuation id
* @param continuation thew new continuation
* @throws ContinuationNotFoundException if there was no previous continuation to update
*/
public void updateContinuation(Serializable continuationId, FlowExecutionContinuation continuation)
throws ContinuationNotFoundException {
if (!continuations.containsKey(continuationId)) {
throw new ContinuationNotFoundException(continuationId);
}
continuations.put(continuationId, continuation);
}
/**
* Remove the continuation with the given id.
* @param continuationId the continuation id
*/
public void removeContinuation(Serializable continuationId) {
continuations.remove(continuationId);
continuationIds.remove(continuationId);
}
/**
* Remove all continuations in this group.
*/
public void removeAllContinuations() {
continuations.clear();
continuationIds.clear();
}
/**
* Has the maximum number of allowed continuations in this group been exceeded?
*/
@@ -112,4 +140,5 @@ class FlowExecutionContinuationGroup implements Serializable {
private void removeOldestContinuation() {
continuations.remove(continuationIds.removeFirst());
}
}

View File

@@ -105,8 +105,8 @@ public class FlowExecutorImpl implements FlowExecutor {
public FlowExecutionResult launchExecution(String flowId, MutableAttributeMap input, ExternalContext context)
throws FlowException {
ExternalContextHolder.setExternalContext(context);
try {
ExternalContextHolder.setExternalContext(context);
FlowDefinition flowDefinition = definitionLocator.getFlowDefinition(flowId);
FlowExecution flowExecution = executionFactory.createFlowExecution(flowDefinition);
flowExecution.start(input, context);

View File

@@ -62,8 +62,8 @@ public class PortletMvcView extends MvcView {
}
request.setAttribute(ViewRendererServlet.VIEW_ATTRIBUTE, view);
request.setAttribute(ViewRendererServlet.MODEL_ATTRIBUTE, model);
// request.setAttribute(org.springframework.web.servlet.support.RequestContext.WEB_APPLICATION_CONTEXT_ATTRIBUTE,
// context.getActiveFlow().getBeanFactory());
request.setAttribute(org.springframework.web.servlet.support.RequestContext.WEB_APPLICATION_CONTEXT_ATTRIBUTE,
context.getActiveFlow().getApplicationContext());
portletContext.getRequestDispatcher(DispatcherPortlet.DEFAULT_VIEW_RENDERER_URL).include(request, response);
}

View File

@@ -44,8 +44,8 @@ public class ServletMvcView extends MvcView {
ExternalContext externalContext = context.getExternalContext();
HttpServletRequest request = (HttpServletRequest) externalContext.getNativeRequest();
HttpServletResponse response = (HttpServletResponse) externalContext.getNativeResponse();
// request.setAttribute(org.springframework.web.servlet.support.RequestContext.WEB_APPLICATION_CONTEXT_ATTRIBUTE,
// context.getActiveFlow().getBeanFactory());
request.setAttribute(org.springframework.web.servlet.support.RequestContext.WEB_APPLICATION_CONTEXT_ATTRIBUTE,
context.getActiveFlow().getApplicationContext());
getView().render(model, request, response);
}
}

View File

@@ -16,6 +16,9 @@ import org.springframework.binding.message.Message;
import org.springframework.binding.message.MessageContext;
import org.springframework.binding.message.MessageCriteria;
import org.springframework.binding.message.Severity;
import org.springframework.util.Assert;
import org.springframework.validation.AbstractErrors;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
@@ -29,7 +32,9 @@ import org.springframework.validation.ObjectError;
*
* @author Keith Donald
*/
public class BindingModel extends ViewRenderingErrors {
public class BindingModel extends AbstractErrors {
private String objectName;
private Object boundObject;
@@ -43,13 +48,17 @@ public class BindingModel extends ViewRenderingErrors {
/**
* Creates a new Spring Binding model.
* @param objectName the name of the bound model object
* @param boundObject the bound model object
* @param expressionParser the expression parser used to access model object properties
* @param formatterRegistry the formatter registry used to access formatters for formatting properties
* @param messageContext the message context containing flow messages to display
*/
public BindingModel(Object boundObject, ExpressionParser expressionParser, FormatterRegistry formatterRegistry,
MessageContext messageContext) {
public BindingModel(String objectName, Object boundObject, ExpressionParser expressionParser,
FormatterRegistry formatterRegistry, MessageContext messageContext) {
Assert.hasText(objectName, "The object name is required");
Assert.notNull(boundObject, "The bound object instance is required");
this.objectName = objectName;
this.boundObject = boundObject;
this.expressionParser = expressionParser;
this.formatterRegistry = formatterRegistry;
@@ -94,6 +103,12 @@ public class BindingModel extends ViewRenderingErrors {
return getFormattedValue(parseFieldExpression(field));
}
// not typically used by mvc views, but implemented to be on the safe side
public List getFieldErrors() {
return toErrors(messageContext.getMessagesByCriteria(new FieldErrorMessage()));
}
// internal helpers
private Expression parseFieldExpression(String field) {
@@ -126,10 +141,9 @@ public class BindingModel extends ViewRenderingErrors {
for (int i = 0; i < messages.length; i++) {
Message message = messages[i];
if (message.getSource() == null) {
errors.add(new ObjectError("boundObject", null, null, message.getText()));
errors.add(new ObjectError(objectName, message.getText()));
} else {
errors.add(new FieldError("boundObject", (String) message.getSource(), null, false, null, null, message
.getText()));
errors.add(new FieldError(objectName, (String) message.getSource(), message.getText()));
}
}
return errors;
@@ -167,13 +181,36 @@ public class BindingModel extends ViewRenderingErrors {
private static class FieldErrorMessage implements MessageCriteria {
private String field;
public FieldErrorMessage() {
}
public FieldErrorMessage(String field) {
this.field = field;
}
public boolean test(Message message) {
return field.equals(message.getSource()) && message.getSeverity() == Severity.ERROR;
if (field != null) {
return field.equals(message.getSource()) && message.getSeverity() == Severity.ERROR;
} else {
return message.getSource() != null && message.getSeverity() == Severity.ERROR;
}
}
}
public String getObjectName() {
return objectName;
}
public void addAllErrors(Errors errors) {
throw new UnsupportedOperationException("Should not be called during view rendering");
}
public void reject(String errorCode, Object[] errorArgs, String defaultMessage) {
throw new UnsupportedOperationException("Should not be called during view rendering");
}
public void rejectValue(String field, String errorCode, Object[] errorArgs, String defaultMessage) {
throw new UnsupportedOperationException("Should not be called during view rendering");
}
}

View File

@@ -6,7 +6,6 @@ import org.springframework.binding.format.FormatterRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.ContextResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.ClassUtils;
import org.springframework.web.servlet.view.InternalResourceView;
import org.springframework.web.servlet.view.JstlView;
@@ -28,45 +27,44 @@ class InternalFlowResourceMvcViewFactory implements ViewFactory {
private Expression viewIdExpression;
private ApplicationContext applicationContext;
private ResourceLoader resourceLoader;
private ExpressionParser expressionParser;
private FormatterRegistry formatterRegistry;
public InternalFlowResourceMvcViewFactory(Expression viewIdExpression, ExpressionParser expressionParser,
FormatterRegistry formatterRegistry, ApplicationContext context, ResourceLoader resourceLoader) {
FormatterRegistry formatterRegistry) {
this.viewIdExpression = viewIdExpression;
this.expressionParser = expressionParser;
this.formatterRegistry = formatterRegistry;
this.applicationContext = context;
this.resourceLoader = resourceLoader;
}
public View getView(RequestContext context) {
String viewId = (String) viewIdExpression.getValue(context);
if (viewId.startsWith("/")) {
return getViewInternal(viewId, context);
return getViewInternal(viewId, context, context.getActiveFlow().getApplicationContext());
} else {
Resource viewResource = resourceLoader.getResource(viewId);
ApplicationContext flowContext = context.getActiveFlow().getApplicationContext();
if (flowContext == null) {
throw new IllegalStateException("A Flow ApplicationContext is required to resolve Flow View Resources");
}
Resource viewResource = flowContext.getResource(viewId);
if (!(viewResource instanceof ContextResource)) {
throw new IllegalStateException(
"A ContextResource is required to get relative view paths within this context");
}
return getViewInternal(((ContextResource) viewResource).getPathWithinContext(), context);
return getViewInternal(((ContextResource) viewResource).getPathWithinContext(), context, flowContext);
}
}
private View getViewInternal(String viewPath, RequestContext context) {
private View getViewInternal(String viewPath, RequestContext context, ApplicationContext flowContext) {
if (viewPath.endsWith(".jsp")) {
if (JSTL_PRESENT) {
JstlView view = new JstlView(viewPath);
view.setApplicationContext(context.getActiveFlow().getApplicationContext());
view.setApplicationContext(flowContext);
return createMvcView(view, context);
} else {
InternalResourceView view = new InternalResourceView(viewPath);
view.setApplicationContext(flowContext);
return createMvcView(view, context);
}
} else {

View File

@@ -4,9 +4,8 @@ import java.util.List;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.validation.AbstractErrors;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
/**
* Adapts a MessageContext object to the Spring Errors interface. Allows Spring Validators to record errors that are
@@ -14,7 +13,7 @@ import org.springframework.validation.ObjectError;
*
* @author Keith Donald
*/
public class MessageContextErrors implements Errors {
public class MessageContextErrors extends AbstractErrors {
private MessageContext messageContext;
@@ -26,122 +25,32 @@ public class MessageContextErrors implements Errors {
this.messageContext = messageContext;
}
public void reject(String errorCode) {
messageContext.addMessage(new MessageBuilder().error().code(errorCode).build());
}
public void reject(String errorCode, String defaultMessage) {
messageContext.addMessage(new MessageBuilder().error().code(errorCode).defaultText(defaultMessage).build());
}
public void reject(String errorCode, Object[] errorArgs, String defaultMessage) {
messageContext.addMessage(new MessageBuilder().error().code(errorCode).defaultText(defaultMessage).build());
}
public void rejectValue(String field, String errorCode) {
messageContext.addMessage(new MessageBuilder().error().source(field).code(errorCode).build());
}
public void rejectValue(String field, String errorCode, String defaultMessage) {
messageContext.addMessage(new MessageBuilder().error().source(field).code(errorCode)
.defaultText(defaultMessage).build());
}
public void rejectValue(String field, String errorCode, Object[] errorArgs, String defaultMessage) {
messageContext.addMessage(new MessageBuilder().error().source(field).code(errorCode).args(errorArgs)
.defaultText(defaultMessage).build());
messageContext.addMessage(new MessageBuilder().error().code(errorCode).defaultText(defaultMessage).build());
}
public void addAllErrors(Errors errors) {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public List getAllErrors() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public int getErrorCount() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public FieldError getFieldError() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public FieldError getFieldError(String field) {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public int getFieldErrorCount() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public int getFieldErrorCount(String field) {
throw new UnsupportedOperationException("Should not be called by a validator");
throw new UnsupportedOperationException("Auto-generated method stub");
}
public List getFieldErrors() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public List getFieldErrors(String field) {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public Class getFieldType(String field) {
throw new UnsupportedOperationException("Should not be called by a validator");
throw new UnsupportedOperationException("Auto-generated method stub");
}
public Object getFieldValue(String field) {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public ObjectError getGlobalError() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public int getGlobalErrorCount() {
throw new UnsupportedOperationException("Should not be called by a validator");
throw new UnsupportedOperationException("Auto-generated method stub");
}
public List getGlobalErrors() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public String getNestedPath() {
throw new UnsupportedOperationException("Should not be called by a validator");
throw new UnsupportedOperationException("Auto-generated method stub");
}
public String getObjectName() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public boolean hasErrors() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public boolean hasFieldErrors() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public boolean hasFieldErrors(String field) {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public boolean hasGlobalErrors() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public void popNestedPath() throws IllegalStateException {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public void pushNestedPath(String subPath) {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public void setNestedPath(String nestedPath) {
throw new UnsupportedOperationException("Should not be called by a validator");
throw new UnsupportedOperationException("Auto-generated method stub");
}
}

View File

@@ -191,8 +191,8 @@ public abstract class MvcView implements View {
private void exposeBindingModel(Map model) {
Object modelObject = getModelObject();
if (modelObject != null) {
BindingModel bindingModel = new BindingModel(modelObject, expressionParser, formatterRegistry,
requestContext.getMessageContext());
BindingModel bindingModel = new BindingModel(getModelExpression().getExpressionString(), modelObject,
expressionParser, formatterRegistry, requestContext.getMessageContext());
bindingModel.setMappingResults(mappingResults);
model.put(BindingResult.MODEL_KEY_PREFIX + getModelExpression().getExpressionString(), bindingModel);
}

View File

@@ -20,9 +20,6 @@ import java.util.List;
import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ExpressionParser;
import org.springframework.binding.format.FormatterRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.ResourceLoader;
import org.springframework.webflow.engine.builder.ViewFactoryCreator;
import org.springframework.webflow.execution.ViewFactory;
@@ -38,12 +35,10 @@ import org.springframework.webflow.execution.ViewFactory;
* @author Keith Donald
* @author Scott Andrews
*/
public class MvcViewFactoryCreator implements ViewFactoryCreator, ApplicationContextAware {
public class MvcViewFactoryCreator implements ViewFactoryCreator {
private List viewResolvers;
private ApplicationContext applicationContext;
/**
* Sets the view resolvers that will be used to resolve views selected by flows. If multiple resolvers are to be
* used, the resolvers should be ordered in the manner they should be applied.
@@ -53,18 +48,12 @@ public class MvcViewFactoryCreator implements ViewFactoryCreator, ApplicationCon
this.viewResolvers = viewResolvers;
}
public void setApplicationContext(ApplicationContext context) {
this.applicationContext = context;
}
public ViewFactory createViewFactory(Expression viewIdExpression, ExpressionParser expressionParser,
FormatterRegistry formatterRegistry, ResourceLoader resourceLoader) {
FormatterRegistry formatterRegistry) {
if (viewResolvers != null) {
return new ViewResolvingMvcViewFactory(viewIdExpression, expressionParser, formatterRegistry,
viewResolvers, applicationContext);
return new ViewResolvingMvcViewFactory(viewIdExpression, expressionParser, formatterRegistry, viewResolvers);
} else {
return new InternalFlowResourceMvcViewFactory(viewIdExpression, expressionParser, formatterRegistry,
applicationContext, resourceLoader);
return new InternalFlowResourceMvcViewFactory(viewIdExpression, expressionParser, formatterRegistry);
}
}

View File

@@ -7,7 +7,6 @@ import java.util.Locale;
import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ExpressionParser;
import org.springframework.binding.format.FormatterRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.webflow.context.portlet.PortletExternalContext;
@@ -32,13 +31,12 @@ class ViewResolvingMvcViewFactory implements ViewFactory {
private List viewResolvers;
private ApplicationContext applicationContext;
public ViewResolvingMvcViewFactory(Expression viewIdExpression, ExpressionParser expressionParser,
FormatterRegistry formatterRegistry, List viewResolvers, ApplicationContext context) {
FormatterRegistry formatterRegistry, List viewResolvers) {
this.viewIdExpression = viewIdExpression;
this.expressionParser = expressionParser;
this.formatterRegistry = formatterRegistry;
this.viewResolvers = viewResolvers;
this.applicationContext = context;
}
public View getView(RequestContext context) {

View File

@@ -14,4 +14,14 @@ public class MockFlowExecutionKeyFactory implements FlowExecutionKeyFactory {
public FlowExecutionKey getKey(FlowExecution execution) {
return new GeneratedFlowExecutionKey();
}
public void removeAllFlowExecutionSnapshots(FlowExecution execution) {
}
public void removeFlowExecutionSnapshot(FlowExecution execution) {
}
public void updateFlowExecutionSnapshot(FlowExecution execution) {
}
}

View File

@@ -64,6 +64,33 @@ public class MockRequestControlContext extends MockRequestContext implements Req
getMockFlowExecutionContext().getMockActiveSession().setState(state);
}
public FlowExecutionKey assignFlowExecutionKey() {
GeneratedFlowExecutionKey key = new GeneratedFlowExecutionKey();
getMockFlowExecutionContext().setKey(key);
return key;
}
public void removeAllFlowExecutionSnapshots() {
}
public void removeCurrentFlowExecutionSnapshot() {
}
public void updateCurrentFlowExecutionSnapshot() {
}
public boolean handleEvent(Event event) {
setCurrentEvent(event);
return ((Flow) getActiveFlow()).handleEvent(this);
}
public boolean execute(Transition transition) {
return transition.execute((TransitionableState) getCurrentState(), this);
}
public void start(Flow flow, MutableAttributeMap input) throws IllegalStateException {
MockFlowSession session = new MockFlowSession(flow, input);
if (getFlowExecutionContext().isActive()) {
@@ -73,11 +100,6 @@ public class MockRequestControlContext extends MockRequestContext implements Req
flow.start(this, input);
}
public boolean handleEvent(Event event) {
setCurrentEvent(event);
return ((Flow) getActiveFlow()).handleEvent(this);
}
public FlowSession endActiveFlowSession(MutableAttributeMap output) throws IllegalStateException {
MockFlowSession endingSession = getMockFlowExecutionContext().getMockActiveSession();
endingSession.getDefinitionInternal().end(this, output);
@@ -85,24 +107,16 @@ public class MockRequestControlContext extends MockRequestContext implements Req
return endingSession;
}
public boolean execute(Transition transition) {
return transition.execute((TransitionableState) getCurrentState(), this);
public boolean getAlwaysRedirectOnPause() {
return alwaysRedirectOnPause;
}
public FlowExecutionKey assignFlowExecutionKey() {
GeneratedFlowExecutionKey key = new GeneratedFlowExecutionKey();
getMockFlowExecutionContext().setKey(key);
return key;
}
// implementation specific accessors for testing
public boolean getFlowExecutionRedirectSent() {
return this.flowExecutionRedirectSent;
}
public boolean getAlwaysRedirectOnPause() {
return alwaysRedirectOnPause;
}
public void setAlwaysRedirectOnPause(boolean alwaysRedirectOnPause) {
this.alwaysRedirectOnPause = alwaysRedirectOnPause;
}

View File

@@ -20,7 +20,6 @@ import java.io.IOException;
import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ExpressionParser;
import org.springframework.binding.format.FormatterRegistry;
import org.springframework.core.io.ResourceLoader;
import org.springframework.webflow.engine.builder.ViewFactoryCreator;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;
@@ -36,7 +35,7 @@ import org.springframework.webflow.execution.ViewFactory;
class MockViewFactoryCreator implements ViewFactoryCreator {
public ViewFactory createViewFactory(Expression viewIdExpression, ExpressionParser expressionParser,
FormatterRegistry formatterRegistry, ResourceLoader resourceLoader) {
FormatterRegistry formatterRegistry) {
return new MockViewFactory(viewIdExpression);
}

View File

@@ -19,9 +19,11 @@ import org.springframework.webflow.config.FlowDefinitionResource;
import org.springframework.webflow.config.FlowDefinitionResourceFactory;
import org.springframework.webflow.core.collection.AttributeMap;
import org.springframework.webflow.definition.FlowDefinition;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.engine.builder.FlowAssembler;
import org.springframework.webflow.engine.builder.FlowBuilder;
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
import org.springframework.webflow.engine.impl.FlowExecutionImplFactory;
import org.springframework.webflow.execution.FlowExecutionListener;
import org.springframework.webflow.execution.factory.StaticFlowExecutionListenerLoader;
@@ -39,7 +41,7 @@ public abstract class AbstractExternalizedFlowExecutionTests extends AbstractFlo
/**
* The cached flow definition.
*/
private static FlowDefinition cachedFlowDefinition;
private static Flow cachedFlowDefinition;
/**
* The flag indicating if the flow definition built from an externalized resource as part of this test should be
@@ -52,6 +54,11 @@ public abstract class AbstractExternalizedFlowExecutionTests extends AbstractFlo
*/
private FlowDefinitionResourceFactory resourceFactory = new FlowDefinitionResourceFactory();
/**
* Private flow builder context object.
*/
private MockFlowBuilderContext flowBuilderContext;
/**
* Constructs a default externalized flow execution test.
* @see #setName(String)
@@ -128,9 +135,6 @@ public abstract class AbstractExternalizedFlowExecutionTests extends AbstractFlo
return resourceFactory;
}
/**
* Returns the flow definition being tested.
*/
protected final FlowDefinition getFlowDefinition() {
if (isCacheFlowDefinition() && cachedFlowDefinition != null) {
return cachedFlowDefinition;
@@ -142,6 +146,14 @@ public abstract class AbstractExternalizedFlowExecutionTests extends AbstractFlo
return flow;
}
/**
* Returns the flow definition being tested as a {@link Flow} implementation. Useful if you need to do specific
* assertions against the configuration of the implementation.
*/
protected final Flow getFlow() {
return (Flow) getFlowDefinition();
}
/**
* Factory method to assemble a flow definition from a resource. Called by {@link #getFlowDefinition()} to create
* the "main" flow to test. May also be called by subclasses to create subflow definitions whose executions should
@@ -150,22 +162,33 @@ public abstract class AbstractExternalizedFlowExecutionTests extends AbstractFlo
*/
protected final Flow buildFlow() {
FlowDefinitionResource resource = getResource(getResourceFactory());
MockFlowBuilderContext builderContext = new MockFlowBuilderContext(resource.getId(), resource.getAttributes());
configureFlowBuilderContext(builderContext);
flowBuilderContext = new MockFlowBuilderContext(resource.getId(), resource.getAttributes());
configureFlowBuilderContext(flowBuilderContext);
FlowBuilder builder = createFlowBuilder(resource);
FlowAssembler assembler = new FlowAssembler(builder, builderContext);
FlowAssembler assembler = new FlowAssembler(builder, flowBuilderContext);
return assembler.assembleFlow();
}
/**
* Subclasses may override this hook to customize the builder context for the flow being tested. Useful for
* registering mock subflows or other builder services. By default, this method does nothing.
* registering mock subflows or other {@link FlowBuilderServices flow builder services}. By default, this method
* does nothing.
* @param builderContext the mock flow builder context to configure
*/
protected void configureFlowBuilderContext(MockFlowBuilderContext builderContext) {
}
/**
* Returns a reference to the flow definition registry used by the flow being tested to load subflows. Allows late
* registration of dependent subflows on a per test-case basis. This is an alternative to registering such subflows
* upfront in {@link #configureFlowBuilderContext(MockFlowBuilderContext)}.
* @return the flow definition registry
*/
protected FlowDefinitionRegistry getFlowDefinitionRegistry() {
return (FlowDefinitionRegistry) flowBuilderContext.getFlowDefinitionLocator();
}
/**
* Get the resource defining the flow to be tested.
* @param resourceFactory a helper for constructing the resource to be tested

View File

@@ -21,7 +21,9 @@ import org.springframework.util.Assert;
import org.springframework.webflow.context.ExternalContext;
import org.springframework.webflow.core.collection.MutableAttributeMap;
import org.springframework.webflow.definition.FlowDefinition;
import org.springframework.webflow.engine.impl.FlowExecutionImpl;
import org.springframework.webflow.engine.impl.FlowExecutionImplFactory;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.FlowExecution;
import org.springframework.webflow.execution.FlowExecutionException;
import org.springframework.webflow.execution.FlowExecutionFactory;
@@ -65,6 +67,11 @@ public abstract class AbstractFlowExecutionTests extends TestCase {
*/
private FlowExecution flowExecution;
/**
* The outcome that was reached when the flow ends; initially null.
*/
private Event flowExecutionOutcome;
/**
* Constructs a default flow execution test.
* @see #setName(String)
@@ -114,6 +121,9 @@ public abstract class AbstractFlowExecutionTests extends TestCase {
protected void startFlow(MutableAttributeMap input, ExternalContext context) throws FlowExecutionException {
flowExecution = getFlowExecutionFactory().createFlowExecution(getFlowDefinition());
flowExecution.start(input, context);
if (flowExecution.hasEnded()) {
flowExecutionOutcome = flowExecution.getOutcome();
}
}
/**
@@ -126,6 +136,21 @@ public abstract class AbstractFlowExecutionTests extends TestCase {
Assert.state(flowExecution != null, "The flow execution to test is [null]; "
+ "you must start the flow execution before you can resume it!");
flowExecution.resume(context);
if (flowExecution.hasEnded()) {
flowExecutionOutcome = flowExecution.getOutcome();
}
}
/**
* Sets the current state of the flow execution being tested. If the execution has not been started, it will be
* created and activated.
* @param stateId the state id
*/
protected void setCurrentState(String stateId) {
if (flowExecution == null) {
flowExecution = getFlowExecutionFactory().createFlowExecution(getFlowDefinition());
}
((FlowExecutionImpl) flowExecution).setCurrentState(stateId);
}
// convenience accessors
@@ -136,40 +161,39 @@ public abstract class AbstractFlowExecutionTests extends TestCase {
* @throws IllegalStateException the execution has not been started
*/
protected FlowExecution getFlowExecution() throws IllegalStateException {
Assert.state(flowExecution != null,
"The flow execution to test is [null]; you must start the flow execution before you can access it!");
return flowExecution;
}
/**
* Returns the attribute in flash scope. Flash-scoped attributes are cleared on the next view rendering.
* @param attributeName the name of the attribute
* @return the attribute value
* Returns the flow execution outcome that was reached.
* @return the flow execution outcome, or null if the flow execution has not ended
*/
protected Object getFlashAttribute(String attributeName) {
return getFlowExecution().getFlashScope().get(attributeName);
protected Event getFlowExecutionOutcome() {
return flowExecutionOutcome;
}
/**
* Returns the required attribute in flash scope; asserts the attribute is present. Flash-scoped attributes are
* cleared on the next view rendering.
* @param attributeName the name of the attribute
* @return the attribute value
* @throws IllegalStateException if the attribute was not present
* Returns view scope.
* @return view scope
*/
protected Object getRequiredFlashAttribute(String attributeName) throws IllegalStateException {
return getFlowExecution().getFlashScope().getRequired(attributeName);
protected MutableAttributeMap getViewScope() throws IllegalStateException {
return getFlowExecution().getActiveSession().getViewScope();
}
/**
* Returns the required attribute in flash scope; asserts the attribute is present and of the correct type.
* Flash-scoped attributes are cleared on the next view rendering.
* @param attributeName the name of the attribute
* @return the attribute value
* @throws IllegalStateException if the attribute was not present or was of the wrong type
* Returns flow scope.
* @return flow scope
*/
protected Object getRequiredFlashAttribute(String attributeName, Class requiredType) throws IllegalStateException {
return getFlowExecution().getFlashScope().getRequired(attributeName, requiredType);
protected MutableAttributeMap getFlowScope() throws IllegalStateException {
return getFlowExecution().getActiveSession().getScope();
}
/**
* Returns conversation scope.
* @return conversation scope
*/
protected MutableAttributeMap getConversationScope() throws IllegalStateException {
return getFlowExecution().getConversationScope();
}
/**
@@ -290,8 +314,15 @@ public abstract class AbstractFlowExecutionTests extends TestCase {
* Assert that the entire flow execution has ended; that is, it is no longer active.
*/
protected void assertFlowExecutionEnded() {
assertTrue("The flow execution is still active but it should have ended", getFlowExecution().hasStarted()
&& !getFlowExecution().isActive());
assertTrue("The flow execution is still active but it should have ended", getFlowExecution().hasEnded());
}
/**
* Assert that the entire flow execution has ended; that is, it is no longer active.
*/
protected void assertFlowExecutionOutcomeEquals(String outcome) {
assertNotNull("There has been no flow execution outcome", flowExecutionOutcome);
assertEquals("The flow execution outcome is wrong", flowExecutionOutcome.getId(), outcome);
}
/**

View File

@@ -10,7 +10,6 @@ import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ExpressionParser;
import org.springframework.binding.format.FormatterRegistry;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ResourceLoader;
import org.springframework.webflow.engine.builder.ViewFactoryCreator;
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
import org.springframework.webflow.execution.ViewFactory;
@@ -44,7 +43,7 @@ public class FlowBuilderServicesBeanDefinitionParserTests extends TestCase {
public static class TestViewFactoryCreator implements ViewFactoryCreator {
public ViewFactory createViewFactory(Expression viewIdExpression, ExpressionParser expressionParser,
FormatterRegistry formatterRegistry, ResourceLoader resourceLoader) {
FormatterRegistry formatterRegistry) {
throw new UnsupportedOperationException("Auto-generated method stub");
}

View File

@@ -193,7 +193,7 @@ public class FlowTests extends TestCase {
public void restoreReferences(Object value, RequestContext context) {
}
}, true));
}));
flow.start(context, new LocalAttributeMap());
context.getFlowScope().getRequired("var1", ArrayList.class);
}
@@ -202,7 +202,8 @@ public class FlowTests extends TestCase {
DefaultMapper attributeMapper = new DefaultMapper();
ExpressionParser parser = DefaultExpressionParserFactory.getExpressionParser();
Expression x = parser.parseExpression("attr", new FluentParserContext().evaluate(AttributeMap.class));
Expression y = parser.parseExpression("flowScope.attr", new FluentParserContext().evaluate(RequestContext.class));
Expression y = parser.parseExpression("flowScope.attr", new FluentParserContext()
.evaluate(RequestContext.class));
attributeMapper.addMapping(new DefaultMapping(x, y));
flow.setInputMapper(attributeMapper);
MockRequestControlContext context = new MockRequestControlContext(flow);
@@ -216,7 +217,8 @@ public class FlowTests extends TestCase {
DefaultMapper attributeMapper = new DefaultMapper();
ExpressionParser parser = DefaultExpressionParserFactory.getExpressionParser();
Expression x = parser.parseExpression("attr", new FluentParserContext().evaluate(AttributeMap.class));
Expression y = parser.parseExpression("flowScope.attr", new FluentParserContext().evaluate(RequestContext.class));
Expression y = parser.parseExpression("flowScope.attr", new FluentParserContext()
.evaluate(RequestContext.class));
attributeMapper.addMapping(new DefaultMapping(x, y));
flow.setInputMapper(attributeMapper);
MockRequestControlContext context = new MockRequestControlContext(flow);
@@ -304,7 +306,8 @@ public class FlowTests extends TestCase {
public void testEndWithOutputMapper() {
DefaultMapper attributeMapper = new DefaultMapper();
ExpressionParser parser = DefaultExpressionParserFactory.getExpressionParser();
Expression x = parser.parseExpression("flowScope.attr", new FluentParserContext().evaluate(RequestContext.class));
Expression x = parser.parseExpression("flowScope.attr", new FluentParserContext()
.evaluate(RequestContext.class));
Expression y = parser.parseExpression("attr", new FluentParserContext().evaluate(MutableAttributeMap.class));
attributeMapper.addMapping(new DefaultMapping(x, y));
flow.setOutputMapper(attributeMapper);

View File

@@ -17,7 +17,7 @@ public class FlowVariableTests extends TestCase {
public void restoreReferences(Object value, RequestContext context) {
}
}, true);
});
MockRequestContext context = new MockRequestContext();
var.create(context);
assertEquals("bar", context.getFlowScope().get("foo"));
@@ -31,7 +31,7 @@ public class FlowVariableTests extends TestCase {
public void restoreReferences(Object value, RequestContext context) {
}
}, true);
});
MockRequestContext context = new MockRequestContext();
var.create(context);
assertEquals("bar", context.getFlowScope().get("foo"));
@@ -39,36 +39,6 @@ public class FlowVariableTests extends TestCase {
assertFalse(context.getFlowScope().contains("foo"));
}
public void testCreateConversationVariable() {
FlowVariable var = new FlowVariable("foo", new VariableValueFactory() {
public Object createInitialValue(RequestContext context) {
return "bar";
}
public void restoreReferences(Object value, RequestContext context) {
}
}, false);
MockRequestContext context = new MockRequestContext();
var.create(context);
assertEquals("bar", context.getConversationScope().get("foo"));
}
public void testDestroyConversationVariable() {
FlowVariable var = new FlowVariable("foo", new VariableValueFactory() {
public Object createInitialValue(RequestContext context) {
return "bar";
}
public void restoreReferences(Object value, RequestContext context) {
}
}, false);
MockRequestContext context = new MockRequestContext();
var.create(context);
assertEquals("bar", context.getConversationScope().get("foo"));
var.destroy(context);
assertFalse(context.getConversationScope().contains("foo"));
}
public void testRestoreVariable() {
FlowVariable var = new FlowVariable("foo", new VariableValueFactory() {
public Object createInitialValue(RequestContext context) {
@@ -79,11 +49,11 @@ public class FlowVariableTests extends TestCase {
restoreCalled = true;
assertEquals("bar", value);
}
}, false);
});
MockRequestContext context = new MockRequestContext();
var.create(context);
var.restore(context);
assertEquals("bar", context.getConversationScope().get("foo"));
assertEquals("bar", context.getFlowScope().get("foo"));
assertTrue(restoreCalled);
}

View File

@@ -262,14 +262,9 @@ public class FlowModelFlowBuilderTests extends TestCase {
public void testFlowVariable() {
model.addVar(new VarModel("flow-foo", "org.springframework.webflow.TestBean"));
VarModel var = new VarModel("conversation-foo", "org.springframework.webflow.TestBean");
model.addVar(var);
model.addEndState(new EndStateModel("end"));
Flow flow = getFlow(model);
assertEquals("flow-foo", flow.getVariable("flow-foo").getName());
assertEquals(false, flow.getVariable("flow-foo").isLocal());
assertEquals("conversation-foo", flow.getVariables()[1].getName());
assertEquals(false, flow.getVariables()[1].isLocal());
}
public void testViewStateVariable() {
@@ -325,6 +320,13 @@ public class FlowModelFlowBuilderTests extends TestCase {
assertEquals("end", flow.getStartState().getId());
}
public void testResourceBackedFlowBuilderWithMessages() {
ClassPathResource resource = new ClassPathResource("resources/flow.xml", XmlFlowModelBuilderTests.class);
Flow flow = getFlow(resource);
assertNotNull(flow.getApplicationContext());
assertEquals("bar", flow.getApplicationContext().getMessage("foo", null, null));
}
public void testAbstractFlow() {
model.setAbstract("true");
try {

View File

@@ -0,0 +1,7 @@
<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">
<end-state id="end"/>
</flow>

View File

@@ -47,6 +47,12 @@ public class FlowExecutionImplFactoryTests extends TestCase {
private boolean getKeyCalled;
private boolean updateSnapshotCalled;
private boolean removeSnapshotCalled;
private boolean removeAllSnapshotsCalled;
public void setUp() {
flowDefinition = new Flow("flow");
new EndState(flowDefinition, "end");
@@ -93,6 +99,9 @@ public class FlowExecutionImplFactoryTests extends TestCase {
State state = new State(flowDefinition, "state") {
protected void doEnter(RequestControlContext context) throws FlowExecutionException {
context.assignFlowExecutionKey();
context.updateCurrentFlowExecutionSnapshot();
context.removeCurrentFlowExecutionSnapshot();
context.removeAllFlowExecutionSnapshots();
}
};
flowDefinition.setStartState(state);
@@ -101,10 +110,26 @@ public class FlowExecutionImplFactoryTests extends TestCase {
getKeyCalled = true;
return null;
}
public void removeAllFlowExecutionSnapshots(FlowExecution execution) {
removeAllSnapshotsCalled = true;
}
public void removeFlowExecutionSnapshot(FlowExecution execution) {
removeSnapshotCalled = true;
}
public void updateFlowExecutionSnapshot(FlowExecution execution) {
updateSnapshotCalled = true;
}
});
FlowExecution execution = factory.createFlowExecution(flowDefinition);
execution.start(null, new MockExternalContext());
assertTrue(getKeyCalled);
assertTrue(removeAllSnapshotsCalled);
assertTrue(removeSnapshotCalled);
assertTrue(updateSnapshotCalled);
assertNull(execution.getKey());
}
}

View File

@@ -30,6 +30,15 @@ public class FlowExecutionStateRestorerImplTests extends TestCase {
public FlowExecutionKey getKey(FlowExecution execution) {
return newKey;
}
public void removeAllFlowExecutionSnapshots(FlowExecution execution) {
}
public void removeFlowExecutionSnapshot(FlowExecution execution) {
}
public void updateFlowExecutionSnapshot(FlowExecution execution) {
}
};
protected void setUp() {

View File

@@ -0,0 +1,7 @@
<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">
<end-state id="end"/>
</flow>

View File

@@ -25,22 +25,40 @@ import org.springframework.web.servlet.ViewResolver;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.View;
import org.springframework.webflow.execution.ViewFactory;
import org.springframework.webflow.mvc.view.MvcViewFactoryCreator;
import org.springframework.webflow.test.GeneratedFlowExecutionKey;
import org.springframework.webflow.test.MockExternalContext;
import org.springframework.webflow.test.MockRequestContext;
public class MvcViewFactoryTests extends TestCase {
private MvcViewFactoryCreator creator;
private StaticApplicationContext context;
private StaticApplicationContext applicationContext;
protected void setUp() {
creator = new MvcViewFactoryCreator();
context = new StaticApplicationContext();
applicationContext = new StaticApplicationContext();
applicationContext.refresh();
}
public void testGetViewNoFlowApplicationContext() {
Expression viewId = new StaticExpression("flowrelativeview.jsp");
InternalFlowResourceMvcViewFactory factory = new InternalFlowResourceMvcViewFactory(viewId, null, null);
MockRequestContext context = new MockRequestContext();
try {
factory.getView(context);
fail("Expected illegal state");
} catch (IllegalStateException e) {
// expected;
}
}
public void testGetViewNoFlowApplicationContextAbsolutePath() {
Expression viewId = new StaticExpression("/absoluteview.jsp");
InternalFlowResourceMvcViewFactory factory = new InternalFlowResourceMvcViewFactory(viewId, null, null);
MockRequestContext context = new MockRequestContext();
assertNotNull(factory.getView(context));
}
public void testNoResolversGetResource() throws Exception {
creator.setApplicationContext(context);
ResourceLoader viewResourceLoader = new ResourceLoader() {
public ClassLoader getClassLoader() {
return ClassUtils.getDefaultClassLoader();
@@ -50,9 +68,11 @@ public class MvcViewFactoryTests extends TestCase {
return new TestContextResource("/parent/" + name);
}
};
applicationContext.setResourceLoader(viewResourceLoader);
Expression viewId = new StaticExpression("myview.jsp");
ViewFactory viewFactory = creator.createViewFactory(viewId, null, null, viewResourceLoader);
ViewFactory viewFactory = creator.createViewFactory(viewId, null, null);
MockRequestContext context = new MockRequestContext();
context.getRootFlow().setApplicationContext(applicationContext);
MockExternalContext externalContext = new MockExternalContext();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
@@ -68,10 +88,9 @@ public class MvcViewFactoryTests extends TestCase {
public void testViewResolversGetResource() throws Exception {
MockViewResolver viewResolver = new MockViewResolver("myview");
creator.setApplicationContext(context);
creator.setViewResolvers(Collections.singletonList(viewResolver));
Expression viewId = new StaticExpression("myview");
ViewFactory viewFactory = creator.createViewFactory(viewId, null, null, null);
ViewFactory viewFactory = creator.createViewFactory(viewId, null, null);
MockRequestContext context = new MockRequestContext();
MockExternalContext externalContext = new MockExternalContext();
MockHttpServletRequest request = new MockHttpServletRequest();
@@ -87,7 +106,6 @@ public class MvcViewFactoryTests extends TestCase {
}
public void testRestoreView() throws Exception {
creator.setApplicationContext(context);
ResourceLoader viewResourceLoader = new ResourceLoader() {
public ClassLoader getClassLoader() {
return ClassUtils.getDefaultClassLoader();
@@ -97,9 +115,11 @@ public class MvcViewFactoryTests extends TestCase {
return new TestContextResource("/parent/" + name);
}
};
applicationContext.setResourceLoader(viewResourceLoader);
Expression viewId = new StaticExpression("myview.jsp");
ViewFactory viewFactory = creator.createViewFactory(viewId, null, null, viewResourceLoader);
ViewFactory viewFactory = creator.createViewFactory(viewId, null, null);
MockRequestContext context = new MockRequestContext();
context.getRootFlow().setApplicationContext(applicationContext);
MockExternalContext externalContext = new MockExternalContext();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
@@ -119,7 +139,6 @@ public class MvcViewFactoryTests extends TestCase {
}
public void testRestoreViewButtonEventIdFormat() throws Exception {
creator.setApplicationContext(context);
ResourceLoader viewResourceLoader = new ResourceLoader() {
public ClassLoader getClassLoader() {
return ClassUtils.getDefaultClassLoader();
@@ -129,9 +148,11 @@ public class MvcViewFactoryTests extends TestCase {
return new TestContextResource("/parent/" + name);
}
};
applicationContext.setResourceLoader(viewResourceLoader);
Expression viewId = new StaticExpression("myview.jsp");
ViewFactory viewFactory = creator.createViewFactory(viewId, null, null, viewResourceLoader);
ViewFactory viewFactory = creator.createViewFactory(viewId, null, null);
MockRequestContext context = new MockRequestContext();
context.getRootFlow().setApplicationContext(applicationContext);
MockExternalContext externalContext = new MockExternalContext();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();

View File

@@ -85,7 +85,7 @@ public class MvcViewTests extends TestCase {
assertNotNull(bm);
assertEquals(null, bm.getFieldValue("stringProperty"));
assertEquals("3", bm.getFieldValue("integerProperty"));
assertEquals("Jan 1, 2008", bm.getFieldValue("dateProperty"));
assertEquals("1/1/08", bm.getFieldValue("dateProperty"));
}
public void testResumeNoEvent() throws Exception {
@@ -121,7 +121,7 @@ public class MvcViewTests extends TestCase {
context.putRequestParameter("_eventId", "submit");
context.putRequestParameter("stringProperty", "foo");
context.putRequestParameter("integerProperty", "5");
context.putRequestParameter("dateProperty", "Jan 1, 2007");
context.putRequestParameter("dateProperty", "1/1/2007");
BindBean bindBean = new BindBean();
StaticExpression modelObject = new StaticExpression(bindBean);
modelObject.setExpressionString("bindBean");