Browse Source

[SPR-7960][SPR-8386] First draft of SmartContextLoader SPI, MergedContextConfiguration, and ContextConfigurationAttributes.

git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@4416 50f2f4bb-b051-0410-bef5-90022cba6387
pull/1/merge
Sam Brannen 15 years ago
parent
commit
4497a9ad8d
  1. 151
      org.springframework.test/src/main/java/org/springframework/test/context/ContextConfigurationAttributes.java
  2. 82
      org.springframework.test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java
  3. 110
      org.springframework.test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java
  4. 38
      org.springframework.test/src/main/java/org/springframework/test/context/SmartContextLoader.java
  5. 155
      org.springframework.test/src/test/java/org/springframework/test/context/ContextLoaderUtilsTests.java

151
org.springframework.test/src/main/java/org/springframework/test/context/ContextConfigurationAttributes.java

@ -0,0 +1,151 @@ @@ -0,0 +1,151 @@
/*
* 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.
* 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.springframework.test.context;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.ObjectUtils;
/**
* TODO [SPR-8386] Document ContextConfigurationAttributes.
*
* @author Sam Brannen
* @since 3.1
* @see ContextConfiguration
*/
public class ContextConfigurationAttributes {
private static final Log logger = LogFactory.getLog(ContextConfigurationAttributes.class);
private final Class<?> declaringClass;
private final String[] locations;
private final Class<?>[] classes;
private final boolean inheritLocations;
private final Class<? extends ContextLoader> contextLoader;
/**
* Resolves resource locations from the {@link ContextConfiguration#locations() locations}
* and {@link ContextConfiguration#value() value} attributes of the supplied
* {@link ContextConfiguration} annotation.
*
* @throws IllegalStateException if both the locations and value attributes have been declared
*/
static String[] resolveLocations(Class<?> declaringClass, ContextConfiguration contextConfiguration) {
String[] locations = contextConfiguration.locations();
String[] valueLocations = contextConfiguration.value();
if (!ObjectUtils.isEmpty(valueLocations) && !ObjectUtils.isEmpty(locations)) {
String msg = String.format("Test class [%s] has been configured with @ContextConfiguration's 'value' [%s] "
+ "and 'locations' [%s] attributes. Only one declaration of resource "
+ "locations is permitted per @ContextConfiguration annotation.", declaringClass,
ObjectUtils.nullSafeToString(valueLocations), ObjectUtils.nullSafeToString(locations));
logger.error(msg);
throw new IllegalStateException(msg);
}
else if (!ObjectUtils.isEmpty(valueLocations)) {
locations = valueLocations;
}
return locations;
}
/**
* TODO Document ContextConfigurationAttributes constructor.
*
* @param declaringClass
* @param contextConfiguration
*/
public ContextConfigurationAttributes(Class<?> declaringClass, ContextConfiguration contextConfiguration) {
this(declaringClass, resolveLocations(declaringClass, contextConfiguration), contextConfiguration.classes(),
contextConfiguration.inheritLocations(), contextConfiguration.loader());
}
/**
* TODO Document ContextConfigurationAttributes constructor.
*
* @param declaringClass
* @param locations
* @param classes
* @param inheritLocations
* @param contextLoader
*/
public ContextConfigurationAttributes(Class<?> declaringClass, String[] locations, Class<?>[] classes,
boolean inheritLocations, Class<? extends ContextLoader> contextLoader) {
this.declaringClass = declaringClass;
this.locations = locations;
this.classes = classes;
this.inheritLocations = inheritLocations;
this.contextLoader = contextLoader;
}
/**
* TODO Document getDeclaringClass().
*/
public Class<?> getDeclaringClass() {
return this.declaringClass;
}
/**
* TODO Document getLocations().
*/
public String[] getLocations() {
return this.locations;
}
/**
* TODO Document getClasses().
*/
public Class<?>[] getClasses() {
return this.classes;
}
/**
* TODO Document isInheritLocations().
*/
public boolean isInheritLocations() {
return this.inheritLocations;
}
/**
* TODO Document getContextLoader().
*/
public Class<? extends ContextLoader> getContextLoader() {
return this.contextLoader;
}
/**
* TODO Document overridden toString().
*/
@Override
public String toString() {
return new ToStringCreator(this)//
.append("declaringClass", this.declaringClass)//
.append("locations", ObjectUtils.nullSafeToString(this.locations))//
.append("classes", ObjectUtils.nullSafeToString(this.classes))//
.append("inheritLocations", this.inheritLocations)//
.append("contextLoader", this.contextLoader)//
.toString();
}
}

