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