001/*
002 * Copyright 2002-2019 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.support;
018
019import java.lang.reflect.Constructor;
020import java.lang.reflect.Executable;
021
022import org.apache.commons.logging.Log;
023import org.apache.commons.logging.LogFactory;
024
025import org.springframework.beans.factory.annotation.Autowired;
026import org.springframework.core.SpringProperties;
027import org.springframework.core.annotation.AnnotatedElementUtils;
028import org.springframework.test.context.TestConstructor;
029import org.springframework.test.context.TestConstructor.AutowireMode;
030
031/**
032 * Utility methods for working with {@link TestConstructor @TestConstructor}.
033 *
034 * <p>Primarily intended for use within the framework.
035 *
036 * @author Sam Brannen
037 * @since 5.2
038 * @see TestConstructor
039 */
040public abstract class TestConstructorUtils {
041
042        private static final Log logger = LogFactory.getLog(TestConstructorUtils.class);
043
044
045        private TestConstructorUtils() {
046        }
047
048        /**
049         * Determine if the supplied executable for the given test class is an
050         * autowirable constructor.
051         *
052         * <p>This method delegates to {@link #isAutowirableConstructor(Constructor, Class)}
053         * if the executable is a constructor.
054         *
055         * @param executable an executable for the test class
056         * @param testClass the test class
057         * @return {@code true} if the executable is an autowirable constructor
058         * @see #isAutowirableConstructor(Constructor, Class)
059         */
060        public static boolean isAutowirableConstructor(Executable executable, Class<?> testClass) {
061                return (executable instanceof Constructor &&
062                                isAutowirableConstructor((Constructor<?>) executable, testClass));
063        }
064
065        /**
066         * Determine if the supplied constructor for the given test class is
067         * autowirable.
068         *
069         * <p>A constructor is considered to be autowirable if one of the following
070         * conditions is {@code true}.
071         *
072         * <ol>
073         * <li>The constructor is annotated with {@link Autowired @Autowired}.</li>
074         * <li>{@link TestConstructor @TestConstructor} is <em>present</em> or
075         * <em>meta-present</em> on the test class with
076         * {@link TestConstructor#autowireMode() autowireMode} set to
077         * {@link AutowireMode#ALL ALL}.</li>
078         * <li>The default <em>test constructor autowire mode</em> has been changed
079         * to {@code ALL} (see
080         * {@link TestConstructor#TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME}).</li>
081         * </ol>
082         *
083         * @param constructor a constructor for the test class
084         * @param testClass the test class
085         * @return {@code true} if the constructor is autowirable
086         * @see #isAutowirableConstructor(Executable, Class)
087         */
088        public static boolean isAutowirableConstructor(Constructor<?> constructor, Class<?> testClass) {
089                // Is the constructor annotated with @Autowired?
090                if (AnnotatedElementUtils.hasAnnotation(constructor, Autowired.class)) {
091                        return true;
092                }
093
094                AutowireMode autowireMode = null;
095
096                // Is the test class annotated with @TestConstructor?
097                TestConstructor testConstructor = AnnotatedElementUtils.findMergedAnnotation(testClass, TestConstructor.class);
098                if (testConstructor != null) {
099                        autowireMode = testConstructor.autowireMode();
100                }
101                else {
102                        // Custom global default?
103                        String value = SpringProperties.getProperty(TestConstructor.TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME);
104                        if (value != null) {
105                                try {
106                                        autowireMode = AutowireMode.valueOf(value.trim().toUpperCase());
107                                }
108                                catch (Exception ex) {
109                                        if (logger.isDebugEnabled()) {
110                                                logger.debug(String.format("Failed to parse autowire mode '%s' for property '%s': %s", value,
111                                                        TestConstructor.TEST_CONSTRUCTOR_AUTOWIRE_MODE_PROPERTY_NAME, ex.getMessage()));
112                                        }
113                                }
114                        }
115                }
116
117                return (autowireMode == AutowireMode.ALL);
118        }
119
120}