001: /*
002: * Copyright 2002-2007 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: * http://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:
017: package org.springframework.web.servlet.view;
018:
019: import java.util.HashMap;
020: import java.util.Locale;
021: import java.util.Map;
022:
023: import org.springframework.web.context.support.WebApplicationObjectSupport;
024: import org.springframework.web.servlet.View;
025: import org.springframework.web.servlet.ViewResolver;
026:
027: /**
028: * Convenient base class for {@link org.springframework.web.servlet.ViewResolver}
029: * implementations. Caches {@link org.springframework.web.servlet.View} objects
030: * once resolved: This means that view resolution won't be a performance problem,
031: * no matter how costly initial view retrieval is.
032: *
033: * <p>Subclasses need to implement the {@link #loadView} template method,
034: * building the View object for a specific view name and locale.
035: *
036: * @author Rod Johnson
037: * @author Juergen Hoeller
038: * @see #loadView
039: */
040: public abstract class AbstractCachingViewResolver extends
041: WebApplicationObjectSupport implements ViewResolver {
042:
043: /** Whether we should cache views, once resolved */
044: private boolean cache = true;
045:
046: /** Map from view name to View instance */
047: private final Map viewCache = new HashMap();
048:
049: /**
050: * Enable or disable caching.
051: * <p>Default is "true": caching is enabled.
052: * Disable this only for debugging and development.
053: * <p><b>Warning: Disabling caching can severely impact performance.</b>
054: */
055: public void setCache(boolean cache) {
056: this .cache = cache;
057: }
058:
059: /**
060: * Return if caching is enabled.
061: */
062: public boolean isCache() {
063: return this .cache;
064: }
065:
066: public View resolveViewName(String viewName, Locale locale)
067: throws Exception {
068: if (!isCache()) {
069: logger
070: .warn("View caching is SWITCHED OFF -- DEVELOPMENT SETTING ONLY: This can severely impair performance");
071: return createView(viewName, locale);
072: } else {
073: Object cacheKey = getCacheKey(viewName, locale);
074: synchronized (this .viewCache) {
075: View view = (View) this .viewCache.get(cacheKey);
076: if (view == null) {
077: // Ask the subclass to create the View object.
078: view = createView(viewName, locale);
079: this .viewCache.put(cacheKey, view);
080: if (logger.isDebugEnabled()) {
081: logger.debug("Cached view [" + cacheKey + "]");
082: }
083: }
084: return view;
085: }
086: }
087: }
088:
089: /**
090: * Return the cache key for the given view name and the given locale.
091: * <p>Default is a String consisting of view name and locale suffix.
092: * Can be overridden in subclasses.
093: * <p>Needs to respect the locale in general, as a different locale can
094: * lead to a different view resource.
095: */
096: protected Object getCacheKey(String viewName, Locale locale) {
097: return viewName + "_" + locale;
098: }
099:
100: /**
101: * Provides functionality to clear the cache for a certain view.
102: * <p>This can be handy in case developer are able to modify views
103: * (e.g. Velocity templates) at runtime after which you'd need to
104: * clear the cache for the specified view.
105: * @param viewName the view name for which the cached view object
106: * (if any) needs to be removed
107: * @param locale the locale for which the view object should be removed
108: */
109: public void removeFromCache(String viewName, Locale locale) {
110: if (!this .cache) {
111: logger
112: .warn("View caching is SWITCHED OFF -- removal not necessary");
113: } else {
114: Object cacheKey = getCacheKey(viewName, locale);
115: Object cachedView = null;
116: synchronized (this .viewCache) {
117: cachedView = this .viewCache.remove(cacheKey);
118: }
119: if (cachedView == null) {
120: // Some debug output might be useful...
121: if (logger.isDebugEnabled()) {
122: logger.debug("No cached instance for view '"
123: + cacheKey + "' was found");
124: }
125: } else {
126: if (logger.isDebugEnabled()) {
127: logger.debug("Cache for view " + cacheKey
128: + " has been cleared");
129: }
130: }
131: }
132: }
133:
134: /**
135: * Clear the entire view cache, removing all cached view objects.
136: * Subsequent resolve calls will lead to recreation of demanded view objects.
137: */
138: public void clearCache() {
139: logger.debug("Clearing entire view cache");
140: synchronized (this .viewCache) {
141: this .viewCache.clear();
142: }
143: }
144:
145: /**
146: * Create the actual View object.
147: * <p>The default implementation delegates to {@link #loadView}.
148: * This can be overridden to resolve certain view names in a special fashion,
149: * before delegating to the actual <code>loadView</code> implementation
150: * provided by the subclass.
151: * @param viewName the name of the view to retrieve
152: * @param locale the Locale to retrieve the view for
153: * @return the View instance, or <code>null</code> if not found
154: * (optional, to allow for ViewResolver chaining)
155: * @throws Exception if the view couldn't be resolved
156: * @see #loadView
157: */
158: protected View createView(String viewName, Locale locale)
159: throws Exception {
160: return loadView(viewName, locale);
161: }
162:
163: /**
164: * Subclasses must implement this method, building a View object
165: * for the specified view. The returned View objects will be
166: * cached by this ViewResolver base class.
167: * <p>Subclasses are not forced to support internationalization:
168: * A subclass that does not may simply ignore the locale parameter.
169: * @param viewName the name of the view to retrieve
170: * @param locale the Locale to retrieve the view for
171: * @return the View instance, or <code>null</code> if not found
172: * (optional, to allow for ViewResolver chaining)
173: * @throws Exception if the view couldn't be resolved
174: * @see #resolveViewName
175: */
176: protected abstract View loadView(String viewName, Locale locale)
177: throws Exception;
178:
179: }
|