You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by sk...@apache.org on 2007/11/26 18:05:13 UTC

svn commit: r598345 - /myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/conversation/spring/AbstractSpringOrchestraScope.java

Author: skitching
Date: Mon Nov 26 09:05:12 2007
New Revision: 598345

URL: http://svn.apache.org/viewvc?rev=598345&view=rev
Log:
Fix ORCHESTRA-9 (NullPointerException in registerDestructionCallback). The critical change here is this one:
  String convName = _SpringUtils.getRealBeanName(beanName);

Modified:
    myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/conversation/spring/AbstractSpringOrchestraScope.java

Modified: myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/conversation/spring/AbstractSpringOrchestraScope.java
URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/conversation/spring/AbstractSpringOrchestraScope.java?rev=598345&r1=598344&r2=598345&view=diff
==============================================================================
--- myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/conversation/spring/AbstractSpringOrchestraScope.java (original)
+++ myfaces/orchestra/trunk/core/src/main/java/org/apache/myfaces/orchestra/conversation/spring/AbstractSpringOrchestraScope.java Mon Nov 26 09:05:12 2007
@@ -20,6 +20,8 @@
 package org.apache.myfaces.orchestra.conversation.spring;
 
 import org.aopalliance.aop.Advice;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.myfaces.orchestra.conversation.Conversation;
 import org.apache.myfaces.orchestra.conversation.ConversationAware;
 import org.apache.myfaces.orchestra.conversation.ConversationBindingEvent;
@@ -58,6 +60,8 @@
 public abstract class AbstractSpringOrchestraScope implements ConversationFactory, 
 	Scope, BeanFactoryAware, ApplicationContextAware
 {
+	private final Log log = LogFactory.getLog(AbstractSpringOrchestraScope.class);
+
 	private ConfigurableApplicationContext applicationContext;
 	private Advice[] advices;
 
@@ -234,11 +238,13 @@
 
 	/**
 	 * Get the conversation for the given beanName.
+	 * Returns null if the conversation does not exist.
 	 */
 	protected Conversation getConversationForBean(String beanName)
 	{
 		ConversationManager manager = ConversationManager.getInstance();
-		Conversation conversation = manager.getConversation(getConversationNameForBean(beanName));
+		String conversationName = getConversationNameForBean(beanName);
+		Conversation conversation = manager.getConversation(conversationName);
 		return conversation;
 	}
 
@@ -247,22 +253,49 @@
 	 */
 	protected String getConversationNameForBean(String beanName)
 	{
-		if (applicationContext != null)
+		if (applicationContext == null)
 		{
-			BeanDefinition beanDefinition = applicationContext.getBeanFactory().getBeanDefinition(beanName);
-			if (ScopedProxyFactoryBean.class.getName().equals(beanDefinition.getBeanClassName()))
-			{
-				// bad hack to get access to the real definition
-				// TODO: is there a better way to do this?
-				beanDefinition = applicationContext.getBeanFactory().getBeanDefinition(_SpringUtils.getAlternateBeanName(beanName)); // NON-NLS
-			}
-			if (beanDefinition.hasAttribute(BeanDefinitionConversationNameAttrDecorator.CONVERSATION_NAME_ATTRIBUTE))
-			{
-				return (String) beanDefinition.getAttribute(BeanDefinitionConversationNameAttrDecorator.CONVERSATION_NAME_ATTRIBUTE);
-			}
+			// TODO: when can this happen? A scope method is being invoked but no context exists??
+			return beanName;
+		}
+		
+		// First, look up the definition with the specified name. 
+		BeanDefinition beanDefinition = applicationContext.getBeanFactory().getBeanDefinition(beanName);
+
+		// When a definition is present in the config file like this:
+		//  <bean name="foo" class="example.Foo">
+		//    <....>
+		//    <aop:scopedProxy/>
+		//  </bean>
+		// then what Spring actually does is create two BeanDefinition objects, one
+		// with name "foo" that creates a proxy object, and one with name "scopedTarget.foo"
+		// that actually defines the bean of type example.Foo.
+		//
+		// So what we do here is look up the definition by the provided name, then if that
+		// def is creating a proxy then look up the "renamed" definition.
+		if (ScopedProxyFactoryBean.class.getName().equals(beanDefinition.getBeanClassName()))
+		{
+			// bad hack to get access to the real definition
+			// TODO: is there a better way to do this?
+			
+			// Note that we deliberately change the value that will be returned, so that if there is no
+			// conversation name attribute then we return the 
+			beanName = _SpringUtils.getAlternateBeanName(beanName);
+			beanDefinition = applicationContext.getBeanFactory().getBeanDefinition(beanName); // NON-NLS
 		}
 
-		return beanName;
+		if (beanDefinition.hasAttribute(BeanDefinitionConversationNameAttrDecorator.CONVERSATION_NAME_ATTRIBUTE))
+		{
+			// The bean def explicitly included a conversation-name, so return that.
+			return (String) beanDefinition.getAttribute(BeanDefinitionConversationNameAttrDecorator.CONVERSATION_NAME_ATTRIBUTE);
+		}
+		else
+		{
+			// The beanname might have been of form "scopedTarget.foo" (esp from registerDestructionCallback).
+			// But in this case, the conversation name will just be "foo", so strip the prefix off.
+			String convName = _SpringUtils.getRealBeanName(beanName);
+			return convName;
+		}
 	}
 
 	/**
@@ -297,10 +330,32 @@
 	 * the conversation map.
 	 * <p>
 	 * This ensures it will be called during conversation destroy.
+	 * <p>
+	 * Spring calls this method whenever a bean in this scope is created, if that bean
+	 * has a "destroy method". Note however that it appears that it can also call it even
+	 * for beans that do not have a destroy method when there is a "destruction aware"
+	 * BeanPostProcessor attached to the context (spring version 2.5 at least).
 	 */
 	public void registerDestructionCallback(String name, final Runnable runnable)
 	{
+		if (log.isDebugEnabled())
+		{
+			log.debug("registerDestructionCallback for [" + name + "]");
+		}
+
 		Conversation conversation = getConversationForBean(name);
+		if (conversation == null)
+		{
+			// This should never happen because this should only be called after the bean
+			// instance has been created via scope.getBean, which always creates the
+			// conversation for the bean.
+			throw new IllegalStateException("No conversation for bean [" + name + "]");
+		}
+		if (runnable == null)
+		{
+			throw new IllegalStateException("No runnable object for bean [" + name + "]");
+		}
+
 		conversation.setAttribute(
 			runnable.getClass().getName() + "@" + System.identityHashCode(runnable),
 			new ConversationBindingListener()