This project has retired. For details please refer to its Attic page.
BasicTilesContainer xref
View Javadoc

1   /*
2    * $Id: BasicTilesContainer.java 749983 2009-03-04 12:41:31Z apetrelli $
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   * http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  package org.apache.tiles.impl;
22  
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  import org.apache.tiles.Attribute;
26  import org.apache.tiles.AttributeContext;
27  import org.apache.tiles.BasicAttributeContext;
28  import org.apache.tiles.Definition;
29  import org.apache.tiles.TilesApplicationContext;
30  import org.apache.tiles.TilesContainer;
31  import org.apache.tiles.context.TilesRequestContext;
32  import org.apache.tiles.context.TilesRequestContextFactory;
33  import org.apache.tiles.definition.DefinitionsFactory;
34  import org.apache.tiles.definition.DefinitionsFactoryException;
35  import org.apache.tiles.definition.NoSuchDefinitionException;
36  import org.apache.tiles.evaluator.AttributeEvaluator;
37  import org.apache.tiles.preparer.NoSuchPreparerException;
38  import org.apache.tiles.preparer.PreparerFactory;
39  import org.apache.tiles.preparer.ViewPreparer;
40  import org.apache.tiles.renderer.AttributeRenderer;
41  import org.apache.tiles.renderer.RendererFactory;
42  
43  import java.io.IOException;
44  import java.io.Writer;
45  import java.util.ArrayList;
46  import java.util.List;
47  import java.util.Map;
48  import java.util.Stack;
49  import java.util.StringTokenizer;
50  
51  /***
52   * Basic implementation of the tiles container interface.
53   * In most cases, this container will be customized by
54   * injecting customized services, not necessarily by
55   * override the container
56   *
57   * @since 2.0
58   * @version $Rev: 749983 $ $Date: 2009-03-04 13:41:31 +0100 (mer, 04 mar 2009) $
59   */
60  public class BasicTilesContainer implements TilesContainer {
61  
62      /***
63       * Constant representing the configuration parameter used to define the
64       * tiles definition resources.
65       *
66       * @deprecated Use
67       * {@link org.apache.tiles.definition.DefinitionsFactory#DEFINITIONS_CONFIG}.
68       */
69      public static final String DEFINITIONS_CONFIG = "org.apache.tiles.impl.BasicTilesContainer.DEFINITIONS_CONFIG";
70  
71      /***
72       * Compatibility constant.
73       *
74       * @deprecated use {@link #DEFINITIONS_CONFIG} to avoid namespace collisions.
75       */
76      private static final String LEGACY_DEFINITIONS_CONFIG = "definitions-config";
77  
78      /***
79       * Name used to store attribute context stack.
80       */
81      private static final String ATTRIBUTE_CONTEXT_STACK =
82          "org.apache.tiles.AttributeContext.STACK";
83  
84      /***
85       * Log instance for all BasicTilesContainer
86       * instances.
87       */
88      private final Log log =
89          LogFactory.getLog(BasicTilesContainer.class);
90  
91      /***
92       * The Tiles application context object.
93       */
94      private TilesApplicationContext context;
95  
96      /***
97       * The definitions factory.
98       */
99      private DefinitionsFactory definitionsFactory;
100 
101     /***
102      * The preparer factory.
103      */
104     private PreparerFactory preparerFactory;
105 
106     /***
107      * The renderer factory.
108      */
109     private RendererFactory rendererFactory;
110 
111     /***
112      * The attribute evaluator.
113      */
114     private AttributeEvaluator evaluator;
115 
116     /***
117      * The Tiles request context factory.
118      */
119     private TilesRequestContextFactory contextFactory;
120 
121     /***
122      * Initialization flag. If set, this container cannot be changed.
123      */
124     private boolean initialized = false;
125 
126     /***
127      * Initialize the Container with the given configuration.
128      *
129      * @param initParameters application context for this container
130      * @throws IllegalStateException If the container has been already
131      * initialized.
132      * @throws DefinitionsFactoryException If something goes wrong during
133      * initialization.
134      */
135     public void init(Map<String, String> initParameters) {
136         checkInit();
137         initialized = true;
138 
139         if (rendererFactory == null) {
140             throw new IllegalStateException("RendererFactory not specified");
141         }
142         if (preparerFactory == null) {
143             throw new IllegalStateException("PreparerFactory not specified");
144         }
145         if (definitionsFactory == null) {
146             throw new IllegalStateException("DefinitionsFactory not specified");
147         }
148         if (evaluator == null) {
149             throw new IllegalStateException("AttributeEvaluator not specified");
150         }
151         if (contextFactory == null) {
152             throw new IllegalStateException("TilesContextFactory not specified");
153         }
154         if (context == null) {
155             throw new IllegalStateException("TilesApplicationContext not specified");
156         }
157     }
158 
159     /*** {@inheritDoc} */
160     public AttributeContext startContext(Object... requestItems) {
161         TilesRequestContext tilesContext = getRequestContext(requestItems);
162         return startContext(tilesContext);
163     }
164 
165     /*** {@inheritDoc} */
166     public void endContext(Object... requestItems) {
167         TilesRequestContext tilesContext = getRequestContext(requestItems);
168         endContext(tilesContext);
169     }
170 
171     /*** {@inheritDoc} */
172     public void renderContext(Object... requestItems) {
173         TilesRequestContext request = getRequestContext(requestItems);
174         AttributeContext attributeContext = getAttributeContext(request);
175 
176         render(request, attributeContext);
177     }
178 
179     /***
180      * Returns the Tiles application context used by this container.
181      *
182      * @return the application context for this container.
183      */
184     public TilesApplicationContext getApplicationContext() {
185         return context;
186     }
187 
188     /***
189      * Sets the Tiles application context to use.
190      *
191      * @param context The Tiles application context.
192      */
193     public void setApplicationContext(TilesApplicationContext context) {
194         this.context = context;
195     }
196 
197     /*** {@inheritDoc} */
198     public AttributeContext getAttributeContext(Object... requestItems) {
199         TilesRequestContext tilesContext = getRequestContext(requestItems);
200         return getAttributeContext(tilesContext);
201 
202     }
203 
204     /***
205      * Returns the context factory.
206      *
207      * @return Always <code>null</code>.
208      * @deprecated Do not use it, it returns <code>null</code>. Use
209      * {@link #getRequestContextFactory()}.
210      */
211     @Deprecated
212     public org.apache.tiles.context.TilesContextFactory getContextFactory() {
213         return null;
214     }
215 
216     /***
217      * Returns the request context factory.
218      *
219      * @return The request context factory.
220      * @since 2.1.1
221      */
222     protected TilesRequestContextFactory getRequestContextFactory() {
223         return contextFactory;
224     }
225 
226     /***
227      * Sets the context factory.
228      *
229      * @param contextFactory The context factory.
230      * @deprecated Use
231      * {@link #setRequestContextFactory(TilesRequestContextFactory)}.
232      */
233     public void setContextFactory(org.apache.tiles.context.TilesContextFactory contextFactory) {
234         // Does nothing
235     }
236 
237     /***
238      * Sets the request context factory.
239      *
240      * @param contextFactory The context factory.
241      * @since 2.1.1
242      */
243     public void setRequestContextFactory(TilesRequestContextFactory contextFactory) {
244         checkInit();
245         this.contextFactory = contextFactory;
246     }
247 
248     /***
249      * Returns the definitions factory.
250      *
251      * @return The definitions factory used by this container.
252      */
253     public DefinitionsFactory getDefinitionsFactory() {
254         return definitionsFactory;
255     }
256 
257     /***
258      * Set the definitions factory. This method first ensures
259      * that the container has not yet been initialized.
260      *
261      * @param definitionsFactory the definitions factory for this instance.
262      */
263     public void setDefinitionsFactory(DefinitionsFactory definitionsFactory) {
264         checkInit();
265         this.definitionsFactory = definitionsFactory;
266     }
267 
268     /***
269      * Returns the preparer factory used by this container.
270      *
271      * @return return the preparerInstance factory used by this container.
272      */
273     public PreparerFactory getPreparerFactory() {
274         return preparerFactory;
275     }
276 
277     /***
278      * Set the preparerInstance factory.  This method first ensures
279      * that the container has not yet been initialized.
280      *
281      * @param preparerFactory the preparerInstance factory for this conainer.
282      */
283     public void setPreparerFactory(PreparerFactory preparerFactory) {
284         this.preparerFactory = preparerFactory;
285     }
286 
287     /***
288      * Sets the renderer instance factory.
289      *
290      * @param rendererFactory the renderer instance factory for this container.
291      * @since 2.1.0
292      */
293     public void setRendererFactory(RendererFactory rendererFactory) {
294         this.rendererFactory = rendererFactory;
295     }
296 
297     /***
298      * Sets the evaluator to use.
299      *
300      * @param evaluator The evaluator to use.
301      * @since 2.1.0
302      */
303     public void setEvaluator(AttributeEvaluator evaluator) {
304         this.evaluator = evaluator;
305     }
306 
307     /*** {@inheritDoc} */
308     public void prepare(String preparer, Object... requestItems) {
309         TilesRequestContext requestContext = getRequestContextFactory().createRequestContext(
310             getApplicationContext(),
311             requestItems
312         );
313         prepare(requestContext, preparer, false);
314     }
315 
316     /*** {@inheritDoc} */
317     public void render(String definitionName, Object... requestItems) {
318         TilesRequestContext requestContext = getRequestContextFactory().createRequestContext(
319             getApplicationContext(),
320             requestItems
321         );
322         render(requestContext, definitionName);
323     }
324 
325     /*** {@inheritDoc} */
326     @Deprecated
327     public void render(Attribute attr, Writer writer, Object... requestItems)
328         throws IOException {
329         render(attr, requestItems);
330     }
331 
332     /*** {@inheritDoc} */
333     public void render(Attribute attr, Object... requestItems)
334         throws IOException {
335         TilesRequestContext requestContext = getRequestContextFactory()
336                 .createRequestContext(getApplicationContext(), requestItems);
337         render(attr, requestContext);
338     }
339 
340     /*** {@inheritDoc} */
341     public Object evaluate(Attribute attribute, Object... requestItems) {
342         TilesRequestContext request = getRequestContextFactory()
343                 .createRequestContext(context, requestItems);
344         return evaluator.evaluate(attribute, request);
345     }
346 
347     /*** {@inheritDoc} */
348     public boolean isValidDefinition(String definitionName, Object... requestItems) {
349         return isValidDefinition(getRequestContext(requestItems), definitionName);
350     }
351 
352     /***
353      * Returns a definition specifying its name.
354      *
355      * @param definitionName The name of the definition to find.
356      * @param request The request context.
357      * @return The definition, if found.
358      * @throws DefinitionsFactoryException If the definitions factory throws an
359      * exception.
360      */
361     protected Definition getDefinition(String definitionName,
362             TilesRequestContext request) {
363         Definition definition =
364             definitionsFactory.getDefinition(definitionName, request);
365         return definition;
366     }
367 
368     /***
369      * Derive the resource string from the initialization parameters.
370      * If no parameter {@link #DEFINITIONS_CONFIG} is available, attempts
371      * to retrieve {@link #LEGACY_DEFINITIONS_CONFIG}.  If niether are
372      * available, returns "/WEB-INF/tiles.xml".
373      *
374      * @return resource string to be parsed.
375      */
376     protected String getResourceString() {
377         return getResourceString(context.getInitParams());
378     }
379 
380     /***
381      * Derive the resource string from the initialization parameters.
382      * If no parameter {@link #DEFINITIONS_CONFIG} is available, attempts
383      * to retrieve {@link #LEGACY_DEFINITIONS_CONFIG}.  If niether are
384      * available, returns "/WEB-INF/tiles.xml".
385      *
386      * @param parms The initialization parameters.
387      * @return resource string to be parsed.
388      */
389     protected String getResourceString(Map<String, String> parms) {
390         String resourceStr = parms.get(DEFINITIONS_CONFIG);
391         if (resourceStr == null) {
392             resourceStr = parms.get(LEGACY_DEFINITIONS_CONFIG);
393         }
394         if (resourceStr == null) {
395             resourceStr = "/WEB-INF/tiles.xml";
396         }
397         return resourceStr;
398     }
399 
400     /***
401      * Parse the resourceString into a list of resource paths
402      * which can be loaded by the application context.
403      *
404      * @param resourceString comma seperated resources
405      * @return parsed resources
406      */
407     protected List<String> getResourceNames(String resourceString) {
408         StringTokenizer tokenizer = new StringTokenizer(resourceString, ",");
409         List<String> filenames = new ArrayList<String>(tokenizer.countTokens());
410         while (tokenizer.hasMoreTokens()) {
411             filenames.add(tokenizer.nextToken().trim());
412         }
413         return filenames;
414     }
415 
416     /***
417      * Determine whether or not the container has been
418      * initialized. Utility method used for methods which
419      * can not be invoked after the container has been
420      * started.
421      *
422      * @throws IllegalStateException if the container has already been initialized.
423      */
424     protected void checkInit() {
425         if (initialized) {
426             throw new IllegalStateException("Container allready initialized");
427         }
428     }
429 
430     /***
431      * Initializes a definitions factory.
432      *
433      * @param definitionsFactory The factory to initialize.
434      * @param resourceString The string containing a comma-separated-list of
435      * resources.
436      * @param initParameters A map containing the initialization parameters.
437      * @throws DefinitionsFactoryException If something goes wrong.
438      * @deprecated Do not use, the Definitions Factory should be initialized by
439      * the Tiles Container Factory.
440      */
441     @Deprecated
442     protected void initializeDefinitionsFactory(
443             DefinitionsFactory definitionsFactory, String resourceString,
444             Map<String, String> initParameters) {
445         if (rendererFactory == null) {
446             throw new IllegalStateException("No RendererFactory found");
447         }
448 
449         definitionsFactory.init(initParameters);
450 
451         if (log.isInfoEnabled()) {
452             log.info("Tiles2 container initialization complete.");
453         }
454     }
455 
456     /***
457      * Returns the context stack.
458      *
459      * @param tilesContext The Tiles context object to use.
460      * @return The needed stack of contexts.
461      * @since 2.0.6
462      */
463     @SuppressWarnings("unchecked")
464     protected Stack<AttributeContext> getContextStack(TilesRequestContext tilesContext) {
465         Stack<AttributeContext> contextStack =
466             (Stack<AttributeContext>) tilesContext
467                 .getRequestScope().get(ATTRIBUTE_CONTEXT_STACK);
468         if (contextStack == null) {
469             contextStack = new Stack<AttributeContext>();
470             tilesContext.getRequestScope().put(ATTRIBUTE_CONTEXT_STACK,
471                     contextStack);
472         }
473 
474         return contextStack;
475     }
476 
477     /***
478      * Pushes a context object in the stack.
479      *
480      * @param context The context to push.
481      * @param tilesContext The Tiles context object to use.
482      * @since 2.0.6
483      */
484     protected void pushContext(AttributeContext context,
485             TilesRequestContext tilesContext) {
486         Stack<AttributeContext> contextStack = getContextStack(tilesContext);
487         contextStack.push(context);
488     }
489 
490     /***
491      * Pops a context object out of the stack.
492      *
493      * @param tilesContext The Tiles context object to use.
494      * @return The popped context object.
495      * @since 2.0.6
496      */
497     protected AttributeContext popContext(TilesRequestContext tilesContext) {
498         Stack<AttributeContext> contextStack = getContextStack(tilesContext);
499         return contextStack.pop();
500     }
501 
502     /***
503      * Get attribute context from request.
504      *
505      * @param tilesContext current Tiles application context.
506      * @return BasicAttributeContext or null if context is not found.
507      * @since 2.0.6
508      */
509     protected AttributeContext getContext(TilesRequestContext tilesContext) {
510         Stack<AttributeContext> contextStack = getContextStack(tilesContext);
511         if (!contextStack.isEmpty()) {
512             return contextStack.peek();
513         } else {
514             return null;
515         }
516     }
517 
518     /***
519      * Returns the current attribute context.
520      *
521      * @param tilesContext The request context to use.
522      * @return The current attribute context.
523      */
524     private AttributeContext getAttributeContext(TilesRequestContext tilesContext) {
525         AttributeContext context = getContext(tilesContext);
526         if (context == null) {
527             context = new BasicAttributeContext();
528             pushContext(context, tilesContext);
529         }
530         return context;
531     }
532 
533     /***
534      * Creates a Tiles request context from request items.
535      *
536      * @param requestItems The request items.
537      * @return The created Tiles request context.
538      */
539     private TilesRequestContext getRequestContext(Object... requestItems) {
540         return getRequestContextFactory().createRequestContext(
541                 getApplicationContext(), requestItems);
542     }
543 
544     /***
545      * Starts an attribute context inside the container.
546      *
547      * @param tilesContext The request context to use.
548      * @return The newly created attribute context.
549      */
550     private AttributeContext startContext(TilesRequestContext tilesContext) {
551         AttributeContext context = new BasicAttributeContext();
552         Stack<AttributeContext>  stack = getContextStack(tilesContext);
553         if (!stack.isEmpty()) {
554             AttributeContext parent = stack.peek();
555             context.inheritCascadedAttributes(parent);
556         }
557         stack.push(context);
558         return context;
559     }
560 
561     /***
562      * Releases and removes a previously created attribute context.
563      *
564      * @param tilesContext The request context to use.
565      */
566     private void endContext(TilesRequestContext tilesContext) {
567         popContext(tilesContext);
568     }
569 
570     /***
571      * Execute a preparer.
572      *
573      * @param context The request context.
574      * @param preparerName The name of the preparer.
575      * @param ignoreMissing If <code>true</code> if the preparer is not found,
576      * it ignores the problem.
577      * @throws NoSuchPreparerException If the preparer is not found (and
578      * <code>ignoreMissing</code> is not set) or if the preparer itself threw an
579      * exception.
580      */
581     private void prepare(TilesRequestContext context, String preparerName, boolean ignoreMissing) {
582 
583         if (log.isDebugEnabled()) {
584             log.debug("Prepare request received for '" + preparerName);
585         }
586 
587         ViewPreparer preparer = preparerFactory.getPreparer(preparerName, context);
588         if (preparer == null && ignoreMissing) {
589             return;
590         }
591 
592         if (preparer == null) {
593             throw new NoSuchPreparerException("Preparer '" + preparerName + " not found");
594         }
595 
596         AttributeContext attributeContext = getContext(context);
597 
598         preparer.execute(context, attributeContext);
599     }
600 
601     /***
602      * Renders the definition with specified name.
603      *
604      * @param request The request context.
605      * @param definitionName The name of the definition to render.
606      * @throws NoSuchDefinitionException If the definition has not been found.
607      * @throws DefinitionsFactoryException If something goes wrong when
608      * obtaining the definition.
609      * @since 2.1.3
610      */
611     protected void render(TilesRequestContext request, String definitionName) {
612 
613         if (log.isDebugEnabled()) {
614             log.debug("Render request recieved for definition '" + definitionName + "'");
615         }
616 
617         Definition definition = getDefinition(definitionName, request);
618 
619         if (definition == null) {
620             if (log.isWarnEnabled()) {
621                 String message = "Unable to find the definition '" + definitionName + "'";
622                 log.warn(message);
623             }
624             throw new NoSuchDefinitionException(definitionName);
625         }
626         render(request, definition);
627     }
628 
629     /***
630      * Renders the specified definition
631      * @param request The request context.
632      * @param definition The definition to render.
633      * @since 2.1.3
634      */
635     protected void render(TilesRequestContext request, Definition definition) {
636         AttributeContext originalContext = getAttributeContext(request);
637         BasicAttributeContext subContext = new BasicAttributeContext(originalContext);
638         subContext.inherit(definition);
639 
640         pushContext(subContext, request);
641 
642         try {
643             render(request, subContext);
644         } finally {
645             popContext(request);
646         }
647     }
648 
649     /***
650      * Renders an attribute.
651      *
652      * @param attr The attribute to render.
653      * @param requestContext The Tiles request context.
654      * @throws IOException If something goes wrong during rendering.
655      */
656     private void render(Attribute attr, TilesRequestContext requestContext)
657             throws IOException {
658         if (attr == null) {
659             throw new CannotRenderException("Cannot render a null attribute");
660         }
661 
662         AttributeRenderer renderer = rendererFactory.getRenderer(attr
663                 .getRenderer());
664         if (renderer == null) {
665             throw new CannotRenderException(
666                     "Cannot render an attribute with renderer name "
667                             + attr.getRenderer());
668         }
669         renderer.render(attr, requestContext);
670     }
671 
672     /***
673      * Renders the specified attribute context.
674      *
675      * @param request The request context.
676      * @param attributeContext The context to render.
677      * @throws InvalidTemplateException If the template is not valid.
678      * @throws CannotRenderException If something goes wrong during rendering.
679      * @since 2.1.3
680      */
681     protected void render(TilesRequestContext request,
682             AttributeContext attributeContext) {
683 
684         try {
685             if (attributeContext.getPreparer() != null) {
686                 prepare(request, attributeContext.getPreparer(), true);
687             }
688 
689             render(attributeContext.getTemplateAttribute(), request);
690         } catch (IOException e) {
691             throw new CannotRenderException(e.getMessage(), e);
692         }
693     }
694 
695     /***
696      * Checks if a string is a valid definition name.
697      *
698      * @param context The request context.
699      * @param definitionName The name of the definition to find.
700      * @return <code>true</code> if <code>definitionName</code> is a valid
701      * definition name.
702      */
703     private boolean isValidDefinition(TilesRequestContext context, String definitionName) {
704         try {
705             Definition definition = getDefinition(definitionName, context);
706             return definition != null;
707         } catch (NoSuchDefinitionException nsde) {
708             return false;
709         } catch (DefinitionsFactoryException e) {
710             // TODO, is this the right thing to do?
711             return false;
712         }
713     }
714 }