org.broadleafcommerce.test.BroadleafGenericGroovyXmlWebContextLoader.java Source code

Java tutorial

Introduction

Here is the source code for org.broadleafcommerce.test.BroadleafGenericGroovyXmlWebContextLoader.java

Source

/*
 * #%L
 * BroadleafCommerce Custom Field
 * %%
 * Copyright (C) 2009 - 2014 Broadleaf Commerce
 * %%
 * 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.
 * #L%
 */

package org.broadleafcommerce.test;

import org.apache.commons.lang3.StringUtils;
import org.broadleafcommerce.common.extensibility.context.StandardConfigLocations;
import org.broadleafcommerce.common.web.extensibility.MergeXmlWebApplicationContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.FileSystemResourceLoader;
import org.springframework.core.io.ResourceLoader;
import org.springframework.mock.web.MockServletContext;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.support.AbstractContextLoader;
import org.springframework.test.context.web.AbstractGenericWebContextLoader;
import org.springframework.test.context.web.GenericGroovyXmlWebContextLoader;
import org.springframework.test.context.web.GenericXmlWebContextLoader;
import org.springframework.test.context.web.WebMergedContextConfiguration;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.WebApplicationContext;

import javax.servlet.ServletContext;

/**
 * <p>
 * This class was created due to AbstractGenericWebContextLoader utilizing the qualifier "final" for its
 * loadContext(MergedContextConfiguration) method which we needed to override and provide our Broadleaf created
 * MergeXmlWebApplicationContext object in place of the GenericWebApplicationContext it used. Since we are using
 * Groovy/Spock, the other methods included are to support Groovy test classes. As such, are included some methods
 * from the GenericGroovyXmlWebContextLoader and GenericXmlWebContextLoader classes, refactored to compensate for
 * the levels of inheritance between them.
 * <p>
 * This class loader should be used with the @ContextConfiguration annotation to be placed in the 'loader'
 * parameter for all Broadleaf integration tests which use the Spock testing framework.
 * 
 * @author austinrooke
 *
 */
public class BroadleafGenericGroovyXmlWebContextLoader extends AbstractContextLoader {

    /*
     * Some notes on usage of this context loader.
     * 
     * The set up for the spock/groovy test is as follows. You will need to include the dependencies:
     * spock-spring
     * spring-test
     * javax-servlet
     * integration
     * 
     * Then you will need to also set the following notations on the test class itself:
     * @TransactionConfiguration(transactionManager = "blTransactionManager")
     * @ContextConfiguration(locations = [Include application context's that are required for your
     *  integration test but you MUST include the following application context as the last
     *  entry into this set, "classpath:/bl-applicationContext-test.xml"]
     * @WebAppConfiguration
     * 
     * With these annotations, you are now set up to test any features and classes.
     * 
     * In addition, with the usage of spring's testframework and spock/groovy, we can also test
     * RESTful services directly utilizing spring's mockMVC api. Please see its documentation
     * if you would like to test RESTful services.
     */

    /**
     * {@code BroadleafGenericGroovyXmlWebContextLoader} supports the XML merging that
     * Broadleaf's framework features, but this is handled during the .refresh() method
     * in the loadContext method so this method was only pulled from the original class
     * {@link GenericGroovyXmlWebContextLoader} in case of the Spring-Test framework requiring
     * this method to have a particular behavior.
     */
    protected String[] getResourceSuffixes() {
        return new String[] { "-context.xml", "Context.groovy" };
    }

    /**
     * {@code BroadleafGenericGroovyXmlWebContextLoader} supports the XML merging that
     * Broadleaf's framework features, but this is handled during the .refresh() method
     * in the loadContext method so this method was only pulled from the original class
     * {@link GenericGroovyXmlWebContextLoader} in case of the Spring-Test framework requiring
     * this method to have a particular behavior.
     */
    @Override
    protected String getResourceSuffix() {
        throw new UnsupportedOperationException(
                "BroadleafGenericGroovyXmlWebContextLoader does not support the getResourceSuffix() method");
    }