82
org.springframework.test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java

@ -281,6 +281,69 @@ abstract class ContextLoaderUtils { @@ -281,6 +281,69 @@ abstract class ContextLoaderUtils {
return StringUtils.toStringArray(activeProfiles);
}
/**
* TODO Document resolveContextConfigurationAttributes().
*
* @param clazz
* @return
*/
static List<ContextConfigurationAttributes> resolveContextConfigurationAttributes(Class<?> clazz) {
Assert.notNull(clazz, "Class must not be null");
final List<ContextConfigurationAttributes> attributesList = new ArrayList<ContextConfigurationAttributes>();
Class<ContextConfiguration> annotationType = ContextConfiguration.class;
Class<?> declaringClass = AnnotationUtils.findAnnotationDeclaringClass(annotationType, clazz);
Assert.notNull(declaringClass, String.format(
"Could not find an 'annotation declaring class' for annotation type [%s] and class [%s]", annotationType,
clazz));
while (declaringClass != null) {
ContextConfiguration contextConfiguration = declaringClass.getAnnotation(annotationType);
if (logger.isTraceEnabled()) {
logger.trace(String.format("Retrieved @ContextConfiguration [%s] for declaring class [%s].",
contextConfiguration, declaringClass));
}
ContextConfigurationAttributes attributes = new ContextConfigurationAttributes(declaringClass,
contextConfiguration);
if (logger.isTraceEnabled()) {
logger.trace("Resolved context configuration attributes: " + attributes);
}
attributesList.add(0, attributes);
declaringClass = contextConfiguration.inheritLocations() ? AnnotationUtils.findAnnotationDeclaringClass(
annotationType, declaringClass.getSuperclass()) : null;
}
return attributesList;
}
/**
* TODO Document buildMergedContextConfiguration().
*
* @param testClass
* @param defaultContextLoaderClassName
* @return
*/
static MergedContextConfiguration buildMergedContextConfiguration(Class<?> testClass,
String defaultContextLoaderClassName) {
ContextLoader contextLoader = resolveContextLoader(testClass, defaultContextLoaderClassName);
// TODO Merge locations from List<ContextConfigurationAttributes>
String[] locations = resolveContextLocations(contextLoader, testClass);
// TODO Merge classes from List<ContextConfigurationAttributes>
Class<?>[] classes = {};
String[] activeProfiles = resolveActiveProfiles(testClass);
return new MergedContextConfiguration(testClass, locations, classes, activeProfiles, contextLoader);
}
/**
* Strategy interface for resolving application context resource locations.
@ -317,24 +380,7 @@ abstract class ContextLoaderUtils { @@ -317,24 +380,7 @@ abstract class ContextLoaderUtils {
* attributes have been declared
*/
public String[] resolveLocations(ContextConfiguration contextConfiguration, Class<?> declaringClass) {
String[] locations = contextConfiguration.locations();
String[] valueLocations = contextConfiguration.value();
if (!ObjectUtils.isEmpty(valueLocations) && !ObjectUtils.isEmpty(locations)) {
String msg = String.format(
"Test class [%s] has been configured with @ContextConfiguration's 'value' [%s] "
+ "and 'locations' [%s] attributes. Only one declaration of resource "
+ "locations is permitted per @ContextConfiguration annotation.", declaringClass,
ObjectUtils.nullSafeToString(valueLocations), ObjectUtils.nullSafeToString(locations));
ContextLoaderUtils.logger.error(msg);
throw new IllegalStateException(msg);
}
else if (!ObjectUtils.isEmpty(valueLocations)) {
locations = valueLocations;
}
return locations;
return ContextConfigurationAttributes.resolveLocations(declaringClass, contextConfiguration);
}
}

