From 98ad8633bd22e28e4705ee72e73d6d862fe6e8d2 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 1 Aug 2011 10:20:49 +0000 Subject: [PATCH] SPR-8524 Add flag to AbstractCachingViewResolver to suppress subsequent resolution of unresolved view names. --- .../view/AbstractCachingViewResolver.java | 26 +++++++++++++- .../web/servlet/view/ViewResolverTests.java | 35 +++++++++++++++++-- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/AbstractCachingViewResolver.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/AbstractCachingViewResolver.java index 0ed7464c48b..99d54547c5f 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/AbstractCachingViewResolver.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/view/AbstractCachingViewResolver.java @@ -42,6 +42,9 @@ public abstract class AbstractCachingViewResolver extends WebApplicationObjectSu /** Whether we should cache views, once resolved */ private boolean cache = true; + /** Whether we should attempt to resolve views again if unresolved once */ + private boolean cacheUnresolved = false; + /** Map from view key to View instance */ private final Map viewCache = new HashMap(); @@ -62,7 +65,27 @@ public abstract class AbstractCachingViewResolver extends WebApplicationObjectSu public boolean isCache() { return this.cache; } + + /** + * Whether a view name once resolved to {@code null} should be cached and + * automatically resolved to {@code null} subsequently. + *

Default is "false": unresolved view names are not cached. + *

Of specific interest is the ability for some AbstractUrlBasedView + * implementations (Freemarker, Velocity, Tiles) to check if an underlying + * resource exists via {@link AbstractUrlBasedView#checkResource(Locale)}. + * With this flag set to "false", an underlying resource that re-appears + * is noticed and used. With the flag set to "true", one check is made only. + */ + public void setCacheUnresolved(boolean cacheUnresolved) { + this.cacheUnresolved = cacheUnresolved; + } + /** + * Return if caching of unresolved views is enabled. + */ + public boolean isCacheUnresolved() { + return cacheUnresolved; + } public View resolveViewName(String viewName, Locale locale) throws Exception { if (!isCache()) { @@ -72,7 +95,8 @@ public abstract class AbstractCachingViewResolver extends WebApplicationObjectSu Object cacheKey = getCacheKey(viewName, locale); synchronized (this.viewCache) { View view = this.viewCache.get(cacheKey); - if (view == null) { + boolean isCached = this.cacheUnresolved && this.viewCache.containsKey(cacheKey); + if (view == null && !isCached) { // Ask the subclass to create the View object. view = createView(viewName, locale); this.viewCache.put(cacheKey, view); diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/ViewResolverTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/ViewResolverTests.java index 3e5db14a59b..b14ef645265 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/ViewResolverTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/view/ViewResolverTests.java @@ -16,10 +16,18 @@ package org.springframework.web.servlet.view; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Properties; +import java.util.concurrent.atomic.AtomicInteger; + import javax.servlet.RequestDispatcher; import javax.servlet.ServletContext; import javax.servlet.ServletException; @@ -30,9 +38,7 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.jsp.jstl.core.Config; import javax.servlet.jsp.jstl.fmt.LocalizationContext; -import static org.junit.Assert.*; import org.junit.Test; - import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.PropertyValue; import org.springframework.beans.TestBean; @@ -497,6 +503,31 @@ public class ViewResolverTests { } } + @Test + public void testCacheUnresolved() throws Exception { + final AtomicInteger count = new AtomicInteger(); + AbstractCachingViewResolver viewResolver = new AbstractCachingViewResolver() { + @Override + protected View loadView(String viewName, Locale locale) throws Exception { + count.incrementAndGet(); + return null; + } + }; + + viewResolver.setCacheUnresolved(false); + + viewResolver.resolveViewName("view", Locale.getDefault()); + viewResolver.resolveViewName("view", Locale.getDefault()); + + assertEquals(2, count.intValue()); + + viewResolver.setCacheUnresolved(true); + + viewResolver.resolveViewName("view", Locale.getDefault()); + viewResolver.resolveViewName("view", Locale.getDefault()); + + assertEquals(2, count.intValue()); + } public static class TestView extends InternalResourceView {