diff --git a/spring-test-mvc/src/test/java/org/springframework/test/web/mock/servlet/samples/context/GenericWebContextLoader.java b/spring-test-mvc/src/test/java/org/springframework/test/web/mock/servlet/samples/context/GenericWebContextLoader.java deleted file mode 100644 index 55c477ccc342..000000000000 --- a/spring-test-mvc/src/test/java/org/springframework/test/web/mock/servlet/samples/context/GenericWebContextLoader.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2002-2012 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.web.mock.servlet.samples.context; - -import javax.servlet.RequestDispatcher; - -import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.AnnotatedBeanDefinitionReader; -import org.springframework.context.annotation.AnnotationConfigUtils; -import org.springframework.core.io.DefaultResourceLoader; -import org.springframework.core.io.FileSystemResourceLoader; -import org.springframework.core.io.ResourceLoader; -import org.springframework.mock.web.MockRequestDispatcher; -import org.springframework.mock.web.MockServletContext; -import org.springframework.test.context.MergedContextConfiguration; -import org.springframework.test.context.support.AbstractContextLoader; -import org.springframework.web.context.WebApplicationContext; -import org.springframework.web.context.support.GenericWebApplicationContext; - -/** - * This class is here temporarily until the TestContext framework provides - * support for WebApplicationContext yet: - * - * https://jira.springsource.org/browse/SPR-5243 - * - *
After that this class will no longer be needed. It's provided here as an example - * and to serve as a temporary solution. - */ -public class GenericWebContextLoader extends AbstractContextLoader { - protected final MockServletContext servletContext; - - public GenericWebContextLoader(String warRootDir, boolean isClasspathRelative) { - ResourceLoader resourceLoader = isClasspathRelative ? new DefaultResourceLoader() : new FileSystemResourceLoader(); - this.servletContext = initServletContext(warRootDir, resourceLoader); - } - - private MockServletContext initServletContext(String warRootDir, ResourceLoader resourceLoader) { - return new MockServletContext(warRootDir, resourceLoader) { - // Required for DefaultServletHttpRequestHandler... - public RequestDispatcher getNamedDispatcher(String path) { - return (path.equals("default")) ? new MockRequestDispatcher(path) : super.getNamedDispatcher(path); - } - }; - } - - public ApplicationContext loadContext(MergedContextConfiguration mergedConfig) throws Exception { - GenericWebApplicationContext context = new GenericWebApplicationContext(); - context.getEnvironment().setActiveProfiles(mergedConfig.getActiveProfiles()); - prepareContext(context); - loadBeanDefinitions(context, mergedConfig); - return context; - } - - public ApplicationContext loadContext(String... locations) throws Exception { - // should never be called - throw new UnsupportedOperationException(); - } - - protected void prepareContext(GenericWebApplicationContext context) { - this.servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context); - context.setServletContext(this.servletContext); - } - - protected void loadBeanDefinitions(GenericWebApplicationContext context, String[] locations) { - new XmlBeanDefinitionReader(context).loadBeanDefinitions(locations); - AnnotationConfigUtils.registerAnnotationConfigProcessors(context); - context.refresh(); - context.registerShutdownHook(); - } - - protected void loadBeanDefinitions(GenericWebApplicationContext context, MergedContextConfiguration mergedConfig) { - new AnnotatedBeanDefinitionReader(context).register(mergedConfig.getClasses()); - loadBeanDefinitions(context, mergedConfig.getLocations()); - } - - @Override - protected String getResourceSuffix() { - return "-context.xml"; - } -} diff --git a/spring-test-mvc/src/test/java/org/springframework/test/web/mock/servlet/samples/context/JavaTestContextTests.java b/spring-test-mvc/src/test/java/org/springframework/test/web/mock/servlet/samples/context/JavaTestContextTests.java index 722acba41e71..dbb5ff73aebd 100644 --- a/spring-test-mvc/src/test/java/org/springframework/test/web/mock/servlet/samples/context/JavaTestContextTests.java +++ b/spring-test-mvc/src/test/java/org/springframework/test/web/mock/servlet/samples/context/JavaTestContextTests.java @@ -16,9 +16,8 @@ package org.springframework.test.web.mock.servlet.samples.context; -import static org.springframework.test.web.mock.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.mock.servlet.result.MockMvcResultMatchers.forwardedUrl; -import static org.springframework.test.web.mock.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.mock.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.mock.servlet.result.MockMvcResultMatchers.*; import org.junit.Before; import org.junit.Test; @@ -26,21 +25,20 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.mock.servlet.MockMvc; import org.springframework.test.web.mock.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.ContextLoader; import org.springframework.web.context.WebApplicationContext; /** * Tests with Java configuration. * - * The TestContext framework doesn't support WebApplicationContext yet: - * https://jira.springsource.org/browse/SPR-5243 - * - * A custom {@link ContextLoader} is used to load the WebApplicationContext. + * @author Rossen Stoyanchev + * @author Sam Brannen */ @RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(loader=WebContextLoader.class, classes={WebConfig.class}) +@WebAppConfiguration("src/test/resources/META-INF/web-resources") +@ContextConfiguration(classes = WebConfig.class) public class JavaTestContextTests { @Autowired @@ -48,6 +46,7 @@ public class JavaTestContextTests { private MockMvc mockMvc; + @Before public void setup() { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); @@ -55,9 +54,9 @@ public void setup() { @Test public void tilesDefinitions() throws Exception { - this.mockMvc.perform(get("/")) - .andExpect(status().isOk()) - .andExpect(forwardedUrl("/WEB-INF/layouts/standardLayout.jsp")); + this.mockMvc.perform(get("/"))// + .andExpect(status().isOk())// + .andExpect(forwardedUrl("/WEB-INF/layouts/standardLayout.jsp")); } } diff --git a/spring-test-mvc/src/test/java/org/springframework/test/web/mock/servlet/samples/context/SpringSecurityTests.java b/spring-test-mvc/src/test/java/org/springframework/test/web/mock/servlet/samples/context/SpringSecurityTests.java index e24bdba57da6..b1da11b99ffd 100644 --- a/spring-test-mvc/src/test/java/org/springframework/test/web/mock/servlet/samples/context/SpringSecurityTests.java +++ b/spring-test-mvc/src/test/java/org/springframework/test/web/mock/servlet/samples/context/SpringSecurityTests.java @@ -10,6 +10,7 @@ * 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.web.mock.servlet.samples.context; import static org.springframework.test.web.mock.servlet.request.MockMvcRequestBuilders.get; @@ -34,6 +35,7 @@ import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.mock.servlet.MockMvc; import org.springframework.test.web.mock.servlet.MvcResult; import org.springframework.test.web.mock.servlet.ResultMatcher; @@ -44,32 +46,25 @@ /** * Basic example that includes Spring Security configuration. * - *
Note that currently there are no {@link ResultMatcher}' built specifically - * for asserting the Spring Security context. However, it's quite easy to put - * them together as shown below and Spring Security extensions will become - * available in the near future. + *
Note that currently there are no {@linkplain ResultMatcher ResultMatchers} + * built specifically for asserting the Spring Security context. However, it's + * quite easy to put them together as shown below, and Spring Security extensions + * will become available in the near future. * *
This also demonstrates a custom {@link RequestPostProcessor} which authenticates * a user to a particular {@link HttpServletRequest}. * - *
Also see the Javadoc of {@link GenericWebContextLoader}, a class that
- * provides temporary support for loading WebApplicationContext by extending
- * the TestContext framework.
- *
* @author Rob Winch
* @author Rossen Stoyanchev
+ * @author Sam Brannen
* @see SecurityRequestPostProcessors
*/
@RunWith(SpringJUnit4ClassRunner.class)
-@ContextConfiguration(
- loader=WebContextLoader.class,
- value={
- "classpath:org/springframework/test/web/mock/servlet/samples/context/security.xml",
- "classpath:org/springframework/test/web/mock/servlet/samples/servlet-context.xml"
- })
+@WebAppConfiguration("src/test/resources/META-INF/web-resources")
+@ContextConfiguration({ "security.xml", "../servlet-context.xml" })
public class SpringSecurityTests {
- private static String SEC_CONTEXT_ATTR = HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY;
+ private static final String SEC_CONTEXT_ATTR = HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY;
@Autowired
private FilterChainProxy springSecurityFilterChain;
@@ -79,57 +74,67 @@ public class SpringSecurityTests {
private MockMvc mockMvc;
+
@Before
public void setup() {
- this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
- .addFilters(this.springSecurityFilterChain).build();
+ this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)//
+ .addFilters(this.springSecurityFilterChain)//
+ .build();
}
@Test
public void requiresAuthentication() throws Exception {
- mockMvc.perform(get("/user"))
- .andExpect(redirectedUrl("http://localhost/spring_security_login"));
+ mockMvc.perform(get("/user")).//
+ andExpect(redirectedUrl("http://localhost/spring_security_login"));
}
@Test
public void accessGranted() throws Exception {
- this.mockMvc.perform(get("/").with(userDeatilsService("user")))
- .andExpect(status().isOk())
- .andExpect(forwardedUrl("/WEB-INF/layouts/standardLayout.jsp"));
+ this.mockMvc.perform(get("/").//
+ with(userDeatilsService("user"))).//
+ andExpect(status().isOk()).//
+ andExpect(forwardedUrl("/WEB-INF/layouts/standardLayout.jsp"));
}
@Test
public void accessDenied() throws Exception {
- this.mockMvc.perform(get("/").with(user("user").roles("DENIED")))
- .andExpect(status().isForbidden());
+ this.mockMvc.perform(get("/")//
+ .with(user("user").roles("DENIED")))//
+ .andExpect(status().isForbidden());
}
@Test
public void userAuthenticates() throws Exception {
final String username = "user";
- mockMvc.perform(post("/j_spring_security_check").param("j_username", username).param("j_password", "password"))
- .andExpect(redirectedUrl("/"))
- .andExpect(new ResultMatcher() {
- public void match(MvcResult mvcResult) throws Exception {
- HttpSession session = mvcResult.getRequest().getSession();
- SecurityContext securityContext = (SecurityContext) session.getAttribute(SEC_CONTEXT_ATTR);
- Assert.assertEquals(securityContext.getAuthentication().getName(), username);
- }
- });
+ mockMvc.perform(post("/j_spring_security_check").//
+ param("j_username", username).//
+ param("j_password", "password")).//
+ andExpect(redirectedUrl("/")).//
+ andExpect(new ResultMatcher() {
+
+ public void match(MvcResult mvcResult) throws Exception {
+ HttpSession session = mvcResult.getRequest().getSession();
+ SecurityContext securityContext = (SecurityContext) session.getAttribute(SEC_CONTEXT_ATTR);
+ Assert.assertEquals(securityContext.getAuthentication().getName(), username);
+ }
+ });
}
@Test
public void userAuthenticateFails() throws Exception {
final String username = "user";
- mockMvc.perform(post("/j_spring_security_check").param("j_username", username).param("j_password", "invalid"))
- .andExpect(redirectedUrl("/spring_security_login?login_error"))
- .andExpect(new ResultMatcher() {
- public void match(MvcResult mvcResult) throws Exception {
- HttpSession session = mvcResult.getRequest().getSession();
- SecurityContext securityContext = (SecurityContext) session.getAttribute(SEC_CONTEXT_ATTR);
- Assert.assertNull(securityContext);
- }
- });
+ mockMvc.perform(post("/j_spring_security_check").//
+ param("j_username", username).//
+ param("j_password", "invalid")).//
+ andExpect(redirectedUrl("/spring_security_login?login_error")).//
+ andExpect(new ResultMatcher() {
+
+ public void match(MvcResult mvcResult) throws Exception {
+ HttpSession session = mvcResult.getRequest().getSession();
+ SecurityContext securityContext = (SecurityContext) session.getAttribute(SEC_CONTEXT_ATTR);
+ Assert.assertNull(securityContext);
+ }
+ });
}
}
diff --git a/spring-test-mvc/src/test/java/org/springframework/test/web/mock/servlet/samples/context/XmlTestContextTests.java b/spring-test-mvc/src/test/java/org/springframework/test/web/mock/servlet/samples/context/XmlTestContextTests.java
index 2edc313d033d..3274c6137d4d 100644
--- a/spring-test-mvc/src/test/java/org/springframework/test/web/mock/servlet/samples/context/XmlTestContextTests.java
+++ b/spring-test-mvc/src/test/java/org/springframework/test/web/mock/servlet/samples/context/XmlTestContextTests.java
@@ -16,9 +16,8 @@
package org.springframework.test.web.mock.servlet.samples.context;
-import static org.springframework.test.web.mock.servlet.request.MockMvcRequestBuilders.get;
-import static org.springframework.test.web.mock.servlet.result.MockMvcResultMatchers.forwardedUrl;
-import static org.springframework.test.web.mock.servlet.result.MockMvcResultMatchers.status;
+import static org.springframework.test.web.mock.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.mock.servlet.result.MockMvcResultMatchers.*;
import org.junit.Before;
import org.junit.Test;
@@ -26,23 +25,20 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.mock.servlet.MockMvc;
import org.springframework.test.web.mock.servlet.setup.MockMvcBuilders;
-import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
/**
* Tests with XML configuration.
*
- * The TestContext framework doesn't support WebApplicationContext yet:
- * https://jira.springsource.org/browse/SPR-5243
- *
- * A custom {@link ContextLoader} is used to load the WebApplicationContext.
+ * @author Rossen Stoyanchev
+ * @author Sam Brannen
*/
@RunWith(SpringJUnit4ClassRunner.class)
-@ContextConfiguration(
- loader=WebContextLoader.class,
- locations={"/org/springframework/test/web/mock/servlet/samples/servlet-context.xml"})
+@WebAppConfiguration("src/test/resources/META-INF/web-resources")
+@ContextConfiguration("../servlet-context.xml")
public class XmlTestContextTests {
@Autowired
@@ -50,6 +46,7 @@ public class XmlTestContextTests {
private MockMvc mockMvc;
+
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
@@ -57,10 +54,9 @@ public void setup() {
@Test
public void tilesDefinitions() throws Exception {
- this.mockMvc.perform(get("/"))
- .andExpect(status().isOk())
- .andExpect(forwardedUrl("/WEB-INF/layouts/standardLayout.jsp"));
+ this.mockMvc.perform(get("/"))//
+ .andExpect(status().isOk())//
+ .andExpect(forwardedUrl("/WEB-INF/layouts/standardLayout.jsp"));
}
}
-
diff --git a/spring-test/.springBeans b/spring-test/.springBeans
index 7d64f21cfb8a..a4a7e241718f 100644
--- a/spring-test/.springBeans
+++ b/spring-test/.springBeans
@@ -9,6 +9,7 @@
The default implementation:
+ * Any {@code ApplicationContextInitializers} implementing
+ * {@link org.springframework.core.Ordered Ordered} or marked 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
+ * @see ApplicationContextInitializer#initialize(ConfigurableApplicationContext)
+ * @see #loadContext(MergedContextConfiguration)
+ * @see ConfigurableApplicationContext#setId
+ * @since 3.2
+ */
+ @SuppressWarnings("unchecked")
+ protected void prepareContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
+
+ context.getEnvironment().setActiveProfiles(mergedConfig.getActiveProfiles());
+
+ Set Placing an empty {@code @ContextConfiguration} annotation on a test class signals
+ * that default resource locations (i.e., XML configuration files) or default
+ * {@link org.springframework.context.annotation.Configuration configuration classes}
+ * should be detected. Furthermore, if a specific {@link ContextLoader} or
+ * {@link SmartContextLoader} is not explicitly declared via
+ * {@code @ContextConfiguration}, a concrete subclass of
+ * {@code AbstractDelegatingSmartContextLoader} will be used as the default loader,
+ * thus providing automatic support for either XML configuration files or annotated
+ * classes, but not both simultaneously.
+ *
+ * As of Spring 3.2, a test class may optionally declare neither XML configuration
+ * files nor annotated classes and instead declare only {@linkplain
+ * ContextConfiguration#initializers application context initializers}. In such
+ * cases, an attempt will still be made to detect defaults, but their absence will
+ * not result an an exception.
+ *
+ * @author Sam Brannen
+ * @since 3.2
+ * @see SmartContextLoader
+ */
+abstract class AbstractDelegatingSmartContextLoader implements SmartContextLoader {
+
+ private static final Log logger = LogFactory.getLog(AbstractDelegatingSmartContextLoader.class);
+
+
+ /**
+ * Get the delegate {@code SmartContextLoader} that supports XML configuration files.
+ */
+ protected abstract SmartContextLoader getXmlLoader();
+
+ /**
+ * Get the delegate {@code SmartContextLoader} that supports annotated classes.
+ */
+ protected abstract SmartContextLoader getAnnotationConfigLoader();
+
+ // --- SmartContextLoader --------------------------------------------------
+
+ private static String name(SmartContextLoader loader) {
+ return loader.getClass().getSimpleName();
+ }
+
+ private static void delegateProcessing(SmartContextLoader loader, ContextConfigurationAttributes configAttributes) {
+ if (logger.isDebugEnabled()) {
+ logger.debug(String.format("Delegating to %s to process context configuration %s.", name(loader),
+ configAttributes));
+ }
+ loader.processContextConfiguration(configAttributes);
+ }
+
+ private static ApplicationContext delegateLoading(SmartContextLoader loader, MergedContextConfiguration mergedConfig)
+ throws Exception {
+ if (logger.isDebugEnabled()) {
+ logger.debug(String.format("Delegating to %s to load context from %s.", name(loader), mergedConfig));
+ }
+ return loader.loadContext(mergedConfig);
+ }
+
+ private boolean supports(SmartContextLoader loader, MergedContextConfiguration mergedConfig) {
+ if (loader == getAnnotationConfigLoader()) {
+ return ObjectUtils.isEmpty(mergedConfig.getLocations()) && !ObjectUtils.isEmpty(mergedConfig.getClasses());
+ } else {
+ return !ObjectUtils.isEmpty(mergedConfig.getLocations()) && ObjectUtils.isEmpty(mergedConfig.getClasses());
+ }
+ }
+
+ /**
+ * Delegates to candidate {@code SmartContextLoaders} to process the supplied
+ * {@link ContextConfigurationAttributes}.
+ *
+ * Delegation is based on explicit knowledge of the implementations of the
+ * default loaders for {@link #getXmlLoader() XML configuration files} and
+ * {@link #getAnnotationConfigLoader() annotated classes}. Specifically, the
+ * delegation algorithm is as follows:
+ *
+ * Delegation is based on explicit knowledge of the implementations of the
+ * default loaders for {@link #getXmlLoader() XML configuration files} and
+ * {@link #getAnnotationConfigLoader() annotated classes}. Specifically, the
+ * delegation algorithm is as follows:
+ *
+ * The default implementation:
- * Any {@code ApplicationContextInitializers} implementing
- * {@link org.springframework.core.Ordered Ordered} or marked with {@link
- * org.springframework.core.annotation.Order @Order} will be sorted appropriately.
- *
- * @param applicationContext the newly created application context
- * @param mergedConfig the merged context configuration
- * @see ApplicationContextInitializer#initialize(GenericApplicationContext)
- * @see #loadContext(MergedContextConfiguration)
- * @see GenericApplicationContext#setAllowBeanDefinitionOverriding
- * @see GenericApplicationContext#setResourceLoader
- * @see GenericApplicationContext#setId
- * @since 3.2
- */
- @SuppressWarnings("unchecked")
- protected void prepareContext(GenericApplicationContext applicationContext,
- MergedContextConfiguration mergedConfig) {
-
- prepareContext(applicationContext);
-
- applicationContext.getEnvironment().setActiveProfiles(mergedConfig.getActiveProfiles());
-
- Set Specifically, such candidates:
- *
- * The returned class array will contain all static inner classes of
- * the supplied class that meet the requirements for {@code @Configuration}
- * class implementations as specified in the documentation for
- * {@link Configuration @Configuration}.
+ * The default implementation simply delegates to
+ * {@link AnnotationConfigContextLoaderUtils#detectDefaultConfigurationClasses(Class)}.
*
- * The implementation of this method adheres to the contract defined in the
- * {@link org.springframework.test.context.SmartContextLoader SmartContextLoader}
- * SPI. Specifically, this method uses introspection to detect default
- * configuration classes that comply with the constraints required of
- * {@code @Configuration} class implementations. If a potential candidate
- * configuration class does not meet these requirements, this method will log a
- * warning, and the potential candidate class will be ignored.
* @param declaringClass the test class that declared {@code @ContextConfiguration}
* @return an array of default configuration classes, potentially empty but
* never Specifically, such candidates:
+ *
+ * The returned class array will contain all static inner classes of
+ * the supplied class that meet the requirements for {@code @Configuration}
+ * class implementations as specified in the documentation for
+ * {@link Configuration @Configuration}.
+ *
+ * The implementation of this method adheres to the contract defined in the
+ * {@link org.springframework.test.context.SmartContextLoader SmartContextLoader}
+ * SPI. Specifically, this method uses introspection to detect default
+ * configuration classes that comply with the constraints required of
+ * {@code @Configuration} class implementations. If a potential candidate
+ * configuration class does not meet these requirements, this method will log a
+ * warning, and the potential candidate class will be ignored.
+ * @param declaringClass the test class that declared {@code @ContextConfiguration}
+ * @return an array of default configuration classes, potentially empty but
+ * never If the annotated classes are The default implementation simply delegates to
+ * {@link AnnotationConfigContextLoaderUtils#detectDefaultConfigurationClasses(Class)}.
+ *
+ * @param declaringClass the test class that declared {@code @ContextConfiguration}
+ * @return an array of default configuration classes, potentially empty but
+ * never Each class must represent an annotated class. An
+ * {@link AnnotatedBeanDefinitionReader} is used to register the appropriate
+ * bean definitions.
+ *
+ * @param context the context in which the annotated classes should be registered
+ * @param webMergedConfig the merged configuration from which the classes should be retrieved
+ *
+ * @see AbstractGenericWebContextLoader#loadBeanDefinitions
+ */
+ @Override
+ protected void loadBeanDefinitions(GenericWebApplicationContext context,
+ WebMergedContextConfiguration webMergedConfig) {
+ Class>[] annotatedClasses = webMergedConfig.getClasses();
+ if (logger.isDebugEnabled()) {
+ logger.debug("Registering annotated classes: " + ObjectUtils.nullSafeToString(annotatedClasses));
+ }
+ new AnnotatedBeanDefinitionReader(context).register(annotatedClasses);
+ }
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/DelegatingSmartContextLoader.java b/spring-test/src/main/java/org/springframework/test/context/support/DelegatingSmartContextLoader.java
index 61408d4eeaf5..4d2d7c324aef 100644
--- a/spring-test/src/main/java/org/springframework/test/context/support/DelegatingSmartContextLoader.java
+++ b/spring-test/src/main/java/org/springframework/test/context/support/DelegatingSmartContextLoader.java
@@ -16,263 +16,32 @@
package org.springframework.test.context.support;
-import java.util.Arrays;
-import java.util.List;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import org.springframework.context.ApplicationContext;
-import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.ContextConfigurationAttributes;
-import org.springframework.test.context.ContextLoader;
-import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.SmartContextLoader;
-import org.springframework.util.Assert;
-import org.springframework.util.ObjectUtils;
/**
- * {@code DelegatingSmartContextLoader} is an implementation of the {@link SmartContextLoader}
- * SPI that delegates to a set of candidate SmartContextLoaders (i.e.,
- * {@link GenericXmlContextLoader} and {@link AnnotationConfigContextLoader}) to
- * determine which context loader is appropriate for a given test class's configuration.
- * Each candidate is given a chance to {@link #processContextConfiguration process} the
- * {@link ContextConfigurationAttributes} for each class in the test class hierarchy that
- * is annotated with {@link ContextConfiguration @ContextConfiguration}, and the candidate
- * that supports the merged, processed configuration will be used to actually
- * {@link #loadContext load} the context.
- *
- * Placing an empty {@code @ContextConfiguration} annotation on a test class signals
- * that default resource locations (i.e., XML configuration files) or default
- * {@link org.springframework.context.annotation.Configuration configuration classes}
- * should be detected. Furthermore, if a specific {@link ContextLoader} or
- * {@link SmartContextLoader} is not explicitly declared via
- * {@code @ContextConfiguration}, {@code DelegatingSmartContextLoader} will be used as
- * the default loader, thus providing automatic support for either XML configuration
- * files or annotated classes, but not both simultaneously.
- *
- * As of Spring 3.2, a test class may optionally declare neither XML configuration
- * files nor annotated classes and instead declare only {@linkplain
- * ContextConfiguration#initializers application context initializers}. In such
- * cases, an attempt will still be made to detect defaults, but their absence will
- * not result an an exception.
+ * {@code DelegatingSmartContextLoader} is a concrete implementation of
+ * {@link AbstractDelegatingSmartContextLoader} that delegates to a
+ * {@link GenericXmlContextLoader} and an {@link AnnotationConfigContextLoader}.
*
* @author Sam Brannen
* @since 3.1
* @see SmartContextLoader
+ * @see AbstractDelegatingSmartContextLoader
* @see GenericXmlContextLoader
* @see AnnotationConfigContextLoader
*/
-public class DelegatingSmartContextLoader implements SmartContextLoader {
-
- private static final Log logger = LogFactory.getLog(DelegatingSmartContextLoader.class);
+public class DelegatingSmartContextLoader extends AbstractDelegatingSmartContextLoader {
private final SmartContextLoader xmlLoader = new GenericXmlContextLoader();
private final SmartContextLoader annotationConfigLoader = new AnnotationConfigContextLoader();
- // --- SmartContextLoader --------------------------------------------------
-
- private static String name(SmartContextLoader loader) {
- return loader.getClass().getSimpleName();
- }
-
- private static void delegateProcessing(SmartContextLoader loader, ContextConfigurationAttributes configAttributes) {
- if (logger.isDebugEnabled()) {
- logger.debug(String.format("Delegating to %s to process context configuration %s.", name(loader),
- configAttributes));
- }
- loader.processContextConfiguration(configAttributes);
- }
-
- private static ApplicationContext delegateLoading(SmartContextLoader loader, MergedContextConfiguration mergedConfig)
- throws Exception {
- if (logger.isDebugEnabled()) {
- logger.debug(String.format("Delegating to %s to load context from %s.", name(loader), mergedConfig));
- }
- return loader.loadContext(mergedConfig);
- }
-
- private static boolean supports(SmartContextLoader loader, MergedContextConfiguration mergedConfig) {
- if (loader instanceof AnnotationConfigContextLoader) {
- return ObjectUtils.isEmpty(mergedConfig.getLocations()) && !ObjectUtils.isEmpty(mergedConfig.getClasses());
- } else {
- return !ObjectUtils.isEmpty(mergedConfig.getLocations()) && ObjectUtils.isEmpty(mergedConfig.getClasses());
- }
- }
-
- /**
- * Delegates to candidate {@code SmartContextLoaders} to process the supplied
- * {@link ContextConfigurationAttributes}.
- *
- * Delegation is based on explicit knowledge of the implementations of
- * {@link GenericXmlContextLoader} and {@link AnnotationConfigContextLoader}.
- * Specifically, the delegation algorithm is as follows:
- *
- * Delegation is based on explicit knowledge of the implementations of
- * {@link GenericXmlContextLoader} and {@link AnnotationConfigContextLoader}.
- * Specifically, the delegation algorithm is as follows:
- *
- * Defaults to {@code "src/main/webapp"}.
+ */
+ String value() default "src/main/webapp";
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/context/web/WebMergedContextConfiguration.java b/spring-test/src/main/java/org/springframework/test/context/web/WebMergedContextConfiguration.java
new file mode 100644
index 000000000000..c5a0dc6c2a57
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/web/WebMergedContextConfiguration.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2002-2012 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.web;
+
+import java.util.Set;
+
+import org.springframework.context.ApplicationContextInitializer;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.core.style.ToStringCreator;
+import org.springframework.test.context.ContextLoader;
+import org.springframework.test.context.MergedContextConfiguration;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * TODO [SPR-5243] Document WebMergedContextConfiguration.
+ *
+ * @author Sam Brannen
+ * @since 3.2
+ */
+public class WebMergedContextConfiguration extends MergedContextConfiguration {
+
+ private static final long serialVersionUID = 7323361588604247458L;
+
+ private final String resourceBasePath;
+
+
+ /**
+ * TODO [SPR-5243] Document WebMergedContextConfiguration constructor.
+ */
+ public WebMergedContextConfiguration(
+ Class> testClass,
+ String[] locations,
+ Class>[] classes,
+ Setnull
.
*/
- private static String nullSafeToString(ContextLoader contextLoader) {
+ protected static String nullSafeToString(ContextLoader contextLoader) {
return contextLoader == null ? "null" : contextLoader.getClass().getName();
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/TestContext.java b/spring-test/src/main/java/org/springframework/test/context/TestContext.java
index 7b7426b1049f..6d3a90c663d0 100644
--- a/spring-test/src/main/java/org/springframework/test/context/TestContext.java
+++ b/spring-test/src/main/java/org/springframework/test/context/TestContext.java
@@ -114,7 +114,7 @@ public class TestContext extends AttributeAccessorSupport {
*/
private ApplicationContext loadApplicationContext() throws Exception {
ContextLoader contextLoader = mergedContextConfiguration.getContextLoader();
- Assert.notNull(contextLoader, "Can not load an ApplicationContext with a NULL 'contextLoader'. "
+ Assert.notNull(contextLoader, "Cannot load an ApplicationContext with a NULL 'contextLoader'. "
+ "Consider annotating your test class with @ContextConfiguration.");
ApplicationContext applicationContext;
@@ -125,7 +125,7 @@ private ApplicationContext loadApplicationContext() throws Exception {
}
else {
String[] locations = mergedContextConfiguration.getLocations();
- Assert.notNull(locations, "Can not load an ApplicationContext with a NULL 'locations' array. "
+ Assert.notNull(locations, "Cannot load an ApplicationContext with a NULL 'locations' array. "
+ "Consider annotating your test class with @ContextConfiguration.");
applicationContext = contextLoader.loadContext(locations);
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java b/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java
index b5ba968ba0ca..e3e42b6706c9 100644
--- a/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java
+++ b/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java
@@ -76,6 +76,7 @@
public class TestContextManager {
private static final String[] DEFAULT_TEST_EXECUTION_LISTENER_CLASS_NAMES = new String[] {
+ "org.springframework.test.context.web.WebTestExecutionListener",
"org.springframework.test.context.support.DependencyInjectionTestExecutionListener",
"org.springframework.test.context.support.DirtiesContextTestExecutionListener",
"org.springframework.test.context.transaction.TransactionalTestExecutionListener" };
@@ -126,7 +127,6 @@ protected final TestContext getTestContext() {
return this.testContext;
}
-
/**
* Register the supplied {@link TestExecutionListener TestExecutionListeners}
* by appending them to the set of listeners used by this TestContextManager
.
@@ -155,8 +155,8 @@ public final ListTestContextManager
in reverse order.
*/
private List
+ *
+ *
+ * MergedContextConfiguration
in the
+ * {@link org.springframework.core.env.Environment Environment} of the context.
+ *
+ *
+ * @param configAttributes the context configuration attributes to process
+ * @throws IllegalArgumentException if the supplied configuration attributes are
+ * null
, or if the supplied configuration attributes include both
+ * resource locations and annotated classes
+ * @throws IllegalStateException if the XML-based loader detects default
+ * configuration classes; if the annotation-based loader detects default
+ * resource locations; if neither candidate loader detects defaults for the supplied
+ * context configuration; or if both candidate loaders detect defaults for the
+ * supplied context configuration
+ */
+ public void processContextConfiguration(final ContextConfigurationAttributes configAttributes) {
+
+ Assert.notNull(configAttributes, "configAttributes must not be null");
+ Assert.isTrue(!(configAttributes.hasLocations() && configAttributes.hasClasses()), String.format(
+ "Cannot process locations AND classes for context "
+ + "configuration %s; configure one or the other, but not both.", configAttributes));
+
+ // If the original locations or classes were not empty, there's no
+ // need to bother with default detection checks; just let the
+ // appropriate loader process the configuration.
+ if (configAttributes.hasLocations()) {
+ delegateProcessing(getXmlLoader(), configAttributes);
+ } else if (configAttributes.hasClasses()) {
+ delegateProcessing(getAnnotationConfigLoader(), configAttributes);
+ } else {
+ // Else attempt to detect defaults...
+
+ // Let the XML loader process the configuration.
+ delegateProcessing(getXmlLoader(), configAttributes);
+ boolean xmlLoaderDetectedDefaults = configAttributes.hasLocations();
+
+ if (xmlLoaderDetectedDefaults) {
+ if (logger.isInfoEnabled()) {
+ logger.info(String.format("%s detected default locations for context configuration %s.",
+ name(getXmlLoader()), configAttributes));
+ }
+ }
+
+ if (configAttributes.hasClasses()) {
+ throw new IllegalStateException(String.format(
+ "%s should NOT have detected default configuration classes for context configuration %s.",
+ name(getXmlLoader()), configAttributes));
+ }
+
+ // Now let the annotation config loader process the configuration.
+ delegateProcessing(getAnnotationConfigLoader(), configAttributes);
+
+ if (configAttributes.hasClasses()) {
+ if (logger.isInfoEnabled()) {
+ logger.info(String.format(
+ "%s detected default configuration classes for context configuration %s.",
+ name(getAnnotationConfigLoader()), configAttributes));
+ }
+ }
+
+ if (!xmlLoaderDetectedDefaults && configAttributes.hasLocations()) {
+ throw new IllegalStateException(String.format(
+ "%s should NOT have detected default locations for context configuration %s.",
+ name(getAnnotationConfigLoader()), configAttributes));
+ }
+
+ // If neither loader detected defaults and no initializers were declared,
+ // throw an exception.
+ if (!configAttributes.hasResources() && ObjectUtils.isEmpty(configAttributes.getInitializers())) {
+ throw new IllegalStateException(String.format(
+ "Neither %s nor %s was able to detect defaults, and no ApplicationContextInitializers "
+ + "were declared for context configuration %s", name(getXmlLoader()),
+ name(getAnnotationConfigLoader()), configAttributes));
+ }
+
+ if (configAttributes.hasLocations() && configAttributes.hasClasses()) {
+ String message = String.format(
+ "Configuration error: both default locations AND default configuration classes "
+ + "were detected for context configuration %s; configure one or the other, but not both.",
+ configAttributes);
+ logger.error(message);
+ throw new IllegalStateException(message);
+ }
+ }
+ }
+
+ /**
+ * Delegates to an appropriate candidate {@code SmartContextLoader} to load
+ * an {@link ApplicationContext}.
+ *
+ *
+ *
+ *
+ * @param mergedConfig the merged context configuration to use to load the application context
+ * @throws IllegalArgumentException if the supplied merged configuration is null
+ * @throws IllegalStateException if neither candidate loader is capable of loading an
+ * {@code ApplicationContext} from the supplied merged context configuration
+ */
+ public ApplicationContext loadContext(MergedContextConfiguration mergedConfig) throws Exception {
+ Assert.notNull(mergedConfig, "mergedConfig must not be null");
+
+ List
*
DefaultListableBeanFactory
.
- *
- *
- * MergedContextConfiguration
in the
- * {@link org.springframework.core.env.Environment Environment} of the context.ContextLoader
.
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AbstractGenericWebContextLoader.java b/spring-test/src/main/java/org/springframework/test/context/support/AbstractGenericWebContextLoader.java
new file mode 100644
index 000000000000..6f7946a34b97
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/support/AbstractGenericWebContextLoader.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2002-2012 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.support;
+
+import javax.servlet.ServletContext;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.AnnotationConfigUtils;
+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.web.WebMergedContextConfiguration;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.context.support.GenericWebApplicationContext;
+
+/**
+ * TODO [SPR-5243] Document AbstractGenericWebContextLoader.
+ *
+ * @author Sam Brannen
+ * @since 3.2
+ */
+public abstract class AbstractGenericWebContextLoader extends AbstractContextLoader {
+
+ private static final Log logger = LogFactory.getLog(AbstractGenericWebContextLoader.class);
+
+
+ // --- SmartContextLoader -----------------------------------------------
+
+ /**
+ * TODO [SPR-5243] Document overridden loadContext(MergedContextConfiguration).
+ *
+ * @see org.springframework.test.context.SmartContextLoader#loadContext(org.springframework.test.context.MergedContextConfiguration)
+ */
+ public final 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;
+
+ if (logger.isDebugEnabled()) {
+ logger.debug(String.format("Loading WebApplicationContext for merged context configuration %s.",
+ webMergedConfig));
+ }
+
+ GenericWebApplicationContext context = new GenericWebApplicationContext();
+ configureWebResources(context, webMergedConfig);
+ prepareContext(context, webMergedConfig);
+ customizeBeanFactory(context.getDefaultListableBeanFactory(), webMergedConfig);
+ loadBeanDefinitions(context, webMergedConfig);
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
+ customizeContext(context, webMergedConfig);
+ context.refresh();
+ context.registerShutdownHook();
+ return context;
+ }
+
+ /**
+ * TODO [SPR-5243] Document configureWebResources().
+ */
+ protected void configureWebResources(GenericWebApplicationContext context,
+ WebMergedContextConfiguration webMergedConfig) {
+
+ 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);
+ }
+
+ /**
+ * TODO [SPR-5243] Document customizeBeanFactory().
+ */
+ protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory,
+ WebMergedContextConfiguration webMergedConfig) {
+ }
+
+ /**
+ * TODO [SPR-5243] Document loadBeanDefinitions().
+ */
+ protected abstract void loadBeanDefinitions(GenericWebApplicationContext context,
+ WebMergedContextConfiguration webMergedConfig);
+
+ /**
+ * TODO [SPR-5243] Document customizeContext().
+ */
+ protected void customizeContext(GenericWebApplicationContext context, WebMergedContextConfiguration webMergedConfig) {
+ }
+
+ // --- ContextLoader -------------------------------------------------------
+
+ /**
+ * TODO [SPR-5243] Document overridden loadContext(String...).
+ *
+ * @see org.springframework.test.context.ContextLoader#loadContext(java.lang.String[])
+ */
+ public final ApplicationContext loadContext(String... locations) throws Exception {
+ throw new UnsupportedOperationException(
+ "AbstractGenericWebContextLoader does not support the loadContext(String... locations) method");
+ }
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AnnotationConfigContextLoader.java b/spring-test/src/main/java/org/springframework/test/context/support/AnnotationConfigContextLoader.java
index 39542ad34b14..1976947e9b54 100644
--- a/spring-test/src/main/java/org/springframework/test/context/support/AnnotationConfigContextLoader.java
+++ b/spring-test/src/main/java/org/springframework/test/context/support/AnnotationConfigContextLoader.java
@@ -16,19 +16,13 @@
package org.springframework.test.context.support;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.List;
-
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
-import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.MergedContextConfiguration;
-import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
@@ -89,80 +83,19 @@ public void processContextConfiguration(ContextConfigurationAttributes configAtt
// --- AnnotationConfigContextLoader ---------------------------------------
- private boolean isStaticNonPrivateAndNonFinal(Class> clazz) {
- Assert.notNull(clazz, "Class must not be null");
- int modifiers = clazz.getModifiers();
- return (Modifier.isStatic(modifiers) && !Modifier.isPrivate(modifiers) && !Modifier.isFinal(modifiers));
- }
-
- /**
- * Determine if the supplied {@link Class} meets the criteria for being
- * considered a default configuration class candidate.
- *
- *
- *
- *
- * @param clazz the class to check
- * @return null
private
final
static
true
if the supplied class meets the candidate criteria
- */
- private boolean isDefaultConfigurationClassCandidate(Class> clazz) {
- return clazz != null && isStaticNonPrivateAndNonFinal(clazz) && clazz.isAnnotationPresent(Configuration.class);
- }
-
/**
* Detect the default configuration classes for the supplied test class.
*
- * null
+ * @see AnnotationConfigContextLoaderUtils
*/
protected Class>[] detectDefaultConfigurationClasses(Class> declaringClass) {
- Assert.notNull(declaringClass, "Declaring class must not be null");
-
- List
+ *
+ *
+ * @param clazz the class to check
+ * @return null
private
final
static
true
if the supplied class meets the candidate criteria
+ */
+ private static boolean isDefaultConfigurationClassCandidate(Class> clazz) {
+ return clazz != null && isStaticNonPrivateAndNonFinal(clazz) && clazz.isAnnotationPresent(Configuration.class);
+ }
+
+ /**
+ * Detect the default configuration classes for the supplied test class.
+ *
+ * null
+ */
+ static Class>[] detectDefaultConfigurationClasses(Class> declaringClass) {
+ Assert.notNull(declaringClass, "Declaring class must not be null");
+
+ Listnull
or empty and
+ * {@link #isGenerateDefaultLocations()} returns true
, this
+ * SmartContextLoader
will attempt to {@link
+ * #detectDefaultConfigurationClasses detect default configuration classes}.
+ * If defaults are detected they will be
+ * {@link ContextConfigurationAttributes#setClasses(Class[]) set} in the
+ * supplied configuration attributes. Otherwise, properties in the supplied
+ * configuration attributes will not be modified.
+ *
+ * @param configAttributes the context configuration attributes to process
+ * @see org.springframework.test.context.SmartContextLoader#processContextConfiguration(ContextConfigurationAttributes)
+ * @see #isGenerateDefaultLocations()
+ * @see #detectDefaultConfigurationClasses(Class)
+ */
+ public void processContextConfiguration(ContextConfigurationAttributes configAttributes) {
+ if (ObjectUtils.isEmpty(configAttributes.getClasses()) && isGenerateDefaultLocations()) {
+ Class>[] defaultConfigClasses = detectDefaultConfigurationClasses(configAttributes.getDeclaringClass());
+ configAttributes.setClasses(defaultConfigClasses);
+ }
+ }
+
+ /**
+ * Detect the default configuration classes for the supplied test class.
+ *
+ * null
+ * @see AnnotationConfigContextLoaderUtils
+ */
+ protected Class>[] detectDefaultConfigurationClasses(Class> declaringClass) {
+ return AnnotationConfigContextLoaderUtils.detectDefaultConfigurationClasses(declaringClass);
+ }
+
+ // --- AbstractContextLoader -----------------------------------------------
+
+ /**
+ * {@code AnnotationConfigWebContextLoader} 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.
+ *
+ * @see AbstractContextLoader#modifyLocations
+ * @throws UnsupportedOperationException
+ */
+ @Override
+ protected String[] modifyLocations(Class> clazz, String... locations) {
+ throw new UnsupportedOperationException(
+ "AnnotationConfigWebContextLoader does not support the modifyLocations(Class, String...) method");
+ }
+
+ /**
+ * {@code AnnotationConfigWebContextLoader} 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.
+ *
+ * @see AbstractContextLoader#generateDefaultLocations
+ * @throws UnsupportedOperationException
+ */
+ @Override
+ protected String[] generateDefaultLocations(Class> clazz) {
+ throw new UnsupportedOperationException(
+ "AnnotationConfigWebContextLoader does not support the generateDefaultLocations(Class) method");
+ }
+
+ /**
+ * {@code AnnotationConfigWebContextLoader} 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.
+ *
+ * @see AbstractContextLoader#getResourceSuffix
+ * @throws UnsupportedOperationException
+ */
+ @Override
+ protected String getResourceSuffix() {
+ throw new UnsupportedOperationException(
+ "AnnotationConfigWebContextLoader does not support the getResourceSuffix() method");
+ }
+
+ // --- AbstractGenericWebContextLoader -------------------------------------
+
+ /**
+ * Register classes in the supplied {@link GenericWebApplicationContext context}
+ * from the classes in the supplied {@link WebMergedContextConfiguration}.
+ *
+ *
- *
- *
- * @param configAttributes the context configuration attributes to process
- * @throws IllegalArgumentException if the supplied configuration attributes are
- * null
, or if the supplied configuration attributes include both
- * resource locations and annotated classes
- * @throws IllegalStateException if {@code GenericXmlContextLoader} detects default
- * configuration classes; if {@code AnnotationConfigContextLoader} detects default
- * resource locations; if neither candidate loader detects defaults for the supplied
- * context configuration; or if both candidate loaders detect defaults for the
- * supplied context configuration
- */
- public void processContextConfiguration(final ContextConfigurationAttributes configAttributes) {
-
- Assert.notNull(configAttributes, "configAttributes must not be null");
- Assert.isTrue(!(configAttributes.hasLocations() && configAttributes.hasClasses()), String.format(
- "Cannot process locations AND classes for context "
- + "configuration %s; configure one or the other, but not both.", configAttributes));
-
- // If the original locations or classes were not empty, there's no
- // need to bother with default detection checks; just let the
- // appropriate loader process the configuration.
- if (configAttributes.hasLocations()) {
- delegateProcessing(xmlLoader, configAttributes);
- } else if (configAttributes.hasClasses()) {
- delegateProcessing(annotationConfigLoader, configAttributes);
- } else {
- // Else attempt to detect defaults...
-
- // Let the XML loader process the configuration.
- delegateProcessing(xmlLoader, configAttributes);
- boolean xmlLoaderDetectedDefaults = configAttributes.hasLocations();
-
- if (xmlLoaderDetectedDefaults) {
- if (logger.isInfoEnabled()) {
- logger.info(String.format("%s detected default locations for context configuration %s.",
- name(xmlLoader), configAttributes));
- }
- }
-
- if (configAttributes.hasClasses()) {
- throw new IllegalStateException(String.format(
- "%s should NOT have detected default configuration classes for context configuration %s.",
- name(xmlLoader), configAttributes));
- }
-
- // Now let the annotation config loader process the configuration.
- delegateProcessing(annotationConfigLoader, configAttributes);
-
- if (configAttributes.hasClasses()) {
- if (logger.isInfoEnabled()) {
- logger.info(String.format(
- "%s detected default configuration classes for context configuration %s.",
- name(annotationConfigLoader), configAttributes));
- }
- }
-
- if (!xmlLoaderDetectedDefaults && configAttributes.hasLocations()) {
- throw new IllegalStateException(String.format(
- "%s should NOT have detected default locations for context configuration %s.",
- name(annotationConfigLoader), configAttributes));
- }
-
- // If neither loader detected defaults and no initializers were declared,
- // throw an exception.
- if (!configAttributes.hasResources() && ObjectUtils.isEmpty(configAttributes.getInitializers())) {
- throw new IllegalStateException(String.format(
- "Neither %s nor %s was able to detect defaults, and no ApplicationContextInitializers "
- + "were declared for context configuration %s", name(xmlLoader),
- name(annotationConfigLoader), configAttributes));
- }
-
- if (configAttributes.hasLocations() && configAttributes.hasClasses()) {
- String message = String.format(
- "Configuration error: both default locations AND default configuration classes "
- + "were detected for context configuration %s; configure one or the other, but not both.",
- configAttributes);
- logger.error(message);
- throw new IllegalStateException(message);
- }
- }
- }
-
- /**
- * Delegates to an appropriate candidate {@code SmartContextLoader} to load
- * an {@link ApplicationContext}.
- *
- *
- *
- *
- * @param mergedConfig the merged context configuration to use to load the application context
- * @throws IllegalArgumentException if the supplied merged configuration is null
- * @throws IllegalStateException if neither candidate loader is capable of loading an
- * {@code ApplicationContext} from the supplied merged context configuration
- */
- public ApplicationContext loadContext(MergedContextConfiguration mergedConfig) throws Exception {
- Assert.notNull(mergedConfig, "mergedConfig must not be null");
-
- List-context.xml
".
+ */
+ protected String getResourceSuffix() {
+ return "-context.xml";
+ }
+
+ /**
+ * TODO [SPR-5243] Document overridden loadBeanDefinitions().
+ *
+ * @see org.springframework.test.context.support.AbstractGenericWebContextLoader#loadBeanDefinitions(org.springframework.web.context.support.GenericWebApplicationContext, org.springframework.test.context.web.WebMergedContextConfiguration)
+ */
+ @Override
+ protected void loadBeanDefinitions(GenericWebApplicationContext context,
+ WebMergedContextConfiguration webMergedConfig) {
+ new XmlBeanDefinitionReader(context).loadBeanDefinitions(webMergedConfig.getLocations());
+ }
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/context/web/WebAppConfiguration.java b/spring-test/src/main/java/org/springframework/test/context/web/WebAppConfiguration.java
new file mode 100644
index 000000000000..3b84965feb66
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/web/WebAppConfiguration.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2002-2012 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.web;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * TODO [SPR-5243] Document WebAppConfiguration.
+ *
+ * @author Sam Brannen
+ * @since 3.2
+ */
+@Documented
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface WebAppConfiguration {
+
+ /**
+ * The root directory of the web application (i.e., WAR); should not end with a slash.
+ *
+ *