110
org.springframework.test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java

@ -0,0 +1,110 @@ @@ -0,0 +1,110 @@
/*
* 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.
* 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.springframework.test.context;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.ObjectUtils;
/**
* TODO [SPR-8386] Document MergedContextConfiguration.
*
* @author Sam Brannen
* @since 3.1
* @see ContextConfiguration
* @see ActiveProfiles
*/
public class MergedContextConfiguration {
private final Class<?> testClass;
private final String[] locations;
private final Class<?>[] classes;
private final String[] activeProfiles;
private final ContextLoader contextLoader;
/**
* TODO Document MergedContextConfiguration constructor.
*
* @param testClass
* @param locations
* @param classes
* @param activeProfiles
* @param contextLoader
*/
public MergedContextConfiguration(Class<?> testClass, String[] locations, Class<?>[] classes,
String[] activeProfiles, ContextLoader contextLoader) {
this.testClass = testClass;
this.locations = locations;
this.classes = classes;
this.activeProfiles = activeProfiles;
this.contextLoader = contextLoader;
}
/**
* TODO Document getTestClass().
*/
public Class<?> getTestClass() {
return this.testClass;
}
/**
* TODO Document getLocations().
*/
public String[] getLocations() {
return this.locations;
}
/**
* TODO Document getClasses().
*/
public Class<?>[] getClasses() {
return this.classes;
}
/**
* TODO Document getActiveProfiles().
*/
public String[] getActiveProfiles() {
return this.activeProfiles;
}
/**
* TODO Document getContextLoader().
*/
public ContextLoader getContextLoader() {
return this.contextLoader;
}
/**
* TODO Document overridden toString().
*/
@Override
public String toString() {
return new ToStringCreator(this)//
.append("testClass", this.testClass)//
.append("locations", ObjectUtils.nullSafeToString(this.locations))//
.append("classes", ObjectUtils.nullSafeToString(this.classes))//
.append("activeProfiles", ObjectUtils.nullSafeToString(this.activeProfiles))//
.append("contextLoader", this.contextLoader)//
.toString();
}
}

38
org.springframework.test/src/main/java/org/springframework/test/context/SmartContextLoader.java

@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
/*
* 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.
* 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.springframework.test.context;
import org.springframework.context.ApplicationContext;
/**
* TODO [SPR-8386] Document SmartContextLoader.
*
* @author Sam Brannen
* @since 3.1
*/
public interface SmartContextLoader extends ContextLoader {
/**
* TODO Document loadContext().
*
* @param mergedContextConfiguration
* @return
* @throws Exception
*/
ApplicationContext loadContext(MergedContextConfiguration mergedContextConfiguration) throws Exception;
}

155
org.springframework.test/src/test/java/org/springframework/test/context/ContextLoaderUtilsTests.java

