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 }