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    }