/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet.view.tiles2;
import java.util.Enumeration;
import java.util.Properties;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
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.access.TilesAccess;
import org.apache.tiles.context.ChainedTilesContextFactory;
import org.apache.tiles.definition.UrlDefinitionsFactory;
import org.apache.tiles.definition.digester.DigesterDefinitionsReader;
import org.apache.tiles.factory.TilesContainerFactory;
import org.apache.tiles.impl.BasicTilesContainer;
import org.apache.tiles.jsp.context.JspTilesContextFactory;
import org.apache.tiles.preparer.BasicPreparerFactory;
import org.apache.tiles.servlet.context.ServletTilesContextFactory;
import org.apache.tiles.web.util.ServletContextAdapter;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.ServletContextAware;
/**
* Helper class to configure Tiles2 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 JSP-based web applications.
*
* <p>The TilesConfigurer simply configures a TilesContainer using a set
* of files containing definitions, to be accessed by {@link TilesView}
* instances.
*
* <p>TilesViews can be managed by any {@link org.springframework.web.servlet.ViewResolver}.
* For simple convention-based view resolution, consider using
* {@link org.springframework.web.servlet.view.UrlBasedViewResolver} with the
* "viewClass" property set to "org.springframework.web.servlet.view.tiles2.TilesView".
*
* <p>A typical TilesConfigurer bean definition looks as follows:
*
* <pre>
* <bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.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></pre>
*
* The values in the list are the actual files containing the definitions.
*
* @author Juergen Hoeller
* @since 2.5
* @see TilesView
* @see org.springframework.web.servlet.view.UrlBasedViewResolver
*/
public class TilesConfigurer implements ServletContextAware, InitializingBean, DisposableBean {
protected final Log logger = LogFactory.getLog(getClass());
private final Properties tilesPropertyMap = new Properties();
private ServletContext servletContext;
public TilesConfigurer() {
this.tilesPropertyMap.put(
TilesContainerFactory.CONTAINER_FACTORY_INIT_PARAM,
TilesContainerFactory.class.getName());
this.tilesPropertyMap.put(
TilesContainerFactory.CONTEXT_FACTORY_INIT_PARAM,
ChainedTilesContextFactory.class.getName());
this.tilesPropertyMap.put(
TilesContainerFactory.DEFINITIONS_FACTORY_INIT_PARAM,
UrlDefinitionsFactory.class.getName());
this.tilesPropertyMap.put(
TilesContainerFactory.PREPARER_FACTORY_INIT_PARAM,
BasicPreparerFactory.class.getName());
this.tilesPropertyMap.put(
ChainedTilesContextFactory.FACTORY_CLASS_NAMES,
ServletTilesContextFactory.class.getName() + "," + JspTilesContextFactory.class.getName());
this.tilesPropertyMap.put(
UrlDefinitionsFactory.LOCALE_RESOLVER_IMPL_PROPERTY,
SpringLocaleResolver.class.getName());
}
/**
* Set the Tiles definitions, i.e. the list of files containing the definitions.
* Default is "/WEB-INF/tiles.xml".
*/
public void setDefinitions(String[] definitions) {
if (definitions != null) {
String defs = StringUtils.arrayToCommaDelimitedString(definitions);
if (logger.isInfoEnabled()) {
logger.info("TilesConfigurer: adding definitions [" + defs + "]");
}
this.tilesPropertyMap.put(BasicTilesContainer.DEFINITIONS_CONFIG, defs);
}
}
/**
* Set whether to validate the Tiles XML definitions. Default is "true".
*/
public void setValidateDefinitions(boolean validateDefinitions) {
this.tilesPropertyMap.put(DigesterDefinitionsReader.PARSER_VALIDATE_PARAMETER_NAME,
Boolean.toString(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 definitionsFactoryClass) {
this.tilesPropertyMap.put(TilesContainerFactory.DEFINITIONS_FACTORY_INIT_PARAM,
definitionsFactoryClass.getName());
}
/**
* 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 preparerFactoryClass) {
this.tilesPropertyMap.put(TilesContainerFactory.PREPARER_FACTORY_INIT_PARAM,
preparerFactoryClass.getName());
}
/**
* Set whether to use a MutableTilesContainer for this application.
* Default is "false".
*/
public void setUseMutableTilesContainer(boolean useMutableTilesContainer) {
this.tilesPropertyMap.put(TilesContainerFactory.CONTAINER_FACTORY_MUTABLE_INIT_PARAM,
Boolean.toString(useMutableTilesContainer));
}
/**
* Set Tiles properties (equivalent to the ServletContext init-params in
* the Tiles documentation), overriding the default settings.
*/
public void setTilesProperties(Properties tilesProperties) {
CollectionUtils.mergePropertiesIntoMap(tilesProperties, this.tilesPropertyMap);
}
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
/**
* Creates and exposes a TilesContainer for this web application.
* @throws TilesException in case of setup failure
*/
public void afterPropertiesSet() throws TilesException {
TilesContainer container = createTilesContainer(this.servletContext);
TilesAccess.setContainer(this.servletContext, container);
}
/**
* Create a TilesContainer for this web application.
* @param context this web application's ServletContext
* @return the TilesContainer to expose
* @throws TilesException in case of setup failure
*/
protected TilesContainer createTilesContainer(ServletContext context) throws TilesException {
ServletContextAdapter adaptedContext = new ServletContextAdapter(new DelegatingServletConfig());
TilesContainerFactory factory = TilesContainerFactory.getFactory(adaptedContext);
return factory.createContainer(adaptedContext);
}
/**
* Removes the TilesContainer from this web application.
* @throws TilesException in case of cleanup failure
*/
public void destroy() throws TilesException {
TilesAccess.setContainer(this.servletContext, null);
}
/**
* Internal implementation of the ServletConfig interface, to be passed
* to the wrapped servlet. Delegates to ServletWrappingController fields
* and methods to provide init parameters and other environment info.
*/
private class DelegatingServletConfig implements ServletConfig {
public String getServletName() {
return "TilesConfigurer";
}
public ServletContext getServletContext() {
return servletContext;
}
public String getInitParameter(String paramName) {
return tilesPropertyMap.getProperty(paramName);
}
public Enumeration getInitParameterNames() {
return tilesPropertyMap.keys();
}
}
}