All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mule.config.spring.SpringRegistry Maven / Gradle / Ivy

There is a newer version: 3.9.0
Show newest version
/*
 * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 * The software in this package is published under the terms of the CPAL v1.0
 * license, a copy of which has been included with this distribution in the
 * LICENSE.txt file.
 */
package org.mule.config.spring;

import static org.apache.commons.lang.StringUtils.EMPTY;
import static org.mule.config.i18n.MessageFactory.createStaticMessage;
import org.mule.api.Injector;
import org.mule.api.MuleContext;
import org.mule.api.MuleException;
import org.mule.api.MuleRuntimeException;
import org.mule.api.lifecycle.InitialisationException;
import org.mule.api.lifecycle.LifecycleException;
import org.mule.api.registry.LifecycleRegistry;
import org.mule.api.registry.RegistrationException;
import org.mule.api.registry.TransformerResolver;
import org.mule.api.transformer.Converter;
import org.mule.config.spring.processors.PostRegistrationActionsPostProcessor;
import org.mule.lifecycle.RegistryLifecycleManager;
import org.mule.lifecycle.phases.NotInLifecyclePhase;
import org.mule.registry.AbstractRegistry;
import org.mule.util.StringUtils;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;

public class SpringRegistry extends AbstractRegistry implements LifecycleRegistry, Injector
{

    public static final String REGISTRY_ID = "org.mule.Registry.Spring";

    /**
     * Key used to lookup Spring Application Context from SpringRegistry via Mule's
     * Registry interface.
     */
    public static final String SPRING_APPLICATION_CONTEXT = "springApplicationContext";

    protected ApplicationContext applicationContext;

    private boolean readOnly;

    private RegistrationDelegate registrationDelegate;

    //This is used to track the Spring context lifecycle since there is no way to confirm the
    //lifecycle phase from the application context
    protected AtomicBoolean springContextInitialised = new AtomicBoolean(false);

    public SpringRegistry(ApplicationContext applicationContext, MuleContext muleContext)
    {
        super(REGISTRY_ID, muleContext);
        setApplicationContext(applicationContext);
    }

    public SpringRegistry(String id, ApplicationContext applicationContext, MuleContext muleContext)
    {
        super(id, muleContext);
        setApplicationContext(applicationContext);
    }

    public SpringRegistry(ConfigurableApplicationContext applicationContext, ApplicationContext parentContext, MuleContext muleContext)
    {
        super(REGISTRY_ID, muleContext);
        applicationContext.setParent(parentContext);
        setApplicationContext(applicationContext);
    }

    public SpringRegistry(String id, ConfigurableApplicationContext applicationContext, ApplicationContext parentContext, MuleContext muleContext)
    {
        super(id, muleContext);
        applicationContext.setParent(parentContext);
        setApplicationContext(applicationContext);
    }

    private void setApplicationContext(ApplicationContext applicationContext)
    {
        this.applicationContext = applicationContext;
        if (applicationContext instanceof ConfigurableApplicationContext)
        {
            readOnly = false;
            registrationDelegate = new ConfigurableRegistrationDelegate((ConfigurableApplicationContext) applicationContext);
        }
        else
        {
            readOnly = true;
            registrationDelegate = new ReadOnlyRegistrationDelegate();
        }
    }

    @Override
    protected void doInitialise() throws InitialisationException
    {
        if (!readOnly)
        {
            ((ConfigurableApplicationContext) applicationContext).refresh();
        }

        initTransformers();
        //This is used to track the Spring context lifecycle since there is no way to confirm the lifecycle phase from the application context
        springContextInitialised.set(true);
    }

    /**
     * Forces prototype instances of {@link TransformerResolver}
     * and {@link Converter} to be created, so that
     * {@link PostRegistrationActionsPostProcessor} can work
     * its magic
     */
    private void initTransformers()
    {
        applicationContext.getBeansOfType(TransformerResolver.class);
        applicationContext.getBeansOfType(Converter.class);
    }

    @Override
    public void doDispose()
    {
        // check we aren't trying to close a context which has never been started,
        // spring's appContext.isActive() isn't working for this case
        if (!springContextInitialised.get())
        {
            return;
        }

        if (!isReadOnly() && ((ConfigurableApplicationContext) applicationContext).isActive())
        {
            ((ConfigurableApplicationContext) applicationContext).close();
        }

        // release the circular implicit ref to MuleContext
        applicationContext = null;
        this.springContextInitialised.set(false);
    }

