001    // Copyright 2006, 2007, 2008, 2009, 2010, 2011 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    // http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry5.services;
016    
017    import org.apache.tapestry5.*;
018    import org.apache.tapestry5.ajax.MultiZoneUpdate;
019    import org.apache.tapestry5.alerts.AlertManager;
020    import org.apache.tapestry5.annotations.*;
021    import org.apache.tapestry5.annotations.ContentType;
022    import org.apache.tapestry5.beaneditor.DataTypeConstants;
023    import org.apache.tapestry5.beaneditor.Validate;
024    import org.apache.tapestry5.corelib.ClientValidation;
025    import org.apache.tapestry5.grid.GridConstants;
026    import org.apache.tapestry5.grid.GridDataSource;
027    import org.apache.tapestry5.internal.*;
028    import org.apache.tapestry5.internal.alerts.AlertManagerImpl;
029    import org.apache.tapestry5.internal.beaneditor.EnvironmentMessages;
030    import org.apache.tapestry5.internal.beaneditor.MessagesConstraintGenerator;
031    import org.apache.tapestry5.internal.beaneditor.PrimitiveFieldConstraintGenerator;
032    import org.apache.tapestry5.internal.beaneditor.ValidateAnnotationConstraintGenerator;
033    import org.apache.tapestry5.internal.bindings.*;
034    import org.apache.tapestry5.internal.dynamic.DynamicTemplateParserImpl;
035    import org.apache.tapestry5.internal.grid.CollectionGridDataSource;
036    import org.apache.tapestry5.internal.grid.NullDataSource;
037    import org.apache.tapestry5.internal.gzip.GZipFilter;
038    import org.apache.tapestry5.internal.renderers.*;
039    import org.apache.tapestry5.internal.services.*;
040    import org.apache.tapestry5.internal.services.ajax.AjaxFormUpdateFilter;
041    import org.apache.tapestry5.internal.services.ajax.AjaxResponseRendererImpl;
042    import org.apache.tapestry5.internal.services.ajax.JavaScriptSupportImpl;
043    import org.apache.tapestry5.internal.services.ajax.MultiZoneUpdateEventResultProcessor;
044    import org.apache.tapestry5.internal.services.assets.AssetPathConstructorImpl;
045    import org.apache.tapestry5.internal.services.assets.ClasspathAssetRequestHandler;
046    import org.apache.tapestry5.internal.services.assets.ContextAssetRequestHandler;
047    import org.apache.tapestry5.internal.services.assets.StackAssetRequestHandler;
048    import org.apache.tapestry5.internal.services.javascript.CoreJavaScriptStack;
049    import org.apache.tapestry5.internal.services.javascript.DateFieldStack;
050    import org.apache.tapestry5.internal.services.javascript.JavaScriptStackPathConstructor;
051    import org.apache.tapestry5.internal.services.javascript.JavaScriptStackSourceImpl;
052    import org.apache.tapestry5.internal.services.linktransform.LinkTransformerImpl;
053    import org.apache.tapestry5.internal.services.linktransform.LinkTransformerInterceptor;
054    import org.apache.tapestry5.internal.services.messages.PropertiesFileParserImpl;
055    import org.apache.tapestry5.internal.services.meta.ContentTypeExtractor;
056    import org.apache.tapestry5.internal.services.meta.MetaAnnotationExtractor;
057    import org.apache.tapestry5.internal.services.meta.MetaWorkerImpl;
058    import org.apache.tapestry5.internal.services.security.ClientWhitelistImpl;
059    import org.apache.tapestry5.internal.services.security.LocalhostOnly;
060    import org.apache.tapestry5.internal.services.templates.DefaultTemplateLocator;
061    import org.apache.tapestry5.internal.services.templates.PageTemplateLocator;
062    import org.apache.tapestry5.internal.transform.*;
063    import org.apache.tapestry5.internal.translator.NumericTranslator;
064    import org.apache.tapestry5.internal.translator.NumericTranslatorSupport;
065    import org.apache.tapestry5.internal.translator.StringTranslator;
066    import org.apache.tapestry5.internal.util.RenderableAsBlock;
067    import org.apache.tapestry5.internal.util.StringRenderable;
068    import org.apache.tapestry5.internal.validator.ValidatorMacroImpl;
069    import org.apache.tapestry5.ioc.*;
070    import org.apache.tapestry5.ioc.annotations.*;
071    import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
072    import org.apache.tapestry5.ioc.services.*;
073    import org.apache.tapestry5.ioc.util.AvailableValues;
074    import org.apache.tapestry5.ioc.util.IdAllocator;
075    import org.apache.tapestry5.ioc.util.StrategyRegistry;
076    import org.apache.tapestry5.json.JSONArray;
077    import org.apache.tapestry5.json.JSONObject;
078    import org.apache.tapestry5.plastic.MethodDescription;
079    import org.apache.tapestry5.runtime.Component;
080    import org.apache.tapestry5.runtime.ComponentResourcesAware;
081    import org.apache.tapestry5.runtime.RenderCommand;
082    import org.apache.tapestry5.runtime.RenderQueue;
083    import org.apache.tapestry5.services.ajax.AjaxResponseRenderer;
084    import org.apache.tapestry5.services.assets.AssetPathConstructor;
085    import org.apache.tapestry5.services.assets.AssetRequestHandler;
086    import org.apache.tapestry5.services.assets.AssetsModule;
087    import org.apache.tapestry5.services.dynamic.DynamicTemplate;
088    import org.apache.tapestry5.services.dynamic.DynamicTemplateParser;
089    import org.apache.tapestry5.services.javascript.JavaScriptStack;
090    import org.apache.tapestry5.services.javascript.JavaScriptStackSource;
091    import org.apache.tapestry5.services.javascript.JavaScriptSupport;
092    import org.apache.tapestry5.services.javascript.StylesheetLink;
093    import org.apache.tapestry5.services.linktransform.ComponentEventLinkTransformer;
094    import org.apache.tapestry5.services.linktransform.LinkTransformer;
095    import org.apache.tapestry5.services.linktransform.PageRenderLinkTransformer;
096    import org.apache.tapestry5.services.messages.ComponentMessagesSource;
097    import org.apache.tapestry5.services.messages.PropertiesFileParser;
098    import org.apache.tapestry5.services.meta.FixedExtractor;
099    import org.apache.tapestry5.services.meta.MetaDataExtractor;
100    import org.apache.tapestry5.services.meta.MetaWorker;
101    import org.apache.tapestry5.services.pageload.PageLoadModule;
102    import org.apache.tapestry5.services.security.ClientWhitelist;
103    import org.apache.tapestry5.services.security.WhitelistAnalyzer;
104    import org.apache.tapestry5.services.templates.ComponentTemplateLocator;
105    import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
106    import org.apache.tapestry5.services.transform.InjectionProvider2;
107    import org.apache.tapestry5.util.StringToEnumCoercion;
108    import org.apache.tapestry5.validator.*;
109    import org.slf4j.Logger;
110    
111    import javax.servlet.ServletContext;
112    import javax.servlet.http.HttpServletRequest;
113    import javax.servlet.http.HttpServletResponse;
114    import java.io.IOException;
115    import java.lang.annotation.Annotation;
116    import java.math.BigDecimal;
117    import java.math.BigInteger;
118    import java.net.URL;
119    import java.text.DateFormat;
120    import java.text.SimpleDateFormat;
121    import java.util.*;
122    import java.util.regex.Pattern;
123    
124    /**
125     * The root module for Tapestry.
126     */
127    @Marker(Core.class)
128    @SubModule(
129            {InternalModule.class, AssetsModule.class, PageLoadModule.class})
130    public final class TapestryModule
131    {
132        private final PipelineBuilder pipelineBuilder;
133    
134        private final ApplicationGlobals applicationGlobals;
135    
136        private final PropertyShadowBuilder shadowBuilder;
137    
138        private final Environment environment;
139    
140        private final StrategyBuilder strategyBuilder;
141    
142        private final PropertyAccess propertyAccess;
143    
144        private final ChainBuilder chainBuilder;
145    
146        private final Request request;
147    
148        private final Response response;
149    
150        private final RequestGlobals requestGlobals;
151    
152        private final EnvironmentalShadowBuilder environmentalBuilder;
153    
154        private final EndOfRequestEventHub endOfRequestEventHub;
155    
156        /**
157         * We inject all sorts of common dependencies (including builders) into the
158         * module itself (note: even though some of
159         * these service are defined by the module itself, that's ok because
160         * services are always lazy proxies). This isn't
161         * about efficiency (it may be slightly more efficient, but not in any
162         * noticeable way), it's about eliminating the
163         * need to keep injecting these dependencies into individual service builder
164         * and contribution methods.
165         */
166        public TapestryModule(PipelineBuilder pipelineBuilder,
167    
168                              PropertyShadowBuilder shadowBuilder,
169    
170                              RequestGlobals requestGlobals,
171    
172                              ApplicationGlobals applicationGlobals,
173    
174                              ChainBuilder chainBuilder,
175    
176                              Environment environment,
177    
178                              StrategyBuilder strategyBuilder,
179    
180                              PropertyAccess propertyAccess,
181    
182                              Request request,
183    
184                              Response response,
185    
186                              EnvironmentalShadowBuilder environmentalBuilder,
187    
188                              EndOfRequestEventHub endOfRequestEventHub)
189        {
190            this.pipelineBuilder = pipelineBuilder;
191            this.shadowBuilder = shadowBuilder;
192            this.requestGlobals = requestGlobals;
193            this.applicationGlobals = applicationGlobals;
194            this.chainBuilder = chainBuilder;
195            this.environment = environment;
196            this.strategyBuilder = strategyBuilder;
197            this.propertyAccess = propertyAccess;
198            this.request = request;
199            this.response = response;
200            this.environmentalBuilder = environmentalBuilder;
201            this.endOfRequestEventHub = endOfRequestEventHub;
202        }
203    
204        // A bunch of classes "promoted" from inline inner class to nested classes,
205        // just so that the stack trace would be more readable. Most of these
206        // are terminators for pipeline services.
207    
208        /**
209         * @since 5.1.0.0
210         */
211        private class ApplicationInitializerTerminator implements ApplicationInitializer
212        {
213            public void initializeApplication(Context context)
214            {
215                applicationGlobals.storeContext(context);
216            }
217        }
218    
219        /**
220         * @since 5.1.0.0
221         */
222        private class HttpServletRequestHandlerTerminator implements HttpServletRequestHandler
223        {
224            private final RequestHandler handler;
225            private final String applicationCharset;
226            private final TapestrySessionFactory sessionFactory;
227    
228            public HttpServletRequestHandlerTerminator(RequestHandler handler, String applicationCharset,
229                                                       TapestrySessionFactory sessionFactory)
230            {
231                this.handler = handler;
232                this.applicationCharset = applicationCharset;
233                this.sessionFactory = sessionFactory;
234            }
235    
236            public boolean service(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
237                    throws IOException
238            {
239                requestGlobals.storeServletRequestResponse(servletRequest, servletResponse);
240    
241                Request request = new RequestImpl(servletRequest, applicationCharset, sessionFactory);
242                Response response = new ResponseImpl(servletRequest, servletResponse);
243    
244                // TAP5-257: Make sure that the "initial guess" for request/response
245                // is available, even if
246                // some filter in the RequestHandler pipeline replaces them.
247    
248                requestGlobals.storeRequestResponse(request, response);
249    
250                // Transition from the Servlet API-based pipeline, to the
251                // Tapestry-based pipeline.
252    
253                return handler.service(request, response);
254            }
255        }
256    
257        /**
258         * @since 5.1.0.0
259         */
260        private class ServletApplicationInitializerTerminator implements ServletApplicationInitializer
261        {
262            private final ApplicationInitializer initializer;
263    
264            public ServletApplicationInitializerTerminator(ApplicationInitializer initializer)
265            {
266                this.initializer = initializer;
267            }
268    
269            public void initializeApplication(ServletContext servletContext)
270            {
271                applicationGlobals.storeServletContext(servletContext);
272    
273                // And now, down the (Web) ApplicationInitializer pipeline ...
274    
275                ContextImpl context = new ContextImpl(servletContext);
276    
277                applicationGlobals.storeContext(context);
278    
279                initializer.initializeApplication(context);
280            }
281        }
282    
283        /**
284         * @since 5.1.0.0
285         */
286        private class RequestHandlerTerminator implements RequestHandler
287        {
288            private final Dispatcher masterDispatcher;
289    
290            public RequestHandlerTerminator(Dispatcher masterDispatcher)
291            {
292                this.masterDispatcher = masterDispatcher;
293            }
294    
295            public boolean service(Request request, Response response) throws IOException
296            {
297                // Update RequestGlobals with the current request/response (in case
298                // some filter replaced the
299                // normal set).
300                requestGlobals.storeRequestResponse(request, response);
301    
302                return masterDispatcher.dispatch(request, response);
303            }
304        }
305    
306        public static void bind(ServiceBinder binder)
307        {
308            binder.bind(ClasspathAssetAliasManager.class, ClasspathAssetAliasManagerImpl.class);
309            binder.bind(PersistentLocale.class, PersistentLocaleImpl.class);
310            binder.bind(ApplicationStateManager.class, ApplicationStateManagerImpl.class);
311            binder.bind(ApplicationStatePersistenceStrategySource.class,
312                    ApplicationStatePersistenceStrategySourceImpl.class);
313            binder.bind(BindingSource.class, BindingSourceImpl.class);
314            binder.bind(FieldValidatorSource.class, FieldValidatorSourceImpl.class);
315            binder.bind(ApplicationGlobals.class, ApplicationGlobalsImpl.class);
316            binder.bind(AssetSource.class, AssetSourceImpl.class);
317            binder.bind(Cookies.class, CookiesImpl.class);
318            binder.bind(FieldValidatorDefaultSource.class, FieldValidatorDefaultSourceImpl.class);
319            binder.bind(RequestGlobals.class, RequestGlobalsImpl.class);
320            binder.bind(ResourceDigestGenerator.class, ResourceDigestGeneratorImpl.class);
321            binder.bind(ValidationConstraintGenerator.class, ValidationConstraintGeneratorImpl.class);
322            binder.bind(EnvironmentalShadowBuilder.class, EnvironmentalShadowBuilderImpl.class);
323            binder.bind(ComponentSource.class, ComponentSourceImpl.class);
324            binder.bind(BeanModelSource.class, BeanModelSourceImpl.class);
325            binder.bind(BeanBlockSource.class, BeanBlockSourceImpl.class);
326            binder.bind(ComponentDefaultProvider.class, ComponentDefaultProviderImpl.class);
327            binder.bind(MarkupWriterFactory.class, MarkupWriterFactoryImpl.class);
328            binder.bind(FieldValidationSupport.class, FieldValidationSupportImpl.class);
329            binder.bind(ObjectRenderer.class, LocationRenderer.class).withSimpleId();
330            binder.bind(ObjectProvider.class, AssetObjectProvider.class).withSimpleId();
331            binder.bind(RequestExceptionHandler.class, DefaultRequestExceptionHandler.class);
332            binder.bind(ComponentEventResultProcessor.class, ComponentInstanceResultProcessor.class).withSimpleId();
333            binder.bind(NullFieldStrategySource.class, NullFieldStrategySourceImpl.class);
334            binder.bind(HttpServletRequestFilter.class, IgnoredPathsFilter.class).withSimpleId();
335            binder.bind(ContextValueEncoder.class, ContextValueEncoderImpl.class);
336            binder.bind(BaseURLSource.class, BaseURLSourceImpl.class);
337            binder.bind(BeanBlockOverrideSource.class, BeanBlockOverrideSourceImpl.class);
338            binder.bind(HiddenFieldLocationRules.class, HiddenFieldLocationRulesImpl.class);
339            binder.bind(PageDocumentGenerator.class, PageDocumentGeneratorImpl.class);
340            binder.bind(ResponseRenderer.class, ResponseRendererImpl.class);
341            binder.bind(FieldTranslatorSource.class, FieldTranslatorSourceImpl.class);
342            binder.bind(BindingFactory.class, MessageBindingFactory.class).withSimpleId();
343            binder.bind(BindingFactory.class, ValidateBindingFactory.class).withSimpleId();
344            binder.bind(BindingFactory.class, TranslateBindingFactory.class).withSimpleId();
345            binder.bind(BindingFactory.class, AssetBindingFactory.class).withSimpleId();
346            binder.bind(BindingFactory.class, ContextBindingFactory.class).withSimpleId();
347            binder.bind(BindingFactory.class, NullFieldStrategyBindingFactory.class).withSimpleId();
348            binder.bind(BindingFactory.class, SymbolBindingFactory.class).withSimpleId();
349            binder.bind(URLEncoder.class, URLEncoderImpl.class);
350            binder.bind(ContextPathEncoder.class, ContextPathEncoderImpl.class);
351            binder.bind(ApplicationStatePersistenceStrategy.class, SessionApplicationStatePersistenceStrategy.class).withSimpleId();
352            binder.bind(TapestrySessionFactory.class, TapestrySessionFactoryImpl.class);
353            binder.bind(AssetPathConverter.class, IdentityAssetPathConverter.class);
354            binder.bind(NumericTranslatorSupport.class);
355            binder.bind(ClientDataEncoder.class, ClientDataEncoderImpl.class);
356            binder.bind(ComponentEventLinkEncoder.class, ComponentEventLinkEncoderImpl.class);
357            binder.bind(PageRenderLinkSource.class, PageRenderLinkSourceImpl.class);
358            binder.bind(ValidatorMacro.class, ValidatorMacroImpl.class);
359            binder.bind(PropertiesFileParser.class, PropertiesFileParserImpl.class);
360            binder.bind(PageActivator.class, PageActivatorImpl.class);
361            binder.bind(Dispatcher.class, AssetDispatcher.class).withSimpleId();
362            binder.bind(AssetPathConstructor.class, AssetPathConstructorImpl.class);
363            binder.bind(JavaScriptStackSource.class, JavaScriptStackSourceImpl.class);
364            binder.bind(TranslatorAlternatesSource.class, TranslatorAlternatesSourceImpl.class);
365            binder.bind(MetaWorker.class, MetaWorkerImpl.class);
366            binder.bind(LinkTransformer.class, LinkTransformerImpl.class);
367            binder.bind(SelectModelFactory.class, SelectModelFactoryImpl.class);
368            binder.bind(DynamicTemplateParser.class, DynamicTemplateParserImpl.class);
369            binder.bind(AjaxResponseRenderer.class, AjaxResponseRendererImpl.class);
370            binder.bind(AlertManager.class, AlertManagerImpl.class);
371            binder.bind(ValidationDecoratorFactory.class, ValidationDecoratorFactoryImpl.class);
372            binder.bind(PropertyConduitSource.class, PropertyConduitSourceImpl.class);
373            binder.bind(ClientWhitelist.class, ClientWhitelistImpl.class);
374            binder.bind(AssetFactory.class, ClasspathAssetFactory.class).withSimpleId();
375        }
376    
377        // ========================================================================
378        //
379        // Service Builder Methods (static)
380        //
381        // ========================================================================
382    
383        // ========================================================================
384        //
385        // Service Contribution Methods (static)
386        //
387        // ========================================================================
388    
389        /**
390         * Contributes the factory for serveral built-in binding prefixes ("asset",
391         * "block", "component", "literal", prop",
392         * "nullfieldstrategy", "message", "validate", "translate", "var").
393         */
394        public static void contributeBindingSource(MappedConfiguration<String, BindingFactory> configuration,
395    
396                                                   @InjectService("PropBindingFactory")
397                                                   BindingFactory propBindingFactory,
398    
399                                                   @InjectService("MessageBindingFactory")
400                                                   BindingFactory messageBindingFactory,
401    
402                                                   @InjectService("ValidateBindingFactory")
403                                                   BindingFactory validateBindingFactory,
404    
405                                                   @InjectService("TranslateBindingFactory")
406                                                   BindingFactory translateBindingFactory,
407    
408                                                   @InjectService("AssetBindingFactory")
409                                                   BindingFactory assetBindingFactory,
410    
411                                                   @InjectService("NullFieldStrategyBindingFactory")
412                                                   BindingFactory nullFieldStrategyBindingFactory,
413    
414                                                   @InjectService("ContextBindingFactory")
415                                                   BindingFactory contextBindingFactory,
416    
417                                                   @InjectService("SymbolBindingFactory")
418                                                   BindingFactory symbolBindingFactory)
419        {
420            configuration.add(BindingConstants.LITERAL, new LiteralBindingFactory());
421            configuration.add(BindingConstants.COMPONENT, new ComponentBindingFactory());
422            configuration.add(BindingConstants.VAR, new RenderVariableBindingFactory());
423            configuration.add(BindingConstants.BLOCK, new BlockBindingFactory());
424    
425            configuration.add(BindingConstants.PROP, propBindingFactory);
426            configuration.add(BindingConstants.MESSAGE, messageBindingFactory);
427            configuration.add(BindingConstants.VALIDATE, validateBindingFactory);
428            configuration.add(BindingConstants.TRANSLATE, translateBindingFactory);
429            configuration.add(BindingConstants.ASSET, assetBindingFactory);
430            configuration.add(BindingConstants.NULLFIELDSTRATEGY, nullFieldStrategyBindingFactory);
431            configuration.add(BindingConstants.CONTEXT, contextBindingFactory);
432            configuration.add(BindingConstants.SYMBOL, symbolBindingFactory);
433        }
434    
435        @Contribute(ClasspathAssetAliasManager.class)
436        public static void addMappingsForLibraryVirtualFolders(MappedConfiguration<String, String> configuration,
437                                                               ComponentClassResolver resolver)
438        {
439            // Each library gets a mapping or its folder automatically
440    
441            Map<String, String> folderToPackageMapping = resolver.getFolderToPackageMapping();
442    
443            for (String folder : folderToPackageMapping.keySet())
444            {
445                configuration.add(folder, toPackagePath(folderToPackageMapping.get(folder)));
446            }
447        }
448    
449        @Contribute(ClasspathAssetAliasManager.class)
450        public static void addApplicationAndTapestryMappings(MappedConfiguration<String, String> configuration,
451    
452                                                             @Symbol(InternalConstants.TAPESTRY_APP_PACKAGE_PARAM)
453                                                             String appPackage)
454        {
455            configuration.add("tapestry", "org/apache/tapestry5");
456    
457            configuration.add("app", toPackagePath(appPackage));
458        }
459    
460        /**
461         * Contributes an handler for each mapped classpath alias, as well handlers for context assets
462         * and stack assets (combined {@link JavaScriptStack} files).
463         */
464        public static void contributeAssetDispatcher(MappedConfiguration<String, AssetRequestHandler> configuration,
465    
466                                                     @ContextProvider
467                                                     AssetFactory contextAssetFactory,
468    
469                                                     @Autobuild
470                                                     StackAssetRequestHandler stackAssetRequestHandler,
471    
472                                                     ClasspathAssetAliasManager classpathAssetAliasManager, ResourceStreamer streamer,
473                                                     AssetResourceLocator assetResourceLocator)
474        {
475            Map<String, String> mappings = classpathAssetAliasManager.getMappings();
476    
477            for (String folder : mappings.keySet())
478            {
479                String path = mappings.get(folder);
480    
481                configuration.add(folder, new ClasspathAssetRequestHandler(streamer, assetResourceLocator, path));
482            }
483    
484            configuration.add(RequestConstants.CONTEXT_FOLDER,
485                    new ContextAssetRequestHandler(streamer, contextAssetFactory.getRootResource()));
486    
487            configuration.add(RequestConstants.STACK_FOLDER, stackAssetRequestHandler);
488    
489        }
490    
491        private static String toPackagePath(String packageName)
492        {
493            return packageName.replace('.', '/');
494        }
495    
496        @Contribute(ComponentClassResolver.class)
497        public static void setupCoreAndAppLibraries(Configuration<LibraryMapping> configuration,
498                                                    @Symbol(InternalConstants.TAPESTRY_APP_PACKAGE_PARAM)
499                                                    String appRootPackage)
500        {
501            configuration.add(new LibraryMapping(InternalConstants.CORE_LIBRARY, "org.apache.tapestry5.corelib"));
502            configuration.add(new LibraryMapping("t5internal", "org.apache.tapestry5.internal.t5internal"));
503            configuration.add(new LibraryMapping("", appRootPackage));
504        }
505    
506        /**
507         * Adds a number of standard component class transform workers:
508         * <dl>
509         * <dt>Parameter</dt>
510         * <dd>Identifies parameters based on the {@link org.apache.tapestry5.annotations.Parameter} annotation</dd>
511         * <dt>BindParameter</dt>
512         * <dd>Support for the {@link BindParameter} annotation</dd>
513         * <dt>Property</dt>
514         * <dd>Generates accessor methods if {@link org.apache.tapestry5.annotations.Property} annotation is present</dd>
515         * <dt>Import</dt>
516         * <dd>Supports the {@link Import} annotation</dd>
517         * <dt>UnclaimedField</dt>
518         * <dd>Manages unclaimed fields, storing their value in a {@link PerThreadValue}</dd>
519         * <dt>OnEvent</dt>
520         * <dd>Handle the @OnEvent annotation, and related naming convention</dd>
521         * <dt>RenderCommand</dt>
522         * <dd>Ensures all components also implement {@link org.apache.tapestry5.runtime.RenderCommand}</dd>
523         * <dt>SupportsInformalParameters</dt>
524         * <dd>Checks for the annotation</dd>
525         * <dt>RenderPhase</dt>
526         * <dd>Link in render phase methods</dd>
527         * <dt>Retain</dt>
528         * <dd>Allows fields to retain their values between requests</dd>
529         * <dt>Meta</dt>
530         * <dd>Checks for meta data annotations and adds it to the component model</dd>
531         * <dt>PageActivationContext</dt> <dd>Support for {@link PageActivationContext} annotation</dd>
532         * <dt>DiscardAfter</dt> <dd>Support for {@link DiscardAfter} method annotation </dd>
533         * <dt>MixinAfter</dt> <dd>Support for the {@link MixinAfter} mixin class annotation</dd>
534         * <dt>PageReset</dt>
535         * <dd>Checks for the {@link PageReset} annotation</dd>
536         * <dt>Mixin</dt>
537         * <dd>Adds a mixin as part of a component's implementation</dd>
538         * <dt>Cached</dt>
539         * <dd>Checks for the {@link org.apache.tapestry5.annotations.Cached} annotation</dd>
540         * <dt>ActivationRequestParameter</dt>
541         * <dd>Support for the {@link ActivationRequestParameter} annotation</dd>
542         * <dt>PageLoaded, PageAttached, PageDetached</dt>
543         * <dd>Support for annotations {@link PageLoaded}, {@link PageAttached}, {@link PageDetached}</dd>
544         * <dt>InjectService</dt>
545         * <dd>Handles the {@link org.apache.tapestry5.ioc.annotations.InjectService} annotation</dd>
546         * <dt>Component</dt>
547         * <dd>Defines embedded components based on the {@link org.apache.tapestry5.annotations.Component} annotation</dd>
548         * <dt>Environment</dt>
549         * <dd>Allows fields to contain values extracted from the {@link org.apache.tapestry5.services.Environment} service</dd>
550         * <dt>ApplicationState</dt>
551         * <dd>Converts fields that reference application state objects</dd>
552         * <dt>Persist</dt>
553         * <dd>Allows fields to store their their value persistently between requests via {@link Persist}</dd>
554         * <dt>SessionAttribute</dt>
555         * <dd>Support for the {@link SessionAttribute}</dd>
556         * <dt>Log</dt>
557         * <dd>Checks for the {@link org.apache.tapestry5.annotations.Log} annotation</dd>
558         * <dt>HeartbeatDeferred
559         * <dd>Support for the {@link HeartbeatDeferred} annotation, which defers method invocation to the end of the {@link Heartbeat}
560         * <dt>Inject</dt>
561         * <dd>Used with the {@link org.apache.tapestry5.ioc.annotations.Inject} annotation, when a value is supplied</dd>
562         * </dl>
563         */
564        @Contribute(ComponentClassTransformWorker2.class)
565        @Primary
566        public static void provideTransformWorkers(
567                OrderedConfiguration<ComponentClassTransformWorker2> configuration,
568                MetaWorker metaWorker,
569                ComponentClassResolver resolver)
570        {
571            configuration.add("Property", new PropertyWorker());
572    
573            configuration.add("RenderCommand", new RenderCommandWorker());
574    
575            configuration.addInstance("OnEvent", OnEventWorker.class);
576    
577            configuration.add("MixinAfter", new MixinAfterWorker());
578    
579            // These must come after Property, since they actually delete fields
580            // that may still have the annotation
581            configuration.addInstance("ApplicationState", ApplicationStateWorker.class);
582            configuration.addInstance("Environment", EnvironmentalWorker.class);
583    
584            configuration.add("Component", new ComponentWorker(resolver));
585            configuration.add("Mixin", new MixinWorker(resolver));
586            configuration.addInstance("InjectPage", InjectPageWorker.class);
587            configuration.addInstance("InjectComponent", InjectComponentWorker.class);
588            configuration.addInstance("InjectContainer", InjectContainerWorker.class);
589    
590            // Default values for parameters are often some form of injection, so
591            // make sure that Parameter fields are processed after injections.
592    
593            configuration.addInstance("Parameter", ParameterWorker.class);
594    
595            // bind parameter should always go after parameter to make sure all
596            // parameters have been properly setup.
597            configuration.addInstance("BindParameter", BindParameterWorker.class);
598    
599            configuration.add("SupportsInformalParameters", new SupportsInformalParametersWorker());
600    
601            configuration.addInstance("RenderPhase", RenderPhaseMethodWorker.class);
602    
603            // Import advises methods, usually render phase methods, so it must come after RenderPhase.
604    
605            configuration.addInstance("Import", ImportWorker.class);
606    
607            configuration.add("Meta", metaWorker.getWorker());
608    
609            configuration.add("Retain", new RetainWorker());
610    
611            configuration.add("PageActivationContext", new PageActivationContextWorker());
612            configuration
613                    .addInstance("ActivationRequestParameter", ActivationRequestParameterWorker.class);
614    
615            configuration.addInstance("Cached", CachedWorker.class);
616    
617            configuration.addInstance("DiscardAfter", DiscardAfterWorker.class);
618    
619            add(configuration, PageLoaded.class, TransformConstants.CONTAINING_PAGE_DID_LOAD_DESCRIPTION);
620            add(configuration, PageAttached.class, TransformConstants.CONTAINING_PAGE_DID_ATTACH_DESCRIPTION);
621            add(configuration, PageDetached.class, TransformConstants.CONTAINING_PAGE_DID_DETACH_DESCRIPTION);
622    
623            configuration.addInstance("PageReset", PageResetAnnotationWorker.class);
624            configuration.addInstance("InjectService", InjectServiceWorker.class);
625    
626            configuration.addInstance("Inject", InjectWorker.class);
627    
628            configuration.addInstance("Persist", PersistWorker.class);
629    
630            configuration.addInstance("SessionAttribute", SessionAttributeWorker.class);
631    
632            configuration.addInstance("Log", LogWorker.class);
633    
634            configuration.addInstance("HeartbeatDeferred", HeartbeatDeferredWorker.class);
635    
636            // This one is always last. Any additional private fields that aren't
637            // annotated will
638            // be converted to clear out at the end of the request.
639    
640            configuration.addInstance("UnclaimedField", UnclaimedFieldWorker.class, "after:*");
641        }
642    
643        /**
644         * <dl>
645         * <dt>Annotation</dt>
646         * <dd>Checks for {@link org.apache.tapestry5.beaneditor.DataType} annotation</dd>
647         * <dt>Default (ordered last)</dt>
648         * <dd>
649         * {@link org.apache.tapestry5.internal.services.DefaultDataTypeAnalyzer} service (
650         * {@link #contributeDefaultDataTypeAnalyzer(org.apache.tapestry5.ioc.MappedConfiguration)} )</dd>
651         * </dl>
652         */
653        public static void contributeDataTypeAnalyzer(OrderedConfiguration<DataTypeAnalyzer> configuration,
654                                                      @InjectService("DefaultDataTypeAnalyzer")
655                                                      DataTypeAnalyzer defaultDataTypeAnalyzer)
656        {
657            configuration.add("Annotation", new AnnotationDataTypeAnalyzer());
658            configuration.add("Default", defaultDataTypeAnalyzer, "after:*");
659        }
660    
661        /**
662         * Maps property types to data type names:
663         * <ul>
664         * <li>String --&gt; text
665         * <li>Number --&gt; number
666         * <li>Enum --&gt; enum
667         * <li>Boolean --&gt; boolean
668         * <li>Date --&gt; date
669         * </ul>
670         */
671        public static void contributeDefaultDataTypeAnalyzer(MappedConfiguration<Class, String> configuration)
672        {
673            // This is a special case contributed to avoid exceptions when a
674            // property type can't be
675            // matched. DefaultDataTypeAnalyzer converts the empty string to null.
676    
677            configuration.add(Object.class, "");
678    
679            configuration.add(String.class, DataTypeConstants.TEXT);
680            configuration.add(Number.class, DataTypeConstants.NUMBER);
681            configuration.add(Enum.class, DataTypeConstants.ENUM);
682            configuration.add(Boolean.class, DataTypeConstants.BOOLEAN);
683            configuration.add(Date.class, DataTypeConstants.DATE);
684            configuration.add(Calendar.class, DataTypeConstants.CALENDAR);
685        }
686    
687        @Contribute(BeanBlockSource.class)
688        public static void provideDefaultBeanBlocks(Configuration<BeanBlockContribution> configuration)
689        {
690            addEditBlock(configuration, DataTypeConstants.TEXT);
691            addEditBlock(configuration, DataTypeConstants.NUMBER);
692            addEditBlock(configuration, DataTypeConstants.ENUM);
693            addEditBlock(configuration, DataTypeConstants.BOOLEAN);
694            addEditBlock(configuration, DataTypeConstants.DATE);
695            addEditBlock(configuration, DataTypeConstants.PASSWORD);
696            addEditBlock(configuration, DataTypeConstants.CALENDAR);
697    
698            // longtext uses a text area, not a text field
699    
700            addEditBlock(configuration, DataTypeConstants.LONG_TEXT);
701    
702            addDisplayBlock(configuration, DataTypeConstants.ENUM);
703            addDisplayBlock(configuration, DataTypeConstants.DATE);
704            addDisplayBlock(configuration, DataTypeConstants.CALENDAR);
705    
706            // Password and long text have special output needs.
707            addDisplayBlock(configuration, DataTypeConstants.PASSWORD);
708            addDisplayBlock(configuration, DataTypeConstants.LONG_TEXT);
709        }
710    
711        private static void addEditBlock(Configuration<BeanBlockContribution> configuration, String dataType)
712        {
713            addEditBlock(configuration, dataType, dataType);
714        }
715    
716        private static void addEditBlock(Configuration<BeanBlockContribution> configuration, String dataType, String blockId)
717        {
718            configuration.add(new EditBlockContribution(dataType, "PropertyEditBlocks", blockId));
719        }
720    
721        private static void addDisplayBlock(Configuration<BeanBlockContribution> configuration, String dataType)
722        {
723            addDisplayBlock(configuration, dataType, dataType);
724        }
725    
726        private static void addDisplayBlock(Configuration<BeanBlockContribution> configuration, String dataType,
727                                            String blockId)
728        {
729            configuration.add(new DisplayBlockContribution(dataType, "PropertyDisplayBlocks", blockId));
730        }
731    
732        /**
733         * Contributes the basic set of validators:
734         * <ul>
735         * <li>required</li>
736         * <li>minlength</li>
737         * <li>maxlength</li>
738         * <li>min</li>
739         * <li>max</li>
740         * <li>regexp</li>
741         * <li>email</li>
742         * <li>none</li>
743         * </ul>
744         */
745        public static void contributeFieldValidatorSource(MappedConfiguration<String, Validator> configuration)
746        {
747            configuration.add("required", new Required());
748            configuration.add("minlength", new MinLength());
749            configuration.add("maxlength", new MaxLength());
750            configuration.add("min", new Min());
751            configuration.add("max", new Max());
752            configuration.add("regexp", new Regexp());
753            configuration.add("email", new Email());
754            configuration.add("none", new None());
755        }
756    
757        /**
758         * <dl>
759         * <dt>Default</dt>
760         * <dd>based on {@link MasterObjectProvider}</dd>
761         * <dt>Named</dt> <dd>Handles fields with the {@link javax.inject.Named} annotation</dd>
762         * <dt>Block</dt>
763         * <dd>injects fields of type {@link Block}</dd>
764         * <dt>CommonResources</dt>
765         * <dd>Access to properties of resources (log, messages, etc.)</dd>
766         * <dt>Asset</dt>
767         * <dd>injection of assets (triggered via {@link Path} annotation), with the path relative to the component class</dd>
768         * <dt>Service</dt>
769         * <dd>Ordered last, for use when Inject is present and nothing else works, matches field type against Tapestry IoC
770         * services</dd>
771         * </dl>
772         */
773        @Contribute(InjectionProvider2.class)
774        public static void provideStandardInjectionProviders(OrderedConfiguration<InjectionProvider2> configuration, SymbolSource symbolSource,
775    
776                                                             AssetSource assetSource)
777        {
778            configuration.addInstance("Named", InjectNamedProvider.class);
779            configuration.add("Block", new BlockInjectionProvider());
780            configuration.add("Asset", new AssetInjectionProvider(symbolSource, assetSource));
781    
782            configuration.add("CommonResources", new CommonResourcesInjectionProvider());
783    
784            configuration.addInstance("Default", DefaultInjectionProvider.class);
785    
786            // This needs to be the last one, since it matches against services
787            // and might blow up if there is no match.
788            configuration.addInstance("Service", ServiceInjectionProvider.class, "after:*");
789        }
790    
791        /**
792         * Contributes two object providers:
793         * <dl>
794         * <dt>Asset
795         * <dt>
796         * <dd>Checks for the {@link Path} annotation, and injects an {@link Asset}</dd>
797         * <dt>Service</dt>
798         * <dd>Injects based on the {@link Service} annotation, if present</dd>
799         * <dt>ApplicationMessages</dt>
800         * <dd>Injects the global application messages</dd>
801         * </dl>
802         */
803        public static void contributeMasterObjectProvider(OrderedConfiguration<ObjectProvider> configuration,
804    
805                                                          @InjectService("AssetObjectProvider")
806                                                          ObjectProvider assetObjectProvider,
807    
808                                                          ObjectLocator locator)
809        {
810            configuration.add("Asset", assetObjectProvider, "before:AnnotationBasedContributions");
811    
812            configuration.add("Service", new ServiceAnnotationObjectProvider(), "before:AnnotationBasedContributions");
813    
814            configuration.add("ApplicationMessages", new ApplicationMessageCatalogObjectProvider(locator),
815                    "before:AnnotationBasedContributions");
816    
817        }
818    
819        /**
820         * <dl>
821         * <dt>StoreIntoGlobals</dt>
822         * <dd>Stores the request and response into {@link org.apache.tapestry5.services.RequestGlobals} at the start of the
823         * pipeline</dd>
824         * <dt>IgnoredPaths</dt>
825         * <dd>Identifies requests that are known (via the IgnoredPathsFilter service's configuration) to be mapped to other
826         * applications</dd>
827         * <dt>GZip</dt>
828         * <dd>Handles GZIP compression of response streams (if supported by client)</dd>
829         */
830        public void contributeHttpServletRequestHandler(OrderedConfiguration<HttpServletRequestFilter> configuration,
831    
832                                                        @Symbol(SymbolConstants.GZIP_COMPRESSION_ENABLED)
833                                                        boolean gzipCompressionEnabled,
834    
835                                                        @Autobuild
836                                                        GZipFilter gzipFilter,
837    
838                                                        @InjectService("IgnoredPathsFilter")
839                                                        HttpServletRequestFilter ignoredPathsFilter)
840        {
841            configuration.add("IgnoredPaths", ignoredPathsFilter);
842    
843            configuration.add("GZIP", gzipCompressionEnabled ? gzipFilter : null);
844    
845            HttpServletRequestFilter storeIntoGlobals = new HttpServletRequestFilter()
846            {
847                public boolean service(HttpServletRequest request, HttpServletResponse response,
848                                       HttpServletRequestHandler handler) throws IOException
849                {
850                    requestGlobals.storeServletRequestResponse(request, response);
851    
852                    return handler.service(request, response);
853                }
854            };
855    
856            configuration.add("StoreIntoGlobals", storeIntoGlobals, "before:*");
857        }
858    
859        /**
860         * Continues a number of filters into the RequestHandler service:
861         * <dl>
862         * <dt>StaticFiles</dt>
863         * <dd>Checks to see if the request is for an actual file, if so, returns true to let the servlet container process
864         * the request</dd>
865         * <dt>CheckForUpdates</dt>
866         * <dd>Periodically fires events that checks to see if the file system sources for any cached data has changed (see
867         * {@link org.apache.tapestry5.internal.services.CheckForUpdatesFilter}). Starting in 5.3, this filter will be null
868         * in production mode (it will only be active in development mode).
869         * <dt>ErrorFilter</dt>
870         * <dd>Catches request errors and lets the {@link org.apache.tapestry5.services.RequestExceptionHandler} handle them
871         * </dd>
872         * <dt>StoreIntoGlobals</dt>
873         * <dd>Stores the request and response into the {@link org.apache.tapestry5.services.RequestGlobals} service (this
874         * is repeated at the end of the pipeline, in case any filter substitutes the request or response).
875         * <dt>EndOfRequest</dt>
876         * <dd>Notifies internal services that the request has ended</dd>
877         * </dl>
878         */
879        public void contributeRequestHandler(OrderedConfiguration<RequestFilter> configuration, Context context,
880    
881                                             @Symbol(SymbolConstants.PRODUCTION_MODE)
882                                             boolean productionMode)
883        {
884            RequestFilter staticFilesFilter = new StaticFilesFilter(context);
885    
886            RequestFilter storeIntoGlobals = new RequestFilter()
887            {
888                public boolean service(Request request, Response response, RequestHandler handler) throws IOException
889                {
890                    requestGlobals.storeRequestResponse(request, response);
891    
892                    return handler.service(request, response);
893                }
894            };
895    
896            RequestFilter fireEndOfRequestEvent = new RequestFilter()
897            {
898                public boolean service(Request request, Response response, RequestHandler handler) throws IOException
899                {
900                    try
901                    {
902                        return handler.service(request, response);
903                    } finally
904                    {
905                        endOfRequestEventHub.fire();
906                    }
907                }
908            };
909    
910            if (productionMode)
911            {
912                configuration.add("CheckForUpdates", null, "before:*");
913            } else
914            {
915                configuration.addInstance("CheckForUpdates", CheckForUpdatesFilter.class, "before:*");
916            }
917    
918            configuration.add("StaticFiles", staticFilesFilter);
919    
920            configuration.add("StoreIntoGlobals", storeIntoGlobals);
921    
922            configuration.add("EndOfRequest", fireEndOfRequestEvent);
923    
924            configuration.addInstance("ErrorFilter", RequestErrorFilter.class);
925        }
926    
927        /**
928         * Contributes the basic set of translators:
929         * <ul>
930         * <li>string</li>
931         * <li>byte</li>
932         * <li>short</li>
933         * <li>integer</li>
934         * <li>long</li>
935         * <li>float</li>
936         * <li>double</li>
937         * <li>BigInteger</li>
938         * <li>BigDecimal</li>
939         * </ul>
940         */
941        public static void contributeTranslatorSource(MappedConfiguration<Class, Translator> configuration,
942                                                      NumericTranslatorSupport support)
943        {
944    
945            configuration.add(String.class, new StringTranslator());
946    
947            Class[] types = new Class[]
948                    {Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, BigInteger.class,
949                            BigDecimal.class};
950    
951            for (Class type : types)
952            {
953                String name = type.getSimpleName().toLowerCase();
954    
955                configuration.add(type, new NumericTranslator(name, type, support));
956            }
957        }
958    
959        /**
960         * Adds coercions:
961         * <ul>
962         * <li>String to {@link SelectModel}
963         * <li>Map to {@link SelectModel}
964         * <li>Collection to {@link GridDataSource}
965         * <li>null to {@link GridDataSource}
966         * <li>List to {@link SelectModel}
967         * <li>{@link ComponentResourcesAware} (typically, a component) to {@link ComponentResources}
968         * <li> {@link ComponentResources} to {@link PropertyOverrides}
969         * <li>String to {@link Renderable}
970         * <li>{@link Renderable} to {@link Block}
971         * <li>String to {@link DateFormat}
972         * <li>String to {@link Resource} (via {@link AssetSource#resourceForPath(String)})
973         * <li>{@link Renderable} to {@link RenderCommand}</li>
974         * <li>String to {@link Pattern}</li>
975         * <li>String to {@link DateFormat}</li>
976         * <li>{@link ComponentClassTransformWorker} to {@link ComponentClassTransformWorker2}</li>
977         * <li>{@link InjectionProvider} to {@link InjectionProvider2}</li>
978         * <li>{@link Resource} to {@link DynamicTemplate}</li>
979         * <li>{@link Asset} to {@link Resource}</li>
980         * <li>{@link ValueEncoder} to {@link ValueEncoderFactory}</li>
981         * </ul>
982         */
983        public static void contributeTypeCoercer(Configuration<CoercionTuple> configuration,
984    
985                                                 @Builtin
986                                                 TypeCoercer coercer,
987    
988                                                 @Builtin
989                                                 final ThreadLocale threadLocale,
990    
991                                                 @Core
992                                                 final AssetSource assetSource,
993    
994                                                 @Core
995                                                 final ComponentClassCache classCache,
996    
997                                                 @Core
998                                                 final DynamicTemplateParser dynamicTemplateParser)
999        {
1000            configuration.add(CoercionTuple.create(ComponentResources.class, PropertyOverrides.class,
1001                    new Coercion<ComponentResources, PropertyOverrides>()
1002                    {
1003                        public PropertyOverrides coerce(ComponentResources input)
1004                        {
1005                            return new PropertyOverridesImpl(input);
1006                        }
1007                    }));
1008    
1009            configuration.add(CoercionTuple.create(String.class, SelectModel.class, new Coercion<String, SelectModel>()
1010            {
1011                public SelectModel coerce(String input)
1012                {
1013                    return TapestryInternalUtils.toSelectModel(input);
1014                }
1015            }));
1016    
1017            configuration.add(CoercionTuple.create(Map.class, SelectModel.class, new Coercion<Map, SelectModel>()
1018            {
1019                @SuppressWarnings("unchecked")
1020                public SelectModel coerce(Map input)
1021                {
1022                    return TapestryInternalUtils.toSelectModel(input);
1023                }
1024            }));
1025    
1026            configuration.add(CoercionTuple.create(Collection.class, GridDataSource.class,
1027                    new Coercion<Collection, GridDataSource>()
1028                    {
1029                        public GridDataSource coerce(Collection input)
1030                        {
1031                            return new CollectionGridDataSource(input);
1032                        }
1033                    }));
1034    
1035            configuration.add(CoercionTuple.create(void.class, GridDataSource.class, new Coercion<Void, GridDataSource>()
1036            {
1037                private final GridDataSource source = new NullDataSource();
1038    
1039                public GridDataSource coerce(Void input)
1040                {
1041                    return source;
1042                }
1043            }));
1044    
1045            configuration.add(CoercionTuple.create(List.class, SelectModel.class, new Coercion<List, SelectModel>()
1046            {
1047                @SuppressWarnings("unchecked")
1048                public SelectModel coerce(List input)
1049                {
1050                    return TapestryInternalUtils.toSelectModel(input);
1051                }
1052            }));
1053    
1054            configuration.add(CoercionTuple.create(String.class, Pattern.class, new Coercion<String, Pattern>()
1055            {
1056                public Pattern coerce(String input)
1057                {
1058                    return Pattern.compile(input);
1059                }
1060            }));
1061    
1062            configuration.add(CoercionTuple.create(ComponentResourcesAware.class, ComponentResources.class,
1063                    new Coercion<ComponentResourcesAware, ComponentResources>()
1064                    {
1065    
1066                        public ComponentResources coerce(ComponentResourcesAware input)
1067                        {
1068                            return input.getComponentResources();
1069                        }
1070                    }));
1071    
1072            configuration.add(CoercionTuple.create(String.class, Renderable.class, new Coercion<String, Renderable>()
1073            {
1074                public Renderable coerce(String input)
1075                {
1076                    return new StringRenderable(input);
1077                }
1078            }));
1079    
1080            configuration.add(CoercionTuple.create(Renderable.class, Block.class, new Coercion<Renderable, Block>()
1081            {
1082                public Block coerce(Renderable input)
1083                {
1084                    return new RenderableAsBlock(input);
1085                }
1086            }));
1087    
1088            configuration.add(CoercionTuple.create(String.class, DateFormat.class, new Coercion<String, DateFormat>()
1089            {
1090                public DateFormat coerce(String input)
1091                {
1092                    return new SimpleDateFormat(input, threadLocale.getLocale());
1093                }
1094            }));
1095    
1096            configuration.add(CoercionTuple.create(String.class, Resource.class, new Coercion<String, Resource>()
1097            {
1098                public Resource coerce(String input)
1099                {
1100                    return assetSource.resourceForPath(input);
1101                }
1102            }));
1103    
1104            configuration.add(CoercionTuple.create(Renderable.class, RenderCommand.class,
1105                    new Coercion<Renderable, RenderCommand>()
1106                    {
1107                        public RenderCommand coerce(final Renderable input)
1108                        {
1109                            return new RenderCommand()
1110                            {
1111                                public void render(MarkupWriter writer, RenderQueue queue)
1112                                {
1113                                    input.render(writer);
1114                                }
1115                            };
1116                        }
1117                    }));
1118    
1119            configuration.add(CoercionTuple.create(Date.class, Calendar.class, new Coercion<Date, Calendar>()
1120            {
1121                public Calendar coerce(Date input)
1122                {
1123                    Calendar calendar = Calendar.getInstance(threadLocale.getLocale());
1124                    calendar.setTime(input);
1125                    return calendar;
1126                }
1127            }));
1128    
1129            configuration.add(CoercionTuple.create(Resource.class, DynamicTemplate.class,
1130                    new Coercion<Resource, DynamicTemplate>()
1131                    {
1132                        public DynamicTemplate coerce(Resource input)
1133                        {
1134                            return dynamicTemplateParser.parseTemplate(input);
1135                        }
1136                    }));
1137    
1138            configuration.add(CoercionTuple.create(Asset.class, Resource.class, new Coercion<Asset, Resource>()
1139            {
1140                public Resource coerce(Asset input)
1141                {
1142                    return input.getResource();
1143                }
1144            }));
1145    
1146            // Add support for "true" and "false", for compatibility with Tapestry 5.1 and earlier.
1147            // These aliases may be removed in some later release.
1148    
1149            StringToEnumCoercion<ClientValidation> stringToClientValidationCoercion = StringToEnumCoercion
1150                    .create(ClientValidation.class).addAlias("true", ClientValidation.BLUR)
1151                    .addAlias("false", ClientValidation.NONE);
1152    
1153            configuration.add(CoercionTuple.create(String.class, ClientValidation.class, stringToClientValidationCoercion));
1154    
1155            configuration.add(CCTWToCCTW2Coercion.TUPLE);
1156    
1157            configuration.add(CoercionTuple.create(ValueEncoder.class, ValueEncoderFactory.class, new Coercion<ValueEncoder, ValueEncoderFactory>()
1158            {
1159                public ValueEncoderFactory coerce(ValueEncoder input)
1160                {
1161                    return new GenericValueEncoderFactory(input);
1162                }
1163            }));
1164    
1165            configuration.add(CoercionTuple.create(InjectionProvider.class, InjectionProvider2.class,
1166                    new InjectionProviderToInjectionProvider2(classCache)));
1167        }
1168    
1169        /**
1170         * Adds built-in constraint generators:
1171         * <ul>
1172         * <li>PrimtiveField -- primitive fields are always required
1173         * <li>ValidateAnnotation -- adds constraints from a {@link Validate} annotation
1174         * </ul>
1175         */
1176        public static void contributeValidationConstraintGenerator(
1177                OrderedConfiguration<ValidationConstraintGenerator> configuration)
1178        {
1179            configuration.add("PrimitiveField", new PrimitiveFieldConstraintGenerator());
1180            configuration.add("ValidateAnnotation", new ValidateAnnotationConstraintGenerator());
1181            configuration.addInstance("Messages", MessagesConstraintGenerator.class);
1182        }
1183    
1184        private static void add(OrderedConfiguration<ComponentClassTransformWorker2> configuration,
1185                                Class<? extends Annotation> annotationClass, MethodDescription description)
1186        {
1187            String name = TapestryInternalUtils.lastTerm(annotationClass.getName());
1188    
1189            ComponentClassTransformWorker2 worker = new PageLifecycleAnnotationWorker(annotationClass,
1190                    description, name);
1191    
1192            configuration.add(name, worker);
1193        }
1194    
1195        // ========================================================================
1196        //
1197        // Service Builder Methods (instance)
1198        //
1199        // ========================================================================
1200    
1201        public Context buildContext(ApplicationGlobals globals)
1202        {
1203            return shadowBuilder.build(globals, "context", Context.class);
1204        }
1205    
1206        public static ComponentClassResolver buildComponentClassResolver(@Autobuild
1207                                                                         ComponentClassResolverImpl service, @ComponentClasses
1208        InvalidationEventHub hub)
1209        {
1210            // Allow the resolver to clean its cache when the component classes
1211            // change
1212    
1213            hub.addInvalidationListener(service);
1214    
1215            return service;
1216        }
1217    
1218        @Marker(ContextProvider.class)
1219        public AssetFactory buildContextAssetFactory(ApplicationGlobals globals,
1220    
1221                                                     AssetPathConstructor assetPathConstructor,
1222    
1223                                                     AssetPathConverter converter)
1224        {
1225            return new ContextAssetFactory(assetPathConstructor, globals.getContext(), converter);
1226        }
1227    
1228        /**
1229         * Builds the PropBindingFactory as a chain of command. The terminator of
1230         * the chain is responsible for ordinary
1231         * property names (and property paths).
1232         * <p/>
1233         * This mechanism has been replaced in 5.1 with a more sophisticated parser based on ANTLR. See <a
1234         * href="https://issues.apache.org/jira/browse/TAP5-79">TAP5-79</a> for details. There are no longer any built-in
1235         * contributions to the configuration.
1236         *
1237         * @param configuration contributions of special factories for some constants, each
1238         *                      contributed factory may return a
1239         *                      binding if applicable, or null otherwise
1240         */
1241        public BindingFactory buildPropBindingFactory(List<BindingFactory> configuration, @Autobuild
1242        PropBindingFactory service)
1243        {
1244            configuration.add(service);
1245    
1246            return chainBuilder.build(BindingFactory.class, configuration);
1247        }
1248    
1249        public static MetaDataLocator buildMetaDataLocator(@Autobuild
1250                                                           MetaDataLocatorImpl service, @ComponentClasses
1251        InvalidationEventHub hub)
1252        {
1253            hub.addInvalidationListener(service);
1254    
1255            return service;
1256        }
1257    
1258        public PersistentFieldStrategy buildClientPersistentFieldStrategy(LinkCreationHub linkCreationHub, @Autobuild
1259        ClientPersistentFieldStrategy service)
1260        {
1261            linkCreationHub.addListener(service);
1262    
1263            return service;
1264        }
1265    
1266        /**
1267         * Builds a proxy to the current {@link org.apache.tapestry5.RenderSupport} inside this thread's
1268         * {@link org.apache.tapestry5.services.Environment}.
1269         */
1270        public RenderSupport buildRenderSupport()
1271        {
1272            return environmentalBuilder.build(RenderSupport.class);
1273        }
1274    
1275        /**
1276         * Builds a proxy to the current {@link JavaScriptSupport} inside this thread's {@link Environment}.
1277         *
1278         * @since 5.2.0
1279         */
1280        public JavaScriptSupport buildJavaScriptSupport()
1281        {
1282            return environmentalBuilder.build(JavaScriptSupport.class);
1283        }
1284    
1285        /**
1286         * Builds a proxy to the current {@link org.apache.tapestry5.services.ClientBehaviorSupport} inside this
1287         * thread's {@link org.apache.tapestry5.services.Environment}.
1288         *
1289         * @since 5.1.0.1
1290         */
1291    
1292        public ClientBehaviorSupport buildClientBehaviorSupport()
1293        {
1294            return environmentalBuilder.build(ClientBehaviorSupport.class);
1295        }
1296    
1297        /**
1298         * Builds a proxy to the current {@link org.apache.tapestry5.services.FormSupport} inside this
1299         * thread's {@link org.apache.tapestry5.services.Environment}.
1300         */
1301        public FormSupport buildFormSupport()
1302        {
1303            return environmentalBuilder.build(FormSupport.class);
1304        }
1305    
1306        /**
1307         * Allows the exact steps in the component class transformation process to
1308         * be defined.
1309         */
1310        @Marker(Primary.class)
1311        public ComponentClassTransformWorker2 buildComponentClassTransformWorker(
1312                List<ComponentClassTransformWorker2> configuration)
1313    
1314        {
1315            return chainBuilder.build(ComponentClassTransformWorker2.class, configuration);
1316        }
1317    
1318        /**
1319         * Analyzes properties to determine the data types, used to
1320         * {@linkplain #provideDefaultBeanBlocks(org.apache.tapestry5.ioc.Configuration)} locale
1321         * display and edit blocks for properties. The default behaviors
1322         * look for a {@link org.apache.tapestry5.beaneditor.DataType} annotation
1323         * before deriving the data type from the property type.
1324         */
1325        @Marker(Primary.class)
1326        public DataTypeAnalyzer buildDataTypeAnalyzer(List<DataTypeAnalyzer> configuration)
1327        {
1328            return chainBuilder.build(DataTypeAnalyzer.class, configuration);
1329        }
1330    
1331        /**
1332         * A chain of command for providing values for {@link Inject}-ed fields in
1333         * component classes. The service's
1334         * configuration can be extended to allow for different automatic injections
1335         * (based on some combination of field
1336         * type and field name).
1337         * <p/>
1338         * Note that contributions to this service may be old-style {@link InjectionProvider}, which will
1339         * be coerced to {@link InjectionProvider2}.
1340         */
1341        public InjectionProvider2 buildInjectionProvider(List<InjectionProvider2> configuration)
1342        {
1343            return chainBuilder.build(InjectionProvider2.class, configuration);
1344        }
1345    
1346        /**
1347         * Initializes the application, using a pipeline of {@link org.apache.tapestry5.services.ApplicationInitializer}s.
1348         */
1349        @Marker(Primary.class)
1350        public ApplicationInitializer buildApplicationInitializer(Logger logger,
1351                                                                  List<ApplicationInitializerFilter> configuration)
1352        {
1353            ApplicationInitializer terminator = new ApplicationInitializerTerminator();
1354    
1355            return pipelineBuilder.build(logger, ApplicationInitializer.class, ApplicationInitializerFilter.class,
1356                    configuration, terminator);
1357        }
1358    
1359        public HttpServletRequestHandler buildHttpServletRequestHandler(Logger logger,
1360    
1361                                                                        List<HttpServletRequestFilter> configuration,
1362    
1363                                                                        @Primary
1364                                                                        RequestHandler handler,
1365    
1366                                                                        @Symbol(SymbolConstants.CHARSET)
1367                                                                        String applicationCharset,
1368    
1369                                                                        TapestrySessionFactory sessionFactory)
1370        {
1371            HttpServletRequestHandler terminator = new HttpServletRequestHandlerTerminator(handler, applicationCharset,
1372                    sessionFactory);
1373    
1374            return pipelineBuilder.build(logger, HttpServletRequestHandler.class, HttpServletRequestFilter.class,
1375                    configuration, terminator);
1376        }
1377    
1378        @Marker(Primary.class)
1379        public RequestHandler buildRequestHandler(Logger logger, List<RequestFilter> configuration,
1380    
1381                                                  @Primary
1382                                                  Dispatcher masterDispatcher)
1383        {
1384            RequestHandler terminator = new RequestHandlerTerminator(masterDispatcher);
1385    
1386            return pipelineBuilder.build(logger, RequestHandler.class, RequestFilter.class, configuration, terminator);
1387        }
1388    
1389        public ServletApplicationInitializer buildServletApplicationInitializer(Logger logger,
1390                                                                                List<ServletApplicationInitializerFilter> configuration,
1391    
1392                                                                                @Primary
1393                                                                                ApplicationInitializer initializer)
1394        {
1395            ServletApplicationInitializer terminator = new ServletApplicationInitializerTerminator(initializer);
1396    
1397            return pipelineBuilder.build(logger, ServletApplicationInitializer.class,
1398                    ServletApplicationInitializerFilter.class, configuration, terminator);
1399        }
1400    
1401        /**
1402         * The component event result processor used for normal component requests.
1403         */
1404        @Marker(
1405                {Primary.class, Traditional.class})
1406        public ComponentEventResultProcessor buildComponentEventResultProcessor(
1407                Map<Class, ComponentEventResultProcessor> configuration)
1408        {
1409            return constructComponentEventResultProcessor(configuration);
1410        }
1411    
1412        /**
1413         * The component event result processor used for Ajax-oriented component
1414         * requests.
1415         */
1416        @Marker(Ajax.class)
1417        public ComponentEventResultProcessor buildAjaxComponentEventResultProcessor(
1418                Map<Class, ComponentEventResultProcessor> configuration)
1419        {
1420            return constructComponentEventResultProcessor(configuration);
1421        }
1422    
1423        private ComponentEventResultProcessor constructComponentEventResultProcessor(
1424                Map<Class, ComponentEventResultProcessor> configuration)
1425        {
1426            Set<Class> handledTypes = CollectionFactory.newSet(configuration.keySet());
1427    
1428            // A slight hack!
1429    
1430            configuration.put(Object.class, new ObjectComponentEventResultProcessor(handledTypes));
1431    
1432            StrategyRegistry<ComponentEventResultProcessor> registry = StrategyRegistry.newInstance(
1433                    ComponentEventResultProcessor.class, configuration);
1434    
1435            return strategyBuilder.build(registry);
1436        }
1437    
1438        /**
1439         * The default data type analyzer is the final analyzer consulted and
1440         * identifies the type entirely pased on the
1441         * property type, working against its own configuration (mapping property
1442         * type class to data type).
1443         */
1444        public static DataTypeAnalyzer buildDefaultDataTypeAnalyzer(@Autobuild
1445                                                                    DefaultDataTypeAnalyzer service, @ComponentClasses
1446        InvalidationEventHub hub)
1447        {
1448            hub.addInvalidationListener(service);
1449    
1450            return service;
1451        }
1452    
1453        public static TranslatorSource buildTranslatorSource(Map<Class, Translator> configuration,
1454                                                             TranslatorAlternatesSource alternatesSource, @ComponentClasses
1455        InvalidationEventHub hub)
1456        {
1457            TranslatorSourceImpl service = new TranslatorSourceImpl(configuration,
1458                    alternatesSource.getTranslatorAlternates());
1459    
1460            hub.addInvalidationListener(service);
1461    
1462            return service;
1463        }
1464    
1465        @Marker(Primary.class)
1466        public ObjectRenderer buildObjectRenderer(Map<Class, ObjectRenderer> configuration)
1467        {
1468            return strategyBuilder.build(ObjectRenderer.class, configuration);
1469        }
1470    
1471        /**
1472         * Returns a {@link org.apache.tapestry5.ioc.services.ClassFactory} that can
1473         * be used to create extra classes around
1474         * component classes. This ClassFactory will be cleared whenever an
1475         * underlying component class is discovered to have
1476         * changed. Use of this class factory implies that your code will become
1477         * aware of this (if necessary) to discard any
1478         * cached object (alas, this currently involves dipping into the internals
1479         * side to register for the correct
1480         * notifications). Failure to properly clean up can result in really nasty
1481         * PermGen space memory leaks.
1482         */
1483        @Marker(ComponentLayer.class)
1484        public ClassFactory buildComponentClassFactory(ComponentInstantiatorSource source)
1485        {
1486            return shadowBuilder.build(source, "classFactory", ClassFactory.class);
1487        }
1488    
1489        /**
1490         * Returns a {@link PlasticProxyFactory} that can be used to create extra classes around component classes. This
1491         * factory will be cleared whenever an underlying component class is discovered to have changed. Use of this
1492         * factory implies that your code will become aware of this (if necessary) to discard any cached object (alas,
1493         * this currently involves dipping into the internals side to register for the correct notifications). Failure to
1494         * properly clean up can result in really nasty PermGen space memory leaks.
1495         */
1496        @Marker(ComponentLayer.class)
1497        public PlasticProxyFactory buildComponentProxyFactory(ComponentInstantiatorSource source)
1498        {
1499            return shadowBuilder.build(source, "proxyFactory", PlasticProxyFactory.class);
1500        }
1501    
1502        /**
1503         * Ordered contributions to the MasterDispatcher service allow different URL
1504         * matching strategies to occur.
1505         */
1506        @Marker(Primary.class)
1507        public Dispatcher buildMasterDispatcher(List<Dispatcher> configuration)
1508        {
1509            return chainBuilder.build(Dispatcher.class, configuration);
1510        }
1511    
1512        /**
1513         * Builds a shadow of the RequestGlobals.request property. Note again that
1514         * the shadow can be an ordinary singleton,
1515         * even though RequestGlobals is perthread.
1516         */
1517        public Request buildRequest()
1518        {
1519            return shadowBuilder.build(requestGlobals, "request", Request.class);
1520        }
1521    
1522        /**
1523         * Builds a shadow of the RequestGlobals.HTTPServletRequest property.
1524         * Generally, you should inject the {@link Request} service instead, as
1525         * future version of Tapestry may operate beyond just the servlet API.
1526         */
1527        public HttpServletRequest buildHttpServletRequest()
1528        {
1529            return shadowBuilder.build(requestGlobals, "HTTPServletRequest", HttpServletRequest.class);
1530        }
1531    
1532        /**
1533         * @since 5.1.0.0
1534         */
1535        public HttpServletResponse buildHttpServletResponse()
1536        {
1537            return shadowBuilder.build(requestGlobals, "HTTPServletResponse", HttpServletResponse.class);
1538        }
1539    
1540        /**
1541         * Builds a shadow of the RequestGlobals.response property. Note again that
1542         * the shadow can be an ordinary singleton,
1543         * even though RequestGlobals is perthread.
1544         */
1545        public Response buildResponse()
1546        {
1547            return shadowBuilder.build(requestGlobals, "response", Response.class);
1548        }
1549    
1550        /**
1551         * The MarkupRenderer service is used to render a full page as markup.
1552         * Supports an ordered configuration of {@link org.apache.tapestry5.services.MarkupRendererFilter}s.
1553         */
1554        public MarkupRenderer buildMarkupRenderer(Logger logger, @Autobuild
1555        MarkupRendererTerminator terminator, List<MarkupRendererFilter> configuration)
1556        {
1557            return pipelineBuilder.build(logger, MarkupRenderer.class, MarkupRendererFilter.class, configuration,
1558                    terminator);
1559        }
1560    
1561        /**
1562         * A wrapper around {@link org.apache.tapestry5.internal.services.PageRenderQueue} used for
1563         * partial page renders.
1564         * Supports an ordered configuration of {@link org.apache.tapestry5.services.PartialMarkupRendererFilter}s.
1565         *
1566         * @see #contributePartialMarkupRenderer(org.apache.tapestry5.ioc.OrderedConfiguration, org.apache.tapestry5.Asset, org.apache.tapestry5.services.javascript.JavaScriptStackSource, org.apache.tapestry5.internal.services.javascript.JavaScriptStackPathConstructor, org.apache.tapestry5.ioc.services.SymbolSource, AssetSource)
1567         */
1568        public PartialMarkupRenderer buildPartialMarkupRenderer(Logger logger,
1569                                                                List<PartialMarkupRendererFilter> configuration, @Autobuild
1570        PartialMarkupRendererTerminator terminator)
1571        {
1572    
1573            return pipelineBuilder.build(logger, PartialMarkupRenderer.class, PartialMarkupRendererFilter.class,
1574                    configuration, terminator);
1575        }
1576    
1577        public PageRenderRequestHandler buildPageRenderRequestHandler(List<PageRenderRequestFilter> configuration,
1578                                                                      Logger logger, @Autobuild
1579        PageRenderRequestHandlerImpl terminator)
1580        {
1581            return pipelineBuilder.build(logger, PageRenderRequestHandler.class, PageRenderRequestFilter.class,
1582                    configuration, terminator);
1583        }
1584    
1585        /**
1586         * Builds the component action request handler for traditional (non-Ajax)
1587         * requests. These typically result in a
1588         * redirect to a Tapestry render URL.
1589         */
1590        @Marker(
1591                {Traditional.class, Primary.class})
1592        public ComponentEventRequestHandler buildComponentEventRequestHandler(
1593                List<ComponentEventRequestFilter> configuration, Logger logger, @Autobuild
1594        ComponentEventRequestHandlerImpl terminator)
1595        {
1596            return pipelineBuilder.build(logger, ComponentEventRequestHandler.class, ComponentEventRequestFilter.class,
1597                    configuration, terminator);
1598        }
1599    
1600        /**
1601         * Builds the action request handler for Ajax requests, based on a
1602         * {@linkplain org.apache.tapestry5.ioc.services.PipelineBuilder
1603         * pipeline} around {@link org.apache.tapestry5.internal.services.AjaxComponentEventRequestHandler} . Filters on
1604         * the
1605         * request handler are supported here as well.
1606         */
1607        @Marker(
1608                {Ajax.class, Primary.class})
1609        public ComponentEventRequestHandler buildAjaxComponentEventRequestHandler(
1610                List<ComponentEventRequestFilter> configuration, Logger logger, @Autobuild
1611        AjaxComponentEventRequestHandler terminator)
1612        {
1613            return pipelineBuilder.build(logger, ComponentEventRequestHandler.class, ComponentEventRequestFilter.class,
1614                    configuration, terminator);
1615        }
1616    
1617        // ========================================================================
1618        //
1619        // Service Contribution Methods (instance)
1620        //
1621        // ========================================================================
1622    
1623        /**
1624         * Contributes the default "session" strategy.
1625         */
1626        public void contributeApplicationStatePersistenceStrategySource(
1627                MappedConfiguration<String, ApplicationStatePersistenceStrategy> configuration,
1628    
1629                @Local
1630                ApplicationStatePersistenceStrategy sessionStategy)
1631        {
1632            configuration.add("session", sessionStategy);
1633        }
1634    
1635        public void contributeAssetSource(MappedConfiguration<String, AssetFactory> configuration, @ContextProvider
1636        AssetFactory contextAssetFactory,
1637    
1638                                          @ClasspathProvider
1639                                          AssetFactory classpathAssetFactory)
1640        {
1641            configuration.add(AssetConstants.CONTEXT, contextAssetFactory);
1642            configuration.add(AssetConstants.CLASSPATH, classpathAssetFactory);
1643        }
1644    
1645        /**
1646         * Contributes handlers for the following types:
1647         * <dl>
1648         * <dt>Object</dt>
1649         * <dd>Failure case, added to provide a more useful exception message</dd>
1650         * <dt>{@link Link}</dt>
1651         * <dd>Sends a redirect to the link (which is typically a page render link)</dd>
1652         * <dt>String</dt>
1653         * <dd>Sends a page render redirect</dd>
1654         * <dt>Class</dt>
1655         * <dd>Interpreted as the class name of a page, sends a page render render redirect (this is more refactoring safe
1656         * than the page name)</dd>
1657         * <dt>{@link Component}</dt>
1658         * <dd>A page's root component (though a non-root component will work, but will generate a warning). A direct to the
1659         * containing page is sent.</dd>
1660         * <dt>{@link org.apache.tapestry5.StreamResponse}</dt>
1661         * <dd>The stream response is sent as the actual reply.</dd>
1662         * <dt>URL</dt>
1663         * <dd>Sends a redirect to a (presumably) external URL</dd>
1664         * </dl>
1665         */
1666        public void contributeComponentEventResultProcessor(@Traditional
1667                                                            @ComponentInstanceProcessor
1668                                                            ComponentEventResultProcessor componentInstanceProcessor,
1669    
1670                                                            MappedConfiguration<Class, ComponentEventResultProcessor> configuration)
1671        {
1672            configuration.add(Link.class, new ComponentEventResultProcessor<Link>()
1673            {
1674                public void processResultValue(Link value) throws IOException
1675                {
1676                    response.sendRedirect(value);
1677                }
1678            });
1679    
1680            configuration.add(URL.class, new ComponentEventResultProcessor<URL>()
1681            {
1682                public void processResultValue(URL value) throws IOException
1683                {
1684                    response.sendRedirect(value.toExternalForm());
1685                }
1686            });
1687    
1688            configuration.addInstance(HttpError.class, HttpErrorComponentEventResultProcessor.class);
1689    
1690            configuration.addInstance(String.class, PageNameComponentEventResultProcessor.class);
1691    
1692            configuration.addInstance(Class.class, ClassResultProcessor.class);
1693    
1694            configuration.add(Component.class, componentInstanceProcessor);
1695    
1696            configuration.addInstance(StreamResponse.class, StreamResponseResultProcessor.class);
1697    
1698            configuration.addInstance(StreamPageContent.class, StreamPageContentResultProcessor.class);
1699        }
1700    
1701        /**
1702         * Contributes handlers for the following types:
1703         * <dl>
1704         * <dt>Object</dt>
1705         * <dd>Failure case, added to provide more useful exception message</dd>
1706         * <dt>{@link RenderCommand}</dt>
1707         * <dd>Typically, a {@link org.apache.tapestry5.Block}</dd>
1708         * <dt>{@link org.apache.tapestry5.annotations.Component}</dt>
1709         * <dd>Renders the component and its body (unless its a page, in which case a redirect JSON response is sent)</dd>
1710         * <dt>{@link org.apache.tapestry5.json.JSONObject} or {@link org.apache.tapestry5.json.JSONArray}</dt>
1711         * <dd>The JSONObject is returned as a text/javascript response</dd>
1712         * <dt>{@link org.apache.tapestry5.StreamResponse}</dt>
1713         * <dd>The stream response is sent as the actual response</dd>
1714         * <dt>String</dt>
1715         * <dd>Interprets the value as a logical page name and sends a client response to redirect to that page</dd>
1716         * <dt>{@link org.apache.tapestry5.Link}</dt>
1717         * <dd>Sends a JSON response to redirect to the link</dd>
1718         * <dt>{@link Class}</dt>
1719         * <dd>Treats the class as a page class and sends a redirect for a page render for that page</dd>
1720         * <dt>{@link org.apache.tapestry5.ajax.MultiZoneUpdate}</dt>
1721         * <dd>Sends a single JSON response to update the content of multiple zones
1722         * </dl>
1723         * <p/>
1724         * In most cases, when you want to support a new type, you should convert it to one of the built-in supported types
1725         * (such as {@link RenderCommand}. You can then inject the master AjaxComponentEventResultProcessor (use the
1726         * {@link Ajax} marker annotation) and delegate to it.
1727         */
1728        @Contribute(ComponentEventResultProcessor.class)
1729        @Ajax
1730        public static void provideBaseAjaxComponentEventResultProcessors(
1731                MappedConfiguration<Class, ComponentEventResultProcessor> configuration)
1732        {
1733            configuration.addInstance(RenderCommand.class, RenderCommandComponentEventResultProcessor.class);
1734            configuration.addInstance(Component.class, AjaxComponentInstanceEventResultProcessor.class);
1735            configuration.addInstance(JSONObject.class, JSONObjectEventResultProcessor.class);
1736            configuration.addInstance(JSONArray.class, JSONArrayEventResultProcessor.class);
1737            configuration.addInstance(StreamResponse.class, StreamResponseResultProcessor.class);
1738            configuration.addInstance(String.class, AjaxPageNameComponentEventResultProcessor.class);
1739            configuration.addInstance(Link.class, AjaxLinkComponentEventResultProcessor.class);
1740            configuration.addInstance(Class.class, AjaxPageClassComponentEventResultProcessor.class);
1741            configuration.addInstance(MultiZoneUpdate.class, MultiZoneUpdateEventResultProcessor.class);
1742            configuration.addInstance(HttpError.class, HttpErrorComponentEventResultProcessor.class);
1743        }
1744    
1745        /**
1746         * The MasterDispatcher is a chain-of-command of individual Dispatchers,
1747         * each handling (like a servlet) a particular
1748         * kind of incoming request.
1749         * <dl>
1750         * <dt>RootPath</dt>
1751         * <dd>Renders the start page for the "/" request (outdated)</dd>
1752         * <dt>Asset</dt>
1753         * <dd>Provides access to assets (context, classpath and virtual) via {@link AssetDispatcher}</dd>
1754         * <dt>PageRender</dt>
1755         * <dd>Identifies the {@link org.apache.tapestry5.services.PageRenderRequestParameters} and forwards onto
1756         * {@link PageRenderRequestHandler}</dd>
1757         * <dt>ComponentEvent</dt>
1758         * <dd>Identifies the {@link ComponentEventRequestParameters} and forwards onto the
1759         * {@link ComponentEventRequestHandler}</dd>
1760         * </dl>
1761         */
1762        public static void contributeMasterDispatcher(OrderedConfiguration<Dispatcher> configuration,
1763    
1764                                                      @InjectService("AssetDispatcher")
1765                                                      Dispatcher assetDispatcher)
1766        {
1767            // Looks for the root path and renders the start page. This is
1768            // maintained for compatibility
1769            // with earlier versions of Tapestry 5, it is recommended that an Index
1770            // page be used instead.
1771    
1772            configuration.addInstance("RootPath", RootPathDispatcher.class, "before:Asset");
1773    
1774            // This goes first because an asset to be streamed may have an file
1775            // extension, such as
1776            // ".html", that will confuse the later dispatchers.
1777    
1778            configuration.add("Asset", assetDispatcher, "before:ComponentEvent");
1779    
1780            configuration.addInstance("ComponentEvent", ComponentEventDispatcher.class, "before:PageRender");
1781    
1782            configuration.addInstance("PageRender", PageRenderDispatcher.class);
1783        }
1784    
1785        /**
1786         * Contributes a default object renderer for type Object, plus specialized
1787         * renderers for {@link org.apache.tapestry5.services.Request}, {@link org.apache.tapestry5.ioc.Location},
1788         * {@link org.apache.tapestry5.ComponentResources}, {@link org.apache.tapestry5.EventContext},
1789         * {@link AvailableValues},
1790         * List, and Object[].
1791         */
1792        @SuppressWarnings("unchecked")
1793        public void contributeObjectRenderer(MappedConfiguration<Class, ObjectRenderer> configuration,
1794    
1795                                             @InjectService("LocationRenderer")
1796                                             ObjectRenderer locationRenderer,
1797    
1798                                             final TypeCoercer typeCoercer)
1799        {
1800            configuration.add(Object.class, new DefaultObjectRenderer());
1801    
1802            configuration.addInstance(Request.class, RequestRenderer.class);
1803    
1804            configuration.add(Location.class, locationRenderer);
1805    
1806            ObjectRenderer preformatted = new ObjectRenderer<Object>()
1807            {
1808                public void render(Object object, MarkupWriter writer)
1809                {
1810                    writer.element("pre");
1811                    writer.write(typeCoercer.coerce(object, String.class));
1812                    writer.end();
1813                }
1814            };
1815    
1816            configuration.add(ClassTransformation.class, preformatted);
1817    
1818            configuration.addInstance(List.class, ListRenderer.class);
1819            configuration.addInstance(Object[].class, ObjectArrayRenderer.class);
1820            configuration.addInstance(ComponentResources.class, ComponentResourcesRenderer.class);
1821            configuration.addInstance(EventContext.class, EventContextRenderer.class);
1822            configuration.add(AvailableValues.class, new AvailableValuesRenderer());
1823        }
1824    
1825        /**
1826         * Adds page render filters, each of which provides an {@link org.apache.tapestry5.annotations.Environmental}
1827         * service. Filters
1828         * often provide {@link org.apache.tapestry5.annotations.Environmental} services needed by
1829         * components as they render.
1830         * <dl>
1831         * <dt>DocumentLinker</dt>
1832         * <dd>Provides {@link org.apache.tapestry5.internal.services.DocumentLinker}</dd>
1833         * <dt>JavascriptSupport</dt>
1834         * <dd>Provides {@link JavaScriptSupport}</dd>
1835         * <dt>RenderSupport</dt>
1836         * <dd>Provides {@link org.apache.tapestry5.RenderSupport}</dd>
1837         * <dt>InjectDefaultStylesheet</dt>
1838         * <dd>Injects the default stylesheet into all pages</dd></dt>
1839         * <dt>ClientBehaviorSupport</dt>
1840         * <dd>Provides {@link ClientBehaviorSupport}</dd>
1841         * <dt>Heartbeat</dt>
1842         * <dd>Provides {@link org.apache.tapestry5.services.Heartbeat}</dd>
1843         * <dt>ValidationDecorator</dt>
1844         * <dd>Provides {@link org.apache.tapestry5.ValidationDecorator} (via {@link ValidationDecoratorFactory#newInstance(org.apache.tapestry5.MarkupWriter)})</dd>
1845         * </dl>
1846         */
1847        public void contributeMarkupRenderer(OrderedConfiguration<MarkupRendererFilter> configuration,
1848    
1849                                             @Symbol(SymbolConstants.OMIT_GENERATOR_META)
1850                                             final boolean omitGeneratorMeta,
1851    
1852                                             @Symbol(SymbolConstants.TAPESTRY_VERSION)
1853                                             final String tapestryVersion,
1854    
1855                                             @Symbol(SymbolConstants.COMPACT_JSON)
1856                                             final boolean compactJSON,
1857    
1858                                             final SymbolSource symbolSource,
1859    
1860                                             final AssetSource assetSource,
1861    
1862                                             final JavaScriptStackSource javascriptStackSource,
1863    
1864                                             final JavaScriptStackPathConstructor javascriptStackPathConstructor,
1865    
1866                                             final ValidationDecoratorFactory validationDecoratorFactory,
1867    
1868                                             @Path("${tapestry.default-stylesheet}")
1869                                             final Asset defaultStylesheet)
1870        {
1871            MarkupRendererFilter documentLinker = new MarkupRendererFilter()
1872            {
1873                public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1874                {
1875                    DocumentLinkerImpl linker = new DocumentLinkerImpl(omitGeneratorMeta, tapestryVersion, compactJSON);
1876    
1877                    environment.push(DocumentLinker.class, linker);
1878    
1879                    renderer.renderMarkup(writer);
1880    
1881                    environment.pop(DocumentLinker.class);
1882    
1883                    linker.updateDocument(writer.getDocument());
1884                }
1885            };
1886    
1887            MarkupRendererFilter javaScriptSupport = new MarkupRendererFilter()
1888            {
1889                public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1890                {
1891                    DocumentLinker linker = environment.peekRequired(DocumentLinker.class);
1892    
1893                    JavaScriptSupportImpl support = new JavaScriptSupportImpl(linker, javascriptStackSource,
1894                            javascriptStackPathConstructor);
1895    
1896                    environment.push(JavaScriptSupport.class, support);
1897    
1898                    renderer.renderMarkup(writer);
1899    
1900                    environment.pop(JavaScriptSupport.class);
1901    
1902                    support.commit();
1903                }
1904            };
1905    
1906            MarkupRendererFilter renderSupport = new MarkupRendererFilter()
1907            {
1908                public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1909                {
1910                    JavaScriptSupport javascriptSupport = environment.peekRequired(JavaScriptSupport.class);
1911    
1912                    RenderSupportImpl support = new RenderSupportImpl(symbolSource, assetSource, javascriptSupport);
1913    
1914                    environment.push(RenderSupport.class, support);
1915    
1916                    renderer.renderMarkup(writer);
1917    
1918                    environment.pop(RenderSupport.class);
1919                }
1920            };
1921    
1922            MarkupRendererFilter injectDefaultStylesheet = new MarkupRendererFilter()
1923            {
1924                public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1925                {
1926                    DocumentLinker linker = environment.peekRequired(DocumentLinker.class);
1927    
1928                    linker.addStylesheetLink(new StylesheetLink(defaultStylesheet.toClientURL()));
1929    
1930                    renderer.renderMarkup(writer);
1931                }
1932            };
1933    
1934            MarkupRendererFilter clientBehaviorSupport = new MarkupRendererFilter()
1935            {
1936                public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1937                {
1938                    JavaScriptSupport javascriptSupport = environment.peekRequired(JavaScriptSupport.class);
1939    
1940                    ClientBehaviorSupportImpl clientBehaviorSupport = new ClientBehaviorSupportImpl(javascriptSupport,
1941                            environment);
1942    
1943                    environment.push(ClientBehaviorSupport.class, clientBehaviorSupport);
1944    
1945                    renderer.renderMarkup(writer);
1946    
1947                    environment.pop(ClientBehaviorSupport.class);
1948    
1949                    clientBehaviorSupport.commit();
1950                }
1951            };
1952    
1953            MarkupRendererFilter heartbeat = new MarkupRendererFilter()
1954            {
1955                public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1956                {
1957                    Heartbeat heartbeat = new HeartbeatImpl();
1958    
1959                    heartbeat.begin();
1960    
1961                    environment.push(Heartbeat.class, heartbeat);
1962    
1963                    renderer.renderMarkup(writer);
1964    
1965                    environment.pop(Heartbeat.class);
1966    
1967                    heartbeat.end();
1968                }
1969            };
1970    
1971            MarkupRendererFilter defaultValidationDecorator = new MarkupRendererFilter()
1972            {
1973                public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1974                {
1975                    ValidationDecorator decorator = validationDecoratorFactory.newInstance(writer);
1976    
1977                    environment.push(ValidationDecorator.class, decorator);
1978    
1979                    renderer.renderMarkup(writer);
1980    
1981                    environment.pop(ValidationDecorator.class);
1982                }
1983            };
1984    
1985            configuration.add("DocumentLinker", documentLinker);
1986            configuration.add("JavaScriptSupport", javaScriptSupport);
1987            configuration.add("RenderSupport", renderSupport);
1988            configuration.add("InjectDefaultStylesheet", injectDefaultStylesheet);
1989            configuration.add("ClientBehaviorSupport", clientBehaviorSupport);
1990            configuration.add("Heartbeat", heartbeat);
1991            configuration.add("ValidationDecorator", defaultValidationDecorator);
1992        }
1993    
1994        /**
1995         * Contributes {@link PartialMarkupRendererFilter}s used when rendering a
1996         * partial Ajax response.
1997         * <dl>
1998         * <dt>DocumentLinker
1999         * <dd>Provides {@link org.apache.tapestry5.internal.services.DocumentLinker}
2000         * <dt>JavaScriptSupport
2001         * <dd>Provides {@link JavaScriptSupport}</dd>
2002         * <dt>PageRenderSupport</dt>
2003         * <dd>Provides {@link org.apache.tapestry5.RenderSupport}</dd>
2004         * <dt>ClientBehaviorSupport</dt>
2005         * <dd>Provides {@link ClientBehaviorSupport}</dd>
2006         * <dt>Heartbeat</dt>
2007         * <dd>Provides {@link org.apache.tapestry5.services.Heartbeat}</dd>
2008         * <dt>DefaultValidationDecorator</dt>
2009         * <dt>ValidationDecorator</dt>
2010         * <dd>Provides {@link org.apache.tapestry5.ValidationDecorator} (via {@link ValidationDecoratorFactory#newInstance(org.apache.tapestry5.MarkupWriter)})</dd>
2011         * </dl>
2012         */
2013        public void contributePartialMarkupRenderer(OrderedConfiguration<PartialMarkupRendererFilter> configuration,
2014    
2015                                                    final ValidationDecoratorFactory validationDecoratorFactory,
2016    
2017                                                    final JavaScriptStackSource javascriptStackSource,
2018    
2019                                                    final JavaScriptStackPathConstructor javascriptStackPathConstructor,
2020    
2021                                                    final SymbolSource symbolSource,
2022    
2023                                                    final AssetSource assetSource)
2024        {
2025            PartialMarkupRendererFilter documentLinker = new PartialMarkupRendererFilter()
2026            {
2027                public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2028                {
2029                    PartialMarkupDocumentLinker linker = new PartialMarkupDocumentLinker();
2030    
2031                    environment.push(DocumentLinker.class, linker);
2032    
2033                    renderer.renderMarkup(writer, reply);
2034    
2035                    environment.pop(DocumentLinker.class);
2036    
2037                    linker.commit(reply);
2038                }
2039            };
2040    
2041            PartialMarkupRendererFilter javascriptSupport = new PartialMarkupRendererFilter()
2042            {
2043                public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2044                {
2045                    String uid = Long.toHexString(System.currentTimeMillis());
2046    
2047                    String namespace = "_" + uid;
2048    
2049                    IdAllocator idAllocator = new IdAllocator(namespace);
2050    
2051                    DocumentLinker linker = environment.peekRequired(DocumentLinker.class);
2052    
2053                    JavaScriptSupportImpl support = new JavaScriptSupportImpl(linker, javascriptStackSource,
2054                            javascriptStackPathConstructor, idAllocator, true);
2055    
2056                    environment.push(JavaScriptSupport.class, support);
2057    
2058                    renderer.renderMarkup(writer, reply);
2059    
2060                    environment.pop(JavaScriptSupport.class);
2061    
2062                    support.commit();
2063                }
2064            };
2065    
2066            PartialMarkupRendererFilter renderSupport = new PartialMarkupRendererFilter()
2067            {
2068                public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2069                {
2070                    JavaScriptSupport javascriptSupport = environment.peekRequired(JavaScriptSupport.class);
2071    
2072                    RenderSupportImpl support = new RenderSupportImpl(symbolSource, assetSource, javascriptSupport);
2073    
2074                    environment.push(RenderSupport.class, support);
2075    
2076                    renderer.renderMarkup(writer, reply);
2077    
2078                    environment.pop(RenderSupport.class);
2079                }
2080            };
2081    
2082            PartialMarkupRendererFilter clientBehaviorSupport = new PartialMarkupRendererFilter()
2083            {
2084                public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2085                {
2086                    JavaScriptSupport javascriptSupport = environment.peekRequired(JavaScriptSupport.class);
2087    
2088                    ClientBehaviorSupportImpl support = new ClientBehaviorSupportImpl(javascriptSupport, environment);
2089    
2090                    environment.push(ClientBehaviorSupport.class, support);
2091    
2092                    renderer.renderMarkup(writer, reply);
2093    
2094                    environment.pop(ClientBehaviorSupport.class);
2095    
2096                    support.commit();
2097                }
2098            };
2099    
2100            PartialMarkupRendererFilter heartbeat = new PartialMarkupRendererFilter()
2101            {
2102                public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2103                {
2104                    Heartbeat heartbeat = new HeartbeatImpl();
2105    
2106                    heartbeat.begin();
2107    
2108                    environment.push(Heartbeat.class, heartbeat);
2109    
2110                    renderer.renderMarkup(writer, reply);
2111    
2112                    environment.pop(Heartbeat.class);
2113    
2114                    heartbeat.end();
2115                }
2116            };
2117    
2118            PartialMarkupRendererFilter defaultValidationDecorator = new PartialMarkupRendererFilter()
2119            {
2120                public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2121                {
2122                    ValidationDecorator decorator = validationDecoratorFactory.newInstance(writer);
2123    
2124                    environment.push(ValidationDecorator.class, decorator);
2125    
2126                    renderer.renderMarkup(writer, reply);
2127    
2128                    environment.pop(ValidationDecorator.class);
2129                }
2130            };
2131    
2132            configuration.add("DocumentLinker", documentLinker);
2133            configuration.add("JavaScriptSupport", javascriptSupport);
2134            configuration.add("RenderSupport", renderSupport);
2135            configuration.add("ClientBehaviorSupport", clientBehaviorSupport);
2136            configuration.add("Heartbeat", heartbeat);
2137            configuration.add("ValidationDecorator", defaultValidationDecorator);
2138        }
2139    
2140        /**
2141         * Contributes several strategies:
2142         * <dl>
2143         * <dt>session
2144         * <dd>Values are stored in the {@link Session}
2145         * <dt>flash
2146         * <dd>Values are stored in the {@link Session}, until the next request (for the page)
2147         * <dt>client
2148         * <dd>Values are encoded into URLs (or hidden form fields)
2149         * </dl>
2150         */
2151        public void contributePersistentFieldManager(MappedConfiguration<String, PersistentFieldStrategy> configuration,
2152    
2153                                                     Request request,
2154    
2155                                                     @InjectService("ClientPersistentFieldStrategy")
2156                                                     PersistentFieldStrategy clientStrategy)
2157        {
2158            configuration.add(PersistenceConstants.SESSION, new SessionPersistentFieldStrategy(request));
2159            configuration.add(PersistenceConstants.FLASH, new FlashPersistentFieldStrategy(request));
2160            configuration.add(PersistenceConstants.CLIENT, clientStrategy);
2161        }
2162    
2163        @SuppressWarnings("rawtypes")
2164        public static ValueEncoderSource buildValueEncoderSource(Map<Class, ValueEncoderFactory> configuration,
2165                                                                 @ComponentClasses
2166                                                                 InvalidationEventHub hub)
2167        {
2168            ValueEncoderSourceImpl service = new ValueEncoderSourceImpl(configuration);
2169    
2170            hub.addInvalidationListener(service);
2171    
2172            return service;
2173        }
2174    
2175        /**
2176         * Contributes {@link ValueEncoder}s or {@link ValueEncoderFactory}s for types:
2177         * <ul>
2178         * <li>Object
2179         * <li>String
2180         * <li>Enum
2181         * </ul>
2182         */
2183        @SuppressWarnings("all")
2184        public static void contributeValueEncoderSource(MappedConfiguration<Class, Object> configuration)
2185        {
2186            configuration.addInstance(Object.class, TypeCoercedValueEncoderFactory.class);
2187            configuration.add(String.class, new StringValueEncoder());
2188            configuration.addInstance(Enum.class, EnumValueEncoderFactory.class);
2189        }
2190    
2191        /**
2192         * Contributes a single filter, "Secure", which checks for non-secure
2193         * requests that access secure pages.
2194         */
2195        public void contributePageRenderRequestHandler(OrderedConfiguration<PageRenderRequestFilter> configuration,
2196                                                       final RequestSecurityManager securityManager)
2197        {
2198            PageRenderRequestFilter secureFilter = new PageRenderRequestFilter()
2199            {
2200                public void handle(PageRenderRequestParameters parameters, PageRenderRequestHandler handler)
2201                        throws IOException
2202                {
2203    
2204                    if (securityManager.checkForInsecurePageRenderRequest(parameters))
2205                        return;
2206    
2207                    handler.handle(parameters);
2208                }
2209            };
2210    
2211            configuration.add("Secure", secureFilter);
2212        }
2213    
2214        /**
2215         * Configures the extensions that will require a digest to be downloaded via
2216         * the asset dispatcher. Most resources
2217         * are "safe", they don't require a digest. For unsafe resources, the digest
2218         * is incorporated into the URL to ensure
2219         * that the client side isn't just "fishing".
2220         * <p/>
2221         * The extensions must be all lower case.
2222         * <p/>
2223         * This contributes "class", "properties" and "tml" (the template extension).
2224         *
2225         * @param configuration collection of extensions
2226         */
2227        public static void contributeResourceDigestGenerator(Configuration<String> configuration)
2228        {
2229            // Java class files always require a digest.
2230            configuration.add("class");
2231    
2232            // Even though properties don't contain sensible data we should protect
2233            // them.
2234            configuration.add("properties");
2235    
2236            // Likewise, we don't want people fishing for templates.
2237            configuration.add(TapestryConstants.TEMPLATE_EXTENSION);
2238        }
2239    
2240        public static void contributeTemplateParser(MappedConfiguration<String, URL> config)
2241        {
2242            // Any class inside the internal module would do. Or we could move all
2243            // these
2244            // files to o.a.t.services.
2245    
2246            Class c = TemplateParserImpl.class;
2247    
2248            config.add("-//W3C//DTD XHTML 1.0 Strict//EN", c.getResource("xhtml1-strict.dtd"));
2249            config.add("-//W3C//DTD XHTML 1.0 Transitional//EN", c.getResource("xhtml1-transitional.dtd"));
2250            config.add("-//W3C//DTD XHTML 1.0 Frameset//EN", c.getResource("xhtml1-frameset.dtd"));
2251            config.add("-//W3C//DTD HTML 4.01//EN", c.getResource("xhtml1-strict.dtd"));
2252            config.add("-//W3C//DTD HTML 4.01 Transitional//EN", c.getResource("xhtml1-transitional.dtd"));
2253            config.add("-//W3C//DTD HTML 4.01 Frameset//EN", c.getResource("xhtml1-frameset.dtd"));
2254            config.add("-//W3C//ENTITIES Latin 1 for XHTML//EN", c.getResource("xhtml-lat1.ent"));
2255            config.add("-//W3C//ENTITIES Symbols for XHTML//EN", c.getResource("xhtml-symbol.ent"));
2256            config.add("-//W3C//ENTITIES Special for XHTML//EN", c.getResource("xhtml-special.ent"));
2257        }
2258    
2259        /**
2260         * Contributes factory defaults that may be overridden.
2261         */
2262        public static void contributeFactoryDefaults(MappedConfiguration<String, Object> configuration)
2263        {
2264            // Remember this is request-to-request time, presumably it'll take the
2265            // developer more than
2266            // one second to make a change, save it, and switch back to the browser.
2267    
2268            configuration.add(SymbolConstants.FILE_CHECK_INTERVAL, "1 s");
2269            configuration.add(SymbolConstants.FILE_CHECK_UPDATE_TIMEOUT, "50 ms");
2270    
2271            // This should be overridden for particular applications. These are the
2272            // locales for which we have (at least some) localized messages.
2273            configuration.add(SymbolConstants.SUPPORTED_LOCALES,
2274                    "en,it,es,zh_CN,pt_PT,de,ru,hr,fi_FI,sv_SE,fr_FR,da,pt_BR,ja,el,bg,no_NB,sr_RS,mk_MK");
2275    
2276            configuration.add(SymbolConstants.TAPESTRY_VERSION,
2277                    VersionUtils.readVersionNumber("META-INF/gradle/org.apache.tapestry/tapestry-core/project.properties"));
2278    
2279            configuration.add(SymbolConstants.COOKIE_MAX_AGE, "7 d");
2280    
2281            configuration.add(SymbolConstants.START_PAGE_NAME, "start");
2282    
2283            configuration.add(SymbolConstants.DEFAULT_STYLESHEET, "classpath:/org/apache/tapestry5/default.css");
2284            configuration.add("tapestry.spacer-image", "classpath:/org/apache/tapestry5/spacer.gif");
2285    
2286            configuration.add(SymbolConstants.SUPPRESS_REDIRECT_FROM_ACTION_REQUESTS, false);
2287    
2288            configuration.add(SymbolConstants.PRODUCTION_MODE, true);
2289    
2290            configuration.add(SymbolConstants.CLUSTERED_SESSIONS, true);
2291    
2292            configuration.add(SymbolConstants.ASSET_PATH_PREFIX, "/assets/");
2293    
2294            configuration.add(SymbolConstants.COMPRESS_WHITESPACE, true);
2295    
2296            configuration.add(MetaDataConstants.SECURE_PAGE, false);
2297    
2298            configuration.add(SymbolConstants.FORM_CLIENT_LOGIC_ENABLED, true);
2299    
2300            // This is designed to make it easy to keep synchronized with
2301            // script.aculo.ous. As we support a new version, we create a new folder, and update the
2302            // path entry. We can then delete the old version folder (or keep it around). This should
2303            // be more manageable than overwriting the local copy with updates (it's too easy for
2304            // files deleted between scriptaculous releases to be accidentally left lying around).
2305            // There's also a ClasspathAliasManager contribution based on the path.
2306    
2307            configuration.add(SymbolConstants.SCRIPTACULOUS, "classpath:${tapestry.scriptaculous.path}");
2308            configuration.add("tapestry.scriptaculous.path", "org/apache/tapestry5/scriptaculous_1_9_0");
2309    
2310            // Likewise for WebFX DatePicker, currently version 1.0.6
2311    
2312            configuration.add("tapestry.datepicker.path", "org/apache/tapestry5/datepicker_106");
2313            configuration.add(SymbolConstants.DATEPICKER, "classpath:${tapestry.datepicker.path}");
2314    
2315            configuration.add("tapestry.underscore", "classpath:org/apache/tapestry5/underscore_1_3_3.js");
2316    
2317            configuration.add(SymbolConstants.BLACKBIRD, "");
2318    
2319            configuration.add(SymbolConstants.PERSISTENCE_STRATEGY, PersistenceConstants.SESSION);
2320    
2321            configuration.add(MetaDataConstants.RESPONSE_CONTENT_TYPE, "text/html");
2322    
2323            configuration.add(SymbolConstants.CHARSET, "UTF-8");
2324    
2325            configuration.add(SymbolConstants.APPLICATION_CATALOG,
2326                    String.format("context:WEB-INF/${%s}.properties", InternalSymbols.APP_NAME));
2327    
2328            configuration.add(SymbolConstants.EXCEPTION_REPORT_PAGE, "ExceptionReport");
2329    
2330            configuration.add(SymbolConstants.MIN_GZIP_SIZE, 100);
2331    
2332            Random random = new Random(System.currentTimeMillis());
2333    
2334            configuration.add(SymbolConstants.APPLICATION_VERSION, Long.toHexString(random.nextLong()));
2335    
2336            configuration.add(SymbolConstants.OMIT_GENERATOR_META, false);
2337    
2338            configuration.add(SymbolConstants.SECURE_ENABLED, SymbolConstants.PRODUCTION_MODE_VALUE);
2339            configuration.add(SymbolConstants.COMPACT_JSON, SymbolConstants.PRODUCTION_MODE_VALUE);
2340    
2341            configuration.add(SymbolConstants.ENCODE_LOCALE_INTO_PATH, true);
2342    
2343            configuration.add(SymbolConstants.BLACKBIRD_ENABLED, false);
2344    
2345            configuration.add(InternalSymbols.PRE_SELECTED_FORM_NAMES, "reset,submit,select,id,method,action,onsubmit," + InternalConstants.CANCEL_NAME);
2346    
2347            configuration.add(SymbolConstants.COMPONENT_RENDER_TRACING_ENABLED, false);
2348    
2349            // The default values denote "use values from request"
2350            configuration.add(SymbolConstants.HOSTNAME, "");
2351            configuration.add(SymbolConstants.HOSTPORT, 0);
2352            configuration.add(SymbolConstants.HOSTPORT_SECURE, 0);
2353    
2354            configuration.add(SymbolConstants.UNKNOWN_COMPONENT_ID_CHECK_ENABLED, true);
2355    
2356            configuration.add(SymbolConstants.APPLICATION_FOLDER, "");
2357    
2358            // Grid component parameters defaults
2359            configuration.add(ComponentParameterConstants.GRID_ROWS_PER_PAGE, GridConstants.ROWS_PER_PAGE);
2360            configuration.add(ComponentParameterConstants.GRID_PAGER_POSITION, GridConstants.PAGER_POSITION);
2361            configuration.add(ComponentParameterConstants.GRID_EMPTY_BLOCK, GridConstants.EMPTY_BLOCK);
2362            configuration.add(ComponentParameterConstants.GRID_TABLE_CSS_CLASS, GridConstants.TABLE_CLASS);
2363            configuration.add(ComponentParameterConstants.GRIDPAGER_PAGE_RANGE, GridConstants.PAGER_PAGE_RANGE);
2364            configuration.add(ComponentParameterConstants.GRIDCOLUMNS_SORTABLE_ASSET, GridConstants.COLUMNS_SORTABLE);
2365            configuration.add(ComponentParameterConstants.GRIDCOLUMNS_ASCENDING_ASSET, GridConstants.COLUMNS_ASCENDING);
2366            configuration.add(ComponentParameterConstants.GRIDCOLUMNS_DESCENDING_ASSET, GridConstants.COLUMNS_DESCENDING);
2367    
2368            // FormInjector component parameters defaults
2369            configuration.add(ComponentParameterConstants.FORMINJECTOR_INSERT_POSITION, "above");
2370            configuration.add(ComponentParameterConstants.FORMINJECTOR_SHOW_FUNCTION, "highlight");
2371    
2372            // Palette component parameters defaults
2373            configuration.add(ComponentParameterConstants.PALETTE_ROWS_SIZE, 10);
2374    
2375            // Zone component parameters defaults
2376            configuration.add(ComponentParameterConstants.ZONE_SHOW_METHOD, "show");
2377            configuration.add(ComponentParameterConstants.ZONE_UPDATE_METHOD, "highlight");
2378    
2379            // By default, no page is on the whitelist unless it has the @WhitelistAccessOnly annotation
2380            configuration.add(MetaDataConstants.WHITELIST_ONLY_PAGE, false);
2381        }
2382    
2383        /**
2384         * Adds a listener to the {@link org.apache.tapestry5.internal.services.ComponentInstantiatorSource} that clears the
2385         * {@link PropertyAccess} and {@link TypeCoercer} caches on
2386         * a class loader invalidation. In addition, forces the
2387         * realization of {@link ComponentClassResolver} at startup.
2388         */
2389        public void contributeApplicationInitializer(OrderedConfiguration<ApplicationInitializerFilter> configuration,
2390                                                     final TypeCoercer typeCoercer, final ComponentClassResolver componentClassResolver, @ComponentClasses
2391        final InvalidationEventHub invalidationEventHub, final @Autobuild
2392        RestoreDirtySessionObjects restoreDirtySessionObjects)
2393        {
2394            final InvalidationListener listener = new InvalidationListener()
2395            {
2396                public void objectWasInvalidated()
2397                {
2398                    propertyAccess.clearCache();
2399    
2400                    typeCoercer.clearCache();
2401                }
2402            };
2403    
2404            ApplicationInitializerFilter clearCaches = new ApplicationInitializerFilter()
2405            {
2406                public void initializeApplication(Context context, ApplicationInitializer initializer)
2407                {
2408                    // Snuck in here is the logic to clear the PropertyAccess
2409                    // service's cache whenever
2410                    // the component class loader is invalidated.
2411    
2412                    invalidationEventHub.addInvalidationListener(listener);
2413    
2414                    endOfRequestEventHub.addEndOfRequestListener(restoreDirtySessionObjects);
2415    
2416                    // Perform other pending initialization
2417    
2418                    initializer.initializeApplication(context);
2419    
2420                    // We don't care about the result, but this forces a load of the
2421                    // service
2422                    // at application startup, rather than on first request.
2423    
2424                    componentClassResolver.isPageName("ForceLoadAtStartup");
2425                }
2426            };
2427    
2428            configuration.add("ClearCachesOnInvalidation", clearCaches);
2429        }
2430    
2431        /**
2432         * Contributes filters:
2433         * <dl>
2434         * <dt>Ajax</dt>
2435         * <dd>Determines if the request is Ajax oriented, and redirects to an alternative handler if so</dd>
2436         * <dt>ImmediateRender</dt>
2437         * <dd>When {@linkplain SymbolConstants#SUPPRESS_REDIRECT_FROM_ACTION_REQUESTS
2438         * immediate action response rendering} is enabled, generates the markup response (instead of a page redirect
2439         * response, which is the normal behavior)</dd>
2440         * <dt>Secure</dt>
2441         * <dd>Sends a redirect if an non-secure request accesses a secure page</dd>
2442         * </dl>
2443         */
2444        public void contributeComponentEventRequestHandler(OrderedConfiguration<ComponentEventRequestFilter> configuration,
2445                                                           final RequestSecurityManager requestSecurityManager, @Ajax
2446        ComponentEventRequestHandler ajaxHandler)
2447        {
2448            ComponentEventRequestFilter secureFilter = new ComponentEventRequestFilter()
2449            {
2450                public void handle(ComponentEventRequestParameters parameters, ComponentEventRequestHandler handler)
2451                        throws IOException
2452                {
2453                    if (requestSecurityManager.checkForInsecureComponentEventRequest(parameters))
2454                        return;
2455    
2456                    handler.handle(parameters);
2457                }
2458            };
2459    
2460            configuration.add("Ajax", new AjaxFilter(request, ajaxHandler));
2461    
2462            configuration.addInstance("ImmediateRender", ImmediateActionRenderResponseFilter.class);
2463    
2464            configuration.add("Secure", secureFilter, "before:Ajax");
2465        }
2466    
2467        /**
2468         * Contributes:
2469         * <dl>
2470         * <dt>AjaxFormUpdate</dt>
2471         * <dd>{@link AjaxFormUpdateFilter}</dd>
2472         * </dl>
2473         *
2474         * @since 5.2.0
2475         */
2476        public static void contributeAjaxComponentEventRequestHandler(
2477                OrderedConfiguration<ComponentEventRequestFilter> configuration)
2478        {
2479            configuration.addInstance("AjaxFormUpdate", AjaxFormUpdateFilter.class);
2480        }
2481    
2482        /**
2483         * Contributes strategies accessible via the {@link NullFieldStrategySource} service.
2484         * <p/>
2485         * <dl>
2486         * <dt>default</dt>
2487         * <dd>Does nothing, nulls stay null.</dd>
2488         * <dt>zero</dt>
2489         * <dd>Null values are converted to zero.</dd>
2490         * </dl>
2491         */
2492        public static void contributeNullFieldStrategySource(MappedConfiguration<String, NullFieldStrategy> configuration)
2493        {
2494            configuration.add("default", new DefaultNullFieldStrategy());
2495            configuration.add("zero", new ZeroNullFieldStrategy());
2496        }
2497    
2498        /**
2499         * Determines positioning of hidden fields relative to other elements (this
2500         * is needed by {@link org.apache.tapestry5.corelib.components.FormFragment} and others.
2501         * <p/>
2502         * For elements input, select, textarea and label the hidden field is positioned after.
2503         * <p/>
2504         * For elements p, div, li and td, the hidden field is positioned inside.
2505         */
2506        public static void contributeHiddenFieldLocationRules(
2507                MappedConfiguration<String, RelativeElementPosition> configuration)
2508        {
2509            configuration.add("input", RelativeElementPosition.AFTER);
2510            configuration.add("select", RelativeElementPosition.AFTER);
2511            configuration.add("textarea", RelativeElementPosition.AFTER);
2512            configuration.add("label", RelativeElementPosition.AFTER);
2513    
2514            configuration.add("p", RelativeElementPosition.INSIDE);
2515            configuration.add("div", RelativeElementPosition.INSIDE);
2516            configuration.add("td", RelativeElementPosition.INSIDE);
2517            configuration.add("li", RelativeElementPosition.INSIDE);
2518        }
2519    
2520        /**
2521         * @since 5.1.0.0
2522         */
2523        public static LinkCreationHub buildLinkCreationHub(LinkSource source)
2524        {
2525            return source.getLinkCreationHub();
2526        }
2527    
2528        /**
2529         * Exposes the public portion of the internal {@link InternalComponentInvalidationEventHub} service.
2530         *
2531         * @since 5.1.0.0
2532         */
2533        @Marker(ComponentClasses.class)
2534        public static InvalidationEventHub buildComponentClassesInvalidationEventHub(
2535                InternalComponentInvalidationEventHub trueHub)
2536        {
2537            return trueHub;
2538        }
2539    
2540        /**
2541         * @since 5.1.0.0
2542         */
2543        @Marker(ComponentTemplates.class)
2544        public static InvalidationEventHub buildComponentTemplatesInvalidationEventHub(
2545                ComponentTemplateSource templateSource)
2546        {
2547            return templateSource.getInvalidationEventHub();
2548        }
2549    
2550        /**
2551         * @since 5.1.0.0
2552         */
2553        @Marker(ComponentMessages.class)
2554        public static InvalidationEventHub buildComponentMessagesInvalidationEventHub(ComponentMessagesSource messagesSource)
2555        {
2556            return messagesSource.getInvalidationEventHub();
2557        }
2558    
2559        @Scope(ScopeConstants.PERTHREAD)
2560        public Environment buildEnvironment(PerthreadManager perthreadManager)
2561        {
2562            EnvironmentImpl service = new EnvironmentImpl();
2563    
2564            perthreadManager.addThreadCleanupListener(service);
2565    
2566            return service;
2567        }
2568    
2569        /**
2570         * The master SessionPersistedObjectAnalyzer.
2571         *
2572         * @since 5.1.0.0
2573         */
2574        @Marker(Primary.class)
2575        public SessionPersistedObjectAnalyzer buildSessionPersistedObjectAnalyzer(
2576                Map<Class, SessionPersistedObjectAnalyzer> configuration)
2577        {
2578            return strategyBuilder.build(SessionPersistedObjectAnalyzer.class, configuration);
2579        }
2580    
2581        /**
2582         * Identifies String, Number and Boolean as immutable objects, a catch-all
2583         * handler for Object (that understands
2584         * the {@link org.apache.tapestry5.annotations.ImmutableSessionPersistedObject} annotation),
2585         * and a handler for {@link org.apache.tapestry5.OptimizedSessionPersistedObject}.
2586         *
2587         * @since 5.1.0.0
2588         */
2589        public static void contributeSessionPersistedObjectAnalyzer(
2590                MappedConfiguration<Class, SessionPersistedObjectAnalyzer> configuration)
2591        {
2592            configuration.add(Object.class, new DefaultSessionPersistedObjectAnalyzer());
2593    
2594            SessionPersistedObjectAnalyzer<Object> immutable = new SessionPersistedObjectAnalyzer<Object>()
2595            {
2596                public boolean checkAndResetDirtyState(Object sessionPersistedObject)
2597                {
2598                    return false;
2599                }
2600            };
2601    
2602            configuration.add(String.class, immutable);
2603            configuration.add(Number.class, immutable);
2604            configuration.add(Boolean.class, immutable);
2605    
2606            configuration.add(OptimizedSessionPersistedObject.class, new OptimizedSessionPersistedObjectAnalyzer());
2607        }
2608    
2609        /**
2610         * @since 5.1.1.0
2611         */
2612        @Marker(Primary.class)
2613        public StackTraceElementAnalyzer buildMasterStackTraceElementAnalyzer(List<StackTraceElementAnalyzer> configuration)
2614        {
2615            return chainBuilder.build(StackTraceElementAnalyzer.class, configuration);
2616        }
2617    
2618        /**
2619         * Contributes:
2620         * <dl>
2621         * <dt>Application</dt>
2622         * <dd>Checks for classes in the application package</dd>
2623         * <dt>Proxies</dt>
2624         * <dd>Checks for classes that appear to be generated proxies.</dd>
2625         * <dt>SunReflect</dt>
2626         * <dd>Checks for <code>sun.reflect</code> (which are omitted)
2627         * <dt>TapestryAOP</dt>
2628         * <dd>Omits stack frames for classes related to Tapestry AOP (such as advice, etc.)</dd>
2629         * <dt>OperationTracker</dt>
2630         * <dd>Omits stack frames related to {@link OperationTracker}</dd>
2631         * </dl>
2632         *
2633         * @since 5.1.0.0
2634         */
2635        public static void contributeMasterStackTraceElementAnalyzer(
2636                OrderedConfiguration<StackTraceElementAnalyzer> configuration)
2637        {
2638            configuration.addInstance("Application", ApplicationStackTraceElementAnalyzer.class);
2639            configuration.add("Proxies", new ProxiesStackTraceElementAnalyzer(), "before:Application");
2640            configuration.add("Synthetic", new SyntheticStackTraceElementAnalyzer(), "before:Application");
2641            configuration.add("SunReflect", new PrefixCheckStackTraceElementAnalyzer(
2642                    StackTraceElementClassConstants.OMITTED, "sun.reflect."));
2643            configuration.addInstance("TapestryAOP", TapestryAOPStackFrameAnalyzer.class, "before:Application");
2644            configuration.add("OperationTracker", new RegexpStackTraceElementAnalyzer(Pattern.compile("internal\\.(RegistryImpl|PerThreadOperationTracker|OperationTrackerImpl)\\.invoke\\("), StackTraceElementClassConstants.OMITTED));
2645        }
2646    
2647        /**
2648         * Advises the {@link org.apache.tapestry5.services.messages.ComponentMessagesSource} service so
2649         * that the creation
2650         * of {@link org.apache.tapestry5.ioc.Messages} instances can be deferred.
2651         *
2652         * @since 5.1.0.0
2653         */
2654        @Match("ComponentMessagesSource")
2655        public static void adviseLazy(LazyAdvisor advisor, MethodAdviceReceiver receiver)
2656        {
2657            advisor.addLazyMethodInvocationAdvice(receiver);
2658        }
2659    
2660        /**
2661         * @since 5.1.0.0
2662         */
2663        public ComponentRequestHandler buildComponentRequestHandler(List<ComponentRequestFilter> configuration,
2664    
2665                                                                    @Autobuild
2666                                                                    ComponentRequestHandlerTerminator terminator,
2667    
2668                                                                    Logger logger)
2669        {
2670            return pipelineBuilder.build(logger, ComponentRequestHandler.class, ComponentRequestFilter.class,
2671                    configuration, terminator);
2672        }
2673    
2674        /**
2675         * Contributes:
2676         * <dl>
2677         * <dt>InitializeActivePageName
2678         * <dd>{@link InitializeActivePageName}
2679         * </dl>
2680         *
2681         * @since 5.2.0
2682         */
2683        public void contributeComponentRequestHandler(OrderedConfiguration<ComponentRequestFilter> configuration)
2684        {
2685            configuration.addInstance("InitializeActivePageName", InitializeActivePageName.class);
2686        }
2687    
2688        /**
2689         * Decorate FieldValidatorDefaultSource to setup the EnvironmentMessages
2690         * object and place it in the environment.
2691         * Although this could have been implemented directly in the default
2692         * implementation of the service, doing it
2693         * as service decoration ensures that the environment will be properly setup
2694         * even if a user overrides the default
2695         * service implementation.
2696         *
2697         * @param defaultSource The service to decorate
2698         * @param environment
2699         */
2700        public static FieldValidatorDefaultSource decorateFieldValidatorDefaultSource(
2701                final FieldValidatorDefaultSource defaultSource, final Environment environment)
2702        {
2703            return new FieldValidatorDefaultSource()
2704            {
2705    
2706                public FieldValidator createDefaultValidator(Field field, String overrideId, Messages overrideMessages,
2707                                                             Locale locale, Class propertyType, AnnotationProvider propertyAnnotations)
2708                {
2709                    environment.push(EnvironmentMessages.class, new EnvironmentMessages(overrideMessages, overrideId));
2710                    FieldValidator fieldValidator = defaultSource.createDefaultValidator(field, overrideId,
2711                            overrideMessages, locale, propertyType, propertyAnnotations);
2712                    environment.pop(EnvironmentMessages.class);
2713                    return fieldValidator;
2714                }
2715    
2716                public FieldValidator createDefaultValidator(ComponentResources resources, String parameterName)
2717                {
2718    
2719                    EnvironmentMessages em = new EnvironmentMessages(resources.getContainerMessages(), resources.getId());
2720                    environment.push(EnvironmentMessages.class, em);
2721                    FieldValidator fieldValidator = defaultSource.createDefaultValidator(resources, parameterName);
2722                    environment.pop(EnvironmentMessages.class);
2723                    return fieldValidator;
2724                }
2725            };
2726        }
2727    
2728        /**
2729         * Exposes the Environmental {@link Heartbeat} as an injectable service.
2730         *
2731         * @since 5.2.0
2732         */
2733        public Heartbeat buildHeartbeat()
2734        {
2735            return environmentalBuilder.build(Heartbeat.class);
2736        }
2737    
2738        /**
2739         * Contributes the "core" and "core-datefield" {@link JavaScriptStack}s
2740         *
2741         * @since 5.2.0
2742         */
2743        public static void contributeJavaScriptStackSource(MappedConfiguration<String, JavaScriptStack> configuration)
2744        {
2745            configuration.addInstance(InternalConstants.CORE_STACK_NAME, CoreJavaScriptStack.class);
2746            configuration.addInstance("core-datefield", DateFieldStack.class);
2747        }
2748    
2749        public static ComponentMessagesSource buildComponentMessagesSource(UpdateListenerHub updateListenerHub, @Autobuild
2750        ComponentMessagesSourceImpl service)
2751        {
2752            updateListenerHub.addUpdateListener(service);
2753    
2754            return service;
2755        }
2756    
2757        /**
2758         * Contributes:
2759         * <dl>
2760         * <dt>AppCatalog</dt>
2761         * <dd>The Resource defined by {@link SymbolConstants#APPLICATION_CATALOG}</dd>
2762         * <dt>ValidationMessages</dt>
2763         * <dd>Messages used by validators (before:AppCatalog)</dd>
2764         * <dt>
2765         *
2766         * @since 5.2.0
2767         */
2768        public static void contributeComponentMessagesSource(AssetSource assetSource,
2769                                                             @Symbol(SymbolConstants.APPLICATION_CATALOG)
2770                                                             Resource applicationCatalog, OrderedConfiguration<Resource> configuration)
2771        {
2772            configuration.add("ValidationMessages",
2773                    assetSource.resourceForPath("org/apache/tapestry5/internal/ValidationMessages.properties"),
2774                    "before:AppCatalog");
2775            configuration.add("AppCatalog", applicationCatalog);
2776        }
2777    
2778        /**
2779         * Contributes extractors for {@link Meta}, {@link Secure}, {@link ContentType} and {@link WhitelistAccessOnly} annotations.
2780         *
2781         * @since 5.2.0
2782         */
2783        @SuppressWarnings("unchecked")
2784        public static void contributeMetaWorker(MappedConfiguration<Class, MetaDataExtractor> configuration)
2785        {
2786            configuration.addInstance(Meta.class, MetaAnnotationExtractor.class);
2787            configuration.add(Secure.class, new FixedExtractor(MetaDataConstants.SECURE_PAGE));
2788            configuration.addInstance(ContentType.class, ContentTypeExtractor.class);
2789            configuration.add(WhitelistAccessOnly.class, new FixedExtractor(MetaDataConstants.WHITELIST_ONLY_PAGE));
2790        }
2791    
2792        /**
2793         * Builds the {@link ComponentTemplateLocator} as a chain of command.
2794         *
2795         * @since 5.2.0
2796         */
2797        @Marker(Primary.class)
2798        public ComponentTemplateLocator buildComponentTemplateLocator(List<ComponentTemplateLocator> configuration)
2799        {
2800            return chainBuilder.build(ComponentTemplateLocator.class, configuration);
2801        }
2802    
2803        /**
2804         * Contributes two template locators:
2805         * <dl>
2806         * <dt>Default</dt>
2807         * <dd>Searches for the template on the classpath ({@link DefaultTemplateLocator}</dd>
2808         * <dt>Page</dt>
2809         * <dd>Searches for <em>page</em> templates in the context ({@link PageTemplateLocator})</dd>
2810         * </dl>
2811         *
2812         * @since 5.2.0
2813         */
2814        public static void contributeComponentTemplateLocator(OrderedConfiguration<ComponentTemplateLocator> configuration,
2815                                                              @ContextProvider
2816                                                              AssetFactory contextAssetFactory,
2817                                                              @Symbol(SymbolConstants.APPLICATION_FOLDER) String applicationFolder,
2818                                                              ComponentClassResolver componentClassResolver)
2819        {
2820            configuration.add("Default", new DefaultTemplateLocator());
2821            configuration
2822                    .add("Page", new PageTemplateLocator(contextAssetFactory.getRootResource(), componentClassResolver, applicationFolder));
2823    
2824        }
2825    
2826        /**
2827         * Builds {@link ComponentEventLinkTransformer} service as a chain of command.
2828         *
2829         * @since 5.2.0
2830         */
2831        @Marker(Primary.class)
2832        public ComponentEventLinkTransformer buildComponentEventLinkTransformer(
2833                List<ComponentEventLinkTransformer> configuration)
2834        {
2835            return chainBuilder.build(ComponentEventLinkTransformer.class, configuration);
2836        }
2837    
2838        /**
2839         * Builds {@link PageRenderLinkTransformer} service as a chain of command.
2840         *
2841         * @since 5.2.0
2842         */
2843        @Marker(Primary.class)
2844        public PageRenderLinkTransformer buildPageRenderLinkTransformer(List<PageRenderLinkTransformer> configuration)
2845        {
2846            return chainBuilder.build(PageRenderLinkTransformer.class, configuration);
2847        }
2848    
2849        /**
2850         * Provides the "LinkTransformer" interceptor for the {@link ComponentEventLinkEncoder} service.
2851         * Other decorations
2852         * should come after LinkTransformer.
2853         *
2854         * @since 5.2.0
2855         */
2856        @Match("ComponentEventLinkEncoder")
2857        public ComponentEventLinkEncoder decorateLinkTransformer(LinkTransformer linkTransformer,
2858                                                                 ComponentEventLinkEncoder delegate)
2859        {
2860            return new LinkTransformerInterceptor(linkTransformer, delegate);
2861        }
2862    
2863        /**
2864         * In production mode, override {@link UpdateListenerHub} to be an empty placeholder.
2865         */
2866        @Contribute(ServiceOverride.class)
2867        public static void productionModeOverrides(MappedConfiguration<Class, Object> configuration,
2868                                                   @Symbol(SymbolConstants.PRODUCTION_MODE)
2869                                                   boolean productionMode)
2870        {
2871            if (productionMode)
2872            {
2873                configuration.add(UpdateListenerHub.class, new UpdateListenerHub()
2874                {
2875                    public void fireCheckForUpdates()
2876                    {
2877                    }
2878    
2879                    public void addUpdateListener(UpdateListener listener)
2880                    {
2881    
2882                    }
2883                });
2884            }
2885        }
2886    
2887        /**
2888         * Contributes a single default analyzer:
2889         * <dl>
2890         * <dt>LocalhostOnly</dt>
2891         * <dd>Identifies requests from localhost as on client whitelist</dd>
2892         * </dl>
2893         *
2894         * @since 5.3
2895         */
2896        @Contribute(ClientWhitelist.class)
2897        public static void defaultWhitelist(OrderedConfiguration<WhitelistAnalyzer> configuration)
2898        {
2899            configuration.add("LocalhostOnly", new LocalhostOnly());
2900        }
2901    }