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