diff --git a/org.springframework.context/src/main/java/org/springframework/conversation/Conversation.java b/org.springframework.context/src/main/java/org/springframework/conversation/Conversation.java
index 0372cdbebe..e3041563b6 100644
--- a/org.springframework.context/src/main/java/org/springframework/conversation/Conversation.java
+++ b/org.springframework.context/src/main/java/org/springframework/conversation/Conversation.java
@@ -20,11 +20,10 @@ import java.util.List;
import java.util.Map;
import org.springframework.conversation.manager.ConversationManager;
-import org.springframework.conversation.scope.ConversationScope;
/**
* The interface for a conversation, the instance behind the
- * {@link ConversationScope}.
+ * {@link org.springframework.conversation.scope.ConversationScope}.
* Conversations are created through the {@link ConversationManager} which is
* the main API for conversation management unless the annotations are used. See
* {@link ConversationManager} for a more detailed description on how the
diff --git a/org.springframework.context/src/main/java/org/springframework/conversation/manager/ConversationManager.java b/org.springframework.context/src/main/java/org/springframework/conversation/manager/ConversationManager.java
index a92d17616f..b00313b636 100644
--- a/org.springframework.context/src/main/java/org/springframework/conversation/manager/ConversationManager.java
+++ b/org.springframework.context/src/main/java/org/springframework/conversation/manager/ConversationManager.java
@@ -26,105 +26,68 @@ import org.springframework.conversation.JoinMode;
import org.springframework.conversation.annotation.BeginConversation;
import org.springframework.conversation.annotation.Conversational;
import org.springframework.conversation.annotation.EndConversation;
-import org.springframework.conversation.scope.ConversationScope;
/**
- *
- * The interface to be implemented by any conversation manager. It is used by
- * the advice behind the conversational annotations like
- * {@link BeginConversation}, {@link EndConversation} and {@link Conversational}
- * but might be used for even fine grained access to the underlying conversation
- * management, if a initializer callback is needed for instance or if
- * conversation switching should be used. If used directly, the conversation
- * manager can be injected into any bean to be used as it is a singleton and
- * thread safe.
- *
- * Conversations are a good way to scope beans and attributes depending on
- * business logic boundary rather than a technical boundary of a scope like
- * session, request etc. Usually a conversation boundary is defined by the
- * starting point of a use case and ended accordingly or in other words a
- * conversation defines the boundary for a unit of work.
- *
- *
- * Here is a short description on how to use the conversation management:
- *
- * Starting a conversation
- * A conversation can be started in two different ways, either by placing the
- * {@link BeginConversation} annotation on a method defining the starting point
- * of the conversation (use case) or by invoking
- * {@link ConversationManager#beginConversation(boolean, JoinMode)} manually
- * through the conversation manager. The {@link JoinMode} declares on how a new
- * conversation is created (see its javadoc for more details on the different
- * join modes).
- *
- *
- * Ending a conversation
- * A current conversation is ended by either placing the {@link EndConversation}
- * annotation on the ending method or by manually invoke the
- * {@link ConversationManager#endCurrentConversation(ConversationEndingType)}
- * method. A current conversation might also be ended, if a new conversation is
- * started having {@link JoinMode#NEW} being declared where the current
- * conversation is ended silently and a new one is created. This might be the
- * obvious way to end a conversation, if the end of a use case is not always
- * forced to be invoked by a user.
- *
- *
- * Temporary conversations
- * A temporary conversation is automatically created, if the container is about
- * to create a bean having conversation scope but there is no current
- * conversation in progress, hence a new, temporary conversation is created. A
- * temporary conversation might be turned into a long running one by joining it
- * or manually invoke {@link Conversation#begin()}.
- *
- *
- * Initializing the conversation
- * If there is any need to initialize beans or entities while starting a new
- * conversation, the {@link ConversationInitializationCallback} might be used to
- * be invoked if the new conversation was started in order to initialize it.
- * This is done by providing such a callback to the
- * {@link ConversationManager#beginConversation(JoinMode, ConversationType, ConversationInitializationCallback...)}
- * method. The callback feature is not available through the annotation support
- * and hence is only available through the conversation manager API. Here is an
- * example on such an initializing feature; if a conversation is used in
- * conjunction of a JPA entity manager or Hibernate session, there might be
- * entity beans already loaded, cached or referenced within backing beans which
- * have to be merged into the entity manager or session in order to be used
- * within the conversation's work. So it would be possible to implement the
- * callback, merging the necessary entities used within the conversation into
- * the entity manager.
- *
- *
- * Listening to conversation events
- * If there is a need to listening to events of a conversation, add yourself as
- * a {@link ConversationListener} to a new {@link Conversation} as being
- * returned by this {@link ConversationManager}. The same goal can be achieved
- * by implementing the {@link ConversationListener} interface on a conversation
- * scoped bean which will be registered automatically to receive events.
- *
- *
- * Nesting conversations
- * Conversations might be nested either by inheriting the state of the parent (
- * {@link JoinMode#NESTED}) or by isolating its state from the parent (
- * {@link JoinMode#ISOLATED}). Ending a nested conversation automatically
- * switches back to its parent making it the current conversation.
- *
- *
- * Where are conversations stored?
- * Conversations are created by the {@link ConversationManager} and registered
- * within the {@link ConversationScope}. The scope handler is injected into the
- * manager by Spring (statically as both beans are singletons) and registered as
- * a custom scope. As the scope handler is a singleton, it needs a store where
- * conversations are stored within which is usually bound to the current session
- * or window of a user and represented by the {@link ConversationStore}
- * interface. The store is usually being injected into the scope handler using
- * method injection as the store has a narrower scope than the scope handler. In
- * a web environment, the store would typically live in session scope to
- * separate the conversations from each of the sessions. There is always one
- * current conversation either stored within the session or as a request
- * parameter passed along every request to set the current conversation and is
- * accessed by the manager using a {@link ConversationResolver}.
- *
The interface to be implemented by any conversation manager. It is used by the advice behind the conversational
+ * annotations like {@link BeginConversation}, {@link EndConversation} and {@link Conversational} but might be used for
+ * even fine grained access to the underlying conversation management, if a initializer callback is needed for instance
+ * or if conversation switching should be used. If used directly, the conversation manager can be injected into any bean
+ * to be used as it is a singleton and thread safe.
+ *
+ * Conversations are a good way to scope beans and attributes depending on business logic boundary rather than a
+ * technical boundary of a scope like session, request etc. Usually a conversation boundary is defined by the starting
+ * point of a use case and ended accordingly or in other words a conversation defines the boundary for a unit of
+ * work.
+ *
+ * Here is a short description on how to use the conversation management:
Starting a conversation
+ * A conversation can be started in two different ways, either by placing the {@link BeginConversation} annotation on a
+ * method defining the starting point of the conversation (use case) or by invoking {@link
+ * ConversationManager#beginConversation(boolean, JoinMode)} manually through the conversation manager. The {@link
+ * JoinMode} declares on how a new conversation is created (see its javadoc for more details on the different join
+ * modes).
+ *
+ * Ending a conversation
A current conversation is ended by either placing the {@link EndConversation}
+ * annotation on the ending method or by manually invoke the {@link ConversationManager#endCurrentConversation(ConversationEndingType)}
+ * method. A current conversation might also be ended, if a new conversation is started having {@link JoinMode#NEW}
+ * being declared where the current conversation is ended silently and a new one is created. This might be the obvious
+ * way to end a conversation, if the end of a use case is not always forced to be invoked by a user.
+ *
+ * Temporary conversations
A temporary conversation is automatically created, if the container is about to
+ * create a bean having conversation scope but there is no current conversation in progress, hence a new, temporary
+ * conversation is created. A temporary conversation might be turned into a long running one by joining it or manually
+ * invoke {@link Conversation#begin()}.
+ *
+ * Initializing the conversation
If there is any need to initialize beans or entities while starting a new
+ * conversation, the {@link ConversationInitializationCallback} might be used to be invoked if the new conversation was
+ * started in order to initialize it. This is done by providing such a callback to the {@link
+ * ConversationManager#beginConversation(JoinMode, ConversationType, ConversationInitializationCallback...)} method. The
+ * callback feature is not available through the annotation support and hence is only available through the conversation
+ * manager API. Here is an example on such an initializing feature; if a conversation is used in conjunction of a JPA
+ * entity manager or Hibernate session, there might be entity beans already loaded, cached or referenced within backing
+ * beans which have to be merged into the entity manager or session in order to be used within the conversation's work.
+ * So it would be possible to implement the callback, merging the necessary entities used within the conversation into
+ * the entity manager.
+ *
+ * Listening to conversation events
If there is a need to listening to events of a conversation, add
+ * yourself as a {@link ConversationListener} to a new {@link Conversation} as being returned by this {@link
+ * ConversationManager}. The same goal can be achieved by implementing the {@link ConversationListener} interface on a
+ * conversation scoped bean which will be registered automatically to receive events.
+ *
+ * Nesting conversations
Conversations might be nested either by inheriting the state of the parent ( {@link
+ * JoinMode#NESTED}) or by isolating its state from the parent ( {@link JoinMode#ISOLATED}). Ending a nested
+ * conversation automatically switches back to its parent making it the current conversation.
+ *
+ * Where are conversations stored?
Conversations are created by the {@link ConversationManager} and
+ * registered within the {@link org.springframework.conversation.scope.ConversationScope}. The scope handler is
+ * injected into the manager by Spring (statically as both beans are singletons) and registered as a custom scope. As
+ * the scope handler is a singleton, it needs a store where conversations are stored within which is usually bound to
+ * the current session or window of a user and represented by the {@link ConversationStore} interface. The store is
+ * usually being injected into the scope handler using method injection as the store has a narrower scope than the scope
+ * handler. In a web environment, the store would typically live in session scope to separate the conversations from
+ * each of the sessions. There is always one current conversation either stored within the session or as a request
+ * parameter passed along every request to set the current conversation and is accessed by the manager using a {@link
+ * ConversationResolver}.
"conversation"
scope. It needs the {@link ConversationStore} and
+ * the {@link ConversationResolver} to resolve and request the current
+ * conversation where attributes are resolved with and registered in.
*
* @author Micha Kiener
* @since 3.1
*/
-public interface ConversationScope extends Scope {
+public class ConversationScope implements Scope {
/** The name of the scope being exposed within the application context. */
public static final String CONVERSATION_SCOPE_NAME = "conversation";
/**
* The name of the contextual object for the conversation manager (see
- * {@link Scope#resolveContextualObject(String)}).
+ * {@link org.springframework.beans.factory.config.Scope#resolveContextualObject(String)}).
*/
public static final String REFERENCE_CONVERSATION_MANAGER = "conversationManager";
/**
* The name of the contextual object for the conversation store (see
- * {@link Scope#resolveContextualObject(String)}).
+ * {@link org.springframework.beans.factory.config.Scope#resolveContextualObject(String)}).
*/
public static final String REFERENCE_CONVERSATION_STORE = "conversationStore";
/**
* The name of the contextual object for the conversation resolver (see
- * {@link Scope#resolveContextualObject(String)}).
+ * {@link org.springframework.beans.factory.config.Scope#resolveContextualObject(String)}).
*/
public static final String REFERENCE_CONVERSATION_RESOLVER = "conversationResolver";
-}
+
+
+ /** Holds the conversation manager reference, if statically injected. */
+ private ConversationManager conversationManager;
+
+ /** Holds the conversation store reference, if statically injected. */
+ private ConversationStore conversationStore;
+
+ /** Holds the conversation resolver reference, if statically injected. */
+ private ConversationResolver conversationResolver;
+
+ /**
+ * This method is invoked to resolve the current conversation used where
+ * attributes having conversation scope are being resolved with or stored
+ * in.
+ *
+ * @return the currently used conversation, or null
, if no one
+ * currently available and createIfNotExisting
is
+ * false
+ */
+ protected Conversation getCurrentConversation(boolean createNewIfNotExisting) {
+ ConversationResolver resolver = getConversationResolver();
+ Assert.notNull(resolver, "No conversation resolver available within the conversation scope");
+
+ String conversationId = resolver.getCurrentConversationId();
+ Conversation conversation;
+ if (conversationId == null) {
+ if (createNewIfNotExisting) {
+ // start a new, temporary conversation using the default join
+ // mode
+ ConversationManager manager = getConversationManager();
+ conversation = manager.beginConversation(true, JoinMode.getDefaultJoinMode());
+ } else {
+ return null;
+ }
+ } else {
+ ConversationStore store = getConversationStore();
+ Assert.notNull(store, "No conversation store available within the conversation scope");
+ conversation = store.getConversation(conversationId);
+ Assert.notNull(conversation, "The conversation with id <" + conversationId
+ + "> is not available within the store");
+ }
+
+ return conversation;
+ }
+
+ /**
+ * @see org.springframework.beans.factory.config.Scope#get(java.lang.String,
+ * org.springframework.beans.factory.ObjectFactory)
+ */
+ public Object get(String name, ObjectFactory> objectFactory) {
+ Conversation conversation = getCurrentConversation(true);
+ Object attribute = conversation.getAttribute(name);
+ if (attribute == null) {
+ attribute = objectFactory.getObject();
+ conversation.setAttribute(name, attribute);
+ }
+
+ return attribute;
+ }
+
+ /**
+ * @see org.springframework.beans.factory.config.Scope#getConversationId()
+ */
+ public String getConversationId() {
+ Conversation conversation = getCurrentConversation(false);
+ if (conversation != null) {
+ return conversation.getId();
+ }
+
+ return null;
+ }
+
+ /**
+ * @see org.springframework.beans.factory.config.Scope#registerDestructionCallback(java.lang.String,
+ * java.lang.Runnable)
+ */
+ public void registerDestructionCallback(String name, Runnable callback) {
+ Conversation conversation = getCurrentConversation(false);
+ if (conversation != null) {
+ conversation.registerDestructionCallback(name, callback);
+ }
+ }
+
+ /**
+ * @see org.springframework.beans.factory.config.Scope#remove(java.lang.String)
+ */
+ public Object remove(String name) {
+ Conversation conversation = getCurrentConversation(false);
+ if (conversation != null) {
+ return conversation.removeAttribute(name);
+ }
+
+ return null;
+ }
+
+ /**
+ * Supports the following objects:
+ * "conversationManager"
, returns the {@link ConversationManager}"conversationStore"
, returns the {@link ConversationStore}"conversationResolver"
, returns the {@link ConversationResolver}"conversation"
scope. It needs the {@link ConversationStore} and
- * the {@link ConversationResolver} to resolve and request the current
- * conversation where attributes are resolved with and registered in.
- *
- * @author Micha Kiener
- * @since 3.1
- */
-public class DefaultConversationScope implements ConversationScope{
-
- /** Holds the conversation manager reference, if statically injected. */
- private ConversationManager conversationManager;
-
- /** Holds the conversation store reference, if statically injected. */
- private ConversationStore conversationStore;
-
- /** Holds the conversation resolver reference, if statically injected. */
- private ConversationResolver conversationResolver;
-
- /**
- * This method is invoked to resolve the current conversation used where
- * attributes having conversation scope are being resolved with or stored
- * in.
- *
- * @return the currently used conversation, or null
, if no one
- * currently available and createIfNotExisting
is
- * false
- */
- protected Conversation getCurrentConversation(boolean createNewIfNotExisting) {
- ConversationResolver resolver = getConversationResolver();
- Assert.notNull(resolver, "No conversation resolver available within the conversation scope");
-
- String conversationId = resolver.getCurrentConversationId();
- Conversation conversation;
- if (conversationId == null) {
- if (createNewIfNotExisting) {
- // start a new, temporary conversation using the default join
- // mode
- ConversationManager manager = getConversationManager();
- conversation = manager.beginConversation(true, JoinMode.getDefaultJoinMode());
- } else {
- return null;
- }
- } else {
- ConversationStore store = getConversationStore();
- Assert.notNull(store, "No conversation store available within the conversation scope");
- conversation = store.getConversation(conversationId);
- Assert.notNull(conversation, "The conversation with id <" + conversationId
- + "> is not available within the store");
- }
-
- return conversation;
- }
-
- /**
- * @see org.springframework.beans.factory.config.Scope#get(java.lang.String,
- * org.springframework.beans.factory.ObjectFactory)
- */
- public Object get(String name, ObjectFactory> objectFactory) {
- Conversation conversation = getCurrentConversation(true);
- Object attribute = conversation.getAttribute(name);
- if (attribute == null) {
- attribute = objectFactory.getObject();
- conversation.setAttribute(name, attribute);
- }
-
- return attribute;
- }
-
- /**
- * @see org.springframework.beans.factory.config.Scope#getConversationId()
- */
- public String getConversationId() {
- Conversation conversation = getCurrentConversation(false);
- if (conversation != null) {
- return conversation.getId();
- }
-
- return null;
- }
-
- /**
- * @see org.springframework.beans.factory.config.Scope#registerDestructionCallback(java.lang.String,
- * java.lang.Runnable)
- */
- public void registerDestructionCallback(String name, Runnable callback) {
- Conversation conversation = getCurrentConversation(false);
- if (conversation != null) {
- conversation.registerDestructionCallback(name, callback);
- }
- }
-
- /**
- * @see org.springframework.beans.factory.config.Scope#remove(java.lang.String)
- */
- public Object remove(String name) {
- Conversation conversation = getCurrentConversation(false);
- if (conversation != null) {
- return conversation.removeAttribute(name);
- }
-
- return null;
- }
-
- /**
- * Supports the following objects:
- * "conversationManager"
, returns the
- * {@link ConversationManager}"conversationStore"
, returns the
- * {@link ConversationStore}"conversationResolver"
, returns the
- * {@link ConversationResolver}