KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tapestry > pageload > PageLoader


1 // Copyright 2004, 2005 The Apache Software Foundation
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15 package org.apache.tapestry.pageload;
16
17 import java.util.ArrayList JavaDoc;
18 import java.util.Iterator JavaDoc;
19 import java.util.List JavaDoc;
20 import java.util.Locale JavaDoc;
21
22 import org.apache.commons.logging.Log;
23 import org.apache.hivemind.ApplicationRuntimeException;
24 import org.apache.hivemind.ClassResolver;
25 import org.apache.hivemind.HiveMind;
26 import org.apache.hivemind.Location;
27 import org.apache.hivemind.service.ThreadLocale;
28 import org.apache.tapestry.AbstractComponent;
29 import org.apache.tapestry.BaseComponent;
30 import org.apache.tapestry.IAsset;
31 import org.apache.tapestry.IBinding;
32 import org.apache.tapestry.IComponent;
33 import org.apache.tapestry.IEngine;
34 import org.apache.tapestry.INamespace;
35 import org.apache.tapestry.IPage;
36 import org.apache.tapestry.IRequestCycle;
37 import org.apache.tapestry.ITemplateComponent;
38 import org.apache.tapestry.asset.AssetSource;
39 import org.apache.tapestry.binding.BindingConstants;
40 import org.apache.tapestry.binding.BindingSource;
41 import org.apache.tapestry.binding.BindingUtils;
42 import org.apache.tapestry.binding.ExpressionBinding;
43 import org.apache.tapestry.binding.ListenerBinding;
44 import org.apache.tapestry.coerce.ValueConverter;
45 import org.apache.tapestry.engine.IPageLoader;
46 import org.apache.tapestry.event.ChangeObserver;
47 import org.apache.tapestry.resolver.ComponentSpecificationResolver;
48 import org.apache.tapestry.services.BSFManagerFactory;
49 import org.apache.tapestry.services.ComponentConstructor;
50 import org.apache.tapestry.services.ComponentConstructorFactory;
51 import org.apache.tapestry.services.ComponentTemplateLoader;
52 import org.apache.tapestry.spec.BindingType;
53 import org.apache.tapestry.spec.IAssetSpecification;
54 import org.apache.tapestry.spec.IBindingSpecification;
55 import org.apache.tapestry.spec.IComponentSpecification;
56 import org.apache.tapestry.spec.IContainedComponent;
57 import org.apache.tapestry.spec.IListenerBindingSpecification;
58 import org.apache.tapestry.spec.IParameterSpecification;
59
60 /**
61  * Runs the process of building the component hierarchy for an entire page.
62  * <p>
63  * This implementation is not threadsafe, therefore the pooled service model must be used.
64  *
65  * @author Howard Lewis Ship
66  */

