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

org.springframework.test.context.support.AbstractContextLoader Maven / Gradle / Ivy

There is a newer version: 6.1.6
Show newest version
/*
 * Copyright 2002-2018 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
 *
 *      https://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.springframework.test.context.support;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextLoader;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.SmartContextLoader;
import org.springframework.test.context.util.TestContextResourceUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ResourceUtils;

/**
 * Abstract application context loader that provides a basis for all concrete
 * implementations of the {@link ContextLoader} SPI. Provides a
 * Template Method based approach for {@link #processLocations processing}
 * resource locations.
 *
 * 

As of Spring 3.1, {@code AbstractContextLoader} also provides a basis * for all concrete implementations of the {@link SmartContextLoader} SPI. For * backwards compatibility with the {@code ContextLoader} SPI, * {@link #processContextConfiguration(ContextConfigurationAttributes)} delegates * to {@link #processLocations(Class, String...)}. * * @author Sam Brannen * @author Juergen Hoeller * @author Phillip Webb * @since 2.5 * @see #generateDefaultLocations * @see #getResourceSuffixes * @see #modifyLocations * @see #prepareContext * @see #customizeContext */ public abstract class AbstractContextLoader implements SmartContextLoader { private static final String[] EMPTY_STRING_ARRAY = new String[0]; private static final Log logger = LogFactory.getLog(AbstractContextLoader.class); // SmartContextLoader /** * For backwards compatibility with the {@link ContextLoader} SPI, the * default implementation simply delegates to {@link #processLocations(Class, String...)}, * passing it the {@link ContextConfigurationAttributes#getDeclaringClass() * declaring class} and {@link ContextConfigurationAttributes#getLocations() * resource locations} retrieved from the supplied * {@link ContextConfigurationAttributes configuration attributes}. The * processed locations are then * {@link ContextConfigurationAttributes#setLocations(String[]) set} in * the supplied configuration attributes. *

Can be overridden in subclasses — for example, to process * annotated classes instead of resource locations. * @since 3.1 * @see #processLocations(Class, String...) */ @Override public void processContextConfiguration(ContextConfigurationAttributes configAttributes) { String[] processedLocations = processLocations(configAttributes.getDeclaringClass(), configAttributes.getLocations()); configAttributes.setLocations(processedLocations); } /** * Prepare the {@link ConfigurableApplicationContext} created by this * {@code SmartContextLoader} before bean definitions are read. *

The default implementation: *

    *
  • Sets the active bean definition profiles from the supplied * {@code MergedContextConfiguration} in the * {@link org.springframework.core.env.Environment Environment} of the * context.
  • *
  • Adds {@link PropertySource PropertySources} for all * {@linkplain MergedContextConfiguration#getPropertySourceLocations() * resource locations} and * {@linkplain MergedContextConfiguration#getPropertySourceProperties() * inlined properties} from the supplied {@code MergedContextConfiguration} * to the {@code Environment} of the context.
  • *
  • Determines what (if any) context initializer classes have been supplied * via the {@code MergedContextConfiguration} and instantiates and * {@linkplain ApplicationContextInitializer#initialize invokes} each with the * given application context. *
      *
    • Any {@code ApplicationContextInitializers} implementing * {@link org.springframework.core.Ordered Ordered} or annotated with {@link * org.springframework.core.annotation.Order @Order} will be sorted appropriately.
    • *
    *
  • *
* @param context the newly created application context * @param mergedConfig the merged context configuration * @since 3.2 * @see TestPropertySourceUtils#addPropertiesFilesToEnvironment * @see TestPropertySourceUtils#addInlinedPropertiesToEnvironment * @see ApplicationContextInitializer#initialize(ConfigurableApplicationContext) * @see #loadContext(MergedContextConfiguration) * @see ConfigurableApplicationContext#setId */ protected void prepareContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) { context.getEnvironment().setActiveProfiles(mergedConfig.getActiveProfiles()); TestPropertySourceUtils.addPropertiesFilesToEnvironment(context, mergedConfig.getPropertySourceLocations()); TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context, mergedConfig.getPropertySourceProperties()); invokeApplicationContextInitializers(context, mergedConfig); } @SuppressWarnings("unchecked") private void invokeApplicationContextInitializers(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) { Set>> initializerClasses = mergedConfig.getContextInitializerClasses(); if (initializerClasses.isEmpty()) { // no ApplicationContextInitializers have been declared -> nothing to do return; } List> initializerInstances = new ArrayList<>(); Class contextClass = context.getClass(); for (Class> initializerClass : initializerClasses) { Class initializerContextClass = GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class); if (initializerContextClass != null && !initializerContextClass.isInstance(context)) { throw new ApplicationContextException(String.format( "Could not apply context initializer [%s] since its generic parameter [%s] " + "is not assignable from the type of application context used by this " + "context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(), contextClass.getName())); } initializerInstances.add((ApplicationContextInitializer) BeanUtils.instantiateClass(initializerClass)); } AnnotationAwareOrderComparator.sort(initializerInstances); for (ApplicationContextInitializer initializer : initializerInstances) { initializer.initialize(context); } } /** * Customize the {@link ConfigurableApplicationContext} created by this * {@code ContextLoader} after bean definitions have been loaded * into the context but before the context has been refreshed. *

The default implementation delegates to all * {@link MergedContextConfiguration#getContextCustomizers context customizers} * that have been registered with the supplied {@code mergedConfig}. * @param context the newly created application context * @param mergedConfig the merged context configuration * @since 4.3 */ protected void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) { for (ContextCustomizer contextCustomizer : mergedConfig.getContextCustomizers()) { contextCustomizer.customizeContext(context, mergedConfig); } } // ContextLoader /** * If the supplied {@code locations} are {@code null} or empty * and {@link #isGenerateDefaultLocations()} returns {@code true}, * default locations will be {@link #generateDefaultLocations(Class) * generated} (i.e., detected) for the specified {@link Class class} * and the configured {@linkplain #getResourceSuffixes() resource suffixes}; * otherwise, the supplied {@code locations} will be * {@linkplain #modifyLocations modified} if necessary and returned. * @param clazz the class with which the locations are associated: to be * used when generating default locations * @param locations the unmodified locations to use for loading the * application context (can be {@code null} or empty) * @return a processed array of application context resource locations * @since 2.5 * @see #isGenerateDefaultLocations() * @see #generateDefaultLocations(Class) * @see #modifyLocations(Class, String...) * @see org.springframework.test.context.ContextLoader#processLocations(Class, String...) * @see #processContextConfiguration(ContextConfigurationAttributes) */ @Override public final String[] processLocations(Class clazz, String... locations) { return (ObjectUtils.isEmpty(locations) && isGenerateDefaultLocations()) ? generateDefaultLocations(clazz) : modifyLocations(clazz, locations); } /** * Generate the default classpath resource locations array based on the * supplied class. *

For example, if the supplied class is {@code com.example.MyTest}, * the generated locations will contain a single string with a value of * {@code "classpath:com/example/MyTest"}, where {@code } * is the value of the first configured * {@linkplain #getResourceSuffixes() resource suffix} for which the * generated location actually exists in the classpath. *

As of Spring 3.1, the implementation of this method adheres to the * contract defined in the {@link SmartContextLoader} SPI. Specifically, * this method will preemptively verify that the generated default * location actually exists. If it does not exist, this method will log a * warning and return an empty array. *

Subclasses can override this method to implement a different * default location generation strategy. * @param clazz the class for which the default locations are to be generated * @return an array of default application context resource locations * @since 2.5 * @see #getResourceSuffixes() */ protected String[] generateDefaultLocations(Class clazz) { Assert.notNull(clazz, "Class must not be null"); String[] suffixes = getResourceSuffixes(); for (String suffix : suffixes) { Assert.hasText(suffix, "Resource suffix must not be empty"); String resourcePath = ClassUtils.convertClassNameToResourcePath(clazz.getName()) + suffix; String prefixedResourcePath = ResourceUtils.CLASSPATH_URL_PREFIX + resourcePath; ClassPathResource classPathResource = new ClassPathResource(resourcePath); if (classPathResource.exists()) { if (logger.isInfoEnabled()) { logger.info(String.format("Detected default resource location \"%s\" for test class [%s]", prefixedResourcePath, clazz.getName())); } return new String[] {prefixedResourcePath}; } else if (logger.isDebugEnabled()) { logger.debug(String.format("Did not detect default resource location for test class [%s]: " + "%s does not exist", clazz.getName(), classPathResource)); } } if (logger.isInfoEnabled()) { logger.info(String.format("Could not detect default resource locations for test class [%s]: " + "no resource found for suffixes %s.", clazz.getName(), ObjectUtils.nullSafeToString(suffixes))); } return EMPTY_STRING_ARRAY; } /** * Generate a modified version of the supplied locations array and return it. *

The default implementation simply delegates to * {@link TestContextResourceUtils#convertToClasspathResourcePaths}. *

Subclasses can override this method to implement a different * location modification strategy. * @param clazz the class with which the locations are associated * @param locations the resource locations to be modified * @return an array of modified application context resource locations * @since 2.5 */ protected String[] modifyLocations(Class clazz, String... locations) { return TestContextResourceUtils.convertToClasspathResourcePaths(clazz, locations); } /** * Determine whether or not default resource locations should be * generated if the {@code locations} provided to * {@link #processLocations(Class, String...)} are {@code null} or empty. *

As of Spring 3.1, the semantics of this method have been overloaded * to include detection of either default resource locations or default * configuration classes. Consequently, this method can also be used to * determine whether or not default configuration classes should be * detected if the {@code classes} present in the * {@link ContextConfigurationAttributes configuration attributes} supplied * to {@link #processContextConfiguration(ContextConfigurationAttributes)} * are {@code null} or empty. *

Can be overridden by subclasses to change the default behavior. * @return always {@code true} by default * @since 2.5 */ protected boolean isGenerateDefaultLocations() { return true; } /** * Get the suffixes to append to {@link ApplicationContext} resource locations * when detecting default locations. *

The default implementation simply wraps the value returned by * {@link #getResourceSuffix()} in a single-element array, but this * can be overridden by subclasses in order to support multiple suffixes. * @return the resource suffixes; never {@code null} or empty * @since 4.1 * @see #generateDefaultLocations(Class) */ protected String[] getResourceSuffixes() { return new String[] {getResourceSuffix()}; } /** * Get the suffix to append to {@link ApplicationContext} resource locations * when detecting default locations. *

Subclasses must provide an implementation of this method that returns * a single suffix. Alternatively subclasses may provide a no-op * implementation of this method and override {@link #getResourceSuffixes()} * in order to provide multiple custom suffixes. * @return the resource suffix; never {@code null} or empty * @since 2.5 * @see #generateDefaultLocations(Class) * @see #getResourceSuffixes() */ protected abstract String getResourceSuffix(); }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy