org.springframework.web.servlet.view.tiles3.TilesConfigurer Maven / Gradle / Ivy
/*
* Copyright 2002-2017 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
*
* https://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.Collection;
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.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tiles.TilesContainer;
import org.apache.tiles.TilesException;
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.ScopeELResolver;
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.extras.complete.CompleteAutoloadTilesContainerFactory;
import org.apache.tiles.extras.complete.CompleteAutoloadTilesInitializer;
import org.apache.tiles.factory.AbstractTilesContainerFactory;
import org.apache.tiles.factory.BasicTilesContainerFactory;
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.ApplicationContextAware;
import org.apache.tiles.request.ApplicationResource;
import org.apache.tiles.startup.DefaultTilesInitializer;
import org.apache.tiles.startup.TilesInitializer;
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.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.web.context.ServletContextAware;
/**
* Helper class to configure Tiles 3.x for the Spring Framework. See
* https://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
* {@code ServletContextListener}
* (e.g. {@link org.apache.tiles.extras.complete.CompleteAutoloadTilesListener}
* for usage in {@code 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"}.
*
* Note that in Tiles 3 an underscore in the name of a file containing Tiles
* definitions is used to indicate locale information, for example:
*
*
* <bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
* <property name="definitions">
* <list>
* <value>/WEB-INF/defs/tiles.xml</value>
* <value>/WEB-INF/defs/tiles_fr_FR.xml</value>
* </list>
* </property>
* </bean>
*
*
* @author mick semb wever
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 3.2
* @see TilesView
* @see TilesViewResolver
*/
public class TilesConfigurer implements ServletContextAware, InitializingBean, DisposableBean {
private static final boolean tilesElPresent =
ClassUtils.isPresent("org.apache.tiles.el.ELAttributeEvaluator", TilesConfigurer.class.getClassLoader());
protected final Log logger = LogFactory.getLog(getClass());
@Nullable
private TilesInitializer tilesInitializer;
@Nullable
private String[] definitions;
private boolean checkRefresh = false;
private boolean validateDefinitions = true;
@Nullable
private Class definitionsFactoryClass;
@Nullable
private Class preparerFactoryClass;
private boolean useMutableTilesContainer = false;
@Nullable
private ServletContext servletContext;
/**
* 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 {
this.tilesInitializer = new SpringCompleteAutoloadTilesInitializer();
}
catch (Throwable ex) {
throw new IllegalStateException("Tiles-Extras 3.0 not available", ex);
}
}
else {
this.tilesInitializer = null;
}
}
/**
* 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.UnresolvingLocaleDefinitionsFactory},
* 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.factory.PreparerFactory} implementation to use.
* Default is {@link org.apache.tiles.preparer.factory.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.impl.mgmt.CachingTilesContainer
*/
public void setUseMutableTilesContainer(boolean useMutableTilesContainer) {
this.useMutableTilesContainer = useMutableTilesContainer;
}
@Override
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
*/
@Override
public void afterPropertiesSet() throws TilesException {
Assert.state(this.servletContext != null, "No ServletContext available");
ApplicationContext preliminaryContext = new SpringWildcardServletTilesApplicationContext(this.servletContext);
if (this.tilesInitializer == null) {
this.tilesInitializer = new SpringTilesInitializer();
}
this.tilesInitializer.initialize(preliminaryContext);
}
/**
* Removes the TilesContainer from this web application.
* @throws TilesException in case of cleanup failure
*/
@Override
public void destroy() throws TilesException {
if (this.tilesInitializer != null) {
this.tilesInitializer.destroy();
}
}
private class SpringTilesInitializer extends DefaultTilesInitializer {
@Override
protected AbstractTilesContainerFactory createContainerFactory(ApplicationContext context) {
return new SpringTilesContainerFactory();
}
}
private class SpringTilesContainerFactory extends BasicTilesContainerFactory {
@Override
protected TilesContainer createDecoratedContainer(TilesContainer originalContainer, ApplicationContext context) {
return (useMutableTilesContainer ? new CachingTilesContainer(originalContainer) : originalContainer);
}
@Override
protected List getSources(ApplicationContext applicationContext) {
if (definitions != null) {
List result = new LinkedList<>();
for (String definition : definitions) {
Collection resources = applicationContext.getResources(definition);
if (resources != null) {
result.addAll(resources);
}
}
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(true);
}
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.instantiateClass(definitionsFactoryClass);
if (factory instanceof ApplicationContextAware) {
((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.instantiateClass(preparerFactoryClass);
}
else {
return super.createPreparerFactory(context);
}
}
@Override
protected LocaleResolver createLocaleResolver(ApplicationContext context) {
return new SpringLocaleResolver();
}
@Override
protected AttributeEvaluatorFactory createAttributeEvaluatorFactory(ApplicationContext context,
LocaleResolver resolver) {
AttributeEvaluator evaluator;
if (tilesElPresent && JspFactory.getDefaultFactory() != null) {
evaluator = new TilesElActivator().createEvaluator();
}
else {
evaluator = new DirectAttributeEvaluator();
}
return new BasicAttributeEvaluatorFactory(evaluator);
}
}
private static class SpringCompleteAutoloadTilesInitializer extends CompleteAutoloadTilesInitializer {
@Override
protected AbstractTilesContainerFactory createContainerFactory(ApplicationContext context) {
return new SpringCompleteAutoloadTilesContainerFactory();
}
}
private static class SpringCompleteAutoloadTilesContainerFactory extends CompleteAutoloadTilesContainerFactory {
@Override
protected LocaleResolver createLocaleResolver(ApplicationContext applicationContext) {
return new SpringLocaleResolver();
}
}
private class TilesElActivator {
public AttributeEvaluator createEvaluator() {
ELAttributeEvaluator evaluator = new ELAttributeEvaluator();
evaluator.setExpressionFactory(
JspFactory.getDefaultFactory().getJspApplicationContext(servletContext).getExpressionFactory());
evaluator.setResolver(new CompositeELResolverImpl());
return evaluator;
}
}
private static class CompositeELResolverImpl extends CompositeELResolver {
public CompositeELResolverImpl() {
add(new ScopeELResolver());
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));
}
}
}