67
68 public class PageLoader implements IPageLoader
69 {
70     private Log _log;
71
72     /** @since 4.0 */
73
74     private ComponentSpecificationResolver _componentResolver;
75
76     /** @since 4.0 */
77
78     private String JavaDoc _defaultScriptLanguage;
79
80     /** @since 4.0 */
81
82     private BindingSource _bindingSource;
83
84     /** @since 4.0 */
85
86     private ComponentTemplateLoader _componentTemplateLoader;
87
88     /** @since 4.0 */
89
90     private BSFManagerFactory _managerFactory;
91
92     private List JavaDoc _inheritedBindingQueue = new ArrayList JavaDoc();
93
94     /** @since 4.0 */
95     private IComponentVisitor _establishDefaultParameterValuesVisitor;
96
97     private ComponentTreeWalker _establishDefaultParameterValuesWalker;
98
99     private ComponentTreeWalker _verifyRequiredParametersWalker;
100
101     /** @since 4.0 */
102
103     private ComponentConstructorFactory _componentConstructorFactory;
104
105     /** @since 4.0 */
106
107     private ValueConverter _valueConverter;
108
109     /** @since 4.0 */
110
111     private AssetSource _assetSource;
112
113     /**
114      * Used to find the correct Java component class for a page.
115      *
116      * @since 4.0
117      */

118
119     private ComponentClassProvider _pageClassProvider;
120
121     /**
122      * Used to find the correct Java component class for a component (a similar process to resolving
123      * a page, but with slightly differen steps and defaults).
124      *
125      * @since 4.0
126      */

127
128     private ComponentClassProvider _componentClassProvider;
129
130     /**
131      * Tracks the current locale into which pages are loaded.
132      *
133      * @since 4.0
134      */

135
136     private ThreadLocale _threadLocale;
137
138     /**
139      * The locale of the application, which is also the locale of the page being loaded.
140      */

141
142     private Locale JavaDoc _locale;
143
144     /**
145      * Number of components instantiated, excluding the page itself.
146      */

147
148     private int _count;
149
150     /**
151      * The recursion depth. A page with no components is zero. A component on a page is one.
152      */

153
154     private int _depth;
155
156     /**
157      * The maximum depth reached while building the page.
158      */

159
160     private int _maxDepth;
161
162     /** @since 4.0 */
163
164     private ClassResolver _classResolver;
165
166     public void initializeService()
167     {
168
169         // Create the mechanisms for walking the component tree when it is
170
// complete
171
IComponentVisitor verifyRequiredParametersVisitor = new VerifyRequiredParametersVisitor();
172
173         _verifyRequiredParametersWalker = new ComponentTreeWalker(new IComponentVisitor[]
174         { verifyRequiredParametersVisitor });
175
176         _establishDefaultParameterValuesWalker = new ComponentTreeWalker(new IComponentVisitor[]
177         { _establishDefaultParameterValuesVisitor });
178     }
179
180     /**
181      * Binds properties of the component as defined by the container's specification.
182      * <p>
183      * This implementation is very simple, we will need a lot more sanity checking and eror checking
184      * in the final version.
185      *
186      * @param container
187      * The containing component. For a dynamic binding ({@link ExpressionBinding}) the
188      * property name is evaluated with the container as the root.
189      * @param component
190      * The contained component being bound.
191      * @param spec
192      * The specification of the contained component.
193      * @param contained
194      * The contained component specification (from the container's
195      * {@link IComponentSpecification}).
196      */

197
198     void bind(IComponent container, IComponent component, IContainedComponent contained)
199     {
200         IComponentSpecification spec = component.getSpecification();
201         boolean formalOnly = !spec.getAllowInformalParameters();
202
203         if (contained.getInheritInformalParameters())
204         {
205             if (formalOnly)
206                 throw new ApplicationRuntimeException(PageloadMessages
207                         .inheritInformalInvalidComponentFormalOnly(component), component, contained
208                         .getLocation(), null);
209
210             IComponentSpecification containerSpec = container.getSpecification();
211
212             if (!containerSpec.getAllowInformalParameters())
213                 throw new ApplicationRuntimeException(PageloadMessages
214                         .inheritInformalInvalidContainerFormalOnly(container, component),
215                         component, contained.getLocation(), null);
216
217             IQueuedInheritedBinding queued = new QueuedInheritInformalBindings(component);
218             _inheritedBindingQueue.add(queued);
219         }
220
221         Iterator JavaDoc i = contained.getBindingNames().iterator();
222
223         while (i.hasNext())
224         {
225             String JavaDoc name = (String JavaDoc) i.next();
226
227             IParameterSpecification pspec = spec.getParameter(name);
228
229             boolean isFormal = pspec != null;
230
231             String JavaDoc parameterName = isFormal ? pspec.getParameterName() : name;
232
233             IBindingSpecification bspec = contained.getBinding(name);
234
235             // If not allowing informal parameters, check that each binding
236
// matches
237
// a formal parameter.
238

239             if (formalOnly && !isFormal)
240                 throw new ApplicationRuntimeException(PageloadMessages.formalParametersOnly(
241                         component,
242                         name), component, bspec.getLocation(), null);
243
244             // If an informal parameter that conflicts with a reserved name,
245
// then skip it.
246

247             if (!isFormal && spec.isReservedParameterName(name))
248                 continue;
249
250             if (isFormal)
251             {
252                 if (!name.equals(parameterName))
253                 {
254                     _log.error(PageloadMessages.usedParameterAlias(
255                             contained,
256                             name,
257                             parameterName,
258                             bspec.getLocation()));
259                 }
260                 else if (pspec.isDeprecated())
261                     _log.error(PageloadMessages.deprecatedParameter(
262                             name,
263                             bspec.getLocation(),
264                             contained.getType()));
265             }
266
267             // The type determines how to interpret the value:
268
// As a simple static String
269
// As a nested property name (relative to the component)
270
// As the name of a binding inherited from the containing component.
271
// As the name of a public field
272
// As a script for a listener
273

274             BindingType type = bspec.getType();
275
276             // For inherited bindings, defer until later. This gives components
277
// a chance to setup bindings from static values and expressions in
278
// the template. The order of operations is tricky, template bindings
279
// come later. Note that this is a hold over from the Tapestry 3.0 DTD
280
// and will some day no longer be supported.
281

282             if (type == BindingType.INHERITED)
283             {
284                 QueuedInheritedBinding queued = new QueuedInheritedBinding(component, bspec
285                         .getValue(), parameterName);
286                 _inheritedBindingQueue.add(queued);
287                 continue;
288             }
289
290             if (type == BindingType.LISTENER)
291             {
292                 constructListenerBinding(
293                         component,
294                         parameterName,
295                         (IListenerBindingSpecification) bspec);
296                 continue;
297             }
298
299             String JavaDoc description = PageloadMessages.parameterName(name);
300
301             // For informal parameters, or formal parameters that fail to specify a default binding
302
// type, use OGNL.
303

304             String JavaDoc defaultBindingType = BindingUtils.getDefaultBindingType(
305                     spec,
306                     parameterName,
307                     BindingConstants.OGNL_PREFIX);
308
309             IBinding binding = convert(container, description, defaultBindingType, bspec);
310
311             addBindingToComponent(component, parameterName, binding);
312         }
313     }
314
315     /**
316      * Adds a binding to the component, checking to see if there's a name conflict (an existing
317      * binding for the same parameter ... possibly because parameter names can be aliased).
318      *
319      * @param component
320      * to which the binding should be added
321      * @param parameterName
322      * the name of the parameter to bind, which should be a true name, not an alias
323      * @param binding
324      * the binding to add
325      * @throws ApplicationRuntimeException
326      * if a binding already exists
327      * @since 4.0
328      */

329
330     static void addBindingToComponent(IComponent component, String JavaDoc parameterName, IBinding binding)
331     {
332         IBinding existing = component.getBinding(parameterName);
333
334         if (existing != null)
335             throw new ApplicationRuntimeException(PageloadMessages.duplicateParameter(
336                     parameterName,
337                     existing), component, binding.getLocation(), null);
338
339         component.setBinding(parameterName, binding);
340     }
341
342     private IBinding convert(IComponent container, String JavaDoc description, String JavaDoc defaultBindingType,
343             IBindingSpecification spec)
344     {
345         Location location = spec.getLocation();
346         String JavaDoc bindingReference = spec.getValue();
347
348         return _bindingSource.createBinding(
349                 container,
350                 description,
351                 bindingReference,
352                 defaultBindingType,
353                 location);
354     }
355
356     /**
357      * Construct a {@link ListenerBinding} for the component, and add it.
358      *
359      * @since 3.0
360      */

361
362     private void constructListenerBinding(IComponent component, String JavaDoc parameterName,
363             IListenerBindingSpecification spec)
364     {
365         String JavaDoc language = spec.getLanguage();
366
367         // If not provided in the page or component specification, then
368
// search for a default (factory default is "jython").
369

370         if (HiveMind.isBlank(language))
371             language = _defaultScriptLanguage;
372
373         // Construct the binding. The first parameter is the compononent
374
// (not the DirectLink or Form, but the page or component containing the
375
// link or form).
376

377         String JavaDoc description = PageloadMessages.parameterName(parameterName);
378
379         IBinding binding = new ListenerBinding(description, _valueConverter, spec.getLocation(),
380                 component.getContainer(), language, spec.getScript(), _managerFactory);
381
382         addBindingToComponent(component, parameterName, binding);
383     }
384
385     /**
386      * Sets up a component. This involves:
387      * <ul>
388      * <li>Instantiating any contained components.
389      * <li>Add the contained components to the container.
390      * <li>Setting up bindings between container and containees.
391      * <li>Construct the containees recursively.
392      * <li>Invoking
393      * {@link IComponent#finishLoad(IRequestCycle, IPageLoader, IComponentSpecification)}
394      * </ul>
395      *
396      * @param cycle
397      * the request cycle for which the page is being (initially) constructed
398      * @param page
399      * The page on which the container exists.
400      * @param container
401      * The component to be set up.
402      * @param containerSpec
403      * The specification for the container.
404      * @param the
405      * namespace of the container
406      */

407
408     private void constructComponent(IRequestCycle cycle, IPage page, IComponent container,
409             IComponentSpecification containerSpec, INamespace namespace)
410     {
411         _depth++;
412         if (_depth > _maxDepth)
413             _maxDepth = _depth;
414
415         List JavaDoc ids = new ArrayList JavaDoc(containerSpec.getComponentIds());
416         int count = ids.size();
417
418         try
419         {
420             for (int i = 0; i < count; i++)
421             {
422                 String JavaDoc id = (String JavaDoc) ids.get(i);
423
424                 // Get the sub-component specification from the
425
// container's specification.
426

427                 IContainedComponent contained = containerSpec.getComponent(id);
428
429                 String JavaDoc type = contained.getType();
430                 Location location = contained.getLocation();
431
432                 _componentResolver.resolve(cycle, namespace, type, location);
433
434                 IComponentSpecification componentSpecification = _componentResolver
435                         .getSpecification();
436                 INamespace componentNamespace = _componentResolver.getNamespace();
437
438                 // Instantiate the contained component.
439

440                 IComponent component = instantiateComponent(
441                         page,
442                         container,
443                         id,
444                         componentSpecification,
445                         _componentResolver.getType(),
446                         componentNamespace,
447                         location);
448
449                 // Add it, by name, to the container.
450

451                 container.addComponent(component);
452
453                 // Set up any bindings in the IContainedComponent specification
454

455                 bind(container, component, contained);
456
457                 // Now construct the component recusively; it gets its chance
458
// to create its subcomponents and set their bindings.
459

460                 constructComponent(
461                         cycle,
462                         page,
463                         component,
464                         componentSpecification,
465                         componentNamespace);
466             }
467
468             addAssets(container, containerSpec);
469
470             // Finish the load of the component; most components (which
471
// subclass BaseComponent) load their templates here.
472
// Properties with initial values will be set here (or the
473
// initial value will be recorded for later use in pageDetach().
474
// That may cause yet more components to be created, and more
475
// bindings to be set, so we defer some checking until
476
// later.
477

478             container.finishLoad(cycle, this, containerSpec);
479
480             // Have the component switch over to its active state.
481

482             container.enterActiveState();
483         }
484         catch (ApplicationRuntimeException ex)
485         {
486             throw ex;
487         }
488         catch (RuntimeException JavaDoc ex)
489         {
490             throw new ApplicationRuntimeException(PageloadMessages.unableToInstantiateComponent(
491                     container,
492                     ex), container, null, ex);
493         }
494
495         _depth--;
496     }
497
498     /**
499      * Invoked to create an implicit component (one which is defined in the containing component's
500      * template, rather that in the containing component's specification).
501      *
502      * @see org.apache.tapestry.services.impl.ComponentTemplateLoaderImpl
503      * @since 3.0
504      */

505
506     public IComponent createImplicitComponent(IRequestCycle cycle, IComponent container,
507             String JavaDoc componentId, String JavaDoc componentType, Location location)
508     {
509         IPage page = container.getPage();
510
511         _componentResolver.resolve(cycle, container.getNamespace(), componentType, location);
512
513         INamespace componentNamespace = _componentResolver.getNamespace();
514         IComponentSpecification spec = _componentResolver.getSpecification();
515
516         IComponent result = instantiateComponent(
517                 page,
518                 container,
519                 componentId,
520                 spec,
521                 _componentResolver.getType(),
522                 componentNamespace,
523                 location);
524
525         container.addComponent(result);
526
527         // Recusively build the component.
528

529         constructComponent(cycle, page, result, spec, componentNamespace);
530
531         return result;
532     }
533
534     /**
535      * Instantiates a component from its specification. We instantiate the component object, then
536      * set its specification, page, container and id.
537      *
538      * @see AbstractComponent
539      */

540
541     private IComponent instantiateComponent(IPage page, IComponent container, String JavaDoc id,
542             IComponentSpecification spec, String JavaDoc type, INamespace namespace, Location location)
543     {
544         ComponentClassProviderContext context = new ComponentClassProviderContext(type, spec,
545                 namespace);
546         String JavaDoc className = _componentClassProvider.provideComponentClassName(context);
547
548         // String className = spec.getComponentClassName();
549

550         if (HiveMind.isBlank(className))
551             className = BaseComponent.class.getName();
552         else
553         {
554             Class JavaDoc componentClass = _classResolver.findClass(className);
555
556             if (!IComponent.class.isAssignableFrom(componentClass))
557                 throw new ApplicationRuntimeException(PageloadMessages
558                         .classNotComponent(componentClass), container, spec.getLocation(), null);
559
560             if (IPage.class.isAssignableFrom(componentClass))
561                 throw new ApplicationRuntimeException(PageloadMessages.pageNotAllowed(id),
562                         container, spec.getLocation(), null);
563         }
564
565         ComponentConstructor cc = _componentConstructorFactory.getComponentConstructor(
566                 spec,
567                 className);
568
569         IComponent result = (IComponent) cc.newInstance();
570
571         result.setNamespace(namespace);
572         result.setPage(page);
573         result.setContainer(container);
574         result.setId(id);
575         result.setLocation(location);
576
577         _count++;
578
579         return result;
580     }
581
582     /**
583      * Instantitates a page from its specification.
584      *
585      * @param name
586      * the unqualified, simple, name for the page
587      * @param namespace
588      * the namespace containing the page's specification
589      * @param spec
590      * the page's specification We instantiate the page object, then set its
591      * specification, names and locale.
592      * @see IEngine
593      * @see ChangeObserver
594      */

595
596     private IPage instantiatePage(String JavaDoc name, INamespace namespace, IComponentSpecification spec)
597     {
598         Location location = spec.getLocation();
599         ComponentClassProviderContext context = new ComponentClassProviderContext(name, spec,
600                 namespace);
601         String JavaDoc className = _pageClassProvider.provideComponentClassName(context);
602
603         Class JavaDoc pageClass = _classResolver.findClass(className);
604
605         if (!IPage.class.isAssignableFrom(pageClass))
606             throw new ApplicationRuntimeException(PageloadMessages.classNotPage(pageClass),
607                     location, null);
608
609         String JavaDoc pageName = namespace.constructQualifiedName(name);
610
611         ComponentConstructor cc = _componentConstructorFactory.getComponentConstructor(
612                 spec,
613                 className);
614
615         IPage result = (IPage) cc.newInstance();
616
617         result.setNamespace(namespace);
618         result.setPageName(pageName);
619         result.setPage(result);
620         result.setLocale(_locale);
621         result.setLocation(location);
622
623         return result;
624     }
625
626     public IPage loadPage(String JavaDoc name, INamespace namespace, IRequestCycle cycle,
627             IComponentSpecification specification)
628     {
629         IPage page = null;
630
631         _count = 0;
632         _depth = 0;
633         _maxDepth = 0;
634
635         _locale = _threadLocale.getLocale();
636
637         try
638         {
639             page = instantiatePage(name, namespace, specification);
640
641             constructComponent(cycle, page, page, specification, namespace);
642
643             // Walk through the complete component tree to set up the default
644
// parameter values.
645
_establishDefaultParameterValuesWalker.walkComponentTree(page);
646
647             establishInheritedBindings();
648
649             // Walk through the complete component tree to ensure that required
650
// parameters are bound
651
_verifyRequiredParametersWalker.walkComponentTree(page);
652         }
653         finally
654         {
655             _locale = null;
656             _inheritedBindingQueue.clear();
657         }
658
659         if (_log.isDebugEnabled())
660             _log.debug("Loaded page " + page + " with " + _count + " components (maximum depth "
661                     + _maxDepth + ")");
662
663         return page;
664     }
665
666     /** @since 4.0 */
667
668     public void loadTemplateForComponent(IRequestCycle cycle, ITemplateComponent component)
669     {
670         _componentTemplateLoader.loadTemplate(cycle, component);
671     }
672
673     private void establishInheritedBindings()
674     {
675         _log.debug("Establishing inherited bindings");
676
677         int count = _inheritedBindingQueue.size();
678
679         for (int i = 0; i < count; i++)
680         {
681             IQueuedInheritedBinding queued = (IQueuedInheritedBinding) _inheritedBindingQueue
682                     .get(i);
683
684             queued.connect();
685         }
686     }
687
688     private void addAssets(IComponent component, IComponentSpecification specification)
689     {
690         List JavaDoc names = specification.getAssetNames();
691
692         if (names.isEmpty())
693             return;
694
695         Iterator JavaDoc i = names.iterator();
696
697         while (i.hasNext())
698         {
699             String JavaDoc name = (String JavaDoc) i.next();
700
701             IAssetSpecification assetSpec = specification.getAsset(name);
702
703             IAsset asset = convertAsset(assetSpec);
704
705             component.addAsset(name, asset);
706         }
707     }
708
709     /**
710      * Builds an instance of {@link IAsset}from the specification.
711      */

712
713     private IAsset convertAsset(IAssetSpecification spec)
714     {
715         // AssetType type = spec.getType();
716
String JavaDoc path = spec.getPath();
717         Location location = spec.getLocation();
718
719         return _assetSource.findAsset(location.getResource(), path, _locale, location);
720     }
721
722     /** @since 4.0 */
723
724     public void setLog(Log log)
725     {
726         _log = log;
727     }
728
729     /** @since 4.0 */
730
731     public void setComponentResolver(ComponentSpecificationResolver resolver)
732     {
733         _componentResolver = resolver;
734     }
735
736     /** @since 4.0 */
737
738     public void setDefaultScriptLanguage(String JavaDoc string)
739     {
740         _defaultScriptLanguage = string;
741     }
742
743     /** @since 4.0 */
744
745     public void setBindingSource(BindingSource bindingSource)
746     {
747         _bindingSource = bindingSource;
748     }
749
750     /**
751      * @since 4.0
752      */

753     public void setComponentTemplateLoader(ComponentTemplateLoader componentTemplateLoader)
754     {
755         _componentTemplateLoader = componentTemplateLoader;
756     }
757
758     /** @since 4.0 */
759     public void setEstablishDefaultParameterValuesVisitor(
760             IComponentVisitor establishDefaultParameterValuesVisitor)
761     {
762         _establishDefaultParameterValuesVisitor = establishDefaultParameterValuesVisitor;
763     }
764
765     /** @since 4.0 */
766     public void setComponentConstructorFactory(
767             ComponentConstructorFactory componentConstructorFactory)
768     {
769         _componentConstructorFactory = componentConstructorFactory;
770     }
771
772     /** @since 4.0 */
773     public void setValueConverter(ValueConverter valueConverter)
774     {
775         _valueConverter = valueConverter;
776     }
777
778     /** @since 4.0 */
779     public void setAssetSource(AssetSource assetSource)
780     {
781         _assetSource = assetSource;
782     }
783
784     /** @since 4.0 */
785     public void setManagerFactory(BSFManagerFactory managerFactory)
786     {
787         _managerFactory = managerFactory;
788     }
789
790     /** @since 4.0 */
791     public void setPageClassProvider(ComponentClassProvider pageClassProvider)
792     {
793         _pageClassProvider = pageClassProvider;
794     }
795
796     /** @since 4.0 */
797     public void setClassResolver(ClassResolver classResolver)
798     {
799         _classResolver = classResolver;
800     }
801
802     /**
803      * @since 4.0
804      */

805     public void setComponentClassProvider(ComponentClassProvider componentClassProvider)
806     {
807         _componentClassProvider = componentClassProvider;
808     }
809
810     /** @since 4.0 */
811     public void setThreadLocale(ThreadLocale threadLocale)
812     {
813         _threadLocale = threadLocale;
814     }
815 }
Popular Tags