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.viewController.spring; 21 22 import org.apache.myfaces.orchestra.conversation.Conversation; 23 import org.apache.myfaces.orchestra.conversation.ConversationContext; 24 import org.apache.myfaces.orchestra.conversation.spring.AbstractSpringOrchestraScope; 25 import org.apache.myfaces.orchestra.frameworkAdapter.FrameworkAdapter; 26 import org.apache.myfaces.orchestra.lib.OrchestraException; 27 import org.apache.myfaces.orchestra.viewController.DefaultViewControllerManager; 28 import org.apache.myfaces.orchestra.viewController.ViewControllerManager; 29 import org.springframework.beans.factory.NoSuchBeanDefinitionException; 30 31 /** 32 * This hooks into the Spring2.x scope-handling mechanism to provide a dummy scope type 33 * which will place a bean configured for it into the same conversation that the current 34 * viewController lives in. 35 * <p> 36 * To use this, in the spring config file add an element with id=N that defines a spring 37 * scope with this class as its controller. Then define managed beans with scope="N". When 38 * code references such a bean, then the current "view controller" bean is located 39 * (ie the bean handling lifecycle events for the current view), and the instance of 40 * the target bean from the same conversation is returned. If no such instance currently 41 * exists, then one is created and added to that conversation (even when an instance 42 * already exists in a different scope). 43 * <p> 44 * Note that this means a bean configured with a scope of this type will actually 45 * have a separate instance per conversation. 46 * <p> 47 * In particular, this works well with spring aop-proxy, where the proxy looks up the 48 * bean on each method call, and so always returns the instance in the conversation 49 * associated with the current view. 50 * <p> 51 * One use for this is implementing custom JSF converters or validators that access 52 * persistent objects. When accessing the database they need to use the same 53 * PersistenceContext that the beans handing this view use. Defining the converter 54 * using this scope type ensures that this happens. 55 * <p> 56 * It is an error (ie an exception is thrown) if a bean of this scope is referenced 57 * but there is no "view controller" bean associated with the current view. 58 */ 59 public class SpringViewControllerScope extends AbstractSpringOrchestraScope 60 { 61 private final static ViewControllerManager DEFAULT_VCM = new DefaultViewControllerManager(); 62 63 public SpringViewControllerScope() 64 { 65 } 66 67 public Conversation createConversation(ConversationContext context, String conversationName) 68 { 69 throw new IllegalStateException("the viewController scope is not supposed to start a conversation on its own. conversation to start: " + conversationName); 70 } 71 72 protected void assertSameScope(String beanName, Conversation conversation) 73 { 74 // since we do not start any conversation, there is no need to check 75 // if this scope uses the same conversationFactory 76 } 77 78 /** 79 * Find the conversation-controller bean for the current view, then return the conversation that 80 * is configured for that controller bean. 81 * <p> 82 * The parameter is completely ignored; the conversation-name returned is that associated with the 83 * controller bean, not the specified bean at all. 84 */ 85 protected String getConversationNameForBean(String beanName) 86 { 87 ViewControllerManager viewControllerManager = getViewControllerManager(); 88 String viewId = FrameworkAdapter.getCurrentInstance().getCurrentViewId(); 89 String viewControllerName = viewControllerManager.getViewControllerName(viewId); 90 if (viewControllerName == null) 91 { 92 throw new OrchestraException("no view controller name found for view " + viewId); 93 } 94 95 String conversationName = super.getConversationNameForBean(viewControllerName); 96 if (conversationName == null) 97 { 98 throw new OrchestraException("no view controller definition found for view " + viewId); 99 } 100 101 return conversationName; 102 } 103 104 /** 105 * Look for a Spring bean definition that defines a custom ViewControllerManager; 106 * if not found then return the default instance. 107 * <p> 108 * Never returns null. 109 */ 110 private ViewControllerManager getViewControllerManager() 111 { 112 try 113 { 114 return (ViewControllerManager) 115 getApplicationContext().getBean( 116 ViewControllerManager.VIEW_CONTROLLER_MANAGER_NAME, 117 ViewControllerManager.class); 118 } 119 catch(NoSuchBeanDefinitionException e) 120 { 121 return DEFAULT_VCM; 122 } 123 } 124 }