Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
爱吃血肠
spring-framework
提交
f812cd74
S
spring-framework
项目概览
爱吃血肠
/
spring-framework
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
S
spring-framework
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
f812cd74
编写于
4月 12, 2011
作者:
M
Micha Kiener
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
SPR-6416, initial commit for the conversation management
上级
0c736776
变更
29
隐藏空白更改
内联
并排
Showing
29 changed file
with
2395 addition
and
0 deletion
+2395
-0
org.springframework.beans/src/main/java/org/springframework/beans/factory/config/DestructionAwareAttributeHolder.java
...beans/factory/config/DestructionAwareAttributeHolder.java
+202
-0
org.springframework.context/src/main/java/org/springframework/conversation/Conversation.java
...n/java/org/springframework/conversation/Conversation.java
+146
-0
org.springframework.context/src/main/java/org/springframework/conversation/ConversationManager.java
...org/springframework/conversation/ConversationManager.java
+109
-0
org.springframework.context/src/main/java/org/springframework/conversation/ConversationType.java
...va/org/springframework/conversation/ConversationType.java
+45
-0
org.springframework.context/src/main/java/org/springframework/conversation/annotation/AnnotationConversationAttributeSource.java
...ion/annotation/AnnotationConversationAttributeSource.java
+105
-0
org.springframework.context/src/main/java/org/springframework/conversation/annotation/BeginConversation.java
...gframework/conversation/annotation/BeginConversation.java
+56
-0
org.springframework.context/src/main/java/org/springframework/conversation/annotation/BeginConversationAnnotationParser.java
...rsation/annotation/BeginConversationAnnotationParser.java
+40
-0
org.springframework.context/src/main/java/org/springframework/conversation/annotation/ConversationAnnotationParser.java
...conversation/annotation/ConversationAnnotationParser.java
+36
-0
org.springframework.context/src/main/java/org/springframework/conversation/annotation/EndConversation.java
...ingframework/conversation/annotation/EndConversation.java
+47
-0
org.springframework.context/src/main/java/org/springframework/conversation/annotation/EndConversationAnnotationParser.java
...versation/annotation/EndConversationAnnotationParser.java
+39
-0
org.springframework.context/src/main/java/org/springframework/conversation/interceptor/BeanFactoryConversationAttributeSourceAdvisor.java
...ceptor/BeanFactoryConversationAttributeSourceAdvisor.java
+55
-0
org.springframework.context/src/main/java/org/springframework/conversation/interceptor/ConversationAttribute.java
...ework/conversation/interceptor/ConversationAttribute.java
+77
-0
org.springframework.context/src/main/java/org/springframework/conversation/interceptor/ConversationAttributeSource.java
...conversation/interceptor/ConversationAttributeSource.java
+41
-0
org.springframework.context/src/main/java/org/springframework/conversation/interceptor/ConversationAttributeSourcePointcut.java
...tion/interceptor/ConversationAttributeSourcePointcut.java
+41
-0
org.springframework.context/src/main/java/org/springframework/conversation/interceptor/ConversationInterceptor.java
...ork/conversation/interceptor/ConversationInterceptor.java
+95
-0
org.springframework.context/src/main/java/org/springframework/conversation/interceptor/DefaultConversationAttribute.java
...onversation/interceptor/DefaultConversationAttribute.java
+76
-0
org.springframework.context/src/main/java/org/springframework/conversation/manager/AbstractConversationRepository.java
.../conversation/manager/AbstractConversationRepository.java
+125
-0
org.springframework.context/src/main/java/org/springframework/conversation/manager/ConversationRepository.java
...ramework/conversation/manager/ConversationRepository.java
+85
-0
org.springframework.context/src/main/java/org/springframework/conversation/manager/ConversationResolver.java
...gframework/conversation/manager/ConversationResolver.java
+49
-0
org.springframework.context/src/main/java/org/springframework/conversation/manager/DefaultConversation.java
...ngframework/conversation/manager/DefaultConversation.java
+272
-0
org.springframework.context/src/main/java/org/springframework/conversation/manager/DefaultConversationManager.java
...work/conversation/manager/DefaultConversationManager.java
+178
-0
org.springframework.context/src/main/java/org/springframework/conversation/manager/LocalTransientConversationRepository.java
...rsation/manager/LocalTransientConversationRepository.java
+69
-0
org.springframework.context/src/main/java/org/springframework/conversation/manager/MutableConversation.java
...ngframework/conversation/manager/MutableConversation.java
+93
-0
org.springframework.context/src/main/java/org/springframework/conversation/manager/ThreadLocalConversationResolver.java
...conversation/manager/ThreadLocalConversationResolver.java
+46
-0
org.springframework.context/src/main/java/org/springframework/conversation/scope/ConversationScope.java
...springframework/conversation/scope/ConversationScope.java
+109
-0
org.springframework.context/template.mf
org.springframework.context/template.mf
+1
-0
org.springframework.web/src/main/java/org/springframework/web/conversation/SessionBasedConversationRepository.java
.../web/conversation/SessionBasedConversationRepository.java
+111
-0
org.springframework.web/src/main/java/org/springframework/web/conversation/WebAwareConversationScope.java
...framework/web/conversation/WebAwareConversationScope.java
+45
-0
org.springframework.web/template.mf
org.springframework.web/template.mf
+2
-0
未找到文件。
org.springframework.beans/src/main/java/org/springframework/beans/factory/config/DestructionAwareAttributeHolder.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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.beans.factory.config
;
import
java.io.Serializable
;
import
java.util.Map
;
import
java.util.concurrent.ConcurrentHashMap
;
/**
* A container object holding a map of attributes and optionally destruction callbacks. The callbacks will be invoked,
* if an attribute is being removed or if the holder is cleaned out.
*
* @author Micha Kiener
* @since 3.1
*/
public
class
DestructionAwareAttributeHolder
implements
Serializable
{
/** The map containing the registered attributes. */
private
final
Map
<
String
,
Object
>
attributes
=
new
ConcurrentHashMap
<
String
,
Object
>();
/**
* The optional map having any destruction callbacks registered using the
* name of the bean as the key.
*/
private
Map
<
String
,
Runnable
>
registeredDestructionCallbacks
;
/**
* Returns the map representation of the registered attributes directly. Be
* aware to synchronize any invocations to it on the map object itself to
* avoid concurrent modification exceptions.
*
* @return the attributes as a map representation
*/
public
Map
<
String
,
Object
>
getAttributeMap
()
{
return
attributes
;
}
/**
* Returns the attribute having the specified name, if available,
* <code>null</code> otherwise.
*
* @param name
* the name of the attribute to be returned
* @return the attribute value or <code>null</code> if not available
*/
@SuppressWarnings
(
"unchecked"
)
public
Object
getAttribute
(
String
name
)
{
return
attributes
.
get
(
name
);
}
/**
* Puts the given object with the specified name as an attribute to the
* underlying map.
*
* @param name
* the name of the attribute
* @param value
* the value to be stored
* @return any previously object stored under the same name, if any,
* <code>null</code> otherwise
*/
@SuppressWarnings
(
"unchecked"
)
public
Object
setAttribute
(
String
name
,
Object
value
)
{
return
attributes
.
put
(
name
,
value
);
}
/**
* Remove the object with the given <code>name</code> from the underlying
* scope.
* <p>
* Returns <code>null</code> if no object was found; otherwise returns the
* removed <code>Object</code>.
* <p>
* Note that an implementation should also remove a registered destruction
* callback for the specified object, if any. It does, however, <i>not</i>
* need to <i>execute</i> a registered destruction callback in this case,
* since the object will be destroyed by the caller (if appropriate).
* <p>
* <b>Note: This is an optional operation.</b> Implementations may throw
* {@link UnsupportedOperationException} if they do not support explicitly
* removing an object.
*
* @param name
* the name of the object to remove
* @return the removed object, or <code>null</code> if no object was present
* @see #registerDestructionCallback
*/
@SuppressWarnings
(
"unchecked"
)
public
Object
removeAttribute
(
String
name
)
{
Object
value
=
attributes
.
remove
(
name
);
// check for a destruction callback to be invoked
Runnable
callback
=
getDestructionCallback
(
name
,
true
);
if
(
callback
!=
null
)
{
callback
.
run
();
}
return
value
;
}
/**
* Clears the map by removing all registered attribute values and invokes
* every destruction callback registered.
*/
public
void
clear
()
{
synchronized
(
this
)
{
// step through the attribute map and invoke destruction callbacks,
// if any
if
(
registeredDestructionCallbacks
!=
null
)
{
for
(
Runnable
runnable
:
registeredDestructionCallbacks
.
values
())
{
runnable
.
run
();
}
registeredDestructionCallbacks
.
clear
();
}
}
// clear out the registered attribute map
attributes
.
clear
();
}
/**
* Register a callback to be executed on destruction of the specified object
* in the scope (or at destruction of the entire scope, if the scope does
* not destroy individual objects but rather only terminates in its
* entirety).
* <p>
* <b>Note: This is an optional operation.</b> This method will only be
* called for scoped beans with actual destruction configuration
* (DisposableBean, destroy-method, DestructionAwareBeanPostProcessor).
* Implementations should do their best to execute a given callback at the
* appropriate time. If such a callback is not supported by the underlying
* runtime environment at all, the callback <i>must be ignored and a
* corresponding warning should be logged</i>.
* <p>
* Note that 'destruction' refers to to automatic destruction of the object
* as part of the scope's own lifecycle, not to the individual scoped object
* having been explicitly removed by the application. If a scoped object
* gets removed via this facade's {@link #removeAttribute(String)} method,
* any registered destruction callback should be removed as well, assuming
* that the removed object will be reused or manually destroyed.
*
* @param name
* the name of the object to execute the destruction callback for
* @param callback
* the destruction callback to be executed. Note that the
* passed-in Runnable will never throw an exception, so it can
* safely be executed without an enclosing try-catch block.
* Furthermore, the Runnable will usually be serializable,
* provided that its target object is serializable as well.
* @see org.springframework.beans.factory.DisposableBean
* @see org.springframework.beans.factory.support.AbstractBeanDefinition#getDestroyMethodName()
* @see org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor
*/
public
void
registerDestructionCallback
(
String
name
,
Runnable
callback
)
{
if
(
registeredDestructionCallbacks
==
null
)
{
registeredDestructionCallbacks
=
new
ConcurrentHashMap
<
String
,
Runnable
>();
}
registeredDestructionCallbacks
.
put
(
name
,
callback
);
}
/**
* Returns the destruction callback, if any registered for the attribute
* with the given name or <code>null</code> if no such callback was
* registered.
*
* @param name
* the name of the registered callback requested
* @param remove
* <code>true</code>, if the callback should be removed after
* this call, <code>false</code>, if it stays
* @return the callback, if found, <code>null</code> otherwise
*/
public
Runnable
getDestructionCallback
(
String
name
,
boolean
remove
)
{
if
(
registeredDestructionCallbacks
==
null
)
{
return
null
;
}
if
(
remove
)
{
return
registeredDestructionCallbacks
.
remove
(
name
);
}
return
registeredDestructionCallbacks
.
get
(
name
);
}
}
org.springframework.context/src/main/java/org/springframework/conversation/Conversation.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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
;
import
java.util.List
;
/**
* The interface for a conversation object being managed by the {@link ConversationManager} and created, stored and
* removed by the {@link org.springframework.conversation.manager.ConversationRepository}.<br/>
* The conversation object is most likely never used directly but rather indirectly through the
* {@link org.springframework.conversation.scope.ConversationScope}. It supports fine grained access to the conversation
* container for storing and retrieving attributes, access the conversation hierarchy or manage the timeout behavior
* of the conversation.
*
* @author Micha Kiener
* @since 3.1
*/
public
interface
Conversation
{
/**
* Returns the id of this conversation which must be unique within the scope it is used to identify the conversation
* object. The id is set by the {@link org.springframework.conversation.manager.ConversationRepository} and most
* likely be used by the {@link org.springframework.conversation.manager.ConversationResolver} in order to manage
* the current conversation.
*
* @return the id of this conversation
*/
String
getId
();
/**
* Stores the given value in this conversation using the specified name. If this state already contains a value
* attached to the given name, it is returned, <code>null</code> otherwise.<br/> This method stores the attribute
* within this conversation so it will be available through this and all nested conversations.
*
* @param name the name of the value to be stored in this conversation
* @param value the value to be stored
* @return the old value attached to the same name, if any, <code>null</code> otherwise
*/
Object
setAttribute
(
String
name
,
Object
value
);
/**
* Returns the value attached to the given name, if any previously registered, <code>null</code> otherwise.<br/>
* Returns the attribute stored with the given name within this conversation or any within the path through its parent
* to the top level root conversation. If this is a nested, isolated conversation, attributes are only being resolved
* within this conversation, not from its parent.
*
* @param name the name of the value to be retrieved
* @return the value, if available in the current state, <code>null</code> otherwise
*/
Object
getAttribute
(
String
name
);
/**
* Removes the value in the current conversation having the given name and returns it, if found and removed,
* <code>null</code> otherwise.<br/> Removes the attribute from this specific conversation, does not remove it, if
* found within its parent.
*
* @param name the name of the value to be removed from this conversation
* @return the removed value, if found, <code>null</code> otherwise
*/
Object
removeAttribute
(
String
name
);
/**
* Returns the top level root conversation, if this is a nested conversation or this conversation, if it is the top
* level root conversation. This method never returns <code>null</code>.
*
* @return the root conversation (top level conversation)
*/
Conversation
getRoot
();
/**
* Returns the parent conversation, if this is a nested conversation, <code>null</code> otherwise.
*
* @return the parent conversation, if any, <code>null</code> otherwise
*/
Conversation
getParent
();
/**
* Returns a list of child conversations, if any, an empty list otherwise, must never return <code>null</code>.
*
* @return a list of child conversations (may be empty, never <code>null</code>)
*/
List
<?
extends
Conversation
>
getChildren
();
/**
* Returns <code>true</code>, if this is a nested conversation and hence {@link #getParent()} will returns a non-null
* value.
*
* @return <code>true</code>, if this is a nested conversation, <code>false</code> otherwise
*/
boolean
isNested
();
/**
* Returns <code>true</code>, if this is a nested, isolated conversation so that it does not inherit the state from its
* parent but rather has its own state. See {@link ConversationType#ISOLATED} for more details.
*
* @return <code>true</code>, if this is a nested, isolated conversation
*/
boolean
isIsolated
();
/**
* Returns the timestamp in milliseconds this conversation has been created.
*
* @return the creation timestamp in millis
*/
long
getCreationTime
();
/**
* Returns the timestamp in milliseconds this conversation was last accessed (usually through a {@link
* #getAttribute(String)}, {@link #setAttribute(String, Object)} or {@link #removeAttribute(String)} access).
*
* @return the system time in milliseconds for the last access of this conversation
*/
long
getLastAccessedTime
();
/**
* Returns the timeout of this conversation object in seconds. A value of <code>0</code> stands for no timeout.
* The timeout is usually managed on the root conversation object and will be returned regardless of the hierarchy
* of this conversation.
*
* @return the timeout in seconds if any, <code>0</code> otherwise
*/
int
getTimeout
();
/**
* Set the timeout of this conversation hierarchy in seconds. A value of <code>0</code> stands for no timeout.
* Regardless of the hierarchy of this conversation, a timeout is always set on the top root conversation and is
* valid for all conversations within the same hierarchy.
*
* @param timeout the timeout in seconds to set, <code>0</code> for no timeout
*/
void
setTimeout
(
int
timeout
);
}
org.springframework.context/src/main/java/org/springframework/conversation/ConversationManager.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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
;
/**
* <p>
* A conversation manager is used to manage conversations, most of all, the current conversation. It is used by
* the advice behind the conversation annotations {@link org.springframework.conversation.annotation.BeginConversation}
* and {@link org.springframework.conversation.annotation.EndConversation} in order to start and end conversations.
* </p>
* <p>
* A conversation manager uses a {@link org.springframework.conversation.manager.ConversationRepository} to create,
* store and remove conversation objects and a {@link org.springframework.conversation.manager.ConversationResolver}
* to set and remove the current conversation id.
* </p>
* <p>
* A conversation manager might be used manually in order to start and end conversations manually.
* </p>
* <p>
* 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.<br/><br/>
*
* A conversation is either implicitly started upon the first request of a conversation scoped bean or it is
* explicitly started by using the conversation manager manually or by placing the begin conversation on a method.<br/>
* The same applies for ending conversations as they are either implicitly ended by starting a new one or if the
* timeout of a conversation is reached or they are ended explicitly by placing the end conversation annotation or
* using the conversation manager manually.
* </p>
* <p>
* Conversations might have child conversations which are either nested and hence will inherit the state of their
* parent or they are isolated by having its own state and hence being independent from its parent.
* </p>
* <p>
* <b>Extending the conversation management</b><br/>
* The conversation management ships with different out-of-the box implementations but is easy to extend.
* To extend the storage mechanism of conversations, the {@link org.springframework.conversation.manager.ConversationRepository}
* and maybe the {@link org.springframework.conversation.manager.DefaultConversation} have to be extended or
* overwritten to support the desired behavior.<br/>
* To change the behavior where the current conversation is stored, either overwrite the
* {@link org.springframework.conversation.manager.ConversationResolver} or make sure the current conversation id
* is being resolved, stored and removed within the default {@link org.springframework.conversation.manager.ThreadLocalConversationResolver}.
* </p>
*
* @author Micha Kiener
* @since 3.1
*/
public
interface
ConversationManager
{
/**
* Returns the current conversation and creates a new one, if there is currently no active conversation yet.
* Internally, the manager will use the {@link org.springframework.conversation.manager.ConversationResolver}
* to resolve the current conversation id and the {@link org.springframework.conversation.manager.ConversationRepository}
* to load the conversation object being returned.
*
* @return the current conversation, never <code>null</code>, will create a new conversation, if no one existing
*/
Conversation
getCurrentConversation
();
/**
* Returns the current conversation, if existing or creates a new one, if currently no active conversation available
* and <code>createIfNotExisting</code> is specified as <code>true</code>.
*
* @param createNewIfNotExisting <code>true</code>, if a new conversation should be created, if there is currently
* no active conversation in place, <code>false</code> to return <code>null</code>, if no current conversation active
* @return the current conversation or <code>null</code>, if no current conversation available and
* <code>createIfNotExisting</code> is set as <code>false</code>
*/
Conversation
getCurrentConversation
(
boolean
createNewIfNotExisting
);
/**
* Creates a new conversation according the given <code>conversationType</code> and makes it the current active
* conversation. See {@link ConversationType} for more detailed information about the different conversation
* creation types available.<br/>
* If {@link ConversationType#NEW} is specified, the current conversation will automatically be ended
*
* @param conversationType the type used to start a new conversation
* @return the newly created conversation
*/
Conversation
beginConversation
(
ConversationType
conversationType
);
/**
* Ends the current conversation, if any. If <code>root</code> is <code>true</code>, the whole conversation
* hierarchy is ended and there will no current conversation be active afterwards. If <code>root</code> is
* <code>false</code>, the current conversation is ended and if it is a nested one, its parent is made the
* current conversation.
*
* @param root <code>true</code> to end the whole current conversation hierarchy or <code>false</code> to just
* end the current conversation
*/
void
endCurrentConversation
(
boolean
root
);
}
org.springframework.context/src/main/java/org/springframework/conversation/ConversationType.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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
;
/**
* The conversation type is used while starting a new conversation and declares how the conversation manager
* should create and start it as well how to end the current conversation, if any.
*
* @author Micha Kiener
* @since 3.1
*/
public
enum
ConversationType
{
/**
* The type NEW creates a new root conversation and will end a current one, if any.
*/
NEW
,
/**
* The type NESTED will create a new conversation and add it as a child conversation to the current one,
* if available. If there is no current conversation, this type is the same as NEW.
* A nested conversation will inherit the state from its parent.
*/
NESTED
,
/**
* The type ISOLATED is basically the same as NESTED but will isolate the state from its parent. While a
* nested conversation will inherit the state from its parent, an isolated one does not but rather has its
* own state.
*/
ISOLATED
}
org.springframework.context/src/main/java/org/springframework/conversation/annotation/AnnotationConversationAttributeSource.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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.annotation
;
import
java.lang.reflect.Method
;
import
java.util.Collections
;
import
java.util.LinkedHashSet
;
import
java.util.Set
;
import
org.springframework.aop.support.AopUtils
;
import
org.springframework.conversation.interceptor.ConversationAttribute
;
import
org.springframework.conversation.interceptor.ConversationAttributeSource
;
import
org.springframework.util.Assert
;
/**
* ConversationAttributeSource implementation that uses annotation meta-data to provide a ConversationAttribute instance
* for a particular method.
*
* @author Agim Emruli
*/
public
class
AnnotationConversationAttributeSource
implements
ConversationAttributeSource
{
private
final
Set
<
ConversationAnnotationParser
>
conversationAnnotationParsers
;
/**
* Default constructor that uses the Spring standard ConversationAnnotationParser instances to parse the annotation
* meta-data.
*
* @see org.springframework.conversation.annotation.BeginConversationAnnotationParser
* @see org.springframework.conversation.annotation.EndConversationAnnotationParser
* @see org.springframework.conversation.annotation.ConversationAnnotationParser
*/
public
AnnotationConversationAttributeSource
()
{
Set
<
ConversationAnnotationParser
>
defaultParsers
=
new
LinkedHashSet
<
ConversationAnnotationParser
>();
Collections
.
addAll
(
defaultParsers
,
new
BeginConversationAnnotationParser
(),
new
EndConversationAnnotationParser
());
conversationAnnotationParsers
=
defaultParsers
;
}
/**
* Constructor that uses the custom ConversationAnnotationParser to parse the annotation meta-data.
*
* @param conversationAnnotationParsers The ConversationAnnotationParser instance that will be used to parse annotation
* meta-data
*/
public
AnnotationConversationAttributeSource
(
ConversationAnnotationParser
conversationAnnotationParsers
)
{
this
(
Collections
.
singleton
(
conversationAnnotationParsers
));
}
/**
* Constructor that uses a pre-built set with annotation parsers to retrieve the conversation meta-data. It is up to
* the caller to provide a sorted set of annotation parsers if the order of them is important.
*
* @param parsers The Set of annotation parsers used to retrieve conversation meta-data.
*/
public
AnnotationConversationAttributeSource
(
Set
<
ConversationAnnotationParser
>
parsers
)
{
Assert
.
notNull
(
parsers
,
"ConversationAnnotationParsers must not be null"
);
conversationAnnotationParsers
=
parsers
;
}
/**
* Resolves the conversation meta-data by delegating to the ConversationAnnotationParser instances. This implementation
* returns the first ConversationAttribute instance that will be returned by a ConversationAnnotationParser. This
* method returns null if no ConversationAnnotationParser returns a non-null result.
*
* The implementation searches for the most specific method (e.g. if there is a interface method this methods searches
* for the implementation method) before calling the underlying ConversationAnnotationParser instances. If there is no
* Annotation available on the implementation method, this methods falls back to the interface method.
*
* @param method The method for which the ConversationAttribute should be returned.
* @param targetClass The target class where the implementation should look for.
*/
public
ConversationAttribute
getConversationAttribute
(
Method
method
,
Class
<?>
targetClass
)
{
Method
specificMethod
=
AopUtils
.
getMostSpecificMethod
(
method
,
targetClass
);
for
(
ConversationAnnotationParser
parser
:
conversationAnnotationParsers
)
{
ConversationAttribute
attribute
=
parser
.
parseConversationAnnotation
(
specificMethod
);
if
(
attribute
!=
null
)
{
return
attribute
;
}
if
(
method
!=
specificMethod
){
attribute
=
parser
.
parseConversationAnnotation
(
method
);
if
(
attribute
!=
null
){
return
attribute
;
}
}
}
return
null
;
}
}
org.springframework.context/src/main/java/org/springframework/conversation/annotation/BeginConversation.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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.annotation
;
import
java.lang.annotation.Documented
;
import
java.lang.annotation.ElementType
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.Target
;
import
org.springframework.conversation.Conversation
;
import
org.springframework.conversation.ConversationManager
;
import
org.springframework.conversation.ConversationType
;
import
org.springframework.conversation.interceptor.ConversationAttribute
;
/**
* This annotation can be placed on any method to start a new conversation. This has the same effect as invoking {@link
* org.springframework.conversation.ConversationManager#beginConversation(boolean, JoinMode)} using <code>false</code>
* for the temporary mode and the join mode as being specified within the annotation or {@link JoinMode#NEW} as the
* default.<br/> The new conversation is always long running (not a temporary one) and is ended by either manually
* invoke {@link ConversationManager#endCurrentConversation(ConversationEndingType)}, invoking the {@link
* Conversation#end(ConversationEndingType)} method on the conversation itself or by placing the {@link EndConversation}
* annotation on a method.<br/> The new conversation is created BEFORE the method itself is invoked as a before-advice.
*
* @author Micha Kiener
* @author Agim Emruli
* @since 3.1
*/
@Retention
(
RetentionPolicy
.
RUNTIME
)
@Target
(
ElementType
.
METHOD
)
@Documented
public
@interface
BeginConversation
{
/**
* The conversation type declares how to start a new conversation and how to handle an existing current one.
* See {@link ConversationType} for more information.
*/
ConversationType
value
()
default
ConversationType
.
NEW
;
/** The timeout for this conversation in seconds. */
int
timeout
()
default
ConversationAttribute
.
DEFAULT_TIMEOUT
;
}
org.springframework.context/src/main/java/org/springframework/conversation/annotation/BeginConversationAnnotationParser.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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.annotation
;
import
java.lang.reflect.AnnotatedElement
;
import
org.springframework.conversation.interceptor.ConversationAttribute
;
import
org.springframework.conversation.interceptor.DefaultConversationAttribute
;
/**
* ConversationAnnotationParser for the BeginConversation annotation
*
* @author Agim Emruli
* @see BeginConversation
*/
class
BeginConversationAnnotationParser
implements
ConversationAnnotationParser
{
public
ConversationAttribute
parseConversationAnnotation
(
AnnotatedElement
annotatedElement
)
{
BeginConversation
beginConversation
=
annotatedElement
.
getAnnotation
(
BeginConversation
.
class
);
if
(
beginConversation
!=
null
)
{
return
new
DefaultConversationAttribute
(
beginConversation
.
value
(),
beginConversation
.
timeout
());
}
return
null
;
}
}
org.springframework.context/src/main/java/org/springframework/conversation/annotation/ConversationAnnotationParser.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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.annotation
;
import
java.lang.reflect.AnnotatedElement
;
import
org.springframework.conversation.interceptor.ConversationAttribute
;
/**
* Parser interface that resolver the concrete conversation annotation from an AnnotatedElement.
*
* @author Agim Emruli
*/
interface
ConversationAnnotationParser
{
/**
* This method returns the ConversationAttribute for a particular AnnotatedElement which can be a method or class at
* all.
*/
ConversationAttribute
parseConversationAnnotation
(
AnnotatedElement
annotatedElement
);
}
org.springframework.context/src/main/java/org/springframework/conversation/annotation/EndConversation.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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.annotation
;
import
java.lang.annotation.Documented
;
import
java.lang.annotation.ElementType
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.Target
;
/**
* This annotation can be placed on a method to end the current conversation. It
* has the same effect as a manual invocation of
* {@link org.springframework.conversation.ConversationManager#endCurrentConversation(ConversationEndingType)}.<br/>
* The conversation is ended AFTER the method was invoked as an after-advice.
*
* @author Micha Kiener
* @since 3.1
*/
@Retention
(
RetentionPolicy
.
RUNTIME
)
@Target
(
ElementType
.
METHOD
)
@Documented
public
@interface
EndConversation
{
/**
* If root is <code>true</code> which is the default, using this annotation will end a current conversation
* completely including its path up to the top root conversation. If declared as <code>false</code>, it will
* only end the current conversation, making its parent as the new current conversation.
* If the current conversation is not a nested or isolated conversation, the <code>root</code> parameter has
* no impact.
*/
boolean
root
()
default
true
;
}
org.springframework.context/src/main/java/org/springframework/conversation/annotation/EndConversationAnnotationParser.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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.annotation
;
import
java.lang.reflect.AnnotatedElement
;
import
org.springframework.conversation.interceptor.ConversationAttribute
;
import
org.springframework.conversation.interceptor.DefaultConversationAttribute
;
/**
* ConversationAnnotationParser for the EndConversation annotation
*
* @author Agim Emruli
* @see EndConversation
*/
class
EndConversationAnnotationParser
implements
ConversationAnnotationParser
{
public
ConversationAttribute
parseConversationAnnotation
(
AnnotatedElement
annotatedElement
)
{
EndConversation
endConversation
=
annotatedElement
.
getAnnotation
(
EndConversation
.
class
);
if
(
endConversation
!=
null
)
{
return
new
DefaultConversationAttribute
(
endConversation
.
root
());
}
return
null
;
}
}
org.springframework.context/src/main/java/org/springframework/conversation/interceptor/BeanFactoryConversationAttributeSourceAdvisor.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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.interceptor
;
import
org.springframework.aop.Pointcut
;
import
org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor
;
/**
* Advisor implementation that advises beans if they contain conversation meta-data. Uses a
* ConversationAttributeSourcePointcut to specify if the bean should be advised or not.
*
* @author Agim Emruli
*/
public
class
BeanFactoryConversationAttributeSourceAdvisor
extends
AbstractBeanFactoryPointcutAdvisor
{
private
ConversationAttributeSource
conversationAttributeSource
;
private
Pointcut
pointcut
=
new
ConversationAttributeSourcePointcut
()
{
@Override
protected
ConversationAttributeSource
getConversationAttributeSource
()
{
return
conversationAttributeSource
;
}
};
/**
* Sets the ConversationAttributeSource instance that will be used to retrieve the ConversationDefinition meta-data.
* This instance will be used by the point-cut do specify if the target bean should be advised or not.
*/
public
void
setConversationAttributeSource
(
ConversationAttributeSource
conversationAttributeSource
)
{
this
.
conversationAttributeSource
=
conversationAttributeSource
;
}
/**
* Returns the pointcut that will be used at runtime to test if the bean should be advised or not.
*
* @see org.springframework.conversation.interceptor.ConversationAttributeSourcePointcut
*/
public
Pointcut
getPointcut
()
{
return
pointcut
;
}
}
org.springframework.context/src/main/java/org/springframework/conversation/interceptor/ConversationAttribute.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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.interceptor
;
import
org.springframework.conversation.ConversationType
;
/**
* ConversationDefinition Attributes that will be used by the AOP interceptor to start and end conversation before and
* after methods. This attributes are not in the conversation definition itself because these attributes are only
* relevant for a interceptor based approach to handle conversations.
*
* @author Agim Emruli
*/
public
interface
ConversationAttribute
{
/**
* The default timeout for a conversation, means there is no timeout defined for the conversation. It is up to the
* concrete conversation manager implementation to handle conversation without a timeout, like a indefinite
* conversation or some other system specific timeout
*/
int
DEFAULT_TIMEOUT
=
-
1
;
/**
* Defines if the a conversation should be started before the interceptor delegates to the target (like a method
* invocation). This can be a short-running conversation where the method is the whole life-cycle of a conversation or
* a long-running conversation where the conversation will be started but not stopped while calling the target.
*
* @return if the conversation should be started
*/
boolean
shouldStartConversation
();
/**
* Defines if the a conversation should be ended after the interceptor delegates to the target (like a method
* invocation). The stopped that should be stopped after the call to the target can be a short-running conversation
* that has been start before the method call or a long-running conversation that has been started on some other method
* call before in the life-cycle of the application.
*
* @return if the conversation should be ended
*/
boolean
shouldEndConversation
();
boolean
shouldEndRoot
();
/**
* Returns the type used to start a new conversation. See {@link org.springframework.conversation.ConversationType}
* for a more detailed description of the different types available.<br/>
* Default type is {@link org.springframework.conversation.ConversationType#NEW} which will create a new root
* conversation and will automatically end the current one, if not ended before.
*
* @return the conversation type to use for creating a new conversation
*/
ConversationType
getConversationType
();
/**
* Returns the timeout to be set within the newly created conversation, default is <code>-1</code> which means to use
* the default timeout as being configured on the {@link org.springframework.conversation.ConversationManager}.
* A value of <code>0</code> means there is no timeout any other positive value is interpreted as a timeout in
* milliseconds.
*
* @return the timeout in milliseconds to be set on the new conversation
*/
int
getTimeout
();
}
org.springframework.context/src/main/java/org/springframework/conversation/interceptor/ConversationAttributeSource.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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.interceptor
;
import
java.lang.reflect.Method
;
/**
* Interface used by the ConversationInterceptor to retrieve the meta-data for the particular conversation. The
* meta-data can be provided by any implementation which is capable to return a ConversationAttribute instance based on
* a method and class. The implementation could be a annotation-based one or a XML-based implementation that retrieves
* the meta-data through a XML-configuration.
*
* @author Agim Emruli
*/
public
interface
ConversationAttributeSource
{
/**
* Resolves the ConversatioNAttribute for a particular method if available. This method must return null if there are
* no ConversationAttribute meta-data available for one particular method. It is up to the implementation to look for
* alternative sources like class-level annotations that applies to all methods inside a particular class.
*
* @param method The method for which the ConversationAttribute should be returned.
* @param targetClass The target class where the implementation should look for.
* @return the conversation attributes if available for the method, otherwise null.
*/
ConversationAttribute
getConversationAttribute
(
Method
method
,
Class
<?>
targetClass
);
}
org.springframework.context/src/main/java/org/springframework/conversation/interceptor/ConversationAttributeSourcePointcut.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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.interceptor
;
import
java.io.Serializable
;
import
java.lang.reflect.Method
;
import
org.springframework.aop.support.StaticMethodMatcherPointcut
;
/**
* Pointcut implementation that matches for methods where conversation meta-data is available for. This class is a base
* class for concrete Pointcut implementations that will provide the particular ConversationAttributeSource instance.
*
* @author Agim Emruli
*/
abstract
class
ConversationAttributeSourcePointcut
extends
StaticMethodMatcherPointcut
implements
Serializable
{
public
boolean
matches
(
Method
method
,
Class
<?>
targetClass
)
{
ConversationAttributeSource
attributeSource
=
getConversationAttributeSource
();
return
(
attributeSource
!=
null
&&
attributeSource
.
getConversationAttribute
(
method
,
targetClass
)
!=
null
);
}
/**
* @return - the ConversationAttributeSource instance that will be used to retrieve the conversation meta-data
*/
protected
abstract
ConversationAttributeSource
getConversationAttributeSource
();
}
org.springframework.context/src/main/java/org/springframework/conversation/interceptor/ConversationInterceptor.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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.interceptor
;
import
org.aopalliance.intercept.MethodInterceptor
;
import
org.aopalliance.intercept.MethodInvocation
;
import
org.springframework.conversation.Conversation
;
import
org.springframework.conversation.ConversationManager
;
/**
* MethodInterceptor that manages conversations based on ConversationAttribute meta-data.
*
* @author Agim Emruli
*/
public
class
ConversationInterceptor
implements
MethodInterceptor
{
private
ConversationManager
conversationManager
;
private
ConversationAttributeSource
conversationAttributeSource
;
/**
* Sets the ConversationManager implementation that will be used to actually handle the conversations.
*
* @see org.springframework.conversation.manager.DefaultConversationManager
*/
public
void
setConversationManager
(
ConversationManager
conversationManager
)
{
this
.
conversationManager
=
conversationManager
;
}
/**
* Sets the ConversationAttributeSource that will be used to retrieve the meta-data for one particular method at
* runtime.
*
* @see org.springframework.conversation.annotation.AnnotationConversationAttributeSource
*/
public
void
setConversationAttributeSource
(
ConversationAttributeSource
conversationAttributeSource
)
{
this
.
conversationAttributeSource
=
conversationAttributeSource
;
}
/**
* Advice implementations that actually handles the conversations. This method retrieves and consults the
* ConversationAttribute at runtime and performs the particular actions before and after the target method call.
*
* @param invocation The MethodInvocation that represents the context object for this interceptor.
*/
public
Object
invoke
(
MethodInvocation
invocation
)
throws
Throwable
{
Class
targetClass
=
(
invocation
.
getThis
()
!=
null
?
invocation
.
getThis
().
getClass
()
:
null
);
ConversationAttribute
conversationAttribute
=
conversationAttributeSource
.
getConversationAttribute
(
invocation
.
getMethod
(),
targetClass
);
Object
returnValue
;
try
{
if
(
conversationAttribute
!=
null
&&
conversationAttribute
.
shouldStartConversation
())
{
Conversation
conversation
=
conversationManager
.
beginConversation
(
conversationAttribute
.
getConversationType
());
if
(
conversationAttribute
.
getTimeout
()
!=
ConversationAttribute
.
DEFAULT_TIMEOUT
)
{
conversation
.
setTimeout
(
conversationAttribute
.
getTimeout
());
}
}
returnValue
=
invocation
.
proceed
();
if
(
conversationAttribute
!=
null
&&
conversationAttribute
.
shouldEndConversation
())
{
conversationManager
.
endCurrentConversation
(
conversationAttribute
.
shouldEndRoot
());
}
}
catch
(
Throwable
th
)
{
if
(
conversationAttribute
!=
null
)
{
conversationManager
.
endCurrentConversation
(
conversationAttribute
.
shouldEndRoot
());
}
throw
th
;
}
return
returnValue
;
}
}
org.springframework.context/src/main/java/org/springframework/conversation/interceptor/DefaultConversationAttribute.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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.interceptor
;
import
org.springframework.conversation.ConversationType
;
/**
* Default implementation of the ConversationAttribute used by the conversation system.
*
* @author Agim Emruli
*/
public
class
DefaultConversationAttribute
implements
ConversationAttribute
{
private
final
boolean
shouldStartConversation
;
private
final
boolean
shouldEndConversation
;
private
final
ConversationType
conversationType
;
private
final
boolean
shouldEndRoot
;
private
int
timeout
=
DEFAULT_TIMEOUT
;
private
DefaultConversationAttribute
(
boolean
startConversation
,
boolean
endConversation
,
ConversationType
conversationType
,
int
timeout
,
boolean
shouldEndRootConversation
)
{
shouldStartConversation
=
startConversation
;
shouldEndConversation
=
endConversation
;
this
.
conversationType
=
conversationType
;
this
.
timeout
=
timeout
;
this
.
shouldEndRoot
=
shouldEndRootConversation
;
}
public
DefaultConversationAttribute
(
ConversationType
conversationType
,
int
timeout
)
{
this
(
true
,
false
,
conversationType
,
timeout
,
false
);
}
public
DefaultConversationAttribute
(
boolean
shouldEndRoot
)
{
this
(
false
,
true
,
null
,
DEFAULT_TIMEOUT
,
shouldEndRoot
);
}
public
boolean
shouldStartConversation
()
{
return
shouldStartConversation
;
}
public
boolean
shouldEndConversation
()
{
return
shouldEndConversation
;
}
public
boolean
shouldEndRoot
()
{
return
shouldEndRoot
;
}
public
ConversationType
getConversationType
()
{
return
conversationType
;
}
public
int
getTimeout
()
{
return
timeout
;
}
}
org.springframework.context/src/main/java/org/springframework/conversation/manager/AbstractConversationRepository.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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
org.springframework.conversation.Conversation
;
/**
* An abstract implementation for a conversation repository. Its implementation is based on the
* {@link org.springframework.conversation.manager.DefaultConversation} and manages its initial timeout and provides
* easy removal functionality. Internally, there is no explicit check for the conversation object implementing the
* {@link MutableConversation} interface, it is assumed to be implemented as the abstract repository also creates the
* conversation objects.
*
* @author Micha Kiener
* @since 3.1
*/
public
abstract
class
AbstractConversationRepository
implements
ConversationRepository
{
/**
* The default timeout in seconds for new conversations, can be setup using the Spring configuration of the
* repository. A value of <code>0</code> means there is no timeout.
*/
private
int
defaultTimeout
=
0
;
/**
* Creates a new conversation object and initializes its timeout by using the default timeout being set on this
* repository.
*/
public
MutableConversation
createNewConversation
()
{
MutableConversation
conversation
=
new
DefaultConversation
();
conversation
.
setTimeout
(
getDefaultConversationTimeout
());
return
conversation
;
}
/**
* Creates a new conversation, attaches it to the parent and initializes its timeout as being set on the parent.
*/
public
MutableConversation
createNewChildConversation
(
MutableConversation
parentConversation
,
boolean
isIsolated
)
{
MutableConversation
childConversation
=
createNewConversation
();
parentConversation
.
addChildConversation
(
childConversation
,
isIsolated
);
childConversation
.
setTimeout
(
parentConversation
.
getTimeout
());
return
childConversation
;
}
/**
* Generic implementation of the remove method of a repository, handling the <code>root</code> flag automatically
* by invoking the {@link #removeConversation(org.springframework.conversation.Conversation)} by either passing in
* the root conversation or just the given conversation.<br/>
* Concrete repository implementations can overwrite the
* {@link #removeConversation(org.springframework.conversation.Conversation)} method to finally remove the
* conversation object or they might provide their own custom implementation for the remove operation by overwriting
* this method completely.
*
* @param id the id of the conversation to be removed
* @param root flag indicating whether the whole conversation hierarchy should be removed (<code>true</code>) or just
* the specified conversation
*/
public
void
removeConversation
(
String
id
,
boolean
root
)
{
MutableConversation
conversation
=
getConversation
(
id
);
if
(
conversation
==
null
)
{
return
;
}
if
(
root
)
{
removeConversation
((
MutableConversation
)
conversation
.
getRoot
());
}
else
{
removeConversation
(
conversation
);
}
}
/**
* Internal, final method recursively invoking this method for all children of the given conversation.
*
* @param conversation the conversation to be removed, including its children, if any
*/
protected
final
void
removeConversation
(
MutableConversation
conversation
)
{
for
(
Conversation
child
:
conversation
.
getChildren
())
{
// remove the child from its parent and recursively invoke this method to remove the children of the
// current conversation
conversation
.
removeChildConversation
((
MutableConversation
)
child
);
removeConversation
((
MutableConversation
)
child
);
}
// end the conversation (will internally clear the attributes, invoke destruction callbacks, if any, and
// invalidates the conversation
conversation
.
clear
();
conversation
.
invalidate
();
// finally, remove the single object from the repository
removeSingleConversationObject
((
MutableConversation
)
conversation
);
}
/**
* Abstract removal method to be implemented by concrete repository implementations to remove the given, single
* conversation object. Any parent and child relations must not be handled within this method, just the removal of
* the given object.
*
* @param conversation the single conversation object to be removed from this repository
*/
protected
abstract
void
removeSingleConversationObject
(
MutableConversation
conversation
);
public
void
setDefaultConversationTimeout
(
int
defaultTimeout
)
{
this
.
defaultTimeout
=
defaultTimeout
;
}
public
int
getDefaultConversationTimeout
()
{
return
defaultTimeout
;
}
}
org.springframework.context/src/main/java/org/springframework/conversation/manager/ConversationRepository.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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
;
/**
* The conversation repository is responsible for creating new conversation objects, store them within its own storage
* and finally remove the after they have been ended.<br/>
* The repository might be transient (most likely in a web environment) but might support long running, persisted
* conversations as well.<br/>
* The repository is responsible for the timeout management of the conversation objects as well and might use an
* existing mechanism to do so (in a distributed, cached storage for instance).<br/>
* Depending on the underlying storage mechanism, the repository might support destruction callbacks within the
* conversation objects or not. If they are not supported, make sure an appropriate warning will be logged if
* registering a destruction callback.
*
* @author Micha Kiener
* @since 3.1
*/
public
interface
ConversationRepository
{
/**
* Creates a new root conversation object and returns it. Be aware that this method does not store the conversation,
* this has to be done using the {@link #storeConversation(MutableConversation)} method. The id of the conversation
* will typically be set within the store method rather than the creation method.
*
* @return the newly created conversation object
*/
MutableConversation
createNewConversation
();
/**
* Creates a new child conversation and attaches it as a child to the given parent conversation. Like the
* {@link #createNewConversation()} method, this one does not store the new child conversation object, this has to
* be done using the {@link #storeConversation(MutableConversation)} method. The id of the new child conversation
* will typically be set within the store method rather than the creation method.
*
* @param parentConversation the parent conversation to create and attach a new child conversation to
* @param isIsolated <code>true</code> if the new child conversation has to be isolated from its parent state,
* <code>false</code> if it will inherit the state from the given parent
* @return the newly created child conversation, attached to the given parent conversation
*/
MutableConversation
createNewChildConversation
(
MutableConversation
parentConversation
,
boolean
isIsolated
);
/**
* Returns the conversation with the given id which has to be registered before. If no such conversation is found,
* <code>null</code> is returned rather than throwing an exception.
*
* @param id the id to return the conversation from this store
* @return the conversation, if found, <code>null</code> otherwise
*/
MutableConversation
getConversation
(
String
id
);
/**
* Stores the given conversation object within this repository. Depending on its implementation, the storage might be
* transient or persistent, it might relay on other mechanisms like a session (in the area of web conversations for
* instance). After the conversation has been stored, its id must be set hence the id of the conversation will be
* available only after the store method has been invoked.
*
* @param conversation the conversation to be stored within the repository
*/
void
storeConversation
(
MutableConversation
conversation
);
/**
* Removes the conversation with the given id from this store. Depending on the <code>root</code> flag, the whole
* conversation hierarchy is removed or just the specified conversation.
*
* @param id the id of the conversation to be removed
* @param root flag indicating whether the whole conversation hierarchy should be removed (<code>true</code>) or just
* the specified conversation (<code>false</code>)
*/
void
removeConversation
(
String
id
,
boolean
root
);
}
org.springframework.context/src/main/java/org/springframework/conversation/manager/ConversationResolver.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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
;
/**
* The current conversation resolver is another extension point for the conversation manager to easily change the
* default behavior of storing the currently used conversation id.
*
* In a web environment, the current conversation id would most likely be bound to the current window / tab and hence be
* based on the window management. This makes it possible to run different conversations per browser window and
* isolating them from each other by default.
*
* In a unit-test or batch environment the current conversation could be bound to the current thread to make
* conversations be available in a non-web environment as well.
*
* @author Micha Kiener
* @since 3.1
*/
public
interface
ConversationResolver
{
/**
* Returns the id of the currently used conversation, if any, <code>null</code> otherwise.
*
* @return the id of the current conversation, if any, <code>null</code> otherwise
*/
String
getCurrentConversationId
();
/**
* Set the given conversation id to be the currently used one. Replaces the current one, if any, but is not removing
* the current conversation.
*
* @param conversationId the id of the conversation to be made the current one
*/
void
setCurrentConversationId
(
String
conversationId
);
}
org.springframework.context/src/main/java/org/springframework/conversation/manager/DefaultConversation.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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.io.Serializable
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.List
;
import
org.springframework.beans.factory.config.DestructionAwareAttributeHolder
;
import
org.springframework.conversation.Conversation
;
/**
* <p> The default implementation of the {@link org.springframework.conversation.Conversation} and {@link
* MutableConversation} interfaces.<br/>
* This default implementation is also used within the {@link AbstractConversationRepository}.
* </p>
* <p>
* The implementation supports destruction callbacks for attributes. The conversation object is serializable as long as
* all of its attributes are serializable as well.
* </p>
*
* @author Micha Kiener
* @since 3.1
*/
public
class
DefaultConversation
implements
MutableConversation
,
Serializable
{
/** Serializable identifier. */
private
static
final
long
serialVersionUID
=
1L
;
/** The conversation id which must be unique within the scope of its storage. The id is set by the repository. */
private
String
id
;
/** The parent conversation, if this is a nested or isolated conversation. */
private
MutableConversation
parent
;
/** The optional nested conversation(s), if this is a parent conversation. */
private
List
<
MutableConversation
>
children
;
/** The map with all the registered attributes and destruction callbacks. */
private
DestructionAwareAttributeHolder
attributes
=
new
DestructionAwareAttributeHolder
();
/**
* If set to <code>true</code>, this conversation does not inherit the state of its parent but rather has its own,
* isolated state. This is set to <code>true</code>, if a new conversation with
* {@link org.springframework.conversation.ConversationType#ISOLATED} is created.
*/
private
boolean
isolated
;
/** The timeout in seconds or <code>0</code>, if no timeout specified. */
private
int
timeout
;
/** The system timestamp of the creation of this conversation. */
private
final
long
creationTime
=
System
.
currentTimeMillis
();
/** The timestamp in milliseconds of the last access to this conversation. */
private
long
lastAccess
;
/** Flag indicating whether this conversation has been invalidated already. */
private
boolean
invalidated
;
public
DefaultConversation
()
{
touch
();
}
/**
* Considers the internal attribute map as well as the map from the parent, if this is a nested conversation and only
* if it is not isolated.
*/
public
Object
getAttribute
(
String
name
)
{
checkValidity
();
touch
();
// first try to get the attribute from this conversation state
Object
value
=
attributes
.
getAttribute
(
name
);
if
(
value
!=
null
)
{
return
value
;
}
// the value was not found, try the parent conversation, if any and if
// not isolated
if
(
parent
!=
null
&&
!
isolated
)
{
return
parent
.
getAttribute
(
name
);
}
// this is the root conversation and the requested bean is not
// available, so return null instead
return
null
;
}
public
Object
setAttribute
(
String
name
,
Object
value
)
{
checkValidity
();
touch
();
return
attributes
.
setAttribute
(
name
,
value
);
}
public
Object
removeAttribute
(
String
name
)
{
checkValidity
();
touch
();
return
attributes
.
removeAttribute
(
name
);
}
public
void
clear
()
{
attributes
.
clear
();
}
public
void
setId
(
String
id
)
{
this
.
id
=
id
;
}
public
String
getId
()
{
return
id
;
}
public
Conversation
getRoot
()
{
// check for having a parent to be returned as the root
if
(
parent
!=
null
)
{
return
parent
.
getRoot
();
}
return
this
;
}
public
Conversation
getParent
()
{
return
parent
;
}
public
List
<?
extends
Conversation
>
getChildren
()
{
if
(
children
==
null
){
return
Collections
.
emptyList
();
}
return
children
;
}
protected
void
setParentConversation
(
MutableConversation
parentConversation
,
boolean
isIsolated
)
{
checkValidity
();
this
.
parent
=
parentConversation
;
this
.
isolated
=
isIsolated
;
}
public
void
addChildConversation
(
MutableConversation
conversation
,
boolean
isIsolated
)
{
checkValidity
();
if
(
conversation
instanceof
DefaultConversation
)
{
// set this conversation as the parent within the given child conversation
((
DefaultConversation
)
conversation
).
setParentConversation
(
this
,
isIsolated
);
}
if
(
children
==
null
)
{
children
=
new
ArrayList
<
MutableConversation
>();
}
children
.
add
(
conversation
);
}
public
void
removeChildConversation
(
MutableConversation
conversation
)
{
if
(
children
!=
null
)
{
children
.
remove
(
conversation
);
if
(
children
.
size
()
==
0
)
{
children
=
null
;
}
}
}
protected
void
removeFromParent
()
{
if
(
parent
!=
null
)
{
parent
.
removeChildConversation
(
this
);
}
parent
=
null
;
}
public
boolean
isNested
()
{
return
(
parent
!=
null
);
}
public
boolean
isParent
()
{
return
(
children
!=
null
&&
children
.
size
()
>
0
);
}
public
boolean
isIsolated
()
{
return
isolated
;
}
/**
* Always returns the timeout value being set on the root as the root conversation is responsible for the timeout management.
*/
public
int
getTimeout
()
{
if
(
parent
==
null
)
{
return
timeout
;
}
else
{
return
getRoot
().
getTimeout
();
}
}
/**
* The timeout will be set on the root only.
*/
public
void
setTimeout
(
int
timeout
)
{
if
(
parent
==
null
)
{
this
.
timeout
=
timeout
;
}
else
{
getRoot
().
setTimeout
(
timeout
);
}
}
public
long
getCreationTime
()
{
return
creationTime
;
}
public
long
getLastAccessedTime
()
{
return
lastAccess
;
}
public
void
invalidate
()
{
invalidated
=
true
;
clear
();
}
protected
void
checkValidity
()
{
if
(
invalidated
)
{
throw
new
IllegalStateException
(
"The conversation has been invalidated!"
);
}
}
/**
* Return <code>true</code> if the top root conversation has expired as the timeout is only tracked on the
* root conversation.
*
* @return <code>true</code> if the root of this conversation has been expired
*/
public
boolean
isExpired
()
{
if
(
parent
!=
null
)
{
return
parent
.
isExpired
();
}
return
(
timeout
!=
0
&&
(
lastAccess
+
timeout
*
1000
<
System
.
currentTimeMillis
()));
}
public
void
touch
()
{
lastAccess
=
System
.
currentTimeMillis
();
// if this is a nested conversation, also touch its parent to make sure
// the parent is never timed out, if the
// current conversation is one of its nested conversations
if
(
parent
!=
null
)
{
parent
.
touch
();
}
}
public
void
registerDestructionCallback
(
String
name
,
Runnable
callback
)
{
attributes
.
registerDestructionCallback
(
name
,
callback
);
}
}
org.springframework.context/src/main/java/org/springframework/conversation/manager/DefaultConversationManager.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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
org.springframework.conversation.ConversationManager
;
import
org.springframework.conversation.ConversationType
;
/**
* The default implementation for the {@link org.springframework.conversation.ConversationManager} interface.
*
* @author Micha Kiener
* @since 3.1
*/
public
class
DefaultConversationManager
implements
ConversationManager
{
/**
* The conversation resolver used to resolve and expose the currently used conversation id. Do not use this attribute
* directly, always use the {@link #getConversationResolver()} method as the getter could have been injected.
*/
private
ConversationResolver
conversationResolver
;
/**
* If the store was injected, this attribute holds the reference to it. Never use the attribute directly, always use
* the {@link #getConversationRepository()} getter as it could have been injected.
*/
private
ConversationRepository
conversationRepository
;
public
MutableConversation
getCurrentConversation
()
{
return
getCurrentConversation
(
true
);
}
public
MutableConversation
getCurrentConversation
(
boolean
createNewIfNotExisting
)
{
ConversationResolver
resolver
=
getConversationResolver
();
MutableConversation
currentConversation
=
null
;
String
conversationId
=
resolver
.
getCurrentConversationId
();
if
(
conversationId
!=
null
)
{
ConversationRepository
repository
=
getConversationRepository
();
currentConversation
=
repository
.
getConversation
(
conversationId
);
}
if
(
currentConversation
==
null
&&
createNewIfNotExisting
)
{
currentConversation
=
beginConversation
(
ConversationType
.
NEW
);
}
return
currentConversation
;
}
/**
* The implementation uses the conversation repository to create a new root or a new child conversation depending on
* the conversation type specified.
*
* @param conversationType the type used to start a new conversation
* @return the newly created conversation
*/
public
MutableConversation
beginConversation
(
ConversationType
conversationType
)
{
ConversationRepository
repository
=
getConversationRepository
();
ConversationResolver
resolver
=
getConversationResolver
();
MutableConversation
newConversation
=
null
;
switch
(
conversationType
)
{
case
NEW:
// end the current conversation and create a new root one
endCurrentConversation
(
true
);
newConversation
=
repository
.
createNewConversation
();
break
;
case
NESTED:
case
ISOLATED:
MutableConversation
parentConversation
=
getCurrentConversation
(
false
);
// if a parent conversation is available, add the new conversation as its child conversation
if
(
parentConversation
!=
null
)
{
newConversation
=
repository
.
createNewChildConversation
(
parentConversation
,
conversationType
==
ConversationType
.
ISOLATED
);
}
else
{
// if no parent conversation found, create a new root one
newConversation
=
repository
.
createNewConversation
();
}
break
;
}
// store the newly created conversation within its store and make it the current one through the resolver
repository
.
storeConversation
(
newConversation
);
resolver
.
setCurrentConversationId
(
newConversation
.
getId
());
return
newConversation
;
}
/**
* The implementation only resolves the current conversation object using the repository, if only the given
* current conversation object and not the whole conversation hierarchy should be removed which can improve the
* removal from the underlying storage mechanism.
*
* @param root <code>true</code> to end the whole current conversation hierarchy or <code>false</code> to just
* remove the current conversation
*/
public
void
endCurrentConversation
(
boolean
root
)
{
// remove the conversation from the repository and clear the current conversation id through the resolver
ConversationResolver
resolver
=
getConversationResolver
();
ConversationRepository
repository
=
getConversationRepository
();
String
currentConversationId
=
resolver
.
getCurrentConversationId
();
if
(
currentConversationId
==
null
)
{
return
;
}
// if only the current conversation has to be removed without the full conversation hierarchy, the
// current conversation must be set to the parent, if available
if
(!
root
)
{
MutableConversation
currentConversation
=
repository
.
getConversation
(
currentConversationId
);
if
(
currentConversation
!=
null
&&
currentConversation
.
getParent
()
!=
null
)
{
MutableConversation
parentConversation
=
(
MutableConversation
)
currentConversation
.
getParent
();
resolver
.
setCurrentConversationId
(
parentConversation
.
getId
());
}
}
repository
.
removeConversation
(
currentConversationId
,
root
);
}
/**
* Returns the conversation resolver used to resolve the currently used conversation id. If the resolver itself has
* another scope than the manager, this method must be injected.
*
* @return the conversation resolver
*/
public
ConversationResolver
getConversationResolver
()
{
return
conversationResolver
;
}
/**
* Inject the conversation resolver, if the method {@link #getConversationResolver()} is not injected and if the
* resolver has the same scope as the manager or even a more wider scope.
*
* @param conversationResolver the resolver to be injected
*/
public
void
setConversationResolver
(
ConversationResolver
conversationResolver
)
{
this
.
conversationResolver
=
conversationResolver
;
}
/**
* Returns the repository where conversation objects are being registered. If the manager is in a wider scope than the
* repository, this method has to be injected.
*
* @return the conversation repository used to register conversation objects
*/
public
ConversationRepository
getConversationRepository
()
{
return
conversationRepository
;
}
/**
* Inject the conversation repository to this manager which should only be done, if the method {@link
* #getConversationRepository()} is not injected and hence the repository has the same scope as the manager or wider.
*
* @param conversationRepository the repository to be injected
*/
public
void
setConversationRepository
(
ConversationRepository
conversationRepository
)
{
this
.
conversationRepository
=
conversationRepository
;
}
}
org.springframework.context/src/main/java/org/springframework/conversation/manager/LocalTransientConversationRepository.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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.concurrent.ConcurrentHashMap
;
import
java.util.concurrent.ConcurrentMap
;
import
java.util.concurrent.atomic.AtomicInteger
;
import
org.springframework.conversation.Conversation
;
/**
* A {@link ConversationRepository} storing the conversations within an internal map and hence assuming the conversation
* objects being transient.
*
* @author Micha Kiener
* @since 3.1
*/
public
class
LocalTransientConversationRepository
extends
AbstractConversationRepository
{
/** The map for the conversation storage. */
private
final
ConcurrentMap
<
String
,
MutableConversation
>
conversationMap
=
new
ConcurrentHashMap
<
String
,
MutableConversation
>();
/** Using an atomic integer, there is no need for synchronization while increasing the number. */
private
final
AtomicInteger
nextAvailableConversationId
=
new
AtomicInteger
(
0
);
public
MutableConversation
getConversation
(
String
id
)
{
return
conversationMap
.
get
(
id
);
}
public
void
storeConversation
(
MutableConversation
conversation
)
{
conversation
.
setId
(
createNextConversationId
());
conversationMap
.
put
(
conversation
.
getId
(),
conversation
);
}
@Override
protected
void
removeSingleConversationObject
(
MutableConversation
conversation
)
{
conversationMap
.
remove
(
conversation
.
getId
());
}
public
List
<
Conversation
>
getConversations
()
{
return
new
ArrayList
<
Conversation
>(
conversationMap
.
values
());
}
public
int
size
()
{
return
conversationMap
.
size
();
}
protected
String
createNextConversationId
(){
return
Integer
.
toString
(
nextAvailableConversationId
.
incrementAndGet
());
}
}
org.springframework.context/src/main/java/org/springframework/conversation/manager/MutableConversation.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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
org.springframework.conversation.Conversation
;
/**
* <p>
* This interface extends the {@link Conversation} interface and is most likely used internally to modify the
* conversation object. Should never be used outside of the conversation management infrastructure.
* </p>
*
* @author Micha Kiener
* @since 3.1
*/
public
interface
MutableConversation
extends
Conversation
{
/**
* Set the id for this conversation which must be unique within the scope the conversation objects are being stored.
* The id of the conversation objects is usually managed by the {@link ConversationRepository}.
*
* @param id the id of the conversation
*/
void
setId
(
String
id
);
/**
* Method being invoked to add the given conversation as a child conversation to this parent conversation. If
* <code>isIsolated</code> is <code>true</code>, the state of the child conversation is isolated from its parent
* state, if it is set to <code>false</code>, the child conversation will inherit the state from its parent.
*
* @param conversation the conversation to be added as a child to this parent conversation
* @param isIsolated flag indicating whether this conversation should be isolated from the given parent conversation
*/
void
addChildConversation
(
MutableConversation
conversation
,
boolean
isIsolated
);
/**
* Removes the given child conversation from this parent conversation.
*
* @param conversation the conversation to be removed from this one
*/
void
removeChildConversation
(
MutableConversation
conversation
);
/**
* Reset the last access timestamp using the current time in milliseconds from the system. This is usually done if a
* conversation is used behind a scope and beans are being accessed or added to it.
*/
void
touch
();
/**
* Clears the state of this conversation by removing all of its attributes. It will, however, not invalidate the
* conversation. All attributes having a destruction callback being registered will fire, if the underlying
* {@link ConversationRepository} supports destruction callbacks.
*/
void
clear
();
/**
* Returns <code>true</code> if this conversation has been expired. The expiration time (timeout) is only managed
* on the root conversation and is valid for the whole conversation hierarchy.
*
* @return <code>true</code> if this conversation has been expired, <code>false</code> otherwise
*/
boolean
isExpired
();
/**
* Invalidates this conversation object. An invalidated conversation will throw an {@link IllegalStateException},
* if it is accessed or modified.
*/
void
invalidate
();
/**
* Registers the given callback to be invoked if the attribute having the specified name is being removed from this
* conversation. Supporting destruction callbacks is dependant of the underlying {@link ConversationRepository}, so
* this operation is optional and might not be supported.
*
* @param attributeName the name of the attribute to register the destruction callback for
* @param callback the callback to be invoked if the specified attribute is removed from this conversation
*/
void
registerDestructionCallback
(
String
attributeName
,
Runnable
callback
);
}
org.springframework.context/src/main/java/org/springframework/conversation/manager/ThreadLocalConversationResolver.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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
org.springframework.core.NamedThreadLocal
;
/**
* An implementation of the {@link org.springframework.conversation.manager.ConversationResolver} where the currently
* used conversation id is bound to the current thread.
* If this implementation is used in a web environment, make sure the current conversation id is set and removed through
* a filter accordingly as the id is bound to the current thread using a thread local.
*
* @author Micha Kiener
* @since 3.1
*/
public
class
ThreadLocalConversationResolver
implements
ConversationResolver
{
/** The thread local attribute where the current conversation id is stored. */
private
final
NamedThreadLocal
<
String
>
currentConversationId
=
new
NamedThreadLocal
<
String
>(
"Current Conversation"
);
public
String
getCurrentConversationId
()
{
return
currentConversationId
.
get
();
}
public
void
setCurrentConversationId
(
String
conversationId
)
{
currentConversationId
.
set
(
conversationId
);
if
(
conversationId
==
null
)
{
currentConversationId
.
remove
();
}
}
}
org.springframework.context/src/main/java/org/springframework/conversation/scope/ConversationScope.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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.scope
;
import
org.springframework.beans.factory.ObjectFactory
;
import
org.springframework.beans.factory.config.Scope
;
import
org.springframework.conversation.Conversation
;
import
org.springframework.conversation.ConversationManager
;
import
org.springframework.conversation.manager.MutableConversation
;
/**
* The default implementation of a conversation scope most likely being exposed as <code>"conversation"</code> scope.
* It uses the {@link ConversationManager} to get access to the current conversation being used as the container for
* storing and retrieving attributes and beans.
*
* @author Micha Kiener
* @since 3.1
*/
public
class
ConversationScope
implements
Scope
{
/** Holds the conversation manager reference, if statically injected. */
private
ConversationManager
conversationManager
;
/** The name of the current conversation object, made available through {@link #resolveContextualObject(String)}. */
public
static
final
String
CURRENT_CONVERSATION_ATTRIBUTE_NAME
=
"currentConversation"
;
public
Object
get
(
String
name
,
ObjectFactory
<?>
objectFactory
)
{
Conversation
conversation
=
getConversationManager
().
getCurrentConversation
(
true
);
Object
attribute
=
conversation
.
getAttribute
(
name
);
if
(
attribute
==
null
)
{
attribute
=
objectFactory
.
getObject
();
conversation
.
setAttribute
(
name
,
attribute
);
}
return
attribute
;
}
/**
* Will return <code>null</code> if there is no current conversation. It will not implicitly start a new one, if
* no current conversation object in place.
*/
public
String
getConversationId
()
{
Conversation
conversation
=
getConversationManager
().
getCurrentConversation
(
false
);
if
(
conversation
!=
null
)
{
return
conversation
.
getId
();
}
return
null
;
}
/**
* Registering a destruction callback is only possible, if supported by the underlying
* {@link org.springframework.conversation.manager.ConversationRepository}.
*/
public
void
registerDestructionCallback
(
String
name
,
Runnable
callback
)
{
Conversation
conversation
=
getConversationManager
().
getCurrentConversation
(
false
);
if
(
conversation
instanceof
MutableConversation
)
{
((
MutableConversation
)
conversation
).
registerDestructionCallback
(
name
,
callback
);
}
}
public
Object
remove
(
String
name
)
{
Conversation
conversation
=
getConversationManager
().
getCurrentConversation
(
false
);
if
(
conversation
!=
null
)
{
return
conversation
.
removeAttribute
(
name
);
}
return
null
;
}
/**
* Supports the following contextual objects:
* <ul>
* <li><code>"currentConversation"</code>, returns the current {@link org.springframework.conversation.Conversation}</li>
* </ul>
*
* @see org.springframework.beans.factory.config.Scope#resolveContextualObject(String)
*/
public
Object
resolveContextualObject
(
String
key
)
{
if
(
CURRENT_CONVERSATION_ATTRIBUTE_NAME
.
equals
(
key
))
{
return
getConversationManager
().
getCurrentConversation
(
true
);
}
return
null
;
}
public
void
setConversationManager
(
ConversationManager
conversationManager
)
{
this
.
conversationManager
=
conversationManager
;
}
public
ConversationManager
getConversationManager
()
{
return
conversationManager
;
}
}
org.springframework.context/template.mf
浏览文件 @
f812cd74
...
...
@@ -35,6 +35,7 @@ Import-Template:
org.springframework.aop.*;version=${spring.osgi.range};resolution:=optional,
org.springframework.beans.*;version=${spring.osgi.range},
org.springframework.core.*;version=${spring.osgi.range},
org.springframework.conversation.*;version=${spring.osgi.range},
org.springframework.expression.*;version=${spring.osgi.range};resolution:=optional,
org.springframework.instrument.*;version="0";resolution:=optional,
org.springframework.util.*;version=${spring.osgi.range},
...
...
org.springframework.web/src/main/java/org/springframework/web/conversation/SessionBasedConversationRepository.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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.web.conversation
;
import
java.io.Serializable
;
import
org.springframework.conversation.Conversation
;
import
org.springframework.conversation.manager.AbstractConversationRepository
;
import
org.springframework.conversation.manager.MutableConversation
;
import
org.springframework.util.Assert
;
import
org.springframework.web.context.request.RequestAttributes
;
import
org.springframework.web.context.request.RequestContextHolder
;
/**
* A conversation store implementation ({@link org.springframework.conversation.manager.ConversationRepository}) based
* on a web environment where the conversations are most likely to be stored in the current session.
*
* @author Micha Kiener
* @since 3.1
*/
public
class
SessionBasedConversationRepository
extends
AbstractConversationRepository
{
/** The name of the attribute for the conversation map within the session. */
public
static
final
String
CONVERSATION_STORE_ATTR_NAME
=
SessionBasedConversationRepository
.
class
.
getName
();
public
MutableConversation
getConversation
(
String
id
)
{
RequestAttributes
attributes
=
RequestContextHolder
.
currentRequestAttributes
();
Object
conversation
=
attributes
.
getAttribute
(
getSessionAttributeNameForConversationId
(
id
),
RequestAttributes
.
SCOPE_GLOBAL_SESSION
);
Assert
.
isInstanceOf
(
Conversation
.
class
,
conversation
);
return
(
MutableConversation
)
conversation
;
}
public
void
storeConversation
(
MutableConversation
conversation
)
{
conversation
.
setId
(
createNextConversationId
());
RequestAttributes
attributes
=
RequestContextHolder
.
currentRequestAttributes
();
String
conversationSessionAttributeName
=
getSessionAttributeNameForConversationId
(
conversation
.
getId
());
attributes
.
setAttribute
(
conversationSessionAttributeName
,
conversation
,
RequestAttributes
.
SCOPE_GLOBAL_SESSION
);
attributes
.
registerDestructionCallback
(
conversationSessionAttributeName
,
new
ConversationDestructionCallback
(
conversation
),
RequestAttributes
.
SCOPE_GLOBAL_SESSION
);
}
@Override
protected
void
removeSingleConversationObject
(
MutableConversation
conversation
)
{
RequestAttributes
attributes
=
RequestContextHolder
.
currentRequestAttributes
();
attributes
.
removeAttribute
(
getSessionAttributeNameForConversationId
(
conversation
.
getId
()),
RequestAttributes
.
SCOPE_GLOBAL_SESSION
);
}
protected
String
getSessionAttributeNameForConversationId
(
String
conversationId
)
{
return
CONVERSATION_STORE_ATTR_NAME
+
conversationId
;
}
protected
String
createNextConversationId
()
{
int
nextAvailableConversationId
=
1
;
RequestAttributes
attributes
=
RequestContextHolder
.
currentRequestAttributes
();
synchronized
(
attributes
.
getSessionMutex
())
{
String
[]
attributeNames
=
attributes
.
getAttributeNames
(
RequestAttributes
.
SCOPE_GLOBAL_SESSION
);
for
(
String
attributeName
:
attributeNames
)
{
if
(
attributeName
.
startsWith
(
CONVERSATION_STORE_ATTR_NAME
))
{
MutableConversation
conversation
=
(
MutableConversation
)
attributes
.
getAttribute
(
attributeName
,
RequestAttributes
.
SCOPE_GLOBAL_SESSION
);
if
(
conversation
.
isExpired
())
{
conversation
.
clear
();
attributes
.
removeAttribute
(
attributeName
,
RequestAttributes
.
SCOPE_GLOBAL_SESSION
);
}
String
conversationId
=
conversation
.
getId
();
int
currentConversationId
=
Integer
.
parseInt
(
conversationId
);
if
(
currentConversationId
>
nextAvailableConversationId
)
{
nextAvailableConversationId
=
currentConversationId
;
}
}
}
}
return
Integer
.
toString
(
nextAvailableConversationId
);
}
private
static
final
class
ConversationDestructionCallback
implements
Runnable
,
Serializable
{
private
final
MutableConversation
conversation
;
private
ConversationDestructionCallback
(
MutableConversation
conversation
)
{
this
.
conversation
=
conversation
;
}
public
void
run
()
{
conversation
.
clear
();
}
}
}
org.springframework.web/src/main/java/org/springframework/web/conversation/WebAwareConversationScope.java
0 → 100644
浏览文件 @
f812cd74
/*
* Copyright 2002-2011 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.web.conversation
;
import
org.springframework.conversation.scope.ConversationScope
;
import
org.springframework.web.context.request.RequestAttributes
;
import
org.springframework.web.context.request.RequestContextHolder
;
/**
* The extension of the default conversation scope ( {@link ConversationScope}) by supporting contextual web
* objects returned by overwriting {@link #resolveContextualObject(String)}.
*
* @author Micha Kiener
* @since 3.1
*/
public
class
WebAwareConversationScope
extends
ConversationScope
{
/** @see org.springframework.conversation.scope.ConversationScope#resolveContextualObject(String) */
@Override
public
Object
resolveContextualObject
(
String
key
)
{
// invoke super to support the conversation context objects
Object
object
=
super
.
resolveContextualObject
(
key
);
if
(
object
!=
null
)
{
return
object
;
}
// support web context objects
RequestAttributes
attributes
=
RequestContextHolder
.
currentRequestAttributes
();
return
attributes
.
resolveReference
(
key
);
}
}
org.springframework.web/template.mf
浏览文件 @
f812cd74
...
...
@@ -27,6 +27,8 @@ Import-Template:
org.springframework.beans.*;version=${spring.osgi.range},
org.springframework.context.*;version=${spring.osgi.range},
org.springframework.core.*;version=${spring.osgi.range},
org.springframework.conversation.*;version=${spring.osgi.range},
org.springframework.web.conversation.*;version=${spring.osgi.range},
org.springframework.jndi.*;version=${spring.osgi.range};resolution:=optional,
org.springframework.oxm.*;version=${spring.osgi.range};resolution:=optional,
org.springframework.remoting.*;version=${spring.osgi.range};resolution:=optional,
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录