org.codehaus.groovy.grails.commons.spring.GrailsRuntimeConfigurator.java Source code

Java tutorial

Introduction

Here is the source code for org.codehaus.groovy.grails.commons.spring.GrailsRuntimeConfigurator.java

Source

/*
 * Copyright 2004-2005 the original author or authors.
 *
 * Licensed 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.codehaus.groovy.grails.commons.spring;

import grails.spring.BeanBuilder;
import grails.util.CollectionUtils;
import grails.util.Environment;
import grails.util.Holders;
import groovy.lang.Binding;
import groovy.lang.Closure;
import groovy.lang.Script;

import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.groovy.grails.commons.ClassPropertyFetcher;
import org.codehaus.groovy.grails.commons.GrailsApplication;
import org.codehaus.groovy.grails.plugins.DefaultGrailsPluginManager;
import org.codehaus.groovy.grails.plugins.GrailsPluginManager;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.mock.web.MockServletContext;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.web.context.WebApplicationContext;

/**
 * Handles the runtime configuration of the Grails ApplicationContext.
 *
 * @author Graeme Rocher
 * @since 0.3
 */
public class GrailsRuntimeConfigurator implements ApplicationContextAware {

    public static final String BEAN_ID = "grailsConfigurator";
    public static final String GRAILS_URL_MAPPINGS = "grailsUrlMappings";
    public static final String SPRING_RESOURCES_XML = "/WEB-INF/spring/resources.xml";
    public static final String SPRING_RESOURCES_GROOVY = "/WEB-INF/spring/resources.groovy";
    public static final String SPRING_RESOURCES_CLASS = "resources";
    public static final String OPEN_SESSION_IN_VIEW_INTERCEPTOR_BEAN = "openSessionInViewInterceptor";
    public static final String TRANSACTION_MANAGER_BEAN = "transactionManager";
    public static final String HIBERNATE_PROPERTIES_BEAN = "hibernateProperties";
    public static final String DIALECT_DETECTOR_BEAN = "dialectDetector";
    public static final String SESSION_FACTORY_BEAN = "sessionFactory";
    public static final String DATA_SOURCE_BEAN = "dataSource";
    public static final String MESSAGE_SOURCE_BEAN = "messageSource";
    public static final String MULTIPART_RESOLVER_BEAN = "multipartResolver";
    public static final String EXCEPTION_HANDLER_BEAN = "exceptionHandler";
    public static final String CUSTOM_EDITORS_BEAN = "customEditors";
    public static final String CLASS_EDITOR_BEAN = "classEditor";
    public static final String CLASS_LOADER_BEAN = "classLoader";

    private static final Log LOG = LogFactory.getLog(GrailsRuntimeConfigurator.class);
    public static final String GRAILS_INITIALIZING = "org.grails.internal.INITIALIZING";

    private GrailsApplication application;
    private ApplicationContext parent;
    private GrailsPluginManager pluginManager;
    private WebRuntimeSpringConfiguration webSpringConfig;
    private static final String DEVELOPMENT_SPRING_RESOURCES_XML = "file:./grails-app/conf/spring/resources.xml";

    public GrailsRuntimeConfigurator(GrailsApplication application) {
        this(application, null);
    }

    public GrailsRuntimeConfigurator(GrailsApplication application, ApplicationContext parent) {
        this.application = application;
        this.parent = parent;

        try {
            pluginManager = parent == null ? null : parent.getBean(GrailsPluginManager.class);
        } catch (BeansException e) {
            // ignore
        }
        if (pluginManager == null) {
            pluginManager = Holders.getPluginManager();
        }
        if (pluginManager == null) {
            pluginManager = new DefaultGrailsPluginManager("**/plugins/*/**GrailsPlugin.groovy", application);
        }
        Holders.setPluginManager(pluginManager);
    }

    /**
     * Configures the Grails application context at runtime.
     *
     * @return A WebApplicationContext instance
     */
    public WebApplicationContext configure() {
        return configure(null);
    }

    /**
     * Configures the Grails application context at runtime
     *
     * @param context A ServletContext instance
     * @return An ApplicationContext instance
     */
    public WebApplicationContext configure(ServletContext context) {
        return configure(context, true);
    }

    public WebApplicationContext configure(ServletContext context, boolean loadExternalBeans) {
        Assert.notNull(application);

        // TODO GRAILS-720 this causes plugin beans to be re-created - should get getApplicationContext always call refresh?
        WebApplicationContext ctx;
        try {
            webSpringConfig = createWebRuntimeSpringConfiguration(application, parent,
                    application.getClassLoader());
            webSpringConfig.setBeanFactory(new ReloadAwareAutowireCapableBeanFactory());

            if (context != null) {
                webSpringConfig.setServletContext(context);
                pluginManager.setServletContext(context);
            }
            if (!pluginManager.isInitialised()) {
                pluginManager.loadPlugins();
            }

            if (!application.isInitialised()) {
                pluginManager.doArtefactConfiguration();
                application.initialise();
            }

            pluginManager.registerProvidedArtefacts(application);

            registerParentBeanFactoryPostProcessors(webSpringConfig);

            pluginManager.doRuntimeConfiguration(webSpringConfig);

            LOG.debug("[RuntimeConfiguration] Processing additional external configurations");

            if (loadExternalBeans) {
                doPostResourceConfiguration(application, webSpringConfig);
            }

            reset();

            application.setMainContext(webSpringConfig.getUnrefreshedApplicationContext());

            Environment.setInitializing(true);
            ctx = (WebApplicationContext) webSpringConfig.getApplicationContext();
            Environment.setInitializing(false);

            pluginManager.setApplicationContext(ctx);
            pluginManager.doDynamicMethods();

            ctx.publishEvent(new GrailsContextEvent(ctx, GrailsContextEvent.DYNAMIC_METHODS_REGISTERED));

            performPostProcessing(ctx);

            application.refreshConstraints();
        } finally {
            ClassPropertyFetcher.clearClassPropertyFetcherCache();
        }

        return ctx;
    }

    protected WebRuntimeSpringConfiguration createWebRuntimeSpringConfiguration(GrailsApplication app,
            ApplicationContext parentCtx, ClassLoader classLoader) {
        return new WebRuntimeSpringConfiguration(parentCtx, classLoader);
    }

    @SuppressWarnings("rawtypes")
    private void registerParentBeanFactoryPostProcessors(WebRuntimeSpringConfiguration springConfig) {
        if (parent == null) {
            return;
        }

        Map parentPostProcessors = parent.getBeansOfType(BeanFactoryPostProcessor.class);
        for (Object o : parentPostProcessors.values()) {
            BeanFactoryPostProcessor postProcessor = (BeanFactoryPostProcessor) o;
            ((ConfigurableApplicationContext) springConfig.getUnrefreshedApplicationContext())
                    .addBeanFactoryPostProcessor(postProcessor);
        }
    }

    public void reconfigure(GrailsApplicationContext current, ServletContext servletContext,
            boolean loadExternalBeans) {
        RuntimeSpringConfiguration springConfig = parent != null ? new DefaultRuntimeSpringConfiguration(parent)
                : new DefaultRuntimeSpringConfiguration();
        Assert.state(pluginManager.isInitialised(),
                "Cannot re-configure Grails application when it hasn't even been configured yet!");

        pluginManager.doRuntimeConfiguration(springConfig);

        List<String> beanNames = springConfig.getBeanNames();
        for (Object beanName : beanNames) {
            String name = (String) beanName;
            if (LOG.isDebugEnabled()) {
                LOG.debug("Re-creating bean definition [" + name + "]");
            }
            current.registerBeanDefinition(name, springConfig.createBeanDefinition(name));
            // force initialisation
            current.getBean(name);
        }
        pluginManager.doDynamicMethods();
        pluginManager.doPostProcessing(current);

        if (loadExternalBeans) {
            doPostResourceConfiguration(application, springConfig);
        }

        reset();
    }

    private void performPostProcessing(WebApplicationContext ctx) {
        pluginManager.doPostProcessing(ctx);
    }

    public WebApplicationContext configureDomainOnly() {
        WebRuntimeSpringConfiguration springConfig = new WebRuntimeSpringConfiguration(parent,
                application.getClassLoader());
        springConfig.setServletContext(new MockServletContext());

        if (!pluginManager.isInitialised()) {
            pluginManager.loadPlugins();
        }

        if (pluginManager.hasGrailsPlugin("hibernate")) {
            pluginManager.doRuntimeConfiguration("hibernate", springConfig);
        } else if (pluginManager.hasGrailsPlugin("hibernate4")) {
            pluginManager.doRuntimeConfiguration("hibernate4", springConfig);
        }

        WebApplicationContext ctx = (WebApplicationContext) springConfig.getApplicationContext();

        performPostProcessing(ctx);
        application.refreshConstraints();

        return ctx;
    }

