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

This commit is contained in:
Sam Brannen
2011-06-02 14:45:22 +00:00
parent cee9da36eb
commit ac735d73ac
5 changed files with 506 additions and 30 deletions

View File

@@ -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();
}
}

View File

@@ -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 {
* 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);
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}