diff --git a/spring-binding/src/main/java/org/springframework/binding/message/DefaultMessageContext.java b/spring-binding/src/main/java/org/springframework/binding/message/DefaultMessageContext.java
index b74306e0..923fa006 100644
--- a/spring-binding/src/main/java/org/springframework/binding/message/DefaultMessageContext.java
+++ b/spring-binding/src/main/java/org/springframework/binding/message/DefaultMessageContext.java
@@ -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);
}
diff --git a/spring-binding/src/main/java/org/springframework/binding/message/DefaultMessageContextFactory.java b/spring-binding/src/main/java/org/springframework/binding/message/DefaultMessageContextFactory.java
index 2ec9d3f0..feed725d 100644
--- a/spring-binding/src/main/java/org/springframework/binding/message/DefaultMessageContextFactory.java
+++ b/spring-binding/src/main/java/org/springframework/binding/message/DefaultMessageContextFactory.java
@@ -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;
+ }
+ }
}
\ No newline at end of file
diff --git a/spring-binding/src/main/java/org/springframework/binding/message/DefaultMessageResolver.java b/spring-binding/src/main/java/org/springframework/binding/message/DefaultMessageResolver.java
new file mode 100644
index 00000000..103cfa72
--- /dev/null
+++ b/spring-binding/src/main/java/org/springframework/binding/message/DefaultMessageResolver.java
@@ -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();
+ }
+}
\ No newline at end of file
diff --git a/spring-binding/src/main/java/org/springframework/binding/message/MessageBuilder.java b/spring-binding/src/main/java/org/springframework/binding/message/MessageBuilder.java
index bc6596e8..e768a3d2 100644
--- a/spring-binding/src/main/java/org/springframework/binding/message/MessageBuilder.java
+++ b/spring-binding/src/main/java/org/springframework/binding/message/MessageBuilder.java
@@ -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();
+ }
+
}
}
diff --git a/spring-binding/src/test/resources/log4j.xml b/spring-binding/src/test/resources/log4j.xml
new file mode 100644
index 00000000..767b96d6
--- /dev/null
+++ b/spring-binding/src/test/resources/log4j.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-faces/src/main/java/org/springframework/faces/model/ManySelectionTrackingListDataModel.java b/spring-faces/src/main/java/org/springframework/faces/model/ManySelectionTrackingListDataModel.java
index af38353f..70494509 100644
--- a/spring-faces/src/main/java/org/springframework/faces/model/ManySelectionTrackingListDataModel.java
+++ b/spring-faces/src/main/java/org/springframework/faces/model/ManySelectionTrackingListDataModel.java
@@ -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;
}
diff --git a/spring-faces/src/main/java/org/springframework/faces/model/OneSelectionTrackingListDataModel.java b/spring-faces/src/main/java/org/springframework/faces/model/OneSelectionTrackingListDataModel.java
index 757d66c9..788bb44c 100644
--- a/spring-faces/src/main/java/org/springframework/faces/model/OneSelectionTrackingListDataModel.java
+++ b/spring-faces/src/main/java/org/springframework/faces/model/OneSelectionTrackingListDataModel.java
@@ -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;
}
diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/JsfViewFactory.java b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfViewFactory.java
index 306570a2..c8ae6fdf 100644
--- a/spring-faces/src/main/java/org/springframework/faces/webflow/JsfViewFactory.java
+++ b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfViewFactory.java
@@ -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();
}
}
diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/JsfViewFactoryCreator.java b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfViewFactoryCreator.java
index 56725ffc..060ac6b4 100644
--- a/spring-faces/src/main/java/org/springframework/faces/webflow/JsfViewFactoryCreator.java
+++ b/spring-faces/src/main/java/org/springframework/faces/webflow/JsfViewFactoryCreator.java
@@ -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) {
diff --git a/spring-faces/src/test/java/org/springframework/faces/webflow/JsfFinalResponseActionTests.java b/spring-faces/src/test/java/org/springframework/faces/webflow/JsfFinalResponseActionTests.java
index 690f7270..adf32a69 100644
--- a/spring-faces/src/test/java/org/springframework/faces/webflow/JsfFinalResponseActionTests.java
+++ b/spring-faces/src/test/java/org/springframework/faces/webflow/JsfFinalResponseActionTests.java
@@ -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());
diff --git a/spring-faces/src/test/java/org/springframework/faces/webflow/JsfViewFactoryTests.java b/spring-faces/src/test/java/org/springframework/faces/webflow/JsfViewFactoryTests.java
index 5345abec..d5989bfc 100644
--- a/spring-faces/src/test/java/org/springframework/faces/webflow/JsfViewFactoryTests.java
+++ b/spring-faces/src/test/java/org/springframework/faces/webflow/JsfViewFactoryTests.java
@@ -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);
diff --git a/spring-webflow-samples/booking-faces/.classpath b/spring-webflow-samples/booking-faces/.classpath
index 5abb4eb7..35e84375 100755
--- a/spring-webflow-samples/booking-faces/.classpath
+++ b/spring-webflow-samples/booking-faces/.classpath
@@ -7,5 +7,6 @@
+
diff --git a/spring-webflow-samples/booking-faces/src/test/java/org/springframework/webflow/samples/booking/MainFlowExecutionTests.java b/spring-webflow-samples/booking-faces/src/test/java/org/springframework/webflow/samples/booking/MainFlowExecutionTests.java
index c8a4392a..463e7f86 100644
--- a/spring-webflow-samples/booking-faces/src/test/java/org/springframework/webflow/samples/booking/MainFlowExecutionTests.java
+++ b/spring-webflow-samples/booking-faces/src/test/java/org/springframework/webflow/samples/booking/MainFlowExecutionTests.java
@@ -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 bookings = new ArrayList();
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 hotels = new ArrayList();
- // 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 hotels = new ArrayList();
+ 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 hotels = new ArrayList();
+ 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");
+ }
}
diff --git a/spring-webflow-samples/booking-mvc/src/main/webapp/WEB-INF/hotels/booking/enterBookingDetails.jsp b/spring-webflow-samples/booking-mvc/src/main/webapp/WEB-INF/hotels/booking/enterBookingDetails.jsp
index 1521340b..cc6dfc05 100644
--- a/spring-webflow-samples/booking-mvc/src/main/webapp/WEB-INF/hotels/booking/enterBookingDetails.jsp
+++ b/spring-webflow-samples/booking-mvc/src/main/webapp/WEB-INF/hotels/booking/enterBookingDetails.jsp
@@ -45,7 +45,6 @@
-
@@ -54,7 +53,6 @@
-
@@ -84,7 +82,6 @@
-
@@ -93,7 +90,6 @@
-
diff --git a/spring-webflow-samples/booking-mvc/src/main/webapp/WEB-INF/hotels/booking/messages.properties b/spring-webflow-samples/booking-mvc/src/main/webapp/WEB-INF/hotels/booking/messages.properties
new file mode 100644
index 00000000..692328a7
--- /dev/null
+++ b/spring-webflow-samples/booking-mvc/src/main/webapp/WEB-INF/hotels/booking/messages.properties
@@ -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
\ No newline at end of file
diff --git a/spring-webflow-samples/booking-mvc/src/main/webapp/WEB-INF/web-application-config.xml b/spring-webflow-samples/booking-mvc/src/main/webapp/WEB-INF/web-application-config.xml
index 4a664641..f7b12c5f 100644
--- a/spring-webflow-samples/booking-mvc/src/main/webapp/WEB-INF/web-application-config.xml
+++ b/spring-webflow-samples/booking-mvc/src/main/webapp/WEB-INF/web-application-config.xml
@@ -1,7 +1,7 @@
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
+
+
+
+
diff --git a/spring-webflow-samples/booking-mvc/src/test/resources/log4j.properties b/spring-webflow-samples/booking-mvc/src/test/resources/log4j.properties
deleted file mode 100755
index 5ba9222c..00000000
--- a/spring-webflow-samples/booking-mvc/src/test/resources/log4j.properties
+++ /dev/null
@@ -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
\ No newline at end of file
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutionRepositoryType.java b/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutionRepositoryType.java
index da7fc663..988b78ed 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutionRepositoryType.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutionRepositoryType.java
@@ -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!
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorFactoryBean.java b/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorFactoryBean.java
index efc2f6a3..6f6e1a2b 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorFactoryBean.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorFactoryBean.java
@@ -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);
}
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/config/spring-webflow-config-2.0.xsd b/spring-webflow/src/main/java/org/springframework/webflow/config/spring-webflow-config-2.0.xsd
index 6661f713..189b7b64 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/config/spring-webflow-config-2.0.xsd
+++ b/spring-webflow/src/main/java/org/springframework/webflow/config/spring-webflow-config-2.0.xsd
@@ -313,16 +313,6 @@ of the browser back button. This is the default if not specified.
-
-
-
-
-
-
-
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistry.java b/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistry.java
index 2889f724..bf9877b5 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistry.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/definition/registry/FlowDefinitionRegistry.java
@@ -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);
+
}
\ No newline at end of file
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/FlowVariable.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/FlowVariable.java
index 59f98b54..a7af2eb2 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/FlowVariable.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/FlowVariable.java
@@ -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();
}
}
\ No newline at end of file
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/History.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/History.java
new file mode 100644
index 00000000..4eaa71fe
--- /dev/null
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/History.java
@@ -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);
+ }
+
+}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/RequestControlContext.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/RequestControlContext.java
index 31804409..d928b6fb 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/RequestControlContext.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/RequestControlContext.java
@@ -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
+ * onEvent() 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
- * onEvent() 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 end() 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
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java
index 462bbfaf..1d9c1ba2 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/ViewState.java
@@ -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);
}
}
\ No newline at end of file
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowArtifactFactory.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowArtifactFactory.java
index 5b28218e..119345d0 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowArtifactFactory.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowArtifactFactory.java
@@ -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;
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/ViewFactoryCreator.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/ViewFactoryCreator.java
index 6165de5f..6772440d 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/ViewFactoryCreator.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/ViewFactoryCreator.java
@@ -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.
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilder.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilder.java
index b22c2c08..3fcd99a4 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilder.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilder.java
@@ -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);
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImpl.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImpl.java
index a17e10ee..959d84eb 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImpl.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImpl.java
@@ -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();
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImplFactory.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImplFactory.java
index 3113ba59..24bcc778 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImplFactory.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImplFactory.java
@@ -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;
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImplServicesConfigurer.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImplServicesConfigurer.java
index 23abdb5e..2ace2c88 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImplServicesConfigurer.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/FlowExecutionImplServicesConfigurer.java
@@ -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()));
}
}
\ No newline at end of file
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/RequestControlContextImpl.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/RequestControlContextImpl.java
index d685debd..1ce4e502 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/RequestControlContextImpl.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/impl/RequestControlContextImpl.java
@@ -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);
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilder.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilder.java
index 731a9daf..32925694 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilder.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilder.java
@@ -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();
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/support/BeanFactoryVariableValueFactory.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/support/BeanFactoryVariableValueFactory.java
index 275a2707..bb48a0e7 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/support/BeanFactoryVariableValueFactory.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/support/BeanFactoryVariableValueFactory.java
@@ -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() {
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionKeyFactory.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionKeyFactory.java
index 7acb90f7..49022ab4 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionKeyFactory.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/FlowExecutionKeyFactory.java
@@ -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);
+
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/impl/DefaultFlowExecutionRepository.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/impl/DefaultFlowExecutionRepository.java
index ceed0ab8..7f441c27 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/impl/DefaultFlowExecutionRepository.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/impl/DefaultFlowExecutionRepository.java
@@ -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() {
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/impl/FlowExecutionContinuationGroup.java b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/impl/FlowExecutionContinuationGroup.java
index 3a29c1c5..f2f0a0d7 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/impl/FlowExecutionContinuationGroup.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/execution/repository/impl/FlowExecutionContinuationGroup.java
@@ -66,23 +66,20 @@ class FlowExecutionContinuationGroup implements Serializable {
/**
* Returns the continuation with the provided id, or null 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());
}
+
}
\ No newline at end of file
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/executor/FlowExecutorImpl.java b/spring-webflow/src/main/java/org/springframework/webflow/executor/FlowExecutorImpl.java
index e142d0ad..c75fc7d8 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/executor/FlowExecutorImpl.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/executor/FlowExecutorImpl.java
@@ -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);
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/PortletMvcView.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/PortletMvcView.java
index a0b50b04..d538afce 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/PortletMvcView.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/portlet/PortletMvcView.java
@@ -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);
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/ServletMvcView.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/ServletMvcView.java
index cb002a45..8334067a 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/ServletMvcView.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/servlet/ServletMvcView.java
@@ -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);
}
}
\ No newline at end of file
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/BindingModel.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/BindingModel.java
index 9b720ec2..4c1899ec 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/BindingModel.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/BindingModel.java
@@ -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");
+ }
+
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/InternalFlowResourceMvcViewFactory.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/InternalFlowResourceMvcViewFactory.java
index 8a22abd6..01c2c0c0 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/InternalFlowResourceMvcViewFactory.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/InternalFlowResourceMvcViewFactory.java
@@ -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 {
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MessageContextErrors.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MessageContextErrors.java
index 5eb95436..bb3f070a 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MessageContextErrors.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MessageContextErrors.java
@@ -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");
}
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MvcView.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MvcView.java
index 622c3a21..8adcac8e 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MvcView.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MvcView.java
@@ -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);
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MvcViewFactoryCreator.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MvcViewFactoryCreator.java
index bd99b05f..655854a2 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MvcViewFactoryCreator.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MvcViewFactoryCreator.java
@@ -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);
}
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/ViewResolvingMvcViewFactory.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/ViewResolvingMvcViewFactory.java
index 4dcfba06..be3c7e20 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/ViewResolvingMvcViewFactory.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/ViewResolvingMvcViewFactory.java
@@ -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) {
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/test/MockFlowExecutionKeyFactory.java b/spring-webflow/src/main/java/org/springframework/webflow/test/MockFlowExecutionKeyFactory.java
index 1a364d0e..ec4624e6 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/test/MockFlowExecutionKeyFactory.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/test/MockFlowExecutionKeyFactory.java
@@ -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) {
+ }
+
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/test/MockRequestControlContext.java b/spring-webflow/src/main/java/org/springframework/webflow/test/MockRequestControlContext.java
index f012b75e..5c946c09 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/test/MockRequestControlContext.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/test/MockRequestControlContext.java
@@ -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;
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/test/MockViewFactoryCreator.java b/spring-webflow/src/main/java/org/springframework/webflow/test/MockViewFactoryCreator.java
index c02cf8aa..bd9ec872 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/test/MockViewFactoryCreator.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/test/MockViewFactoryCreator.java
@@ -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);
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/test/execution/AbstractExternalizedFlowExecutionTests.java b/spring-webflow/src/main/java/org/springframework/webflow/test/execution/AbstractExternalizedFlowExecutionTests.java
index 3507a4b1..5fe31932 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/test/execution/AbstractExternalizedFlowExecutionTests.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/test/execution/AbstractExternalizedFlowExecutionTests.java
@@ -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
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/test/execution/AbstractFlowExecutionTests.java b/spring-webflow/src/main/java/org/springframework/webflow/test/execution/AbstractFlowExecutionTests.java
index 1e34e379..f684ed23 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/test/execution/AbstractFlowExecutionTests.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/test/execution/AbstractFlowExecutionTests.java
@@ -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);
}
/**
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/config/FlowBuilderServicesBeanDefinitionParserTests.java b/spring-webflow/src/test/java/org/springframework/webflow/config/FlowBuilderServicesBeanDefinitionParserTests.java
index 4d7f4013..69473c38 100644
--- a/spring-webflow/src/test/java/org/springframework/webflow/config/FlowBuilderServicesBeanDefinitionParserTests.java
+++ b/spring-webflow/src/test/java/org/springframework/webflow/config/FlowBuilderServicesBeanDefinitionParserTests.java
@@ -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");
}
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/FlowTests.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/FlowTests.java
index ee5629ab..258b8fb3 100644
--- a/spring-webflow/src/test/java/org/springframework/webflow/engine/FlowTests.java
+++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/FlowTests.java
@@ -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);
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/FlowVariableTests.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/FlowVariableTests.java
index e57a134f..4f8ee855 100644
--- a/spring-webflow/src/test/java/org/springframework/webflow/engine/FlowVariableTests.java
+++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/FlowVariableTests.java
@@ -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);
}
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilderTests.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilderTests.java
index 32822523..c691223e 100644
--- a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilderTests.java
+++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilderTests.java
@@ -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 {
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/model/resources/flow.xml b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/model/resources/flow.xml
new file mode 100644
index 00000000..00011745
--- /dev/null
+++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/model/resources/flow.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/model/resources/messages.properties b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/model/resources/messages.properties
new file mode 100644
index 00000000..74d0a43f
--- /dev/null
+++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/builder/model/resources/messages.properties
@@ -0,0 +1 @@
+foo=bar
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/FlowExecutionImplFactoryTests.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/FlowExecutionImplFactoryTests.java
index 373d9504..76fb5f1a 100644
--- a/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/FlowExecutionImplFactoryTests.java
+++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/FlowExecutionImplFactoryTests.java
@@ -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());
}
}
\ No newline at end of file
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/FlowExecutionStateRestorerImplTests.java b/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/FlowExecutionStateRestorerImplTests.java
index 4fda6f3d..76dfb570 100644
--- a/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/FlowExecutionStateRestorerImplTests.java
+++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/impl/FlowExecutionStateRestorerImplTests.java
@@ -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() {
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/resources/flow.xml b/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/resources/flow.xml
new file mode 100644
index 00000000..00011745
--- /dev/null
+++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/resources/flow.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/resources/messages.properties b/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/resources/messages.properties
new file mode 100644
index 00000000..74d0a43f
--- /dev/null
+++ b/spring-webflow/src/test/java/org/springframework/webflow/engine/model/builder/xml/resources/messages.properties
@@ -0,0 +1 @@
+foo=bar
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewFactoryTests.java b/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewFactoryTests.java
index 65c63824..63e6a88a 100644
--- a/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewFactoryTests.java
+++ b/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewFactoryTests.java
@@ -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();
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewTests.java b/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewTests.java
index ecf534aa..13abc353 100644
--- a/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewTests.java
+++ b/spring-webflow/src/test/java/org/springframework/webflow/mvc/view/MvcViewTests.java
@@ -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");