1616
1717package org .springframework .web .servlet .view .tiles2 ;
1818
19+ import java .io .IOException ;
20+ import java .lang .reflect .Constructor ;
21+ import java .lang .reflect .Method ;
22+ import java .net .URL ;
1923import java .util .HashMap ;
24+ import java .util .LinkedList ;
25+ import java .util .List ;
2026import java .util .Map ;
2127import java .util .Properties ;
2228import javax .servlet .ServletContext ;
2632import org .apache .tiles .TilesApplicationContext ;
2733import org .apache .tiles .TilesException ;
2834import org .apache .tiles .context .AbstractTilesApplicationContextFactory ;
35+ import org .apache .tiles .context .TilesRequestContextFactory ;
2936import org .apache .tiles .definition .DefinitionsFactory ;
37+ import org .apache .tiles .definition .DefinitionsFactoryException ;
38+ import org .apache .tiles .definition .DefinitionsReader ;
3039import org .apache .tiles .definition .digester .DigesterDefinitionsReader ;
40+ import org .apache .tiles .evaluator .AttributeEvaluator ;
3141import org .apache .tiles .evaluator .el .ELAttributeEvaluator ;
3242import org .apache .tiles .evaluator .impl .DirectAttributeEvaluator ;
43+ import org .apache .tiles .factory .AbstractTilesContainerFactory ;
44+ import org .apache .tiles .factory .BasicTilesContainerFactory ;
3345import org .apache .tiles .factory .TilesContainerFactory ;
46+ import org .apache .tiles .impl .BasicTilesContainer ;
47+ import org .apache .tiles .impl .mgmt .CachingTilesContainer ;
48+ import org .apache .tiles .locale .LocaleResolver ;
3449import org .apache .tiles .preparer .BasicPreparerFactory ;
50+ import org .apache .tiles .preparer .PreparerFactory ;
3551import org .apache .tiles .servlet .context .ServletUtil ;
3652import org .apache .tiles .startup .BasicTilesInitializer ;
3753import org .apache .tiles .startup .TilesInitializer ;
3854
55+ import org .springframework .beans .BeanUtils ;
3956import org .springframework .beans .factory .DisposableBean ;
4057import org .springframework .beans .factory .InitializingBean ;
4158import org .springframework .util .ClassUtils ;
4259import org .springframework .util .CollectionUtils ;
60+ import org .springframework .util .ReflectionUtils ;
4361import org .springframework .util .StringUtils ;
4462import org .springframework .web .context .ServletContextAware ;
4563
4967 * for more information about Tiles, which basically is a templating
5068 * mechanism for JSP-based web applications.
5169 *
52- * <b>Note: Spring 3.0 requires Tiles 2.1.2 or above.</b>
53- * Tiles EL support will be activated by default when running on JSP 2.1 or above.
70+ * <b>Note: Spring 3.0 requires Tiles 2.1.2 or above, with explicit support for Tiles 2.2.</b>
71+ * Tiles 2.1's EL support will be activated by default when running on JSP 2.1 or above.
72+ * Note that EL support is <i>not</> active by default when running against Tiles 2.2.
5473 *
5574 * <p>The TilesConfigurer simply configures a TilesContainer using a set of files
5675 * containing definitions, to be accessed by {@link TilesView} instances. This is a
@@ -87,8 +106,26 @@ public class TilesConfigurer implements ServletContextAware, InitializingBean, D
87106 private static final boolean jsp21Present = ClassUtils .isPresent (
88107 "javax.servlet.jsp.JspApplicationContext" , TilesConfigurer .class .getClassLoader ());
89108
109+ private static final boolean tiles22Present = ClassUtils .isPresent (
110+ "org.apache.tiles.evaluator.AttributeEvaluatorFactory" , TilesConfigurer .class .getClassLoader ());
111+
112+
90113 protected final Log logger = LogFactory .getLog (getClass ());
91114
115+ private TilesInitializer tilesInitializer ;
116+
117+ private boolean overrideLocaleResolver = false ;
118+
119+ private String [] definitions ;
120+
121+ private boolean validateDefinitions = true ;
122+
123+ private Class <? extends DefinitionsFactory > definitionsFactoryClass ;
124+
125+ private Class <? extends PreparerFactory > preparerFactoryClass ;
126+
127+ private boolean useMutableTilesContainer = false ;
128+
92129 private final Map <String , String > tilesPropertyMap = new HashMap <String , String >();
93130
94131 private ServletContext servletContext ;
@@ -108,24 +145,68 @@ public TilesConfigurer() {
108145 }
109146
110147
148+ /**
149+ * Configure Tiles using a custom TilesInitializer, typically specified as an inner bean.
150+ * <p>Default is a variant of {@link org.apache.tiles.startup.DefaultTilesInitializer},
151+ * respecting the "definitions", "preparerFactoryClass" etc properties on this configurer.
152+ * <p><b>NOTE: Specifying a custom TilesInitializer effectively disables all other bean
153+ * properties on this configurer.</b> The entire initialization procedure is then left
154+ * to the TilesInitializer as specified.
155+ */
156+ public void setTilesInitializer (TilesInitializer tilesInitializer ) {
157+ this .tilesInitializer = tilesInitializer ;
158+ }
159+
160+ /**
161+ * Specify whether to apply Tiles 2.2's "complete-autoload" configuration.
162+ * <p>See {@link org.apache.tiles.extras.complete.CompleteAutoloadTilesContainerFactory}
163+ * for details on the complete-autoload mode.
164+ * <p><b>NOTE: Specifying the complete-autoload mode effectively disables all other bean
165+ * properties on this configurer.</b> The entire initialization procedure is then left
166+ * to {@link org.apache.tiles.extras.complete.CompleteAutoloadTilesInitializer}.
167+ * @see org.apache.tiles.extras.complete.CompleteAutoloadTilesContainerFactory
168+ * @see org.apache.tiles.extras.complete.CompleteAutoloadTilesInitializer
169+ */
170+ public void setCompleteAutoload (boolean completeAutoload ) {
171+ if (completeAutoload ) {
172+ try {
173+ Class clazz = getClass ().getClassLoader ().loadClass (
174+ "org.apache.tiles.extras.complete.CompleteAutoloadTilesInitializer" );
175+ this .tilesInitializer = (TilesInitializer ) clazz .newInstance ();
176+ }
177+ catch (Exception ex ) {
178+ throw new IllegalStateException ("Tiles 2.2 not available" , ex );
179+ }
180+ }
181+ else {
182+ this .tilesInitializer = null ;
183+ }
184+ this .overrideLocaleResolver = completeAutoload ;
185+ }
186+
111187 /**
112188 * Set the Tiles definitions, i.e. the list of files containing the definitions.
113189 * Default is "/WEB-INF/tiles.xml".
114190 */
115191 public void setDefinitions (String [] definitions ) {
192+ this .definitions = definitions ;
116193 if (definitions != null ) {
117194 String defs = StringUtils .arrayToCommaDelimitedString (definitions );
118195 if (logger .isInfoEnabled ()) {
119196 logger .info ("TilesConfigurer: adding definitions [" + defs + "]" );
120197 }
121198 this .tilesPropertyMap .put (DefinitionsFactory .DEFINITIONS_CONFIG , defs );
122199 }
200+ else {
201+ this .tilesPropertyMap .remove (DefinitionsFactory .DEFINITIONS_CONFIG );
202+ }
123203 }
124204
125205 /**
126206 * Set whether to validate the Tiles XML definitions. Default is "true".
127207 */
128208 public void setValidateDefinitions (boolean validateDefinitions ) {
209+ this .validateDefinitions = validateDefinitions ;
129210 this .tilesPropertyMap .put (DigesterDefinitionsReader .PARSER_VALIDATE_PARAMETER_NAME ,
130211 Boolean .toString (validateDefinitions ));
131212 }
@@ -139,7 +220,8 @@ public void setValidateDefinitions(boolean validateDefinitions) {
139220 * DefinitionsFactory has to be able to handle {@link java.net.URL} source objects,
140221 * unless you configure a different TilesContainerFactory.
141222 */
142- public void setDefinitionsFactoryClass (Class definitionsFactoryClass ) {
223+ public void setDefinitionsFactoryClass (Class <? extends DefinitionsFactory > definitionsFactoryClass ) {
224+ this .definitionsFactoryClass = definitionsFactoryClass ;
143225 this .tilesPropertyMap .put (TilesContainerFactory .DEFINITIONS_FACTORY_INIT_PARAM ,
144226 definitionsFactoryClass .getName ());
145227 }
@@ -163,23 +245,29 @@ public void setDefinitionsFactoryClass(Class definitionsFactoryClass) {
163245 * @see SimpleSpringPreparerFactory
164246 * @see SpringBeanPreparerFactory
165247 */
166- public void setPreparerFactoryClass (Class preparerFactoryClass ) {
248+ public void setPreparerFactoryClass (Class <? extends PreparerFactory > preparerFactoryClass ) {
249+ this .preparerFactoryClass = preparerFactoryClass ;
167250 this .tilesPropertyMap .put (TilesContainerFactory .PREPARER_FACTORY_INIT_PARAM ,
168251 preparerFactoryClass .getName ());
169252 }
170253
171254 /**
172- * Set whether to use a MutableTilesContainer for this application.
173- * Default is "false".
255+ * Set whether to use a MutableTilesContainer (typically the CachingTilesContainer
256+ * implementation) for this application. Default is "false".
257+ * @see org.apache.tiles.mgmt.MutableTilesContainer
258+ * @see org.apache.tiles.mgmt.CachingTilesContainer
174259 */
175260 public void setUseMutableTilesContainer (boolean useMutableTilesContainer ) {
261+ this .useMutableTilesContainer = useMutableTilesContainer ;
176262 this .tilesPropertyMap .put (TilesContainerFactory .CONTAINER_FACTORY_MUTABLE_INIT_PARAM ,
177263 Boolean .toString (useMutableTilesContainer ));
178264 }
179265
180266 /**
181267 * Set Tiles properties (equivalent to the ServletContext init-params in
182268 * the Tiles documentation), overriding the default settings.
269+ * <p><b>NOTE: This property is only effective with Tiles 2.1.</b>
270+ * Tiles 2.2 doesn't support property-based configuration anymore.
183271 */
184272 public void setTilesProperties (Properties tilesProperties ) {
185273 CollectionUtils .mergePropertiesIntoMap (tilesProperties , this .tilesPropertyMap );
@@ -200,7 +288,46 @@ public void afterPropertiesSet() throws TilesException {
200288 SpringTilesApplicationContextFactory factory = new SpringTilesApplicationContextFactory ();
201289 factory .init (this .tilesPropertyMap );
202290 TilesApplicationContext preliminaryContext = factory .createApplicationContext (this .servletContext );
203- createTilesInitializer ().initialize (preliminaryContext );
291+ if (this .tilesInitializer == null ) {
292+ this .tilesInitializer = createTilesInitializer ();
293+ }
294+ this .tilesInitializer .initialize (preliminaryContext );
295+
296+ if (this .overrideLocaleResolver ) {
297+ // We need to do this after initialization simply because we're reusing the
298+ // original CompleteAutoloadTilesInitializer above. We cannot subclass
299+ // CompleteAutoloadTilesInitializer when compiling against Tiles 2.1...
300+ try {
301+ BasicTilesContainer container = (BasicTilesContainer ) ServletUtil .getContainer (this .servletContext );
302+ DefinitionsFactory definitionsFactory = container .getDefinitionsFactory ();
303+ Method setter = definitionsFactory .getClass ().getMethod ("setLocaleResolver" , LocaleResolver .class );
304+ setter .invoke (definitionsFactory , new SpringLocaleResolver ());
305+ }
306+ catch (Exception ex ) {
307+ throw new IllegalStateException ("Cannot override LocaleResolver with SpringLocaleResolver" , ex );
308+ }
309+ }
310+
311+ if (jsp21Present && this .tilesInitializer instanceof SpringTilesInitializer ) {
312+ // Again, we need to do this after initialization since SpringTilesContainerFactory
313+ // cannot override template methods that refer to Tiles 2.2 classes: in this case,
314+ // AttributeEvaluatorFactory as createAttributeEvaluatorFactory return type.
315+ try {
316+ BasicTilesContainer container = (BasicTilesContainer ) ServletUtil .getContainer (this .servletContext );
317+ Class aef = getClass ().getClassLoader ().loadClass ("org.apache.tiles.evaluator.AttributeEvaluatorFactory" );
318+ Class baef = getClass ().getClassLoader ().loadClass ("org.apache.tiles.evaluator.BasicAttributeEvaluatorFactory" );
319+ Constructor baefCtor = baef .getConstructor (AttributeEvaluator .class );
320+ ELAttributeEvaluator evaluator = new ELAttributeEvaluator ();
321+ evaluator .setApplicationContext (container .getApplicationContext ());
322+ evaluator .init (new HashMap <String , String >());
323+ Object baefValue = baefCtor .newInstance (evaluator );
324+ Method setter = container .getClass ().getMethod ("setAttributeEvaluatorFactory" , aef );
325+ setter .invoke (container , baefValue );
326+ }
327+ catch (Exception ex ) {
328+ throw new IllegalStateException ("Cannot activate ELAttributeEvaluator" , ex );
329+ }
330+ }
204331 }
205332
206333 /**
@@ -209,15 +336,100 @@ public void afterPropertiesSet() throws TilesException {
209336 * @see org.apache.tiles.web.startup.TilesListener#createTilesInitializer()
210337 */
211338 protected TilesInitializer createTilesInitializer () {
212- return new BasicTilesInitializer ();
339+ return ( tiles22Present ? new SpringTilesInitializer () : new BasicTilesInitializer () );
213340 }
214341
215342 /**
216343 * Removes the TilesContainer from this web application.
217344 * @throws TilesException in case of cleanup failure
218345 */
219346 public void destroy () throws TilesException {
220- ServletUtil .setContainer (this .servletContext , null );
347+ try {
348+ // Tiles 2.2?
349+ ReflectionUtils .invokeMethod (TilesInitializer .class .getMethod ("destroy" ), this .tilesInitializer );
350+ }
351+ catch (NoSuchMethodException ex ) {
352+ // Tiles 2.1...
353+ ServletUtil .setContainer (this .servletContext , null );
354+ }
355+ }
356+
357+
358+ private class SpringTilesInitializer extends BasicTilesInitializer {
359+
360+ @ Override
361+ protected AbstractTilesContainerFactory createContainerFactory (TilesApplicationContext context ) {
362+ return new SpringTilesContainerFactory ();
363+ }
364+ }
365+
366+
367+ private class SpringTilesContainerFactory extends BasicTilesContainerFactory {
368+
369+ @ Override
370+ protected BasicTilesContainer instantiateContainer (TilesApplicationContext context ) {
371+ return (useMutableTilesContainer ? new CachingTilesContainer () : new BasicTilesContainer ());
372+ }
373+
374+ @ Override
375+ protected List <URL > getSourceURLs (TilesApplicationContext applicationContext ,
376+ TilesRequestContextFactory contextFactory ) {
377+ if (definitions != null ) {
378+ try {
379+ List <URL > result = new LinkedList <URL >();
380+ for (String definition : definitions ) {
381+ result .addAll (applicationContext .getResources (definition ));
382+ }
383+ return result ;
384+ }
385+ catch (IOException ex ) {
386+ throw new DefinitionsFactoryException ("Cannot load definition URLs" , ex );
387+ }
388+ }
389+ else {
390+ return super .getSourceURLs (applicationContext , contextFactory );
391+ }
392+ }
393+
394+ @ Override
395+ protected DefinitionsReader createDefinitionsReader (TilesApplicationContext applicationContext ,
396+ TilesRequestContextFactory contextFactory ) {
397+ DigesterDefinitionsReader reader = new DigesterDefinitionsReader ();
398+ if (!validateDefinitions ){
399+ Map <String ,String > map = new HashMap <String ,String >();
400+ map .put (DigesterDefinitionsReader .PARSER_VALIDATE_PARAMETER_NAME , Boolean .FALSE .toString ());
401+ reader .init (map );
402+ }
403+ return reader ;
404+ }
405+
406+ @ Override
407+ protected LocaleResolver createLocaleResolver (TilesApplicationContext applicationContext ,
408+ TilesRequestContextFactory contextFactory ) {
409+ return new SpringLocaleResolver ();
410+ }
411+
412+ @ Override
413+ protected DefinitionsFactory createDefinitionsFactory (TilesApplicationContext applicationContext ,
414+ TilesRequestContextFactory contextFactory , LocaleResolver resolver ) {
415+ if (definitionsFactoryClass != null ) {
416+ return BeanUtils .instantiate (definitionsFactoryClass );
417+ }
418+ else {
419+ return super .createDefinitionsFactory (applicationContext , contextFactory , resolver );
420+ }
421+ }
422+
423+ @ Override
424+ protected PreparerFactory createPreparerFactory (TilesApplicationContext applicationContext ,
425+ TilesRequestContextFactory contextFactory ) {
426+ if (preparerFactoryClass != null ) {
427+ return BeanUtils .instantiate (preparerFactoryClass );
428+ }
429+ else {
430+ return super .createPreparerFactory (applicationContext , contextFactory );
431+ }
432+ }
221433 }
222434
223435}
0 commit comments