/* * Copyright 2002-2015 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 org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.core.io.ClassPathResource; import org.springframework.core.style.ToStringCreator; import org.springframework.test.context.TestPropertySource; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.ResourceUtils; /** * {@code TestPropertySourceAttributes} encapsulates the attributes declared * via {@link TestPropertySource @TestPropertySource}. * * <p>In addition to encapsulating declared attributes, * {@code TestPropertySourceAttributes} also enforces configuration rules * and detects default properties files. * * @author Sam Brannen * @since 4.1 * @see TestPropertySource * @see MergedTestPropertySources */ class TestPropertySourceAttributes { private static final Log logger = LogFactory.getLog(TestPropertySourceAttributes.class); private final Class<?> declaringClass; private final String[] locations; private final boolean inheritLocations; private final String[] properties; private final boolean inheritProperties; /** * Create a new {@code TestPropertySourceAttributes} instance for the * supplied {@link TestPropertySource @TestPropertySource} annotation and * the {@linkplain Class test class} that declared it, enforcing * configuration rules and detecting a default properties file if * necessary. * @param declaringClass the class that declared {@code @TestPropertySource} * @param testPropertySource the annotation from which to retrieve the attributes * @since 4.2 */ TestPropertySourceAttributes(Class<?> declaringClass, TestPropertySource testPropertySource) { this(declaringClass, testPropertySource.locations(), testPropertySource.inheritLocations(), testPropertySource.properties(), testPropertySource.inheritProperties()); } private TestPropertySourceAttributes(Class<?> declaringClass, String[] locations, boolean inheritLocations, String[] properties, boolean inheritProperties) { Assert.notNull(declaringClass, "declaringClass must not be null"); if (ObjectUtils.isEmpty(locations) && ObjectUtils.isEmpty(properties)) { locations = new String[] { detectDefaultPropertiesFile(declaringClass) }; } this.declaringClass = declaringClass; this.locations = locations; this.inheritLocations = inheritLocations; this.properties = properties; this.inheritProperties = inheritProperties; } /** * Get the {@linkplain Class class} that declared {@code @TestPropertySource}. * * @return the declaring class; never {@code null} */ Class<?> getDeclaringClass() { return declaringClass; } /** * Get the resource locations that were declared via {@code @TestPropertySource}. * * <p>Note: The returned value may represent a <em>detected default</em> * that does not match the original value declared via {@code @TestPropertySource}. * * @return the resource locations; potentially {@code null} or <em>empty</em> * @see TestPropertySource#value * @see TestPropertySource#locations * @see #setLocations(String[]) */ String[] getLocations() { return locations; } /** * Get the {@code inheritLocations} flag that was declared via {@code @TestPropertySource}. * * @return the {@code inheritLocations} flag * @see TestPropertySource#inheritLocations */ boolean isInheritLocations() { return inheritLocations; } /** * Get the inlined properties that were declared via {@code @TestPropertySource}. * * @return the inlined properties; potentially {@code null} or <em>empty</em> * @see TestPropertySource#properties */ String[] getProperties() { return this.properties; } /** * Get the {@code inheritProperties} flag that was declared via {@code @TestPropertySource}. * * @return the {@code inheritProperties} flag * @see TestPropertySource#inheritProperties */ boolean isInheritProperties() { return this.inheritProperties; } /** * Provide a String representation of the {@code @TestPropertySource} * attributes and declaring class. */ @Override public String toString() { return new ToStringCreator(this)// .append("declaringClass", declaringClass.getName())// .append("locations", ObjectUtils.nullSafeToString(locations))// .append("inheritLocations", inheritLocations)// .append("properties", ObjectUtils.nullSafeToString(properties))// .append("inheritProperties", inheritProperties)// .toString(); } /** * Detect a default properties file for the supplied class, as specified * in the class-level Javadoc for {@link TestPropertySource}. */ private static String detectDefaultPropertiesFile(Class<?> testClass) { String resourcePath = ClassUtils.convertClassNameToResourcePath(testClass.getName()) + ".properties"; String prefixedResourcePath = ResourceUtils.CLASSPATH_URL_PREFIX + resourcePath; ClassPathResource classPathResource = new ClassPathResource(resourcePath); if (classPathResource.exists()) { if (logger.isInfoEnabled()) { logger.info(String.format("Detected default properties file \"%s\" for test class [%s]", prefixedResourcePath, testClass.getName())); } return prefixedResourcePath; } else { String msg = String.format("Could not detect default properties file for test [%s]: " + "%s does not exist. Either declare the 'locations' or 'properties' attributes " + "of @TestPropertySource or make the default properties file available.", testClass.getName(), classPathResource); logger.error(msg); throw new IllegalStateException(msg); } } }