Project Tiles has retired. For details please refer to its Attic page.
BasicTilesContainer xref
View Javadoc

1   /*
2    * $Id: BasicTilesContainer.java 1330672 2012-04-26 06:58:16Z nlebas $
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 java.io.IOException;
24  import java.util.Deque;
25  import java.util.LinkedList;
26  import java.util.Map;
27  
28  import org.apache.tiles.Attribute;
29  import org.apache.tiles.AttributeContext;
30  import org.apache.tiles.BasicAttributeContext;
31  import org.apache.tiles.Definition;
32  import org.apache.tiles.TilesContainer;
33  import org.apache.tiles.definition.DefinitionsFactory;
34  import org.apache.tiles.definition.NoSuchDefinitionException;
35  import org.apache.tiles.evaluator.AttributeEvaluator;
36  import org.apache.tiles.evaluator.AttributeEvaluatorFactory;
37  import org.apache.tiles.evaluator.AttributeEvaluatorFactoryAware;
38  import org.apache.tiles.preparer.ViewPreparer;
39  import org.apache.tiles.preparer.factory.NoSuchPreparerException;
40  import org.apache.tiles.preparer.factory.PreparerFactory;
41  import org.apache.tiles.request.ApplicationContext;
42  import org.apache.tiles.request.Request;
43  import org.apache.tiles.request.render.CannotRenderException;
44  import org.apache.tiles.request.render.Renderer;
45  import org.apache.tiles.request.render.RendererFactory;
46  import org.slf4j.Logger;
47  import org.slf4j.LoggerFactory;
48  
49  /**
50   * Basic implementation of the tiles container interface.
51   * In most cases, this container will be customized by
52   * injecting customized services, not necessarily by
53   * override the container
54   *
55   * @since 2.0
56   * @version $Rev: 1330672 $ $Date: 2012-04-26 16:58:16 +1000 (Thu, 26 Apr 2012) $
57   */
58  public class BasicTilesContainer implements TilesContainer,
59          AttributeEvaluatorFactoryAware {
60  
61      /**
62       * Name used to store attribute context stack.
63       */
64      private static final String ATTRIBUTE_CONTEXT_STACK =
65          "org.apache.tiles.AttributeContext.STACK";
66  
67      /**
68       * Log instance for all BasicTilesContainer
69       * instances.
70       */
71      private final Logger log = LoggerFactory
72              .getLogger(BasicTilesContainer.class);
73  
74      /**
75       * The Tiles application context object.
76       */
77      private ApplicationContext context;
78  
79      /**
80       * The definitions factory.
81       */
82      private DefinitionsFactory definitionsFactory;
83  
84      /**
85       * The preparer factory.
86       */
87      private PreparerFactory preparerFactory;
88  
89      /**
90       * The renderer factory.
91       */
92      private RendererFactory rendererFactory;
93  
94      /**
95       * The attribute evaluator.
96       */
97      private AttributeEvaluatorFactory attributeEvaluatorFactory;
98  
99      /** {@inheritDoc} */
100     public AttributeContext startContext(Request request) {
101         AttributeContext context = new BasicAttributeContext();
102         Deque<AttributeContext>  stack = getContextStack(request);
103         if (!stack.isEmpty()) {
104             AttributeContext parent = stack.peek();
105             context.inheritCascadedAttributes(parent);
106         }
107         stack.push(context);
108         return context;
109     }
110 
111     /** {@inheritDoc} */
112     public void endContext(Request request) {
113         popContext(request);
114     }
115 
116     /** {@inheritDoc} */
117     public void renderContext(Request request) {
118         AttributeContext attributeContext = getAttributeContext(request);
119 
120         render(request, attributeContext);
121     }
122 
123     /**
124      * Returns the Tiles application context used by this container.
125      *
126      * @return the application context for this container.
127      */
128     public ApplicationContext getApplicationContext() {
129         return context;
130     }
131 
132     /**
133      * Sets the Tiles application context to use.
134      *
135      * @param context The Tiles application context.
136      */
137     public void setApplicationContext(ApplicationContext context) {
138         this.context = context;
139     }
140 
141     /** {@inheritDoc} */
142     public AttributeContext getAttributeContext(Request request) {
143         AttributeContext context = getContext(request);
144         if (context == null) {
145             context = new BasicAttributeContext();
146             pushContext(context, request);
147         }
148         return context;
149 
150     }
151 
152     /**
153      * Returns the definitions factory.
154      *
155      * @return The definitions factory used by this container.
156      */
157     public DefinitionsFactory getDefinitionsFactory() {
158         return definitionsFactory;
159     }
160 
161     /**
162      * Set the definitions factory. This method first ensures
163      * that the container has not yet been initialized.
164      *
165      * @param definitionsFactory the definitions factory for this instance.
166      */
167     public void setDefinitionsFactory(DefinitionsFactory definitionsFactory) {
168         this.definitionsFactory = definitionsFactory;
169     }
170 
171     /**
172      * Returns the preparer factory used by this container.
173      *
174      * @return return the preparerInstance factory used by this container.
175      */
176     public PreparerFactory getPreparerFactory() {
177         return preparerFactory;
178     }
179 
180     /**
181      * Set the preparerInstance factory.  This method first ensures
182      * that the container has not yet been initialized.
183      *
184      * @param preparerFactory the preparerInstance factory for this conainer.
185      */
186     public void setPreparerFactory(PreparerFactory preparerFactory) {
187         this.preparerFactory = preparerFactory;
188     }
189 
190     /**
191      * Sets the renderer instance factory.
192      *
193      * @param rendererFactory the renderer instance factory for this container.
194      * @since 2.1.0
195      */
196     public void setRendererFactory(RendererFactory rendererFactory) {
197         this.rendererFactory = rendererFactory;
198     }
199 
200     /** {@inheritDoc} */
201     public void setAttributeEvaluatorFactory(
202             AttributeEvaluatorFactory attributeEvaluatorFactory) {
203         this.attributeEvaluatorFactory = attributeEvaluatorFactory;
204     }
205 
206     /** {@inheritDoc} */
207     public void prepare(String preparer, Request request) {
208         prepare(request, preparer, false);
209     }
210 
211     /** {@inheritDoc} */
212     public void render(String definitionName, Request request) {
213         log.debug("Render request received for definition '{}'", definitionName);
214 
215         Definition definition = getDefinition(definitionName, request);
216 
217         if (definition == null) {
218             throw new NoSuchDefinitionException("Unable to find the definition '" + definitionName + "'");
219         }
220 
221         render(definition, request);
222     }
223 
224     /**
225      * Renders the specified definition.
226      * @param definition The definition to render.
227      * @param request The request context.
228      * @since 2.1.3
229      */
230     public void render(Definition definition, Request request) {
231         AttributeContext originalContext = getAttributeContext(request);
232         BasicAttributeContext subContext = new BasicAttributeContext(originalContext);
233         subContext.inherit(definition);
234 
235         pushContext(subContext, request);
236 
237         try {
238             render(request, subContext);
239         } finally {
240             popContext(request);
241         }
242     }
243 
244     /** {@inheritDoc} */
245     public void render(Attribute attr, Request request)
246         throws IOException {
247         if (attr == null) {
248             throw new CannotRenderException("Cannot render a null attribute");
249         }
250 
251         if (attr.isPermitted(request)) {
252             Renderer renderer = rendererFactory.getRenderer(attr.getRenderer());
253             Object value = evaluate(attr, request);
254             if (!(value instanceof String)) {
255                 throw new CannotRenderException(
256                         "Cannot render an attribute that is not a string, toString returns: "
257                                 + value);
258             }
259             renderer.render((String) value, request);
260         }
261     }
262 
263     /** {@inheritDoc} */
264     public Object evaluate(Attribute attribute, Request request) {
265         AttributeEvaluator evaluator = attributeEvaluatorFactory
266                 .getAttributeEvaluator(attribute);
267         return evaluator.evaluate(attribute, request);
268     }
269 
270     /** {@inheritDoc} */
271     public boolean isValidDefinition(String definitionName, Request request) {
272         try {
273             Definition definition = getDefinition(definitionName, request);
274             return definition != null;
275         } catch (NoSuchDefinitionException nsde) {
276             log.debug("Cannot find definition '{}'", definitionName);
277             log.debug("Exception related to the not found definition", nsde);
278             return false;
279         }
280     }
281 
282     /** {@inheritDoc} */
283     @Override
284     public Definition getDefinition(String definitionName,
285             Request request) {
286         Definition definition =
287             definitionsFactory.getDefinition(definitionName, request);
288         return definition;
289     }
290 
291     /**
292      * Returns the context stack.
293      *
294      * @param tilesContext The Tiles context object to use.
295      * @return The needed stack of contexts.
296      * @since 2.0.6
297      */
298     @SuppressWarnings("unchecked")
299     protected Deque<AttributeContext> getContextStack(Request tilesContext) {
300         Map<String, Object> requestScope = tilesContext.getContext("request");
301         Deque<AttributeContext> contextStack = (Deque<AttributeContext>) requestScope
302                 .get(ATTRIBUTE_CONTEXT_STACK);
303         if (contextStack == null) {
304             contextStack = new LinkedList<AttributeContext>();
305             requestScope.put(ATTRIBUTE_CONTEXT_STACK, contextStack);
306         }
307 
308         return contextStack;
309     }
310 
311     /**
312      * Pushes a context object in the stack.
313      *
314      * @param context The context to push.
315      * @param tilesContext The Tiles context object to use.
316      * @since 2.0.6
317      */
318     protected void pushContext(AttributeContext context,
319             Request tilesContext) {
320         Deque<AttributeContext> contextStack = getContextStack(tilesContext);
321         contextStack.push(context);
322     }
323 
324     /**
325      * Pops a context object out of the stack.
326      *
327      * @param tilesContext The Tiles context object to use.
328      * @return The popped context object.
329      * @since 2.0.6
330      */
331     protected AttributeContext popContext(Request tilesContext) {
332         Deque<AttributeContext> contextStack = getContextStack(tilesContext);
333         return contextStack.pop();
334     }
335 
336     /**
337      * Get attribute context from request.
338      *
339      * @param tilesContext current Tiles application context.
340      * @return BasicAttributeContext or null if context is not found.
341      * @since 2.0.6
342      */
343     protected AttributeContext getContext(Request tilesContext) {
344         Deque<AttributeContext> contextStack = getContextStack(tilesContext);
345         if (!contextStack.isEmpty()) {
346             return contextStack.peek();
347         }
348         return null;
349     }
350 
351     /**
352      * Execute a preparer.
353      *
354      * @param context The request context.
355      * @param preparerName The name of the preparer.
356      * @param ignoreMissing If <code>true</code> if the preparer is not found,
357      * it ignores the problem.
358      * @throws NoSuchPreparerException If the preparer is not found (and
359      * <code>ignoreMissing</code> is not set) or if the preparer itself threw an
360      * exception.
361      */
362     private void prepare(Request context, String preparerName, boolean ignoreMissing) {
363 
364         log.debug("Prepare request received for '{}'", preparerName);
365 
366         ViewPreparer preparer = preparerFactory.getPreparer(preparerName, context);
367         if (preparer == null && ignoreMissing) {
368             return;
369         }
370 
371         if (preparer == null) {
372             throw new NoSuchPreparerException("Preparer '" + preparerName + " not found");
373         }
374 
375         AttributeContext attributeContext = getContext(context);
376 
377         preparer.execute(context, attributeContext);
378     }
379 
380     /**
381      * Renders the specified attribute context.
382      *
383      * @param request The request context.
384      * @param attributeContext The context to render.
385      * @throws InvalidTemplateException If the template is not valid.
386      * @throws CannotRenderException If something goes wrong during rendering.
387      * @since 2.1.3
388      */
389     protected void render(Request request,
390             AttributeContext attributeContext) {
391 
392         try {
393             if (attributeContext.getPreparer() != null) {
394                 prepare(request, attributeContext.getPreparer(), true);
395             }
396 
397             render(attributeContext.getTemplateAttribute(), request);
398         } catch (IOException e) {
399             throw new CannotRenderException(e.getMessage(), e);
400         }
401     }
402 }