View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
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   * &lt;bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"&gt;
40   *   &lt;property name="scopes"&gt;
41   *     &lt;map&gt;
42   *       &lt;entry key="conversation.manual"&gt;
43   *         &lt;bean class="org.apache.myfaces.orchestra.conversation.spring.SpringConversationScope"&gt;
44   *           &lt;property name="advices"&gt;
45   *             &lt;list&gt;
46   *               &lt;ref bean="persistentContextConversationInterceptor" /&gt;
47   *             &lt;/list&gt;
48   *           &lt;/property&gt;
49   *         &lt;/bean&gt;
50   *       &lt;/entry&gt;
51   *     &lt;/map&gt;
52   *   &lt;/property&gt;
53   * &lt;/bean&gt;
54   * 
55   * 
56   * &lt;bean id="persistentContextConversationInterceptor" class="org.apache.myfaces.orchestra.conversation.spring.PersistenceContextConversationInterceptor"&gt;
57   *   &lt;property name="persistenceContextFactory" ref="yourPersistentContextFactory" /&gt;
58   * &lt;/bean&gt;
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 		// Check for validity here so that an exception gets thrown on startup
119 		// rather than when the first bean in this scope is created.
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 		// invoke child scope classes so they can add any aspects they desire.
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 		// conversation timeout
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 		// conversation lifetime
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 }