From 2a11007a04ab810b594f18517bb447a300b6074f Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 20 Nov 2012 11:57:41 -0500 Subject: [PATCH] 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: https://github.com/SpringSource/spring-framework-issues/commit/1f64be4aa59b4766f9cc5fc6ead621d29f92a0d6 Issue: SPR-8825 --- build.gradle | 27 +- settings.gradle | 1 + .../tiles3/AbstractSpringPreparerFactory.java | 63 +++ .../tiles3/SimpleSpringPreparerFactory.java | 71 +++ .../tiles3/SpringBeanPreparerFactory.java | 42 ++ .../view/tiles3/SpringLocaleResolver.java | 53 +++ .../servlet/view/tiles3/TilesConfigurer.java | 419 ++++++++++++++++++ .../web/servlet/view/tiles3/TilesView.java | 157 +++++++ .../view/tiles3/TilesViewResolver.java | 56 +++ .../web/servlet/view/tiles3/package-info.java | 11 + .../view/tiles3/TilesConfigurerTests.java | 64 +++ .../view/tiles3/TilesViewResolverTests.java | 72 +++ .../servlet/view/tiles3/TilesViewTests.java | 95 ++++ .../src/test/resources/log4j.properties | 7 + .../servlet/view/tiles3/tiles-definitions.xml | 10 + spring-webmvc-tiles3/tiles3.gradle | 2 + src/dist/changelog.txt | 2 +- src/reference/docbook/view.xml | 4 +- 18 files changed, 1151 insertions(+), 5 deletions(-) create mode 100644 spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/AbstractSpringPreparerFactory.java create mode 100644 spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/SimpleSpringPreparerFactory.java create mode 100644 spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/SpringBeanPreparerFactory.java create mode 100644 spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/SpringLocaleResolver.java create mode 100644 spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/TilesConfigurer.java create mode 100644 spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/TilesView.java create mode 100644 spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/TilesViewResolver.java create mode 100644 spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/package-info.java create mode 100644 spring-webmvc-tiles3/src/test/java/org/springframework/web/servlet/view/tiles3/TilesConfigurerTests.java create mode 100644 spring-webmvc-tiles3/src/test/java/org/springframework/web/servlet/view/tiles3/TilesViewResolverTests.java create mode 100644 spring-webmvc-tiles3/src/test/java/org/springframework/web/servlet/view/tiles3/TilesViewTests.java create mode 100644 spring-webmvc-tiles3/src/test/resources/log4j.properties create mode 100644 spring-webmvc-tiles3/src/test/resources/org/springframework/web/servlet/view/tiles3/tiles-definitions.xml create mode 100644 spring-webmvc-tiles3/tiles3.gradle diff --git a/build.gradle b/build.gradle index cb43d3a927..c3f2479a07 100644 --- a/build.gradle +++ b/build.gradle @@ -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) diff --git a/settings.gradle b/settings.gradle index 1a4026d318..f46b8bd6ae 100644 --- a/settings.gradle +++ b/settings.gradle @@ -20,3 +20,4 @@ include 'spring-tx' include 'spring-web' include 'spring-webmvc' include 'spring-webmvc-portlet' +include 'spring-webmvc-tiles3' diff --git a/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/AbstractSpringPreparerFactory.java b/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/AbstractSpringPreparerFactory.java new file mode 100644 index 0000000000..eaa6d58bc2 --- /dev/null +++ b/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/AbstractSpringPreparerFactory.java @@ -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; + +} diff --git a/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/SimpleSpringPreparerFactory.java b/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/SimpleSpringPreparerFactory.java new file mode 100644 index 0000000000..9f05299f1b --- /dev/null +++ b/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/SimpleSpringPreparerFactory.java @@ -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 sharedPreparers = new ConcurrentHashMap(); + + + @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; + } + +} diff --git a/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/SpringBeanPreparerFactory.java b/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/SpringBeanPreparerFactory.java new file mode 100644 index 0000000000..0e252dc231 --- /dev/null +++ b/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/SpringBeanPreparerFactory.java @@ -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); + } + +} diff --git a/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/SpringLocaleResolver.java b/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/SpringLocaleResolver.java new file mode 100644 index 0000000000..69cb716de2 --- /dev/null +++ b/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/SpringLocaleResolver.java @@ -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. + * + *

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); + } + +} diff --git a/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/TilesConfigurer.java b/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/TilesConfigurer.java new file mode 100644 index 0000000000..7b23a60cdc --- /dev/null +++ b/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/TilesConfigurer.java @@ -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 + * http://tiles.apache.org + * for more information about Tiles, which basically is a templating mechanism + * for web applications using JSPs and other template engines. + * + *

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 web.xml). + * + *

TilesViews can be managed by any {@link org.springframework.web.servlet.ViewResolver}. + * For simple convention-based view resolution, consider using {@link TilesViewResolver}. + * + *

A typical TilesConfigurer bean definition looks as follows: + * + *