    @Override
    protected RegistryLifecycleManager createLifecycleManager()
    {
        return new SpringRegistryLifecycleManager(getRegistryId(), this, muleContext);
    }

    /**
     * {@inheritDoc}
     * @return the result of invoking {@link #lookupObject(String, boolean)} with {@code true} as the second argument
     */
    @Override
    public  T lookupObject(String key)
    {
        return (T) lookupObject(key, true);
    }

    /**
     * If looks for the bean registered under {@code key}.
     * If the returned bean is a prototype and {@code applyLifecycle}
     * is {@code true}, then the completed lifecycle phases
     * are applied to the returning bean
     *
     * @param key            the key of the object you're looking for
     * @param applyLifecycle if lifecycle should be applied to the returned object.
     *                       Passing {@code true} doesn't guarantee that the lifecycle is applied
     * @return object or {@code null} if not found
     */
    @SuppressWarnings("unchecked")
    @Override
    public Object lookupObject(String key, boolean applyLifecycle)
    {
        if (StringUtils.isBlank(key))
        {
            logger.warn(
                    createStaticMessage("Detected a lookup attempt with an empty or null key").getMessage(),
                    new Throwable().fillInStackTrace());
            return null;
        }

        if (key.equals(SPRING_APPLICATION_CONTEXT) && applicationContext != null)
        {
            return applicationContext;
        }
        else
        {
            Object object;
            try
            {
                object = applicationContext.getBean(key);
            }
            catch (NoSuchBeanDefinitionException e)
            {
                if (logger.isDebugEnabled())
                {
                    logger.debug(e.getMessage(), e);
                }
                return null;
            }

            if (applyLifecycle && !applicationContext.isSingleton(key))
            {
                try
                {
                    getLifecycleManager().applyCompletedPhases(object);
                }
                catch (Exception e)
                {
                    throw new MuleRuntimeException(createStaticMessage("Could not apply lifecycle into prototype object " + key), e);
                }
            }

            return object;
        }
    }

    @Override
    public  Collection lookupObjects(Class type)
    {
        return lookupByType(type).values();
    }

    @Override
    public  Collection lookupLocalObjects(Class type)
    {
        return internalLookupByTypeWithoutAncestors(type, true, true).values();
    }

    /**
     * For lifecycle we only want spring to return singleton objects from it's application context
     */
    @Override
    public  Collection lookupObjectsForLifecycle(Class type)
    {
        return lookupEntriesForLifecycle(type).values();
    }

    @Override
    public  Map lookupByType(Class type)
    {
        return internalLookupByType(type, true, true);
    }

    @Override
    public void registerObject(String key, Object value) throws RegistrationException
    {
        registrationDelegate.registerObject(key, value);
    }

    @Override
    public void registerObject(String key, Object value, Object metadata) throws RegistrationException
    {
        registrationDelegate.registerObject(key, value, metadata);
    }

    @Override
    public void registerObjects(Map objects) throws RegistrationException
    {
        registrationDelegate.registerObjects(objects);
    }

    @Override
    protected Object doUnregisterObject(String key) throws RegistrationException
    {
        return registrationDelegate.unregisterObject(key);
    }

    /**
     * Will fire any lifecycle methods according to the current lifecycle without actually
     * registering the object in the registry.  This is useful for prototype objects that are created per request and would
     * clutter the registry with single use objects.
     *
     * @param object the object to process
     * @return the same object with lifecycle methods called (if it has any)
     * @throws org.mule.api.MuleException if the registry fails to perform the lifecycle change for the object.
     */
    @Override
    public Object applyLifecycle(Object object) throws MuleException
    {
        getLifecycleManager().applyCompletedPhases(object);
        return object;
    }

    @Override
    public Object applyLifecycle(Object object, String phase) throws MuleException
    {
        if (phase == null)
        {
            getLifecycleManager().applyCompletedPhases(object);
        }
        else
        {
            getLifecycleManager().applyPhase(object, NotInLifecyclePhase.PHASE_NAME, phase);
        }
        return object;
    }

    @Override
    public  T inject(T object)
    {
        try
        {
            return initialiseObject((ConfigurableApplicationContext) applicationContext, EMPTY, object);
        }
        catch (LifecycleException e)
        {
            throw new MuleRuntimeException(e);
        }
    }

    private  T initialiseObject(ConfigurableApplicationContext applicationContext, String key, T object) throws LifecycleException
    {
        applicationContext.getBeanFactory().autowireBean(object);
        T initialised = (T) applicationContext.getBeanFactory().initializeBean(object, key);

        return initialised;
    }

