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 }