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.annotation; 018 019import java.lang.reflect.Method; 020 021import org.apache.commons.logging.Log; 022import org.apache.commons.logging.LogFactory; 023 024import org.springframework.core.annotation.AnnotatedElementUtils; 025import org.springframework.core.annotation.AnnotationUtils; 026import org.springframework.lang.Nullable; 027import org.springframework.util.Assert; 028import org.springframework.util.ObjectUtils; 029import org.springframework.util.ReflectionUtils; 030import org.springframework.util.StringUtils; 031 032/** 033 * General utility methods for working with <em>profile values</em>. 034 * 035 * @author Sam Brannen 036 * @author Juergen Hoeller 037 * @since 2.5 038 * @see ProfileValueSource 039 * @see ProfileValueSourceConfiguration 040 * @see IfProfileValue 041 */ 042public abstract class ProfileValueUtils { 043 044 private static final Log logger = LogFactory.getLog(ProfileValueUtils.class); 045 046 047 /** 048 * Retrieves the {@link ProfileValueSource} type for the specified 049 * {@link Class test class} as configured via the 050 * {@link ProfileValueSourceConfiguration 051 * @ProfileValueSourceConfiguration} annotation and instantiates a new 052 * instance of that type. 053 * <p>If {@link ProfileValueSourceConfiguration 054 * @ProfileValueSourceConfiguration} is not present on the specified 055 * class or if a custom {@link ProfileValueSource} is not declared, the 056 * default {@link SystemProfileValueSource} will be returned instead. 057 * @param testClass the test class for which the ProfileValueSource should 058 * be retrieved 059 * @return the configured (or default) ProfileValueSource for the specified 060 * class 061 * @see SystemProfileValueSource 062 */ 063 @SuppressWarnings("unchecked") 064 public static ProfileValueSource retrieveProfileValueSource(Class<?> testClass) { 065 Assert.notNull(testClass, "testClass must not be null"); 066 067 Class<ProfileValueSourceConfiguration> annotationType = ProfileValueSourceConfiguration.class; 068 ProfileValueSourceConfiguration config = AnnotatedElementUtils.findMergedAnnotation(testClass, annotationType); 069 if (logger.isDebugEnabled()) { 070 logger.debug("Retrieved @ProfileValueSourceConfiguration [" + config + "] for test class [" + 071 testClass.getName() + "]"); 072 } 073 074 Class<? extends ProfileValueSource> profileValueSourceType; 075 if (config != null) { 076 profileValueSourceType = config.value(); 077 } 078 else { 079 profileValueSourceType = (Class<? extends ProfileValueSource>) AnnotationUtils.getDefaultValue(annotationType); 080 Assert.state(profileValueSourceType != null, "No default ProfileValueSource class"); 081 } 082 if (logger.isDebugEnabled()) { 083 logger.debug("Retrieved ProfileValueSource type [" + profileValueSourceType + "] for class [" + 084 testClass.getName() + "]"); 085 } 086 087 ProfileValueSource profileValueSource; 088 if (SystemProfileValueSource.class == profileValueSourceType) { 089 profileValueSource = SystemProfileValueSource.getInstance(); 090 } 091 else { 092 try { 093 profileValueSource = ReflectionUtils.accessibleConstructor(profileValueSourceType).newInstance(); 094 } 095 catch (Exception ex) { 096 if (logger.isWarnEnabled()) { 097 logger.warn("Could not instantiate a ProfileValueSource of type [" + profileValueSourceType + 098 "] for class [" + testClass.getName() + "]: using default.", ex); 099 } 100 profileValueSource = SystemProfileValueSource.getInstance(); 101 } 102 } 103 104 return profileValueSource; 105 } 106 107 /** 108 * Determine if the supplied {@code testClass} is <em>enabled</em> in 109 * the current environment, as specified by the {@link IfProfileValue 110 * @IfProfileValue} annotation at the class level. 111 * <p>Defaults to {@code true} if no {@link IfProfileValue 112 * @IfProfileValue} annotation is declared. 113 * @param testClass the test class 114 * @return {@code true} if the test is <em>enabled</em> in the current 115 * environment 116 */ 117 public static boolean isTestEnabledInThisEnvironment(Class<?> testClass) { 118 IfProfileValue ifProfileValue = AnnotatedElementUtils.findMergedAnnotation(testClass, IfProfileValue.class); 119 return isTestEnabledInThisEnvironment(retrieveProfileValueSource(testClass), ifProfileValue); 120 } 121 122 /** 123 * Determine if the supplied {@code testMethod} is <em>enabled</em> in 124 * the current environment, as specified by the {@link IfProfileValue 125 * @IfProfileValue} annotation, which may be declared on the test 126 * method itself or at the class level. Class-level usage overrides 127 * method-level usage. 128 * <p>Defaults to {@code true} if no {@link IfProfileValue 129 * @IfProfileValue} annotation is declared. 130 * @param testMethod the test method 131 * @param testClass the test class 132 * @return {@code true} if the test is <em>enabled</em> in the current 133 * environment 134 */ 135 public static boolean isTestEnabledInThisEnvironment(Method testMethod, Class<?> testClass) { 136 return isTestEnabledInThisEnvironment(retrieveProfileValueSource(testClass), testMethod, testClass); 137 } 138 139 /** 140 * Determine if the supplied {@code testMethod} is <em>enabled</em> in 141 * the current environment, as specified by the {@link IfProfileValue 142 * @IfProfileValue} annotation, which may be declared on the test 143 * method itself or at the class level. Class-level usage overrides 144 * method-level usage. 145 * <p>Defaults to {@code true} if no {@link IfProfileValue 146 * @IfProfileValue} annotation is declared. 147 * @param profileValueSource the ProfileValueSource to use to determine if 148 * the test is enabled 149 * @param testMethod the test method 150 * @param testClass the test class 151 * @return {@code true} if the test is <em>enabled</em> in the current 152 * environment 153 */ 154 public static boolean isTestEnabledInThisEnvironment(ProfileValueSource profileValueSource, Method testMethod, 155 Class<?> testClass) { 156 157 IfProfileValue ifProfileValue = AnnotatedElementUtils.findMergedAnnotation(testClass, IfProfileValue.class); 158 boolean classLevelEnabled = isTestEnabledInThisEnvironment(profileValueSource, ifProfileValue); 159 160 if (classLevelEnabled) { 161 ifProfileValue = AnnotatedElementUtils.findMergedAnnotation(testMethod, IfProfileValue.class); 162 return isTestEnabledInThisEnvironment(profileValueSource, ifProfileValue); 163 } 164 165 return false; 166 } 167 168 /** 169 * Determine if the {@code value} (or one of the {@code values}) 170 * in the supplied {@link IfProfileValue @IfProfileValue} annotation is 171 * <em>enabled</em> in the current environment. 172 * @param profileValueSource the ProfileValueSource to use to determine if 173 * the test is enabled 174 * @param ifProfileValue the annotation to introspect; may be 175 * {@code null} 176 * @return {@code true} if the test is <em>enabled</em> in the current 177 * environment or if the supplied {@code ifProfileValue} is 178 * {@code null} 179 */ 180 private static boolean isTestEnabledInThisEnvironment(ProfileValueSource profileValueSource, 181 @Nullable IfProfileValue ifProfileValue) { 182 183 if (ifProfileValue == null) { 184 return true; 185 } 186 187 String environmentValue = profileValueSource.get(ifProfileValue.name()); 188 String[] annotatedValues = ifProfileValue.values(); 189 if (StringUtils.hasLength(ifProfileValue.value())) { 190 Assert.isTrue(annotatedValues.length == 0, () -> "Setting both the 'value' and 'values' attributes " + 191 "of @IfProfileValue is not allowed: choose one or the other."); 192 annotatedValues = new String[] { ifProfileValue.value() }; 193 } 194 195 for (String value : annotatedValues) { 196 if (ObjectUtils.nullSafeEquals(value, environmentValue)) { 197 return true; 198 } 199 } 200 return false; 201 } 202 203}