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 }