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.web; 018 019import java.util.Set; 020 021import org.springframework.context.ApplicationContextInitializer; 022import org.springframework.core.style.ToStringCreator; 023import org.springframework.lang.Nullable; 024import org.springframework.test.context.CacheAwareContextLoaderDelegate; 025import org.springframework.test.context.ContextCustomizer; 026import org.springframework.test.context.ContextLoader; 027import org.springframework.test.context.MergedContextConfiguration; 028import org.springframework.util.ObjectUtils; 029import org.springframework.util.StringUtils; 030 031/** 032 * {@code WebMergedContextConfiguration} encapsulates the <em>merged</em> 033 * context configuration declared on a test class and all of its superclasses 034 * via {@link org.springframework.test.context.ContextConfiguration @ContextConfiguration}, 035 * {@link WebAppConfiguration @WebAppConfiguration}, and 036 * {@link org.springframework.test.context.ActiveProfiles @ActiveProfiles}. 037 * 038 * <p>{@code WebMergedContextConfiguration} extends the contract of 039 * {@link MergedContextConfiguration} by adding support for the {@link 040 * #getResourceBasePath() resource base path} configured via {@code @WebAppConfiguration}. 041 * This allows the {@link org.springframework.test.context.TestContext TestContext} 042 * to properly cache the corresponding {@link 043 * org.springframework.web.context.WebApplicationContext WebApplicationContext} 044 * that was loaded using properties of this {@code WebMergedContextConfiguration}. 045 * 046 * @author Sam Brannen 047 * @since 3.2 048 * @see WebAppConfiguration 049 * @see MergedContextConfiguration 050 * @see org.springframework.test.context.ContextConfiguration 051 * @see org.springframework.test.context.ActiveProfiles 052 * @see org.springframework.test.context.ContextConfigurationAttributes 053 * @see org.springframework.test.context.SmartContextLoader#loadContext(MergedContextConfiguration) 054 */ 055public class WebMergedContextConfiguration extends MergedContextConfiguration { 056 057 private static final long serialVersionUID = 7323361588604247458L; 058 059 private final String resourceBasePath; 060 061 062 /** 063 * Create a new {@code WebMergedContextConfiguration} instance by copying 064 * all properties from the supplied {@code MergedContextConfiguration}. 065 * <p>If an <em>empty</em> value is supplied for the {@code resourceBasePath} 066 * an empty string will be used. 067 * @param resourceBasePath the resource path to the root directory of the web application 068 * @since 4.1 069 */ 070 public WebMergedContextConfiguration(MergedContextConfiguration mergedConfig, String resourceBasePath) { 071 super(mergedConfig); 072 this.resourceBasePath = !StringUtils.hasText(resourceBasePath) ? "" : resourceBasePath; 073 } 074 075 /** 076 * Create a new {@code WebMergedContextConfiguration} instance for the 077 * supplied parameters. 078 * <p>If a {@code null} value is supplied for {@code locations}, 079 * {@code classes}, {@code activeProfiles}, {@code propertySourceLocations}, 080 * or {@code propertySourceProperties} an empty array will be stored instead. 081 * If a {@code null} value is supplied for the 082 * {@code contextInitializerClasses} an empty set will be stored instead. 083 * If an <em>empty</em> value is supplied for the {@code resourceBasePath} 084 * an empty string will be used. Furthermore, active profiles will be sorted, 085 * and duplicate profiles will be removed. 086 * @param testClass the test class for which the configuration was merged 087 * @param locations the merged resource locations 088 * @param classes the merged annotated classes 089 * @param contextInitializerClasses the merged context initializer classes 090 * @param activeProfiles the merged active bean definition profiles 091 * @param propertySourceLocations the merged {@code PropertySource} locations 092 * @param propertySourceProperties the merged {@code PropertySource} properties 093 * @param resourceBasePath the resource path to the root directory of the web application 094 * @param contextLoader the resolved {@code ContextLoader} 095 * @param cacheAwareContextLoaderDelegate a cache-aware context loader 096 * delegate with which to retrieve the parent context 097 * @param parent the parent configuration or {@code null} if there is no parent 098 * @since 4.1 099 */ 100 public WebMergedContextConfiguration(Class<?> testClass, @Nullable String[] locations, @Nullable Class<?>[] classes, 101 @Nullable Set<Class<? extends ApplicationContextInitializer<?>>> contextInitializerClasses, 102 @Nullable String[] activeProfiles, @Nullable String[] propertySourceLocations, @Nullable String[] propertySourceProperties, 103 String resourceBasePath, ContextLoader contextLoader, 104 CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, @Nullable MergedContextConfiguration parent) { 105 106 this(testClass, locations, classes, contextInitializerClasses, activeProfiles, propertySourceLocations, 107 propertySourceProperties, null, resourceBasePath, contextLoader, cacheAwareContextLoaderDelegate, parent); 108 } 109 110 /** 111 * Create a new {@code WebMergedContextConfiguration} instance for the 112 * supplied parameters. 113 * <p>If a {@code null} value is supplied for {@code locations}, 114 * {@code classes}, {@code activeProfiles}, {@code propertySourceLocations}, 115 * or {@code propertySourceProperties} an empty array will be stored instead. 116 * If a {@code null} value is supplied for {@code contextInitializerClasses} 117 * or {@code contextCustomizers}, an empty set will be stored instead. 118 * If an <em>empty</em> value is supplied for the {@code resourceBasePath} 119 * an empty string will be used. Furthermore, active profiles will be sorted, 120 * and duplicate profiles will be removed. 121 * @param testClass the test class for which the configuration was merged 122 * @param locations the merged context resource locations 123 * @param classes the merged annotated classes 124 * @param contextInitializerClasses the merged context initializer classes 125 * @param activeProfiles the merged active bean definition profiles 126 * @param propertySourceLocations the merged {@code PropertySource} locations 127 * @param propertySourceProperties the merged {@code PropertySource} properties 128 * @param contextCustomizers the context customizers 129 * @param resourceBasePath the resource path to the root directory of the web application 130 * @param contextLoader the resolved {@code ContextLoader} 131 * @param cacheAwareContextLoaderDelegate a cache-aware context loader 132 * delegate with which to retrieve the parent context 133 * @param parent the parent configuration or {@code null} if there is no parent 134 * @since 4.3 135 */ 136 public WebMergedContextConfiguration(Class<?> testClass, @Nullable String[] locations, @Nullable Class<?>[] classes, 137 @Nullable Set<Class<? extends ApplicationContextInitializer<?>>> contextInitializerClasses, 138 @Nullable String[] activeProfiles, @Nullable String[] propertySourceLocations, @Nullable String[] propertySourceProperties, 139 @Nullable Set<ContextCustomizer> contextCustomizers, String resourceBasePath, ContextLoader contextLoader, 140 CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate, @Nullable MergedContextConfiguration parent) { 141 142 super(testClass, locations, classes, contextInitializerClasses, activeProfiles, propertySourceLocations, 143 propertySourceProperties, contextCustomizers, contextLoader, cacheAwareContextLoaderDelegate, parent); 144 145 this.resourceBasePath = (StringUtils.hasText(resourceBasePath) ? resourceBasePath : ""); 146 } 147 148 /** 149 * Get the resource path to the root directory of the web application for the 150 * {@linkplain #getTestClass() test class}, configured via {@code @WebAppConfiguration}. 151 * @see WebAppConfiguration 152 */ 153 public String getResourceBasePath() { 154 return this.resourceBasePath; 155 } 156 157 158 /** 159 * Determine if the supplied object is equal to this {@code WebMergedContextConfiguration} 160 * instance by comparing both object's {@linkplain #getLocations() locations}, 161 * {@linkplain #getClasses() annotated classes}, 162 * {@linkplain #getContextInitializerClasses() context initializer classes}, 163 * {@linkplain #getActiveProfiles() active profiles}, 164 * {@linkplain #getResourceBasePath() resource base path}, 165 * {@linkplain #getParent() parents}, and the fully qualified names of their 166 * {@link #getContextLoader() ContextLoaders}. 167 */ 168 @Override 169 public boolean equals(@Nullable Object other) { 170 return (this == other || (super.equals(other) && 171 this.resourceBasePath.equals(((WebMergedContextConfiguration) other).resourceBasePath))); 172 } 173 174 /** 175 * Generate a unique hash code for all properties of this 176 * {@code WebMergedContextConfiguration} excluding the 177 * {@linkplain #getTestClass() test class}. 178 */ 179 @Override 180 public int hashCode() { 181 return (31 * super.hashCode() + this.resourceBasePath.hashCode()); 182 } 183 184 /** 185 * Provide a String representation of the {@linkplain #getTestClass() test class}, 186 * {@linkplain #getLocations() locations}, {@linkplain #getClasses() annotated classes}, 187 * {@linkplain #getContextInitializerClasses() context initializer classes}, 188 * {@linkplain #getActiveProfiles() active profiles}, 189 * {@linkplain #getPropertySourceLocations() property source locations}, 190 * {@linkplain #getPropertySourceProperties() property source properties}, 191 * {@linkplain #getContextCustomizers() context customizers}, 192 * {@linkplain #getResourceBasePath() resource base path}, the name of the 193 * {@link #getContextLoader() ContextLoader}, and the 194 * {@linkplain #getParent() parent configuration}. 195 */ 196 @Override 197 public String toString() { 198 return new ToStringCreator(this) 199 .append("testClass", getTestClass()) 200 .append("locations", ObjectUtils.nullSafeToString(getLocations())) 201 .append("classes", ObjectUtils.nullSafeToString(getClasses())) 202 .append("contextInitializerClasses", ObjectUtils.nullSafeToString(getContextInitializerClasses())) 203 .append("activeProfiles", ObjectUtils.nullSafeToString(getActiveProfiles())) 204 .append("propertySourceLocations", ObjectUtils.nullSafeToString(getPropertySourceLocations())) 205 .append("propertySourceProperties", ObjectUtils.nullSafeToString(getPropertySourceProperties())) 206 .append("contextCustomizers", getContextCustomizers()) 207 .append("resourceBasePath", getResourceBasePath()) 208 .append("contextLoader", nullSafeClassName(getContextLoader())) 209 .append("parent", getParent()) 210 .toString(); 211 } 212 213}