Package org.jboss.resteasy.plugins.spring

Source Code of org.jboss.resteasy.plugins.spring.SpringBeanProcessor$ResteasyBeanPostProcessor

package org.jboss.resteasy.plugins.spring;

import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.spi.*;
import org.jboss.resteasy.util.GetRestful;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.beans.factory.config.*;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.core.type.StandardMethodMetadata;
import org.springframework.util.ClassUtils;

import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import java.lang.reflect.Method;
import java.util.*;

/**
* <p>
* The processor will register any bean annotated with @Path or @Provider into
* the Resteasy framework.
* </p>
* <p>
* It also sets up Registry and ResteasyProviderFactory to be autowirable via @Autowire
* in Controllers/service layers.
* </p>
* <p/>
* <p>
* There's quite a bit of spring integration functionality under the covers:
* </p>
* <ol>
* <li>@Providers, such as RESTEasy interceptors and String converters have to
* be registered in RESTEasy before resources and registers. That gets a bit
* tricky, so depends-on functionality is used as well</li>
* <p/>
* <li>
* </ol>
* <p/>
*
* @author <a href="mailto:sduskis@gmail.com">Solomon Duskis</a>
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SpringBeanProcessor implements BeanFactoryPostProcessor, SmartApplicationListener
{
    protected Registry registry;
    protected ResteasyProviderFactory providerFactory;
    protected Dispatcher dispatcher;

    protected Set<String> resourceFactoryNames;
    protected Map<String, SpringResourceFactory> resourceFactories = new HashMap<String, SpringResourceFactory>();

    protected Set<String> providerNames = new HashSet<String>();
    private Set<String> registrations = new HashSet<String>();

    private int order;

    protected class ResteasyBeanPostProcessor implements BeanPostProcessor
    {
        private ConfigurableListableBeanFactory beanFactory;

        protected ResteasyBeanPostProcessor(ConfigurableListableBeanFactory beanFactory)
        {
            this.beanFactory = beanFactory;
        }

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

        /**
         * This method is invoked after postProcessBeanFactory.
         * <p/>
         * this method is invoked when ever a new bean is created. This will
         * perform the following:
         * <p/>
         * <ol>
         * <p/>
         * <li>RESTEasy injection of singleton @Providers, as well as @Provider
         * registration
         * <p/>
         * <li>either singleton or request/prototype RESTeasy injection... but not
         * registration. The RESTEasy registration happens in the
         * onApplicationEvent() below, which happens at the end of the Spring
         * life-cycle
         * <p/>
         * </ol>
         *
         * @see SpringBeanProcessor.postProcessBeanFactory
         */
        public Object postProcessAfterInitialization(Object bean, String beanName)
                throws BeansException
        {
            if (providerNames.contains(beanName))
            {
                PropertyInjector injector = getInjector(AopUtils.getTargetClass(bean));
                injector.inject(bean);
                providerFactory.registerProviderInstance(bean);
            }

            else if(registrations.contains(beanName) && bean instanceof ResteasyRegistration)
            {
                ResteasyRegistration registration = (ResteasyRegistration)bean;
                String registeredBeanName = registration.getBeanName();
                BeanDefinition beanDef = beanFactory.getBeanDefinition(registeredBeanName);
                Class<?> beanClass = getBeanClass(registeredBeanName, beanDef, beanFactory);
                SpringResourceFactory resourceFactory = new SpringResourceFactory(registeredBeanName, beanFactory, beanClass);
                resourceFactory.setContext(registration.getContext());
                resourceFactories.put(registeredBeanName, resourceFactory);
            }

            else
            {
                SpringResourceFactory resourceFactory = resourceFactories.get(beanName);
                if (resourceFactory != null)
                {
                    inject(beanName, bean, getInjector(resourceFactory.getScannableClass()));
                }
            }

            return bean;
        }

        public PropertyInjector getInjector(Class<?> clazz)
        {
            return providerFactory.getInjectorFactory().createPropertyInjector(clazz, providerFactory);
        }

        public void inject(String beanName, Object bean, PropertyInjector propertyInjector)
        {
            if (propertyInjector == null)
            {
                return;
            }
            HttpRequest request = ResteasyProviderFactory.getContextData(HttpRequest.class);
            if (request == null || isSingleton(beanName))
            {
                propertyInjector.inject(bean);
            }
            else
            {
                HttpResponse response = ResteasyProviderFactory.getContextData(HttpResponse.class);
                propertyInjector.inject(request, response, bean);
            }
        }

        private boolean isSingleton(String beanName)
        {
            boolean isSingleton = false;
            try
            {
                BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
                isSingleton = beanDef.isSingleton();
            }
            catch (org.springframework.beans.factory.NoSuchBeanDefinitionException nsbde)
            {
                // cannot distinguish between singleton & prototype
            }
            return isSingleton;
        }
    }

    public SpringBeanProcessor(ResteasyDeployment deployment)
    {
        this(deployment.getDispatcher(), deployment.getRegistry(), deployment.getProviderFactory());
    }

    public SpringBeanProcessor(Dispatcher dispatcher)
    {
        this(dispatcher, dispatcher.getRegistry(), dispatcher.getProviderFactory());
    }

    public SpringBeanProcessor(Dispatcher dispatcher, Registry registry,
                               ResteasyProviderFactory providerFactory)
    {
        this.setRegistry(registry);
        this.setProviderFactory(providerFactory);
        this.setDispatcher(dispatcher);
    }

    public SpringBeanProcessor()
    {
    }

    @Required
    public Registry getRegistry()
    {
        return registry;
    }

    public void setRegistry(Registry registry)
    {
        this.registry = registry;
    }

    public ResteasyProviderFactory getProviderFactory()
    {
        return providerFactory;
    }

    public void setProviderFactory(ResteasyProviderFactory providerFactory)
    {
        this.providerFactory = providerFactory;
    }

    public Dispatcher getDispatcher()
    {
        return dispatcher;
    }

    public void setDispatcher(Dispatcher dispatcher)
    {
        this.dispatcher = dispatcher;
    }

    /**
     * <p>
     * This method keeps track of @Provider and resources for future use. It also
     * registers the RESTEasy Registry, ProviderFactry, and Dispatcher for @Autowire
     * injection.
     * </p>
     * <p/>
     * <p>
     * Beyond tracking, this will ensure that non-MessageBody(Reader|Writer) @Providers
     * are created by Spring before any resources by having the resources
     * "depends-on" the @Providers.
     * </p>
     */
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
            throws BeansException
    {
        beanFactory.registerResolvableDependency(Registry.class, getRegistry());
        beanFactory.registerResolvableDependency(ResteasyProviderFactory.class, getProviderFactory());
        if (dispatcher != null)
        {
            beanFactory.registerResolvableDependency(Dispatcher.class, getDispatcher());
        }
        beanFactory.addBeanPostProcessor(new ResteasyBeanPostProcessor(beanFactory));

        List<String> dependsOnBeans = new ArrayList<String>();
        for (String name : beanFactory.getBeanDefinitionNames())
        {
            BeanDefinition beanDef = beanFactory.getBeanDefinition(name);
            if ( (beanDef.getBeanClassName() != null || beanDef.getFactoryBeanName() != null)
                    && !beanDef.isAbstract())
                processBean(beanFactory, dependsOnBeans, name, beanDef);
        }

        dependsOnBeans.addAll(registrations);
        String[] dependsOnArray = dependsOnBeans.toArray(new String[dependsOnBeans.size()]);

        if (dependsOnArray.length > 0)
        {
            for (SpringResourceFactory resourceFactory : resourceFactories.values())
            {
                BeanDefinition beanDef = beanFactory.getBeanDefinition(resourceFactory.getBeanName());
                beanDef.setDependsOn(concat(beanDef.getDependsOn(), dependsOnArray));
            }
        }
    }

    /**
     * process a single @Provider or a single resource.
     */
    protected Class<?> processBean(final ConfigurableListableBeanFactory beanFactory,
                                   List<String> dependsOnProviders, String name, BeanDefinition beanDef)
    {
        Class<?> beanClass = getBeanClass(name, beanDef, beanFactory);
        if (beanClass == null) {
            return null;
        }
        if (beanClass.isAnnotationPresent(Provider.class))
        {
            if (!isSingleton(beanDef))
            {
                throw new RuntimeException("Provider " + name
                        + " is not a singleton.  That's not allowed");
            }

            providerNames.add(name);

            if (!MessageBodyWriter.class.isAssignableFrom(beanClass)
                    && !MessageBodyReader.class.isAssignableFrom(beanClass))
            {
                dependsOnProviders.add(name);
            }
        }
        if (GetRestful.isRootResource(beanClass))
        {
            resourceFactories.put(name, new SpringResourceFactory(name, beanFactory, beanClass));
        }
        if(beanClass == ResteasyRegistration.class)
        {
            registrations.add(name);
        }
        return beanClass;
    }

    public String getPropertyValue(
            MutablePropertyValues registrationPropertyValues, String propertyName)
    {
        if(registrationPropertyValues == null)
        {
            return null;
        }
        PropertyValue propertyValue = registrationPropertyValues.getPropertyValue(propertyName);
        if(propertyValue == null)
        {
            return null;
        }
        Object value = propertyValue.getValue();
        if(value == null)
        {
            return null;
        }
        if(value.getClass() == String.class)
        {
            return (String) value;
        }
        if(value instanceof BeanReference)
        {
            return ((BeanReference)value).getBeanName();
        }
        throw new IllegalStateException("ResteasyRegistration references must be String values or a reference to a bean name");
    }

    /**
     * merge two arrays.
     *
     * @param dependsOn
     * @param dependsOnProviders
     * @return
     */
    private static String[] concat(String[] dependsOn, String[] dependsOnProviders)
    {
        if (dependsOn == null || dependsOn.length == 0)
        {
            return dependsOnProviders;
        }

        String[] result = new String[dependsOn.length + dependsOnProviders.length];

        System.arraycopy(dependsOn, 0, result, 0, dependsOn.length);
        System.arraycopy(dependsOnProviders, 0, result, dependsOn.length, dependsOnProviders.length);

        return result;
    }

    /**
     * Get the bean class, and take @Configuration @Beans into consideration
     *
     * @param beanDef
     * @param beanFactory
     * @return
     */
    private static Class<?> getBeanClass(String name, BeanDefinition beanDef,
                                         ConfigurableListableBeanFactory beanFactory)
    {
        if (beanDef instanceof RootBeanDefinition)
        {
            RootBeanDefinition rootBeanDef = (RootBeanDefinition) beanDef;
            try
            {
                if (rootBeanDef.getBeanClass() != null)
                {
                    return rootBeanDef.getBeanClass();
                }
            }
            catch (IllegalStateException e)
            {
                // do nothing. This gets thrown for factory beans
            }
        }

        // final String factoryBeanName = beanDef.getFactoryBeanName();
        final String factoryMethodName = beanDef.getFactoryMethodName();

        if (beanDef.getBeanClassName() != null && factoryMethodName == null)
        {
            return getBeanClass(beanDef.getBeanClassName());
        }

        if (factoryMethodName != null)
        {
            String factoryClassName = null;

            if (beanDef instanceof AnnotatedBeanDefinition)
            {
                factoryClassName = ((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName();
            }
            else
            {
                // Checks if beanDefinition has a factorybean defined. If so, lookup the classname of that bean
                // definition and use that as the factory class name.
                if (beanDef.getFactoryBeanName() != null)
                {
                    factoryClassName = beanFactory.getBeanDefinition(beanDef.getFactoryBeanName()).getBeanClassName();
                }
                else
                {
                    factoryClassName = beanDef.getBeanClassName();
                }
            }

            for (Method method : getBeanClass(factoryClassName).getMethods())
            {
                if (method.getName().equals(factoryMethodName))
                {
                    return method.getReturnType();
                }
            }
        }

        Object source = beanDef.getSource();
        if (source instanceof StandardMethodMetadata) {
            StandardMethodMetadata metadata = (StandardMethodMetadata) source;
            return metadata.getIntrospectedMethod().getReturnType();
        }

        //throw new IllegalStateException("could not find the type for bean named " + name);
        return null;
    }

    private static boolean isSingleton(BeanDefinition beanDef)
    {
        try
        {
            return beanDef.isSingleton();
        }
        catch (NoSuchBeanDefinitionException nsbde)
        {
            // cannot distinguish between singleton & prototype
            return false;
        }
    }

    private static Class<?> getBeanClass(final String beanClassName)
    {
        try
        {
            return ClassUtils.forName(beanClassName, Thread.currentThread().getContextClassLoader());
        }
        catch (final ClassNotFoundException e)
        {
            throw new IllegalStateException("Could not convert '" + beanClassName + "' to a class.", e);
        }
    }

    /**
     * Register all of the resources into RESTEasy only when Spring finishes it's
     * life-cycle and the spring singleton bean creation is completed
     */
    @Override
    public void onApplicationEvent(ApplicationEvent event)
    {
        for (SpringResourceFactory resourceFactory : resourceFactories.values())
        {
            getRegistry().removeRegistrations(resourceFactory.getScannableClass());
        }

//  The following code would re-process the bean factory, in case the configuration changed.
//  However, it needs work.
//      if (event.getSource() instanceof XmlWebApplicationContext)
//      {
//         ConfigurableListableBeanFactory beanFactory = ((XmlWebApplicationContext) event.getSource()).getBeanFactory();
//         postProcessBeanFactory(beanFactory);
//      }
        for (SpringResourceFactory resourceFactory : resourceFactories.values())
        {
            getRegistry().addResourceFactory(resourceFactory, resourceFactory.getContext());
        }
    }

    @Override
    public int getOrder()
    {
        return this.order;
    }

    public void setOrder(int order)
    {
        this.order = order;
    }

    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType)
    {
        return eventType == ContextRefreshedEvent.class;
    }

    @Override
    public boolean supportsSourceType(Class<?> sourceType)
    {
        return ApplicationContext.class.isAssignableFrom(sourceType);
    }
}
TOP

Related Classes of org.jboss.resteasy.plugins.spring.SpringBeanProcessor$ResteasyBeanPostProcessor

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.