diff --git a/org.springframework.context/src/main/java/org/springframework/conversation/manager/AbstractConversationStore.java b/org.springframework.context/src/main/java/org/springframework/conversation/manager/AbstractConversationStore.java
new file mode 100644
index 000000000000..e49d8d37b490
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/conversation/manager/AbstractConversationStore.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2002-2008 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.conversation.manager;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.conversation.Conversation;
+
+/**
+ * An abstract base implementation for the {@link ConversationStore} interface
+ * used by the {@link ConversationManager} to register conversation objects
+ * within. This implementation supports the basic mechanism and exposes two
+ * methods to be implemented by concrete stores to provide the conversation map
+ * and optionally a fallback map used, if the default one is not reachable. This
+ * could be the case, if the default one is session scoped and the conversation
+ * management is used from within a non web thread so the fallback map could be
+ * a thread attached map using a {@link ThreadLocal} for instance.
+ *
+ * @author Micha Kiener
+ * @since 3.1
+ */
+public abstract class AbstractConversationStore implements ConversationStore {
+ /**
+ * Flag indicating whether to use the fallback conversation map, if the
+ * default one is not reachable, see {@link #getConversationMapSafe()} for
+ * more information. Default is true
.
+ */
+ private boolean useFallbackMap;
+
+ /**
+ * Default constructor, turning the fallback map on by default.
+ */
+ public AbstractConversationStore() {
+ useFallbackMap = true;
+ }
+
+ /**
+ * @see org.springframework.conversation.manager.ConversationStore#getConversation(java.lang.String)
+ */
+ public Conversation getConversation(String id) {
+ Map map = getConversationMapSafe();
+ return map.get(id);
+ }
+
+ /**
+ * @see org.springframework.conversation.manager.ConversationStore#getConversations()
+ */
+ public List getConversations() {
+ Map map = getConversationMapSafe();
+ return new ArrayList(map.values());
+ }
+
+ /**
+ * @see org.springframework.conversation.manager.ConversationStore#registerConversation(org.springframework.conversation.Conversation)
+ */
+ public void registerConversation(Conversation conversation) {
+ Map map = getConversationMapSafe();
+ map.put(conversation.getId(), conversation);
+ }
+
+ /**
+ * @see org.springframework.conversation.manager.ConversationStore#removeConversation(java.lang.String)
+ */
+ public Conversation removeConversation(String id) {
+ Map map = getConversationMapSafe();
+ return map.remove(id);
+ }
+
+ /**
+ * @see org.springframework.conversation.manager.ConversationStore#size()
+ */
+ public int size() {
+ return getConversationMapSafe().size();
+ }
+
+ /**
+ * Internal method returning the conversation map in a safe way as the
+ * method {@link #getConversationMap()} could actually raise an exception,
+ * if the method is injected and the map is currently not available (for
+ * instance if the map is in session scope and this method is invoked from a
+ * non web thread). If the fallback mechanism is turned on (as done by
+ * default), in this case the fallback map as being returned by
+ * {@link #getConversationMapFallback()} is returned in order to use the
+ * conversation management in a non web thread as well (for instance for
+ * asynchronous tasks or batch stuff).
+ *
+ * @return the conversation map to be used currently
+ */
+ protected Map getConversationMapSafe() {
+ try {
+ return getConversationMap();
+ } catch (Exception e) {
+ // if the map is not reachable and fallback is on, return the
+ // fallback map
+ if (isUseFallbackMap()) {
+ return getConversationMapFallback();
+ }
+ }
+
+ throw new IllegalAccessError("The conversation map to store conversations in is currently not available.");
+ }
+
+ /**
+ * Returns the map used to store the conversations in using their id as the
+ * key. This method can either be injected by providing a map implementation
+ * in session or window scope or might be implemented.
+ *
+ * @return the map for storing the conversations
+ */
+ public abstract Map getConversationMap();
+
+ /**
+ * Returns the conversation map used if the default one returned by
+ * {@link #getConversationMap()} is not available. This could be the case,
+ * if the default one is session based and the conversation management is
+ * used from within a non web thread. This method must always be able to
+ * provide a map, if the conversation management should be available in any
+ * situation.
+ *
+ * @return the fallback conversation map
+ */
+ public abstract Map getConversationMapFallback();
+
+ /**
+ * @param useFallbackMap true
, if the fallback map should be
+ * used in the case the default one is not reachable, false
to
+ * raise an exception in that case
+ */
+ public void setUseFallbackMap(boolean useFallbackMap) {
+ this.useFallbackMap = useFallbackMap;
+ }
+
+ /**
+ * @return the true
, if the fallback map should be used in the
+ * case the default one is not reachable
+ */
+ public boolean isUseFallbackMap() {
+ return useFallbackMap;
+ }
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/conversation/manager/ConversationStore.java b/org.springframework.context/src/main/java/org/springframework/conversation/manager/ConversationStore.java
index acfc78666662..56eb24761635 100644
--- a/org.springframework.context/src/main/java/org/springframework/conversation/manager/ConversationStore.java
+++ b/org.springframework.context/src/main/java/org/springframework/conversation/manager/ConversationStore.java
@@ -76,7 +76,8 @@ public interface ConversationStore {
/**
* Returns a list of available conversations in the current store which have
* been registered using the {@link #registerConversation(Conversation)}
- * method.
+ * method. The list being returned is not backed by the store so it can be
+ * modified safely from outside.
*
* @return the list of available conversations or an empty list, never
* null