This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
28
spring-binding/src/test/resources/log4j.xml
Normal file
28
spring-binding/src/test/resources/log4j.xml
Normal 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>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
@@ -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" />
|
||||
|
||||
@@ -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
|
||||
@@ -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!
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
@@ -0,0 +1 @@
|
||||
foo=bar
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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>
|
||||
@@ -0,0 +1 @@
|
||||
foo=bar
|
||||
@@ -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();
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user