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 }