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 }