    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.add("none", new None());
    698    }
    699
    700    /**
    701     * <dl>
    702     * <dt>Default</dt>
    703     * <dd>based on {@link MasterObjectProvider}</dd>
    704     * <dt>Named</dt> <dd>Handles fields with the {@link javax.inject.Named} annotation</dd>
    705     * <dt>Block</dt>
    706     * <dd>injects fields of type {@link Block}</dd>
    707     * <dt>CommonResources</dt>
    708     * <dd>Access to properties of resources (log, messages, etc.)</dd>
    709     * <dt>Asset</dt>
    710     * <dd>injection of assets (triggered via {@link Path} annotation), with the path relative to the component class</dd>
    711     * <dt>Service</dt>
    712     * <dd>Ordered last, for use when Inject is present and nothing else works, matches field type against Tapestry IoC
    713     * services</dd>
    714     * </dl>
    715     */
    716    @Contribute(InjectionProvider2.class)
    717    public static void provideStandardInjectionProviders(OrderedConfiguration<InjectionProvider2> configuration, SymbolSource symbolSource,
    718
    719                                                         AssetSource assetSource)
    720    {
    721        configuration.addInstance("Named", InjectNamedProvider.class);
    722        configuration.add("Block", new BlockInjectionProvider());
    723        configuration.add("Asset", new AssetInjectionProvider(assetSource));
    724
    725        configuration.add("CommonResources", new CommonResourcesInjectionProvider());
    726
    727        configuration.addInstance("Default", DefaultInjectionProvider.class);
    728
    729        // This needs to be the last one, since it matches against services
    730        // and might blow up if there is no match.
    731        configuration.addInstance("Service", ServiceInjectionProvider.class, "after:*");
    732    }
    733
    734    /**
    735     * Contributes two object providers:
    736     * <dl>
    737     * <dt>Asset
    738     * <dt>
    739     * <dd>Checks for the {@link Path} annotation, and injects an {@link Asset}</dd>
    740     * <dt>Service</dt>
    741     * <dd>Injects based on the {@link Service} annotation, if present</dd>
    742     * <dt>ApplicationMessages</dt>
    743     * <dd>Injects the global application messages</dd>
    744     * </dl>
    745     */
    746    public static void contributeMasterObjectProvider(OrderedConfiguration<ObjectProvider> configuration,
    747
    748                                                      @InjectService("AssetObjectProvider")
    749                                                      ObjectProvider assetObjectProvider,
    750
    751                                                      ObjectLocator locator)
    752    {
    753        configuration.add("Asset", assetObjectProvider, "before:AnnotationBasedContributions");
    754
    755        configuration.add("Service", new ServiceAnnotationObjectProvider(), "before:AnnotationBasedContributions");
    756
    757        configuration.add("ApplicationMessages", new ApplicationMessageCatalogObjectProvider(locator),
    758                "before:AnnotationBasedContributions");
    759
    760    }
    761
    762    /**
    763     * <dl>
    764     * <dt>StoreIntoGlobals</dt>
    765     * <dd>Stores the request and response into {@link org.apache.tapestry5.services.RequestGlobals} at the start of the
    766     * pipeline</dd>
    767     * <dt>IgnoredPaths</dt>
    768     * <dd>Identifies requests that are known (via the IgnoredPathsFilter service's configuration) to be mapped to other
    769     * applications</dd>
    770     * <dt>GZip</dt>
    771     * <dd>Handles GZIP compression of response streams (if supported by client)</dd>
    772     * </dl>
    773     */
    774    public void contributeHttpServletRequestHandler(OrderedConfiguration<HttpServletRequestFilter> configuration,
    775
    776                                                    @Symbol(SymbolConstants.GZIP_COMPRESSION_ENABLED)
    777                                                    boolean gzipCompressionEnabled,
    778
    779                                                    @Autobuild
    780                                                    GZipFilter gzipFilter,
    781
    782                                                    @InjectService("IgnoredPathsFilter")
    783                                                    HttpServletRequestFilter ignoredPathsFilter)
    784    {
    785        configuration.add("IgnoredPaths", ignoredPathsFilter);
    786
    787        configuration.add("GZIP", gzipCompressionEnabled ? gzipFilter : null);
    788
    789        HttpServletRequestFilter storeIntoGlobals = new HttpServletRequestFilter()
    790        {
    791            public boolean service(HttpServletRequest request, HttpServletResponse response,
    792                                   HttpServletRequestHandler handler) throws IOException
    793            {
    794                requestGlobals.storeServletRequestResponse(request, response);
    795
    796                return handler.service(request, response);
    797            }
    798        };
    799
    800        configuration.add("StoreIntoGlobals", storeIntoGlobals, "before:*");
    801    }
    802
    803    /**
    804     * Continues a number of filters into the RequestHandler service:
    805     * <dl>
    806     * <dt>StaticFiles</dt>
    807     * <dd>Checks to see if the request is for an actual file, if so, returns true to let the servlet container process
    808     * the request</dd>
    809     * <dt>CheckForUpdates</dt>
    810     * <dd>Periodically fires events that checks to see if the file system sources for any cached data has changed (see
    811     * {@link org.apache.tapestry5.internal.services.CheckForUpdatesFilter}). Starting in 5.3, this filter will be null
    812     * in production mode (it will only be active in development mode).
    813     * <dt>ErrorFilter</dt>
    814     * <dd>Catches request errors and lets the {@link org.apache.tapestry5.services.RequestExceptionHandler} handle them
    815     * </dd>
    816     * <dt>StoreIntoGlobals</dt>
    817     * <dd>Stores the request and response into the {@link org.apache.tapestry5.services.RequestGlobals} service (this
    818     * is repeated at the end of the pipeline, in case any filter substitutes the request or response).
    819     * <dt>EndOfRequest</dt>
    820     * <dd>Notifies internal services that the request has ended</dd>
    821     * </dl>
    822     */
    823    public void contributeRequestHandler(OrderedConfiguration<RequestFilter> configuration, Context context,
    824
    825                                         @Symbol(SymbolConstants.PRODUCTION_MODE)
    826                                         boolean productionMode)
    827    {
    828        RequestFilter staticFilesFilter = new StaticFilesFilter(context);
    829
    830        RequestFilter storeIntoGlobals = new RequestFilter()
    831        {
    832            public boolean service(Request request, Response response, RequestHandler handler) throws IOException
    833            {
    834                requestGlobals.storeRequestResponse(request, response);
    835
    836                return handler.service(request, response);
    837            }
    838        };
    839
    840        RequestFilter fireEndOfRequestEvent = new RequestFilter()
    841        {
    842            public boolean service(Request request, Response response, RequestHandler handler) throws IOException
    843            {
    844                try
    845                {
    846                    return handler.service(request, response);
    847                } finally
    848                {
    849                    endOfRequestEventHub.fire();
    850                }
    851            }
    852        };
    853
    854        if (productionMode)
    855        {
    856            configuration.add("CheckForUpdates", null, "before:*");
    857        } else
    858        {
    859            configuration.addInstance("CheckForUpdates", CheckForUpdatesFilter.class, "before:*");
    860        }
    861
    862        configuration.add("StaticFiles", staticFilesFilter);
    863
    864        configuration.add("StoreIntoGlobals", storeIntoGlobals);
    865
    866        configuration.add("EndOfRequest", fireEndOfRequestEvent);
    867
    868        configuration.addInstance("ErrorFilter", RequestErrorFilter.class);
    869    }
    870
    871    /**
    872     * Contributes the basic set of translators:
    873     * <ul>
    874     * <li>string</li>
    875     * <li>byte</li>
    876     * <li>short</li>
    877     * <li>integer</li>
    878     * <li>long</li>
    879     * <li>float</li>
    880     * <li>double</li>
    881     * <li>BigInteger</li>
    882     * <li>BigDecimal</li>
    883     * </ul>
    884     */
    885    public static void contributeTranslatorSource(MappedConfiguration<Class, Translator> configuration,
    886                                                  NumericTranslatorSupport support, Html5Support html5Support)
    887    {
    888
    889        configuration.add(String.class, new StringTranslator());
    890
    891        Class[] types = new Class[]
    892                {Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, BigInteger.class,
    893                        BigDecimal.class};
    894
    895        for (Class type : types)
    896        {
    897            String name = type.getSimpleName().toLowerCase();
    898
    899            configuration.add(type, new NumericTranslator(name, type, support, html5Support));
    900        }
    901    }
    902
    903    /**
    904     * Adds coercions:
    905     * <ul>
    906     * <li>String to {@link SelectModel}
    907     * <li>Map to {@link SelectModel}
    908     * <li>Collection to {@link GridDataSource}
    909     * <li>null to {@link GridDataSource}
    910     * <li>List to {@link SelectModel}
    911     * <li>{@link ComponentResourcesAware} (typically, a component) to {@link ComponentResources}
    912     * <li>{@link ComponentResources} to {@link PropertyOverrides}
    913     * <li>String to {@link Renderable}
    914     * <li>{@link Renderable} to {@link Block}
    915     * <li>String to {@link DateFormat}
    916     * <li>String to {@link Resource} (via {@link AssetSource#resourceForPath(String)})
    917     * <li>{@link Renderable} to {@link RenderCommand}</li>
    918     * <li>String to {@link Pattern}</li>
    919     * <li>String to {@link DateFormat}</li>
    920     * <li>{@link Resource} to {@link DynamicTemplate}</li>
    921     * <li>{@link Asset} to {@link Resource}</li>
    922     * <li>{@link ValueEncoder} to {@link ValueEncoderFactory}</li>
    923     * </ul>
    924     */
    925    public static void contributeTypeCoercer(Configuration<CoercionTuple> configuration,
    926
    927                                             final ObjectLocator objectLocator,
    928
    929                                             @Builtin
    930                                             final ThreadLocale threadLocale,
    931
    932                                             @Core
    933                                             final AssetSource assetSource,
    934
    935                                             @Core
    936                                             final DynamicTemplateParser dynamicTemplateParser)
    937    {
    938        configuration.add(CoercionTuple.create(ComponentResources.class, PropertyOverrides.class,
    939                new Coercion<ComponentResources, PropertyOverrides>()
    940                {
    941                    public PropertyOverrides coerce(ComponentResources input)
    942                    {
    943                        return new PropertyOverridesImpl(input);
    944                    }
    945                }));
    946
    947
    948        // See TAP5-2184 for why this causes some trouble!
    949        configuration.add(CoercionTuple.create(String.class, SelectModel.class, new Coercion<String, SelectModel>()
    950        {
    951            public SelectModel coerce(String input)
    952            {
    953                return TapestryInternalUtils.toSelectModel(input);
    954            }
    955        }));
    956
    957        configuration.add(CoercionTuple.create(Map.class, SelectModel.class, new Coercion<Map, SelectModel>()
    958        {
    959            @SuppressWarnings("unchecked")
    960            public SelectModel coerce(Map input)
    961            {
    962                return TapestryInternalUtils.toSelectModel(input);
    963            }
    964        }));
    965
    966        configuration.add(CoercionTuple.create(Collection.class, GridDataSource.class,
    967                new Coercion<Collection, GridDataSource>()
    968                {
    969                    public GridDataSource coerce(Collection input)
    970                    {
    971                        return new CollectionGridDataSource(input);
    972                    }
    973                }));
    974
    975        configuration.add(CoercionTuple.create(void.class, GridDataSource.class, new Coercion<Void, GridDataSource>()
    976        {
    977            private final GridDataSource source = new NullDataSource();
    978
    979            public GridDataSource coerce(Void input)
    980            {
    981                return source;
    982            }
    983        }));
    984
    985        configuration.add(CoercionTuple.create(List.class, SelectModel.class, new Coercion<List, SelectModel>()
    986        {
    987            private SelectModelFactory selectModelFactory;
    988
    989            @SuppressWarnings("unchecked")
    990            public SelectModel coerce(List input)
    991            {
    992                // This doesn't look thread safe, but it is because its a one-time transition from null
    993                // to another value, and a race condition is harmless.
    994                if (selectModelFactory == null)
    995                {
    996                    selectModelFactory = objectLocator.getService(SelectModelFactory.class);
    997                }
    998
    999                return selectModelFactory.create(input);
    1000            }
    1001        }));
    1002
    1003        configuration.add(CoercionTuple.create(String.class, Pattern.class, new Coercion<String, Pattern>()
    1004        {
    1005            public Pattern coerce(String input)
    1006            {
    1007                return Pattern.compile(input);
    1008            }
    1009        }));
    1010
    1011        configuration.add(CoercionTuple.create(ComponentResourcesAware.class, ComponentResources.class,
    1012                new Coercion<ComponentResourcesAware, ComponentResources>()
    1013                {
    1014
    1015                    public ComponentResources coerce(ComponentResourcesAware input)
    1016                    {
    1017                        return input.getComponentResources();
    1018                    }
    1019                }));
    1020
    1021        configuration.add(CoercionTuple.create(String.class, Renderable.class, new Coercion<String, Renderable>()
    1022        {
    1023            public Renderable coerce(String input)
    1024            {
    1025                return new StringRenderable(input);
    1026            }
    1027        }));
    1028
    1029        configuration.add(CoercionTuple.create(Renderable.class, Block.class, new Coercion<Renderable, Block>()
    1030        {
    1031            public Block coerce(Renderable input)
    1032            {
    1033                return new RenderableAsBlock(input);
    1034            }
    1035        }));
    1036
    1037        configuration.add(CoercionTuple.create(String.class, DateFormat.class, new Coercion<String, DateFormat>()
    1038        {
    1039            public DateFormat coerce(String input)
    1040            {
    1041                final SimpleDateFormat dateFormat = new SimpleDateFormat(input, threadLocale.getLocale());
    1042                final String lenient = objectLocator.getService(SymbolSource.class).valueForSymbol(SymbolConstants.LENIENT_DATE_FORMAT);
    1043                dateFormat.setLenient(Boolean.parseBoolean(lenient));
    1044                return dateFormat;
    1045            }
    1046        }));
    1047
    1048        configuration.add(CoercionTuple.create(String.class, Resource.class, new Coercion<String, Resource>()
    1049        {
    1050            public Resource coerce(String input)
    1051            {
    1052                return assetSource.resourceForPath(input);
    1053            }
    1054        }));
    1055
    1056        configuration.add(CoercionTuple.create(Renderable.class, RenderCommand.class,
    1057                new Coercion<Renderable, RenderCommand>()
    1058                {
    1059                    public RenderCommand coerce(final Renderable input)
    1060                    {
    1061                        return new RenderCommand()
    1062                        {
    1063                            public void render(MarkupWriter writer, RenderQueue queue)
    1064                            {
    1065                                input.render(writer);
    1066                            }
    1067                        };
    1068                    }
    1069                }));
    1070
    1071        configuration.add(CoercionTuple.create(Date.class, Calendar.class, new Coercion<Date, Calendar>()
    1072        {
    1073            public Calendar coerce(Date input)
    1074            {
    1075                Calendar calendar = Calendar.getInstance(threadLocale.getLocale());
    1076                calendar.setTime(input);
    1077                return calendar;
    1078            }
    1079        }));
    1080
    1081        configuration.add(CoercionTuple.create(Resource.class, DynamicTemplate.class,
    1082                new Coercion<Resource, DynamicTemplate>()
    1083                {
    1084                    public DynamicTemplate coerce(Resource input)
    1085                    {
    1086                        return dynamicTemplateParser.parseTemplate(input);
    1087                    }
    1088                }));
    1089
    1090        configuration.add(CoercionTuple.create(Asset.class, Resource.class, new Coercion<Asset, Resource>()
    1091        {
    1092            public Resource coerce(Asset input)
    1093            {
    1094                return input.getResource();
    1095            }
    1096        }));
    1097
    1098        configuration.add(CoercionTuple.create(ValueEncoder.class, ValueEncoderFactory.class, new Coercion<ValueEncoder, ValueEncoderFactory>()
    1099        {
    1100            public ValueEncoderFactory coerce(ValueEncoder input)
    1101            {
    1102                return new GenericValueEncoderFactory(input);
    1103            }
    1104        }));
    1105    }
    1106
    1107    /**
    1108     * Adds built-in constraint generators:
    1109     * <ul>
    1110     * <li>PrimtiveField -- primitive fields are always required
    1111     * <li>ValidateAnnotation -- adds constraints from a {@link Validate} annotation
    1112     * </ul>
    1113     */
    1114    public static void contributeValidationConstraintGenerator(
    1115            OrderedConfiguration<ValidationConstraintGenerator> configuration)
    1116    {
    1117        configuration.add("PrimitiveField", new PrimitiveFieldConstraintGenerator());
    1118        configuration.add("ValidateAnnotation", new ValidateAnnotationConstraintGenerator());
    1119        configuration.addInstance("Messages", MessagesConstraintGenerator.class);
    1120    }
    1121
    1122    private static void add(OrderedConfiguration<ComponentClassTransformWorker2> configuration,
    1123                            Class<? extends Annotation> annotationClass, MethodDescription description)
    1124    {
    1125        String name = TapestryInternalUtils.lastTerm(annotationClass.getName());
    1126
    1127        ComponentClassTransformWorker2 worker = new PageLifecycleAnnotationWorker(annotationClass,
    1128                description, name);
    1129
    1130        configuration.add(name, worker);
    1131    }
    1132
    1133    // ========================================================================
    1134    //
    1135    // Service Builder Methods (instance)
    1136    //
    1137    // ========================================================================
    1138
    1139    public Context buildContext(ApplicationGlobals globals)
    1140    {
    1141        return shadowBuilder.build(globals, "context", Context.class);
    1142    }
    1143
    1144    public static ComponentClassResolver buildComponentClassResolver(@Autobuild
    1145                                                                     ComponentClassResolverImpl service, @ComponentClasses
    1146                                                                     InvalidationEventHub hub)
    1147    {
    1148        // Allow the resolver to clean its cache when the component classes
    1149        // change
    1150
    1151        hub.addInvalidationListener(service);
    1152
    1153        return service;
    1154    }
    1155
    1156
    1157    /**
    1158     * Builds the PropBindingFactory as a chain of command. The terminator of
    1159     * the chain is responsible for ordinary
    1160     * property names (and property paths).
    1161     *
    1162     * This mechanism has been replaced in 5.1 with a more sophisticated parser based on ANTLR. See <a
    1163     * href="https://issues.apache.org/jira/browse/TAP5-79">TAP5-79</a> for details. There are no longer any built-in
    1164     * contributions to the configuration.
    1165     *
    1166     * @param configuration
    1167     *         contributions of special factories for some constants, each
    1168     *         contributed factory may return a
    1169     *         binding if applicable, or null otherwise
    1170     */
    1171    public BindingFactory buildPropBindingFactory(List<BindingFactory> configuration, @Autobuild
    1172    PropBindingFactory service)
    1173    {
    1174        configuration.add(service);
    1175
    1176        return chainBuilder.build(BindingFactory.class, configuration);
    1177    }
    1178
    1179    public PersistentFieldStrategy buildClientPersistentFieldStrategy(LinkCreationHub linkCreationHub, @Autobuild
    1180    ClientPersistentFieldStrategy service)
    1181    {
    1182        linkCreationHub.addListener(service);
    1183
    1184        return service;
    1185    }
    1186
    1187    /**
    1188     * Builds a proxy to the current {@link org.apache.tapestry5.services.ClientBehaviorSupport} inside this
    1189     * thread's {@link org.apache.tapestry5.services.Environment}.
    1190     *
    1191     * @since 5.1.0.1
    1192     */
    1193
    1194    public ClientBehaviorSupport buildClientBehaviorSupport()
    1195    {
    1196        return environmentalBuilder.build(ClientBehaviorSupport.class);
    1197    }
    1198
    1199    /**
    1200     * Builds a proxy to the current {@link org.apache.tapestry5.services.FormSupport} inside this
    1201     * thread's {@link org.apache.tapestry5.services.Environment}.
    1202     */
    1203    public FormSupport buildFormSupport()
    1204    {
    1205        return environmentalBuilder.build(FormSupport.class);
    1206    }
    1207
    1208    /**
    1209     * Allows the exact steps in the component class transformation process to
    1210     * be defined.
    1211     */
    1212    @Marker(Primary.class)
    1213    public ComponentClassTransformWorker2 buildComponentClassTransformWorker(
    1214            List<ComponentClassTransformWorker2> configuration)
    1215
    1216    {
    1217        return chainBuilder.build(ComponentClassTransformWorker2.class, configuration);
    1218    }
    1219
    1220    /**
    1221     * Analyzes properties to determine the data types, used to
    1222     * {@linkplain #provideDefaultBeanBlocks(org.apache.tapestry5.ioc.Configuration)} locale
    1223     * display and edit blocks for properties. The default behaviors
    1224     * look for a {@link org.apache.tapestry5.beaneditor.DataType} annotation
    1225     * before deriving the data type from the property type.
    1226     */
    1227    @Marker(Primary.class)
    1228    public DataTypeAnalyzer buildDataTypeAnalyzer(List<DataTypeAnalyzer> configuration)
    1229    {
    1230        return chainBuilder.build(DataTypeAnalyzer.class, configuration);
    1231    }
    1232
    1233    /**
    1234     * A chain of command for providing values for {@link Inject}-ed fields in
    1235     * component classes. The service's
    1236     * configuration can be extended to allow for different automatic injections
    1237     * (based on some combination of field
    1238     * type and field name).
    1239     */
    1240    public InjectionProvider2 buildInjectionProvider(List<InjectionProvider2> configuration)
    1241    {
    1242        return chainBuilder.build(InjectionProvider2.class, configuration);
    1243    }
    1244
    1245    /**
    1246     * Initializes the application, using a pipeline of {@link org.apache.tapestry5.services.ApplicationInitializer}s.
    1247     */
    1248    @Marker(Primary.class)
    1249    public ApplicationInitializer buildApplicationInitializer(Logger logger,
    1250                                                              List<ApplicationInitializerFilter> configuration)
    1251    {
    1252        ApplicationInitializer terminator = new ApplicationInitializerTerminator();
    1253
    1254        return pipelineBuilder.build(logger, ApplicationInitializer.class, ApplicationInitializerFilter.class,
    1255                configuration, terminator);
    1256    }
    1257
    1258    public HttpServletRequestHandler buildHttpServletRequestHandler(Logger logger,
    1259
    1260                                                                    List<HttpServletRequestFilter> configuration,
    1261
    1262                                                                    @Primary
    1263                                                                    RequestHandler handler,
    1264
    1265                                                                    @Symbol(SymbolConstants.CHARSET)
    1266                                                                    String applicationCharset,
    1267
    1268                                                                    TapestrySessionFactory sessionFactory)
    1269    {
    1270        HttpServletRequestHandler terminator = new HttpServletRequestHandlerTerminator(handler, applicationCharset,
    1271                sessionFactory);
    1272
    1273        return pipelineBuilder.build(logger, HttpServletRequestHandler.class, HttpServletRequestFilter.class,
    1274                configuration, terminator);
    1275    }
    1276
    1277    @Marker(Primary.class)
    1278    public RequestHandler buildRequestHandler(Logger logger, List<RequestFilter> configuration,
    1279
    1280                                              @Primary
    1281                                              Dispatcher masterDispatcher)
    1282    {
    1283        RequestHandler terminator = new RequestHandlerTerminator(masterDispatcher);
    1284
    1285        return pipelineBuilder.build(logger, RequestHandler.class, RequestFilter.class, configuration, terminator);
    1286    }
    1287
    1288    public ServletApplicationInitializer buildServletApplicationInitializer(Logger logger,
    1289                                                                            List<ServletApplicationInitializerFilter> configuration,
    1290
    1291                                                                            @Primary
    1292                                                                            ApplicationInitializer initializer)
    1293    {
    1294        ServletApplicationInitializer terminator = new ServletApplicationInitializerTerminator(initializer);
    1295
    1296        return pipelineBuilder.build(logger, ServletApplicationInitializer.class,
    1297                ServletApplicationInitializerFilter.class, configuration, terminator);
    1298    }
    1299
    1300    /**
    1301     * The component event result processor used for normal component requests.
    1302     */
    1303    @Marker(
    1304            {Primary.class, Traditional.class})
    1305    public ComponentEventResultProcessor buildComponentEventResultProcessor(
    1306            Map<Class, ComponentEventResultProcessor> configuration, @ComponentClasses
    1307    InvalidationEventHub hub)
    1308    {
    1309        return constructComponentEventResultProcessor(configuration, hub);
    1310    }
    1311
    1312    /**
    1313     * The component event result processor used for Ajax-oriented component
    1314     * requests.
    1315     */
    1316    @Marker(Ajax.class)
    1317    public ComponentEventResultProcessor buildAjaxComponentEventResultProcessor(
    1318            Map<Class, ComponentEventResultProcessor> configuration, @ComponentClasses
    1319    InvalidationEventHub hub)
    1320    {
    1321        return constructComponentEventResultProcessor(configuration, hub);
    1322    }
    1323
    1324    private ComponentEventResultProcessor constructComponentEventResultProcessor(
    1325            Map<Class, ComponentEventResultProcessor> configuration, InvalidationEventHub hub)
    1326    {
    1327        Set<Class> handledTypes = CollectionFactory.newSet(configuration.keySet());
    1328
    1329        // A slight hack!
    1330
    1331        configuration.put(Object.class, new ObjectComponentEventResultProcessor(handledTypes));
    1332
    1333        final StrategyRegistry<ComponentEventResultProcessor> registry = StrategyRegistry.newInstance(
    1334                ComponentEventResultProcessor.class, configuration);
    1335
    1336        //As the registry will cache component classes, we need to clear the cache when we reload components to avoid memory leaks in permgen
    1337        hub.addInvalidationCallback(new Runnable()
    1338        {
    1339            public void run()
    1340            {
    1341                registry.clearCache();
    1342            }
    1343        });
    1344
    1345        return strategyBuilder.build(registry);
    1346    }
    1347
    1348    /**
    1349     * The default data type analyzer is the final analyzer consulted and
    1350     * identifies the type entirely pased on the
    1351     * property type, working against its own configuration (mapping property
    1352     * type class to data type).
    1353     */
    1354    public static DataTypeAnalyzer buildDefaultDataTypeAnalyzer(@Autobuild
    1355                                                                DefaultDataTypeAnalyzer service, @ComponentClasses
    1356                                                                InvalidationEventHub hub)
    1357    {
    1358        hub.addInvalidationCallback(service);
    1359
    1360        return service;
    1361    }
    1362
    1363    public static TranslatorSource buildTranslatorSource(Map<Class, Translator> configuration,
    1364                                                         TranslatorAlternatesSource alternatesSource,
    1365                                                         @ComponentClasses
    1366                                                         InvalidationEventHub hub)
    1367    {
    1368        TranslatorSourceImpl service = new TranslatorSourceImpl(configuration,
    1369                alternatesSource.getTranslatorAlternates());
    1370
    1371        hub.addInvalidationCallback(service);
    1372
    1373        return service;
    1374    }
    1375
    1376    @Marker(Primary.class)
    1377    public ObjectRenderer buildObjectRenderer(Map<Class, ObjectRenderer> configuration)
    1378    {
    1379        return strategyBuilder.build(ObjectRenderer.class, configuration);
    1380    }
    1381
    1382    /**
    1383     * Returns a {@link PlasticProxyFactory} that can be used to create extra classes around component classes. This
    1384     * factory will be cleared whenever an underlying component class is discovered to have changed. Use of this
    1385     * factory implies that your code will become aware of this (if necessary) to discard any cached object (alas,
    1386     * this currently involves dipping into the internals side to register for the correct notifications). Failure to
    1387     * properly clean up can result in really nasty PermGen space memory leaks.
    1388     */
    1389    @Marker(ComponentLayer.class)
    1390    public PlasticProxyFactory buildComponentProxyFactory(ComponentInstantiatorSource source)
    1391    {
    1392        return shadowBuilder.build(source, "proxyFactory", PlasticProxyFactory.class);
    1393    }
    1394
    1395    /**
    1396     * Ordered contributions to the MasterDispatcher service allow different URL
    1397     * matching strategies to occur.
    1398     */
    1399    @Marker(Primary.class)
    1400    public Dispatcher buildMasterDispatcher(List<Dispatcher> configuration)
    1401    {
    1402        return chainBuilder.build(Dispatcher.class, configuration);
    1403    }
    1404
    1405    /**
    1406     * Builds a shadow of the RequestGlobals.request property. Note again that
    1407     * the shadow can be an ordinary singleton,
    1408     * even though RequestGlobals is perthread.
    1409     */
    1410    public Request buildRequest()
    1411    {
    1412        return shadowBuilder.build(requestGlobals, "request", Request.class);
    1413    }
    1414
    1415    /**
    1416     * Builds a shadow of the RequestGlobals.HTTPServletRequest property.
    1417     * Generally, you should inject the {@link Request} service instead, as
    1418     * future version of Tapestry may operate beyond just the servlet API.
    1419     */
    1420    public HttpServletRequest buildHttpServletRequest()
    1421    {
    1422        return shadowBuilder.build(requestGlobals, "HTTPServletRequest", HttpServletRequest.class);
    1423    }
    1424
    1425    /**
    1426     * @since 5.1.0.0
    1427     */
    1428    public HttpServletResponse buildHttpServletResponse()
    1429    {
    1430        return shadowBuilder.build(requestGlobals, "HTTPServletResponse", HttpServletResponse.class);
    1431    }
    1432
    1433    /**
    1434     * Builds a shadow of the RequestGlobals.response property. Note again that
    1435     * the shadow can be an ordinary singleton,
    1436     * even though RequestGlobals is perthread.
    1437     */
    1438    public Response buildResponse()
    1439    {
    1440        return shadowBuilder.build(requestGlobals, "response", Response.class);
    1441    }
    1442
    1443    /**
    1444     * The MarkupRenderer service is used to render a full page as markup.
    1445     * Supports an ordered configuration of {@link org.apache.tapestry5.services.MarkupRendererFilter}s.
    1446     */
    1447    public MarkupRenderer buildMarkupRenderer(Logger logger, @Autobuild
    1448    MarkupRendererTerminator terminator, List<MarkupRendererFilter> configuration)
    1449    {
    1450        return pipelineBuilder.build(logger, MarkupRenderer.class, MarkupRendererFilter.class, configuration,
    1451                terminator);
    1452    }
    1453
    1454    /**
    1455     * A wrapper around {@link org.apache.tapestry5.internal.services.PageRenderQueue} used for
    1456     * partial page renders.
    1457     * Supports an ordered configuration of {@link org.apache.tapestry5.services.PartialMarkupRendererFilter}s.
    1458     */
    1459    public PartialMarkupRenderer buildPartialMarkupRenderer(Logger logger,
    1460                                                            List<PartialMarkupRendererFilter> configuration, @Autobuild
    1461    PartialMarkupRendererTerminator terminator)
    1462    {
    1463
    1464        return pipelineBuilder.build(logger, PartialMarkupRenderer.class, PartialMarkupRendererFilter.class,
    1465                configuration, terminator);
    1466    }
    1467
    1468    public PageRenderRequestHandler buildPageRenderRequestHandler(List<PageRenderRequestFilter> configuration,
    1469                                                                  Logger logger, @Autobuild
    1470    PageRenderRequestHandlerImpl terminator)
    1471    {
    1472        return pipelineBuilder.build(logger, PageRenderRequestHandler.class, PageRenderRequestFilter.class,
    1473                configuration, terminator);
    1474    }
    1475
    1476    /**
    1477     * Builds the component action request handler for traditional (non-Ajax)
    1478     * requests. These typically result in a
    1479     * redirect to a Tapestry render URL.
    1480     */
    1481    @Marker(
    1482            {Traditional.class, Primary.class})
    1483    public ComponentEventRequestHandler buildComponentEventRequestHandler(
    1484            List<ComponentEventRequestFilter> configuration, Logger logger, @Autobuild
    1485    ComponentEventRequestHandlerImpl terminator)
    1486    {
    1487        return pipelineBuilder.build(logger, ComponentEventRequestHandler.class, ComponentEventRequestFilter.class,
    1488                configuration, terminator);
    1489    }
    1490
    1491    /**
    1492     * Builds the action request handler for Ajax requests, based on a
    1493     * {@linkplain org.apache.tapestry5.ioc.services.PipelineBuilder
    1494     * pipeline} around {@link org.apache.tapestry5.internal.services.AjaxComponentEventRequestHandler} . Filters on
    1495     * the
    1496     * request handler are supported here as well.
    1497     */
    1498    @Marker(
    1499            {Ajax.class, Primary.class})
    1500    public ComponentEventRequestHandler buildAjaxComponentEventRequestHandler(
    1501            List<ComponentEventRequestFilter> configuration, Logger logger, @Autobuild
    1502    AjaxComponentEventRequestHandler terminator)
    1503    {
    1504        return pipelineBuilder.build(logger, ComponentEventRequestHandler.class, ComponentEventRequestFilter.class,
    1505                configuration, terminator);
    1506    }
    1507
    1508    // ========================================================================
    1509    //
    1510    // Service Contribution Methods (instance)
    1511    //
    1512    // ========================================================================
    1513
    1514    /**
    1515     * Contributes the default "session" strategy.
    1516     */
    1517    public void contributeApplicationStatePersistenceStrategySource(
    1518            MappedConfiguration<String, ApplicationStatePersistenceStrategy> configuration,
    1519
    1520            @Local
    1521            ApplicationStatePersistenceStrategy sessionStategy)
    1522    {
    1523        configuration.add("session", sessionStategy);
    1524    }
    1525
    1526    /**
    1527     * Contributes handlers for the following types:
    1528     * <dl>
    1529     * <dt>Object</dt>
    1530     * <dd>Failure case, added to provide a more useful exception message</dd>
    1531     * <dt>{@link Link}</dt>
    1532     * <dd>Sends a redirect to the link (which is typically a page render link)</dd>
    1533     * <dt>String</dt>
    1534     * <dd>Sends a page render redirect</dd>
    1535     * <dt>Class</dt>
    1536     * <dd>Interpreted as the class name of a page, sends a page render render redirect (this is more refactoring safe
    1537     * than the page name)</dd>
    1538     * <dt>{@link Component}</dt>
    1539     * <dd>A page's root component (though a non-root component will work, but will generate a warning). A direct to the
    1540     * containing page is sent.</dd>
    1541     * <dt>{@link org.apache.tapestry5.StreamResponse}</dt>
    1542     * <dd>The stream response is sent as the actual reply.</dd>
    1543     * <dt>URL</dt>
    1544     * <dd>Sends a redirect to a (presumably) external URL</dd>
    1545     * </dl>
    1546     */
    1547    public void contributeComponentEventResultProcessor(@Traditional
    1548                                                        @ComponentInstanceProcessor
    1549                                                        ComponentEventResultProcessor componentInstanceProcessor,
    1550
    1551                                                        MappedConfiguration<Class, ComponentEventResultProcessor> configuration)
    1552    {
    1553        configuration.add(Link.class, new ComponentEventResultProcessor<Link>()
    1554        {
    1555            public void processResultValue(Link value) throws IOException
    1556            {
    1557                response.sendRedirect(value);
    1558            }
    1559        });
    1560
    1561        configuration.add(URL.class, new ComponentEventResultProcessor<URL>()
    1562        {
    1563            public void processResultValue(URL value) throws IOException
    1564            {
    1565                response.sendRedirect(value.toExternalForm());
    1566            }
    1567        });
    1568
    1569        configuration.addInstance(HttpError.class, HttpErrorComponentEventResultProcessor.class);
    1570
    1571        configuration.addInstance(String.class, PageNameComponentEventResultProcessor.class);
    1572
    1573        configuration.addInstance(Class.class, ClassResultProcessor.class);
    1574
    1575        configuration.add(Component.class, componentInstanceProcessor);
    1576
    1577        configuration.addInstance(StreamResponse.class, StreamResponseResultProcessor.class);
    1578
    1579        configuration.addInstance(StreamPageContent.class, StreamPageContentResultProcessor.class);
    1580    }
    1581
    1582    /**
    1583     * Contributes handlers for the following types:
    1584     * <dl>
    1585     * <dt>Object</dt>
    1586     * <dd>Failure case, added to provide more useful exception message</dd>
    1587     * <dt>{@link RenderCommand}</dt>
    1588     * <dd>Typically, a {@link org.apache.tapestry5.Block}</dd>
    1589     * <dt>{@link org.apache.tapestry5.annotations.Component}</dt>
    1590     * <dd>Renders the component and its body (unless its a page, in which case a redirect JSON response is sent)</dd>
    1591     * <dt>{@link org.apache.tapestry5.json.JSONObject} or {@link org.apache.tapestry5.json.JSONArray}</dt>
    1592     * <dd>The JSONObject is returned as a text/javascript response</dd>
    1593     * <dt>{@link org.apache.tapestry5.StreamResponse}</dt>
    1594     * <dd>The stream response is sent as the actual response</dd>
    1595     * <dt>String</dt>
    1596     * <dd>Interprets the value as a logical page name and sends a client response to redirect to that page</dd>
    1597     * <dt>{@link org.apache.tapestry5.Link}</dt>
    1598     * <dd>Sends a JSON response to redirect to the link</dd>
    1599     * <dt>{@link Class}</dt>
    1600     * <dd>Treats the class as a page class and sends a redirect for a page render for that page</dd>
    1601     * <dt>{@link org.apache.tapestry5.ajax.MultiZoneUpdate}</dt>
    1602     * <dd>Sends a single JSON response to update the content of multiple zones
    1603     * </dl>
    1604     *
    1605     * In most cases, when you want to support a new type, you should convert it to one of the built-in supported types
    1606     * (such as {@link RenderCommand}. You can then inject the master AjaxComponentEventResultProcessor (use the
    1607     * {@link Ajax} marker annotation) and delegate to it.
    1608     */
    1609    @Contribute(ComponentEventResultProcessor.class)
    1610    @Ajax
    1611    public static void provideBaseAjaxComponentEventResultProcessors(
    1612            MappedConfiguration<Class, ComponentEventResultProcessor> configuration)
    1613    {
    1614        configuration.addInstance(RenderCommand.class, RenderCommandComponentEventResultProcessor.class);
    1615        configuration.addInstance(Component.class, AjaxComponentInstanceEventResultProcessor.class);
    1616        configuration.addInstance(JSONObject.class, JSONObjectEventResultProcessor.class);
    1617        configuration.addInstance(JSONArray.class, JSONArrayEventResultProcessor.class);
    1618        configuration.addInstance(StreamResponse.class, StreamResponseResultProcessor.class);
    1619        configuration.addInstance(String.class, AjaxPageNameComponentEventResultProcessor.class);
    1620        configuration.addInstance(Link.class, AjaxLinkComponentEventResultProcessor.class);
    1621        configuration.addInstance(URL.class, AjaxURLComponentEventResultProcessor.class);
    1622        configuration.addInstance(Class.class, AjaxPageClassComponentEventResultProcessor.class);
    1623        configuration.addInstance(MultiZoneUpdate.class, MultiZoneUpdateEventResultProcessor.class);
    1624        configuration.addInstance(HttpError.class, HttpErrorComponentEventResultProcessor.class);
    1625    }
    1626
    1627    /**
    1628     * The MasterDispatcher is a chain-of-command of individual Dispatchers,
    1629     * each handling (like a servlet) a particular
    1630     * kind of incoming request.
    1631     * <dl>
    1632     * <dt>RootPath</dt>
    1633     * <dd>Renders the start page for the "/" request (outdated)</dd>
    1634     * <dt>PageRender</dt>
    1635     * <dd>Identifies the {@link org.apache.tapestry5.services.PageRenderRequestParameters} and forwards onto
    1636     * {@link PageRenderRequestHandler}</dd>
    1637     * <dt>ComponentEvent</dt>
    1638     * <dd>Identifies the {@link ComponentEventRequestParameters} and forwards onto the
    1639     * {@link ComponentEventRequestHandler}</dd>
    1640     * </dl>
    1641     */
    1642    public static void contributeMasterDispatcher(OrderedConfiguration<Dispatcher> configuration)
    1643    {
    1644        // Looks for the root path and renders the start page. This is
    1645        // maintained for compatibility
    1646        // with earlier versions of Tapestry 5, it is recommended that an Index
    1647        // page be used instead.
    1648
    1649        configuration.addInstance("RootPath", RootPathDispatcher.class, "before:Asset");
    1650
    1651        configuration.addInstance("ComponentEvent", ComponentEventDispatcher.class, "before:PageRender");
    1652
    1653        configuration.addInstance("PageRender", PageRenderDispatcher.class);
    1654    }
    1655
    1656    /**
    1657     * Contributes a default object renderer for type Object, plus specialized
    1658     * renderers for {@link org.apache.tapestry5.services.Request}, {@link org.apache.tapestry5.ioc.Location},
    1659     * {@link org.apache.tapestry5.ComponentResources}, {@link org.apache.tapestry5.EventContext},
    1660     * {@link AvailableValues},
    1661     * List, and Object[].
    1662     */
    1663    @SuppressWarnings("unchecked")
    1664    public void contributeObjectRenderer(MappedConfiguration<Class, ObjectRenderer> configuration,
    1665
    1666                                         @InjectService("LocationRenderer")
    1667                                         ObjectRenderer locationRenderer,
    1668
    1669                                         final TypeCoercer typeCoercer)
    1670    {
    1671        configuration.add(Object.class, new DefaultObjectRenderer());
    1672
    1673        configuration.addInstance(Request.class, RequestRenderer.class);
    1674
    1675        configuration.add(Location.class, locationRenderer);
    1676
    1677        ObjectRenderer preformatted = new ObjectRenderer<Object>()
    1678        {
    1679            public void render(Object object, MarkupWriter writer)
    1680            {
    1681                writer.element("pre");
    1682                writer.write(typeCoercer.coerce(object, String.class));
    1683                writer.end();
    1684            }
    1685        };
    1686
    1687        configuration.addInstance(List.class, ListRenderer.class);
    1688        configuration.addInstance(Object[].class, ObjectArrayRenderer.class);
    1689        configuration.addInstance(ComponentResources.class, ComponentResourcesRenderer.class);
    1690        configuration.addInstance(EventContext.class, EventContextRenderer.class);
    1691        configuration.add(AvailableValues.class, new AvailableValuesRenderer());
    1692    }
    1693
    1694    /**
    1695     * Adds page render filters, each of which provides an {@link org.apache.tapestry5.annotations.Environmental}
    1696     * service. Filters
    1697     * often provide {@link org.apache.tapestry5.annotations.Environmental} services needed by
    1698     * components as they render.
    1699     * <dl>
    1700     * <dt>DocumentLinker</dt>
    1701     * <dd>Provides {@link org.apache.tapestry5.internal.services.DocumentLinker}</dd>
    1702     * <dt>ClientBehaviorSupport (deprecated in 5.4)</dt>
    1703     * <dd>Provides {@link ClientBehaviorSupport}</dd>
    1704     * <dt>Heartbeat</dt>
    1705     * <dd>Provides {@link org.apache.tapestry5.services.Heartbeat}</dd>
    1706     * <dt>ValidationDecorator (deprecated in 5.4)</dt>
    1707     * <dd>Provides {@link org.apache.tapestry5.ValidationDecorator} (via {@link ValidationDecoratorFactory#newInstance(org.apache.tapestry5.MarkupWriter)})</dd>
    1708     * <dt>PageNameMeta (since 5.4)</dt>
    1709     * <dd>Renders a {@code <meta/>} tag describing the active page name (development mode only)</dd>
    1710     * <dt>ImportCoreStack (since 5.4) </dt>
    1711     * <dd>Imports the "core" stack (necessary to get the Bootstrap CSS, if nothing else).</dd>
    1712     * </dl>
    1713     *
    1714     * @see org.apache.tapestry5.SymbolConstants#OMIT_GENERATOR_META
    1715     * @see org.apache.tapestry5.SymbolConstants#PRODUCTION_MODE
    1716     * @see org.apache.tapestry5.SymbolConstants#INCLUDE_CORE_STACK
    1717     * @see org.apache.tapestry5.SymbolConstants#ENABLE_PAGELOADING_MASK
    1718     */
    1719    public void contributeMarkupRenderer(OrderedConfiguration<MarkupRendererFilter> configuration,
    1720
    1721                                         final ModuleManager moduleManager,
    1722
    1723                                         @Symbol(SymbolConstants.OMIT_GENERATOR_META)
    1724                                         final boolean omitGeneratorMeta,
    1725
    1726                                         @Symbol(SymbolConstants.TAPESTRY_VERSION)
    1727                                         final String tapestryVersion,
    1728
    1729                                         @Symbol(SymbolConstants.PRODUCTION_MODE)
    1730                                         boolean productionMode,
    1731
    1732                                         @Symbol(SymbolConstants.INCLUDE_CORE_STACK)
    1733                                         final boolean includeCoreStack,
    1734
    1735                                         @Symbol(SymbolConstants.ENABLE_PAGELOADING_MASK)
    1736                                         final boolean enablePageloadingMask,
    1737
    1738                                         final ValidationDecoratorFactory validationDecoratorFactory)
    1739    {
    1740        MarkupRendererFilter documentLinker = new MarkupRendererFilter()
    1741        {
    1742            public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
    1743            {
    1744                DocumentLinkerImpl linker = new DocumentLinkerImpl(moduleManager, omitGeneratorMeta, enablePageloadingMask, tapestryVersion);
    1745
    1746                environment.push(DocumentLinker.class, linker);
    1747
    1748                renderer.renderMarkup(writer);
    1749
    1750                environment.pop(DocumentLinker.class);
    1751
    1752                linker.updateDocument(writer.getDocument());
    1753            }
    1754        };
    1755
    1756
    1757        MarkupRendererFilter clientBehaviorSupport = new MarkupRendererFilter()
    1758        {
    1759            public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
    1760            {
    1761                ClientBehaviorSupportImpl clientBehaviorSupport = new ClientBehaviorSupportImpl();
    1762
    1763                environment.push(ClientBehaviorSupport.class, clientBehaviorSupport);
    1764
    1765                renderer.renderMarkup(writer);
    1766
    1767                environment.pop(ClientBehaviorSupport.class);
    1768            }
    1769        };
    1770
    1771        MarkupRendererFilter heartbeat = new MarkupRendererFilter()
    1772        {
    1773            public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
    1774            {
    1775                Heartbeat heartbeat = new HeartbeatImpl();
    1776
    1777                heartbeat.begin();
    1778
    1779                environment.push(Heartbeat.class, heartbeat);
    1780
    1781                renderer.renderMarkup(writer);
    1782
    1783                environment.pop(Heartbeat.class);
    1784
    1785                heartbeat.end();
    1786            }
    1787        };
    1788
    1789        MarkupRendererFilter defaultValidationDecorator = new MarkupRendererFilter()
    1790        {
    1791            public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
    1792            {
    1793                ValidationDecorator decorator = validationDecoratorFactory.newInstance(writer);
    1794
    1795                environment.push(ValidationDecorator.class, decorator);
    1796
    1797                renderer.renderMarkup(writer);
    1798
    1799                environment.pop(ValidationDecorator.class);
    1800            }
    1801        };
    1802
    1803        MarkupRendererFilter importCoreStack = new MarkupRendererFilter()
    1804        {
    1805            public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
    1806            {
    1807                renderer.renderMarkup(writer);
    1808
    1809                environment.peekRequired(JavaScriptSupport.class).importStack(InternalConstants.CORE_STACK_NAME);
    1810            }
    1811        };
    1812
    1813        configuration.add("DocumentLinker", documentLinker);
    1814        configuration.add("ClientBehaviorSupport", clientBehaviorSupport, "after:JavaScriptSupport");
    1815        configuration.add("Heartbeat", heartbeat);
    1816        configuration.add("ValidationDecorator", defaultValidationDecorator);
    1817
    1818        if (includeCoreStack)
    1819        {
    1820            configuration.add("ImportCoreStack", importCoreStack);
    1821        }
    1822
    1823        if (productionMode)
    1824        {
    1825            configuration.add("PageNameMeta", null);
    1826        } else
    1827        {
    1828            configuration.addInstance("PageNameMeta", PageNameMetaInjector.class);
    1829        }
    1830    }
    1831
    1832    /**
    1833     * Contributes {@link PartialMarkupRendererFilter}s used when rendering a
    1834     * partial Ajax response.
    1835     * <dl>
    1836     * <dt>DocumentLinker
    1837     * <dd>Provides {@link org.apache.tapestry5.internal.services.DocumentLinker}
    1838     * <dt>ClientBehaviorSupport</dt>
    1839     * <dd>Provides {@link ClientBehaviorSupport}</dd>
    1840     * <dt>Heartbeat</dt>
    1841     * <dd>Provides {@link org.apache.tapestry5.services.Heartbeat}</dd>
    1842     * <dt>DefaultValidationDecorator</dt>
    1843     * <dt>ValidationDecorator</dt>
    1844     * <dd>Provides {@link org.apache.tapestry5.ValidationDecorator} (via {@link ValidationDecoratorFactory#newInstance(org.apache.tapestry5.MarkupWriter)})</dd>
    1845     * </dl>
    1846     */
    1847    public void contributePartialMarkupRenderer(OrderedConfiguration<PartialMarkupRendererFilter> configuration,
    1848
    1849                                                final ValidationDecoratorFactory validationDecoratorFactory)
    1850    {
    1851        PartialMarkupRendererFilter documentLinker = new PartialMarkupRendererFilter()
    1852        {
    1853            public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
    1854            {
    1855                PartialMarkupDocumentLinker linker = new PartialMarkupDocumentLinker();
    1856
    1857                environment.push(DocumentLinker.class, linker);
    1858
    1859                renderer.renderMarkup(writer, reply);
    1860
    1861                environment.pop(DocumentLinker.class);
    1862
    1863                linker.commit(reply);
    1864            }
    1865        };
    1866
    1867
    1868        PartialMarkupRendererFilter clientBehaviorSupport = new PartialMarkupRendererFilter()
    1869        {
    1870            public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
    1871            {
    1872                ClientBehaviorSupportImpl support = new ClientBehaviorSupportImpl();
    1873
    1874                environment.push(ClientBehaviorSupport.class, support);
    1875
    1876                renderer.renderMarkup(writer, reply);
    1877
    1878                environment.pop(ClientBehaviorSupport.class);
    1879            }
    1880        };
    1881
    1882        PartialMarkupRendererFilter heartbeat = new PartialMarkupRendererFilter()
    1883        {
    1884            public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
    1885            {
    1886                Heartbeat heartbeat = new HeartbeatImpl();
    1887
    1888                heartbeat.begin();
    1889
    1890                environment.push(Heartbeat.class, heartbeat);
    1891
    1892                renderer.renderMarkup(writer, reply);
    1893
    1894                environment.pop(Heartbeat.class);
    1895
    1896                heartbeat.end();
    1897            }
    1898        };
    1899
    1900        PartialMarkupRendererFilter defaultValidationDecorator = new PartialMarkupRendererFilter()
    1901        {
    1902            public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
    1903            {
    1904                ValidationDecorator decorator = validationDecoratorFactory.newInstance(writer);
    1905
    1906                environment.push(ValidationDecorator.class, decorator);
    1907
    1908                renderer.renderMarkup(writer, reply);
    1909
    1910                environment.pop(ValidationDecorator.class);
    1911            }
    1912        };
    1913
    1914        configuration.add("DocumentLinker", documentLinker);
    1915        configuration.add("ClientBehaviorSupport", clientBehaviorSupport, "after:JavaScriptSupport");
    1916        configuration.add("Heartbeat", heartbeat);
    1917        configuration.add("ValidationDecorator", defaultValidationDecorator);
    1918    }
    1919
    1920    /**
    1921     * Contributes several strategies:
    1922     * <dl>
    1923     * <dt>session
    1924     * <dd>Values are stored in the {@link Session}
    1925     * <dt>flash
    1926     * <dd>Values are stored in the {@link Session}, until the next request (for the page)
    1927     * <dt>client
    1928     * <dd>Values are encoded into URLs (or hidden form fields)
    1929     * </dl>
    1930     */
    1931    public void contributePersistentFieldManager(MappedConfiguration<String, PersistentFieldStrategy> configuration,
    1932
    1933                                                 Request request,
    1934
    1935                                                 @InjectService("ClientPersistentFieldStrategy")
    1936                                                 PersistentFieldStrategy clientStrategy)
    1937    {
    1938        configuration.add(PersistenceConstants.SESSION, new SessionPersistentFieldStrategy(request));
    1939        configuration.add(PersistenceConstants.FLASH, new FlashPersistentFieldStrategy(request));
    1940        configuration.add(PersistenceConstants.CLIENT, clientStrategy);
    1941    }
    1942
    1943    /**
    1944     * Contributes {@link ValueEncoder}s or {@link ValueEncoderFactory}s for types:
    1945     * <ul>
    1946     * <li>Object
    1947     * <li>String
    1948     * <li>Enum
    1949     * </ul>
    1950     */
    1951    @SuppressWarnings("all")
    1952    public static void contributeValueEncoderSource(MappedConfiguration<Class, Object> configuration)
    1953    {
    1954        configuration.addInstance(Object.class, TypeCoercedValueEncoderFactory.class);
    1955        configuration.add(String.class, new StringValueEncoder());
    1956        configuration.addInstance(Enum.class, EnumValueEncoderFactory.class);
    1957    }
    1958
    1959    /**
    1960     * Contributes a single filter, "Secure", which checks for non-secure
    1961     * requests that access secure pages.
    1962     */
    1963    public void contributePageRenderRequestHandler(OrderedConfiguration<PageRenderRequestFilter> configuration,
    1964                                                   final RequestSecurityManager securityManager)
    1965    {
    1966        PageRenderRequestFilter secureFilter = new PageRenderRequestFilter()
    1967        {
    1968            public void handle(PageRenderRequestParameters parameters, PageRenderRequestHandler handler)
    1969                    throws IOException
    1970            {
    1971
    1972                if (securityManager.checkForInsecurePageRenderRequest(parameters))
    1973                    return;
    1974
    1975                handler.handle(parameters);
    1976            }
    1977        };
    1978
    1979        configuration.add("Secure", secureFilter);
    1980    }
    1981
    1982    public static void contributeTemplateParser(MappedConfiguration<String, URL> config)
    1983    {
    1984        // Any class inside the internal module would do. Or we could move all
    1985        // these
    1986        // files to o.a.t.services.
    1987
    1988        Class c = TemplateParserImpl.class;
    1989
    1990        config.add("-//W3C//DTD XHTML 1.0 Strict//EN", c.getResource("xhtml1-strict.dtd"));
    1991        config.add("-//W3C//DTD XHTML 1.0 Transitional//EN", c.getResource("xhtml1-transitional.dtd"));
    1992        config.add("-//W3C//DTD XHTML 1.0 Frameset//EN", c.getResource("xhtml1-frameset.dtd"));
    1993        config.add("-//W3C//DTD HTML 4.01//EN", c.getResource("xhtml1-strict.dtd"));
    1994        config.add("-//W3C//DTD HTML 4.01 Transitional//EN", c.getResource("xhtml1-transitional.dtd"));
    1995        config.add("-//W3C//DTD HTML 4.01 Frameset//EN", c.getResource("xhtml1-frameset.dtd"));
    1996        config.add("-//W3C//ENTITIES Latin 1 for XHTML//EN", c.getResource("xhtml-lat1.ent"));
    1997        config.add("-//W3C//ENTITIES Symbols for XHTML//EN", c.getResource("xhtml-symbol.ent"));
    1998        config.add("-//W3C//ENTITIES Special for XHTML//EN", c.getResource("xhtml-special.ent"));
    1999    }
    2000
    2001    /**
    2002     * Contributes factory defaults that may be overridden.
    2003     */
    2004    public static void contributeFactoryDefaults(MappedConfiguration<String, Object> configuration)
    2005    {
    2006        // Remember this is request-to-request time, presumably it'll take the
    2007        // developer more than
    2008        // one second to make a change, save it, and switch back to the browser.
    2009
    2010        configuration.add(SymbolConstants.FILE_CHECK_INTERVAL, "1 s");
    2011        configuration.add(SymbolConstants.FILE_CHECK_UPDATE_TIMEOUT, "50 ms");
    2012
    2013        // This should be overridden for particular applications. These are the
    2014        // locales for which we have (at least some) localized messages.
    2015        configuration.add(SymbolConstants.SUPPORTED_LOCALES,
    2016                "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");
    2017
    2018        configuration.add(SymbolConstants.TAPESTRY_VERSION,
    2019                VersionUtils.readVersionNumber("META-INF/gradle/org.apache.tapestry/tapestry-core/project.properties"));
    2020
    2021        configuration.add(SymbolConstants.COOKIE_MAX_AGE, "7 d");
    2022
    2023        configuration.add(SymbolConstants.START_PAGE_NAME, "start");
    2024
    2025        configuration.add(SymbolConstants.DEFAULT_STYLESHEET, "");
    2026
    2027        configuration.add(SymbolConstants.PRODUCTION_MODE, true);
    2028
    2029        configuration.add(SymbolConstants.CLUSTERED_SESSIONS, true);
    2030
    2031        configuration.add(SymbolConstants.COMPRESS_WHITESPACE, true);
    2032
    2033        configuration.add(MetaDataConstants.SECURE_PAGE, false);
    2034
    2035        configuration.add(SymbolConstants.FORM_CLIENT_LOGIC_ENABLED, true);
    2036
    2037        // This is designed to make it easy to keep synchronized with
    2038        // script.aculo.ous. As we support a new version, we create a new folder, and update the
    2039        // path entry. We can then delete the old version folder (or keep it around). This should
    2040        // be more manageable than overwriting the local copy with updates (it's too easy for
    2041        // files deleted between scriptaculous releases to be accidentally left lying around).
    2042        // There's also a ClasspathAliasManager contribution based on the path.
    2043
    2044        configuration.add(SymbolConstants.SCRIPTACULOUS, "${tapestry.asset.root}/scriptaculous_1_9_0");
    2045
    2046        // Likewise for WebFX DatePicker, currently version 1.0.6
    2047
    2048        configuration.add(SymbolConstants.DATEPICKER, "${tapestry.asset.root}/datepicker_106");
    2049
    2050        configuration.add(SymbolConstants.PERSISTENCE_STRATEGY, PersistenceConstants.SESSION);
    2051
    2052        configuration.add(MetaDataConstants.RESPONSE_CONTENT_TYPE, "text/html");
    2053
    2054        configuration.add(SymbolConstants.CHARSET, "UTF-8");
    2055
    2056        configuration.add(SymbolConstants.APPLICATION_CATALOG,
    2057                String.format("context:WEB-INF/${%s}.properties", InternalSymbols.APP_NAME));
    2058
    2059        configuration.add(SymbolConstants.EXCEPTION_REPORT_PAGE, "ExceptionReport");
    2060
    2061        configuration.add(SymbolConstants.MIN_GZIP_SIZE, 100);
    2062
    2063        configuration.add(SymbolConstants.APPLICATION_VERSION, "0.0.1");
    2064
    2065        configuration.add(SymbolConstants.OMIT_GENERATOR_META, false);
    2066
    2067        configuration.add(SymbolConstants.SECURE_ENABLED, SymbolConstants.PRODUCTION_MODE_VALUE);
    2068        configuration.add(SymbolConstants.COMPACT_JSON, SymbolConstants.PRODUCTION_MODE_VALUE);
    2069
    2070        configuration.add(SymbolConstants.ENCODE_LOCALE_INTO_PATH, true);
    2071
    2072        configuration.add(InternalSymbols.RESERVED_FORM_CONTROL_NAMES, "reset,submit,select,id,method,action,onsubmit," + InternalConstants.CANCEL_NAME);
    2073
    2074        configuration.add(SymbolConstants.COMPONENT_RENDER_TRACING_ENABLED, false);
    2075
    2076        // The default values denote "use values from request"
    2077        configuration.add(SymbolConstants.HOSTNAME, "");
    2078        configuration.add(SymbolConstants.HOSTPORT, 0);
    2079        configuration.add(SymbolConstants.HOSTPORT_SECURE, 0);
    2080
    2081        configuration.add(SymbolConstants.APPLICATION_FOLDER, "");
    2082
    2083        // Grid component parameter defaults
    2084        configuration.add(ComponentParameterConstants.GRID_ROWS_PER_PAGE, GridConstants.ROWS_PER_PAGE);
    2085        configuration.add(ComponentParameterConstants.GRID_PAGER_POSITION, GridConstants.PAGER_POSITION);
    2086        configuration.add(ComponentParameterConstants.GRID_EMPTY_BLOCK, GridConstants.EMPTY_BLOCK);
    2087        configuration.add(ComponentParameterConstants.GRID_TABLE_CSS_CLASS, GridConstants.TABLE_CLASS);
    2088        configuration.add(ComponentParameterConstants.GRIDPAGER_PAGE_RANGE, GridConstants.PAGER_PAGE_RANGE);
    2089        configuration.add(ComponentParameterConstants.GRIDCOLUMNS_SORTABLE_ASSET, GridConstants.COLUMNS_SORTABLE);
    2090        configuration.add(ComponentParameterConstants.GRIDCOLUMNS_ASCENDING_ASSET, GridConstants.COLUMNS_ASCENDING);
    2091        configuration.add(ComponentParameterConstants.GRIDCOLUMNS_DESCENDING_ASSET, GridConstants.COLUMNS_DESCENDING);
    2092
    2093        // FormInjector component parameter defaults
    2094        configuration.add(ComponentParameterConstants.FORMINJECTOR_INSERT_POSITION, "above");
    2095        configuration.add(ComponentParameterConstants.FORMINJECTOR_SHOW_FUNCTION, "highlight");
    2096
    2097        // Palette component parameter defaults
    2098        configuration.add(ComponentParameterConstants.PALETTE_ROWS_SIZE, 10);
    2099
    2100        // Defaults for components that use a SelectModel
    2101        configuration.add(ComponentParameterConstants.VALIDATE_WITH_MODEL, SecureOption.AUTO);
    2102
    2103        // Zone component parameters defaults
    2104        configuration.add(ComponentParameterConstants.ZONE_SHOW_METHOD, "show");
    2105        configuration.add(ComponentParameterConstants.ZONE_UPDATE_METHOD, "highlight");
    2106
    2107        // By default, no page is on the whitelist unless it has the @WhitelistAccessOnly annotation
    2108        configuration.add(MetaDataConstants.WHITELIST_ONLY_PAGE, false);
    2109
    2110        configuration.add(SymbolConstants.CONTEXT_PATH, "");
    2111
    2112        // Leaving this as the default results in a runtime error logged to the console (and a default password is used);
    2113        // you are expected to override this symbol.
    2114        configuration.add(SymbolConstants.HMAC_PASSPHRASE, "");
    2115
    2116        configuration.add(SymbolConstants.SESSION_LOCKING_ENABLED, true);
    2117
    2118        // TAP5-2070 keep the old behavior, defaults to false
    2119        configuration.add(MetaDataConstants.UNKNOWN_ACTIVATION_CONTEXT_CHECK, false);
    2120
    2121        // TAP5-2197
    2122        configuration.add(SymbolConstants.INCLUDE_CORE_STACK, true);
    2123
    2124        // TAP5-2182
    2125        configuration.add(SymbolConstants.FORM_GROUP_WRAPPER_CSS_CLASS, "form-group");
    2126        configuration.add(SymbolConstants.FORM_GROUP_LABEL_CSS_CLASS, "control-label");
    2127        configuration.add(SymbolConstants.FORM_GROUP_FORM_FIELD_WRAPPER_ELEMENT_NAME, "");
    2128        configuration.add(SymbolConstants.FORM_GROUP_FORM_FIELD_WRAPPER_ELEMENT_CSS_CLASS, "");
    2129        configuration.add(SymbolConstants.FORM_FIELD_CSS_CLASS, "form-control");
    2130
    2131        // TAP5-1998
    2132        configuration.add(SymbolConstants.LENIENT_DATE_FORMAT, false);
    2133
    2134        // TAP5-2187
    2135        configuration.add(SymbolConstants.STRICT_CSS_URL_REWRITING, false);
    2136
    2137        configuration.add(SymbolConstants.EXCEPTION_REPORTS_DIR, "build/exceptions");
    2138
    2139        // TAP5-1815
    2140        configuration.add(SymbolConstants.ENABLE_HTML5_SUPPORT, false);
    2141
    2142        configuration.add(SymbolConstants.RESTRICTIVE_ENVIRONMENT, false);
    2143
    2144        configuration.add(SymbolConstants.ENABLE_PAGELOADING_MASK, true);
    2145        configuration.add(SymbolConstants.PRELOADER_MODE, PreloaderMode.PRODUCTION);
    2146    }
    2147
    2148    /**
    2149     * Adds a listener to the {@link org.apache.tapestry5.internal.services.ComponentInstantiatorSource} that clears the
    2150     * {@link PropertyAccess} and {@link TypeCoercer} caches on
    2151     * a class loader invalidation. In addition, forces the
    2152     * realization of {@link ComponentClassResolver} at startup.
    2153     */
    2154    public void contributeApplicationInitializer(OrderedConfiguration<ApplicationInitializerFilter> configuration,
    2155                                                 final TypeCoercer typeCoercer, final ComponentClassResolver componentClassResolver, @ComponentClasses
    2156    final InvalidationEventHub invalidationEventHub, final @Autobuild
    2157                                                 RestoreDirtySessionObjects restoreDirtySessionObjects)
    2158    {
    2159        final Runnable callback = new Runnable()
    2160        {
    2161            public void run()
    2162            {
    2163                propertyAccess.clearCache();
    2164
    2165                typeCoercer.clearCache();
    2166            }
    2167        };
    2168
    2169        ApplicationInitializerFilter clearCaches = new ApplicationInitializerFilter()
    2170        {
    2171            public void initializeApplication(Context context, ApplicationInitializer initializer)
    2172            {
    2173                // Snuck in here is the logic to clear the PropertyAccess
    2174                // service's cache whenever
    2175                // the component class loader is invalidated.
    2176
    2177                invalidationEventHub.addInvalidationCallback(callback);
    2178
    2179                endOfRequestEventHub.addEndOfRequestListener(restoreDirtySessionObjects);
    2180
    2181                // Perform other pending initialization
    2182
    2183                initializer.initializeApplication(context);
    2184
    2185                // We don't care about the result, but this forces a load of the
    2186                // service
    2187                // at application startup, rather than on first request.
    2188
    2189                componentClassResolver.isPageName("ForceLoadAtStartup");
    2190            }
    2191        };
    2192
    2193        configuration.add("ClearCachesOnInvalidation", clearCaches);
    2194    }
    2195
    2196    /**
    2197     * Contributes filters:
    2198     * <dl>
    2199     * <dt>Ajax</dt>
    2200     * <dd>Determines if the request is Ajax oriented, and redirects to an alternative handler if so</dd>
    2201     * <dt>Secure</dt>
    2202     * <dd>Sends a redirect if an non-secure request accesses a secure page</dd>
    2203     * </dl>
    2204     */
    2205    public void contributeComponentEventRequestHandler(OrderedConfiguration<ComponentEventRequestFilter> configuration,
    2206                                                       final RequestSecurityManager requestSecurityManager, @Ajax
    2207    ComponentEventRequestHandler ajaxHandler)
    2208    {
    2209        ComponentEventRequestFilter secureFilter = new ComponentEventRequestFilter()
    2210        {
    2211            public void handle(ComponentEventRequestParameters parameters, ComponentEventRequestHandler handler)
    2212                    throws IOException
    2213            {
    2214                if (requestSecurityManager.checkForInsecureComponentEventRequest(parameters))
    2215                    return;
    2216
    2217                handler.handle(parameters);
    2218            }
    2219        };
    2220        configuration.add("Secure", secureFilter);
    2221
    2222        configuration.add("Ajax", new AjaxFilter(request, ajaxHandler));
    2223    }
    2224
    2225    /**
    2226     * Contributes:
    2227     * <dl>
    2228     * <dt>AjaxFormUpdate</dt>
    2229     * <dd>{@link AjaxFormUpdateFilter}</dd>
    2230     * </dl>
    2231     *
    2232     * @since 5.2.0
    2233     */
    2234    public static void contributeAjaxComponentEventRequestHandler(
    2235            OrderedConfiguration<ComponentEventRequestFilter> configuration)
    2236    {
    2237        configuration.addInstance("AjaxFormUpdate", AjaxFormUpdateFilter.class);
    2238    }
    2239
    2240    /**
    2241     * Contributes strategies accessible via the {@link NullFieldStrategySource} service.
    2242     *
    2243     * <dl>
    2244     * <dt>default</dt>
    2245     * <dd>Does nothing, nulls stay null.</dd>
    2246     * <dt>zero</dt>
    2247     * <dd>Null values are converted to zero.</dd>
    2248     * </dl>
    2249     */
    2250    public static void contributeNullFieldStrategySource(MappedConfiguration<String, NullFieldStrategy> configuration)
    2251    {
    2252        configuration.add("default", new DefaultNullFieldStrategy());
    2253        configuration.add("zero", new ZeroNullFieldStrategy());
    2254    }
    2255
    2256    /**
    2257     * Determines positioning of hidden fields relative to other elements (this
    2258     * is needed by {@link org.apache.tapestry5.corelib.components.FormFragment} and others.
    2259     *
    2260     * For elements input, select, textarea and label the hidden field is positioned after.
    2261     *
    2262     * For elements p, div, li and td, the hidden field is positioned inside.
    2263     */
    2264    public static void contributeHiddenFieldLocationRules(
    2265            MappedConfiguration<String, RelativeElementPosition> configuration)
    2266    {
    2267        configuration.add("input", RelativeElementPosition.AFTER);
    2268        configuration.add("select", RelativeElementPosition.AFTER);
    2269        configuration.add("textarea", RelativeElementPosition.AFTER);
    2270        configuration.add("label", RelativeElementPosition.AFTER);
    2271
    2272        configuration.add("p", RelativeElementPosition.INSIDE);
    2273        configuration.add("div", RelativeElementPosition.INSIDE);
    2274        configuration.add("td", RelativeElementPosition.INSIDE);
    2275        configuration.add("li", RelativeElementPosition.INSIDE);
    2276    }
    2277
    2278    /**
    2279     * @since 5.1.0.0
    2280     */
    2281    public static LinkCreationHub buildLinkCreationHub(LinkSource source)
    2282    {
    2283        return source.getLinkCreationHub();
    2284    }
    2285
    2286    /**
    2287     * Exposes the public portion of the internal {@link InternalComponentInvalidationEventHub} service.
    2288     *
    2289     * @since 5.1.0.0
    2290     */
    2291    @Marker(ComponentClasses.class)
    2292    public static InvalidationEventHub buildComponentClassesInvalidationEventHub(
    2293            InternalComponentInvalidationEventHub trueHub)
    2294    {
    2295        return trueHub;
    2296    }
    2297
    2298    /**
    2299     * @since 5.1.0.0
    2300     */
    2301    @Marker(ComponentTemplates.class)
    2302    public static InvalidationEventHub buildComponentTemplatesInvalidationEventHub(
    2303            ComponentTemplateSource templateSource)
    2304    {
    2305        return templateSource.getInvalidationEventHub();
    2306    }
    2307
    2308    /**
    2309     * @since 5.1.0.0
    2310     */
    2311    @Marker(ComponentMessages.class)
    2312    public static InvalidationEventHub buildComponentMessagesInvalidationEventHub(ComponentMessagesSource messagesSource)
    2313    {
    2314        return messagesSource.getInvalidationEventHub();
    2315    }
    2316
    2317    @Scope(ScopeConstants.PERTHREAD)
    2318    public Environment buildEnvironment(PerthreadManager perthreadManager)
    2319    {
    2320        final EnvironmentImpl service = new EnvironmentImpl();
    2321
    2322        perthreadManager.addThreadCleanupCallback(new Runnable()
    2323        {
    2324            public void run()
    2325            {
    2326                service.threadDidCleanup();
    2327            }
    2328        });
    2329
    2330        return service;
    2331    }
    2332
    2333    /**
    2334     * The master SessionPersistedObjectAnalyzer.
    2335     *
    2336     * @since 5.1.0.0
    2337     */
    2338    @Marker(Primary.class)
    2339    public SessionPersistedObjectAnalyzer buildSessionPersistedObjectAnalyzer(
    2340            Map<Class, SessionPersistedObjectAnalyzer> configuration)
    2341    {
    2342        return strategyBuilder.build(SessionPersistedObjectAnalyzer.class, configuration);
    2343    }
    2344
    2345    /**
    2346     * Identifies String, Number and Boolean as immutable objects, a catch-all
    2347     * handler for Object (that understands
    2348     * the {@link org.apache.tapestry5.annotations.ImmutableSessionPersistedObject} annotation),
    2349     * and a handler for {@link org.apache.tapestry5.OptimizedSessionPersistedObject}.
    2350     *
    2351     * @since 5.1.0.0
    2352     */
    2353    public static void contributeSessionPersistedObjectAnalyzer(
    2354            MappedConfiguration<Class, SessionPersistedObjectAnalyzer> configuration)
    2355    {
    2356        configuration.add(Object.class, new DefaultSessionPersistedObjectAnalyzer());
    2357
    2358        SessionPersistedObjectAnalyzer<Object> immutable = new SessionPersistedObjectAnalyzer<Object>()
    2359        {
    2360            public boolean checkAndResetDirtyState(Object sessionPersistedObject)
    2361            {
    2362                return false;
    2363            }
    2364        };
    2365
    2366        configuration.add(String.class, immutable);
    2367        configuration.add(Number.class, immutable);
    2368        configuration.add(Boolean.class, immutable);
    2369
    2370        configuration.add(OptimizedSessionPersistedObject.class, new OptimizedSessionPersistedObjectAnalyzer());
    2371    }
    2372
    2373    /**
    2374     * @since 5.1.1.0
    2375     */
    2376    @Marker(Primary.class)
    2377    public StackTraceElementAnalyzer buildMasterStackTraceElementAnalyzer(List<StackTraceElementAnalyzer> configuration)
    2378    {
    2379        return chainBuilder.build(StackTraceElementAnalyzer.class, configuration);
    2380    }
    2381
    2382    /**
    2383     * Contributes:
    2384     * <dl>
    2385     * <dt>Application</dt>
    2386     * <dd>Checks for classes in the application package</dd>
    2387     * <dt>Proxies</dt>
    2388     * <dd>Checks for classes that appear to be generated proxies.</dd>
    2389     * <dt>SunReflect</dt>
    2390     * <dd>Checks for <code>sun.reflect</code> (which are omitted)
    2391     * <dt>TapestryAOP</dt>
    2392     * <dd>Omits stack frames for classes related to Tapestry AOP (such as advice, etc.)</dd>
    2393     * <dt>OperationTracker</dt>
    2394     * <dd>Omits stack frames related to {@link OperationTracker}</dd>
    2395     * <dt>Access</dt>
    2396     * <dd>Omits stack frames used to provide access to container class private members</dd>
    2397     * </dl>
    2398     *
    2399     * @since 5.1.0.0
    2400     */
    2401    public static void contributeMasterStackTraceElementAnalyzer(
    2402            OrderedConfiguration<StackTraceElementAnalyzer> configuration)
    2403    {
    2404        configuration.addInstance("TapestryAOP", TapestryAOPStackFrameAnalyzer.class);
    2405        configuration.add("Proxies", new ProxiesStackTraceElementAnalyzer());
    2406        configuration.add("Synthetic", new SyntheticStackTraceElementAnalyzer());
    2407        configuration.add("SunReflect", new PrefixCheckStackTraceElementAnalyzer(
    2408                StackTraceElementClassConstants.OMITTED, "sun.reflect."));
    2409        configuration.add("OperationTracker", new RegexpStackTraceElementAnalyzer(Pattern.compile("internal\\.(RegistryImpl|PerThreadOperationTracker|OperationTrackerImpl).*(run|invoke|perform)\\("), StackTraceElementClassConstants.OMITTED));
    2410        configuration.add("Access", new RegexpStackTraceElementAnalyzer(Pattern.compile("\\.access\\$\\d+\\("), StackTraceElementClassConstants.OMITTED));
    2411
    2412        configuration.addInstance("Application", ApplicationStackTraceElementAnalyzer.class);
    2413
    2414    }
    2415
    2416
    2417    /**
    2418     * Advises the {@link org.apache.tapestry5.services.messages.ComponentMessagesSource} service so
    2419     * that the creation
    2420     * of {@link org.apache.tapestry5.ioc.Messages} instances can be deferred.
    2421     *
    2422     * @since 5.1.0.0
    2423     */
    2424    @Match("ComponentMessagesSource")
    2425    public static void adviseLazy(LazyAdvisor advisor, MethodAdviceReceiver receiver)
    2426    {
    2427        advisor.addLazyMethodInvocationAdvice(receiver);
    2428    }
    2429
    2430    /**
    2431     * @since 5.1.0.0
    2432     */
    2433    public ComponentRequestHandler buildComponentRequestHandler(List<ComponentRequestFilter> configuration,
    2434
    2435                                                                @Autobuild
    2436                                                                ComponentRequestHandlerTerminator terminator,
    2437
    2438                                                                Logger logger)
    2439    {
    2440        return pipelineBuilder.build(logger, ComponentRequestHandler.class, ComponentRequestFilter.class,
    2441                configuration, terminator);
    2442    }
    2443
    2444    /**
    2445     * Contributes:
    2446     * <dl>
    2447     * <dt>OperationTracker</dt>
    2448     * <dd>Tracks general information about the request using {@link OperationTracker}</dd>
    2449     * <dt>UnknownComponentFilter (production mode only)</dt>
    2450     * <dd>{@link org.apache.tapestry5.internal.services.ProductionModeUnknownComponentFilter} - Detects request with unknown component and aborts handling to ultimately deliver a 404 response</dd>
    2451     * <dt>InitializeActivePageName
    2452     * <dd>{@link InitializeActivePageName}
    2453     * <dt>DeferredResponseRenderer</dt>
    2454     * <dd>{@link DeferredResponseRenderer}</dd>
    2455     * </dl>
    2456     *
    2457     * @since 5.2.0
    2458     */
    2459    public void contributeComponentRequestHandler(OrderedConfiguration<ComponentRequestFilter> configuration, @Symbol(SymbolConstants.PRODUCTION_MODE) boolean productionMode)
    2460    {
    2461        configuration.addInstance("OperationTracker", RequestOperationTracker.class);
    2462
    2463        if (productionMode)
    2464        {
    2465            configuration.addInstance("UnknownComponentFilter", ProductionModeUnknownComponentFilter.class);
    2466        }
    2467
    2468        configuration.addInstance("InitializeActivePageName", InitializeActivePageName.class);
    2469        configuration.addInstance("DeferredResponseRenderer", DeferredResponseRenderer.class);
    2470    }
    2471
    2472    /**
    2473     * Decorate FieldValidatorDefaultSource to setup the EnvironmentMessages
    2474     * object and place it in the environment.
    2475     * Although this could have been implemented directly in the default
    2476     * implementation of the service, doing it
    2477     * as service decoration ensures that the environment will be properly setup
    2478     * even if a user overrides the default
    2479     * service implementation.
    2480     *
    2481     * @param defaultSource
    2482     *         The service to decorate
    2483     * @param environment
    2484     */
    2485    public static FieldValidatorDefaultSource decorateFieldValidatorDefaultSource(
    2486            final FieldValidatorDefaultSource defaultSource, final Environment environment)
    2487    {
    2488        return new FieldValidatorDefaultSource()
    2489        {
    2490
    2491            public FieldValidator createDefaultValidator(Field field, String overrideId, Messages overrideMessages,
    2492                                                         Locale locale, Class propertyType, AnnotationProvider propertyAnnotations)
    2493            {
    2494                environment.push(EnvironmentMessages.class, new EnvironmentMessages(overrideMessages, overrideId));
    2495                FieldValidator fieldValidator = defaultSource.createDefaultValidator(field, overrideId,
    2496                        overrideMessages, locale, propertyType, propertyAnnotations);
    2497                environment.pop(EnvironmentMessages.class);
    2498                return fieldValidator;
    2499            }
    2500
    2501            public FieldValidator createDefaultValidator(ComponentResources resources, String parameterName)
    2502            {
    2503
    2504                EnvironmentMessages em = new EnvironmentMessages(resources.getContainerMessages(), resources.getId());
    2505                environment.push(EnvironmentMessages.class, em);
    2506                FieldValidator fieldValidator = defaultSource.createDefaultValidator(resources, parameterName);
    2507                environment.pop(EnvironmentMessages.class);
    2508                return fieldValidator;
    2509            }
    2510        };
    2511    }
    2512
    2513    /**
    2514     * Exposes the Environmental {@link Heartbeat} as an injectable service.
    2515     *
    2516     * @since 5.2.0
    2517     */
    2518    public Heartbeat buildHeartbeat()
    2519    {
    2520        return environmentalBuilder.build(Heartbeat.class);
    2521    }
    2522
    2523    public static ComponentMessagesSource buildComponentMessagesSource(UpdateListenerHub updateListenerHub, @Autobuild
    2524    ComponentMessagesSourceImpl service)
    2525    {
    2526        updateListenerHub.addUpdateListener(service);
    2527
    2528        return service;
    2529    }
    2530
    2531    /**
    2532     * Contributes extractors for {@link Meta}, {@link Secure}, {@link ContentType} and {@link WhitelistAccessOnly} annotations.
    2533     *
    2534     * @since 5.2.0
    2535     */
    2536    @SuppressWarnings("unchecked")
    2537    public static void contributeMetaWorker(MappedConfiguration<Class, MetaDataExtractor> configuration)
    2538    {
    2539        configuration.addInstance(Meta.class, MetaAnnotationExtractor.class);
    2540        configuration.add(Secure.class, new FixedExtractor(MetaDataConstants.SECURE_PAGE));
    2541        configuration.addInstance(ContentType.class, ContentTypeExtractor.class);
    2542        configuration.add(WhitelistAccessOnly.class, new FixedExtractor(MetaDataConstants.WHITELIST_ONLY_PAGE));
    2543        configuration.addInstance(UnknownActivationContextCheck.class, UnknownActivationContextExtractor.class);
    2544    }
    2545
    2546    /**
    2547     * Builds the {@link ComponentTemplateLocator} as a chain of command.
    2548     *
    2549     * @since 5.2.0
    2550     */
    2551    @Marker(Primary.class)
    2552    public ComponentTemplateLocator buildComponentTemplateLocator(List<ComponentTemplateLocator> configuration)
    2553    {
    2554        return chainBuilder.build(ComponentTemplateLocator.class, configuration);
    2555    }
    2556
    2557    /**
    2558     * Contributes two template locators:
    2559     * <dl>
    2560     * <dt>Default</dt>
    2561     * <dd>Searches for the template on the classpath ({@link DefaultTemplateLocator}</dd>
    2562     * <dt>Page</dt>
    2563     * <dd>Searches for <em>page</em> templates in the context ({@link PageTemplateLocator})</dd>
    2564     * </dl>
    2565     *
    2566     * @since 5.2.0
    2567     */
    2568    public static void contributeComponentTemplateLocator(OrderedConfiguration<ComponentTemplateLocator> configuration,
    2569                                                          @ContextProvider
    2570                                                          AssetFactory contextAssetFactory,
    2571                                                          @Symbol(SymbolConstants.APPLICATION_FOLDER) String applicationFolder,
    2572                                                          ComponentClassResolver componentClassResolver)
    2573    {
    2574        configuration.add("Default", new DefaultTemplateLocator());
    2575        configuration
    2576                .add("Page", new PageTemplateLocator(contextAssetFactory.getRootResource(), componentClassResolver, applicationFolder));
    2577
    2578    }
    2579
    2580    /**
    2581     * Builds {@link ComponentEventLinkTransformer} service as a chain of command.
    2582     *
    2583     * @since 5.2.0
    2584     */
    2585    @Marker(Primary.class)
    2586    public ComponentEventLinkTransformer buildComponentEventLinkTransformer(
    2587            List<ComponentEventLinkTransformer> configuration)
    2588    {
    2589        return chainBuilder.build(ComponentEventLinkTransformer.class, configuration);
    2590    }
    2591
    2592    /**
    2593     * Builds {@link PageRenderLinkTransformer} service as a chain of command.
    2594     *
    2595     * @since 5.2.0
    2596     */
    2597    @Marker(Primary.class)
    2598    public PageRenderLinkTransformer buildPageRenderLinkTransformer(List<PageRenderLinkTransformer> configuration)
    2599    {
    2600        return chainBuilder.build(PageRenderLinkTransformer.class, configuration);
    2601    }
    2602
    2603    /**
    2604     * Provides the "LinkTransformer" interceptor for the {@link ComponentEventLinkEncoder} service.
    2605     * Other decorations
    2606     * should come after LinkTransformer.
    2607     *
    2608     * @since 5.2.0
    2609     */
    2610    @Match("ComponentEventLinkEncoder")
    2611    public ComponentEventLinkEncoder decorateLinkTransformer(LinkTransformer linkTransformer,
    2612                                                             ComponentEventLinkEncoder delegate)
    2613    {
    2614        return new LinkTransformerInterceptor(linkTransformer, delegate);
    2615    }
    2616
    2617    /**
    2618     * In production mode, override {@link UpdateListenerHub} to be an empty placeholder.
    2619     */
    2620    @Contribute(ServiceOverride.class)
    2621    public static void productionModeOverrides(MappedConfiguration<Class, Object> configuration,
    2622                                               @Symbol(SymbolConstants.PRODUCTION_MODE)
    2623                                               boolean productionMode)
    2624    {
    2625        if (productionMode)
    2626        {
    2627            configuration.add(UpdateListenerHub.class, new UpdateListenerHub()
    2628            {
    2629                public void fireCheckForUpdates()
    2630                {
    2631                }
    2632
    2633                public void addUpdateListener(UpdateListener listener)
    2634                {
    2635
    2636                }
    2637            });
    2638        }
    2639    }
    2640
    2641    /**
    2642     * Contributes a single default analyzer:
    2643     * <dl>
    2644     * <dt>LocalhostOnly</dt>
    2645     * <dd>Identifies requests from localhost as on client whitelist</dd>
    2646     * </dl>
    2647     *
    2648     * @since 5.3
    2649     */
    2650    @Contribute(ClientWhitelist.class)
    2651    public static void defaultWhitelist(OrderedConfiguration<WhitelistAnalyzer> configuration)
    2652    {
    2653        configuration.add("LocalhostOnly", new LocalhostOnly());
    2654    }
    2655
    2656    @Startup
    2657    public static void registerToClearPlasticProxyFactoryOnInvalidation(@ComponentClasses InvalidationEventHub hub, @Builtin final PlasticProxyFactory proxyFactory)
    2658    {
    2659        hub.addInvalidationCallback(new Runnable()
    2660        {
    2661            public void run()
    2662            {
    2663                proxyFactory.clearCache();
    2664            }
    2665        });
    2666    }
    2667
    2668    /**
    2669     * @since 5.4
    2670     */
    2671    @Contribute(ValueLabelProvider.class)
    2672    public void defaultValueLabelProviders(MappedConfiguration<Class, ValueLabelProvider> configuration)
    2673    {
    2674        configuration.addInstance(Object.class, DefaultValueLabelProvider.class);
    2675        configuration.addInstance(Enum.class, EnumValueLabelProvider.class);
    2676    }
    2677
    2678    /**
    2679     * @since 5.4
    2680     */
    2681    public ValueLabelProvider<?> buildValueLabelProvider(Map<Class, ValueLabelProvider> configuration)
    2682    {
    2683        return strategyBuilder.build(ValueLabelProvider.class, configuration);
    2684    }
    2685
    2686    @Advise(serviceInterface = ComponentInstantiatorSource.class)
    2687    public static void componentReplacer(MethodAdviceReceiver methodAdviceReceiver,
    2688                                         final ComponentOverride componentReplacer) throws NoSuchMethodException, SecurityException
    2689    {
    2690
    2691        if (componentReplacer.hasReplacements())
    2692        {
    2693
    2694            MethodAdvice advice = new MethodAdvice()
    2695            {
    2696                @Override
    2697                public void advise(MethodInvocation invocation)
    2698                {
    2699                    String className = (String) invocation.getParameter(0);
    2700                    final Class<?> replacement = componentReplacer.getReplacement(className);
    2701                    if (replacement != null)
    2702                    {
    2703                        invocation.setParameter(0, replacement.getName());
    2704                    }
    2705                    invocation.proceed();
    2706                }
    2707            };
    2708
    2709            methodAdviceReceiver.adviseMethod(
    2710                    ComponentInstantiatorSource.class.getMethod("getInstantiator", String.class), advice);
    2711
    2712        }
    2713    }
    2714
    2715    public static ComponentLibraryInfoSource buildComponentLibraryInfoSource(List<ComponentLibraryInfoSource> configuration,
    2716                                                                             ChainBuilder chainBuilder)
    2717    {
    2718        return chainBuilder.build(ComponentLibraryInfoSource.class, configuration);
    2719    }
    2720
    2721    @Contribute(ComponentLibraryInfoSource.class)
    2722    public static void addBuiltInComponentLibraryInfoSources(OrderedConfiguration<ComponentLibraryInfoSource> configuration)
    2723    {
    2724        configuration.addInstance("Maven", MavenComponentLibraryInfoSource.class);
    2725        configuration.add("TapestryCore", new TapestryCoreComponentLibraryInfoSource());
    2726    }
    2727
    2728    private static final class TapestryCoreComponentLibraryInfoSource implements
    2729            ComponentLibraryInfoSource
    2730    {
    2731        @Override
    2732        public ComponentLibraryInfo find(LibraryMapping libraryMapping)
    2733        {
    2734            ComponentLibraryInfo info = null;
    2735            if (libraryMapping.libraryName.equals("core"))
    2736            {
    2737
    2738                info = new ComponentLibraryInfo();
    2739
    2740                // the information above will probably not change in the future, or change very
    2741                // infrequently, so I see no problem in hardwiring them here.
    2742                info.setArtifactId("tapestry-core");
    2743                info.setGroupId("org.apache.tapestry");
    2744                info.setName("Tapestry 5 core component library");
    2745                info.setDescription("Components provided out-of-the-box by Tapestry");
    2746                info.setDocumentationUrl("http://tapestry.apache.org/component-reference.html");
    2747                info.setJavadocUrl("http://tapestry.apache.org/current/apidocs/");
    2748                info.setSourceBrowseUrl("https://git-wip-us.apache.org/repos/asf?p=tapestry-5.git;a=summary");
    2749                info.setSourceRootUrl("https://git-wip-us.apache.org/repos/asf?p=tapestry-5.git;a=blob;f=tapestry-core/src/main/java/");
    2750                info.setIssueTrackerUrl("https://issues.apache.org/jira/browse/TAP5");
    2751                info.setHomepageUrl("http://tapestry.apache.org");
    2752                info.setLibraryMapping(libraryMapping);
    2753
    2754                final InputStream inputStream = TapestryModule.class.getResourceAsStream(
    2755                        "/META-INF/gradle/org.apache.tapestry/tapestry-core/project.properties");
    2756
    2757                if (inputStream != null)
    2758                {
    2759                    Properties properties = new Properties();
    2760                    try
    2761                    {
    2762                        properties.load(inputStream);
    2763                    } catch (IOException e)
    2764                    {
    2765                        throw new RuntimeException(e);
    2766                    }
    2767                    info.setVersion(properties.getProperty("version"));
    2768                }
    2769            }
    2770            return info;
    2771        }
    2772    }
    2773
    2774}