    /**
     * {@code BroadleafGenericGroovyXmlWebContextLoader} supports the XML merging that
     * Broadleaf's framework features, but this is handled during the .refresh() method
     * in the loadContext method so this method was only pulled from the original class
     * {@link GenericXmlWebContextLoader} in case of the Spring-Test framework requiring
     * this method to have a particular behavior.
     *
     * @see AbstractGenericWebContextLoader#validateMergedContextConfiguration
     */
    protected void validateMergedContextConfiguration(WebMergedContextConfiguration webMergedConfig) {
        if (webMergedConfig.hasClasses()) {
            String msg = String.format(
                    "Test class [%s] has been configured with @ContextConfiguration's 'classes' attribute %s, "
                            + "but %s does not support annotated classes.",
                    webMergedConfig.getTestClass().getName(),
                    ObjectUtils.nullSafeToString(webMergedConfig.getClasses()), getClass().getSimpleName());
            throw new IllegalStateException(msg);
        }
    }

    /**
     * {@code BroadleafGenericGroovyXmlWebContextLoader} should be used as a
     * {@link org.springframework.test.context.SmartContextLoader SmartContextLoader},
     * not as a legacy {@link org.springframework.test.context.ContextLoader ContextLoader}.
     * Consequently, this method is not supported.
     * 
     * This method was pulled from {@code AbstractGenericWebContextLoader}.
     *
     * @see org.springframework.test.context.ContextLoader#loadContext(java.lang.String[])
     * @throws UnsupportedOperationException
     */
    @Override
    public final ApplicationContext loadContext(String... locations) throws Exception {
        throw new UnsupportedOperationException(
                "BroadleafGenericGroovyXmlWebContextLoader does not support the loadContext(String... locations) method");
    }

    /**
     * Load a {@link MergeXmlWebApplicationContext} from the supplied {@link MergedContextConfiguration}
     * 
     * <p>Implementation details:
     * 
     * <ul>
     * <li>Calls {@link #validateMergedContextConfiguration(WebMergedContextConfiguration)}
     * to allow subclasses to validate the supplied configuration before proceeding.</li>
     * <li>Creates a {@link MergeXmlWebApplicationContext} instance.</li>
     * <li>If the supplied {@link MergeXmlWebApplicationContext} references a
     * {@linkplain MergeXmlWebApplicationContext#getParent() parent configuration},
     * the corresponding {@link MergeXmlWebApplicationContext#getParentApplicationContext()
     * ApplicationContext} will be retrieved and 
     * {@linkplain MergeXmlWebApplicationContext#setParent(ApplicationContext) set as the parent}
     * for the context created by this method.</li>
     * <li>Converts the patch locations into a single string to be set via
     * {@link MergeXmlWebApplicationContext#setPatchLocation(String)}</li>
     * <li>Sets the patch locations via {@link MergeXmlWebApplicationContext#setStandardLocationTypes(String)}
     * to the {@link StandardConfigLocations.TESTCONTEXTTYPE} for integration tests.</li>
     * <li>Delegates to {@link #configureWebResources} to create the {@link MockServletContext} and
     * set it in the {@code MergeXmlWebApplicationContext}.</li>
     * <li>Calls {@link #prepareContext} to allow for customizing the context before bean
     * definitions are loaded.</li>
     * <li>{@link ConfigurableApplicationContext#refresh Refreshes} the context and registers
     * a JVM shutdown hook for it.</li>
     * </ul></p>
     * 
     * Refactored from {@link org.springframework.test.context.web.AbstractGenericWebContextLoader#loadContext(MergedContextConfiguration)}
     * 
     * @return a new merge xml web application context
     * @see org.springframework.test.context.SmartContextLoader#loadContext(MergedContextConfiguration)
     * @see MergeXmlWebApplicationContext
     */
    @Override
    public ConfigurableApplicationContext loadContext(MergedContextConfiguration mergedConfig) throws Exception {

        if (!(mergedConfig instanceof WebMergedContextConfiguration)) {
            throw new IllegalArgumentException(
                    String.format(
                            "Cannot load WebApplicationContext from non-web merged context configuration %s. "
                                    + "Consider annotating your test class with @WebAppConfiguration.",
                            mergedConfig));
        }
        WebMergedContextConfiguration webMergedConfig = (WebMergedContextConfiguration) mergedConfig;

        validateMergedContextConfiguration(webMergedConfig);

        MergeXmlWebApplicationContext context = new MergeXmlWebApplicationContext();
        context.setPatchLocation("");

        ApplicationContext parent = mergedConfig.getParentApplicationContext();
        if (parent != null) {
            context.setParent(parent);
            context.setPatchLocation(
                    StringUtils.removeEnd(((MergeXmlWebApplicationContext) parent).getPatchLocation(),
                            "classpath:/bl-applicationContext-test.xml"));
            System.out.println(context.getPatchLocation());
        }
        //Calls unique to Broadleaf Implementation of the Smart Context Loader
        // the ";classpath:/bl-applicationContext-test.xml" is required by all integration tests so we add it here.
        context.setPatchLocation(context.getPatchLocation() + StringUtils.join(mergedConfig.getLocations(), ";")
                + ";classpath:/bl-applicationContext-test.xml");
        context.setStandardLocationTypes(StandardConfigLocations.TESTCONTEXTTYPE);

        configureWebResources(context, webMergedConfig);
        prepareContext(context, webMergedConfig);
        context.refresh();
        context.registerShutdownHook();
        return context;
    }

