From c2e0e1e7d81fd69c8bc5d63d68d0c078709602b9 Mon Sep 17 00:00:00 2001 From: Keith Donald Date: Wed, 15 Aug 2007 17:29:49 +0000 Subject: [PATCH] RESOLVED - issue SWF-378: Session binding conversation containers cannot be used in a cluster http://opensource.atlassian.com/projects/spring/browse/SWF-378 --- spring-webflow/changelog.txt | 1 + .../SessionBindingConversationManager.java | 26 +++++++++++++------ ...essionBindingConversationManagerTests.java | 14 +++++++++- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/spring-webflow/changelog.txt b/spring-webflow/changelog.txt index dd4e75b9..ee88d3cc 100644 --- a/spring-webflow/changelog.txt +++ b/spring-webflow/changelog.txt @@ -13,6 +13,7 @@ Package org.springframework.webflow.engine Package org.springframework.webflow.execution * Made the Event class non-final to allow for extension (SWF-330). +* Now allow multiple conversation containers per session in a manner that supports use in a cluster (SWF-378). Changes in version 1.0.4 (13.06.2007) ------------------------------------- diff --git a/spring-webflow/src/main/java/org/springframework/webflow/conversation/impl/SessionBindingConversationManager.java b/spring-webflow/src/main/java/org/springframework/webflow/conversation/impl/SessionBindingConversationManager.java index 73b65fa9..dec721b2 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/conversation/impl/SessionBindingConversationManager.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/conversation/impl/SessionBindingConversationManager.java @@ -24,7 +24,6 @@ import org.springframework.webflow.conversation.ConversationId; import org.springframework.webflow.conversation.ConversationManager; import org.springframework.webflow.conversation.ConversationParameters; import org.springframework.webflow.core.collection.SharedAttributeMap; -import org.springframework.webflow.util.RandomGuid; import org.springframework.webflow.util.RandomGuidUidGenerator; import org.springframework.webflow.util.UidGenerator; @@ -44,10 +43,14 @@ public class SessionBindingConversationManager implements ConversationManager { private static final Log logger = LogFactory.getLog(SessionBindingConversationManager.class); /** - * Generate a unique key for the session attribute holding the conversation container managed by this conversation - * manager. + * The name of the session attribute that will hold the conversation container used by this conversation manager. + * + * To support multiple independent conversation containers in the same web application, for example, for use with + * multiple flow executors each configured with their own session-binding conversation manager, set this field's + * value to something unique. + * @see #setSessionKey(String) */ - private final String sessionKey = "webflow.conversation.container." + new RandomGuid().toString(); + private String sessionKey = "webflow.conversationContainer"; /** * The conversation uid generation strategy to use. @@ -62,7 +65,6 @@ public class SessionBindingConversationManager implements ConversationManager { /** * Returns the used generator for conversation ids. Defaults to {@link RandomGuidUidGenerator}. - * @since 1.0.1 */ public UidGenerator getConversationIdGenerator() { return conversationIdGenerator; @@ -77,7 +79,6 @@ public class SessionBindingConversationManager implements ConversationManager { /** * Returns the maximum number of allowed concurrent conversations. The default is 5. - * @since 1.0.1 */ public int getMaxConversations() { return maxConversations; @@ -91,14 +92,23 @@ public class SessionBindingConversationManager implements ConversationManager { } /** - * Returns the key this conversation manager uses to store conversation data in the session. The key is unique for - * this conversation manager instance. + * Returns the key this conversation manager uses to store conversation data in the session. * @return the session key */ public String getSessionKey() { return sessionKey; } + /** + * Sets the key this conversation manager uses to store conversation data in the session. If multiple session + * binding conversation managers are used in the same web application to back independent flow executors, this value + * should be unique among them. + * @param sessionKey the session key + */ + public void setSessionKey(String sessionKey) { + this.sessionKey = sessionKey; + } + public Conversation beginConversation(ConversationParameters conversationParameters) throws ConversationException { ConversationId conversationId = new SimpleConversationId(conversationIdGenerator.generateUid()); if (logger.isDebugEnabled()) { diff --git a/spring-webflow/src/test/java/org/springframework/webflow/conversation/impl/SessionBindingConversationManagerTests.java b/spring-webflow/src/test/java/org/springframework/webflow/conversation/impl/SessionBindingConversationManagerTests.java index 71d54787..93b63d60 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/conversation/impl/SessionBindingConversationManagerTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/conversation/impl/SessionBindingConversationManagerTests.java @@ -87,11 +87,15 @@ public class SessionBindingConversationManagerTests extends TestCase { conversation.putAttribute("testAttribute", "testValue"); ConversationId conversationId = conversation.getId(); ExternalContextHolder.setExternalContext(null); + // simulate write out of session byte[] passiveSession = passivate(externalContext.getSessionMap()); + // simulate start-up of server + conversationManager = new SessionBindingConversationManager(); String id = conversationId.toString(); conversationId = conversationManager.parseConversationId(id); + // simulate restore of session externalContext.setSessionMap(activate(passiveSession)); ExternalContextHolder.setExternalContext(externalContext); Conversation conversation2 = conversationManager.getConversation(conversationId); @@ -125,6 +129,14 @@ public class SessionBindingConversationManagerTests extends TestCase { assertNotNull(conversationManager.getConversation(conversation3.getId())); } + public void testCustomSessionKey() { + conversationManager.setSessionKey("foo"); + MockExternalContext context = new MockExternalContext(); + ExternalContextHolder.setExternalContext(context); + conversationManager.beginConversation(new ConversationParameters("test", "test", "test")); + assertNotNull(context.getSessionMap().get("foo")); + } + private byte[] passivate(SharedAttributeMap session) throws Exception { // session is serialized out ByteArrayOutputStream bout = new ByteArrayOutputStream(); @@ -138,4 +150,4 @@ public class SessionBindingConversationManagerTests extends TestCase { return (SharedAttributeMap) new ObjectInputStream(new ByteArrayInputStream(sessionData)).readObject(); } -} +} \ No newline at end of file