001/* 002 * Copyright 2002-2014 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.servlet.view.tiles2; 018 019import java.io.IOException; 020import java.net.URL; 021import java.util.Collections; 022import java.util.HashMap; 023import java.util.LinkedList; 024import java.util.List; 025import java.util.Map; 026import java.util.Set; 027import javax.servlet.ServletContext; 028import javax.servlet.jsp.JspFactory; 029 030import org.apache.commons.logging.Log; 031import org.apache.commons.logging.LogFactory; 032import org.apache.tiles.TilesApplicationContext; 033import org.apache.tiles.TilesException; 034import org.apache.tiles.awareness.TilesApplicationContextAware; 035import org.apache.tiles.context.TilesRequestContextFactory; 036import org.apache.tiles.definition.DefinitionsFactory; 037import org.apache.tiles.definition.DefinitionsFactoryException; 038import org.apache.tiles.definition.DefinitionsReader; 039import org.apache.tiles.definition.Refreshable; 040import org.apache.tiles.definition.dao.BaseLocaleUrlDefinitionDAO; 041import org.apache.tiles.definition.dao.CachingLocaleUrlDefinitionDAO; 042import org.apache.tiles.definition.digester.DigesterDefinitionsReader; 043import org.apache.tiles.el.ELAttributeEvaluator; 044import org.apache.tiles.evaluator.AttributeEvaluator; 045import org.apache.tiles.evaluator.AttributeEvaluatorFactory; 046import org.apache.tiles.evaluator.BasicAttributeEvaluatorFactory; 047import org.apache.tiles.evaluator.impl.DirectAttributeEvaluator; 048import org.apache.tiles.extras.complete.CompleteAutoloadTilesContainerFactory; 049import org.apache.tiles.extras.complete.CompleteAutoloadTilesInitializer; 050import org.apache.tiles.factory.AbstractTilesContainerFactory; 051import org.apache.tiles.factory.BasicTilesContainerFactory; 052import org.apache.tiles.impl.BasicTilesContainer; 053import org.apache.tiles.impl.mgmt.CachingTilesContainer; 054import org.apache.tiles.locale.LocaleResolver; 055import org.apache.tiles.preparer.PreparerFactory; 056import org.apache.tiles.startup.AbstractTilesInitializer; 057import org.apache.tiles.startup.TilesInitializer; 058 059import org.springframework.beans.BeanUtils; 060import org.springframework.beans.BeanWrapper; 061import org.springframework.beans.PropertyAccessorFactory; 062import org.springframework.beans.factory.DisposableBean; 063import org.springframework.beans.factory.InitializingBean; 064import org.springframework.util.ClassUtils; 065import org.springframework.web.context.ServletContextAware; 066 067/** 068 * Helper class to configure Tiles 2.x for the Spring Framework. See 069 * <a href="https://tiles.apache.org">https://tiles.apache.org</a> 070 * for more information about Tiles, which basically is a templating mechanism 071 * for web applications using JSPs and other template engines. 072 * 073 * <b>Note: Spring 4.0 requires Tiles 2.2.2.</b> Tiles' EL support will 074 * be activated by default when the Tiles EL module is present in the classpath. 075 * 076 * <p>The TilesConfigurer simply configures a TilesContainer using a set of files 077 * containing definitions, to be accessed by {@link TilesView} instances. This is a 078 * Spring-based alternative (for usage in Spring configuration) to the Tiles-provided 079 * {@code ServletContextListener} 080 * (e.g. {@link org.apache.tiles.extras.complete.CompleteAutoloadTilesListener} 081 * for usage in {@code web.xml}. 082 * 083 * <p>TilesViews can be managed by any {@link org.springframework.web.servlet.ViewResolver}. 084 * For simple convention-based view resolution, consider using {@link TilesViewResolver}. 085 * 086 * <p>A typical TilesConfigurer bean definition looks as follows: 087 * 088 * <pre class="code"> 089 * <bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer"> 090 * <property name="definitions"> 091 * <list> 092 * <value>/WEB-INF/defs/general.xml</value> 093 * <value>/WEB-INF/defs/widgets.xml</value> 094 * <value>/WEB-INF/defs/administrator.xml</value> 095 * <value>/WEB-INF/defs/customer.xml</value> 096 * <value>/WEB-INF/defs/templates.xml</value> 097 * </list> 098 * </property> 099 * </bean> 100 * </pre> 101 * 102 * The values in the list are the actual Tiles XML files containing the definitions. 103 * If the list is not specified, the default is {@code "/WEB-INF/tiles.xml"}. 104 * 105 * <p><b>NOTE: Tiles 2 support is deprecated in favor of Tiles 3 and will be removed 106 * as of Spring Framework 5.0.</b>. 107 * 108 * @author Juergen Hoeller 109 * @since 2.5 110 * @see TilesView 111 * @see TilesViewResolver 112 * @deprecated as of Spring 4.2, in favor of Tiles 3 113 */ 114@Deprecated 115public class TilesConfigurer implements ServletContextAware, InitializingBean, DisposableBean { 116 117 private static final boolean tilesElPresent = 118 ClassUtils.isPresent("org.apache.tiles.el.ELAttributeEvaluator", TilesConfigurer.class.getClassLoader()); 119 120 121 protected final Log logger = LogFactory.getLog(getClass()); 122 123 private TilesInitializer tilesInitializer; 124 125 private String[] definitions; 126 127 private boolean checkRefresh = false; 128 129 private boolean validateDefinitions = true; 130 131 private Class<? extends DefinitionsFactory> definitionsFactoryClass; 132 133 private Class<? extends PreparerFactory> preparerFactoryClass; 134 135 private boolean useMutableTilesContainer = false; 136 137 private ServletContext servletContext; 138 139 140 /** 141 * Configure Tiles using a custom TilesInitializer, typically specified as an inner bean. 142 * <p>Default is a variant of {@link org.apache.tiles.startup.DefaultTilesInitializer}, 143 * respecting the "definitions", "preparerFactoryClass" etc properties on this configurer. 144 * <p><b>NOTE: Specifying a custom TilesInitializer effectively disables all other bean 145 * properties on this configurer.</b> The entire initialization procedure is then left 146 * to the TilesInitializer as specified. 147 */ 148 public void setTilesInitializer(TilesInitializer tilesInitializer) { 149 this.tilesInitializer = tilesInitializer; 150 } 151 152 /** 153 * Specify whether to apply Tiles 2.2's "complete-autoload" configuration. 154 * <p>See {@link org.apache.tiles.extras.complete.CompleteAutoloadTilesContainerFactory} 155 * for details on the complete-autoload mode. 156 * <p><b>NOTE: Specifying the complete-autoload mode effectively disables all other bean 157 * properties on this configurer.</b> The entire initialization procedure is then left 158 * to {@link org.apache.tiles.extras.complete.CompleteAutoloadTilesInitializer}. 159 * @see org.apache.tiles.extras.complete.CompleteAutoloadTilesContainerFactory 160 * @see org.apache.tiles.extras.complete.CompleteAutoloadTilesInitializer 161 */ 162 public void setCompleteAutoload(boolean completeAutoload) { 163 if (completeAutoload) { 164 try { 165 this.tilesInitializer = new SpringCompleteAutoloadTilesInitializer(); 166 } 167 catch (Throwable ex) { 168 throw new IllegalStateException("Tiles-Extras 2.2 not available", ex); 169 } 170 } 171 else { 172 this.tilesInitializer = null; 173 } 174 } 175 176 /** 177 * Set the Tiles definitions, i.e. the list of files containing the definitions. 178 * Default is "/WEB-INF/tiles.xml". 179 */ 180 public void setDefinitions(String... definitions) { 181 this.definitions = definitions; 182 } 183 184 /** 185 * Set whether to check Tiles definition files for a refresh at runtime. 186 * Default is "false". 187 */ 188 public void setCheckRefresh(boolean checkRefresh) { 189 this.checkRefresh = checkRefresh; 190 } 191 192 /** 193 * Set whether to validate the Tiles XML definitions. Default is "true". 194 */ 195 public void setValidateDefinitions(boolean validateDefinitions) { 196 this.validateDefinitions = validateDefinitions; 197 } 198 199 /** 200 * Set the {@link org.apache.tiles.definition.DefinitionsFactory} implementation to use. 201 * Default is {@link org.apache.tiles.definition.UnresolvingLocaleDefinitionsFactory}, 202 * operating on definition resource URLs. 203 * <p>Specify a custom DefinitionsFactory, e.g. a UrlDefinitionsFactory subclass, 204 * to customize the creation of Tiles Definition objects. Note that such a 205 * DefinitionsFactory has to be able to handle {@link java.net.URL} source objects, 206 * unless you configure a different TilesContainerFactory. 207 */ 208 public void setDefinitionsFactoryClass(Class<? extends DefinitionsFactory> definitionsFactoryClass) { 209 this.definitionsFactoryClass = definitionsFactoryClass; 210 } 211 212 /** 213 * Set the {@link org.apache.tiles.preparer.PreparerFactory} implementation to use. 214 * Default is {@link org.apache.tiles.preparer.BasicPreparerFactory}, creating 215 * shared instances for specified preparer classes. 216 * <p>Specify {@link SimpleSpringPreparerFactory} to autowire 217 * {@link org.apache.tiles.preparer.ViewPreparer} instances based on specified 218 * preparer classes, applying Spring's container callbacks as well as applying 219 * configured Spring BeanPostProcessors. If Spring's context-wide annotation-config 220 * has been activated, annotations in ViewPreparer classes will be automatically 221 * detected and applied. 222 * <p>Specify {@link SpringBeanPreparerFactory} to operate on specified preparer 223 * <i>names</i> instead of classes, obtaining the corresponding Spring bean from 224 * the DispatcherServlet's application context. The full bean creation process 225 * will be in the control of the Spring application context in this case, 226 * allowing for the use of scoped beans etc. Note that you need to define one 227 * Spring bean definition per preparer name (as used in your Tiles definitions). 228 * @see SimpleSpringPreparerFactory 229 * @see SpringBeanPreparerFactory 230 */ 231 public void setPreparerFactoryClass(Class<? extends PreparerFactory> preparerFactoryClass) { 232 this.preparerFactoryClass = preparerFactoryClass; 233 } 234 235 /** 236 * Set whether to use a MutableTilesContainer (typically the CachingTilesContainer 237 * implementation) for this application. Default is "false". 238 * @see org.apache.tiles.mgmt.MutableTilesContainer 239 * @see org.apache.tiles.impl.mgmt.CachingTilesContainer 240 */ 241 public void setUseMutableTilesContainer(boolean useMutableTilesContainer) { 242 this.useMutableTilesContainer = useMutableTilesContainer; 243 } 244 245 @Override 246 public void setServletContext(ServletContext servletContext) { 247 this.servletContext = servletContext; 248 } 249 250 /** 251 * Creates and exposes a TilesContainer for this web application, 252 * delegating to the TilesInitializer. 253 * @throws TilesException in case of setup failure 254 */ 255 @Override 256 public void afterPropertiesSet() throws TilesException { 257 TilesApplicationContext preliminaryContext = 258 new SpringWildcardServletTilesApplicationContext(this.servletContext); 259 if (this.tilesInitializer == null) { 260 this.tilesInitializer = createTilesInitializer(); 261 } 262 this.tilesInitializer.initialize(preliminaryContext); 263 } 264 265 /** 266 * Creates a new instance of {@code SpringTilesInitializer}. 267 * <p>Override it to use a different initializer. 268 * @see org.apache.tiles.web.startup.AbstractTilesListener#createTilesInitializer() 269 */ 270 protected TilesInitializer createTilesInitializer() { 271 return new SpringTilesInitializer(); 272 } 273 274 /** 275 * Removes the TilesContainer from this web application. 276 * @throws TilesException in case of cleanup failure 277 */ 278 @Override 279 public void destroy() throws TilesException { 280 this.tilesInitializer.destroy(); 281 } 282 283 284 private class SpringTilesInitializer extends AbstractTilesInitializer { 285 286 @Override 287 protected AbstractTilesContainerFactory createContainerFactory(TilesApplicationContext context) { 288 return new SpringTilesContainerFactory(); 289 } 290 } 291 292 293 private class SpringTilesContainerFactory extends BasicTilesContainerFactory { 294 295 @Override 296 protected BasicTilesContainer instantiateContainer(TilesApplicationContext context) { 297 return (useMutableTilesContainer ? new CachingTilesContainer() : new BasicTilesContainer()); 298 } 299 300 @Override 301 protected void registerRequestContextFactory(String className, 302 List<TilesRequestContextFactory> factories, TilesRequestContextFactory parent) { 303 // Avoid Tiles 2.2 warn logging when default RequestContextFactory impl class not found 304 if (ClassUtils.isPresent(className, TilesConfigurer.class.getClassLoader())) { 305 super.registerRequestContextFactory(className, factories, parent); 306 } 307 } 308 309 @Override 310 protected List<URL> getSourceURLs(TilesApplicationContext applicationContext, 311 TilesRequestContextFactory contextFactory) { 312 if (definitions != null) { 313 try { 314 List<URL> result = new LinkedList<URL>(); 315 for (String definition : definitions) { 316 Set<URL> resources = applicationContext.getResources(definition); 317 if (resources != null) { 318 result.addAll(resources); 319 } 320 } 321 return result; 322 } 323 catch (IOException ex) { 324 throw new DefinitionsFactoryException("Cannot load definition URLs", ex); 325 } 326 } 327 else { 328 return super.getSourceURLs(applicationContext, contextFactory); 329 } 330 } 331 332 @Override 333 protected BaseLocaleUrlDefinitionDAO instantiateLocaleDefinitionDao(TilesApplicationContext applicationContext, 334 TilesRequestContextFactory contextFactory, LocaleResolver resolver) { 335 BaseLocaleUrlDefinitionDAO dao = super.instantiateLocaleDefinitionDao( 336 applicationContext, contextFactory, resolver); 337 if (checkRefresh && dao instanceof CachingLocaleUrlDefinitionDAO) { 338 ((CachingLocaleUrlDefinitionDAO) dao).setCheckRefresh(true); 339 } 340 return dao; 341 } 342 343 @Override 344 protected DefinitionsReader createDefinitionsReader(TilesApplicationContext applicationContext, 345 TilesRequestContextFactory contextFactory) { 346 DigesterDefinitionsReader reader = new DigesterDefinitionsReader(); 347 if (!validateDefinitions){ 348 Map<String,String> map = new HashMap<String,String>(); 349 map.put(DigesterDefinitionsReader.PARSER_VALIDATE_PARAMETER_NAME, Boolean.FALSE.toString()); 350 reader.init(map); 351 } 352 return reader; 353 } 354 355 @Override 356 protected DefinitionsFactory createDefinitionsFactory(TilesApplicationContext applicationContext, 357 TilesRequestContextFactory contextFactory, LocaleResolver resolver) { 358 if (definitionsFactoryClass != null) { 359 DefinitionsFactory factory = BeanUtils.instantiate(definitionsFactoryClass); 360 if (factory instanceof TilesApplicationContextAware) { 361 ((TilesApplicationContextAware) factory).setApplicationContext(applicationContext); 362 } 363 BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(factory); 364 if (bw.isWritableProperty("localeResolver")) { 365 bw.setPropertyValue("localeResolver", resolver); 366 } 367 if (bw.isWritableProperty("definitionDAO")) { 368 bw.setPropertyValue("definitionDAO", 369 createLocaleDefinitionDao(applicationContext, contextFactory, resolver)); 370 } 371 if (factory instanceof Refreshable) { 372 ((Refreshable) factory).refresh(); 373 } 374 return factory; 375 } 376 else { 377 return super.createDefinitionsFactory(applicationContext, contextFactory, resolver); 378 } 379 } 380 381 @Override 382 protected PreparerFactory createPreparerFactory(TilesApplicationContext applicationContext, 383 TilesRequestContextFactory contextFactory) { 384 if (preparerFactoryClass != null) { 385 return BeanUtils.instantiate(preparerFactoryClass); 386 } 387 else { 388 return super.createPreparerFactory(applicationContext, contextFactory); 389 } 390 } 391 392 @Override 393 protected LocaleResolver createLocaleResolver(TilesApplicationContext applicationContext, 394 TilesRequestContextFactory contextFactory) { 395 return new SpringLocaleResolver(); 396 } 397 398 @Override 399 protected AttributeEvaluatorFactory createAttributeEvaluatorFactory(TilesApplicationContext applicationContext, 400 TilesRequestContextFactory contextFactory, LocaleResolver resolver) { 401 AttributeEvaluator evaluator; 402 if (tilesElPresent && JspFactory.getDefaultFactory() != null) { 403 evaluator = TilesElActivator.createEvaluator(applicationContext); 404 } 405 else { 406 evaluator = new DirectAttributeEvaluator(); 407 } 408 return new BasicAttributeEvaluatorFactory(evaluator); 409 } 410 } 411 412 413 private static class SpringCompleteAutoloadTilesInitializer extends CompleteAutoloadTilesInitializer { 414 415 @Override 416 protected AbstractTilesContainerFactory createContainerFactory(TilesApplicationContext context) { 417 return new SpringCompleteAutoloadTilesContainerFactory(); 418 } 419 } 420 421 422 private static class SpringCompleteAutoloadTilesContainerFactory extends CompleteAutoloadTilesContainerFactory { 423 424 @Override 425 protected LocaleResolver createLocaleResolver(TilesApplicationContext applicationContext, 426 TilesRequestContextFactory contextFactory) { 427 return new SpringLocaleResolver(); 428 } 429 } 430 431 432 private static class TilesElActivator { 433 434 public static AttributeEvaluator createEvaluator(TilesApplicationContext applicationContext) { 435 ELAttributeEvaluator evaluator = new ELAttributeEvaluator(); 436 evaluator.setApplicationContext(applicationContext); 437 evaluator.init(Collections.<String, String>emptyMap()); 438 return evaluator; 439 } 440 } 441 442}