+ * <bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
+ *   <property name="definitions">
+ *     <list>
+ *       <value>/WEB-INF/defs/general.xml</value>
+ *       <value>/WEB-INF/defs/widgets.xml</value>
+ *       <value>/WEB-INF/defs/administrator.xml</value>
+ *       <value>/WEB-INF/defs/customer.xml</value>
+ *       <value>/WEB-INF/defs/templates.xml</value>
+ *     </list>
+ *   </property>
+ * </bean>
+ * 
+ * + * 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 definitionsFactoryClass; + + private Class preparerFactoryClass; + + private boolean useMutableTilesContainer = false; + + private ServletContext servletContext; + + public TilesConfigurer() { + } + + /** + * Configure Tiles using a custom TilesInitializer, typically specified as an inner bean. + *

Default is a variant of {@link org.apache.tiles.startup.DefaultTilesInitializer}, + * respecting the "definitions", "preparerFactoryClass" etc properties on this configurer. + *

NOTE: Specifying a custom TilesInitializer effectively disables all other bean + * properties on this configurer. 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. + *

See {@link org.apache.tiles.extras.complete.CompleteAutoloadTilesContainerFactory} + * for details on the complete-autoload mode. + *

NOTE: Specifying the complete-autoload mode effectively disables all other bean + * properties on this configurer. 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. + *

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 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. + *

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. + *

Specify {@link SpringBeanPreparerFactory} to operate on specified preparer + * names 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 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 getSources(ApplicationContext applicationContext) { + if (definitions != null) { + List result = new LinkedList(); + 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)); + } + } +} diff --git a/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/TilesView.java b/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/TilesView.java new file mode 100644 index 0000000000..df8cd2d453 --- /dev/null +++ b/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/TilesView.java @@ -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 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); + } + }; + } + +} diff --git a/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/TilesViewResolver.java b/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/TilesViewResolver.java new file mode 100644 index 0000000000..113428877a --- /dev/null +++ b/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/TilesViewResolver.java @@ -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; + } + +} diff --git a/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/package-info.java b/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/package-info.java new file mode 100644 index 0000000000..52c94c61fa --- /dev/null +++ b/spring-webmvc-tiles3/src/main/java/org/springframework/web/servlet/view/tiles3/package-info.java @@ -0,0 +1,11 @@ + +/** + * + * Support classes for the integration of + * Tiles3 + * (the standalone version of Tiles) as Spring web view technology. + * Contains a View implementation for Tiles definitions. + * + */ +package org.springframework.web.servlet.view.tiles3; + diff --git a/spring-webmvc-tiles3/src/test/java/org/springframework/web/servlet/view/tiles3/TilesConfigurerTests.java b/spring-webmvc-tiles3/src/test/java/org/springframework/web/servlet/view/tiles3/TilesConfigurerTests.java new file mode 100644 index 0000000000..e67a748c73 --- /dev/null +++ b/spring-webmvc-tiles3/src/test/java/org/springframework/web/servlet/view/tiles3/TilesConfigurerTests.java @@ -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 { + } + +} diff --git a/spring-webmvc-tiles3/src/test/java/org/springframework/web/servlet/view/tiles3/TilesViewResolverTests.java b/spring-webmvc-tiles3/src/test/java/org/springframework/web/servlet/view/tiles3/TilesViewResolverTests.java new file mode 100644 index 0000000000..c8bb4ab0f5 --- /dev/null +++ b/spring-webmvc-tiles3/src/test/java/org/springframework/web/servlet/view/tiles3/TilesViewResolverTests.java @@ -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); + } +} diff --git a/spring-webmvc-tiles3/src/test/java/org/springframework/web/servlet/view/tiles3/TilesViewTests.java b/spring-webmvc-tiles3/src/test/java/org/springframework/web/servlet/view/tiles3/TilesViewTests.java new file mode 100644 index 0000000000..62ecf45414 --- /dev/null +++ b/spring-webmvc-tiles3/src/test/java/org/springframework/web/servlet/view/tiles3/TilesViewTests.java @@ -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 model = new HashMap(); + 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); + } + +} diff --git a/spring-webmvc-tiles3/src/test/resources/log4j.properties b/spring-webmvc-tiles3/src/test/resources/log4j.properties new file mode 100644 index 0000000000..17a835faff --- /dev/null +++ b/spring-webmvc-tiles3/src/test/resources/log4j.properties @@ -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 diff --git a/spring-webmvc-tiles3/src/test/resources/org/springframework/web/servlet/view/tiles3/tiles-definitions.xml b/spring-webmvc-tiles3/src/test/resources/org/springframework/web/servlet/view/tiles3/tiles-definitions.xml new file mode 100644 index 0000000000..feb4a09cb8 --- /dev/null +++ b/spring-webmvc-tiles3/src/test/resources/org/springframework/web/servlet/view/tiles3/tiles-definitions.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/spring-webmvc-tiles3/tiles3.gradle b/spring-webmvc-tiles3/tiles3.gradle new file mode 100644 index 0000000000..519e150992 --- /dev/null +++ b/spring-webmvc-tiles3/tiles3.gradle @@ -0,0 +1,2 @@ +ext.mergeIntoProject = project(':spring-webmvc') +apply from: "${rootProject.projectDir}/merge-dist.gradle" diff --git a/src/dist/changelog.txt b/src/dist/changelog.txt index 090c75816f..c66e55e43c 100644 --- a/src/dist/changelog.txt +++ b/src/dist/changelog.txt @@ -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) -------------------------------------- diff --git a/src/reference/docbook/view.xml b/src/reference/docbook/view.xml index e1e6e4e2b0..0aecd42624 100644 --- a/src/reference/docbook/view.xml +++ b/src/reference/docbook/view.xml @@ -839,7 +839,9 @@ public String deletePet(@PathVariable int ownerId, @PathVariable int petId) { NOTE: This section focuses on Spring's support for Tiles 2 (the standalone version of Tiles, requiring Java 5+) in the - org.springframework.web.servlet.view.tiles2 package. + org.springframework.web.servlet.view.tiles2 package as + as well as Tiles 3 in the + org.springframework.web.servlet.view.tiles3 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 org.springframework.web.servlet.view.tiles