Add Spring Framework integration with Tiles 3

Tiles 3 has modified packages and classes as well as additional
dependencies, notably "tiles-request-api", which is a request/response
abstraction independent of Servlet and JSP APIs.

In order to have both Tiles 2 and Tiles 3 integrations, the source for
the Tiles 3 integration is in a separate project spring-webmvc-tiles3.
The build process merges the compiled Tiles 3 integration classes into
the spring-webmvc module so in effect it contains both the Tiles 2 and
the Tiles 3 integrations.

This change originated as a pull request at spring-framework-issues:
https://github.com/SpringSource/spring-framework-issues/pull/30

And was additionally updated:
1f64be4aa5

Issue: SPR-8825
This commit is contained in:
Rossen Stoyanchev 2012-11-20 11:57:41 -05:00
parent 63ca14c33e
commit 2a11007a04
18 changed files with 1151 additions and 5 deletions

View File

@ -396,7 +396,7 @@ project('spring-web') {
compile("javax.el:el-api:1.0", optional)
compile("javax.faces:jsf-api:1.2_08", optional)
compile("javax.portlet:portlet-api:2.0", provided)
compile("org.apache.tomcat:tomcat-servlet-api:7.0.8", provided) // servlet-api 3.0
compile("org.apache.tomcat:tomcat-servlet-api:7.0.32", provided) // servlet-api 3.0
compile("javax.servlet.jsp:jsp-api:2.1", provided)
compile("javax.xml.soap:saaj-api:1.3", provided)
compile("axis:axis:1.4", optional)
@ -482,7 +482,7 @@ project('spring-webmvc') {
exclude group: 'log4j', module: 'log4j'
}
compile("javax.servlet:jstl:1.1.2", provided)
compile("org.apache.tomcat:tomcat-servlet-api:7.0.8", provided) // servlet-api 3.0
compile("org.apache.tomcat:tomcat-servlet-api:7.0.32", provided) // servlet-api 3.0
testCompile project(":spring-aop")
testCompile("org.slf4j:slf4j-log4j12:1.6.1") {
exclude group: 'log4j', module: 'log4j'
@ -503,6 +503,27 @@ project('spring-webmvc') {
sourceSets.main.resources.srcDirs += 'src/main/java'
}
project('spring-webmvc-tiles3') {
description = 'Spring Framework Tiles3 Integration'
apply from: 'tiles3.gradle'
dependencies {
compile project(":spring-context")
compile project(":spring-webmvc").sourceSets.main.output
compile("javax.el:el-api:1.0", provided)
compile("javax.servlet:jstl:1.1.2", provided)
compile("javax.servlet.jsp:jsp-api:2.1", provided)
compile("org.apache.tiles:tiles-request-api:1.0.1", optional)
compile("org.apache.tiles:tiles-request-servlet-wildcard:1.0.1", optional)
compile("org.apache.tiles:tiles-api:3.0.1", optional)
compile("org.apache.tiles:tiles-core:3.0.1", optional)
compile("org.apache.tiles:tiles-servlet:3.0.1", optional)
compile("org.apache.tiles:tiles-jsp:3.0.1", optional)
compile("org.apache.tiles:tiles-el:3.0.1", optional)
compile("org.apache.tomcat:tomcat-servlet-api:7.0.32", provided) // servlet-api 3.0
testCompile project(":spring-web").sourceSets*.output // mock request & response
}
}
project('spring-webmvc-portlet') {
description = 'Spring Web Portlet'
dependencies {
@ -556,7 +577,7 @@ project('spring-test-mvc') {
compile project(":spring-context")
compile project(":spring-webmvc")
compile project(":spring-test").sourceSets.main.output
compile("org.apache.tomcat:tomcat-servlet-api:7.0.8", provided)
compile("org.apache.tomcat:tomcat-servlet-api:7.0.32", provided)
compile("org.hamcrest:hamcrest-core:1.3", optional)
compile("com.jayway.jsonpath:json-path:0.8.1", optional)
compile("xmlunit:xmlunit:1.2", optional)

View File

@ -20,3 +20,4 @@ include 'spring-tx'
include 'spring-web'
include 'spring-webmvc'
include 'spring-webmvc-portlet'
include 'spring-webmvc-tiles3'

View File

@ -0,0 +1,63 @@
/*
* Copyright 2002-2012 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.tiles3;
import org.apache.tiles.TilesException;
import org.apache.tiles.preparer.ViewPreparer;
import org.apache.tiles.preparer.factory.PreparerFactory;
import org.apache.tiles.request.Request;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.view.tiles2.SpringBeanPreparerFactory;
/**
* Abstract implementation of the Tiles3 {@link org.apache.tiles.preparer.PreparerFactory}
* interface, obtaining the current Spring WebApplicationContext and delegating to
* {@link #getPreparer(String, org.springframework.web.context.WebApplicationContext)}.
*
* @author Juergen Hoeller
* @since 3.2
* @see #getPreparer(String, org.springframework.web.context.WebApplicationContext)
* @see SimpleSpringPreparerFactory
* @see SpringBeanPreparerFactory
*/
public abstract class AbstractSpringPreparerFactory implements PreparerFactory {
public ViewPreparer getPreparer(String name, Request context) {
WebApplicationContext webApplicationContext = (WebApplicationContext) context.getContext("request").get(
DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE);
if (webApplicationContext == null) {
webApplicationContext = (WebApplicationContext) context.getContext("application").get(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
if (webApplicationContext == null) {
throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");
}
}
return getPreparer(name, webApplicationContext);
}
/**
* Obtain a preparer instance for the given preparer name,
* based on the given Spring WebApplicationContext.
* @param name the name of the preparer
* @param context the current Spring WebApplicationContext
* @return the preparer instance
* @throws TilesException in case of failure
*/
protected abstract ViewPreparer getPreparer(String name, WebApplicationContext context) throws TilesException;
}

View File

@ -0,0 +1,71 @@
/*
* Copyright 2002-2012 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.tiles3;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.tiles.TilesException;
import org.apache.tiles.preparer.PreparerException;
import org.apache.tiles.preparer.ViewPreparer;
import org.apache.tiles.preparer.factory.NoSuchPreparerException;
import org.springframework.web.context.WebApplicationContext;
/**
* Tiles2 {@link org.apache.tiles.preparer.PreparerFactory} implementation
* that expects preparer class names and builds preparer instances for those,
* creating them through the Spring ApplicationContext in order to apply
* Spring container callbacks and configured Spring BeanPostProcessors.
*
* @author Juergen Hoeller
* @since 3.2
* @see SpringBeanPreparerFactory
*/
public class SimpleSpringPreparerFactory extends AbstractSpringPreparerFactory {
/** Cache of shared ViewPreparer instances: bean name --> bean instance */
private final Map<String, ViewPreparer> sharedPreparers = new ConcurrentHashMap<String, ViewPreparer>();
@Override
protected ViewPreparer getPreparer(String name, WebApplicationContext context) throws TilesException {
// Quick check on the concurrent map first, with minimal locking.
ViewPreparer preparer = this.sharedPreparers.get(name);
if (preparer == null) {
synchronized (this.sharedPreparers) {
preparer = this.sharedPreparers.get(name);
if (preparer == null) {
try {
Class<?> beanClass = context.getClassLoader().loadClass(name);
if (!ViewPreparer.class.isAssignableFrom(beanClass)) {
throw new PreparerException(
"Invalid preparer class [" + name + "]: does not implement ViewPreparer interface");
}
preparer = (ViewPreparer) context.getAutowireCapableBeanFactory().createBean(beanClass);
this.sharedPreparers.put(name, preparer);
}
catch (ClassNotFoundException ex) {
throw new NoSuchPreparerException("Preparer class [" + name + "] not found", ex);
}
}
}
}
return preparer;
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2002-2012 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.tiles3;
import org.apache.tiles.TilesException;
import org.apache.tiles.preparer.ViewPreparer;
import org.springframework.web.context.WebApplicationContext;
/**
* Tiles3 {@link org.apache.tiles.preparer.PreparerFactory} implementation
* that expects preparer bean names and obtains preparer beans from the
* Spring ApplicationContext. The full bean creation process will be in
* the control of the Spring application context in this case, allowing
* for the use of scoped beans etc.
*
* @author Juergen Hoeller
* @since 3.2
* @see SimpleSpringPreparerFactory
*/
public class SpringBeanPreparerFactory extends AbstractSpringPreparerFactory {
@Override
protected ViewPreparer getPreparer(String name, WebApplicationContext context) throws TilesException {
return context.getBean(name, ViewPreparer.class);
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright 2002-2012 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.tiles3;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import org.apache.tiles.locale.impl.DefaultLocaleResolver;
import org.apache.tiles.request.Request;
import org.apache.tiles.request.servlet.NotAServletEnvironmentException;
import org.apache.tiles.request.servlet.ServletUtil;
import org.springframework.web.servlet.support.RequestContextUtils;
/**
* Tiles LocaleResolver adapter that delegates to a Spring
* {@link org.springframework.web.servlet.LocaleResolver}, exposing the
* DispatcherServlet-managed locale.
*
* <p>This adapter gets automatically registered by {@link TilesConfigurer}.
*
* @author Nicolas Le Bas
* @since 3.2
* @see org.apache.tiles.definition.UrlDefinitionsFactory#LOCALE_RESOLVER_IMPL_PROPERTY
*/
public class SpringLocaleResolver extends DefaultLocaleResolver {
@Override
public Locale resolveLocale(Request request) {
try {
HttpServletRequest servletRequest = ServletUtil.getServletRequest(request).getRequest();
if (servletRequest != null) {
return RequestContextUtils.getLocale(servletRequest);
}
}
catch (NotAServletEnvironmentException e) {
// Ignore
}
return super.resolveLocale(request);
}
}

View File

@ -0,0 +1,419 @@
/*
* Copyright 2002-2012 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.tiles3;
import java.util.LinkedList;
import java.util.List;
import javax.el.ArrayELResolver;
import javax.el.BeanELResolver;
import javax.el.CompositeELResolver;
import javax.el.ListELResolver;
import javax.el.MapELResolver;
import javax.el.ResourceBundleELResolver;
import javax.servlet.ServletContext;
import javax.servlet.jsp.JspFactory;
import org.apache.tiles.TilesContainer;
import org.apache.tiles.TilesException;
import org.apache.tiles.access.TilesAccess;
import org.apache.tiles.definition.DefinitionsFactory;
import org.apache.tiles.definition.DefinitionsReader;
import org.apache.tiles.definition.dao.BaseLocaleUrlDefinitionDAO;
import org.apache.tiles.definition.dao.CachingLocaleUrlDefinitionDAO;
import org.apache.tiles.definition.digester.DigesterDefinitionsReader;
import org.apache.tiles.el.ELAttributeEvaluator;
import org.apache.tiles.el.TilesContextBeanELResolver;
import org.apache.tiles.el.TilesContextELResolver;
import org.apache.tiles.evaluator.AttributeEvaluator;
import org.apache.tiles.evaluator.AttributeEvaluatorFactory;
import org.apache.tiles.evaluator.BasicAttributeEvaluatorFactory;
import org.apache.tiles.evaluator.impl.DirectAttributeEvaluator;
import org.apache.tiles.factory.AbstractTilesContainerFactory;
import org.apache.tiles.factory.BasicTilesContainerFactory;
import org.apache.tiles.impl.BasicTilesContainer;
import org.apache.tiles.impl.mgmt.CachingTilesContainer;
import org.apache.tiles.locale.LocaleResolver;
import org.apache.tiles.preparer.factory.PreparerFactory;
import org.apache.tiles.request.ApplicationContext;
import org.apache.tiles.request.ApplicationResource;
import org.apache.tiles.request.servlet.wildcard.WildcardServletApplicationContext;
import org.apache.tiles.startup.DefaultTilesInitializer;
import org.apache.tiles.startup.TilesInitializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.ClassUtils;
import org.springframework.web.context.ServletContextAware;
/**
* Helper class to configure Tiles 3.x for the Spring Framework. See
* <a href="http://tiles.apache.org">http://tiles.apache.org</a>
* for more information about Tiles, which basically is a templating mechanism
* for web applications using JSPs and other template engines.
*
* <p>The TilesConfigurer simply configures a TilesContainer using a set of files
* containing definitions, to be accessed by {@link TilesView} instances. This is a
* Spring-based alternative (for usage in Spring configuration) to the Tiles-provided
* {@link org.apache.tiles.web.startup.TilesListener} (for usage in <code>web.xml</code>).
*
* <p>TilesViews can be managed by any {@link org.springframework.web.servlet.ViewResolver}.
* For simple convention-based view resolution, consider using {@link TilesViewResolver}.
*
* <p>A typical TilesConfigurer bean definition looks as follows:
*
* <pre>
* &lt;bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
* &lt;property name="definitions">
* &lt;list>
* &lt;value>/WEB-INF/defs/general.xml&lt;/value>
* &lt;value>/WEB-INF/defs/widgets.xml&lt;/value>
* &lt;value>/WEB-INF/defs/administrator.xml&lt;/value>
* &lt;value>/WEB-INF/defs/customer.xml&lt;/value>
* &lt;value>/WEB-INF/defs/templates.xml&lt;/value>
* &lt;/list>
* &lt;/property>
* &lt;/bean>
* </pre>
*
* The values in the list are the actual Tiles XML files containing the definitions.
* If the list is not specified, the default is {@code "/WEB-INF/tiles.xml"}.
*
* @author mick semb wever
* @author Rossen Stoyanchev
* @since 3.2
*/
public class TilesConfigurer implements ServletContextAware, InitializingBean, DisposableBean {
private static final boolean tilesElPresent = // requires JSP 2.1 as well as Tiles EL module
ClassUtils.isPresent("javax.servlet.jsp.JspApplicationContext", TilesConfigurer.class.getClassLoader()) &&
ClassUtils.isPresent("org.apache.tiles.el.ELAttributeEvaluator", TilesConfigurer.class.getClassLoader());
protected final Logger logger = LoggerFactory.getLogger(getClass());
private TilesInitializer tilesInitializer;
private boolean completeAutoload = false;
private String[] definitions;
private boolean checkRefresh = false;
private boolean validateDefinitions = true;
private Class<? extends DefinitionsFactory> definitionsFactoryClass;
private Class<? extends PreparerFactory> preparerFactoryClass;
private boolean useMutableTilesContainer = false;
private ServletContext servletContext;
public TilesConfigurer() {
}
/**
* Configure Tiles using a custom TilesInitializer, typically specified as an inner bean.
* <p>Default is a variant of {@link org.apache.tiles.startup.DefaultTilesInitializer},
* respecting the "definitions", "preparerFactoryClass" etc properties on this configurer.
* <p><b>NOTE: Specifying a custom TilesInitializer effectively disables all other bean
* properties on this configurer.</b> The entire initialization procedure is then left
* to the TilesInitializer as specified.
*/
public void setTilesInitializer(TilesInitializer tilesInitializer) {
this.tilesInitializer = tilesInitializer;
}
/**
* Specify whether to apply Tiles 3.0's "complete-autoload" configuration.
* <p>See {@link org.apache.tiles.extras.complete.CompleteAutoloadTilesContainerFactory}
* for details on the complete-autoload mode.
* <p><b>NOTE: Specifying the complete-autoload mode effectively disables all other bean
* properties on this configurer.</b> The entire initialization procedure is then left
* to {@link org.apache.tiles.extras.complete.CompleteAutoloadTilesInitializer}.
* @see org.apache.tiles.extras.complete.CompleteAutoloadTilesContainerFactory
* @see org.apache.tiles.extras.complete.CompleteAutoloadTilesInitializer
*/
public void setCompleteAutoload(boolean completeAutoload) {
if (completeAutoload) {
try {
Class<?> clazz = getClass().getClassLoader().loadClass(
"org.apache.tiles.extras.complete.CompleteAutoloadTilesInitializer");
this.tilesInitializer = (TilesInitializer) clazz.newInstance();
}
catch (Exception ex) {
throw new IllegalStateException("Tiles-Extras 3.0 not available", ex);
}
} else {
this.tilesInitializer = null;
}
this.completeAutoload = completeAutoload;
}
/**
* Set the Tiles definitions, i.e. the list of files containing the definitions.
* Default is "/WEB-INF/tiles.xml".
*/
public void setDefinitions(String[] definitions) {
this.definitions = definitions;
}
/**
* Set whether to check Tiles definition files for a refresh at runtime.
* Default is "false".
*/
public void setCheckRefresh(boolean checkRefresh) {
this.checkRefresh = checkRefresh;
}
/**
* Set whether to validate the Tiles XML definitions. Default is "true".
*/
public void setValidateDefinitions(boolean validateDefinitions) {
this.validateDefinitions = validateDefinitions;
}
/**
* Set the {@link org.apache.tiles.definition.DefinitionsFactory} implementation to use.
* Default is {@link org.apache.tiles.definition.UrlDefinitionsFactory},
* operating on definition resource URLs.
* <p>Specify a custom DefinitionsFactory, e.g. a UrlDefinitionsFactory subclass,
* to customize the creation of Tiles Definition objects. Note that such a
* DefinitionsFactory has to be able to handle {@link java.net.URL} source objects,
* unless you configure a different TilesContainerFactory.
*/
public void setDefinitionsFactoryClass(Class<? extends DefinitionsFactory> definitionsFactoryClass) {
this.definitionsFactoryClass = definitionsFactoryClass;
}
/**
* Set the {@link org.apache.tiles.preparer.PreparerFactory} implementation to use.
* Default is {@link org.apache.tiles.preparer.BasicPreparerFactory}, creating
* shared instances for specified preparer classes.
* <p>Specify {@link SimpleSpringPreparerFactory} to autowire
* {@link org.apache.tiles.preparer.ViewPreparer} instances based on specified
* preparer classes, applying Spring's container callbacks as well as applying
* configured Spring BeanPostProcessors. If Spring's context-wide annotation-config
* has been activated, annotations in ViewPreparer classes will be automatically
* detected and applied.
* <p>Specify {@link SpringBeanPreparerFactory} to operate on specified preparer
* <i>names</i> instead of classes, obtaining the corresponding Spring bean from
* the DispatcherServlet's application context. The full bean creation process
* will be in the control of the Spring application context in this case,
* allowing for the use of scoped beans etc. Note that you need to define one
* Spring bean definition per preparer name (as used in your Tiles definitions).
* @see SimpleSpringPreparerFactory
* @see SpringBeanPreparerFactory
*/
public void setPreparerFactoryClass(Class<? extends PreparerFactory> preparerFactoryClass) {
this.preparerFactoryClass = preparerFactoryClass;
}
/**
* Set whether to use a MutableTilesContainer (typically the CachingTilesContainer
* implementation) for this application. Default is "false".
* @see org.apache.tiles.mgmt.MutableTilesContainer
* @see org.apache.tiles.mgmt.CachingTilesContainer
*/
public void setUseMutableTilesContainer(boolean useMutableTilesContainer) {
this.useMutableTilesContainer = useMutableTilesContainer;
}
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
/**
* Creates and exposes a TilesContainer for this web application,
* delegating to the TilesInitializer.
* @throws TilesException in case of setup failure
* @see #createTilesInitializer()
*/
public void afterPropertiesSet() throws TilesException {
WildcardServletApplicationContext preliminaryContext =
new WildcardServletApplicationContext(this.servletContext);
if (this.tilesInitializer == null) {
this.tilesInitializer = new SpringTilesInitializer();
}
this.tilesInitializer.initialize(preliminaryContext);
if (this.completeAutoload) {
// We need to do this after initialization simply because we're reusing the
// original CompleteAutoloadTilesInitializer above. We cannot subclass
// CompleteAutoloadTilesInitializer when compiling against Tiles 2.1...
logger.debug("Registering Tiles 3.0 SpringLocaleResolver for complete-autoload setup");
BasicTilesContainer container = (BasicTilesContainer) TilesAccess.getContainer(preliminaryContext);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(container.getDefinitionsFactory());
if (bw.isWritableProperty("localeResolver")) {
bw.setPropertyValue("localeResolver", new SpringLocaleResolver());
}
}
}
/**
* Removes the TilesContainer from this web application.
* @throws TilesException in case of cleanup failure
*/
public void destroy() throws TilesException {
this.tilesInitializer.destroy();
}
private class SpringTilesInitializer extends DefaultTilesInitializer {
@Override
protected AbstractTilesContainerFactory createContainerFactory(ApplicationContext context) {
return new SpringTilesContainerFactory();
}
}
private class SpringTilesContainerFactory extends BasicTilesContainerFactory {
@Override
public TilesContainer createContainer(ApplicationContext context) {
TilesContainer container = super.createContainer(context);
return (useMutableTilesContainer ? new CachingTilesContainer(container) : container);
}
@Override
protected List<ApplicationResource> getSources(ApplicationContext applicationContext) {
if (definitions != null) {
List<ApplicationResource> result = new LinkedList<ApplicationResource>();
for (String definition : definitions) {
result.addAll(applicationContext.getResources(definition));
}
return result;
}
else {
return super.getSources(applicationContext);
}
}
@Override
protected BaseLocaleUrlDefinitionDAO instantiateLocaleDefinitionDao(ApplicationContext applicationContext,
LocaleResolver resolver) {
BaseLocaleUrlDefinitionDAO dao = super.instantiateLocaleDefinitionDao(applicationContext, resolver);
if (checkRefresh && dao instanceof CachingLocaleUrlDefinitionDAO) {
((CachingLocaleUrlDefinitionDAO) dao).setCheckRefresh(checkRefresh);
}
return dao;
}
@Override
protected DefinitionsReader createDefinitionsReader(ApplicationContext context) {
DigesterDefinitionsReader reader = (DigesterDefinitionsReader) super.createDefinitionsReader(context);
reader.setValidating(validateDefinitions);
return reader;
}
@Override
protected DefinitionsFactory createDefinitionsFactory(ApplicationContext applicationContext,
LocaleResolver resolver) {
if (definitionsFactoryClass != null) {
DefinitionsFactory factory = BeanUtils.instantiate(definitionsFactoryClass);
if (factory instanceof org.apache.tiles.request.ApplicationContextAware) {
((org.apache.tiles.request.ApplicationContextAware) factory)
.setApplicationContext(applicationContext);
}
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(factory);
if (bw.isWritableProperty("localeResolver")) {
bw.setPropertyValue("localeResolver", resolver);
}
if (bw.isWritableProperty("definitionDAO")) {
bw.setPropertyValue("definitionDAO", createLocaleDefinitionDao(applicationContext, resolver));
}
return factory;
}
else {
return super.createDefinitionsFactory(applicationContext, resolver);
}
}
@Override
protected PreparerFactory createPreparerFactory(ApplicationContext context) {
if (preparerFactoryClass != null) {
return BeanUtils.instantiate(preparerFactoryClass);
} else {
return super.createPreparerFactory(context);
}
}
@Override
protected LocaleResolver createLocaleResolver(ApplicationContext context) {
return new SpringLocaleResolver();
}
@Override
protected AttributeEvaluatorFactory createAttributeEvaluatorFactory(ApplicationContext context,
LocaleResolver resolver) {
return new BasicAttributeEvaluatorFactory(createELEvaluator(context));
}
private AttributeEvaluator createELEvaluator(ApplicationContext context) {
if (tilesElPresent) {
AttributeEvaluator evaluator = new TilesElActivator().createEvaluator();
if (evaluator != null) {
return evaluator;
}
}
return new DirectAttributeEvaluator();
}
}
private class TilesElActivator {
public AttributeEvaluator createEvaluator() {
try {
// jsp-api-2.1 doesn't default instantiate a factory for us
JspFactory factory = JspFactory.getDefaultFactory();
if ((factory != null) && (factory.getJspApplicationContext(servletContext).getExpressionFactory() != null)) {
logger.info("Found JSP 2.1 ExpressionFactory");
ELAttributeEvaluator evaluator = new ELAttributeEvaluator();
evaluator.setExpressionFactory(factory.getJspApplicationContext(servletContext).getExpressionFactory());
evaluator.setResolver(new CompositeELResolverImpl());
return evaluator;
}
}
catch (Throwable ex) {
logger.warn("Could not obtain JSP 2.1 ExpressionFactory", ex);
}
return null;
}
}
private static class CompositeELResolverImpl extends CompositeELResolver {
public CompositeELResolverImpl() {
add(new TilesContextELResolver(new TilesContextBeanELResolver()));
add(new TilesContextBeanELResolver());
add(new ArrayELResolver(false));
add(new ListELResolver(false));
add(new MapELResolver(false));
add(new ResourceBundleELResolver());
add(new BeanELResolver(false));
}
}
}

View File

@ -0,0 +1,157 @@
/*
* Copyright 2002-2012 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.tiles3;
import java.util.Locale;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.tiles.TilesContainer;
import org.apache.tiles.access.TilesAccess;
import org.apache.tiles.renderer.DefinitionRenderer;
import org.apache.tiles.request.ApplicationContext;
import org.apache.tiles.request.Request;
import org.apache.tiles.request.render.Renderer;
import org.apache.tiles.request.servlet.ServletRequest;
import org.apache.tiles.request.servlet.ServletUtil;
import org.springframework.web.servlet.support.JstlUtils;
import org.springframework.web.servlet.support.RequestContext;
import org.springframework.web.servlet.support.RequestContextUtils;
import org.springframework.web.servlet.view.AbstractUrlBasedView;
import org.springframework.web.util.WebUtils;
/**
* {@link org.springframework.web.servlet.View} implementation that renders
* through the Tiles Request API. The "url" property is interpreted as name of a
* Tiles definition.
*
* @author Nicolas Le Bas
* @author mick semb wever
* @author Rossen Stoyanchev
* @since 3.2
*/
public class TilesView extends AbstractUrlBasedView {
private Renderer renderer;
private boolean exposeForwardAttributes = false;
private boolean exposeJstlAttributes = true;
private ApplicationContext applicationContext;
/**
* Set the {@link Renderer} to use.
* If not set, by default {@link DefinitionRenderer} is used.
*/
public void setRenderer(Renderer renderer) {
this.renderer = renderer;
}
/**
* Whether to expose JSTL attributes. By default set to {@code true}.
* @see JstlUtils#exposeLocalizationContext(RequestContext)
*/
protected void setExposeJstlAttributes(boolean exposeJstlAttributes) {
this.exposeJstlAttributes = exposeJstlAttributes;
}
@Override
protected void initServletContext(ServletContext servletContext) {
super.initServletContext(servletContext);
if (servletContext.getMajorVersion() == 2 && servletContext.getMinorVersion() < 5) {
this.exposeForwardAttributes = true;
}
}
@Override
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
this.applicationContext = ServletUtil.getApplicationContext(getServletContext());
if (this.renderer == null) {
TilesContainer container = TilesAccess.getContainer(this.applicationContext);
this.renderer = new DefinitionRenderer(container);
}
}
@Override
public boolean checkResource(final Locale locale) throws Exception {
Request request = new ServletRequest(this.applicationContext, null, null) {
@Override
public Locale getRequestLocale() {
return locale;
}
};
return this.renderer.isRenderable(getUrl(), request);
}
@Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
exposeModelAsRequestAttributes(model, request);
if (this.exposeJstlAttributes) {
ServletContext servletContext = getServletContext();
JstlUtils.exposeLocalizationContext(new RequestContext(request, servletContext));
}
if (!response.isCommitted()) {
// Tiles is going to use a forward, but some web containers (e.g.
// OC4J 10.1.3)
// do not properly expose the Servlet 2.4 forward request
// attributes... However,
// must not do this on Servlet 2.5 or above, mainly for GlassFish
// compatibility.
if (this.exposeForwardAttributes) {
try {
WebUtils.exposeForwardRequestAttributes(request);
} catch (Exception ex) {
// Servlet container rejected to set internal attributes,
// e.g. on TriFork.
this.exposeForwardAttributes = false;
}
}
}
Request tilesRequest = createTilesRequest(request, response);
this.renderer.render(getUrl(), tilesRequest);
}
/**
* Create a Tiles {@link Request}. This implementation creates a
* {@link ServletRequest}.
*
* @param request the current request
* @param response the current response
* @return the Tiles request
*/
protected Request createTilesRequest(final HttpServletRequest request, HttpServletResponse response) {
return new ServletRequest(this.applicationContext, request, response) {
@Override
public Locale getRequestLocale() {
return RequestContextUtils.getLocale(request);
}
};
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2002-2012 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.tiles3;
import org.apache.tiles.request.render.Renderer;
import org.springframework.web.servlet.view.UrlBasedViewResolver;
/**
* Convenience subclass of {@link UrlBasedViewResolver} that supports
* {@link TilesView} (i.e. Tiles definitions) and custom subclasses of it.
*
* @author Nicolas Le Bas
* @author Rossen Stoyanchev
* @since 3.2
*/
public class TilesViewResolver extends UrlBasedViewResolver {
private Renderer renderer;
@Override
@SuppressWarnings("rawtypes")
protected Class getViewClass() {
return TilesView.class;
}
/**
* Set the {@link Renderer} to use.
* If not set, by default {@link DefinitionRenderer} is used.
* @see TilesView#setRenderer(Renderer)
*/
public void setRenderer(Renderer renderer) {
this.renderer = renderer;
}
@Override
protected TilesView buildView(String viewName) throws Exception {
TilesView view = (TilesView) super.buildView(viewName);
view.setRenderer(this.renderer);
return view;
}
}

View File

@ -0,0 +1,11 @@
/**
*
* Support classes for the integration of
* <a href="http://tiles.apache.org">Tiles3</a>
* (the standalone version of Tiles) as Spring web view technology.
* Contains a View implementation for Tiles definitions.
*
*/
package org.springframework.web.servlet.view.tiles3;

View File

@ -0,0 +1,64 @@
/*
* Copyright 2002-2012 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.tiles3;
import static org.junit.Assert.assertNotNull;
import org.apache.tiles.access.TilesAccess;
import org.apache.tiles.impl.BasicTilesContainer;
import org.apache.tiles.request.ApplicationContext;
import org.apache.tiles.request.Request;
import org.apache.tiles.request.servlet.ServletRequest;
import org.apache.tiles.request.servlet.ServletUtil;
import org.junit.Test;
import org.springframework.context.annotation.Configuration;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
/**
* Test fixture for {@link TilesConfigurer}.
*
* @author Nicolas Le Bas
*/
public class TilesConfigurerTests {
@Test
public void simpleBootstrap() {
MockServletContext servletContext = new MockServletContext();
TilesConfigurer tc = new TilesConfigurer();
tc.setDefinitions(new String[] { "/org/springframework/web/servlet/view/tiles3/tiles-definitions.xml" });
tc.setCheckRefresh(true);
tc.setServletContext(servletContext);
tc.afterPropertiesSet();
ApplicationContext tilesContext = ServletUtil.getApplicationContext(servletContext);
BasicTilesContainer container = (BasicTilesContainer) TilesAccess.getContainer(tilesContext);
Request requestContext = new ServletRequest(container.getApplicationContext(),
new MockHttpServletRequest(), new MockHttpServletResponse());
assertNotNull(container.getDefinitionsFactory().getDefinition("test", requestContext));
tc.destroy();
}
@Configuration
public static class AppConfig {
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright 2002-2012 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.tiles3;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.isA;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.Locale;
import org.apache.tiles.request.Request;
import org.apache.tiles.request.render.Renderer;
import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockServletContext;
import org.springframework.web.context.support.StaticWebApplicationContext;
/**
* Test fixture for {@link TilesViewResolver}.
*
* @author mick semb wever
*/
public class TilesViewResolverTests {
private TilesViewResolver viewResolver;
private Renderer renderer;
@Before
public void setUp() {
StaticWebApplicationContext wac = new StaticWebApplicationContext();
wac.setServletContext(new MockServletContext());
wac.refresh();
this.renderer = EasyMock.createMock(Renderer.class);
this.viewResolver = new TilesViewResolver();
this.viewResolver.setRenderer(this.renderer);
this.viewResolver.setApplicationContext(wac);
}
@Test
public void testResolve() throws Exception {
expect(this.renderer.isRenderable(eq("/template.test"), isA(Request.class))).andReturn(true);
expect(this.renderer.isRenderable(eq("/nonexistent.test"), isA(Request.class))).andReturn(false);
replay(this.renderer);
assertTrue(this.viewResolver.resolveViewName("/template.test", Locale.ITALY) instanceof TilesView);
assertNull(this.viewResolver.resolveViewName("/nonexistent.test", Locale.ITALY));
verify(this.renderer);
}
}

View File

@ -0,0 +1,95 @@
/*
* Copyright 2002-2012 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.tiles3;
import static org.easymock.EasyMock.and;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.isA;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
import java.util.HashMap;
import java.util.Map;
import org.apache.tiles.request.ApplicationContext;
import org.apache.tiles.request.Request;
import org.apache.tiles.request.render.Renderer;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
/**
* Test fixture for {@link TilesView}.
*
* @author mick semb wever
*/
public class TilesViewTests {
private static final String VIEW_PATH = "template.test";
private TilesView view;
private Renderer renderer;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
@Before
public void setUp() throws Exception {
MockServletContext servletContext = new MockServletContext();
StaticWebApplicationContext wac = new StaticWebApplicationContext();
wac.setServletContext(servletContext);
wac.refresh();
request = new MockHttpServletRequest();
request.setAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);
response = new MockHttpServletResponse();
renderer = createMock(Renderer.class);
view = new TilesView();
view.setServletContext(servletContext);
view.setRenderer(renderer);
view.setUrl(VIEW_PATH);
view.afterPropertiesSet();
}
@Test
public void testRender() throws Exception {
Map<String, Object> model = new HashMap<String, Object>();
model.put("modelAttribute", "modelValue");
ApplicationContext tilesContext = createMock(ApplicationContext.class);
renderer.render(eq(VIEW_PATH), and(isA(Request.class), isA(Request.class)));
replay(tilesContext, renderer);
view.render(model, request, response);
assertEquals("modelValue", request.getAttribute("modelAttribute"));
verify(tilesContext, renderer);
}
}

View File

@ -0,0 +1,7 @@
log4j.rootCategory=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
log4j.category.org.springframework.web=DEBUG

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"
"http://tiles.apache.org/dtds/tiles-config_3_0.dtd">
<tiles-definitions>
<definition name="test" template="/WEB-INF/tiles/test.jsp"/>
</tiles-definitions>

View File

@ -0,0 +1,2 @@
ext.mergeIntoProject = project(':spring-webmvc')
apply from: "${rootProject.projectDir}/merge-dist.gradle"

View File

@ -74,7 +74,7 @@ Changes in version 3.2 RC1 (2012-11-04)
* use concurrent cache to improve performance of GenericTypeResolver (SPR-8701)
* cache and late resolve annotations on bean properties to improve performance (SPR-9166)
* allow PropertyResolver implementations to ignore unresolvable ${placeholders} (SPR-9569)
* add integration with Tiles 3
Changes in version 3.2 M2 (2012-09-11)
--------------------------------------

View File

@ -839,7 +839,9 @@ public String deletePet(@PathVariable int ownerId, @PathVariable int petId) {
<para><emphasis>NOTE:</emphasis> This section focuses on Spring's support
for Tiles 2 (the standalone version of Tiles, requiring Java 5+) in the
<literal>org.springframework.web.servlet.view.tiles2</literal> package.
<literal>org.springframework.web.servlet.view.tiles2</literal> package as
as well as Tiles 3 in the
<literal>org.springframework.web.servlet.view.tiles3</literal> package.
Spring also continues to support Tiles 1.x (a.k.a. "Struts Tiles", as
shipped with Struts 1.1+; compatible with Java 1.4) in the original
<literal>org.springframework.web.servlet.view.tiles</literal>