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;
    21
    22  import org.apache.myfaces.orchestra.frameworkAdapter.FrameworkAdapter;
    23
    24  import java.io.IOException;
    25
    26  /**
    27   * Some helpers usable for public use
    28   */
    29  public final class ConversationUtils
    30  {
    31      private ConversationUtils()
    32      {
    33      }
    34
    35      /**
    36       * End and restart the given conversation.
    37       * <p>
    38       * This method tries to return a bean whose name is the same as the
    39       * name of the specified conversation. If no such bean is defined, then
    40       * null is returned.
    41       * <p>
    42       * Note that the return value is quite different from the
    43       * {@link Conversation#invalidateAndRestart()} method, which returns
    44       * an instance of the most recently invoked conversation-scoped bean.
    45       */
    46      public static Object invalidateAndRestart(Conversation conversation)
    47      {
    48          String name = conversation.getName();
    49
    50          conversation.invalidateAndRestart();
    51
    52          return FrameworkAdapter.getCurrentInstance().getBean(name);
    53      }
    54
    55      /**
    56       * Return a reference to the most recently-invoked bean that is declared as being in
    57       * a conversation scope.
    58       * <p>
    59       * When using an interceptor-based AOP framework, a bean that passes "this" to another
    60       * object is bypassing any aspects. Any "callbacks" invoked via that reference
    61       * will not apply the aspects that Orchestra has configured. This is particularly
    62       * nasty when using Orchestra's persistence support as Orchestra uses an aspect to
    63       * configure the correct "persistence context" for a bean when it is invoked.
    64       * <p>
    65       * Therefore, when a bean wishes to pass a reference to itself elsewhere then it should
    66       * use this method rather than passing "this" directly. It is acknowledged that this is
    67       * less than ideal as it does couple code to Orchestra.
    68       * <p>
    69       * In most cases it is better to call <code>ConversationUtils.bindToCurrent(this)</code>.
    70       * That code works regardless of whether the caller is configured in the dependency-injection
    71       * framework as a conversation-scoped bean or not, ie it makes the code independent of the
    72       * configuration which is always a good idea.
    73       *
    74       * @since 1.1
    75       */
    76
    77      /*
    78       * An alternative to this is to use AOP "load-time-weaving", where a custom classloader
    79       * uses a configuration file to apply the necessary interceptors directly to the class
    80       * rather than using the scope manager (eg AbstractSpringOrchestraScope) to define the
    81       * aspects as interceptors. In this case, the "this" reference is an object that has
    82       * the interceptors attached so the problem does not occur. But the classes which are
    83       * to be modified on class-load-time are determined by the orchestra configuration
    84       * files which specify what beans are conversation-scoped. In the worst case, this
    85       * information is actually held in annotations on the beans themselves, which means
    86       * that the class is loaded before the information on how to weave it exists. The only
    87       * solution here appears to be to instead weave every possible class that might be
    88       * conversation-scoped (eg all those with @Scope annotation, or all those that are in
    89       * a certain package). On object creation the aspect performs a "lookup" to find its
    90       * conversation, and if none then does nothing.
    91       */
    92      public static Object getCurrentBean()
    93      {
    94          CurrentConversationInfo currentConversationInfo = Conversation.getCurrentInstanceInfo();
    95          if (currentConversationInfo == null)
    96          {
    97              return null;
    98          }
    99
    100         String name = currentConversationInfo.getBeanName();
    101
    102         return FrameworkAdapter.getCurrentInstance().getBean(name);
    103     }
    104
    105     /**
    106      * End and restart the current conversation.
    107      * <p>
    108      * The "current conversation" is the conversation associated with the
    109      * most-recently-invoked conversation-scoped bean.
    110      * <p>
    111      * The returned object is a new instance of the most-recently-invoked
    112      * conversation-scope bean.
    113      * <p>
    114      * This method is generally expected to be called from a conversation-scoped
    115      * bean, in which case the conversation that is invalidated is the one in
    116      * which the calling bean instance lives, and the returned object is the
    117      * instance that will replace the calling object.
    118      */
    119     public static Object invalidateAndRestartCurrent()
    120     {
    121         CurrentConversationInfo currentConversationInfo = Conversation.getCurrentInstanceInfo();
    122         String name = currentConversationInfo.getBeanName();
    123
    124         currentConversationInfo.getConversation().invalidateAndRestart();
    125
    126         return FrameworkAdapter.getCurrentInstance().getBean(name);
    127     }
    128
    129     /**
    130      * If no conversation with name <code>conversationName</code> is active a redirect to
    131      * <code>redirectViewId</code> will be issued.
    132      * <p>
    133      * If <code>redirectViewId</code> starts with an slash ('/') the context path will be added.
    134      */
    135     public static void ensureConversationRedirect(String conversationName, String redirectViewId)
    136     {
    137         if (!ConversationManager.getInstance().hasConversation(conversationName))
    138         {
    139             try
    140             {
    141                 FrameworkAdapter.getCurrentInstance().redirect(redirectViewId);
    142             }
    143             catch (IOException e)
    144             {
    145                 throw new RuntimeException(e);
    146             }
    147         }
    148     }
    149
    150     /**
    151      * Invalidates a conversation if it exists.
    152      */
    153     public static void invalidateIfExists(String name)
    154     {
    155         Conversation conversation = ConversationManager.getInstance().getConversation(name);
    156         if (conversation != null)
    157         {
    158             conversation.invalidate();
    159         }
    160     }
    161
    162     /**
    163      * Returns a proxy object that "binds" the specified instance to the current conversation.
    164      * <p>
    165      * Invoking the returned proxy object will set up the current conversation before passing
    166      * the method call on to the provided instance, ie calls to the instance then run in the
    167      * same conversation as the code making a call to this method.
    168      * <p>
    169      * This is simply a shorcut for "Conversation.getCurrentInstance().bind(instance);".
    170      *
    171      * @since 1.3
    172      */
    173     public static Object bindToCurrent(Object instance)
    174     {
    175         return Conversation.getCurrentInstance().bind(instance);
    176     }
    177 }