diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java b/org.springframework.test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java index ee062096fa..341e327936 100644 --- a/org.springframework.test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java +++ b/org.springframework.test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java @@ -16,7 +16,10 @@ package org.springframework.test.context.support; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationContext; +import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.support.ResourcePatternUtils; import org.springframework.test.context.ContextConfigurationAttributes; import org.springframework.test.context.ContextLoader; @@ -29,9 +32,16 @@ import org.springframework.util.StringUtils; /** * Abstract application context loader, which provides a basis for all concrete - * implementations of the {@link ContextLoader} strategy. Provides a + * implementations of the {@link ContextLoader} SPI. Provides a * Template Method based approach for {@link #processLocations processing} - * locations. + * resource locations. + * + *

As of Spring 3.1, AbstractContextLoader also provides a basis + * for all concrete implementations of the {@link SmartContextLoader} SPI. For + * backwards compatibility with the {@code ContextLoader} SPI, + * {@link #processContextConfiguration()} delegates to + * {@link #processLocations()}, and {@link #generatesDefaults()} delegates to + * {@link #isGenerateDefaultLocations()}. * * @author Sam Brannen * @author Juergen Hoeller @@ -41,14 +51,18 @@ import org.springframework.util.StringUtils; */ public abstract class AbstractContextLoader implements SmartContextLoader { + private static final Log logger = LogFactory.getLog(AbstractContextLoader.class); + + private static final String[] EMPTY_STRING_ARRAY = new String[] {}; private static final String SLASH = "/"; // --- SmartContextLoader ----------------------------------------------- /** - * TODO Document generatesDefaults() implementation. - * + * For backwards compatibility with the {@link ContextLoader} SPI, the + * default implementation simply delegates to + * {@link #isGenerateDefaultLocations()}. * @see org.springframework.test.context.SmartContextLoader#generatesDefaults() * @see #isGenerateDefaultLocations() */ @@ -57,14 +71,22 @@ public abstract class AbstractContextLoader implements SmartContextLoader { } /** - * TODO Document processContextConfiguration() implementation. - * - * @see #processLocations(Class, String...) + * For backwards compatibility with the {@link ContextLoader} SPI, the + * default implementation simply delegates to {@link #processLocations()}, + * 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 + * configuration classes instead of resource locations. + * @see #processLocations() */ public void processContextConfiguration(ContextConfigurationAttributes configAttributes) { String[] processedLocations = processLocations(configAttributes.getDeclaringClass(), configAttributes.getLocations()); - configAttributes.setLocations(processedLocations); } @@ -72,7 +94,7 @@ public abstract class AbstractContextLoader implements SmartContextLoader { /** * If the supplied locations are null or - * empty and {@link #isGenerateDefaultLocations()} is + * empty and {@link #isGenerateDefaultLocations()} returns * true, default locations will be * {@link #generateDefaultLocations(Class) generated} for the specified * {@link Class class} and the configured @@ -83,10 +105,12 @@ public abstract class AbstractContextLoader implements SmartContextLoader { * used when generating default locations * @param locations the unmodified locations to use for loading the * application context (can be null or empty) - * @return an array of application context resource locations - * @see #generateDefaultLocations - * @see #modifyLocations - * @see org.springframework.test.context.ContextLoader#processLocations + * @return a processed array of application context resource locations + * @see #isGenerateDefaultLocations() + * @see #generateDefaultLocations() + * @see #modifyLocations() + * @see org.springframework.test.context.ContextLoader#processLocations() + * @see #processContextConfiguration() */ public final String[] processLocations(Class clazz, String... locations) { return (ObjectUtils.isEmpty(locations) && isGenerateDefaultLocations()) ? generateDefaultLocations(clazz) @@ -101,6 +125,11 @@ public abstract class AbstractContextLoader implements SmartContextLoader { * "classpath:/com/example/MyTest<suffix>", * where <suffix> is the value of the * {@link #getResourceSuffix() resource suffix} string. + *

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 @@ -111,12 +140,17 @@ public abstract class AbstractContextLoader implements SmartContextLoader { Assert.notNull(clazz, "Class must not be null"); String suffix = getResourceSuffix(); Assert.hasText(suffix, "Resource suffix must not be empty"); + String resourcePath = SLASH + ClassUtils.convertClassNameToResourcePath(clazz.getName()) + suffix; + + if (!new ClassPathResource(resourcePath, clazz).exists()) { + logger.warn(String.format( + "Cannot generate default resource location for test class [%s]: classpath resource [%s] does not exist.", + clazz.getName(), resourcePath)); + return EMPTY_STRING_ARRAY; + } - // TODO Adhere to SmartContextLoader contract: verify existence of - // default and return an empty array if non-existent, in which case a - // warning should be logged as well. - return new String[] { ResourceUtils.CLASSPATH_URL_PREFIX + SLASH - + ClassUtils.convertClassNameToResourcePath(clazz.getName()) + suffix }; + // else + return new String[] { ResourceUtils.CLASSPATH_URL_PREFIX + resourcePath }; } /** @@ -157,8 +191,7 @@ public abstract class AbstractContextLoader implements SmartContextLoader { /** * Determine whether or not default resource locations should be * generated if the locations provided to - * {@link #processLocations(Class,String...) processLocations()} are - * null or empty. + * {@link #processLocations()} are null or empty. *

Can be overridden by subclasses to change the default behavior. * @return always true by default */ @@ -171,7 +204,7 @@ public abstract class AbstractContextLoader implements SmartContextLoader { * locations when generating default locations. *

Must be implemented by subclasses. * @return the resource suffix; should not be null or empty - * @see #generateDefaultLocations(Class) + * @see #generateDefaultLocations() */ protected abstract String getResourceSuffix(); diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/support/AnnotationConfigContextLoader.java b/org.springframework.test/src/main/java/org/springframework/test/context/support/AnnotationConfigContextLoader.java index d000b4f921..4943a35a23 100644 --- a/org.springframework.test/src/main/java/org/springframework/test/context/support/AnnotationConfigContextLoader.java +++ b/org.springframework.test/src/main/java/org/springframework/test/context/support/AnnotationConfigContextLoader.java @@ -63,7 +63,7 @@ public class AnnotationConfigContextLoader extends AbstractGenericContextLoader /** * TODO Document overridden processContextConfiguration(). * - * @see org.springframework.test.context.SmartContextLoader#processContextConfiguration + * @see org.springframework.test.context.SmartContextLoader#processContextConfiguration() * @see #generatesDefaults * @see #generateDefaultConfigurationClasses */ @@ -102,7 +102,20 @@ public class AnnotationConfigContextLoader extends AbstractGenericContextLoader } /** - * TODO Document generateDefaultConfigurationClasses(). + * TODO Complete JavaDoc for generateDefaultConfigurationClasses(). + * + *

The implementation of this method adheres to the contract defined in the + * {@link org.springframework.test.context.SmartContextLoader SmartContextLoader} + * SPI. Specifically, this method will preemptively verify that the + * generated default configuration classes exist and that such classes + * comply with the constraints required of {@link Configuration @Configuration} + * class implementations. If a candidate configuration class does meet these + * requirements, this method will log a warning and potentially return an empty + * array. + * + * @param declaringClass the test class that declared + * {@link org.springframework.test.context.ContextConfiguration @ContextConfiguration} + * @return */ protected Class[] generateDefaultConfigurationClasses(Class declaringClass) { Assert.notNull(declaringClass, "Declaring class must not be null"); diff --git a/org.springframework.test/src/test/java/org/springframework/test/context/ContextLoaderUtilsTests$BareAnnotations-context.xml b/org.springframework.test/src/test/java/org/springframework/test/context/ContextLoaderUtilsTests$BareAnnotations-context.xml new file mode 100644 index 0000000000..44d110480d --- /dev/null +++ b/org.springframework.test/src/test/java/org/springframework/test/context/ContextLoaderUtilsTests$BareAnnotations-context.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/org.springframework.test/src/test/java/org/springframework/test/context/support/GenericXmlContextLoaderResourceLocationsTests$1ClasspathExistentDefaultLocationsTestCase-context.xml b/org.springframework.test/src/test/java/org/springframework/test/context/support/GenericXmlContextLoaderResourceLocationsTests$1ClasspathExistentDefaultLocationsTestCase-context.xml new file mode 100644 index 0000000000..44d110480d --- /dev/null +++ b/org.springframework.test/src/test/java/org/springframework/test/context/support/GenericXmlContextLoaderResourceLocationsTests$1ClasspathExistentDefaultLocationsTestCase-context.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/org.springframework.test/src/test/java/org/springframework/test/context/support/GenericXmlContextLoaderResourceLocationsTests.java b/org.springframework.test/src/test/java/org/springframework/test/context/support/GenericXmlContextLoaderResourceLocationsTests.java index e54aa1e125..b7947f5dc0 100644 --- a/org.springframework.test/src/test/java/org/springframework/test/context/support/GenericXmlContextLoaderResourceLocationsTests.java +++ b/org.springframework.test/src/test/java/org/springframework/test/context/support/GenericXmlContextLoaderResourceLocationsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2011 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. @@ -27,7 +27,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; - import org.springframework.core.annotation.AnnotationUtils; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextLoader; @@ -63,35 +62,41 @@ public class GenericXmlContextLoaderResourceLocationsTests { @Parameters public static Collection contextConfigurationLocationsData() { @ContextConfiguration - class ClasspathDefaultLocationsTestCase { + class ClasspathNonExistentDefaultLocationsTestCase { + } + + @ContextConfiguration + class ClasspathExistentDefaultLocationsTestCase { } - - @ContextConfiguration(locations = { "context1.xml", "context2.xml" }) + + @ContextConfiguration({ "context1.xml", "context2.xml" }) class ImplicitClasspathLocationsTestCase { } - - @ContextConfiguration(locations = { "classpath:context.xml" }) + + @ContextConfiguration("classpath:context.xml") class ExplicitClasspathLocationsTestCase { } - - @ContextConfiguration(locations = { "file:/testing/directory/context.xml" }) + + @ContextConfiguration("file:/testing/directory/context.xml") class ExplicitFileLocationsTestCase { } - - @ContextConfiguration(locations = { "http://example.com/context.xml" }) + + @ContextConfiguration("http://example.com/context.xml") class ExplicitUrlLocationsTestCase { } - - @ContextConfiguration(locations = { "context1.xml", "classpath:context2.xml", "/context3.xml", + + @ContextConfiguration({ "context1.xml", "classpath:context2.xml", "/context3.xml", "file:/testing/directory/context.xml", "http://example.com/context.xml" }) class ExplicitMixedPathTypesLocationsTestCase { } return Arrays.asList(new Object[][] { + { ClasspathNonExistentDefaultLocationsTestCase.class, new String[] {} }, + { - ClasspathDefaultLocationsTestCase.class, - new String[] { "classpath:/org/springframework/test/context/support/GenericXmlContextLoaderResourceLocationsTests$1ClasspathDefaultLocationsTestCase-context.xml" } }, + ClasspathExistentDefaultLocationsTestCase.class, + new String[] { "classpath:/org/springframework/test/context/support/GenericXmlContextLoaderResourceLocationsTests$1ClasspathExistentDefaultLocationsTestCase-context.xml" } }, { ImplicitClasspathLocationsTestCase.class, @@ -118,7 +123,7 @@ public class GenericXmlContextLoaderResourceLocationsTests { final ContextConfiguration contextConfig = this.testClass.getAnnotation(ContextConfiguration.class); final ContextLoader contextLoader = new GenericXmlContextLoader(); - final String[] configuredLocations = (String[]) AnnotationUtils.getValue(contextConfig, "locations"); + final String[] configuredLocations = (String[]) AnnotationUtils.getValue(contextConfig); final String[] processedLocations = contextLoader.processLocations(this.testClass, configuredLocations); if (logger.isDebugEnabled()) { @@ -129,8 +134,7 @@ public class GenericXmlContextLoaderResourceLocationsTests { } assertArrayEquals("Verifying locations for test [" + this.testClass + "].", this.expectedLocations, - processedLocations); + processedLocations); } - }