    private void doPostResourceConfiguration(GrailsApplication app, RuntimeSpringConfiguration springConfig) {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        String resourceName = null;
        try {
            Resource springResources;
            if (app.isWarDeployed()) {
                resourceName = GrailsRuntimeConfigurator.SPRING_RESOURCES_XML;
                springResources = parent.getResource(resourceName);
            } else {
                resourceName = DEVELOPMENT_SPRING_RESOURCES_XML;
                ResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver();
                springResources = patternResolver.getResource(resourceName);
            }

            if (springResources != null && springResources.exists()) {
                if (LOG.isDebugEnabled())
                    LOG.debug(
                            "[RuntimeConfiguration] Configuring additional beans from " + springResources.getURL());
                DefaultListableBeanFactory xmlBf = new DefaultListableBeanFactory();
                new XmlBeanDefinitionReader(xmlBf).loadBeanDefinitions(springResources);
                xmlBf.setBeanClassLoader(classLoader);
                String[] beanNames = xmlBf.getBeanDefinitionNames();
                if (LOG.isDebugEnabled())
                    LOG.debug("[RuntimeConfiguration] Found [" + beanNames.length + "] beans to configure");
                for (String beanName : beanNames) {
                    BeanDefinition bd = xmlBf.getBeanDefinition(beanName);
                    final String beanClassName = bd.getBeanClassName();
                    Class<?> beanClass = beanClassName == null ? null
                            : ClassUtils.forName(beanClassName, classLoader);

                    springConfig.addBeanDefinition(beanName, bd);
                    String[] aliases = xmlBf.getAliases(beanName);
                    for (String alias : aliases) {
                        springConfig.addAlias(alias, beanName);
                    }
                    if (beanClass != null) {
                        if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass)) {
                            ((ConfigurableApplicationContext) springConfig.getUnrefreshedApplicationContext())
                                    .addBeanFactoryPostProcessor(
                                            (BeanFactoryPostProcessor) xmlBf.getBean(beanName));
                        }
                    }
                }
            } else if (LOG.isDebugEnabled()) {
                LOG.debug("[RuntimeConfiguration] " + resourceName + " not found. Skipping configuration.");
            }
        } catch (Exception ex) {
            LOG.error("[RuntimeConfiguration] Unable to perform post initialization config: " + resourceName, ex);
        }

        GrailsRuntimeConfigurator.loadSpringGroovyResources(springConfig, app);
    }

    private static volatile BeanBuilder springGroovyResourcesBeanBuilder = null;

    /**
     * Attempt to load the beans defined by a BeanBuilder DSL closure in "resources.groovy".
     *
     * @param config
     * @param context
     */
    private static void doLoadSpringGroovyResources(RuntimeSpringConfiguration config,
            GrailsApplication application, GenericApplicationContext context) {

        loadExternalSpringConfig(config, application);
        if (context != null) {
            springGroovyResourcesBeanBuilder.registerBeans(context);
        }
    }

    /**
     * Loads any external Spring configuration into the given RuntimeSpringConfiguration object.
     * @param config The config instance
     */
    public static void loadExternalSpringConfig(RuntimeSpringConfiguration config,
            final GrailsApplication application) {
        if (springGroovyResourcesBeanBuilder == null) {
            try {
                Class<?> groovySpringResourcesClass = null;
                try {
                    groovySpringResourcesClass = ClassUtils.forName(
                            GrailsRuntimeConfigurator.SPRING_RESOURCES_CLASS, application.getClassLoader());
                } catch (ClassNotFoundException e) {
                    // ignore
                }
                if (groovySpringResourcesClass != null) {
                    reloadSpringResourcesConfig(config, application, groovySpringResourcesClass);
                }
            } catch (Exception ex) {
                LOG.error("[RuntimeConfiguration] Unable to load beans from resources.groovy", ex);
            }
        } else {
            if (!springGroovyResourcesBeanBuilder.getSpringConfig().equals(config)) {
                springGroovyResourcesBeanBuilder.registerBeans(config);
            }
        }
    }

    public static BeanBuilder reloadSpringResourcesConfig(RuntimeSpringConfiguration config,
            GrailsApplication application, Class<?> groovySpringResourcesClass)
            throws InstantiationException, IllegalAccessException {
        springGroovyResourcesBeanBuilder = new BeanBuilder(null, config,
                Thread.currentThread().getContextClassLoader());
        springGroovyResourcesBeanBuilder.setBinding(
                new Binding(CollectionUtils.newMap("application", application, "grailsApplication", application))); // GRAILS-7550
        Script script = (Script) groovySpringResourcesClass.newInstance();
        script.run();
        Object beans = script.getProperty("beans");
        springGroovyResourcesBeanBuilder.beans((Closure<?>) beans);
        return springGroovyResourcesBeanBuilder;
    }

    public static void loadSpringGroovyResources(RuntimeSpringConfiguration config, GrailsApplication application) {
        loadExternalSpringConfig(config, application);
    }

    public static void loadSpringGroovyResourcesIntoContext(RuntimeSpringConfiguration config,
            GrailsApplication application, GenericApplicationContext context) {
        loadExternalSpringConfig(config, application);
        doLoadSpringGroovyResources(config, application, context);
    }

    public void setLoadExternalPersistenceConfig(boolean b) {
        // do nothing
    }

    public void setPluginManager(GrailsPluginManager manager) {
        pluginManager = manager;
    }

    public GrailsPluginManager getPluginManager() {
        return pluginManager;
    }

    public void setApplicationContext(ApplicationContext applicationContext) {
        parent = applicationContext;
    }

    /**
     * Resets the GrailsRumtimeConfigurator.
     */
    public static void reset() {
        springGroovyResourcesBeanBuilder = null;
    }

    // for testing
    WebRuntimeSpringConfiguration getWebRuntimeSpringConfiguration() {
        return webSpringConfig;
    }
}