Package org.apache.myfaces.orchestra.conversation.spring

Source Code of org.apache.myfaces.orchestra.conversation.spring.AbstractSpringOrchestraScope

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.myfaces.orchestra.conversation.spring;

import org.aopalliance.aop.Advice;
import org.apache.myfaces.orchestra.conversation.Conversation;
import org.apache.myfaces.orchestra.conversation.ConversationAware;
import org.apache.myfaces.orchestra.conversation.ConversationBindingEvent;
import org.apache.myfaces.orchestra.conversation.ConversationBindingListener;
import org.apache.myfaces.orchestra.conversation.ConversationFactory;
import org.apache.myfaces.orchestra.conversation.ConversationManager;
import org.apache.myfaces.orchestra.conversation.CurrentConversationAdvice;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.scope.ScopedProxyFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.Scope;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;

/**
* Abstract basis class for all the Orchestra scopes.
* <p>
* A scope object has two quite different roles:
* <ol>
* <li>It handles the lookup of beans in a scope, and creates them if needed</li>
* <li>It handles the creation of Conversation objects, using the spring properties
* configured on the scope object.</li>
* </ol>
* <p>
* This base class handles item 1 above, and leaves item 2 to a subclass. The
* declaration of interface ConversationFactory needs to be on this class, however,
* as the createBean method needs to invoke it.
*/
public abstract class AbstractSpringOrchestraScope implements ConversationFactory,
  Scope, BeanFactoryAware, ApplicationContextAware
{
  private ConfigurableApplicationContext applicationContext;
  private Advice[] advices;

  public AbstractSpringOrchestraScope()
  {
  }

  /**
   * The advices (interceptors) which will be applied to the conversation scoped bean.
   */
  public void setAdvices(Advice[] advices)
  {
    this.advices = advices;
  }

  /**
   * Return the conversation context id.
   * <p>
   * Note: This conversationId is something spring requires. It has nothing to do with the Orchestra
   * conversation id.
   */
  public String getConversationId()
  {
    ConversationManager manager = ConversationManager.getInstance();
    if (manager.hasConversationContext())
    {
      return Long.toString(manager.getConversationContextId().longValue(), 10);
    }

    return null;
  }

  /**
   * This is invoked by Spring whenever someone calls getBean(name) on a bean-factory
   * and the bean-definition for that bean has a scope attribute that maps to an
   * instance of this class.
   * <p>
   * First, the appropriate ConversationContext is retrieved.
   * <p>
   * Second, the appropriate Conversation is retrieved; if it does not yet exist then
   * it is created and started. The conversation name is either specified on the
   * bean-definition via a custom attribute, or defaults to the bean name.
   * <p>
   * Then if the bean already exists in the Conversation then it is returned. Otherwise
   * a new instance is created, stored into the Conversation and returned.
   * <p>
   * When a bean is created, a proxy is actually created for it which has one or
   * more AOP "advices" (ie method interceptors). The CurrentConversationAdvice class
   * is always attached. Note that if the bean definition contains the aop:proxy
   * tag (and most do) then the bean that spring creates is already a proxy, ie
   * what is returned is a proxy of a proxy.
   */
  public Object get(String name, ObjectFactory objectFactory)
  {
    name = buildBeanName(name);

    return getBean(name, objectFactory);
  }

  /** See method get(name, objectFactory). */
  protected Object getBean(String beanName, ObjectFactory objectFactory)
  {
    String conversationName = getConversationNameForBean(beanName);

    ConversationManager manager = ConversationManager.getInstance();
    Conversation conversation;

    // check if we have a conversation
    synchronized(manager)
    {
      conversation = manager.getConversation(conversationName);
      if (conversation == null)
      {
        // Start the conversation. This eventually results in a
        // callback to the createConversation method on this class.
        conversation = manager.startConversation(conversationName, this);
      }
      else
      {
                assertSameScope(beanName, conversation);
            }
    }

    // get the conversation
    notifyAccessConversation(conversation);
    synchronized(conversation)
    {
      if (!conversation.hasAttribute(beanName))
      {
        // create the bean (if not already done)
        Object value = objectFactory.getObject();

        ProxyFactory factory = new ProxyFactory(value);
        factory.setProxyTargetClass(true);
        factory.addAdvice(new CurrentConversationAdvice(conversation, beanName));

        if (advices != null && advices.length > 0)
        {
          for (int i = 0; i < advices.length; i++)
          {
            factory.addAdvice(advices[i]);
          }
        }

        value = factory.getProxy();

        conversation.setAttribute(beanName, value);
      }
    }

    // get the bean
    return conversation.getAttribute(beanName);
  }

    protected void assertSameScope(String beanName, Conversation conversation)
    {
        // Check that the conversation's factory is this one.
        //
        // This handles the case where two different beans declare themselves
        // as belonging to the same conversation but with different scope
        // objects. Allowing that would be nasty as the conversation
        // properties (eg flash or manual) would depend upon which bean
        // got created first; some other ConversationFactory would have
        // created the conversation using its configured properties then
        // we are now adding to that conversation a bean that really wants
        // the conversation properties defined on this ConversationFactory.
        //
        // Ideally the conversation properties would be defined using
        // the conversation name, not the scope name; this problem would
        // then not exist. However that would lead to some fairly clumsy
        // configuration, particularly where lots of beans without explicit
        // conversationName attributes are used.

        if (conversation.getFactory() != this)
        {
            throw new IllegalArgumentException(
                "Inconsistent scope and conversation name detected for bean "
                    + beanName);
        }
    }

    protected void notifyAccessConversation(Conversation conversation)
  {
  }

  /**
   * Set the {@link org.apache.myfaces.orchestra.conversation.Conversation} object to the bean if it implements the
   * {@link org.apache.myfaces.orchestra.conversation.ConversationAware} interface.
   */
  public void setBeanFactory(BeanFactory beanFactory) throws BeansException
  {
    ((ConfigurableBeanFactory) beanFactory).addBeanPostProcessor(
      new BeanPostProcessor()
      {
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException
        {
          if (bean instanceof ConversationAware)
          {
            Conversation conversation = getConversationForBean(beanName);

            ((ConversationAware) bean).setConversation(conversation);
          }

          return bean;
        }

        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
        {
          return bean;
        }
      }
    );
  }

  /**
   * Get the conversation for the given beanName.
   */
  protected Conversation getConversationForBean(String beanName)
  {
    ConversationManager manager = ConversationManager.getInstance();
    Conversation conversation = manager.getConversation(getConversationNameForBean(beanName));
    return conversation;
  }

  /**
   * Get the conversation name associated with the beanName.
   */
  protected String getConversationNameForBean(String beanName)
  {
    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);
      }
    }

    return beanName;
  }

  /**
   * Strip off any Spring namespace (eg scopedTarget).
   * <p>
   * This method will simply strip off anything before the first dot.
   */
  protected String buildBeanName(String name)
  {
    if (name == null)
    {
      return null;
    }

    int pos = name.indexOf('.');
    if (pos < 0)
    {
      return name;
    }

    return name.substring(pos + 1);
  }

  public Object remove(String name)
  {
    throw new UnsupportedOperationException();
  }

  /**
   * Add the given runnable wrapped within an
   * {@link org.apache.myfaces.orchestra.conversation.ConversationBindingListener} to
   * the conversation map.
   * <p>
   * This ensures it will be called during conversation destroy.
   */
  public void registerDestructionCallback(String name, final Runnable runnable)
  {
    Conversation conversation = getConversationForBean(name);
    conversation.setAttribute(
      runnable.getClass().getName() + "@" + System.identityHashCode(runnable),
      new ConversationBindingListener()
      {
        public void valueBound(ConversationBindingEvent event)
        {
        }

        public void valueUnbound(ConversationBindingEvent event)
        {
          runnable.run();
        }
      }
    );
  }

  /**
   * Get an ApplicationContext injected by Spring. See ApplicationContextAware interface.
   */
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
  {
    if (!(applicationContext instanceof ConfigurableApplicationContext))
    {
      throw new IllegalArgumentException("a ConfigurableApplicationContext is required");
    }

    this.applicationContext = (ConfigurableApplicationContext) applicationContext;
  }
}
TOP

Related Classes of org.apache.myfaces.orchestra.conversation.spring.AbstractSpringOrchestraScope

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.