1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.myfaces.orchestra.conversation.spring;
21
22 import org.apache.myfaces.orchestra.conversation.Conversation;
23 import org.apache.myfaces.orchestra.conversation.ConversationAspect;
24 import org.apache.myfaces.orchestra.conversation.ConversationContext;
25 import org.apache.myfaces.orchestra.conversation.ConversationFlashLifetimeAspect;
26 import org.apache.myfaces.orchestra.conversation.ConversationTimeoutableAspect;
27
28 /***
29 * Handles creation and lookup of any bean whose bean-definition specifies a scope
30 * that maps to an instance of this type.
31 * <p>
32 * A scope bean handles Spring-specific callbacks in order to locate or create any beans whose definition
33 * specifies that scope. A scope can also be thought of as a "conversation template", as this object
34 * is responsible for creating a conversation when one is needed but does not yet exist.
35 * <p>
36 * <h2>Example</h2>
37 * A sample configuration for a conversation scope with persistence:
38 * <pre>
39 * <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
40 * <property name="scopes">
41 * <map>
42 * <entry key="conversation.manual">
43 * <bean class="org.apache.myfaces.orchestra.conversation.spring.SpringConversationScope">
44 * <property name="advices">
45 * <list>
46 * <ref bean="persistentContextConversationInterceptor" />
47 * </list>
48 * </property>
49 * </bean>
50 * </entry>
51 * </map>
52 * </property>
53 * </bean>
54 *
55 *
56 * <bean id="persistentContextConversationInterceptor" class="org.apache.myfaces.orchestra.conversation.spring.PersistenceContextConversationInterceptor">
57 * <property name="persistenceContextFactory" ref="yourPersistentContextFactory" />
58 * </bean>
59 * </pre>
60 * <p>
61 *
62 * <h2>Conversation properties</h2>
63 * The following properties can be defined on a scope and then apply to any conversation that is created
64 * to hold a bean of this scope:
65 * <ul>
66 * <li>lifetime: may be "manual" or "flash". If not specified, then defaults to "manual".</li>
67 * <li>timeout: a time period (in minutes) after which inactive conversations are terminated.
68 * If not specified, then inactive conversations are never automatically terminated.</li>
69 * </ul>
70 *
71 * <h2>Other Notes</h2>
72 * If the bean definition does not specify a conversation name, then the bean name is used
73 * as the conversation name.
74 * <p>
75 * As shown above, a list of AOP Advices can be specified for the scope, in which case each of the
76 * advices gets configured for any bean declared with this scope.
77 */
78 public class SpringConversationScope extends AbstractSpringOrchestraScope
79 {
80 public static final String LIFETIME_FLASH = "flash";
81 public static final String LIFETIME_MANUAL = "manual";
82
83 private Integer timeout;
84 private String lifetime = "manual";
85
86 /***
87 * See {@link #setTimeout}.
88 */
89 public Integer getTimeout()
90 {
91 return timeout;
92 }
93
94 /***
95 * The timeout in minutes when the conversation will end.
96 * See {@link ConversationTimeoutableAspect#timeout} for the default timeout.
97 */
98 public void setTimeout(Integer timeout)
99 {
100 this.timeout = timeout;
101 }
102
103 /***
104 * See {@link #setLifetime}.
105 */
106 public String getLifetime()
107 {
108 return lifetime;
109 }
110
111 /***
112 * Must be one of "flash" or "manual".
113 * <p>
114 * Defaults to "manual".
115 */
116 public void setLifetime(String lifetime)
117 {
118
119
120 if (LIFETIME_FLASH.equals(lifetime))
121 {
122 this.lifetime = LIFETIME_FLASH;
123 }
124 else if (LIFETIME_MANUAL.equals(lifetime))
125 {
126 this.lifetime = LIFETIME_MANUAL;
127 }
128 else
129 {
130 throw new IllegalArgumentException("Invalid lifetime:" + lifetime);
131 }
132 }
133
134 /***
135 * Implementation of ConversationFactory interface.
136 */
137 public Conversation createConversation(ConversationContext context, String name)
138 {
139 Conversation conversation = new Conversation(context, name, this);
140
141
142 initAspects(conversation);
143
144 return conversation;
145 }
146
147 /***
148 * Add aspects to a newly-created conversation.
149 * <p>
150 * Subclasses are expected to call super.initAspects, then make
151 * zero or more calls to conversation.addAspect.
152 */
153 protected void initAspects(Conversation conversation)
154 {
155
156 if (timeout != null)
157 {
158 long timeoutMsecs = timeout.longValue() * 60L * 1000L;
159 ConversationTimeoutableAspect aspect = new ConversationTimeoutableAspect(conversation);
160 aspect.setTimeout(timeoutMsecs);
161 conversation.addAspect(aspect);
162 }
163
164
165 if (LIFETIME_FLASH == lifetime)
166 {
167 ConversationAspect aspect = new ConversationFlashLifetimeAspect(conversation);
168 conversation.addAspect(aspect);
169 }
170 }
171
172 /***
173 * Mark the specified conversation as having been accessed.
174 * <p>
175 * This affects conversation timeouts, and the removal of flash conversations.
176 */
177 protected void notifyAccessConversation(Conversation conversation)
178 {
179 super.notifyAccessConversation(conversation);
180
181 ConversationFlashLifetimeAspect aspect = (ConversationFlashLifetimeAspect) conversation.getAspect(ConversationFlashLifetimeAspect.class);
182 if (aspect != null)
183 {
184 aspect.markAsAccessed();
185 }
186 }
187 }