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