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.ConversationAccessLifetimeAspect;
24  import org.apache.myfaces.orchestra.conversation.ConversationAspect;
25  import org.apache.myfaces.orchestra.conversation.ConversationContext;
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"
57   *    class="org.apache.myfaces.orchestra.conversation.spring.PersistenceContextConversationInterceptor"&gt;
58   *   &lt;property name="persistenceContextFactory" ref="yourPersistentContextFactory" /&gt;
59   * &lt;/bean&gt;
60   * </pre>
61   * <p>
62   *
63   * <h2>Conversation properties</h2>
64   * The following properties can be defined on a scope and then apply to any conversation that is created
65   * to hold a bean of this scope:
66   * <ul>
67   * <li>lifetime: may be "manual" or "access". If not specified, then defaults to "manual".</li>
68   * <li>timeout: a time period (in minutes) after which inactive conversations are terminated.
69   * If not specified, then inactive conversations are never automatically terminated.</li>
70   * </ul>
71   *
72   * <h2>Other Notes</h2>
73   * If the bean definition does not specify a conversation name, then the bean name is used
74   * as the conversation name.
75   * <p>
76   * As shown above, a list of AOP Advices can be specified for the scope, in which case each of the
77   * advices gets configured for any bean declared with this scope.
78   */
79  public class SpringConversationScope extends AbstractSpringOrchestraScope
80  {
81      /** @deprecated Use LIFETIME_ACCESS instead. */
82      public static final String LIFETIME_FLASH = "flash";
83  
84      public static final String LIFETIME_ACCESS = "access";
85      public static final String LIFETIME_MANUAL = "manual";
86  
87      private Integer timeout;
88      private String lifetime = LIFETIME_MANUAL;
89  
90      /**
91       * See {@link #setTimeout}.
92       */
93      public Integer getTimeout()
94      {
95          return timeout;
96      }
97  
98      /**
99       * The timeout in minutes when the conversation will end.
100      * See {@link ConversationTimeoutableAspect#timeout} for the default timeout.
101      */
102     public void setTimeout(Integer timeout)
103     {
104         this.timeout = timeout;
105     }
106 
107     /**
108      * See {@link #setLifetime}.
109      */
110     public String getLifetime()
111     {
112         return lifetime;
113     }
114 
115     /**
116      * Must be one of "manual" or "access".
117      * <p>
118      * Defaults to "manual".
119      * <p>
120      * Note that "flash" is also supported as an alias for "access", for
121      * reasons of backwards compatibility with release 1.0.
122      */
123     public void setLifetime(String lifetime)
124     {
125         // Check for validity here so that an exception gets thrown on startup
126         // rather than when the first bean in this scope is created.
127         if (LIFETIME_FLASH.equals(lifetime))
128         {
129             this.lifetime = LIFETIME_ACCESS;
130         }
131         else if (LIFETIME_ACCESS.equals(lifetime))
132         {
133             this.lifetime = LIFETIME_ACCESS;
134         }
135         else if (LIFETIME_MANUAL.equals(lifetime))
136         {
137             this.lifetime = LIFETIME_MANUAL;
138         }
139         else
140         {
141             throw new IllegalArgumentException("Invalid lifetime:" + lifetime);
142         }
143     }
144 
145     /**
146      * Implementation of ConversationFactory interface.
147      */
148     public Conversation createConversation(ConversationContext context, String name)
149     {
150         Conversation conversation = new Conversation(context, name, this);
151         conversation.setBinder(new SpringConversationBinder(this, conversation));
152 
153         // invoke child scope classes so they can add any aspects they desire.
154         initAspects(conversation);
155 
156         return conversation;
157     }
158 
159     /**
160      * Add aspects to a newly-created conversation.
161      * <p>
162      * Subclasses are expected to call super.initAspects, then make
163      * zero or more calls to conversation.addAspect.
164      */
165     protected void initAspects(Conversation conversation)
166     {
167         // conversation timeout
168         if (timeout != null)
169         {
170             long timeoutMsecs = timeout.longValue() * 60L * 1000L;
171             ConversationTimeoutableAspect aspect = new ConversationTimeoutableAspect(conversation);
172             aspect.setTimeout(timeoutMsecs);
173             conversation.addAspect(aspect);
174         }
175 
176         // conversation lifetime
177         if (LIFETIME_ACCESS.equals(lifetime))
178         {
179             ConversationAspect aspect = new ConversationAccessLifetimeAspect(conversation);
180             conversation.addAspect(aspect);
181         }
182     }
183 
184     /**
185      * Mark the specified conversation as having been accessed.
186      * <p>
187      * This affects conversation timeouts, and the removal of access-scoped conversations.
188      */
189     protected void notifyAccessConversation(Conversation conversation)
190     {
191         super.notifyAccessConversation(conversation);
192 
193         ConversationAccessLifetimeAspect aspect = (ConversationAccessLifetimeAspect)
194             conversation.getAspect(ConversationAccessLifetimeAspect.class);
195         if (aspect != null)
196         {
197             aspect.markAsAccessed();
198         }
199     }
200 }