    protected  Map internalLookupByType(Class type, boolean nonSingletons, boolean eagerInit)
    {
        try
        {
            return BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, type, nonSingletons, eagerInit);
        }
        catch (FatalBeanException fbex)
        {
            // FBE is a result of a broken config, propagate it (see MULE-3297 for more details)
            String message = String.format("Failed to lookup beans of type %s from the Spring registry", type);
            throw new MuleRuntimeException(createStaticMessage(message), fbex);
        }
        catch (Exception e)
        {
            logger.debug(e.getMessage(), e);
            return Collections.emptyMap();
        }
    }

    protected  Map internalLookupByTypeWithoutAncestors(Class type, boolean nonSingletons, boolean eagerInit)
    {
        try
        {
            return applicationContext.getBeansOfType(type, nonSingletons, eagerInit);
        }
        catch (FatalBeanException fbex)
        {
            // FBE is a result of a broken config, propagate it (see MULE-3297 for more details)
            String message = String.format("Failed to lookup beans of type %s from the Spring registry", type);
            throw new MuleRuntimeException(createStaticMessage(message), fbex);
        }
        catch (Exception e)
        {
            if (logger.isDebugEnabled())
            {
                logger.debug(e.getMessage(), e);
            }
            return Collections.emptyMap();
        }
    }

    protected  Map lookupEntriesForLifecycle(Class type)
    {
        return internalLookupByTypeWithoutAncestors(type, false, false);
    }

    protected Map getDependencies(String key)
    {
        if (!readOnly)
        {
            Map dependents = new HashMap<>();
            for (String dependentKey : ((ConfigurableApplicationContext) applicationContext).getBeanFactory().getDependenciesForBean(key))
            {
                boolean isBeanDefinition = ((ConfigurableApplicationContext) applicationContext).getBeanFactory().containsBeanDefinition(dependentKey);

                if (isBeanDefinition && applicationContext.isSingleton(dependentKey))
                {
                    dependents.put(dependentKey, get(dependentKey));
                }
            }

            return dependents;
        }

        throw new UnsupportedOperationException("This operation is only available when this registry is backed by a ConfigurableApplicationContext");
    }

    private class ConfigurableRegistrationDelegate implements RegistrationDelegate
    {

        private final ConfigurableApplicationContext applicationContext;

        private ConfigurableRegistrationDelegate(ConfigurableApplicationContext applicationContext)
        {
            this.applicationContext = applicationContext;
        }

        @Override
        public void registerObject(String key, Object value) throws RegistrationException
        {
            doRegisterObject(key, value);
        }

        @Override
        public void registerObject(String key, Object value, Object metadata) throws RegistrationException
        {
            registerObject(key, value);
        }

        @Override
        public void registerObjects(Map objects) throws RegistrationException
        {
            if (objects == null || objects.isEmpty())
            {
                return;
            }

            for (Map.Entry entry : objects.entrySet())
            {
                registerObject(entry.getKey(), entry.getValue());
            }
        }

        @Override
        public Object unregisterObject(String key) throws RegistrationException
        {
            Object object = applicationContext.getBean(key);

            if (applicationContext.getBeanFactory().containsBeanDefinition(key))
            {
                ((BeanDefinitionRegistry) applicationContext.getBeanFactory()).removeBeanDefinition(key);
            }

            ((DefaultListableBeanFactory) applicationContext.getBeanFactory()).destroySingleton(key);

            return object;
        }

        private synchronized void doRegisterObject(String key, Object value) throws RegistrationException
        {
            if (applicationContext.containsBean(key))
            {
                if (logger.isWarnEnabled())
                {
                    logger.warn(String.format("Spring registry already contains an object named '%s'. The previous object will be overwritten.", key));
                }
                SpringRegistry.this.unregisterObject(key);
            }

            try
            {
                value = initialiseObject(applicationContext, key, value);
                applyLifecycle(value);
                applicationContext.getBeanFactory().registerSingleton(key, value);
            }
            catch (Exception e)
            {
                throw new RegistrationException(createStaticMessage("Could not register object for key " + key), e);
            }
        }
    }

    ////////////////////////////////////////////////////////////////////////////////////
    // Registry meta-data
    ////////////////////////////////////////////////////////////////////////////////////

    @Override
    public boolean isReadOnly()
    {
        return readOnly;
    }

    @Override
    public boolean isRemote()
    {
        return false;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy