diff --git a/spring-test/src/main/java/org/springframework/test/context/CacheAwareContextLoaderDelegate.java b/spring-test/src/main/java/org/springframework/test/context/CacheAwareContextLoaderDelegate.java index 1a59940476..5dfd5705b2 100644 --- a/spring-test/src/main/java/org/springframework/test/context/CacheAwareContextLoaderDelegate.java +++ b/spring-test/src/main/java/org/springframework/test/context/CacheAwareContextLoaderDelegate.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * 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. @@ -23,7 +23,7 @@ import org.springframework.test.annotation.DirtiesContext.HierarchyMode; /** * A {@code CacheAwareContextLoaderDelegate} is responsible for {@linkplain * #loadContext loading} and {@linkplain #closeContext closing} application - * contexts, interacting transparently with a context cache behind + * contexts, interacting transparently with a {@link ContextCache} behind * the scenes. * *
Note: {@code CacheAwareContextLoaderDelegate} does not extend the @@ -38,7 +38,7 @@ public interface CacheAwareContextLoaderDelegate { * Load the {@linkplain ApplicationContext application context} for the supplied * {@link MergedContextConfiguration} by delegating to the {@link ContextLoader} * configured in the given {@code MergedContextConfiguration}. - *
If the context is present in the context cache it will simply + *
If the context is present in the {@code ContextCache} it will simply * be returned; otherwise, it will be loaded, stored in the cache, and returned. * @param mergedContextConfiguration the merged context configuration to use * to load the application context; never {@code null} @@ -50,7 +50,7 @@ public interface CacheAwareContextLoaderDelegate { /** * Remove the {@linkplain ApplicationContext application context} for the - * supplied {@link MergedContextConfiguration} from the context cache + * supplied {@link MergedContextConfiguration} from the {@code ContextCache} * and {@linkplain ConfigurableApplicationContext#close() close} it if it is * an instance of {@link ConfigurableApplicationContext}. *
The semantics of the supplied {@code HierarchyMode} must be honored when diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextCache.java b/spring-test/src/main/java/org/springframework/test/context/ContextCache.java index 5f18399d80..6dafc4a4cd 100644 --- a/spring-test/src/main/java/org/springframework/test/context/ContextCache.java +++ b/spring-test/src/main/java/org/springframework/test/context/ContextCache.java @@ -16,145 +16,48 @@ package org.springframework.test.context; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; - import org.springframework.context.ApplicationContext; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.core.style.ToStringCreator; import org.springframework.test.annotation.DirtiesContext.HierarchyMode; -import org.springframework.util.Assert; -import org.springframework.util.ConcurrentReferenceHashMap; /** - * Cache for Spring {@link ApplicationContext ApplicationContexts} in a test - * environment. + * {@code ContextCache} defines the public API for caching Spring + * {@link ApplicationContext ApplicationContexts} within the Spring + * TestContext Framework. + * + *
A {@code ContextCache} maintains a cache of {@code ApplicationContexts} + * keyed by {@link MergedContextConfiguration} instances. * *
Caching has significant performance benefits if initializing the context - * takes a considerable about of time. Although initializing a Spring context - * itself is very quick, some beans in a context, such as a - * {@code LocalSessionFactoryBean} for working with Hibernate, may take some - * time to initialize. Hence it often makes sense to perform that initialization - * only once per test suite. - * - *
{@code ContextCache} maintains a cache of {@code ApplicationContexts} - * keyed by {@link MergedContextConfiguration} instances. Behind the scenes, - * Spring's {@link ConcurrentReferenceHashMap} is used to store - * {@linkplain java.lang.ref.SoftReference soft references} to cached contexts - * and {@code MergedContextConfiguration} instances. + *
Context caching can have significant performance benefits if context
+ * initialization is complex. So, although initializing a Spring context itself
+ * is typically very quick, some beans in a context — for example, an
+ * in-memory database or a {@code LocalSessionFactoryBean} for working with
+ * Hibernate — may take several seconds to initialize. Hence it often
+ * makes sense to perform that initialization only once per test suite.
*
* @author Sam Brannen
* @author Juergen Hoeller
- * @since 2.5
- * @see ConcurrentReferenceHashMap
+ * @since 4.2
*/
-class ContextCache {
-
- /**
- * Map of context keys to Spring {@code ApplicationContext} instances.
- */
- private final Map The {@link #getHitCount() hit} and {@link #getMissCount() miss} counts will
- * be updated accordingly.
+ * The {@link #getHitCount() hit} and {@link #getMissCount() miss} counts
+ * must be updated accordingly.
* @param key the context key (never {@code null})
* @return the corresponding {@code ApplicationContext} instance, or {@code null}
* if not found in the cache
* @see #remove
*/
- public ApplicationContext get(MergedContextConfiguration key) {
- Assert.notNull(key, "Key must not be null");
- ApplicationContext context = this.contextMap.get(key);
- if (context == null) {
- this.missCount.incrementAndGet();
- }
- else {
- this.hitCount.incrementAndGet();
- }
- return context;
- }
-
- /**
- * Get the overall hit count for this cache.
- * A hit is any access to the cache that returns a non-null
- * context for the queried key.
- */
- public int getHitCount() {
- return this.hitCount.get();
- }
-
- /**
- * Get the overall miss count for this cache.
- * A miss is any access to the cache that returns a {@code null}
- * context for the queried key.
- */
- public int getMissCount() {
- return this.missCount.get();
- }
+ ApplicationContext get(MergedContextConfiguration key);
/**
* Explicitly add an {@code ApplicationContext} instance to the cache
@@ -162,122 +65,80 @@ class ContextCache {
* @param key the context key (never {@code null})
* @param context the {@code ApplicationContext} instance (never {@code null})
*/
- public void put(MergedContextConfiguration key, ApplicationContext context) {
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(context, "ApplicationContext must not be null");
-
- this.contextMap.put(key, context);
- MergedContextConfiguration child = key;
- MergedContextConfiguration parent = child.getParent();
- while (parent != null) {
- Set Generally speaking, you would only call this method if you change the
- * state of a singleton bean, potentially affecting future interaction with
- * the context.
- * In addition, the semantics of the supplied {@code HierarchyMode} will
+ * {@linkplain org.springframework.context.ConfigurableApplicationContext#close() close}
+ * it if it is an instance of {@code ConfigurableApplicationContext}.
+ * Generally speaking, this method should be called if the state of
+ * a singleton bean has been modified, potentially affecting future
+ * interaction with the context.
+ * In addition, the semantics of the supplied {@code HierarchyMode} must
* be honored. See the Javadoc for {@link HierarchyMode} for details.
* @param key the context key; never {@code null}
* @param hierarchyMode the hierarchy mode; may be {@code null} if the context
* is not part of a hierarchy
*/
- public void remove(MergedContextConfiguration key, HierarchyMode hierarchyMode) {
- Assert.notNull(key, "Key must not be null");
-
- // startKey is the level at which to begin clearing the cache, depending
- // on the configured hierarchy mode.
- MergedContextConfiguration startKey = key;
- if (hierarchyMode == HierarchyMode.EXHAUSTIVE) {
- while (startKey.getParent() != null) {
- startKey = startKey.getParent();
- }
- }
-
- List If the cache contains more than {@code Integer.MAX_VALUE} elements,
- * this method returns {@code Integer.MAX_VALUE}.
+ * this method must return {@code Integer.MAX_VALUE}.
*/
- public int size() {
- return this.contextMap.size();
- }
+ int size();
/**
* Determine the number of parent contexts currently tracked within the cache.
*/
- public int getParentContextCount() {
- return this.hierarchyMap.size();
- }
+ int getParentContextCount();
/**
- * Generate a text string containing the statistics for this cache.
- * Specifically, the returned string contains the {@linkplain #size},
- * {@linkplain #getHitCount() hit count}, {@linkplain #getMissCount() miss count},
- * and {@linkplain #getParentContextCount() parent context count}.
- * @return the statistics for this cache
+ * Get the overall hit count for this cache.
+ * A hit is any access to the cache that returns a non-null
+ * context for the queried key.
+ */
+ int getHitCount();
+
+ /**
+ * Get the overall miss count for this cache.
+ * A miss is any access to the cache that returns a {@code null}
+ * context for the queried key.
+ */
+ int getMissCount();
+
+ /**
+ * Reset all state maintained by this cache including statistics.
+ * @see #clear()
+ * @see #clearStatistics()
+ */
+ void reset();
+
+ /**
+ * Clear all contexts from the cache, clearing context hierarchy information as well.
+ */
+ void clear();
+
+ /**
+ * Clear hit and miss count statistics for the cache (i.e., reset counters to zero).
+ */
+ void clearStatistics();
+
+ /**
+ * Generate a text string containing the implementation type of this
+ * cache and its statistics.
+ * The value returned by this method will be used primarily for
+ * logging purposes.
+ * Specifically, the returned string should contain the name of the
+ * concrete {@code ContextCache} implementation, the {@linkplain #size},
+ * {@linkplain #getParentContextCount() parent context count},
+ * {@linkplain #getHitCount() hit count}, {@linkplain #getMissCount()
+ * miss count}, and any other information useful in monitoring the
+ * state of this cache.
+ * @return a string representation of this cache, including statistics
*/
@Override
- public String toString() {
- return new ToStringCreator(this)
- .append("size", size())
- .append("hitCount", getHitCount())
- .append("missCount", getMissCount())
- .append("parentContextCount", getParentContextCount())
- .toString();
- }
+ abstract String toString();
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/DefaultCacheAwareContextLoaderDelegate.java b/spring-test/src/main/java/org/springframework/test/context/DefaultCacheAwareContextLoaderDelegate.java
index a13e347c2c..cfa482c321 100644
--- a/spring-test/src/main/java/org/springframework/test/context/DefaultCacheAwareContextLoaderDelegate.java
+++ b/spring-test/src/main/java/org/springframework/test/context/DefaultCacheAwareContextLoaderDelegate.java
@@ -21,16 +21,12 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
+import org.springframework.test.context.support.DefaultContextCache;
import org.springframework.util.Assert;
/**
* Default implementation of the {@link CacheAwareContextLoaderDelegate} interface.
*
- * Although {@code DefaultCacheAwareContextLoaderDelegate} was first introduced
- * in Spring Framework 4.1, the initial implementation of this class was extracted
- * from the existing code base for {@code CacheAwareContextLoaderDelegate} when
- * {@code CacheAwareContextLoaderDelegate} was converted into an interface.
- *
* @author Sam Brannen
* @since 4.1
*/
@@ -47,21 +43,21 @@ class DefaultCacheAwareContextLoaderDelegate implements CacheAwareContextLoaderD
* and reused for all subsequent tests that declare the same unique
* context configuration within the same JVM process.
*/
- static final ContextCache defaultContextCache = new ContextCache();
+ static final ContextCache defaultContextCache = new DefaultContextCache();
private final ContextCache contextCache;
/**
- * Construct a new {@code DefaultCacheAwareContextLoaderDelegate} that
- * uses the default, static {@code ContextCache}.
+ * Construct a new {@code DefaultCacheAwareContextLoaderDelegate} using
+ * a static {@code DefaultContextCache}.
*/
DefaultCacheAwareContextLoaderDelegate() {
this(defaultContextCache);
}
/**
- * Construct a new {@code DefaultCacheAwareContextLoaderDelegate} with
+ * Construct a new {@code DefaultCacheAwareContextLoaderDelegate} using
* the supplied {@code ContextCache}.
*/
DefaultCacheAwareContextLoaderDelegate(ContextCache contextCache) {
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/DefaultContextCache.java b/spring-test/src/main/java/org/springframework/test/context/support/DefaultContextCache.java
new file mode 100644
index 0000000000..17ebcb82b1
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/support/DefaultContextCache.java
@@ -0,0 +1,252 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.core.style.ToStringCreator;
+import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
+import org.springframework.test.context.ContextCache;
+import org.springframework.test.context.MergedContextConfiguration;
+import org.springframework.util.Assert;
+import org.springframework.util.ConcurrentReferenceHashMap;
+
+/**
+ * Default implementation of the {@link ContextCache} API.
+ *
+ * Uses Spring's {@link ConcurrentReferenceHashMap} to store
+ * {@linkplain java.lang.ref.SoftReference soft references} to cached
+ * contexts and {@code MergedContextConfiguration} instances.
+ *
+ * @author Sam Brannen
+ * @author Juergen Hoeller
+ * @since 2.5
+ * @see ConcurrentReferenceHashMap
+ */
+public class DefaultContextCache implements ContextCache {
+
+ /**
+ * Map of context keys to Spring {@code ApplicationContext} instances.
+ */
+ private final Map