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