org.impalaframework.web.spring.loader.BaseImpalaContextLoader.java Source code

Java tutorial

Introduction

Here is the source code for org.impalaframework.web.spring.loader.BaseImpalaContextLoader.java

Source

/*
 * Copyright 2007-2010 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.impalaframework.web.spring.loader;

import java.lang.reflect.Method;
import java.util.Arrays;

import javax.servlet.ServletContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.impalaframework.config.PropertiesLoader;
import org.impalaframework.constants.LocationConstants;
import org.impalaframework.exception.InvalidStateException;
import org.impalaframework.facade.ModuleManagementFacade;
import org.impalaframework.module.ModuleDefinitionSource;
import org.impalaframework.module.operation.ModuleOperation;
import org.impalaframework.module.operation.ModuleOperationConstants;
import org.impalaframework.module.operation.ModuleOperationInput;
import org.impalaframework.module.spi.Application;
import org.impalaframework.module.spi.ApplicationManager;
import org.impalaframework.spring.module.SpringModuleUtils;
import org.impalaframework.util.ObjectUtils;
import org.impalaframework.util.ReflectionUtils;
import org.impalaframework.web.WebConstants;
import org.impalaframework.web.bootstrap.ServletContextLocationsRetriever;
import org.impalaframework.web.bootstrap.WebContextLocationResolver;
import org.impalaframework.web.config.ServletContextPropertiesLoader;
import org.impalaframework.web.helper.WebServletUtils;
import org.impalaframework.web.module.WebModuleUtils;
import org.impalaframework.web.module.source.ServletModuleDefinitionSource;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.GenericWebApplicationContext;
import org.springframework.web.context.support.ServletContextResource;
import org.springframework.web.context.support.XmlWebApplicationContext;

/**
 * Extension of <code>ContextLoader</code> which implements base functionality
 * for initializing Impala within a Spring web application. This class is
 * abstract. It defines the abstract method
 * <code>getModuleDefinitionSource</code>, which subclasses must implement.
 * The implementation of this method provides the strategy for loading metadata
 * which specifies the structure of the modules to be loaded as part of this web
 * application.
 * 
 * @author Phil Zoio
 */
public abstract class BaseImpalaContextLoader extends ContextLoader implements ServletModuleDefinitionSource {

    private static final Log logger = LogFactory.getLog(BaseImpalaContextLoader.class);

    /* ************************* Overridden superclass methods ******************** */

    @Override
    public WebApplicationContext initWebApplicationContext(ServletContext servletContext)
            throws IllegalStateException, BeansException {

        final WebApplicationContext superContext = super.initWebApplicationContext(servletContext);
        initImpalaApplicationContext(servletContext, superContext);

        return superContext;
    }

    /**
     * Creates the <code>WebApplicationContext</code> by bootstrapping Impala, retrieving the <code>ModuleDefinitionSource</code>
     * for loading the module metadata. It then instantiates the application context and returns it for further processing by
     * <code>ContextLoader</code>.
     */
    @Override
    protected WebApplicationContext createWebApplicationContext(ServletContext servletContext,
            ApplicationContext parent) throws BeansException {

        final WebApplicationContext newContext;

        //this method is supported for 3.1 and above
        Method configurableMethod = ReflectionUtils.findMethod(super.getClass(),
                "configureAndRefreshWebApplicationContext",
                new Class[] { ConfigurableWebApplicationContext.class, ServletContext.class });

        if (configurableMethod != null) {

            newContext = super.createWebApplicationContext(servletContext);

            //use the configure method to do configuration on the web application context
            if (newContext instanceof ConfigurableWebApplicationContext) {

                ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) newContext;
                if (configurableMethod != null) {

                    ReflectionUtils.invokeMethod(configurableMethod, this, new Object[] { parent, servletContext });
                }

                wac.setParent(parent);
                wac.setServletContext(servletContext);
            }

        } else {

            //for 3.0 and below
            newContext = legacyCreateContext(servletContext, parent);
        }