    /**
     * Configures web resources for the supplied web application context (WAC).
     *
     * <h4>Implementation Details</h4>
     *
     * <p>If the supplied WAC has no parent or its parent is not a WAC, the
     * supplied WAC will be configured as the Root WAC (see "<em>Root WAC
     * Configuration</em>" below).
     *
     * <p>Otherwise the context hierarchy of the supplied WAC will be traversed
     * to find the top-most WAC (i.e., the root); and the {@link ServletContext}
     * of the Root WAC will be set as the {@code ServletContext} for the supplied
     * WAC.
     *
     * <h4>Root WAC Configuration</h4>
     *
     * <ul>
     * <li>The resource base path is retrieved from the supplied
     * {@code WebMergedContextConfiguration}.</li>
     * <li>A {@link ResourceLoader} is instantiated for the {@link MockServletContext}:
     * if the resource base path is prefixed with "{@code classpath:/}", a
     * {@link DefaultResourceLoader} will be used; otherwise, a
     * {@link FileSystemResourceLoader} will be used.</li>
     * <li>A {@code MockServletContext} will be created using the resource base
     * path and resource loader.</li>
     * <li>The supplied {@link MergeXmlWebApplicationContext} is then stored in
     * the {@code MockServletContext} under the
     * {@link MergeXmlWebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE} key.</li>
     * <li>Finally, the {@code MockServletContext} is set in the
     * {@code MergeXmlWebApplicationContext}.</li>
     *
     * @param context the merge xml web application context for which to configure the web
     * resources
     * @param webMergedConfig the merged context configuration to use to load the
     * merge xml web application context
     */
    protected void configureWebResources(MergeXmlWebApplicationContext context,
            WebMergedContextConfiguration webMergedConfig) {

        ApplicationContext parent = context.getParent();

        // if the WAC has no parent or the parent is not a WAC, set the WAC as
        // the Root WAC:
        if (parent == null || (!(parent instanceof WebApplicationContext))) {
            String resourceBasePath = webMergedConfig.getResourceBasePath();
            ResourceLoader resourceLoader = resourceBasePath.startsWith(ResourceLoader.CLASSPATH_URL_PREFIX)
                    ? new DefaultResourceLoader()
                    : new FileSystemResourceLoader();

            ServletContext servletContext = new MockServletContext(resourceBasePath, resourceLoader);
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
            context.setServletContext(servletContext);
        } else {
            ServletContext servletContext = null;

            // find the Root WAC
            while (parent != null) {
                if (parent instanceof WebApplicationContext
                        && !(parent.getParent() instanceof WebApplicationContext)) {
                    servletContext = ((WebApplicationContext) parent).getServletContext();
                    break;
                }
                parent = parent.getParent();
            }
            Assert.state(servletContext != null,
                    "Failed to find Root MergeXmlWebApplicationContext in the context hierarchy");
            context.setServletContext(servletContext);
        }
    }

}