Velocity/FreeMarker/TilesViewResolver only return a view if the target resource exists now

This commit is contained in:
Juergen Hoeller
2009-05-13 14:22:03 +00:00
parent b0ed6d5685
commit 47992ea07c
16 changed files with 211 additions and 154 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2009 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.
@@ -66,7 +66,6 @@ public abstract class AbstractCachingViewResolver extends WebApplicationObjectSu
public View resolveViewName(String viewName, Locale locale) throws Exception {
if (!isCache()) {
logger.warn("View caching is SWITCHED OFF -- DEVELOPMENT SETTING ONLY: This can severely impair performance");
return createView(viewName, locale);
}
else {
@@ -112,7 +111,7 @@ public abstract class AbstractCachingViewResolver extends WebApplicationObjectSu
}
else {
Object cacheKey = getCacheKey(viewName, locale);
Object cachedView = null;
Object cachedView;
synchronized (this.viewCache) {
cachedView = this.viewCache.remove(cacheKey);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2009 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.
@@ -75,6 +75,16 @@ public abstract class AbstractUrlBasedView extends AbstractView implements Initi
return true;
}
/**
* Check whether the underlying resource that the configured URL points to
* actually exists.
* @return <code>true</code> if the resource exists (or is assumed to exist);
* <code>false</code> if we know that it does not exist
* @throws Exception if the resource exists but is invalid (e.g. could not be parsed)
*/
public boolean checkResource() throws Exception {
return true;
}
@Override
public String toString() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2009 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.
@@ -61,9 +61,12 @@ import org.springframework.web.servlet.View;
* <p>Note: This class does not support localized resolution, i.e. resolving
* a symbolic view name to different resources depending on the current locale.
*
* <p>Note: When chaining ViewResolvers, a UrlBasedViewResolver always needs
* to be last, as it will attempt to resolve any view name, no matter whether
* the underlying resource actually exists.
* <p><b>Note:</b> When chaining ViewResolvers, a UrlBasedViewResolver will check whether
* the {@link AbstractUrlBasedView#checkResource specified resource actually exists}.
* However, with {@link InternalResourceView}, it is not generally possible to
* determine the existence of the target resource upfront. In such a scenario,
* a UrlBasedViewResolver will always return View for any given view name;
* as a consequence, it should be configured as the last ViewResolver in the chain.
*
* @author Juergen Hoeller
* @author Rob Harrop
@@ -370,8 +373,7 @@ public class UrlBasedViewResolver extends AbstractCachingViewResolver implements
// Check for special "redirect:" prefix.
if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
return new RedirectView(
redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
return new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
}
// Check for special "forward:" prefix.
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
@@ -415,7 +417,8 @@ public class UrlBasedViewResolver extends AbstractCachingViewResolver implements
@Override
protected View loadView(String viewName, Locale locale) throws Exception {
AbstractUrlBasedView view = buildView(viewName);
return (View) getApplicationContext().getAutowireCapableBeanFactory().initializeBean(view, viewName);
View result = (View) getApplicationContext().getAutowireCapableBeanFactory().initializeBean(view, viewName);
return (view.checkResource() ? result : null);
}
/**

View File

@@ -1,12 +1,12 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Copyright 2002-2009 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.
@@ -22,7 +22,7 @@ import freemarker.ext.jsp.TaglibFactory;
/**
* Interface to be implemented by objects that configure and manage a
* FreeMarker Configuration object in a web environment. Detected and
* used by FreeMarkerView.
* used by {@link FreeMarkerView}.
*
* @author Darren Davison
* @author Rob Harrop

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2006 the original author or authors.
* Copyright 2002-2009 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.
@@ -18,10 +18,10 @@ package org.springframework.web.servlet.view.freemarker;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletContext;
import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.ext.jsp.TaglibFactory;
import freemarker.template.Configuration;
import freemarker.template.TemplateException;
@@ -120,7 +120,7 @@ public class FreeMarkerConfigurer extends FreeMarkerConfigurationFactory
* for the Spring-provided macros, added to the end of the list.
*/
@Override
protected void postProcessTemplateLoaders(List templateLoaders) {
protected void postProcessTemplateLoaders(List<TemplateLoader> templateLoaders) {
templateLoaders.add(new ClassTemplateLoader(FreeMarkerConfigurer.class, ""));
logger.info("ClassTemplateLoader for Spring macros added to FreeMarker configuration");
}

View File

@@ -16,6 +16,7 @@
package org.springframework.web.servlet.view.freemarker;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
@@ -112,7 +113,7 @@ public class FreeMarkerView extends AbstractTemplateView {
/**
* Set the FreeMarker Configuration to be used by this view.
* If this is not set, the default lookup will occur: a single {@link FreeMarkerConfig}
* <p>If this is not set, the default lookup will occur: a single {@link FreeMarkerConfig}
* is expected in the current web application context, with any bean name.
* <strong>Note:</strong> using this method will cause a new instance of {@link TaglibFactory}
* to created for every single {@link FreeMarkerView} instance. This can be quite expensive
@@ -158,8 +159,6 @@ public class FreeMarkerView extends AbstractTemplateView {
throw new BeanInitializationException("Initialization of GenericServlet adapter failed", ex);
}
this.servletContextHashModel = new ServletContextHashModel(servlet, getObjectWrapper());
checkTemplate();
}
/**
@@ -196,12 +195,19 @@ public class FreeMarkerView extends AbstractTemplateView {
* Check that the FreeMarker template used for this view exists and is valid.
* <p>Can be overridden to customize the behavior, for example in case of
* multiple templates to be rendered into a single view.
* @throws ApplicationContextException if the template cannot be found or is invalid
*/
protected void checkTemplate() throws ApplicationContextException {
@Override
public boolean checkResource() throws Exception {
try {
// Check that we can get the template, even if we might subsequently get it again.
getTemplate(getConfiguration().getLocale());
getTemplate(getUrl(), getConfiguration().getLocale());
return true;
}
catch (FileNotFoundException ex) {
if (logger.isDebugEnabled()) {
logger.debug("No FreeMarker view found for URL: " + getUrl());
}
return false;
}
catch (ParseException ex) {
throw new ApplicationContextException(

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2006 the original author or authors.
* Copyright 2002-2009 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.
@@ -19,15 +19,15 @@ package org.springframework.web.servlet.view.freemarker;
import org.springframework.web.servlet.view.AbstractTemplateViewResolver;
/**
* Convenience subclass of UrlBasedViewResolver that supports FreeMarkerView
* (i.e. FreeMarker templates) and custom subclasses of it.
* Convenience subclass of {@link org.springframework.web.servlet.view.UrlBasedViewResolver}
* that supports {@link FreeMarkerView} (i.e. FreeMarker templates) and custom subclasses of it.
*
* <p>The view class for all views generated by this resolver can be specified
* via <code>setViewClass</code>. See UrlBasedViewResolver's javadoc for details.
* via the "viewClass" property. See UrlBasedViewResolver's javadoc for details.
*
* <p><b>Note:</b> When chaining ViewResolvers, a FreeMarkerViewResolver always
* needs to be last, as it will attempt to resolve any view name, no matter
* whether the underlying resource actually exists.
* <p><b>Note:</b> When chaining ViewResolvers, a FreeMarkerViewResolver will
* check for the existence of the specified template resources and only return
* a non-null View object if the template was actually found.
*
* @author Juergen Hoeller
* @since 1.1
@@ -40,18 +40,12 @@ import org.springframework.web.servlet.view.AbstractTemplateViewResolver;
*/
public class FreeMarkerViewResolver extends AbstractTemplateViewResolver {
/**
* Sets default viewClass to <code>requiredViewClass</code>.
* @see #setViewClass
* @see #requiredViewClass
*/
public FreeMarkerViewResolver() {
setViewClass(requiredViewClass());
}
/**
* Requires FreeMarkerView.
* @see FreeMarkerView
* Requires {@link FreeMarkerView}.
*/
@Override
protected Class requiredViewClass() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2009 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.
@@ -18,7 +18,6 @@ package org.springframework.web.servlet.view.tiles2;
import java.util.Enumeration;
import java.util.Properties;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
@@ -49,14 +48,11 @@ import org.springframework.web.context.ServletContextAware;
* for more information about Tiles, which basically is a templating
* mechanism for JSP-based web applications.
*
* <p>The TilesConfigurer simply configures a TilesContainer using a set
* of files containing definitions, to be accessed by {@link TilesView}
* instances.
* <p>The TilesConfigurer simply configures a TilesContainer using a set of files
* containing definitions, to be accessed by {@link TilesView} instances.
*
* <p>TilesViews can be managed by any {@link org.springframework.web.servlet.ViewResolver}.
* For simple convention-based view resolution, consider using
* {@link org.springframework.web.servlet.view.UrlBasedViewResolver} with the
* "viewClass" property set to "org.springframework.web.servlet.view.tiles2.TilesView".
* For simple convention-based view resolution, consider using {@link TilesViewResolver}.
*
* <p>A typical TilesConfigurer bean definition looks as follows:
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2009 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.
@@ -17,7 +17,6 @@
package org.springframework.web.servlet.view.tiles2;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@@ -49,6 +48,12 @@ import org.springframework.web.util.WebUtils;
*/
public class TilesView extends AbstractUrlBasedView {
@Override
public boolean checkResource() throws Exception {
TilesContainer container = TilesAccess.getContainer(getServletContext());
return container.isValidDefinition(getUrl());
}
@Override
protected void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2002-2009 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.web.servlet.view.tiles2;
import org.springframework.web.servlet.view.UrlBasedViewResolver;
/**
* Convenience subclass of {@link org.springframework.web.servlet.view.UrlBasedViewResolver}
* that supports {@link TilesView} (i.e. Tiles definitions) and custom subclasses of it.
*
* <p>The view class for all views generated by this resolver can be specified
* via the "viewClass" property. See UrlBasedViewResolver's javadoc for details.
*
* <p><b>Note:</b> When chaining ViewResolvers, a TilesViewResolver will
* check for the existence of the specified template resources and only return
* a non-null View object if the template was actually found.
*
* @author Juergen Hoeller
* @since 3.0
* @see #setViewClass
* @see #setPrefix
* @see #setSuffix
* @see #setRequestContextAttribute
* @see TilesView
*/
public class TilesViewResolver extends UrlBasedViewResolver {
public TilesViewResolver() {
setViewClass(requiredViewClass());
}
/**
* Requires {@link TilesView}.
*/
@Override
protected Class requiredViewClass() {
return TilesView.class;
}
}

View File

@@ -17,14 +17,13 @@
package org.springframework.web.servlet.view.velocity;
import java.io.StringWriter;
import javax.servlet.http.HttpServletResponse;
import org.apache.velocity.Template;
import org.apache.velocity.context.Context;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.springframework.context.ApplicationContextException;
import org.springframework.core.NestedIOException;
/**
* VelocityLayoutView emulates the functionality offered by Velocity's
@@ -119,19 +118,22 @@ public class VelocityLayoutView extends VelocityToolboxView {
* can be changed which may invalidate any early checking done here.
*/
@Override
protected void checkTemplate() throws ApplicationContextException {
super.checkTemplate();
public boolean checkResource() throws Exception {
if (!super.checkResource()) {
return false;
}
try {
// Check that we can get the template, even if we might subsequently get it again.
getTemplate(this.layoutUrl);
return true;
}
catch (ResourceNotFoundException ex) {
throw new ApplicationContextException("Cannot find Velocity template for URL [" + this.layoutUrl +
throw new NestedIOException("Cannot find Velocity template for URL [" + this.layoutUrl +
"]: Did you specify the correct resource loader path?", ex);
}
catch (Exception ex) {
throw new ApplicationContextException(
throw new NestedIOException(
"Could not load Velocity template for URL [" + this.layoutUrl + "]", ex);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2009 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.
@@ -34,6 +34,7 @@ import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContextException;
import org.springframework.core.NestedIOException;
import org.springframework.web.servlet.support.RequestContextUtils;
import org.springframework.web.servlet.view.AbstractTemplateView;
import org.springframework.web.util.NestedServletException;
@@ -52,10 +53,10 @@ import org.springframework.web.util.NestedServletException;
* view, or <code>null</code> if not needed. VelocityFormatter is part of standard Velocity.
* <li><b>dateToolAttribute</b> (optional, default=null): the name of the
* DateTool helper object to expose in the Velocity context of this view,
* or <code>null</code> if not needed. DateTool is part of Velocity Tools 1.0.
* or <code>null</code> if not needed. DateTool is part of Velocity Tools.
* <li><b>numberToolAttribute</b> (optional, default=null): the name of the
* NumberTool helper object to expose in the Velocity context of this view,
* or <code>null</code> if not needed. NumberTool is part of Velocity Tools 1.1.
* or <code>null</code> if not needed. NumberTool is part of Velocity Tools.
* <li><b>cacheTemplate</b> (optional, default=false): whether or not the Velocity
* template should be cached. It should normally be true in production, but setting
* this to false enables us to modify Velocity templates without restarting the
@@ -67,8 +68,8 @@ import org.springframework.web.util.NestedServletException;
* accessible in the current web application context, with any bean name.
* Alternatively, you can set the VelocityEngine object as bean property.
*
* <p>Note: Spring's VelocityView requires Velocity 1.3 or higher, and optionally
* Velocity Tools 1.0 or higher (depending on the use of DateTool and/or NumberTool).
* <p>Note: Spring 3.0's VelocityView requires Velocity 1.4 or higher, and optionally
* Velocity Tools 1.1 or higher (depending on the use of DateTool and/or NumberTool).
*
* @author Rod Johnson
* @author Juergen Hoeller
@@ -194,7 +195,7 @@ public class VelocityView extends AbstractTemplateView {
/**
* Set the VelocityEngine to be used by this view.
* If this is not set, the default lookup will occur: A single VelocityConfig
* <p>If this is not set, the default lookup will occur: A single VelocityConfig
* is expected in the current web application context, with any bean name.
* @see VelocityConfig
*/
@@ -222,8 +223,6 @@ public class VelocityView extends AbstractTemplateView {
// No explicit VelocityEngine: try to autodetect one.
setVelocityEngine(autodetectVelocityEngine());
}
checkTemplate();
}
/**
@@ -252,19 +251,22 @@ public class VelocityView extends AbstractTemplateView {
* Check that the Velocity template used for this view exists and is valid.
* <p>Can be overridden to customize the behavior, for example in case of
* multiple templates to be rendered into a single view.
* @throws ApplicationContextException if the template cannot be found or is invalid
*/
protected void checkTemplate() throws ApplicationContextException {
@Override
public boolean checkResource() throws Exception {
try {
// Check that we can get the template, even if we might subsequently get it again.
this.template = getTemplate();
this.template = getTemplate(getUrl());
return true;
}
catch (ResourceNotFoundException ex) {
throw new ApplicationContextException("Cannot find Velocity template for URL [" + getUrl() +
"]: Did you specify the correct resource loader path?", ex);
if (logger.isDebugEnabled()) {
logger.debug("No Velocity view found for URL: " + getUrl());
}
return false;
}
catch (Exception ex) {
throw new ApplicationContextException(
throw new NestedIOException(
"Could not load Velocity template for URL [" + getUrl() + "]", ex);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2009 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.
@@ -21,14 +21,14 @@ import org.springframework.web.servlet.view.AbstractUrlBasedView;
/**
* Convenience subclass of {@link org.springframework.web.servlet.view.UrlBasedViewResolver}
* that supports VelocityView (i.e. Velocity templates) and custom subclasses of it.
* that supports {@link VelocityView} (i.e. Velocity templates) and custom subclasses of it.
*
* <p>The view class for all views generated by this resolver can be specified
* via <code>setViewClass</code>. See UrlBasedViewResolver's javadoc for details.
* via the "viewClass" property. See UrlBasedViewResolver's javadoc for details.
*
* <p><b>Note:</b> When chaining ViewResolvers, a VelocityViewResolver always needs
* to be last, as it will attempt to resolve any view name, no matter whether
* the underlying resource actually exists.
* <p><b>Note:</b> When chaining ViewResolvers, a VelocityViewResolver will
* check for the existence of the specified template resources and only return
* a non-null View object if the template was actually found.
*
* @author Juergen Hoeller
* @since 13.12.2003
@@ -50,24 +50,19 @@ public class VelocityViewResolver extends AbstractTemplateViewResolver {
private String toolboxConfigLocation;
/**
* Sets default viewClass to <code>requiredViewClass</code>.
* @see #setViewClass
* @see #requiredViewClass
*/
public VelocityViewResolver() {
setViewClass(requiredViewClass());
}
/**
* Requires VelocityView.
* @see VelocityView
* Requires {@link VelocityView}.
*/
@Override
protected Class requiredViewClass() {
return VelocityView.class;
}
/**
* Set the name of the DateTool helper object to expose in the Velocity context
* of this view, or <code>null</code> if not needed. DateTool is part of Velocity Tools 1.0.