001/*
002 * Copyright 2002-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.test.context.web;
018
019import org.apache.commons.logging.Log;
020import org.apache.commons.logging.LogFactory;
021
022import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
023import org.springframework.test.context.ContextConfigurationAttributes;
024import org.springframework.test.context.MergedContextConfiguration;
025import org.springframework.test.context.support.AnnotationConfigContextLoaderUtils;
026import org.springframework.util.ObjectUtils;
027import org.springframework.web.context.support.GenericWebApplicationContext;
028
029/**
030 * Concrete implementation of {@link AbstractGenericWebContextLoader} that loads
031 * bean definitions from annotated classes.
032 *
033 * <p>See the Javadoc for
034 * {@link org.springframework.test.context.ContextConfiguration @ContextConfiguration}
035 * for a definition of <em>annotated class</em>.
036 *
037 * <p>Note: {@code AnnotationConfigWebContextLoader} supports <em>annotated classes</em>
038 * rather than the String-based resource locations defined by the legacy
039 * {@link org.springframework.test.context.ContextLoader ContextLoader} API. Thus,
040 * although {@code AnnotationConfigWebContextLoader} extends
041 * {@code AbstractGenericWebContextLoader}, {@code AnnotationConfigWebContextLoader}
042 * does <em>not</em> support any String-based methods defined by
043 * {@link org.springframework.test.context.support.AbstractContextLoader
044 * AbstractContextLoader} or {@code AbstractGenericWebContextLoader}.
045 * Consequently, {@code AnnotationConfigWebContextLoader} should chiefly be
046 * considered a {@link org.springframework.test.context.SmartContextLoader SmartContextLoader}
047 * rather than a {@link org.springframework.test.context.ContextLoader ContextLoader}.
048 *
049 * @author Sam Brannen
050 * @since 3.2
051 * @see #processContextConfiguration(ContextConfigurationAttributes)
052 * @see #detectDefaultConfigurationClasses(Class)
053 * @see #loadBeanDefinitions(GenericWebApplicationContext, WebMergedContextConfiguration)
054 * @see GenericXmlWebContextLoader
055 * @see GenericGroovyXmlWebContextLoader
056 */
057public class AnnotationConfigWebContextLoader extends AbstractGenericWebContextLoader {
058
059        private static final Log logger = LogFactory.getLog(AnnotationConfigWebContextLoader.class);
060
061
062        // SmartContextLoader
063
064        /**
065         * Process <em>annotated classes</em> in the supplied {@link ContextConfigurationAttributes}.
066         * <p>If the <em>annotated classes</em> are {@code null} or empty and
067         * {@link #isGenerateDefaultLocations()} returns {@code true}, this
068         * {@code SmartContextLoader} will attempt to {@linkplain
069         * #detectDefaultConfigurationClasses detect default configuration classes}.
070         * If defaults are detected they will be
071         * {@linkplain ContextConfigurationAttributes#setClasses(Class[]) set} in the
072         * supplied configuration attributes. Otherwise, properties in the supplied
073         * configuration attributes will not be modified.
074         * @param configAttributes the context configuration attributes to process
075         * @see org.springframework.test.context.SmartContextLoader#processContextConfiguration(ContextConfigurationAttributes)
076         * @see #isGenerateDefaultLocations()
077         * @see #detectDefaultConfigurationClasses(Class)
078         */
079        @Override
080        public void processContextConfiguration(ContextConfigurationAttributes configAttributes) {
081                if (!configAttributes.hasClasses() && isGenerateDefaultLocations()) {
082                        configAttributes.setClasses(detectDefaultConfigurationClasses(configAttributes.getDeclaringClass()));
083                }
084        }
085
086        /**
087         * Detect the default configuration classes for the supplied test class.
088         * <p>The default implementation simply delegates to
089         * {@link AnnotationConfigContextLoaderUtils#detectDefaultConfigurationClasses(Class)}.
090         * @param declaringClass the test class that declared {@code @ContextConfiguration}
091         * @return an array of default configuration classes, potentially empty but never {@code null}
092         * @see AnnotationConfigContextLoaderUtils
093         */
094        protected Class<?>[] detectDefaultConfigurationClasses(Class<?> declaringClass) {
095                return AnnotationConfigContextLoaderUtils.detectDefaultConfigurationClasses(declaringClass);
096        }
097
098
099        // AbstractContextLoader
100
101        /**
102         * {@code AnnotationConfigWebContextLoader} should be used as a
103         * {@link org.springframework.test.context.SmartContextLoader SmartContextLoader},
104         * not as a legacy {@link org.springframework.test.context.ContextLoader ContextLoader}.
105         * Consequently, this method is not supported.
106         * @throws UnsupportedOperationException in this implementation
107         * @see org.springframework.test.context.support.AbstractContextLoader#modifyLocations
108         */
109        @Override
110        protected String[] modifyLocations(Class<?> clazz, String... locations) {
111                throw new UnsupportedOperationException(
112                                "AnnotationConfigWebContextLoader does not support the modifyLocations(Class, String...) method");
113        }
114
115        /**
116         * {@code AnnotationConfigWebContextLoader} should be used as a
117         * {@link org.springframework.test.context.SmartContextLoader SmartContextLoader},
118         * not as a legacy {@link org.springframework.test.context.ContextLoader ContextLoader}.
119         * Consequently, this method is not supported.
120         * @throws UnsupportedOperationException in this implementation
121         * @see org.springframework.test.context.support.AbstractContextLoader#generateDefaultLocations
122         */
123        @Override
124        protected String[] generateDefaultLocations(Class<?> clazz) {
125                throw new UnsupportedOperationException(
126                                "AnnotationConfigWebContextLoader does not support the generateDefaultLocations(Class) method");
127        }
128
129        /**
130         * {@code AnnotationConfigWebContextLoader} should be used as a
131         * {@link org.springframework.test.context.SmartContextLoader SmartContextLoader},
132         * not as a legacy {@link org.springframework.test.context.ContextLoader ContextLoader}.
133         * Consequently, this method is not supported.
134         * @throws UnsupportedOperationException in this implementation
135         * @see org.springframework.test.context.support.AbstractContextLoader#getResourceSuffix
136         */
137        @Override
138        protected String getResourceSuffix() {
139                throw new UnsupportedOperationException(
140                                "AnnotationConfigWebContextLoader does not support the getResourceSuffix() method");
141        }
142
143
144        // AbstractGenericWebContextLoader
145
146        /**
147         * Register classes in the supplied {@linkplain GenericWebApplicationContext context}
148         * from the classes in the supplied {@link WebMergedContextConfiguration}.
149         * <p>Each class must represent an <em>annotated class</em>. An
150         * {@link AnnotatedBeanDefinitionReader} is used to register the appropriate
151         * bean definitions.
152         * @param context the context in which the annotated classes should be registered
153         * @param webMergedConfig the merged configuration from which the classes should be retrieved
154         * @see AbstractGenericWebContextLoader#loadBeanDefinitions
155         */
156        @Override
157        protected void loadBeanDefinitions(
158                        GenericWebApplicationContext context, WebMergedContextConfiguration webMergedConfig) {
159
160                Class<?>[] annotatedClasses = webMergedConfig.getClasses();
161                if (logger.isDebugEnabled()) {
162                        logger.debug("Registering annotated classes: " + ObjectUtils.nullSafeToString(annotatedClasses));
163                }
164                new AnnotatedBeanDefinitionReader(context).register(annotatedClasses);
165        }
166
167        /**
168         * Ensure that the supplied {@link WebMergedContextConfiguration} does not
169         * contain {@link MergedContextConfiguration#getLocations() locations}.
170         * @since 4.0.4
171         * @see AbstractGenericWebContextLoader#validateMergedContextConfiguration
172         */
173        @Override
174        protected void validateMergedContextConfiguration(WebMergedContextConfiguration webMergedConfig) {
175                if (webMergedConfig.hasLocations()) {
176                        String msg = String.format("Test class [%s] has been configured with @ContextConfiguration's 'locations' " +
177                                                        "(or 'value') attribute %s, but %s does not support resource locations.",
178                                        webMergedConfig.getTestClass().getName(),
179                                        ObjectUtils.nullSafeToString(webMergedConfig.getLocations()), getClass().getSimpleName());
180                        logger.error(msg);
181                        throw new IllegalStateException(msg);
182                }
183        }
184
185}