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
015package org.apache.tapestry5.services;
016
017import org.apache.tapestry5.*;
018import org.apache.tapestry5.ajax.MultiZoneUpdate;
019import org.apache.tapestry5.alerts.AlertManager;
020import org.apache.tapestry5.annotations.*;
021import org.apache.tapestry5.annotations.ContentType;
022import org.apache.tapestry5.beaneditor.DataTypeConstants;
023import org.apache.tapestry5.beaneditor.Validate;
024import org.apache.tapestry5.corelib.ClientValidation;
025import org.apache.tapestry5.grid.GridConstants;
026import org.apache.tapestry5.grid.GridDataSource;
027import org.apache.tapestry5.internal.*;
028import org.apache.tapestry5.internal.alerts.AlertManagerImpl;
029import org.apache.tapestry5.internal.beaneditor.EnvironmentMessages;
030import org.apache.tapestry5.internal.beaneditor.MessagesConstraintGenerator;
031import org.apache.tapestry5.internal.beaneditor.PrimitiveFieldConstraintGenerator;
032import org.apache.tapestry5.internal.beaneditor.ValidateAnnotationConstraintGenerator;
033import org.apache.tapestry5.internal.bindings.*;
034import org.apache.tapestry5.internal.dynamic.DynamicTemplateParserImpl;
035import org.apache.tapestry5.internal.grid.CollectionGridDataSource;
036import org.apache.tapestry5.internal.grid.NullDataSource;
037import org.apache.tapestry5.internal.gzip.GZipFilter;
038import org.apache.tapestry5.internal.renderers.*;
039import org.apache.tapestry5.internal.services.*;
040import org.apache.tapestry5.internal.services.ajax.AjaxFormUpdateFilter;
041import org.apache.tapestry5.internal.services.ajax.AjaxResponseRendererImpl;
042import org.apache.tapestry5.internal.services.ajax.JavaScriptSupportImpl;
043import org.apache.tapestry5.internal.services.ajax.MultiZoneUpdateEventResultProcessor;
044import org.apache.tapestry5.internal.services.assets.AssetPathConstructorImpl;
045import org.apache.tapestry5.internal.services.assets.ClasspathAssetRequestHandler;
046import org.apache.tapestry5.internal.services.assets.ContextAssetRequestHandler;
047import org.apache.tapestry5.internal.services.assets.StackAssetRequestHandler;
048import org.apache.tapestry5.internal.services.javascript.CoreJavaScriptStack;
049import org.apache.tapestry5.internal.services.javascript.DateFieldStack;
050import org.apache.tapestry5.internal.services.javascript.JavaScriptStackPathConstructor;
051import org.apache.tapestry5.internal.services.javascript.JavaScriptStackSourceImpl;
052import org.apache.tapestry5.internal.services.linktransform.LinkTransformerImpl;
053import org.apache.tapestry5.internal.services.linktransform.LinkTransformerInterceptor;
054import org.apache.tapestry5.internal.services.messages.PropertiesFileParserImpl;
055import org.apache.tapestry5.internal.services.meta.ContentTypeExtractor;
056import org.apache.tapestry5.internal.services.meta.MetaAnnotationExtractor;
057import org.apache.tapestry5.internal.services.meta.MetaWorkerImpl;
058import org.apache.tapestry5.internal.services.security.ClientWhitelistImpl;
059import org.apache.tapestry5.internal.services.security.LocalhostOnly;
060import org.apache.tapestry5.internal.services.templates.DefaultTemplateLocator;
061import org.apache.tapestry5.internal.services.templates.PageTemplateLocator;
062import org.apache.tapestry5.internal.transform.*;
063import org.apache.tapestry5.internal.translator.NumericTranslator;
064import org.apache.tapestry5.internal.translator.NumericTranslatorSupport;
065import org.apache.tapestry5.internal.translator.StringTranslator;
066import org.apache.tapestry5.internal.util.RenderableAsBlock;
067import org.apache.tapestry5.internal.util.StringRenderable;
068import org.apache.tapestry5.internal.validator.ValidatorMacroImpl;
069import org.apache.tapestry5.ioc.*;
070import org.apache.tapestry5.ioc.annotations.*;
071import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
072import org.apache.tapestry5.ioc.services.*;
073import org.apache.tapestry5.ioc.util.AvailableValues;
074import org.apache.tapestry5.ioc.util.IdAllocator;
075import org.apache.tapestry5.ioc.util.StrategyRegistry;
076import org.apache.tapestry5.json.JSONArray;
077import org.apache.tapestry5.json.JSONObject;
078import org.apache.tapestry5.plastic.MethodDescription;
079import org.apache.tapestry5.runtime.Component;
080import org.apache.tapestry5.runtime.ComponentResourcesAware;
081import org.apache.tapestry5.runtime.RenderCommand;
082import org.apache.tapestry5.runtime.RenderQueue;
083import org.apache.tapestry5.services.ajax.AjaxResponseRenderer;
084import org.apache.tapestry5.services.assets.AssetPathConstructor;
085import org.apache.tapestry5.services.assets.AssetRequestHandler;
086import org.apache.tapestry5.services.assets.AssetsModule;
087import org.apache.tapestry5.services.dynamic.DynamicTemplate;
088import org.apache.tapestry5.services.dynamic.DynamicTemplateParser;
089import org.apache.tapestry5.services.javascript.JavaScriptStack;
090import org.apache.tapestry5.services.javascript.JavaScriptStackSource;
091import org.apache.tapestry5.services.javascript.JavaScriptSupport;
092import org.apache.tapestry5.services.javascript.StylesheetLink;
093import org.apache.tapestry5.services.linktransform.ComponentEventLinkTransformer;
094import org.apache.tapestry5.services.linktransform.LinkTransformer;
095import org.apache.tapestry5.services.linktransform.PageRenderLinkTransformer;
096import org.apache.tapestry5.services.messages.ComponentMessagesSource;
097import org.apache.tapestry5.services.messages.PropertiesFileParser;
098import org.apache.tapestry5.services.meta.FixedExtractor;
099import org.apache.tapestry5.services.meta.MetaDataExtractor;
100import org.apache.tapestry5.services.meta.MetaWorker;
101import org.apache.tapestry5.services.pageload.PageLoadModule;
102import org.apache.tapestry5.services.security.ClientWhitelist;
103import org.apache.tapestry5.services.security.WhitelistAnalyzer;
104import org.apache.tapestry5.services.templates.ComponentTemplateLocator;
105import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
106import org.apache.tapestry5.services.transform.InjectionProvider2;
107import org.apache.tapestry5.util.StringToEnumCoercion;
108import org.apache.tapestry5.validator.*;
109import org.slf4j.Logger;
110
111import javax.servlet.ServletContext;
112import javax.servlet.http.HttpServletRequest;
113import javax.servlet.http.HttpServletResponse;
114import java.io.IOException;
115import java.lang.annotation.Annotation;
116import java.math.BigDecimal;
117import java.math.BigInteger;
118import java.net.URL;
119import java.text.DateFormat;
120import java.text.SimpleDateFormat;
121import java.util.*;
122import 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})
130public 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
1238     *         contributions of special factories for some constants, each
1239     *         contributed factory may return a
1240     *         binding if applicable, or null otherwise
1241     */
1242    public BindingFactory buildPropBindingFactory(List<BindingFactory> configuration, @Autobuild
1243    PropBindingFactory service)
1244    {
1245        configuration.add(service);
1246
1247        return chainBuilder.build(BindingFactory.class, configuration);
1248    }
1249
1250    public static MetaDataLocator buildMetaDataLocator(@Autobuild
1251                                                       MetaDataLocatorImpl service, @ComponentClasses
1252    InvalidationEventHub hub)
1253    {
1254        hub.addInvalidationListener(service);
1255
1256        return service;
1257    }
1258
1259    public PersistentFieldStrategy buildClientPersistentFieldStrategy(LinkCreationHub linkCreationHub, @Autobuild
1260    ClientPersistentFieldStrategy service)
1261    {
1262        linkCreationHub.addListener(service);
1263
1264        return service;
1265    }
1266
1267    /**
1268     * Builds a proxy to the current {@link org.apache.tapestry5.RenderSupport} inside this thread's
1269     * {@link org.apache.tapestry5.services.Environment}.
1270     */
1271    public RenderSupport buildRenderSupport()
1272    {
1273        return environmentalBuilder.build(RenderSupport.class);
1274    }
1275
1276    /**
1277     * Builds a proxy to the current {@link JavaScriptSupport} inside this thread's {@link Environment}.
1278     *
1279     * @since 5.2.0
1280     */
1281    public JavaScriptSupport buildJavaScriptSupport()
1282    {
1283        return environmentalBuilder.build(JavaScriptSupport.class);
1284    }
1285
1286    /**
1287     * Builds a proxy to the current {@link org.apache.tapestry5.services.ClientBehaviorSupport} inside this
1288     * thread's {@link org.apache.tapestry5.services.Environment}.
1289     *
1290     * @since 5.1.0.1
1291     */
1292
1293    public ClientBehaviorSupport buildClientBehaviorSupport()
1294    {
1295        return environmentalBuilder.build(ClientBehaviorSupport.class);
1296    }
1297
1298    /**
1299     * Builds a proxy to the current {@link org.apache.tapestry5.services.FormSupport} inside this
1300     * thread's {@link org.apache.tapestry5.services.Environment}.
1301     */
1302    public FormSupport buildFormSupport()
1303    {
1304        return environmentalBuilder.build(FormSupport.class);
1305    }
1306
1307    /**
1308     * Allows the exact steps in the component class transformation process to
1309     * be defined.
1310     */
1311    @Marker(Primary.class)
1312    public ComponentClassTransformWorker2 buildComponentClassTransformWorker(
1313            List<ComponentClassTransformWorker2> configuration)
1314
1315    {
1316        return chainBuilder.build(ComponentClassTransformWorker2.class, configuration);
1317    }
1318
1319    /**
1320     * Analyzes properties to determine the data types, used to
1321     * {@linkplain #provideDefaultBeanBlocks(org.apache.tapestry5.ioc.Configuration)} locale
1322     * display and edit blocks for properties. The default behaviors
1323     * look for a {@link org.apache.tapestry5.beaneditor.DataType} annotation
1324     * before deriving the data type from the property type.
1325     */
1326    @Marker(Primary.class)
1327    public DataTypeAnalyzer buildDataTypeAnalyzer(List<DataTypeAnalyzer> configuration)
1328    {
1329        return chainBuilder.build(DataTypeAnalyzer.class, configuration);
1330    }
1331
1332    /**
1333     * A chain of command for providing values for {@link Inject}-ed fields in
1334     * component classes. The service's
1335     * configuration can be extended to allow for different automatic injections
1336     * (based on some combination of field
1337     * type and field name).
1338     * <p/>
1339     * Note that contributions to this service may be old-style {@link InjectionProvider}, which will
1340     * be coerced to {@link InjectionProvider2}.
1341     */
1342    public InjectionProvider2 buildInjectionProvider(List<InjectionProvider2> configuration)
1343    {
1344        return chainBuilder.build(InjectionProvider2.class, configuration);
1345    }
1346
1347    /**
1348     * Initializes the application, using a pipeline of {@link org.apache.tapestry5.services.ApplicationInitializer}s.
1349     */
1350    @Marker(Primary.class)
1351    public ApplicationInitializer buildApplicationInitializer(Logger logger,
1352                                                              List<ApplicationInitializerFilter> configuration)
1353    {
1354        ApplicationInitializer terminator = new ApplicationInitializerTerminator();
1355
1356        return pipelineBuilder.build(logger, ApplicationInitializer.class, ApplicationInitializerFilter.class,
1357                configuration, terminator);
1358    }
1359
1360    public HttpServletRequestHandler buildHttpServletRequestHandler(Logger logger,
1361
1362                                                                    List<HttpServletRequestFilter> configuration,
1363
1364                                                                    @Primary
1365                                                                    RequestHandler handler,
1366
1367                                                                    @Symbol(SymbolConstants.CHARSET)
1368                                                                    String applicationCharset,
1369
1370                                                                    TapestrySessionFactory sessionFactory)
1371    {
1372        HttpServletRequestHandler terminator = new HttpServletRequestHandlerTerminator(handler, applicationCharset,
1373                sessionFactory);
1374
1375        return pipelineBuilder.build(logger, HttpServletRequestHandler.class, HttpServletRequestFilter.class,
1376                configuration, terminator);
1377    }
1378
1379    @Marker(Primary.class)
1380    public RequestHandler buildRequestHandler(Logger logger, List<RequestFilter> configuration,
1381
1382                                              @Primary
1383                                              Dispatcher masterDispatcher)
1384    {
1385        RequestHandler terminator = new RequestHandlerTerminator(masterDispatcher);
1386
1387        return pipelineBuilder.build(logger, RequestHandler.class, RequestFilter.class, configuration, terminator);
1388    }
1389
1390    public ServletApplicationInitializer buildServletApplicationInitializer(Logger logger,
1391                                                                            List<ServletApplicationInitializerFilter> configuration,
1392
1393                                                                            @Primary
1394                                                                            ApplicationInitializer initializer)
1395    {
1396        ServletApplicationInitializer terminator = new ServletApplicationInitializerTerminator(initializer);
1397
1398        return pipelineBuilder.build(logger, ServletApplicationInitializer.class,
1399                ServletApplicationInitializerFilter.class, configuration, terminator);
1400    }
1401
1402    /**
1403     * The component event result processor used for normal component requests.
1404     */
1405    @Marker(
1406            {Primary.class, Traditional.class})
1407    public ComponentEventResultProcessor buildComponentEventResultProcessor(
1408            Map<Class, ComponentEventResultProcessor> configuration, @ComponentClasses
1409    InvalidationEventHub hub)
1410    {
1411        return constructComponentEventResultProcessor(configuration, hub);
1412    }
1413
1414    /**
1415     * The component event result processor used for Ajax-oriented component
1416     * requests.
1417     */
1418    @Marker(Ajax.class)
1419    public ComponentEventResultProcessor buildAjaxComponentEventResultProcessor(
1420            Map<Class, ComponentEventResultProcessor> configuration, @ComponentClasses
1421    InvalidationEventHub hub)
1422    {
1423        return constructComponentEventResultProcessor(configuration, hub);
1424    }
1425
1426    private ComponentEventResultProcessor constructComponentEventResultProcessor(
1427            Map<Class, ComponentEventResultProcessor> configuration, InvalidationEventHub hub)
1428    {
1429        Set<Class> handledTypes = CollectionFactory.newSet(configuration.keySet());
1430
1431        // A slight hack!
1432
1433        configuration.put(Object.class, new ObjectComponentEventResultProcessor(handledTypes));
1434
1435        final StrategyRegistry<ComponentEventResultProcessor> registry = StrategyRegistry.newInstance(
1436                ComponentEventResultProcessor.class, configuration);
1437
1438        //As the registry will cache component classes, we need to clear the cache when we reload components to avoid memory leaks in permgen
1439        hub.addInvalidationListener(new InvalidationListener()
1440        {
1441
1442            @Override
1443            public void objectWasInvalidated()
1444            {
1445                registry.clearCache();
1446            }
1447        });
1448
1449
1450        return strategyBuilder.build(registry);
1451    }
1452
1453    /**
1454     * The default data type analyzer is the final analyzer consulted and
1455     * identifies the type entirely pased on the
1456     * property type, working against its own configuration (mapping property
1457     * type class to data type).
1458     */
1459    public static DataTypeAnalyzer buildDefaultDataTypeAnalyzer(@Autobuild
1460                                                                DefaultDataTypeAnalyzer service, @ComponentClasses
1461    InvalidationEventHub hub)
1462    {
1463        hub.addInvalidationListener(service);
1464
1465        return service;
1466    }
1467
1468    public static TranslatorSource buildTranslatorSource(Map<Class, Translator> configuration,
1469                                                         TranslatorAlternatesSource alternatesSource, @ComponentClasses
1470    InvalidationEventHub hub)
1471    {
1472        TranslatorSourceImpl service = new TranslatorSourceImpl(configuration,
1473                alternatesSource.getTranslatorAlternates());
1474
1475        hub.addInvalidationListener(service);
1476
1477        return service;
1478    }
1479
1480    @Marker(Primary.class)
1481    public ObjectRenderer buildObjectRenderer(Map<Class, ObjectRenderer> configuration)
1482    {
1483        return strategyBuilder.build(ObjectRenderer.class, configuration);
1484    }
1485
1486    /**
1487     * Returns a {@link org.apache.tapestry5.ioc.services.ClassFactory} that can
1488     * be used to create extra classes around
1489     * component classes. This ClassFactory will be cleared whenever an
1490     * underlying component class is discovered to have
1491     * changed. Use of this class factory implies that your code will become
1492     * aware of this (if necessary) to discard any
1493     * cached object (alas, this currently involves dipping into the internals
1494     * side to register for the correct
1495     * notifications). Failure to properly clean up can result in really nasty
1496     * PermGen space memory leaks.
1497     */
1498    @Marker(ComponentLayer.class)
1499    public ClassFactory buildComponentClassFactory(ComponentInstantiatorSource source)
1500    {
1501        return shadowBuilder.build(source, "classFactory", ClassFactory.class);
1502    }
1503
1504    /**
1505     * Returns a {@link PlasticProxyFactory} that can be used to create extra classes around component classes. This
1506     * factory will be cleared whenever an underlying component class is discovered to have changed. Use of this
1507     * factory implies that your code will become aware of this (if necessary) to discard any cached object (alas,
1508     * this currently involves dipping into the internals side to register for the correct notifications). Failure to
1509     * properly clean up can result in really nasty PermGen space memory leaks.
1510     */
1511    @Marker(ComponentLayer.class)
1512    public PlasticProxyFactory buildComponentProxyFactory(ComponentInstantiatorSource source)
1513    {
1514        return shadowBuilder.build(source, "proxyFactory", PlasticProxyFactory.class);
1515    }
1516
1517    /**
1518     * Ordered contributions to the MasterDispatcher service allow different URL
1519     * matching strategies to occur.
1520     */
1521    @Marker(Primary.class)
1522    public Dispatcher buildMasterDispatcher(List<Dispatcher> configuration)
1523    {
1524        return chainBuilder.build(Dispatcher.class, configuration);
1525    }
1526
1527    /**
1528     * Builds a shadow of the RequestGlobals.request property. Note again that
1529     * the shadow can be an ordinary singleton,
1530     * even though RequestGlobals is perthread.
1531     */
1532    public Request buildRequest()
1533    {
1534        return shadowBuilder.build(requestGlobals, "request", Request.class);
1535    }
1536
1537    /**
1538     * Builds a shadow of the RequestGlobals.HTTPServletRequest property.
1539     * Generally, you should inject the {@link Request} service instead, as
1540     * future version of Tapestry may operate beyond just the servlet API.
1541     */
1542    public HttpServletRequest buildHttpServletRequest()
1543    {
1544        return shadowBuilder.build(requestGlobals, "HTTPServletRequest", HttpServletRequest.class);
1545    }
1546
1547    /**
1548     * @since 5.1.0.0
1549     */
1550    public HttpServletResponse buildHttpServletResponse()
1551    {
1552        return shadowBuilder.build(requestGlobals, "HTTPServletResponse", HttpServletResponse.class);
1553    }
1554
1555    /**
1556     * Builds a shadow of the RequestGlobals.response property. Note again that
1557     * the shadow can be an ordinary singleton,
1558     * even though RequestGlobals is perthread.
1559     */
1560    public Response buildResponse()
1561    {
1562        return shadowBuilder.build(requestGlobals, "response", Response.class);
1563    }
1564
1565    /**
1566     * The MarkupRenderer service is used to render a full page as markup.
1567     * Supports an ordered configuration of {@link org.apache.tapestry5.services.MarkupRendererFilter}s.
1568     */
1569    public MarkupRenderer buildMarkupRenderer(Logger logger, @Autobuild
1570    MarkupRendererTerminator terminator, List<MarkupRendererFilter> configuration)
1571    {
1572        return pipelineBuilder.build(logger, MarkupRenderer.class, MarkupRendererFilter.class, configuration,
1573                terminator);
1574    }
1575
1576    /**
1577     * A wrapper around {@link org.apache.tapestry5.internal.services.PageRenderQueue} used for
1578     * partial page renders.
1579     * Supports an ordered configuration of {@link org.apache.tapestry5.services.PartialMarkupRendererFilter}s.
1580     *
1581     * @see #contributePartialMarkupRenderer
1582     */
1583    public PartialMarkupRenderer buildPartialMarkupRenderer(Logger logger,
1584                                                            List<PartialMarkupRendererFilter> configuration, @Autobuild
1585    PartialMarkupRendererTerminator terminator)
1586    {
1587
1588        return pipelineBuilder.build(logger, PartialMarkupRenderer.class, PartialMarkupRendererFilter.class,
1589                configuration, terminator);
1590    }
1591
1592    public PageRenderRequestHandler buildPageRenderRequestHandler(List<PageRenderRequestFilter> configuration,
1593                                                                  Logger logger, @Autobuild
1594    PageRenderRequestHandlerImpl terminator)
1595    {
1596        return pipelineBuilder.build(logger, PageRenderRequestHandler.class, PageRenderRequestFilter.class,
1597                configuration, terminator);
1598    }
1599
1600    /**
1601     * Builds the component action request handler for traditional (non-Ajax)
1602     * requests. These typically result in a
1603     * redirect to a Tapestry render URL.
1604     */
1605    @Marker(
1606            {Traditional.class, Primary.class})
1607    public ComponentEventRequestHandler buildComponentEventRequestHandler(
1608            List<ComponentEventRequestFilter> configuration, Logger logger, @Autobuild
1609    ComponentEventRequestHandlerImpl terminator)
1610    {
1611        return pipelineBuilder.build(logger, ComponentEventRequestHandler.class, ComponentEventRequestFilter.class,
1612                configuration, terminator);
1613    }
1614
1615    /**
1616     * Builds the action request handler for Ajax requests, based on a
1617     * {@linkplain org.apache.tapestry5.ioc.services.PipelineBuilder
1618     * pipeline} around {@link org.apache.tapestry5.internal.services.AjaxComponentEventRequestHandler} . Filters on
1619     * the
1620     * request handler are supported here as well.
1621     */
1622    @Marker(
1623            {Ajax.class, Primary.class})
1624    public ComponentEventRequestHandler buildAjaxComponentEventRequestHandler(
1625            List<ComponentEventRequestFilter> configuration, Logger logger, @Autobuild
1626    AjaxComponentEventRequestHandler terminator)
1627    {
1628        return pipelineBuilder.build(logger, ComponentEventRequestHandler.class, ComponentEventRequestFilter.class,
1629                configuration, terminator);
1630    }
1631
1632    // ========================================================================
1633    //
1634    // Service Contribution Methods (instance)
1635    //
1636    // ========================================================================
1637
1638    /**
1639     * Contributes the default "session" strategy.
1640     */
1641    public void contributeApplicationStatePersistenceStrategySource(
1642            MappedConfiguration<String, ApplicationStatePersistenceStrategy> configuration,
1643
1644            @Local
1645            ApplicationStatePersistenceStrategy sessionStategy)
1646    {
1647        configuration.add("session", sessionStategy);
1648    }
1649
1650    public void contributeAssetSource(MappedConfiguration<String, AssetFactory> configuration, @ContextProvider
1651    AssetFactory contextAssetFactory,
1652
1653                                      @ClasspathProvider
1654                                      AssetFactory classpathAssetFactory)
1655    {
1656        configuration.add(AssetConstants.CONTEXT, contextAssetFactory);
1657        configuration.add(AssetConstants.CLASSPATH, classpathAssetFactory);
1658    }
1659
1660    /**
1661     * Contributes handlers for the following types:
1662     * <dl>
1663     * <dt>Object</dt>
1664     * <dd>Failure case, added to provide a more useful exception message</dd>
1665     * <dt>{@link Link}</dt>
1666     * <dd>Sends a redirect to the link (which is typically a page render link)</dd>
1667     * <dt>String</dt>
1668     * <dd>Sends a page render redirect</dd>
1669     * <dt>Class</dt>
1670     * <dd>Interpreted as the class name of a page, sends a page render render redirect (this is more refactoring safe
1671     * than the page name)</dd>
1672     * <dt>{@link Component}</dt>
1673     * <dd>A page's root component (though a non-root component will work, but will generate a warning). A direct to the
1674     * containing page is sent.</dd>
1675     * <dt>{@link org.apache.tapestry5.StreamResponse}</dt>
1676     * <dd>The stream response is sent as the actual reply.</dd>
1677     * <dt>URL</dt>
1678     * <dd>Sends a redirect to a (presumably) external URL</dd>
1679     * </dl>
1680     */
1681    public void contributeComponentEventResultProcessor(@Traditional
1682                                                        @ComponentInstanceProcessor
1683                                                        ComponentEventResultProcessor componentInstanceProcessor,
1684
1685                                                        MappedConfiguration<Class, ComponentEventResultProcessor> configuration)
1686    {
1687        configuration.add(Link.class, new ComponentEventResultProcessor<Link>()
1688        {
1689            public void processResultValue(Link value) throws IOException
1690            {
1691                response.sendRedirect(value);
1692            }
1693        });
1694
1695        configuration.add(URL.class, new ComponentEventResultProcessor<URL>()
1696        {
1697            public void processResultValue(URL value) throws IOException
1698            {
1699                response.sendRedirect(value.toExternalForm());
1700            }
1701        });
1702
1703        configuration.addInstance(HttpError.class, HttpErrorComponentEventResultProcessor.class);
1704
1705        configuration.addInstance(String.class, PageNameComponentEventResultProcessor.class);
1706
1707        configuration.addInstance(Class.class, ClassResultProcessor.class);
1708
1709        configuration.add(Component.class, componentInstanceProcessor);
1710
1711        configuration.addInstance(StreamResponse.class, StreamResponseResultProcessor.class);
1712
1713        configuration.addInstance(StreamPageContent.class, StreamPageContentResultProcessor.class);
1714    }
1715
1716    /**
1717     * Contributes handlers for the following types:
1718     * <dl>
1719     * <dt>Object</dt>
1720     * <dd>Failure case, added to provide more useful exception message</dd>
1721     * <dt>{@link RenderCommand}</dt>
1722     * <dd>Typically, a {@link org.apache.tapestry5.Block}</dd>
1723     * <dt>{@link org.apache.tapestry5.annotations.Component}</dt>
1724     * <dd>Renders the component and its body (unless its a page, in which case a redirect JSON response is sent)</dd>
1725     * <dt>{@link org.apache.tapestry5.json.JSONObject} or {@link org.apache.tapestry5.json.JSONArray}</dt>
1726     * <dd>The JSONObject is returned as a text/javascript response</dd>
1727     * <dt>{@link org.apache.tapestry5.StreamResponse}</dt>
1728     * <dd>The stream response is sent as the actual response</dd>
1729     * <dt>String</dt>
1730     * <dd>Interprets the value as a logical page name and sends a client response to redirect to that page</dd>
1731     * <dt>{@link org.apache.tapestry5.Link}</dt>
1732     * <dd>Sends a JSON response to redirect to the link</dd>
1733     * <dt>{@link Class}</dt>
1734     * <dd>Treats the class as a page class and sends a redirect for a page render for that page</dd>
1735     * <dt>{@link org.apache.tapestry5.ajax.MultiZoneUpdate}</dt>
1736     * <dd>Sends a single JSON response to update the content of multiple zones
1737     * </dl>
1738     * <p/>
1739     * In most cases, when you want to support a new type, you should convert it to one of the built-in supported types
1740     * (such as {@link RenderCommand}. You can then inject the master AjaxComponentEventResultProcessor (use the
1741     * {@link Ajax} marker annotation) and delegate to it.
1742     */
1743    @Contribute(ComponentEventResultProcessor.class)
1744    @Ajax
1745    public static void provideBaseAjaxComponentEventResultProcessors(
1746            MappedConfiguration<Class, ComponentEventResultProcessor> configuration)
1747    {
1748        configuration.addInstance(RenderCommand.class, RenderCommandComponentEventResultProcessor.class);
1749        configuration.addInstance(Component.class, AjaxComponentInstanceEventResultProcessor.class);
1750        configuration.addInstance(JSONObject.class, JSONObjectEventResultProcessor.class);
1751        configuration.addInstance(JSONArray.class, JSONArrayEventResultProcessor.class);
1752        configuration.addInstance(StreamResponse.class, StreamResponseResultProcessor.class);
1753        configuration.addInstance(String.class, AjaxPageNameComponentEventResultProcessor.class);
1754        configuration.addInstance(Link.class, AjaxLinkComponentEventResultProcessor.class);
1755        configuration.addInstance(Class.class, AjaxPageClassComponentEventResultProcessor.class);
1756        configuration.addInstance(MultiZoneUpdate.class, MultiZoneUpdateEventResultProcessor.class);
1757        configuration.addInstance(HttpError.class, HttpErrorComponentEventResultProcessor.class);
1758    }
1759
1760    /**
1761     * The MasterDispatcher is a chain-of-command of individual Dispatchers,
1762     * each handling (like a servlet) a particular
1763     * kind of incoming request.
1764     * <dl>
1765     * <dt>RootPath</dt>
1766     * <dd>Renders the start page for the "/" request (outdated)</dd>
1767     * <dt>Asset</dt>
1768     * <dd>Provides access to assets (context, classpath and virtual) via {@link AssetDispatcher}</dd>
1769     * <dt>PageRender</dt>
1770     * <dd>Identifies the {@link org.apache.tapestry5.services.PageRenderRequestParameters} and forwards onto
1771     * {@link PageRenderRequestHandler}</dd>
1772     * <dt>ComponentEvent</dt>
1773     * <dd>Identifies the {@link ComponentEventRequestParameters} and forwards onto the
1774     * {@link ComponentEventRequestHandler}</dd>
1775     * </dl>
1776     */
1777    public static void contributeMasterDispatcher(OrderedConfiguration<Dispatcher> configuration,
1778
1779                                                  @InjectService("AssetDispatcher")
1780                                                  Dispatcher assetDispatcher)
1781    {
1782        // Looks for the root path and renders the start page. This is
1783        // maintained for compatibility
1784        // with earlier versions of Tapestry 5, it is recommended that an Index
1785        // page be used instead.
1786
1787        configuration.addInstance("RootPath", RootPathDispatcher.class, "before:Asset");
1788
1789        // This goes first because an asset to be streamed may have an file
1790        // extension, such as
1791        // ".html", that will confuse the later dispatchers.
1792
1793        configuration.add("Asset", assetDispatcher, "before:ComponentEvent");
1794
1795        configuration.addInstance("ComponentEvent", ComponentEventDispatcher.class, "before:PageRender");
1796
1797        configuration.addInstance("PageRender", PageRenderDispatcher.class);
1798    }
1799
1800    /**
1801     * Contributes a default object renderer for type Object, plus specialized
1802     * renderers for {@link org.apache.tapestry5.services.Request}, {@link org.apache.tapestry5.ioc.Location},
1803     * {@link org.apache.tapestry5.ComponentResources}, {@link org.apache.tapestry5.EventContext},
1804     * {@link AvailableValues},
1805     * List, and Object[].
1806     */
1807    @SuppressWarnings("unchecked")
1808    public void contributeObjectRenderer(MappedConfiguration<Class, ObjectRenderer> configuration,
1809
1810                                         @InjectService("LocationRenderer")
1811                                         ObjectRenderer locationRenderer,
1812
1813                                         final TypeCoercer typeCoercer)
1814    {
1815        configuration.add(Object.class, new DefaultObjectRenderer());
1816
1817        configuration.addInstance(Request.class, RequestRenderer.class);
1818
1819        configuration.add(Location.class, locationRenderer);
1820
1821        ObjectRenderer preformatted = new ObjectRenderer<Object>()
1822        {
1823            public void render(Object object, MarkupWriter writer)
1824            {
1825                writer.element("pre");
1826                writer.write(typeCoercer.coerce(object, String.class));
1827                writer.end();
1828            }
1829        };
1830
1831        configuration.add(ClassTransformation.class, preformatted);
1832
1833        configuration.addInstance(List.class, ListRenderer.class);
1834        configuration.addInstance(Object[].class, ObjectArrayRenderer.class);
1835        configuration.addInstance(ComponentResources.class, ComponentResourcesRenderer.class);
1836        configuration.addInstance(EventContext.class, EventContextRenderer.class);
1837        configuration.add(AvailableValues.class, new AvailableValuesRenderer());
1838    }
1839
1840    /**
1841     * Adds page render filters, each of which provides an {@link org.apache.tapestry5.annotations.Environmental}
1842     * service. Filters
1843     * often provide {@link org.apache.tapestry5.annotations.Environmental} services needed by
1844     * components as they render.
1845     * <dl>
1846     * <dt>DocumentLinker</dt>
1847     * <dd>Provides {@link org.apache.tapestry5.internal.services.DocumentLinker}</dd>
1848     * <dt>JavascriptSupport</dt>
1849     * <dd>Provides {@link JavaScriptSupport}</dd>
1850     * <dt>RenderSupport</dt>
1851     * <dd>Provides {@link org.apache.tapestry5.RenderSupport}</dd>
1852     * <dt>InjectDefaultStylesheet</dt>
1853     * <dd>Injects the default stylesheet into all pages</dd></dt>
1854     * <dt>ClientBehaviorSupport</dt>
1855     * <dd>Provides {@link ClientBehaviorSupport}</dd>
1856     * <dt>Heartbeat</dt>
1857     * <dd>Provides {@link org.apache.tapestry5.services.Heartbeat}</dd>
1858     * <dt>ValidationDecorator</dt>
1859     * <dd>Provides {@link org.apache.tapestry5.ValidationDecorator} (via {@link ValidationDecoratorFactory#newInstance(org.apache.tapestry5.MarkupWriter)})</dd>
1860     * </dl>
1861     */
1862    public void contributeMarkupRenderer(OrderedConfiguration<MarkupRendererFilter> configuration,
1863
1864                                         @Symbol(SymbolConstants.OMIT_GENERATOR_META)
1865                                         final boolean omitGeneratorMeta,
1866
1867                                         @Symbol(SymbolConstants.TAPESTRY_VERSION)
1868                                         final String tapestryVersion,
1869
1870                                         @Symbol(SymbolConstants.COMPACT_JSON)
1871                                         final boolean compactJSON,
1872
1873                                         final SymbolSource symbolSource,
1874
1875                                         final AssetSource assetSource,
1876
1877                                         final JavaScriptStackSource javascriptStackSource,
1878
1879                                         final JavaScriptStackPathConstructor javascriptStackPathConstructor,
1880
1881                                         final ValidationDecoratorFactory validationDecoratorFactory,
1882
1883                                         @Path("${tapestry.default-stylesheet}")
1884                                         final Asset defaultStylesheet)
1885    {
1886        MarkupRendererFilter documentLinker = new MarkupRendererFilter()
1887        {
1888            public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1889            {
1890                DocumentLinkerImpl linker = new DocumentLinkerImpl(omitGeneratorMeta, tapestryVersion, compactJSON);
1891
1892                environment.push(DocumentLinker.class, linker);
1893
1894                renderer.renderMarkup(writer);
1895
1896                environment.pop(DocumentLinker.class);
1897
1898                linker.updateDocument(writer.getDocument());
1899            }
1900        };
1901
1902        MarkupRendererFilter javaScriptSupport = new MarkupRendererFilter()
1903        {
1904            public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1905            {
1906                DocumentLinker linker = environment.peekRequired(DocumentLinker.class);
1907
1908                JavaScriptSupportImpl support = new JavaScriptSupportImpl(linker, javascriptStackSource,
1909                        javascriptStackPathConstructor);
1910
1911                environment.push(JavaScriptSupport.class, support);
1912
1913                renderer.renderMarkup(writer);
1914
1915                environment.pop(JavaScriptSupport.class);
1916
1917                support.commit();
1918            }
1919        };
1920
1921        MarkupRendererFilter renderSupport = new MarkupRendererFilter()
1922        {
1923            public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1924            {
1925                JavaScriptSupport javascriptSupport = environment.peekRequired(JavaScriptSupport.class);
1926
1927                RenderSupportImpl support = new RenderSupportImpl(symbolSource, assetSource, javascriptSupport);
1928
1929                environment.push(RenderSupport.class, support);
1930
1931                renderer.renderMarkup(writer);
1932
1933                environment.pop(RenderSupport.class);
1934            }
1935        };
1936
1937        MarkupRendererFilter injectDefaultStylesheet = new MarkupRendererFilter()
1938        {
1939            public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1940            {
1941                DocumentLinker linker = environment.peekRequired(DocumentLinker.class);
1942
1943                linker.addStylesheetLink(new StylesheetLink(defaultStylesheet.toClientURL()));
1944
1945                renderer.renderMarkup(writer);
1946            }
1947        };
1948
1949        MarkupRendererFilter clientBehaviorSupport = new MarkupRendererFilter()
1950        {
1951            public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1952            {
1953                JavaScriptSupport javascriptSupport = environment.peekRequired(JavaScriptSupport.class);
1954
1955                ClientBehaviorSupportImpl clientBehaviorSupport = new ClientBehaviorSupportImpl(javascriptSupport,
1956                        environment);
1957
1958                environment.push(ClientBehaviorSupport.class, clientBehaviorSupport);
1959
1960                renderer.renderMarkup(writer);
1961
1962                environment.pop(ClientBehaviorSupport.class);
1963
1964                clientBehaviorSupport.commit();
1965            }
1966        };
1967
1968        MarkupRendererFilter heartbeat = new MarkupRendererFilter()
1969        {
1970            public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1971            {
1972                Heartbeat heartbeat = new HeartbeatImpl();
1973
1974                heartbeat.begin();
1975
1976                environment.push(Heartbeat.class, heartbeat);
1977
1978                renderer.renderMarkup(writer);
1979
1980                environment.pop(Heartbeat.class);
1981
1982                heartbeat.end();
1983            }
1984        };
1985
1986        MarkupRendererFilter defaultValidationDecorator = new MarkupRendererFilter()
1987        {
1988            public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1989            {
1990                ValidationDecorator decorator = validationDecoratorFactory.newInstance(writer);
1991
1992                environment.push(ValidationDecorator.class, decorator);
1993
1994                renderer.renderMarkup(writer);
1995
1996                environment.pop(ValidationDecorator.class);
1997            }
1998        };
1999
2000        configuration.add("DocumentLinker", documentLinker);
2001        configuration.add("JavaScriptSupport", javaScriptSupport);
2002        configuration.add("RenderSupport", renderSupport);
2003        configuration.add("InjectDefaultStylesheet", injectDefaultStylesheet);
2004        configuration.add("ClientBehaviorSupport", clientBehaviorSupport);
2005        configuration.add("Heartbeat", heartbeat);
2006        configuration.add("ValidationDecorator", defaultValidationDecorator);
2007    }
2008
2009    /**
2010     * Contributes {@link PartialMarkupRendererFilter}s used when rendering a
2011     * partial Ajax response.
2012     * <dl>
2013     * <dt>DocumentLinker
2014     * <dd>Provides {@link org.apache.tapestry5.internal.services.DocumentLinker}
2015     * <dt>JavaScriptSupport
2016     * <dd>Provides {@link JavaScriptSupport}</dd>
2017     * <dt>PageRenderSupport</dt>
2018     * <dd>Provides {@link org.apache.tapestry5.RenderSupport}</dd>
2019     * <dt>ClientBehaviorSupport</dt>
2020     * <dd>Provides {@link ClientBehaviorSupport}</dd>
2021     * <dt>Heartbeat</dt>
2022     * <dd>Provides {@link org.apache.tapestry5.services.Heartbeat}</dd>
2023     * <dt>DefaultValidationDecorator</dt>
2024     * <dt>ValidationDecorator</dt>
2025     * <dd>Provides {@link org.apache.tapestry5.ValidationDecorator} (via {@link ValidationDecoratorFactory#newInstance(org.apache.tapestry5.MarkupWriter)})</dd>
2026     * </dl>
2027     */
2028    public void contributePartialMarkupRenderer(OrderedConfiguration<PartialMarkupRendererFilter> configuration,
2029
2030                                                final ValidationDecoratorFactory validationDecoratorFactory,
2031
2032                                                final JavaScriptStackSource javascriptStackSource,
2033
2034                                                final JavaScriptStackPathConstructor javascriptStackPathConstructor,
2035
2036                                                final SymbolSource symbolSource,
2037
2038                                                final AssetSource assetSource)
2039    {
2040        PartialMarkupRendererFilter documentLinker = new PartialMarkupRendererFilter()
2041        {
2042            public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2043            {
2044                PartialMarkupDocumentLinker linker = new PartialMarkupDocumentLinker();
2045
2046                environment.push(DocumentLinker.class, linker);
2047
2048                renderer.renderMarkup(writer, reply);
2049
2050                environment.pop(DocumentLinker.class);
2051
2052                linker.commit(reply);
2053            }
2054        };
2055
2056        PartialMarkupRendererFilter javascriptSupport = new PartialMarkupRendererFilter()
2057        {
2058            public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2059            {
2060                String uid = Long.toHexString(System.nanoTime());
2061
2062                String namespace = "_" + uid;
2063
2064                IdAllocator idAllocator = new IdAllocator(namespace);
2065
2066                DocumentLinker linker = environment.peekRequired(DocumentLinker.class);
2067
2068                JavaScriptSupportImpl support = new JavaScriptSupportImpl(linker, javascriptStackSource,
2069                        javascriptStackPathConstructor, idAllocator, true);
2070
2071                environment.push(JavaScriptSupport.class, support);
2072
2073                renderer.renderMarkup(writer, reply);
2074
2075                environment.pop(JavaScriptSupport.class);
2076
2077                support.commit();
2078            }
2079        };
2080
2081        PartialMarkupRendererFilter renderSupport = new PartialMarkupRendererFilter()
2082        {
2083            public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2084            {
2085                JavaScriptSupport javascriptSupport = environment.peekRequired(JavaScriptSupport.class);
2086
2087                RenderSupportImpl support = new RenderSupportImpl(symbolSource, assetSource, javascriptSupport);
2088
2089                environment.push(RenderSupport.class, support);
2090
2091                renderer.renderMarkup(writer, reply);
2092
2093                environment.pop(RenderSupport.class);
2094            }
2095        };
2096
2097        PartialMarkupRendererFilter clientBehaviorSupport = new PartialMarkupRendererFilter()
2098        {
2099            public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2100            {
2101                JavaScriptSupport javascriptSupport = environment.peekRequired(JavaScriptSupport.class);
2102
2103                ClientBehaviorSupportImpl support = new ClientBehaviorSupportImpl(javascriptSupport, environment);
2104
2105                environment.push(ClientBehaviorSupport.class, support);
2106
2107                renderer.renderMarkup(writer, reply);
2108
2109                environment.pop(ClientBehaviorSupport.class);
2110
2111                support.commit();
2112            }
2113        };
2114
2115        PartialMarkupRendererFilter heartbeat = new PartialMarkupRendererFilter()
2116        {
2117            public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2118            {
2119                Heartbeat heartbeat = new HeartbeatImpl();
2120
2121                heartbeat.begin();
2122
2123                environment.push(Heartbeat.class, heartbeat);
2124
2125                renderer.renderMarkup(writer, reply);
2126
2127                environment.pop(Heartbeat.class);
2128
2129                heartbeat.end();
2130            }
2131        };
2132
2133        PartialMarkupRendererFilter defaultValidationDecorator = new PartialMarkupRendererFilter()
2134        {
2135            public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2136            {
2137                ValidationDecorator decorator = validationDecoratorFactory.newInstance(writer);
2138
2139                environment.push(ValidationDecorator.class, decorator);
2140
2141                renderer.renderMarkup(writer, reply);
2142
2143                environment.pop(ValidationDecorator.class);
2144            }
2145        };
2146
2147        configuration.add("DocumentLinker", documentLinker);
2148        configuration.add("JavaScriptSupport", javascriptSupport);
2149        configuration.add("RenderSupport", renderSupport);
2150        configuration.add("ClientBehaviorSupport", clientBehaviorSupport);
2151        configuration.add("Heartbeat", heartbeat);
2152        configuration.add("ValidationDecorator", defaultValidationDecorator);
2153    }
2154
2155    /**
2156     * Contributes several strategies:
2157     * <dl>
2158     * <dt>session
2159     * <dd>Values are stored in the {@link Session}
2160     * <dt>flash
2161     * <dd>Values are stored in the {@link Session}, until the next request (for the page)
2162     * <dt>client
2163     * <dd>Values are encoded into URLs (or hidden form fields)
2164     * </dl>
2165     */
2166    public void contributePersistentFieldManager(MappedConfiguration<String, PersistentFieldStrategy> configuration,
2167
2168                                                 Request request,
2169
2170                                                 @InjectService("ClientPersistentFieldStrategy")
2171                                                 PersistentFieldStrategy clientStrategy)
2172    {
2173        configuration.add(PersistenceConstants.SESSION, new SessionPersistentFieldStrategy(request));
2174        configuration.add(PersistenceConstants.FLASH, new FlashPersistentFieldStrategy(request));
2175        configuration.add(PersistenceConstants.CLIENT, clientStrategy);
2176    }
2177
2178    @SuppressWarnings("rawtypes")
2179    public static ValueEncoderSource buildValueEncoderSource(Map<Class, ValueEncoderFactory> configuration,
2180                                                             @ComponentClasses
2181                                                             InvalidationEventHub hub)
2182    {
2183        ValueEncoderSourceImpl service = new ValueEncoderSourceImpl(configuration);
2184
2185        hub.addInvalidationListener(service);
2186
2187        return service;
2188    }
2189
2190    /**
2191     * Contributes {@link ValueEncoder}s or {@link ValueEncoderFactory}s for types:
2192     * <ul>
2193     * <li>Object
2194     * <li>String
2195     * <li>Enum
2196     * </ul>
2197     */
2198    @SuppressWarnings("all")
2199    public static void contributeValueEncoderSource(MappedConfiguration<Class, Object> configuration)
2200    {
2201        configuration.addInstance(Object.class, TypeCoercedValueEncoderFactory.class);
2202        configuration.add(String.class, new StringValueEncoder());
2203        configuration.addInstance(Enum.class, EnumValueEncoderFactory.class);
2204    }
2205
2206    /**
2207     * Contributes a single filter, "Secure", which checks for non-secure
2208     * requests that access secure pages.
2209     */
2210    public void contributePageRenderRequestHandler(OrderedConfiguration<PageRenderRequestFilter> configuration,
2211                                                   final RequestSecurityManager securityManager)
2212    {
2213        PageRenderRequestFilter secureFilter = new PageRenderRequestFilter()
2214        {
2215            public void handle(PageRenderRequestParameters parameters, PageRenderRequestHandler handler)
2216                    throws IOException
2217            {
2218
2219                if (securityManager.checkForInsecurePageRenderRequest(parameters))
2220                    return;
2221
2222                handler.handle(parameters);
2223            }
2224        };
2225
2226        configuration.add("Secure", secureFilter);
2227    }
2228
2229    /**
2230     * Configures the extensions that will require a digest to be downloaded via
2231     * the asset dispatcher. Most resources
2232     * are "safe", they don't require a digest. For unsafe resources, the digest
2233     * is incorporated into the URL to ensure
2234     * that the client side isn't just "fishing".
2235     * <p/>
2236     * The extensions must be all lower case.
2237     * <p/>
2238     * This contributes "class", "properties" and "tml" (the template extension).
2239     *
2240     * @param configuration
2241     *         collection of extensions
2242     */
2243    public static void contributeResourceDigestGenerator(Configuration<String> configuration)
2244    {
2245        // Java class files always require a digest.
2246        configuration.add("class");
2247
2248        // Even though properties don't contain sensible data we should protect
2249        // them.
2250        configuration.add("properties");
2251
2252        // Likewise, we don't want people fishing for templates.
2253        configuration.add(TapestryConstants.TEMPLATE_EXTENSION);
2254    }
2255
2256    public static void contributeTemplateParser(MappedConfiguration<String, URL> config)
2257    {
2258        // Any class inside the internal module would do. Or we could move all
2259        // these
2260        // files to o.a.t.services.
2261
2262        Class c = TemplateParserImpl.class;
2263
2264        config.add("-//W3C//DTD XHTML 1.0 Strict//EN", c.getResource("xhtml1-strict.dtd"));
2265        config.add("-//W3C//DTD XHTML 1.0 Transitional//EN", c.getResource("xhtml1-transitional.dtd"));
2266        config.add("-//W3C//DTD XHTML 1.0 Frameset//EN", c.getResource("xhtml1-frameset.dtd"));
2267        config.add("-//W3C//DTD HTML 4.01//EN", c.getResource("xhtml1-strict.dtd"));
2268        config.add("-//W3C//DTD HTML 4.01 Transitional//EN", c.getResource("xhtml1-transitional.dtd"));
2269        config.add("-//W3C//DTD HTML 4.01 Frameset//EN", c.getResource("xhtml1-frameset.dtd"));
2270        config.add("-//W3C//ENTITIES Latin 1 for XHTML//EN", c.getResource("xhtml-lat1.ent"));
2271        config.add("-//W3C//ENTITIES Symbols for XHTML//EN", c.getResource("xhtml-symbol.ent"));
2272        config.add("-//W3C//ENTITIES Special for XHTML//EN", c.getResource("xhtml-special.ent"));
2273    }
2274
2275    /**
2276     * Contributes factory defaults that may be overridden.
2277     */
2278    public static void contributeFactoryDefaults(MappedConfiguration<String, Object> configuration)
2279    {
2280        // Remember this is request-to-request time, presumably it'll take the
2281        // developer more than
2282        // one second to make a change, save it, and switch back to the browser.
2283
2284        configuration.add(SymbolConstants.FILE_CHECK_INTERVAL, "1 s");
2285        configuration.add(SymbolConstants.FILE_CHECK_UPDATE_TIMEOUT, "50 ms");
2286
2287        // This should be overridden for particular applications. These are the
2288        // locales for which we have (at least some) localized messages.
2289        configuration.add(SymbolConstants.SUPPORTED_LOCALES,
2290                "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");
2291
2292        configuration.add(SymbolConstants.TAPESTRY_VERSION,
2293                VersionUtils.readVersionNumber("META-INF/gradle/org.apache.tapestry/tapestry-core/project.properties"));
2294
2295        configuration.add(SymbolConstants.COOKIE_MAX_AGE, "7 d");
2296
2297        configuration.add(SymbolConstants.START_PAGE_NAME, "start");
2298
2299        configuration.add(SymbolConstants.DEFAULT_STYLESHEET, "classpath:/org/apache/tapestry5/default.css");
2300        configuration.add("tapestry.spacer-image", "classpath:/org/apache/tapestry5/spacer.gif");
2301
2302        configuration.add(SymbolConstants.SUPPRESS_REDIRECT_FROM_ACTION_REQUESTS, false);
2303
2304        configuration.add(SymbolConstants.PRODUCTION_MODE, true);
2305
2306        configuration.add(SymbolConstants.CLUSTERED_SESSIONS, true);
2307
2308        configuration.add(SymbolConstants.ASSET_PATH_PREFIX, "/assets/");
2309
2310        configuration.add(SymbolConstants.COMPRESS_WHITESPACE, true);
2311
2312        configuration.add(MetaDataConstants.SECURE_PAGE, false);
2313
2314        configuration.add(SymbolConstants.FORM_CLIENT_LOGIC_ENABLED, true);
2315
2316        // This is designed to make it easy to keep synchronized with
2317        // script.aculo.ous. As we support a new version, we create a new folder, and update the
2318        // path entry. We can then delete the old version folder (or keep it around). This should
2319        // be more manageable than overwriting the local copy with updates (it's too easy for
2320        // files deleted between scriptaculous releases to be accidentally left lying around).
2321        // There's also a ClasspathAliasManager contribution based on the path.
2322
2323        configuration.add(SymbolConstants.SCRIPTACULOUS, "classpath:${tapestry.scriptaculous.path}");
2324        configuration.add("tapestry.scriptaculous.path", "org/apache/tapestry5/scriptaculous_1_9_0");
2325
2326        // Likewise for WebFX DatePicker, currently version 1.0.6
2327
2328        configuration.add("tapestry.datepicker.path", "org/apache/tapestry5/datepicker_106");
2329        configuration.add(SymbolConstants.DATEPICKER, "classpath:${tapestry.datepicker.path}");
2330
2331        configuration.add("tapestry.underscore", "classpath:org/apache/tapestry5/underscore_1_3_3.js");
2332
2333        configuration.add(SymbolConstants.BLACKBIRD, "");
2334
2335        configuration.add(SymbolConstants.PERSISTENCE_STRATEGY, PersistenceConstants.SESSION);
2336
2337        configuration.add(MetaDataConstants.RESPONSE_CONTENT_TYPE, "text/html");
2338
2339        configuration.add(SymbolConstants.CHARSET, "UTF-8");
2340
2341        configuration.add(SymbolConstants.APPLICATION_CATALOG,
2342                String.format("context:WEB-INF/${%s}.properties", InternalSymbols.APP_NAME));
2343
2344        configuration.add(SymbolConstants.EXCEPTION_REPORT_PAGE, "ExceptionReport");
2345
2346        configuration.add(SymbolConstants.MIN_GZIP_SIZE, 100);
2347
2348        Random random = new Random(System.currentTimeMillis());
2349
2350        configuration.add(SymbolConstants.APPLICATION_VERSION, Long.toHexString(random.nextLong()));
2351
2352        configuration.add(SymbolConstants.OMIT_GENERATOR_META, false);
2353
2354        configuration.add(SymbolConstants.SECURE_ENABLED, SymbolConstants.PRODUCTION_MODE_VALUE);
2355        configuration.add(SymbolConstants.COMPACT_JSON, SymbolConstants.PRODUCTION_MODE_VALUE);
2356
2357        configuration.add(SymbolConstants.ENCODE_LOCALE_INTO_PATH, true);
2358
2359        configuration.add(SymbolConstants.BLACKBIRD_ENABLED, false);
2360
2361        configuration.add(InternalSymbols.PRE_SELECTED_FORM_NAMES, "reset,submit,select,id,method,action,onsubmit," + InternalConstants.CANCEL_NAME);
2362
2363        configuration.add(SymbolConstants.COMPONENT_RENDER_TRACING_ENABLED, false);
2364
2365        // The default values denote "use values from request"
2366        configuration.add(SymbolConstants.HOSTNAME, "");
2367        configuration.add(SymbolConstants.HOSTPORT, 0);
2368        configuration.add(SymbolConstants.HOSTPORT_SECURE, 0);
2369
2370        configuration.add(SymbolConstants.UNKNOWN_COMPONENT_ID_CHECK_ENABLED, true);
2371
2372        configuration.add(SymbolConstants.APPLICATION_FOLDER, "");
2373
2374        // Grid component parameters defaults
2375        configuration.add(ComponentParameterConstants.GRID_ROWS_PER_PAGE, GridConstants.ROWS_PER_PAGE);
2376        configuration.add(ComponentParameterConstants.GRID_PAGER_POSITION, GridConstants.PAGER_POSITION);
2377        configuration.add(ComponentParameterConstants.GRID_EMPTY_BLOCK, GridConstants.EMPTY_BLOCK);
2378        configuration.add(ComponentParameterConstants.GRID_TABLE_CSS_CLASS, GridConstants.TABLE_CLASS);
2379        configuration.add(ComponentParameterConstants.GRIDPAGER_PAGE_RANGE, GridConstants.PAGER_PAGE_RANGE);
2380        configuration.add(ComponentParameterConstants.GRIDCOLUMNS_SORTABLE_ASSET, GridConstants.COLUMNS_SORTABLE);
2381        configuration.add(ComponentParameterConstants.GRIDCOLUMNS_ASCENDING_ASSET, GridConstants.COLUMNS_ASCENDING);
2382        configuration.add(ComponentParameterConstants.GRIDCOLUMNS_DESCENDING_ASSET, GridConstants.COLUMNS_DESCENDING);
2383
2384        // FormInjector component parameters defaults
2385        configuration.add(ComponentParameterConstants.FORMINJECTOR_INSERT_POSITION, "above");
2386        configuration.add(ComponentParameterConstants.FORMINJECTOR_SHOW_FUNCTION, "highlight");
2387
2388        // Palette component parameters defaults
2389        configuration.add(ComponentParameterConstants.PALETTE_ROWS_SIZE, 10);
2390
2391        // Zone component parameters defaults
2392        configuration.add(ComponentParameterConstants.ZONE_SHOW_METHOD, "show");
2393        configuration.add(ComponentParameterConstants.ZONE_UPDATE_METHOD, "highlight");
2394
2395        // By default, no page is on the whitelist unless it has the @WhitelistAccessOnly annotation
2396        configuration.add(MetaDataConstants.WHITELIST_ONLY_PAGE, false);
2397
2398        // Leaving this as the default results in a runtime error logged to the console (and a default password is used);
2399        // you are expected to override this symbol.
2400        configuration.add(SymbolConstants.HMAC_PASSPHRASE, "");
2401    }
2402
2403    /**
2404     * Adds a listener to the {@link org.apache.tapestry5.internal.services.ComponentInstantiatorSource} that clears the
2405     * {@link PropertyAccess} and {@link TypeCoercer} caches on
2406     * a class loader invalidation. In addition, forces the
2407     * realization of {@link ComponentClassResolver} at startup.
2408     */
2409    public void contributeApplicationInitializer(OrderedConfiguration<ApplicationInitializerFilter> configuration,
2410                                                 final TypeCoercer typeCoercer, final ComponentClassResolver componentClassResolver, @ComponentClasses
2411    final InvalidationEventHub invalidationEventHub, final @Autobuild
2412    RestoreDirtySessionObjects restoreDirtySessionObjects)
2413    {
2414        final InvalidationListener listener = new InvalidationListener()
2415        {
2416            public void objectWasInvalidated()
2417            {
2418                propertyAccess.clearCache();
2419
2420                typeCoercer.clearCache();
2421            }
2422        };
2423
2424        ApplicationInitializerFilter clearCaches = new ApplicationInitializerFilter()
2425        {
2426            public void initializeApplication(Context context, ApplicationInitializer initializer)
2427            {
2428                // Snuck in here is the logic to clear the PropertyAccess
2429                // service's cache whenever
2430                // the component class loader is invalidated.
2431
2432                invalidationEventHub.addInvalidationListener(listener);
2433
2434                endOfRequestEventHub.addEndOfRequestListener(restoreDirtySessionObjects);
2435
2436                // Perform other pending initialization
2437
2438                initializer.initializeApplication(context);
2439
2440                // We don't care about the result, but this forces a load of the
2441                // service
2442                // at application startup, rather than on first request.
2443
2444                componentClassResolver.isPageName("ForceLoadAtStartup");
2445            }
2446        };
2447
2448        configuration.add("ClearCachesOnInvalidation", clearCaches);
2449    }
2450
2451    /**
2452     * Contributes filters:
2453     * <dl>
2454     * <dt>Ajax</dt>
2455     * <dd>Determines if the request is Ajax oriented, and redirects to an alternative handler if so</dd>
2456     * <dt>ImmediateRender</dt>
2457     * <dd>When {@linkplain SymbolConstants#SUPPRESS_REDIRECT_FROM_ACTION_REQUESTS
2458     * immediate action response rendering} is enabled, generates the markup response (instead of a page redirect
2459     * response, which is the normal behavior)</dd>
2460     * <dt>Secure</dt>
2461     * <dd>Sends a redirect if an non-secure request accesses a secure page</dd>
2462     * </dl>
2463     */
2464    public void contributeComponentEventRequestHandler(OrderedConfiguration<ComponentEventRequestFilter> configuration,
2465                                                       final RequestSecurityManager requestSecurityManager, @Ajax
2466    ComponentEventRequestHandler ajaxHandler)
2467    {
2468        ComponentEventRequestFilter secureFilter = new ComponentEventRequestFilter()
2469        {
2470            public void handle(ComponentEventRequestParameters parameters, ComponentEventRequestHandler handler)
2471                    throws IOException
2472            {
2473                if (requestSecurityManager.checkForInsecureComponentEventRequest(parameters))
2474                    return;
2475
2476                handler.handle(parameters);
2477            }
2478        };
2479
2480        configuration.add("Ajax", new AjaxFilter(request, ajaxHandler));
2481
2482        configuration.addInstance("ImmediateRender", ImmediateActionRenderResponseFilter.class);
2483
2484        configuration.add("Secure", secureFilter, "before:Ajax");
2485    }
2486
2487    /**
2488     * Contributes:
2489     * <dl>
2490     * <dt>AjaxFormUpdate</dt>
2491     * <dd>{@link AjaxFormUpdateFilter}</dd>
2492     * </dl>
2493     *
2494     * @since 5.2.0
2495     */
2496    public static void contributeAjaxComponentEventRequestHandler(
2497            OrderedConfiguration<ComponentEventRequestFilter> configuration)
2498    {
2499        configuration.addInstance("AjaxFormUpdate", AjaxFormUpdateFilter.class);
2500    }
2501
2502    /**
2503     * Contributes strategies accessible via the {@link NullFieldStrategySource} service.
2504     * <p/>
2505     * <dl>
2506     * <dt>default</dt>
2507     * <dd>Does nothing, nulls stay null.</dd>
2508     * <dt>zero</dt>
2509     * <dd>Null values are converted to zero.</dd>
2510     * </dl>
2511     */
2512    public static void contributeNullFieldStrategySource(MappedConfiguration<String, NullFieldStrategy> configuration)
2513    {
2514        configuration.add("default", new DefaultNullFieldStrategy());
2515        configuration.add("zero", new ZeroNullFieldStrategy());
2516    }
2517
2518    /**
2519     * Determines positioning of hidden fields relative to other elements (this
2520     * is needed by {@link org.apache.tapestry5.corelib.components.FormFragment} and others.
2521     * <p/>
2522     * For elements input, select, textarea and label the hidden field is positioned after.
2523     * <p/>
2524     * For elements p, div, li and td, the hidden field is positioned inside.
2525     */
2526    public static void contributeHiddenFieldLocationRules(
2527            MappedConfiguration<String, RelativeElementPosition> configuration)
2528    {
2529        configuration.add("input", RelativeElementPosition.AFTER);
2530        configuration.add("select", RelativeElementPosition.AFTER);
2531        configuration.add("textarea", RelativeElementPosition.AFTER);
2532        configuration.add("label", RelativeElementPosition.AFTER);
2533
2534        configuration.add("p", RelativeElementPosition.INSIDE);
2535        configuration.add("div", RelativeElementPosition.INSIDE);
2536        configuration.add("td", RelativeElementPosition.INSIDE);
2537        configuration.add("li", RelativeElementPosition.INSIDE);
2538    }
2539
2540    /**
2541     * @since 5.1.0.0
2542     */
2543    public static LinkCreationHub buildLinkCreationHub(LinkSource source)
2544    {
2545        return source.getLinkCreationHub();
2546    }
2547
2548    /**
2549     * Exposes the public portion of the internal {@link InternalComponentInvalidationEventHub} service.
2550     *
2551     * @since 5.1.0.0
2552     */
2553    @Marker(ComponentClasses.class)
2554    public static InvalidationEventHub buildComponentClassesInvalidationEventHub(
2555            InternalComponentInvalidationEventHub trueHub)
2556    {
2557        return trueHub;
2558    }
2559
2560    /**
2561     * @since 5.1.0.0
2562     */
2563    @Marker(ComponentTemplates.class)
2564    public static InvalidationEventHub buildComponentTemplatesInvalidationEventHub(
2565            ComponentTemplateSource templateSource)
2566    {
2567        return templateSource.getInvalidationEventHub();
2568    }
2569
2570    /**
2571     * @since 5.1.0.0
2572     */
2573    @Marker(ComponentMessages.class)
2574    public static InvalidationEventHub buildComponentMessagesInvalidationEventHub(ComponentMessagesSource messagesSource)
2575    {
2576        return messagesSource.getInvalidationEventHub();
2577    }
2578
2579    @Scope(ScopeConstants.PERTHREAD)
2580    public Environment buildEnvironment(PerthreadManager perthreadManager)
2581    {
2582        EnvironmentImpl service = new EnvironmentImpl();
2583
2584        perthreadManager.addThreadCleanupListener(service);
2585
2586        return service;
2587    }
2588
2589    /**
2590     * The master SessionPersistedObjectAnalyzer.
2591     *
2592     * @since 5.1.0.0
2593     */
2594    @Marker(Primary.class)
2595    public SessionPersistedObjectAnalyzer buildSessionPersistedObjectAnalyzer(
2596            Map<Class, SessionPersistedObjectAnalyzer> configuration)
2597    {
2598        return strategyBuilder.build(SessionPersistedObjectAnalyzer.class, configuration);
2599    }
2600
2601    /**
2602     * Identifies String, Number and Boolean as immutable objects, a catch-all
2603     * handler for Object (that understands
2604     * the {@link org.apache.tapestry5.annotations.ImmutableSessionPersistedObject} annotation),
2605     * and a handler for {@link org.apache.tapestry5.OptimizedSessionPersistedObject}.
2606     *
2607     * @since 5.1.0.0
2608     */
2609    public static void contributeSessionPersistedObjectAnalyzer(
2610            MappedConfiguration<Class, SessionPersistedObjectAnalyzer> configuration)
2611    {
2612        configuration.add(Object.class, new DefaultSessionPersistedObjectAnalyzer());
2613
2614        SessionPersistedObjectAnalyzer<Object> immutable = new SessionPersistedObjectAnalyzer<Object>()
2615        {
2616            public boolean checkAndResetDirtyState(Object sessionPersistedObject)
2617            {
2618                return false;
2619            }
2620        };
2621
2622        configuration.add(String.class, immutable);
2623        configuration.add(Number.class, immutable);
2624        configuration.add(Boolean.class, immutable);
2625
2626        configuration.add(OptimizedSessionPersistedObject.class, new OptimizedSessionPersistedObjectAnalyzer());
2627    }
2628
2629    /**
2630     * @since 5.1.1.0
2631     */
2632    @Marker(Primary.class)
2633    public StackTraceElementAnalyzer buildMasterStackTraceElementAnalyzer(List<StackTraceElementAnalyzer> configuration)
2634    {
2635        return chainBuilder.build(StackTraceElementAnalyzer.class, configuration);
2636    }
2637
2638    /**
2639     * Contributes:
2640     * <dl>
2641     * <dt>Application</dt>
2642     * <dd>Checks for classes in the application package</dd>
2643     * <dt>Proxies</dt>
2644     * <dd>Checks for classes that appear to be generated proxies.</dd>
2645     * <dt>SunReflect</dt>
2646     * <dd>Checks for <code>sun.reflect</code> (which are omitted)
2647     * <dt>TapestryAOP</dt>
2648     * <dd>Omits stack frames for classes related to Tapestry AOP (such as advice, etc.)</dd>
2649     * <dt>OperationTracker</dt>
2650     * <dd>Omits stack frames related to {@link OperationTracker}</dd>
2651     * </dl>
2652     *
2653     * @since 5.1.0.0
2654     */
2655    public static void contributeMasterStackTraceElementAnalyzer(
2656            OrderedConfiguration<StackTraceElementAnalyzer> configuration)
2657    {
2658        configuration.addInstance("Application", ApplicationStackTraceElementAnalyzer.class);
2659        configuration.add("Proxies", new ProxiesStackTraceElementAnalyzer(), "before:Application");
2660        configuration.add("Synthetic", new SyntheticStackTraceElementAnalyzer(), "before:Application");
2661        configuration.add("SunReflect", new PrefixCheckStackTraceElementAnalyzer(
2662                StackTraceElementClassConstants.OMITTED, "sun.reflect."));
2663        configuration.addInstance("TapestryAOP", TapestryAOPStackFrameAnalyzer.class, "before:Application");
2664        configuration.add("OperationTracker", new RegexpStackTraceElementAnalyzer(Pattern.compile("internal\\.(RegistryImpl|PerThreadOperationTracker|OperationTrackerImpl)\\.invoke\\("), StackTraceElementClassConstants.OMITTED));
2665    }
2666
2667    /**
2668     * Advises the {@link org.apache.tapestry5.services.messages.ComponentMessagesSource} service so
2669     * that the creation
2670     * of {@link org.apache.tapestry5.ioc.Messages} instances can be deferred.
2671     *
2672     * @since 5.1.0.0
2673     */
2674    @Match("ComponentMessagesSource")
2675    public static void adviseLazy(LazyAdvisor advisor, MethodAdviceReceiver receiver)
2676    {
2677        advisor.addLazyMethodInvocationAdvice(receiver);
2678    }
2679
2680    /**
2681     * @since 5.1.0.0
2682     */
2683    public ComponentRequestHandler buildComponentRequestHandler(List<ComponentRequestFilter> configuration,
2684
2685                                                                @Autobuild
2686                                                                ComponentRequestHandlerTerminator terminator,
2687
2688                                                                Logger logger)
2689    {
2690        return pipelineBuilder.build(logger, ComponentRequestHandler.class, ComponentRequestFilter.class,
2691                configuration, terminator);
2692    }
2693
2694    /**
2695     * Contributes:
2696     * <dl>
2697     * <dt>InitializeActivePageName
2698     * <dd>{@link InitializeActivePageName}
2699     * </dl>
2700     *
2701     * @since 5.2.0
2702     */
2703    public void contributeComponentRequestHandler(OrderedConfiguration<ComponentRequestFilter> configuration)
2704    {
2705        configuration.addInstance("InitializeActivePageName", InitializeActivePageName.class);
2706    }
2707
2708    /**
2709     * Decorate FieldValidatorDefaultSource to setup the EnvironmentMessages
2710     * object and place it in the environment.
2711     * Although this could have been implemented directly in the default
2712     * implementation of the service, doing it
2713     * as service decoration ensures that the environment will be properly setup
2714     * even if a user overrides the default
2715     * service implementation.
2716     *
2717     * @param defaultSource
2718     *         The service to decorate
2719     * @param environment
2720     */
2721    public static FieldValidatorDefaultSource decorateFieldValidatorDefaultSource(
2722            final FieldValidatorDefaultSource defaultSource, final Environment environment)
2723    {
2724        return new FieldValidatorDefaultSource()
2725        {
2726
2727            public FieldValidator createDefaultValidator(Field field, String overrideId, Messages overrideMessages,
2728                                                         Locale locale, Class propertyType, AnnotationProvider propertyAnnotations)
2729            {
2730                environment.push(EnvironmentMessages.class, new EnvironmentMessages(overrideMessages, overrideId));
2731                FieldValidator fieldValidator = defaultSource.createDefaultValidator(field, overrideId,
2732                        overrideMessages, locale, propertyType, propertyAnnotations);
2733                environment.pop(EnvironmentMessages.class);
2734                return fieldValidator;
2735            }
2736
2737            public FieldValidator createDefaultValidator(ComponentResources resources, String parameterName)
2738            {
2739
2740                EnvironmentMessages em = new EnvironmentMessages(resources.getContainerMessages(), resources.getId());
2741                environment.push(EnvironmentMessages.class, em);
2742                FieldValidator fieldValidator = defaultSource.createDefaultValidator(resources, parameterName);
2743                environment.pop(EnvironmentMessages.class);
2744                return fieldValidator;
2745            }
2746        };
2747    }
2748
2749    /**
2750     * Exposes the Environmental {@link Heartbeat} as an injectable service.
2751     *
2752     * @since 5.2.0
2753     */
2754    public Heartbeat buildHeartbeat()
2755    {
2756        return environmentalBuilder.build(Heartbeat.class);
2757    }
2758
2759    /**
2760     * Contributes the "core" and "core-datefield" {@link JavaScriptStack}s
2761     *
2762     * @since 5.2.0
2763     */
2764    public static void contributeJavaScriptStackSource(MappedConfiguration<String, JavaScriptStack> configuration)
2765    {
2766        configuration.addInstance(InternalConstants.CORE_STACK_NAME, CoreJavaScriptStack.class);
2767        configuration.addInstance("core-datefield", DateFieldStack.class);
2768    }
2769
2770    public static ComponentMessagesSource buildComponentMessagesSource(UpdateListenerHub updateListenerHub, @Autobuild
2771    ComponentMessagesSourceImpl service)
2772    {
2773        updateListenerHub.addUpdateListener(service);
2774
2775        return service;
2776    }
2777
2778    /**
2779     * Contributes:
2780     * <dl>
2781     * <dt>AppCatalog</dt>
2782     * <dd>The Resource defined by {@link SymbolConstants#APPLICATION_CATALOG}</dd>
2783     * <dt>ValidationMessages</dt>
2784     * <dd>Messages used by validators (before:AppCatalog)</dd>
2785     * <dt>
2786     *
2787     * @since 5.2.0
2788     */
2789    public static void contributeComponentMessagesSource(AssetSource assetSource,
2790                                                         @Symbol(SymbolConstants.APPLICATION_CATALOG)
2791                                                         Resource applicationCatalog, OrderedConfiguration<Resource> configuration)
2792    {
2793        configuration.add("ValidationMessages",
2794                assetSource.resourceForPath("org/apache/tapestry5/internal/ValidationMessages.properties"),
2795                "before:AppCatalog");
2796        configuration.add("AppCatalog", applicationCatalog);
2797    }
2798
2799    /**
2800     * Contributes extractors for {@link Meta}, {@link Secure}, {@link ContentType} and {@link WhitelistAccessOnly} annotations.
2801     *
2802     * @since 5.2.0
2803     */
2804    @SuppressWarnings("unchecked")
2805    public static void contributeMetaWorker(MappedConfiguration<Class, MetaDataExtractor> configuration)
2806    {
2807        configuration.addInstance(Meta.class, MetaAnnotationExtractor.class);
2808        configuration.add(Secure.class, new FixedExtractor(MetaDataConstants.SECURE_PAGE));
2809        configuration.addInstance(ContentType.class, ContentTypeExtractor.class);
2810        configuration.add(WhitelistAccessOnly.class, new FixedExtractor(MetaDataConstants.WHITELIST_ONLY_PAGE));
2811    }
2812
2813    /**
2814     * Builds the {@link ComponentTemplateLocator} as a chain of command.
2815     *
2816     * @since 5.2.0
2817     */
2818    @Marker(Primary.class)
2819    public ComponentTemplateLocator buildComponentTemplateLocator(List<ComponentTemplateLocator> configuration)
2820    {
2821        return chainBuilder.build(ComponentTemplateLocator.class, configuration);
2822    }
2823
2824    /**
2825     * Contributes two template locators:
2826     * <dl>
2827     * <dt>Default</dt>
2828     * <dd>Searches for the template on the classpath ({@link DefaultTemplateLocator}</dd>
2829     * <dt>Page</dt>
2830     * <dd>Searches for <em>page</em> templates in the context ({@link PageTemplateLocator})</dd>
2831     * </dl>
2832     *
2833     * @since 5.2.0
2834     */
2835    public static void contributeComponentTemplateLocator(OrderedConfiguration<ComponentTemplateLocator> configuration,
2836                                                          @ContextProvider
2837                                                          AssetFactory contextAssetFactory,
2838                                                          @Symbol(SymbolConstants.APPLICATION_FOLDER) String applicationFolder,
2839                                                          ComponentClassResolver componentClassResolver)
2840    {
2841        configuration.add("Default", new DefaultTemplateLocator());
2842        configuration
2843                .add("Page", new PageTemplateLocator(contextAssetFactory.getRootResource(), componentClassResolver, applicationFolder));
2844
2845    }
2846
2847    /**
2848     * Builds {@link ComponentEventLinkTransformer} service as a chain of command.
2849     *
2850     * @since 5.2.0
2851     */
2852    @Marker(Primary.class)
2853    public ComponentEventLinkTransformer buildComponentEventLinkTransformer(
2854            List<ComponentEventLinkTransformer> configuration)
2855    {
2856        return chainBuilder.build(ComponentEventLinkTransformer.class, configuration);
2857    }
2858
2859    /**
2860     * Builds {@link PageRenderLinkTransformer} service as a chain of command.
2861     *
2862     * @since 5.2.0
2863     */
2864    @Marker(Primary.class)
2865    public PageRenderLinkTransformer buildPageRenderLinkTransformer(List<PageRenderLinkTransformer> configuration)
2866    {
2867        return chainBuilder.build(PageRenderLinkTransformer.class, configuration);
2868    }
2869
2870    /**
2871     * Provides the "LinkTransformer" interceptor for the {@link ComponentEventLinkEncoder} service.
2872     * Other decorations
2873     * should come after LinkTransformer.
2874     *
2875     * @since 5.2.0
2876     */
2877    @Match("ComponentEventLinkEncoder")
2878    public ComponentEventLinkEncoder decorateLinkTransformer(LinkTransformer linkTransformer,
2879                                                             ComponentEventLinkEncoder delegate)
2880    {
2881        return new LinkTransformerInterceptor(linkTransformer, delegate);
2882    }
2883
2884    /**
2885     * In production mode, override {@link UpdateListenerHub} to be an empty placeholder.
2886     */
2887    @Contribute(ServiceOverride.class)
2888    public static void productionModeOverrides(MappedConfiguration<Class, Object> configuration,
2889                                               @Symbol(SymbolConstants.PRODUCTION_MODE)
2890                                               boolean productionMode)
2891    {
2892        if (productionMode)
2893        {
2894            configuration.add(UpdateListenerHub.class, new UpdateListenerHub()
2895            {
2896                public void fireCheckForUpdates()
2897                {
2898                }
2899
2900                public void addUpdateListener(UpdateListener listener)
2901                {
2902
2903                }
2904            });
2905        }
2906    }
2907
2908    /**
2909     * Contributes a single default analyzer:
2910     * <dl>
2911     * <dt>LocalhostOnly</dt>
2912     * <dd>Identifies requests from localhost as on client whitelist</dd>
2913     * </dl>
2914     *
2915     * @since 5.3
2916     */
2917    @Contribute(ClientWhitelist.class)
2918    public static void defaultWhitelist(OrderedConfiguration<WhitelistAnalyzer> configuration)
2919    {
2920        configuration.add("LocalhostOnly", new LocalhostOnly());
2921    }
2922
2923    @Startup
2924    public static void registerToClearPlasticProxyFactoryOnInvalidation(@ComponentClasses InvalidationEventHub hub, @Builtin final PlasticProxyFactory proxyFactory)
2925    {
2926        hub.addInvalidationListener(new InvalidationListener()
2927        {
2928            @Override
2929            public void objectWasInvalidated()
2930            {
2931                proxyFactory.clearCache();
2932            }
2933        });
2934    }
2935}