@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
package org.springframework.test.context;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@ -24,6 +25,10 @@ import java.util.Arrays; @@ -24,6 +25,10 @@ import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import org.springframework.test.context.support.GenericPropertiesContextLoader;
import org.springframework.test.context.support.GenericXmlContextLoader;
/**
* Unit tests for {@link ContextLoaderUtils}.
@ -33,25 +38,138 @@ import org.junit.Test; @@ -33,25 +38,138 @@ import org.junit.Test;
*/
public class ContextLoaderUtilsTests {
private static final String[] EMPTY_STRING_ARRAY = new String[] {};
private void assertAttributes(ContextConfigurationAttributes attributes, Class<?> expectedDeclaringClass,
String[] expectedLocations, Class<?>[] expectedClasses,
Class<? extends ContextLoader> expectedContextLoaderClass, boolean expectedInheritLocations) {
assertEquals(expectedDeclaringClass, attributes.getDeclaringClass());
assertArrayEquals(expectedLocations, attributes.getLocations());
assertArrayEquals(expectedClasses, attributes.getClasses());
assertEquals(expectedInheritLocations, attributes.isInheritLocations());
assertEquals(expectedContextLoaderClass, attributes.getContextLoader());
}
private void assertFooAttributes(ContextConfigurationAttributes attributes) {
assertAttributes(attributes, Foo.class, new String[] { "/foo.xml" }, new Class<?>[] { FooConfig.class },
ContextLoader.class, false);
}
private void assertBarAttributes(ContextConfigurationAttributes attributes) {
assertAttributes(attributes, Bar.class, new String[] { "/bar.xml" }, new Class<?>[] { BarConfig.class },
AnnotationConfigContextLoader.class, true);
}
private void assertMergedContextConfiguration(MergedContextConfiguration mergedConfig, Class<?> expectedTestClass,
String[] expectedLocations, Class<? extends ContextLoader> expectedContextLoaderClass) {
assertNotNull(mergedConfig);
assertEquals(expectedTestClass, mergedConfig.getTestClass());
assertNotNull(mergedConfig.getLocations());
assertArrayEquals(expectedLocations, mergedConfig.getLocations());
assertNotNull(mergedConfig.getClasses());
assertNotNull(mergedConfig.getActiveProfiles());
assertEquals(expectedContextLoaderClass, mergedConfig.getContextLoader().getClass());
}
@Test(expected = IllegalStateException.class)
public void resolveContextConfigurationAttributesWithConflictingLocations() {
ContextLoaderUtils.resolveContextConfigurationAttributes(ConflictingLocations.class);
}
@Test
public void resolveContextConfigurationAttributesWithBareAnnotations() {
List<ContextConfigurationAttributes> attributesList = ContextLoaderUtils.resolveContextConfigurationAttributes(BareAnnotations.class);
assertNotNull(attributesList);
assertEquals(1, attributesList.size());
assertAttributes(attributesList.get(0), BareAnnotations.class, EMPTY_STRING_ARRAY, new Class<?>[] {},
ContextLoader.class, true);
}
@Test
public void resolveContextConfigurationAttributesWithLocalAnnotation() {
List<ContextConfigurationAttributes> attributesList = ContextLoaderUtils.resolveContextConfigurationAttributes(Foo.class);
assertNotNull(attributesList);
assertEquals(1, attributesList.size());
assertFooAttributes(attributesList.get(0));
}
@Test
public void resolveContextConfigurationAttributesWithLocalAndInheritedAnnotations() {
List<ContextConfigurationAttributes> attributesList = ContextLoaderUtils.resolveContextConfigurationAttributes(Bar.class);
assertNotNull(attributesList);
assertEquals(2, attributesList.size());
assertFooAttributes(attributesList.get(0));
assertBarAttributes(attributesList.get(1));
}
@Test(expected = IllegalArgumentException.class)
public void buildMergedContextConfigurationWithoutAnnotation() {
ContextLoaderUtils.buildMergedContextConfiguration(Enigma.class, null);
}
@Test
public void buildMergedContextConfigurationWithBareAnnotations() {
Class<BareAnnotations> testClass = BareAnnotations.class;
MergedContextConfiguration mergedConfig = ContextLoaderUtils.buildMergedContextConfiguration(testClass, null);
assertMergedContextConfiguration(
mergedConfig,
testClass,
new String[] { "classpath:/org/springframework/test/context/ContextLoaderUtilsTests$BareAnnotations-context.xml" },
GenericXmlContextLoader.class);
}
@Test
public void buildMergedContextConfigurationWithLocalAnnotation() {
Class<?> testClass = Foo.class;
MergedContextConfiguration mergedConfig = ContextLoaderUtils.buildMergedContextConfiguration(testClass, null);
assertMergedContextConfiguration(mergedConfig, testClass, new String[] { "classpath:/foo.xml" },
GenericXmlContextLoader.class);
}
@Test
public void buildMergedContextConfigurationWithLocalAnnotationAndOverriddenContexLoader() {
Class<?> testClass = Foo.class;
Class<? extends ContextLoader> expectedContextLoaderClass = GenericPropertiesContextLoader.class;
MergedContextConfiguration mergedConfig = ContextLoaderUtils.buildMergedContextConfiguration(testClass,
expectedContextLoaderClass.getName());
assertMergedContextConfiguration(mergedConfig, testClass, new String[] { "classpath:/foo.xml" },
expectedContextLoaderClass);
}
@Test
public void buildMergedContextConfigurationWithLocalAndInheritedAnnotations() {
Class<?> testClass = Bar.class;
MergedContextConfiguration mergedConfig = ContextLoaderUtils.buildMergedContextConfiguration(testClass, null);
// TODO Assert @Configuration classes instead of locations
String[] expectedLocations = new String[] {
"org.springframework.test.context.ContextLoaderUtilsTests$FooConfig",
"org.springframework.test.context.ContextLoaderUtilsTests$BarConfig" };
assertMergedContextConfiguration(mergedConfig, testClass, expectedLocations,
AnnotationConfigContextLoader.class);
}
@Test
public void resolveActiveProfilesWithoutAnnotation() {
String[] profiles = ContextLoaderUtils.resolveActiveProfiles(Enigma.class);
assertNotNull(profiles);
assertEquals(0, profiles.length);
assertArrayEquals(EMPTY_STRING_ARRAY, profiles);
}
@Test
public void resolveActiveProfilesWithNoProfilesDeclared() {
String[] profiles = ContextLoaderUtils.resolveActiveProfiles(NoProfilesDeclared.class);
assertNotNull(profiles);
assertEquals(0, profiles.length);
String[] profiles = ContextLoaderUtils.resolveActiveProfiles(BareAnnotations.class);
assertArrayEquals(EMPTY_STRING_ARRAY, profiles);
}
@Test
public void resolveActiveProfilesWithEmptyProfiles() {
String[] profiles = ContextLoaderUtils.resolveActiveProfiles(EmptyProfiles.class);
assertNotNull(profiles);
assertEquals(0, profiles.length);
assertArrayEquals(EMPTY_STRING_ARRAY, profiles);
}
@Test
@ -70,16 +188,14 @@ public class ContextLoaderUtilsTests { @@ -70,16 +188,14 @@ public class ContextLoaderUtilsTests {
public void resolveActiveProfilesWithLocalAnnotation() {
String[] profiles = ContextLoaderUtils.resolveActiveProfiles(Foo.class);
assertNotNull(profiles);
assertEquals(1, profiles.length);
assertEquals("foo", profiles[0]);
assertArrayEquals(new String[] { "foo" }, profiles);
}
@Test
public void resolveActiveProfilesWithInheritedAnnotation() {
String[] profiles = ContextLoaderUtils.resolveActiveProfiles(InheritedFoo.class);
assertNotNull(profiles);
assertEquals(1, profiles.length);
assertEquals("foo", profiles[0]);
assertArrayEquals(new String[] { "foo" }, profiles);
}
@Test
@ -108,8 +224,13 @@ public class ContextLoaderUtilsTests { @@ -108,8 +224,13 @@ public class ContextLoaderUtilsTests {
private static class Enigma {
}
@ContextConfiguration(value = "x", locations = "y")
private static class ConflictingLocations {
}
@ContextConfiguration
@ActiveProfiles
private static class NoProfilesDeclared {
private static class BareAnnotations {
}
@ActiveProfiles({ " ", "\t" })
@ -120,6 +241,11 @@ public class ContextLoaderUtilsTests { @@ -120,6 +241,11 @@ public class ContextLoaderUtilsTests {
private static class DuplicatedProfiles {
}
@Configuration
private static class FooConfig {
}
@ContextConfiguration(locations = "/foo.xml", classes = FooConfig.class, inheritLocations = false)
@ActiveProfiles(profiles = "foo")
private static class Foo {
}
@ -127,6 +253,11 @@ public class ContextLoaderUtilsTests { @@ -127,6 +253,11 @@ public class ContextLoaderUtilsTests {
private static class InheritedFoo extends Foo {
}
@Configuration
private static class BarConfig {
}
@ContextConfiguration(locations = "/bar.xml", classes = BarConfig.class, inheritLocations = true, loader = AnnotationConfigContextLoader.class)
@ActiveProfiles("bar")
private static class Bar extends Foo {
}

Loading…
Cancel
Save