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

Source Code of org.apache.myfaces.orchestra.conversation.spring.OrchestraAdvisorBeanPostProcessor$SimpleAdvisor

/*
* 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.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.myfaces.orchestra.conversation.Conversation;
import org.apache.myfaces.orchestra.conversation.CurrentConversationAdvice;
import org.springframework.aop.Advisor;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ConfigurableApplicationContext;

import java.io.Serializable;

/**
* Define a BeanPostProcessor that is run when any bean is created by Spring.
* <p>
* This checks whether the scope of the bean is an Orchestra scope. If not, it has
* no effect.
* <p>
* For orchestra-scoped beans, this ensures that a CGLIB-generated proxy object is
* returned that wraps the real bean requested (ie holds an internal reference to
* an instance of the real bean). The proxy has the CurrentConversationAdvice
* attached to it always, plus any other advices that are configured for the scope
* that the bean is associated with.
* <p>
* These advices then run on each invocation of any method on the proxy.
* <p>
* Note that the proxy may have other advices attached to it to, as specified by
* any other BeanPostProcessor objects that happen to be registered and relevant
* to the created bean (eg an advice to handle declarative transactions).
*/
class OrchestraAdvisorBeanPostProcessor extends AbstractAutoProxyCreator
{
    private static final long serialVersionUID = 1;
    private final Log log = LogFactory.getLog(OrchestraAdvisorBeanPostProcessor.class);
    private ConfigurableApplicationContext appContext;

    /**
     * Define a trivial Advisor object that always applies to the class passed to it.
     * <p>
     * Spring requires Advisor objects to be returned rather than Advices. Advisors
     * can implement various interfaces to control which classes or methods they
     * get applied to, but here the OrchestraAdvisorBeanPostProcessor only returns an
     * instance of this for classes that it really *should* apply to, and the advices
     * always apply to all methods, so there is really nothing for this Advisor to do.
     * <p>
     * TODO: maybe it would be nice to allow an orchestra scope object to hold Advisors
     * as well as just Advices, so that users can configure specific code to run only
     * for specific methods of orchestra beans.
     * <p>
     * NB: In Spring2.5, this class can simply implement Advisor, and it will be applied to
     * all methods. However in Spring2.0, class DefaultAdvisorChainFactory only accepts
     * PointcutAdvisor or IntroductionAdvisor, and silently ignores Advisor classes that
     * are not of those types. So here for Spring2.x compatibility we need to make this
     * class a PointcutAdvisor with a dummy pointcut that matches all methods...
     */
    private static class SimpleAdvisor implements PointcutAdvisor, Serializable
    {
        private Advice advice;

        public SimpleAdvisor(Advice advice)
        {
            this.advice = advice;
        }

        public Advice getAdvice()
        {
            return advice;
        }

        public boolean isPerInstance()
        {
            return false;
        }

        public Pointcut getPointcut()
        {
            return Pointcut.TRUE;
        }
    }

    public OrchestraAdvisorBeanPostProcessor(ConfigurableApplicationContext appContext)
    {
        this.appContext = appContext;

        // Always force CGLIB to be used to generate proxies, rather than java.lang.reflect.Proxy.
        //
        // Without this, the Orchestra scoped-proxy instance will not work; it requires
        // the target to fully implement the same class it is proxying, not just the
        // interfaces on the target class.
        //
        //
        // Alas, this is not sufficient to solve all the problems. If a BeanPostProcessor runs
        // before this processor, and it creates a CGLIB based proxy, then this class creates
        // a new proxy that *replaces* that one by peeking into the preceding proxy to find
        // its real target class/interfaces and its advices and merging that data with the
        // settings here (see method Cglib2AopProxy.getProxy for details). However if an
        // earlier BeanPostProcessor has created a java.lang.reflect.Proxy proxy instance
        // then this merging does not occur; instead this class just tries to wrap that proxy
        // in another cglib proxy, but that fails because java.lang.reflect.Proxy creates
        // final (unsubclassable) classes. So in short either this BeanPostProcessor needs to
        // be the *first* processor, or some trick is needed to force all BeanPostProcessors
        // to use cglib. This can be done by setting a special attribute in the BeanDefinition
        // for a bean, and AbstractSpringOrchestraScope does this.
        //
        // Note that forging cglib to be used for proxies is also necessary when creating a
        // "scoped proxy" for an object. The aop:scoped-proxy class also has the same needs
        // as the Orchestra scoped-proxy, and also forces CGLIB usage, using the same method
        // (setting AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE in the attributes of the
        // BeanDefinition of the target bean).
        setProxyTargetClass(true);
    }

    protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName,
            TargetSource customTargetSource) throws BeansException
    {
        BeanDefinition bd;
        try
        {
            bd = appContext.getBeanFactory().getBeanDefinition(beanName);
        }
        catch(NoSuchBeanDefinitionException e)
        {
            // odd. However it appears that there are some "special" beans that Spring
            // creates that cause the BeanPostProcessor to be run even though they do
            // not have a corresponding definition. The name "(inner bean)" is one of them,
            // but there are also names of form "example.classname#xyz123" passed to here.
            if (log.isDebugEnabled())
            {
                log.debug("Bean has no definition:" + beanName);
            }
            return null;
        }

        String scopeName = bd.getScope();
        if (scopeName == null)
        {
            // does this ever happen?
            if (log.isDebugEnabled())
            {
                log.debug("no scope associated with bean " + beanName);
            }
            return null;
        }

        if (log.isDebugEnabled())
        {
            log.debug("Processing scope [" + scopeName + "]");
        }

        Object scopeObj = appContext.getBeanFactory().getRegisteredScope(scopeName);
        if (scopeObj == null)
        {
            // Ok, this is not an orchestra-scoped bean. This happens for standard scopes
            // like Singleton.
            if (log.isDebugEnabled())
            {
                log.debug("No scope object for scope [" + scopeName + "]");
            }
            return null;
        }
        else if (scopeObj instanceof AbstractSpringOrchestraScope == false)
        {
            // ok, this is not an orchestra-scoped bean
            if (log.isDebugEnabled())
            {
                log.debug(
                    "scope associated with bean " + beanName +
                    " is not orchestra:" + scopeObj.getClass().getName());
            }
            return null;
        }

        AbstractSpringOrchestraScope scopeForThisBean = (AbstractSpringOrchestraScope) scopeObj;
        Conversation conversation = scopeForThisBean.getConversationForBean(beanName);
           
        if (conversation == null)
        {
            // In general, getConversationForBean is allowed to return null. However in this case
            // that is really not expected. Calling getBean for a bean in a scope only ever calls
            // the get method on the scope object. The only way an instance can *really* be
            // created is by invoking the ObjectFactory that is passed to the scope object. And
            // the AbstractSpringOrchestraScope type only ever does that after ensuring that the
            // conversation object has been created.
            //
            // Therefore, this is theoretically impossible..
            throw new IllegalStateException("Internal error: null conversation for bean " + beanName);
        }

        Advice[] advices = scopeForThisBean.getAdvices();
        if ((advices == null) || (advices.length == 0))
        {
            advices = new Advice[0];
        }

        // wrap every Advice in an Advisor instance that returns it in all cases
        int len = advices.length + 1;
        Advisor[] advisors = new Advisor[len];

        // always add the standard CurrentConversationAdvice, and do it FIRST, so it runs first
        advisors[0] = new SimpleAdvisor(new CurrentConversationAdvice(conversation, beanName));

        for(int i=0; i<advices.length; ++i)
        {
            advisors[i+1] = new SimpleAdvisor(advices[i]);
        }

        return advisors;
    }
}
TOP

Related Classes of org.apache.myfaces.orchestra.conversation.spring.OrchestraAdvisorBeanPostProcessor$SimpleAdvisor

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.