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