        return newContext;
    }

    /**
     * For Spring 3.0 and below, falls back to the old method of creating a parent application context
     */
    @SuppressWarnings("deprecation")
    private WebApplicationContext legacyCreateContext(ServletContext servletContext, ApplicationContext parent) {
        final WebApplicationContext newContext;
        newContext = super.createWebApplicationContext(servletContext, parent);
        return newContext;
    }

    /**
     * Overrides the empty {@link ContextLoader#customizeContext(ServletContext, ConfigurableWebApplicationContext)}
     * by setting the parent {@link ApplicationContext} to use an empty location if the current location is simply the default,
     * and this does not exist. Effectively provides transparent support for "empty" parent {@link ApplicationContext}
     */
    @Override
    protected void customizeContext(ServletContext servletContext,
            ConfigurableWebApplicationContext applicationContext) {

        final String initParameter = servletContext.getInitParameter(CONFIG_LOCATION_PARAM);
        if (initParameter == null) {
            final ServletContextResource servletContextResource = new ServletContextResource(servletContext,
                    XmlWebApplicationContext.DEFAULT_CONFIG_LOCATION);
            if (!servletContextResource.exists()) {
                try {
                    applicationContext.setConfigLocation("classpath:META-INF/impala-web-empty.xml");
                } catch (UnsupportedOperationException e) {
                    //no longer supported in Spring 3.2, so ignore this
                }
            }
        }
    }

    protected ConfigurableApplicationContext initImpalaApplicationContext(ServletContext servletContext,
            final WebApplicationContext superContext) {

        ModuleManagementFacade facade = createModuleManagementFacade(servletContext, superContext);

        // load the parent context, which is web-independent
        ModuleDefinitionSource moduleDefinitionSource = getModuleDefinitionSource(servletContext, facade);

        // add items to servlet context
        servletContext.setAttribute(WebConstants.MODULE_DEFINITION_SOURCE_ATTRIBUTE, moduleDefinitionSource);
        servletContext.setAttribute(WebConstants.IMPALA_FACTORY_ATTRIBUTE, facade);

        ApplicationManager applicationManager = facade.getApplicationManager();
        Application application = applicationManager.getCurrentApplication();

        ModuleOperationInput input = new ModuleOperationInput(moduleDefinitionSource, null, null);
        ModuleOperation operation = facade.getModuleOperationRegistry()
                .getOperation(ModuleOperationConstants.UpdateRootModuleOperation);
        operation.execute(application, input);

        ConfigurableApplicationContext context = SpringModuleUtils
                .getRootSpringContext(application.getModuleStateHolder());

        if (context == null) {
            logger.warn("Root module context is null: no modules loaded");
        } else if (!(context instanceof WebApplicationContext)) {
            throw new InvalidStateException(
                    "Application context " + context + " has class " + context.getClass().getName()
                            + " which is not an instance of " + WebApplicationContext.class.getName());
        }

        return context;
    }

    /**
     * Overrides <code>ContextLoader</code> superclass. First, the modules are shut down. 
     * The superclass <code>closeWebApplicationContext</code> is then called.
     * Finally Impala is shut down, in the form of the <code>ModuleManagementFacade.close()</code>
     */
    @Override
    public void closeWebApplicationContext(ServletContext servletContext) {

        try {
            // the superclass closes the modules
            ModuleManagementFacade facade = WebServletUtils.getModuleManagementFacade(servletContext);

            if (facade != null) {

                servletContext.log("Closing modules and root application context hierarchy");

                facade.getApplicationManager().close();

                // now close the bootstrap factory
                facade.close();
            }
        } finally {
            super.closeWebApplicationContext(servletContext);
        }
    }

    /**
     * Returns the Impala bootstrap context locations as determined by the implementation of {@link ServletContextLocationsRetriever}
     * and properties specified in the Impala properties file
     * @return an array of Spring config locations
     */
    public String[] getBootstrapContextLocations(ServletContext servletContext) {

        String resourceName = WebModuleUtils.getParamValue(servletContext,
                LocationConstants.BOOTSTRAP_LOCATIONS_RESOURCE_PARAM);
        if (resourceName == null) {
            resourceName = "impala.properties";
        }
        final PropertiesLoader propertiesLoader = new ServletContextPropertiesLoader(servletContext, resourceName);
        final WebContextLocationResolver locationResolver = new WebContextLocationResolver();

        final ServletContextLocationsRetriever resolver = new ServletContextLocationsRetriever(servletContext,
                locationResolver, propertiesLoader);
        final String[] toReturn = resolver.getContextLocations().toArray(new String[0]);

        if (logger.isInfoEnabled()) {
            logger.info("Impala context locations: " + Arrays.toString(toReturn));
        }

        return toReturn;
    }

    /* ************************* Internal helper methods ******************** */

    /**
     * Instantiates Impala in the form of a <code>ModuleManagementFacade</code> instance.
     */
    protected ModuleManagementFacade createModuleManagementFacade(ServletContext servletContext,
            WebApplicationContext parent) {

        String[] locations = getBootstrapContextLocations(servletContext);
        logger.info("Loading bootstrap context from locations " + Arrays.toString(locations));

        final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        final GenericWebApplicationContext applicationContext = new GenericWebApplicationContext(beanFactory);
        applicationContext.setServletContext(servletContext);
        applicationContext.setParent(parent);

        XmlBeanDefinitionReader definitionReader = new XmlBeanDefinitionReader(beanFactory);
        for (int i = 0; i < locations.length; i++) {
            definitionReader.loadBeanDefinitions(new ClassPathResource(locations[i]));
        }
        applicationContext.refresh();

        return ObjectUtils.cast(applicationContext.getBean("moduleManagementFacade"), ModuleManagementFacade.class);
    }

    public abstract ModuleDefinitionSource getModuleDefinitionSource(ServletContext servletContext,
            ModuleManagementFacade factory);

}