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       * 
69       * @since 1.1
70       */
71      
72      /*
73       * An alternative to this is to use AOP "load-time-weaving", where a custom classloader
74       * uses a configuration file to apply the necessary interceptors directly to the class
75       * rather than using the scope manager (eg AbstractSpringOrchestraScope) to define the
76       * aspects as interceptors. In this case, the "this" reference is an object that has
77       * the interceptors attached so the problem does not occur. But the classes which are
78       * to be modified on class-load-time are determined by the orchestra configuration
79       * files which specify what beans are conversation-scoped. In the worst case, this
80       * information is actually held in annotations on the beans themselves, which means
81       * that the class is loaded before the information on how to weave it exists. The only
82       * solution here appears to be to instead weave every possible class that might be
83       * conversation-scoped (eg all those with @Scope annotation, or all those that are in
84       * a certain package). On object creation the aspect performs a "lookup" to find its
85       * conversation, and if none then does nothing.
86       * <p>
87       * TODO: Maybe what we want is instead getCurrentContext(bean), where bean is not
88       * necessarily a directly-configured conversational bean. Instead, this returns a new
89       * proxy for the bean (whatever it is) that will run the interceptors for the current
90       * conversation on entry/exit to that bean's methods. This then makes the method
91       * more generically useful, as it can wrap all sorts of objects rather than just
92       * the conversation-scoped beans themselves.
93       */
94      public static Object getCurrentBean()
95      {
96          CurrentConversationInfo currentConversationInfo = Conversation.getCurrentInstanceInfo();
97          if (currentConversationInfo == null)
98          {
99              return null;
100         }
101 
102         String name = currentConversationInfo.getBeanName();
103 
104         return FrameworkAdapter.getCurrentInstance().getBean(name);
105     }
106 
107     /**
108      * End and restart the current conversation.
109      * <p>
110      * The "current conversation" is the conversation associated with the
111      * most-recently-invoked conversation-scoped bean.
112      * <p>
113      * The returned object is a new instance of the most-recently-invoked
114      * conversation-scope bean.
115      * <p>
116      * This method is generally expected to be called from a conversation-scoped
117      * bean, in which case the conversation that is invalidated is the one in
118      * which the calling bean instance lives, and the returned object is the
119      * instance that will replace the calling object. 
120      */
121     public static Object invalidateAndRestartCurrent()
122     {
123         CurrentConversationInfo currentConversationInfo = Conversation.getCurrentInstanceInfo();
124         String name = currentConversationInfo.getBeanName();
125 
126         currentConversationInfo.getConversation().invalidateAndRestart();
127 
128         return FrameworkAdapter.getCurrentInstance().getBean(name);
129     }
130 
131     /**
132      * If no conversation with name <code>conversationName</code> is active a redirect to
133      * <code>redirectViewId</code> will be issued.
134      * <p>
135      * If <code>redirectViewId</code> starts with an slash ('/') the context path will be added.
136      */
137     public static void ensureConversationRedirect(String conversationName, String redirectViewId)
138     {
139         if (!ConversationManager.getInstance().hasConversation(conversationName))
140         {
141             try
142             {
143                 FrameworkAdapter.getCurrentInstance().redirect(redirectViewId);
144             }
145             catch (IOException e)
146             {
147                 throw new RuntimeException(e);
148             }
149         }
150     }
151 
152     /**
153      * Invalidates a conversation if it exists.
154      */
155     public static void invalidateIfExists(String name)
156     {
157         Conversation conversation = ConversationManager.getInstance().getConversation(name);
158         if (conversation != null)
159         {
160             conversation.invalidate();
161         }
162     }
163 }