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                @Override
1443                public void objectWasInvalidated()
1444                {
1445                    registry.clearCache();
1446                }
1447            });
1448    
1449    
1450            return strategyBuilder.build(registry);
1451        }
1452    
1453        /**
1454         * The default data type analyzer is the final analyzer consulted and
1455         * identifies the type entirely pased on the
1456         * property type, working against its own configuration (mapping property
1457         * type class to data type).
1458         */
1459        public static DataTypeAnalyzer buildDefaultDataTypeAnalyzer(@Autobuild
1460                                                                    DefaultDataTypeAnalyzer service, @ComponentClasses
1461        InvalidationEventHub hub)
1462        {
1463            hub.addInvalidationListener(service);
1464    
1465            return service;
1466        }
1467    
1468        public static TranslatorSource buildTranslatorSource(Map<Class, Translator> configuration,
1469                                                             TranslatorAlternatesSource alternatesSource, @ComponentClasses
1470        InvalidationEventHub hub)
1471        {
1472            TranslatorSourceImpl service = new TranslatorSourceImpl(configuration,
1473                    alternatesSource.getTranslatorAlternates());
1474    
1475            hub.addInvalidationListener(service);
1476    
1477            return service;
1478        }
1479    
1480        @Marker(Primary.class)
1481        public ObjectRenderer buildObjectRenderer(Map<Class, ObjectRenderer> configuration)
1482        {
1483            return strategyBuilder.build(ObjectRenderer.class, configuration);
1484        }
1485    
1486        /**
1487         * Returns a {@link org.apache.tapestry5.ioc.services.ClassFactory} that can
1488         * be used to create extra classes around
1489         * component classes. This ClassFactory will be cleared whenever an
1490         * underlying component class is discovered to have
1491         * changed. Use of this class factory implies that your code will become
1492         * aware of this (if necessary) to discard any
1493         * cached object (alas, this currently involves dipping into the internals
1494         * side to register for the correct
1495         * notifications). Failure to properly clean up can result in really nasty
1496         * PermGen space memory leaks.
1497         */
1498        @Marker(ComponentLayer.class)
1499        public ClassFactory buildComponentClassFactory(ComponentInstantiatorSource source)
1500        {
1501            return shadowBuilder.build(source, "classFactory", ClassFactory.class);
1502        }
1503    
1504        /**
1505         * Returns a {@link PlasticProxyFactory} that can be used to create extra classes around component classes. This
1506         * factory will be cleared whenever an underlying component class is discovered to have changed. Use of this
1507         * factory implies that your code will become aware of this (if necessary) to discard any cached object (alas,
1508         * this currently involves dipping into the internals side to register for the correct notifications). Failure to
1509         * properly clean up can result in really nasty PermGen space memory leaks.
1510         */
1511        @Marker(ComponentLayer.class)
1512        public PlasticProxyFactory buildComponentProxyFactory(ComponentInstantiatorSource source)
1513        {
1514            return shadowBuilder.build(source, "proxyFactory", PlasticProxyFactory.class);
1515        }
1516    
1517        /**
1518         * Ordered contributions to the MasterDispatcher service allow different URL
1519         * matching strategies to occur.
1520         */
1521        @Marker(Primary.class)
1522        public Dispatcher buildMasterDispatcher(List<Dispatcher> configuration)
1523        {
1524            return chainBuilder.build(Dispatcher.class, configuration);
1525        }
1526    
1527        /**
1528         * Builds a shadow of the RequestGlobals.request property. Note again that
1529         * the shadow can be an ordinary singleton,
1530         * even though RequestGlobals is perthread.
1531         */
1532        public Request buildRequest()
1533        {
1534            return shadowBuilder.build(requestGlobals, "request", Request.class);
1535        }
1536    
1537        /**
1538         * Builds a shadow of the RequestGlobals.HTTPServletRequest property.
1539         * Generally, you should inject the {@link Request} service instead, as
1540         * future version of Tapestry may operate beyond just the servlet API.
1541         */
1542        public HttpServletRequest buildHttpServletRequest()
1543        {
1544            return shadowBuilder.build(requestGlobals, "HTTPServletRequest", HttpServletRequest.class);
1545        }
1546    
1547        /**
1548         * @since 5.1.0.0
1549         */
1550        public HttpServletResponse buildHttpServletResponse()
1551        {
1552            return shadowBuilder.build(requestGlobals, "HTTPServletResponse", HttpServletResponse.class);
1553        }
1554    
1555        /**
1556         * Builds a shadow of the RequestGlobals.response property. Note again that
1557         * the shadow can be an ordinary singleton,
1558         * even though RequestGlobals is perthread.
1559         */
1560        public Response buildResponse()
1561        {
1562            return shadowBuilder.build(requestGlobals, "response", Response.class);
1563        }
1564    
1565        /**
1566         * The MarkupRenderer service is used to render a full page as markup.
1567         * Supports an ordered configuration of {@link org.apache.tapestry5.services.MarkupRendererFilter}s.
1568         */
1569        public MarkupRenderer buildMarkupRenderer(Logger logger, @Autobuild
1570        MarkupRendererTerminator terminator, List<MarkupRendererFilter> configuration)
1571        {
1572            return pipelineBuilder.build(logger, MarkupRenderer.class, MarkupRendererFilter.class, configuration,
1573                    terminator);
1574        }
1575    
1576        /**
1577         * A wrapper around {@link org.apache.tapestry5.internal.services.PageRenderQueue} used for
1578         * partial page renders.
1579         * Supports an ordered configuration of {@link org.apache.tapestry5.services.PartialMarkupRendererFilter}s.
1580         *
1581         * @see #contributePartialMarkupRenderer
1582         */
1583        public PartialMarkupRenderer buildPartialMarkupRenderer(Logger logger,
1584                                                                List<PartialMarkupRendererFilter> configuration, @Autobuild
1585        PartialMarkupRendererTerminator terminator)
1586        {
1587    
1588            return pipelineBuilder.build(logger, PartialMarkupRenderer.class, PartialMarkupRendererFilter.class,
1589                    configuration, terminator);
1590        }
1591    
1592        public PageRenderRequestHandler buildPageRenderRequestHandler(List<PageRenderRequestFilter> configuration,
1593                                                                      Logger logger, @Autobuild
1594        PageRenderRequestHandlerImpl terminator)
1595        {
1596            return pipelineBuilder.build(logger, PageRenderRequestHandler.class, PageRenderRequestFilter.class,
1597                    configuration, terminator);
1598        }
1599    
1600        /**
1601         * Builds the component action request handler for traditional (non-Ajax)
1602         * requests. These typically result in a
1603         * redirect to a Tapestry render URL.
1604         */
1605        @Marker(
1606                {Traditional.class, Primary.class})
1607        public ComponentEventRequestHandler buildComponentEventRequestHandler(
1608                List<ComponentEventRequestFilter> configuration, Logger logger, @Autobuild
1609        ComponentEventRequestHandlerImpl terminator)
1610        {
1611            return pipelineBuilder.build(logger, ComponentEventRequestHandler.class, ComponentEventRequestFilter.class,
1612                    configuration, terminator);
1613        }
1614    
1615        /**
1616         * Builds the action request handler for Ajax requests, based on a
1617         * {@linkplain org.apache.tapestry5.ioc.services.PipelineBuilder
1618         * pipeline} around {@link org.apache.tapestry5.internal.services.AjaxComponentEventRequestHandler} . Filters on
1619         * the
1620         * request handler are supported here as well.
1621         */
1622        @Marker(
1623                {Ajax.class, Primary.class})
1624        public ComponentEventRequestHandler buildAjaxComponentEventRequestHandler(
1625                List<ComponentEventRequestFilter> configuration, Logger logger, @Autobuild
1626        AjaxComponentEventRequestHandler terminator)
1627        {
1628            return pipelineBuilder.build(logger, ComponentEventRequestHandler.class, ComponentEventRequestFilter.class,
1629                    configuration, terminator);
1630        }
1631    
1632        // ========================================================================
1633        //
1634        // Service Contribution Methods (instance)
1635        //
1636        // ========================================================================
1637    
1638        /**
1639         * Contributes the default "session" strategy.
1640         */
1641        public void contributeApplicationStatePersistenceStrategySource(
1642                MappedConfiguration<String, ApplicationStatePersistenceStrategy> configuration,
1643    
1644                @Local
1645                ApplicationStatePersistenceStrategy sessionStategy)
1646        {
1647            configuration.add("session", sessionStategy);
1648        }
1649    
1650        public void contributeAssetSource(MappedConfiguration<String, AssetFactory> configuration, @ContextProvider
1651        AssetFactory contextAssetFactory,
1652    
1653                                          @ClasspathProvider
1654                                          AssetFactory classpathAssetFactory)
1655        {
1656            configuration.add(AssetConstants.CONTEXT, contextAssetFactory);
1657            configuration.add(AssetConstants.CLASSPATH, classpathAssetFactory);
1658        }
1659    
1660        /**
1661         * Contributes handlers for the following types:
1662         * <dl>
1663         * <dt>Object</dt>
1664         * <dd>Failure case, added to provide a more useful exception message</dd>
1665         * <dt>{@link Link}</dt>
1666         * <dd>Sends a redirect to the link (which is typically a page render link)</dd>
1667         * <dt>String</dt>
1668         * <dd>Sends a page render redirect</dd>
1669         * <dt>Class</dt>
1670         * <dd>Interpreted as the class name of a page, sends a page render render redirect (this is more refactoring safe
1671         * than the page name)</dd>
1672         * <dt>{@link Component}</dt>
1673         * <dd>A page's root component (though a non-root component will work, but will generate a warning). A direct to the
1674         * containing page is sent.</dd>
1675         * <dt>{@link org.apache.tapestry5.StreamResponse}</dt>
1676         * <dd>The stream response is sent as the actual reply.</dd>
1677         * <dt>URL</dt>
1678         * <dd>Sends a redirect to a (presumably) external URL</dd>
1679         * </dl>
1680         */
1681        public void contributeComponentEventResultProcessor(@Traditional
1682                                                            @ComponentInstanceProcessor
1683                                                            ComponentEventResultProcessor componentInstanceProcessor,
1684    
1685                                                            MappedConfiguration<Class, ComponentEventResultProcessor> configuration)
1686        {
1687            configuration.add(Link.class, new ComponentEventResultProcessor<Link>()
1688            {
1689                public void processResultValue(Link value) throws IOException
1690                {
1691                    response.sendRedirect(value);
1692                }
1693            });
1694    
1695            configuration.add(URL.class, new ComponentEventResultProcessor<URL>()
1696            {
1697                public void processResultValue(URL value) throws IOException
1698                {
1699                    response.sendRedirect(value.toExternalForm());
1700                }
1701            });
1702    
1703            configuration.addInstance(HttpError.class, HttpErrorComponentEventResultProcessor.class);
1704    
1705            configuration.addInstance(String.class, PageNameComponentEventResultProcessor.class);
1706    
1707            configuration.addInstance(Class.class, ClassResultProcessor.class);
1708    
1709            configuration.add(Component.class, componentInstanceProcessor);
1710    
1711            configuration.addInstance(StreamResponse.class, StreamResponseResultProcessor.class);
1712    
1713            configuration.addInstance(StreamPageContent.class, StreamPageContentResultProcessor.class);
1714        }
1715    
1716        /**
1717         * Contributes handlers for the following types:
1718         * <dl>
1719         * <dt>Object</dt>
1720         * <dd>Failure case, added to provide more useful exception message</dd>
1721         * <dt>{@link RenderCommand}</dt>
1722         * <dd>Typically, a {@link org.apache.tapestry5.Block}</dd>
1723         * <dt>{@link org.apache.tapestry5.annotations.Component}</dt>
1724         * <dd>Renders the component and its body (unless its a page, in which case a redirect JSON response is sent)</dd>
1725         * <dt>{@link org.apache.tapestry5.json.JSONObject} or {@link org.apache.tapestry5.json.JSONArray}</dt>
1726         * <dd>The JSONObject is returned as a text/javascript response</dd>
1727         * <dt>{@link org.apache.tapestry5.StreamResponse}</dt>
1728         * <dd>The stream response is sent as the actual response</dd>
1729         * <dt>String</dt>
1730         * <dd>Interprets the value as a logical page name and sends a client response to redirect to that page</dd>
1731         * <dt>{@link org.apache.tapestry5.Link}</dt>
1732         * <dd>Sends a JSON response to redirect to the link</dd>
1733         * <dt>{@link Class}</dt>
1734         * <dd>Treats the class as a page class and sends a redirect for a page render for that page</dd>
1735         * <dt>{@link org.apache.tapestry5.ajax.MultiZoneUpdate}</dt>
1736         * <dd>Sends a single JSON response to update the content of multiple zones
1737         * </dl>
1738         * <p/>
1739         * In most cases, when you want to support a new type, you should convert it to one of the built-in supported types
1740         * (such as {@link RenderCommand}. You can then inject the master AjaxComponentEventResultProcessor (use the
1741         * {@link Ajax} marker annotation) and delegate to it.
1742         */
1743        @Contribute(ComponentEventResultProcessor.class)
1744        @Ajax
1745        public static void provideBaseAjaxComponentEventResultProcessors(
1746                MappedConfiguration<Class, ComponentEventResultProcessor> configuration)
1747        {
1748            configuration.addInstance(RenderCommand.class, RenderCommandComponentEventResultProcessor.class);
1749            configuration.addInstance(Component.class, AjaxComponentInstanceEventResultProcessor.class);
1750            configuration.addInstance(JSONObject.class, JSONObjectEventResultProcessor.class);
1751            configuration.addInstance(JSONArray.class, JSONArrayEventResultProcessor.class);
1752            configuration.addInstance(StreamResponse.class, StreamResponseResultProcessor.class);
1753            configuration.addInstance(String.class, AjaxPageNameComponentEventResultProcessor.class);
1754            configuration.addInstance(Link.class, AjaxLinkComponentEventResultProcessor.class);
1755            configuration.addInstance(Class.class, AjaxPageClassComponentEventResultProcessor.class);
1756            configuration.addInstance(MultiZoneUpdate.class, MultiZoneUpdateEventResultProcessor.class);
1757            configuration.addInstance(HttpError.class, HttpErrorComponentEventResultProcessor.class);
1758        }
1759    
1760        /**
1761         * The MasterDispatcher is a chain-of-command of individual Dispatchers,
1762         * each handling (like a servlet) a particular
1763         * kind of incoming request.
1764         * <dl>
1765         * <dt>RootPath</dt>
1766         * <dd>Renders the start page for the "/" request (outdated)</dd>
1767         * <dt>Asset</dt>
1768         * <dd>Provides access to assets (context, classpath and virtual) via {@link AssetDispatcher}</dd>
1769         * <dt>PageRender</dt>
1770         * <dd>Identifies the {@link org.apache.tapestry5.services.PageRenderRequestParameters} and forwards onto
1771         * {@link PageRenderRequestHandler}</dd>
1772         * <dt>ComponentEvent</dt>
1773         * <dd>Identifies the {@link ComponentEventRequestParameters} and forwards onto the
1774         * {@link ComponentEventRequestHandler}</dd>
1775         * </dl>
1776         */
1777        public static void contributeMasterDispatcher(OrderedConfiguration<Dispatcher> configuration,
1778    
1779                                                      @InjectService("AssetDispatcher")
1780                                                      Dispatcher assetDispatcher)
1781        {
1782            // Looks for the root path and renders the start page. This is
1783            // maintained for compatibility
1784            // with earlier versions of Tapestry 5, it is recommended that an Index
1785            // page be used instead.
1786    
1787            configuration.addInstance("RootPath", RootPathDispatcher.class, "before:Asset");
1788    
1789            // This goes first because an asset to be streamed may have an file
1790            // extension, such as
1791            // ".html", that will confuse the later dispatchers.
1792    
1793            configuration.add("Asset", assetDispatcher, "before:ComponentEvent");
1794    
1795            configuration.addInstance("ComponentEvent", ComponentEventDispatcher.class, "before:PageRender");
1796    
1797            configuration.addInstance("PageRender", PageRenderDispatcher.class);
1798        }
1799    
1800        /**
1801         * Contributes a default object renderer for type Object, plus specialized
1802         * renderers for {@link org.apache.tapestry5.services.Request}, {@link org.apache.tapestry5.ioc.Location},
1803         * {@link org.apache.tapestry5.ComponentResources}, {@link org.apache.tapestry5.EventContext},
1804         * {@link AvailableValues},
1805         * List, and Object[].
1806         */
1807        @SuppressWarnings("unchecked")
1808        public void contributeObjectRenderer(MappedConfiguration<Class, ObjectRenderer> configuration,
1809    
1810                                             @InjectService("LocationRenderer")
1811                                             ObjectRenderer locationRenderer,
1812    
1813                                             final TypeCoercer typeCoercer)
1814        {
1815            configuration.add(Object.class, new DefaultObjectRenderer());
1816    
1817            configuration.addInstance(Request.class, RequestRenderer.class);
1818    
1819            configuration.add(Location.class, locationRenderer);
1820    
1821            ObjectRenderer preformatted = new ObjectRenderer<Object>()
1822            {
1823                public void render(Object object, MarkupWriter writer)
1824                {
1825                    writer.element("pre");
1826                    writer.write(typeCoercer.coerce(object, String.class));
1827                    writer.end();
1828                }
1829            };
1830    
1831            configuration.add(ClassTransformation.class, preformatted);
1832    
1833            configuration.addInstance(List.class, ListRenderer.class);
1834            configuration.addInstance(Object[].class, ObjectArrayRenderer.class);
1835            configuration.addInstance(ComponentResources.class, ComponentResourcesRenderer.class);
1836            configuration.addInstance(EventContext.class, EventContextRenderer.class);
1837            configuration.add(AvailableValues.class, new AvailableValuesRenderer());
1838        }
1839    
1840        /**
1841         * Adds page render filters, each of which provides an {@link org.apache.tapestry5.annotations.Environmental}
1842         * service. Filters
1843         * often provide {@link org.apache.tapestry5.annotations.Environmental} services needed by
1844         * components as they render.
1845         * <dl>
1846         * <dt>DocumentLinker</dt>
1847         * <dd>Provides {@link org.apache.tapestry5.internal.services.DocumentLinker}</dd>
1848         * <dt>JavascriptSupport</dt>
1849         * <dd>Provides {@link JavaScriptSupport}</dd>
1850         * <dt>RenderSupport</dt>
1851         * <dd>Provides {@link org.apache.tapestry5.RenderSupport}</dd>
1852         * <dt>InjectDefaultStylesheet</dt>
1853         * <dd>Injects the default stylesheet into all pages</dd></dt>
1854         * <dt>ClientBehaviorSupport</dt>
1855         * <dd>Provides {@link ClientBehaviorSupport}</dd>
1856         * <dt>Heartbeat</dt>
1857         * <dd>Provides {@link org.apache.tapestry5.services.Heartbeat}</dd>
1858         * <dt>ValidationDecorator</dt>
1859         * <dd>Provides {@link org.apache.tapestry5.ValidationDecorator} (via {@link ValidationDecoratorFactory#newInstance(org.apache.tapestry5.MarkupWriter)})</dd>
1860         * </dl>
1861         */
1862        public void contributeMarkupRenderer(OrderedConfiguration<MarkupRendererFilter> configuration,
1863    
1864                                             @Symbol(SymbolConstants.OMIT_GENERATOR_META)
1865                                             final boolean omitGeneratorMeta,
1866    
1867                                             @Symbol(SymbolConstants.TAPESTRY_VERSION)
1868                                             final String tapestryVersion,
1869    
1870                                             @Symbol(SymbolConstants.COMPACT_JSON)
1871                                             final boolean compactJSON,
1872    
1873                                             final SymbolSource symbolSource,
1874    
1875                                             final AssetSource assetSource,
1876    
1877                                             final JavaScriptStackSource javascriptStackSource,
1878    
1879                                             final JavaScriptStackPathConstructor javascriptStackPathConstructor,
1880    
1881                                             final ValidationDecoratorFactory validationDecoratorFactory,
1882    
1883                                             @Path("${tapestry.default-stylesheet}")
1884                                             final Asset defaultStylesheet)
1885        {
1886            MarkupRendererFilter documentLinker = new MarkupRendererFilter()
1887            {
1888                public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1889                {
1890                    DocumentLinkerImpl linker = new DocumentLinkerImpl(omitGeneratorMeta, tapestryVersion, compactJSON);
1891    
1892                    environment.push(DocumentLinker.class, linker);
1893    
1894                    renderer.renderMarkup(writer);
1895    
1896                    environment.pop(DocumentLinker.class);
1897    
1898                    linker.updateDocument(writer.getDocument());
1899                }
1900            };
1901    
1902            MarkupRendererFilter javaScriptSupport = new MarkupRendererFilter()
1903            {
1904                public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1905                {
1906                    DocumentLinker linker = environment.peekRequired(DocumentLinker.class);
1907    
1908                    JavaScriptSupportImpl support = new JavaScriptSupportImpl(linker, javascriptStackSource,
1909                            javascriptStackPathConstructor);
1910    
1911                    environment.push(JavaScriptSupport.class, support);
1912    
1913                    renderer.renderMarkup(writer);
1914    
1915                    environment.pop(JavaScriptSupport.class);
1916    
1917                    support.commit();
1918                }
1919            };
1920    
1921            MarkupRendererFilter renderSupport = new MarkupRendererFilter()
1922            {
1923                public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1924                {
1925                    JavaScriptSupport javascriptSupport = environment.peekRequired(JavaScriptSupport.class);
1926    
1927                    RenderSupportImpl support = new RenderSupportImpl(symbolSource, assetSource, javascriptSupport);
1928    
1929                    environment.push(RenderSupport.class, support);
1930    
1931                    renderer.renderMarkup(writer);
1932    
1933                    environment.pop(RenderSupport.class);
1934                }
1935            };
1936    
1937            MarkupRendererFilter injectDefaultStylesheet = new MarkupRendererFilter()
1938            {
1939                public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1940                {
1941                    DocumentLinker linker = environment.peekRequired(DocumentLinker.class);
1942    
1943                    linker.addStylesheetLink(new StylesheetLink(defaultStylesheet.toClientURL()));
1944    
1945                    renderer.renderMarkup(writer);
1946                }
1947            };
1948    
1949            MarkupRendererFilter clientBehaviorSupport = new MarkupRendererFilter()
1950            {
1951                public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1952                {
1953                    JavaScriptSupport javascriptSupport = environment.peekRequired(JavaScriptSupport.class);
1954    
1955                    ClientBehaviorSupportImpl clientBehaviorSupport = new ClientBehaviorSupportImpl(javascriptSupport,
1956                            environment);
1957    
1958                    environment.push(ClientBehaviorSupport.class, clientBehaviorSupport);
1959    
1960                    renderer.renderMarkup(writer);
1961    
1962                    environment.pop(ClientBehaviorSupport.class);
1963    
1964                    clientBehaviorSupport.commit();
1965                }
1966            };
1967    
1968            MarkupRendererFilter heartbeat = new MarkupRendererFilter()
1969            {
1970                public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1971                {
1972                    Heartbeat heartbeat = new HeartbeatImpl();
1973    
1974                    heartbeat.begin();
1975    
1976                    environment.push(Heartbeat.class, heartbeat);
1977    
1978                    renderer.renderMarkup(writer);
1979    
1980                    environment.pop(Heartbeat.class);
1981    
1982                    heartbeat.end();
1983                }
1984            };
1985    
1986            MarkupRendererFilter defaultValidationDecorator = new MarkupRendererFilter()
1987            {
1988                public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1989                {
1990                    ValidationDecorator decorator = validationDecoratorFactory.newInstance(writer);
1991    
1992                    environment.push(ValidationDecorator.class, decorator);
1993    
1994                    renderer.renderMarkup(writer);
1995    
1996                    environment.pop(ValidationDecorator.class);
1997                }
1998            };
1999    
2000            configuration.add("DocumentLinker", documentLinker);
2001            configuration.add("JavaScriptSupport", javaScriptSupport);
2002            configuration.add("RenderSupport", renderSupport);
2003            configuration.add("InjectDefaultStylesheet", injectDefaultStylesheet);
2004            configuration.add("ClientBehaviorSupport", clientBehaviorSupport);
2005            configuration.add("Heartbeat", heartbeat);
2006            configuration.add("ValidationDecorator", defaultValidationDecorator);
2007        }
2008    
2009        /**
2010         * Contributes {@link PartialMarkupRendererFilter}s used when rendering a
2011         * partial Ajax response.
2012         * <dl>
2013         * <dt>DocumentLinker
2014         * <dd>Provides {@link org.apache.tapestry5.internal.services.DocumentLinker}
2015         * <dt>JavaScriptSupport
2016         * <dd>Provides {@link JavaScriptSupport}</dd>
2017         * <dt>PageRenderSupport</dt>
2018         * <dd>Provides {@link org.apache.tapestry5.RenderSupport}</dd>
2019         * <dt>ClientBehaviorSupport</dt>
2020         * <dd>Provides {@link ClientBehaviorSupport}</dd>
2021         * <dt>Heartbeat</dt>
2022         * <dd>Provides {@link org.apache.tapestry5.services.Heartbeat}</dd>
2023         * <dt>DefaultValidationDecorator</dt>
2024         * <dt>ValidationDecorator</dt>
2025         * <dd>Provides {@link org.apache.tapestry5.ValidationDecorator} (via {@link ValidationDecoratorFactory#newInstance(org.apache.tapestry5.MarkupWriter)})</dd>
2026         * </dl>
2027         */
2028        public void contributePartialMarkupRenderer(OrderedConfiguration<PartialMarkupRendererFilter> configuration,
2029    
2030                                                    final ValidationDecoratorFactory validationDecoratorFactory,
2031    
2032                                                    final JavaScriptStackSource javascriptStackSource,
2033    
2034                                                    final JavaScriptStackPathConstructor javascriptStackPathConstructor,
2035    
2036                                                    final SymbolSource symbolSource,
2037    
2038                                                    final AssetSource assetSource)
2039        {
2040            PartialMarkupRendererFilter documentLinker = new PartialMarkupRendererFilter()
2041            {
2042                public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2043                {
2044                    PartialMarkupDocumentLinker linker = new PartialMarkupDocumentLinker();
2045    
2046                    environment.push(DocumentLinker.class, linker);
2047    
2048                    renderer.renderMarkup(writer, reply);
2049    
2050                    environment.pop(DocumentLinker.class);
2051    
2052                    linker.commit(reply);
2053                }
2054            };
2055    
2056            PartialMarkupRendererFilter javascriptSupport = new PartialMarkupRendererFilter()
2057            {
2058                public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2059                {
2060                    String uid = Long.toHexString(System.nanoTime());
2061    
2062                    String namespace = "_" + uid;
2063    
2064                    IdAllocator idAllocator = new IdAllocator(namespace);
2065    
2066                    DocumentLinker linker = environment.peekRequired(DocumentLinker.class);
2067    
2068                    JavaScriptSupportImpl support = new JavaScriptSupportImpl(linker, javascriptStackSource,
2069                            javascriptStackPathConstructor, idAllocator, true);
2070    
2071                    environment.push(JavaScriptSupport.class, support);
2072    
2073                    renderer.renderMarkup(writer, reply);
2074    
2075                    environment.pop(JavaScriptSupport.class);
2076    
2077                    support.commit();
2078                }
2079            };
2080    
2081            PartialMarkupRendererFilter renderSupport = new PartialMarkupRendererFilter()
2082            {
2083                public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2084                {
2085                    JavaScriptSupport javascriptSupport = environment.peekRequired(JavaScriptSupport.class);
2086    
2087                    RenderSupportImpl support = new RenderSupportImpl(symbolSource, assetSource, javascriptSupport);
2088    
2089                    environment.push(RenderSupport.class, support);
2090    
2091                    renderer.renderMarkup(writer, reply);
2092    
2093                    environment.pop(RenderSupport.class);
2094                }
2095            };
2096    
2097            PartialMarkupRendererFilter clientBehaviorSupport = new PartialMarkupRendererFilter()
2098            {
2099                public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2100                {
2101                    JavaScriptSupport javascriptSupport = environment.peekRequired(JavaScriptSupport.class);
2102    
2103                    ClientBehaviorSupportImpl support = new ClientBehaviorSupportImpl(javascriptSupport, environment);
2104    
2105                    environment.push(ClientBehaviorSupport.class, support);
2106    
2107                    renderer.renderMarkup(writer, reply);
2108    
2109                    environment.pop(ClientBehaviorSupport.class);
2110    
2111                    support.commit();
2112                }
2113            };
2114    
2115            PartialMarkupRendererFilter heartbeat = new PartialMarkupRendererFilter()
2116            {
2117                public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2118                {
2119                    Heartbeat heartbeat = new HeartbeatImpl();
2120    
2121                    heartbeat.begin();
2122    
2123                    environment.push(Heartbeat.class, heartbeat);
2124    
2125                    renderer.renderMarkup(writer, reply);
2126    
2127                    environment.pop(Heartbeat.class);
2128    
2129                    heartbeat.end();
2130                }
2131            };
2132    
2133            PartialMarkupRendererFilter defaultValidationDecorator = new PartialMarkupRendererFilter()
2134            {
2135                public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2136                {
2137                    ValidationDecorator decorator = validationDecoratorFactory.newInstance(writer);
2138    
2139                    environment.push(ValidationDecorator.class, decorator);
2140    
2141                    renderer.renderMarkup(writer, reply);
2142    
2143                    environment.pop(ValidationDecorator.class);
2144                }
2145            };
2146    
2147            configuration.add("DocumentLinker", documentLinker);
2148            configuration.add("JavaScriptSupport", javascriptSupport);
2149            configuration.add("RenderSupport", renderSupport);
2150            configuration.add("ClientBehaviorSupport", clientBehaviorSupport);
2151            configuration.add("Heartbeat", heartbeat);
2152            configuration.add("ValidationDecorator", defaultValidationDecorator);
2153        }
2154    
2155        /**
2156         * Contributes several strategies:
2157         * <dl>
2158         * <dt>session
2159         * <dd>Values are stored in the {@link Session}
2160         * <dt>flash
2161         * <dd>Values are stored in the {@link Session}, until the next request (for the page)
2162         * <dt>client
2163         * <dd>Values are encoded into URLs (or hidden form fields)
2164         * </dl>
2165         */
2166        public void contributePersistentFieldManager(MappedConfiguration<String, PersistentFieldStrategy> configuration,
2167    
2168                                                     Request request,
2169    
2170                                                     @InjectService("ClientPersistentFieldStrategy")
2171                                                     PersistentFieldStrategy clientStrategy)
2172        {
2173            configuration.add(PersistenceConstants.SESSION, new SessionPersistentFieldStrategy(request));
2174            configuration.add(PersistenceConstants.FLASH, new FlashPersistentFieldStrategy(request));
2175            configuration.add(PersistenceConstants.CLIENT, clientStrategy);
2176        }
2177    
2178        @SuppressWarnings("rawtypes")
2179        public static ValueEncoderSource buildValueEncoderSource(Map<Class, ValueEncoderFactory> configuration,
2180                                                                 @ComponentClasses
2181                                                                 InvalidationEventHub hub)
2182        {
2183            ValueEncoderSourceImpl service = new ValueEncoderSourceImpl(configuration);
2184    
2185            hub.addInvalidationListener(service);
2186    
2187            return service;
2188        }
2189    
2190        /**
2191         * Contributes {@link ValueEncoder}s or {@link ValueEncoderFactory}s for types:
2192         * <ul>
2193         * <li>Object
2194         * <li>String
2195         * <li>Enum
2196         * </ul>
2197         */
2198        @SuppressWarnings("all")
2199        public static void contributeValueEncoderSource(MappedConfiguration<Class, Object> configuration)
2200        {
2201            configuration.addInstance(Object.class, TypeCoercedValueEncoderFactory.class);
2202            configuration.add(String.class, new StringValueEncoder());
2203            configuration.addInstance(Enum.class, EnumValueEncoderFactory.class);
2204        }
2205    
2206        /**
2207         * Contributes a single filter, "Secure", which checks for non-secure
2208         * requests that access secure pages.
2209         */
2210        public void contributePageRenderRequestHandler(OrderedConfiguration<PageRenderRequestFilter> configuration,
2211                                                       final RequestSecurityManager securityManager)
2212        {
2213            PageRenderRequestFilter secureFilter = new PageRenderRequestFilter()
2214            {
2215                public void handle(PageRenderRequestParameters parameters, PageRenderRequestHandler handler)
2216                        throws IOException
2217                {
2218    
2219                    if (securityManager.checkForInsecurePageRenderRequest(parameters))
2220                        return;
2221    
2222                    handler.handle(parameters);
2223                }
2224            };
2225    
2226            configuration.add("Secure", secureFilter);
2227        }
2228    
2229        /**
2230         * Configures the extensions that will require a digest to be downloaded via
2231         * the asset dispatcher. Most resources
2232         * are "safe", they don't require a digest. For unsafe resources, the digest
2233         * is incorporated into the URL to ensure
2234         * that the client side isn't just "fishing".
2235         * <p/>
2236         * The extensions must be all lower case.
2237         * <p/>
2238         * This contributes "class", "properties" and "tml" (the template extension).
2239         *
2240         * @param configuration
2241         *         collection of extensions
2242         */
2243        public static void contributeResourceDigestGenerator(Configuration<String> configuration)
2244        {
2245            // Java class files always require a digest.
2246            configuration.add("class");
2247    
2248            // Even though properties don't contain sensible data we should protect
2249            // them.
2250            configuration.add("properties");
2251    
2252            // Likewise, we don't want people fishing for templates.
2253            configuration.add(TapestryConstants.TEMPLATE_EXTENSION);
2254        }
2255    
2256        public static void contributeTemplateParser(MappedConfiguration<String, URL> config)
2257        {
2258            // Any class inside the internal module would do. Or we could move all
2259            // these
2260            // files to o.a.t.services.
2261    
2262            Class c = TemplateParserImpl.class;
2263    
2264            config.add("-//W3C//DTD XHTML 1.0 Strict//EN", c.getResource("xhtml1-strict.dtd"));
2265            config.add("-//W3C//DTD XHTML 1.0 Transitional//EN", c.getResource("xhtml1-transitional.dtd"));
2266            config.add("-//W3C//DTD XHTML 1.0 Frameset//EN", c.getResource("xhtml1-frameset.dtd"));
2267            config.add("-//W3C//DTD HTML 4.01//EN", c.getResource("xhtml1-strict.dtd"));
2268            config.add("-//W3C//DTD HTML 4.01 Transitional//EN", c.getResource("xhtml1-transitional.dtd"));
2269            config.add("-//W3C//DTD HTML 4.01 Frameset//EN", c.getResource("xhtml1-frameset.dtd"));
2270            config.add("-//W3C//ENTITIES Latin 1 for XHTML//EN", c.getResource("xhtml-lat1.ent"));
2271            config.add("-//W3C//ENTITIES Symbols for XHTML//EN", c.getResource("xhtml-symbol.ent"));
2272            config.add("-//W3C//ENTITIES Special for XHTML//EN", c.getResource("xhtml-special.ent"));
2273        }
2274    
2275        /**
2276         * Contributes factory defaults that may be overridden.
2277         */
2278        public static void contributeFactoryDefaults(MappedConfiguration<String, Object> configuration)
2279        {
2280            // Remember this is request-to-request time, presumably it'll take the
2281            // developer more than
2282            // one second to make a change, save it, and switch back to the browser.
2283    
2284            configuration.add(SymbolConstants.FILE_CHECK_INTERVAL, "1 s");
2285            configuration.add(SymbolConstants.FILE_CHECK_UPDATE_TIMEOUT, "50 ms");
2286    
2287            // This should be overridden for particular applications. These are the
2288            // locales for which we have (at least some) localized messages.
2289            configuration.add(SymbolConstants.SUPPORTED_LOCALES,
2290                    "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");
2291    
2292            configuration.add(SymbolConstants.TAPESTRY_VERSION,
2293                    VersionUtils.readVersionNumber("META-INF/gradle/org.apache.tapestry/tapestry-core/project.properties"));
2294    
2295            configuration.add(SymbolConstants.COOKIE_MAX_AGE, "7 d");
2296    
2297            configuration.add(SymbolConstants.START_PAGE_NAME, "start");
2298    
2299            configuration.add(SymbolConstants.DEFAULT_STYLESHEET, "classpath:/org/apache/tapestry5/default.css");
2300            configuration.add("tapestry.spacer-image", "classpath:/org/apache/tapestry5/spacer.gif");
2301    
2302            configuration.add(SymbolConstants.SUPPRESS_REDIRECT_FROM_ACTION_REQUESTS, false);
2303    
2304            configuration.add(SymbolConstants.PRODUCTION_MODE, true);
2305    
2306            configuration.add(SymbolConstants.CLUSTERED_SESSIONS, true);
2307    
2308            configuration.add(SymbolConstants.ASSET_PATH_PREFIX, "/assets/");
2309    
2310            configuration.add(SymbolConstants.COMPRESS_WHITESPACE, true);
2311    
2312            configuration.add(MetaDataConstants.SECURE_PAGE, false);
2313    
2314            configuration.add(SymbolConstants.FORM_CLIENT_LOGIC_ENABLED, true);
2315    
2316            // This is designed to make it easy to keep synchronized with
2317            // script.aculo.ous. As we support a new version, we create a new folder, and update the
2318            // path entry. We can then delete the old version folder (or keep it around). This should
2319            // be more manageable than overwriting the local copy with updates (it's too easy for
2320            // files deleted between scriptaculous releases to be accidentally left lying around).
2321            // There's also a ClasspathAliasManager contribution based on the path.
2322    
2323            configuration.add(SymbolConstants.SCRIPTACULOUS, "classpath:${tapestry.scriptaculous.path}");
2324            configuration.add("tapestry.scriptaculous.path", "org/apache/tapestry5/scriptaculous_1_9_0");
2325    
2326            // Likewise for WebFX DatePicker, currently version 1.0.6
2327    
2328            configuration.add("tapestry.datepicker.path", "org/apache/tapestry5/datepicker_106");
2329            configuration.add(SymbolConstants.DATEPICKER, "classpath:${tapestry.datepicker.path}");
2330    
2331            configuration.add("tapestry.underscore", "classpath:org/apache/tapestry5/underscore_1_3_3.js");
2332    
2333            configuration.add(SymbolConstants.BLACKBIRD, "");
2334    
2335            configuration.add(SymbolConstants.PERSISTENCE_STRATEGY, PersistenceConstants.SESSION);
2336    
2337            configuration.add(MetaDataConstants.RESPONSE_CONTENT_TYPE, "text/html");
2338    
2339            configuration.add(SymbolConstants.CHARSET, "UTF-8");
2340    
2341            configuration.add(SymbolConstants.APPLICATION_CATALOG,
2342                    String.format("context:WEB-INF/${%s}.properties", InternalSymbols.APP_NAME));
2343    
2344            configuration.add(SymbolConstants.EXCEPTION_REPORT_PAGE, "ExceptionReport");
2345    
2346            configuration.add(SymbolConstants.MIN_GZIP_SIZE, 100);
2347    
2348            Random random = new Random(System.currentTimeMillis());
2349    
2350            configuration.add(SymbolConstants.APPLICATION_VERSION, Long.toHexString(random.nextLong()));
2351    
2352            configuration.add(SymbolConstants.OMIT_GENERATOR_META, false);
2353    
2354            configuration.add(SymbolConstants.SECURE_ENABLED, SymbolConstants.PRODUCTION_MODE_VALUE);
2355            configuration.add(SymbolConstants.COMPACT_JSON, SymbolConstants.PRODUCTION_MODE_VALUE);
2356    
2357            configuration.add(SymbolConstants.ENCODE_LOCALE_INTO_PATH, true);
2358    
2359            configuration.add(SymbolConstants.BLACKBIRD_ENABLED, false);
2360    
2361            configuration.add(InternalSymbols.PRE_SELECTED_FORM_NAMES, "reset,submit,select,id,method,action,onsubmit," + InternalConstants.CANCEL_NAME);
2362    
2363            configuration.add(SymbolConstants.COMPONENT_RENDER_TRACING_ENABLED, false);
2364    
2365            // The default values denote "use values from request"
2366            configuration.add(SymbolConstants.HOSTNAME, "");
2367            configuration.add(SymbolConstants.HOSTPORT, 0);
2368            configuration.add(SymbolConstants.HOSTPORT_SECURE, 0);
2369    
2370            configuration.add(SymbolConstants.UNKNOWN_COMPONENT_ID_CHECK_ENABLED, true);
2371    
2372            configuration.add(SymbolConstants.APPLICATION_FOLDER, "");
2373    
2374            // Grid component parameters defaults
2375            configuration.add(ComponentParameterConstants.GRID_ROWS_PER_PAGE, GridConstants.ROWS_PER_PAGE);
2376            configuration.add(ComponentParameterConstants.GRID_PAGER_POSITION, GridConstants.PAGER_POSITION);
2377            configuration.add(ComponentParameterConstants.GRID_EMPTY_BLOCK, GridConstants.EMPTY_BLOCK);
2378            configuration.add(ComponentParameterConstants.GRID_TABLE_CSS_CLASS, GridConstants.TABLE_CLASS);
2379            configuration.add(ComponentParameterConstants.GRIDPAGER_PAGE_RANGE, GridConstants.PAGER_PAGE_RANGE);
2380            configuration.add(ComponentParameterConstants.GRIDCOLUMNS_SORTABLE_ASSET, GridConstants.COLUMNS_SORTABLE);
2381            configuration.add(ComponentParameterConstants.GRIDCOLUMNS_ASCENDING_ASSET, GridConstants.COLUMNS_ASCENDING);
2382            configuration.add(ComponentParameterConstants.GRIDCOLUMNS_DESCENDING_ASSET, GridConstants.COLUMNS_DESCENDING);
2383    
2384            // FormInjector component parameters defaults
2385            configuration.add(ComponentParameterConstants.FORMINJECTOR_INSERT_POSITION, "above");
2386            configuration.add(ComponentParameterConstants.FORMINJECTOR_SHOW_FUNCTION, "highlight");
2387    
2388            // Palette component parameters defaults
2389            configuration.add(ComponentParameterConstants.PALETTE_ROWS_SIZE, 10);
2390    
2391            // Zone component parameters defaults
2392            configuration.add(ComponentParameterConstants.ZONE_SHOW_METHOD, "show");
2393            configuration.add(ComponentParameterConstants.ZONE_UPDATE_METHOD, "highlight");
2394    
2395            // By default, no page is on the whitelist unless it has the @WhitelistAccessOnly annotation
2396            configuration.add(MetaDataConstants.WHITELIST_ONLY_PAGE, false);
2397    
2398            // Leaving this as the default results in a runtime error logged to the console (and a default password is used);
2399            // you are expected to override this symbol.
2400            configuration.add(SymbolConstants.HMAC_PASSPHRASE, "");
2401        }
2402    
2403        /**
2404         * Adds a listener to the {@link org.apache.tapestry5.internal.services.ComponentInstantiatorSource} that clears the
2405         * {@link PropertyAccess} and {@link TypeCoercer} caches on
2406         * a class loader invalidation. In addition, forces the
2407         * realization of {@link ComponentClassResolver} at startup.
2408         */
2409        public void contributeApplicationInitializer(OrderedConfiguration<ApplicationInitializerFilter> configuration,
2410                                                     final TypeCoercer typeCoercer, final ComponentClassResolver componentClassResolver, @ComponentClasses
2411        final InvalidationEventHub invalidationEventHub, final @Autobuild
2412        RestoreDirtySessionObjects restoreDirtySessionObjects)
2413        {
2414            final InvalidationListener listener = new InvalidationListener()
2415            {
2416                public void objectWasInvalidated()
2417                {
2418                    propertyAccess.clearCache();
2419    
2420                    typeCoercer.clearCache();
2421                }
2422            };
2423    
2424            ApplicationInitializerFilter clearCaches = new ApplicationInitializerFilter()
2425            {
2426                public void initializeApplication(Context context, ApplicationInitializer initializer)
2427                {
2428                    // Snuck in here is the logic to clear the PropertyAccess
2429                    // service's cache whenever
2430                    // the component class loader is invalidated.
2431    
2432                    invalidationEventHub.addInvalidationListener(listener);
2433    
2434                    endOfRequestEventHub.addEndOfRequestListener(restoreDirtySessionObjects);
2435    
2436                    // Perform other pending initialization
2437    
2438                    initializer.initializeApplication(context);
2439    
2440                    // We don't care about the result, but this forces a load of the
2441                    // service
2442                    // at application startup, rather than on first request.
2443    
2444                    componentClassResolver.isPageName("ForceLoadAtStartup");
2445                }
2446            };
2447    
2448            configuration.add("ClearCachesOnInvalidation", clearCaches);
2449        }
2450    
2451        /**
2452         * Contributes filters:
2453         * <dl>
2454         * <dt>Ajax</dt>
2455         * <dd>Determines if the request is Ajax oriented, and redirects to an alternative handler if so</dd>
2456         * <dt>ImmediateRender</dt>
2457         * <dd>When {@linkplain SymbolConstants#SUPPRESS_REDIRECT_FROM_ACTION_REQUESTS
2458         * immediate action response rendering} is enabled, generates the markup response (instead of a page redirect
2459         * response, which is the normal behavior)</dd>
2460         * <dt>Secure</dt>
2461         * <dd>Sends a redirect if an non-secure request accesses a secure page</dd>
2462         * </dl>
2463         */
2464        public void contributeComponentEventRequestHandler(OrderedConfiguration<ComponentEventRequestFilter> configuration,
2465                                                           final RequestSecurityManager requestSecurityManager, @Ajax
2466        ComponentEventRequestHandler ajaxHandler)
2467        {
2468            ComponentEventRequestFilter secureFilter = new ComponentEventRequestFilter()
2469            {
2470                public void handle(ComponentEventRequestParameters parameters, ComponentEventRequestHandler handler)
2471                        throws IOException
2472                {
2473                    if (requestSecurityManager.checkForInsecureComponentEventRequest(parameters))
2474                        return;
2475    
2476                    handler.handle(parameters);
2477                }
2478            };
2479    
2480            configuration.add("Ajax", new AjaxFilter(request, ajaxHandler));
2481    
2482            configuration.addInstance("ImmediateRender", ImmediateActionRenderResponseFilter.class);
2483    
2484            configuration.add("Secure", secureFilter, "before:Ajax");
2485        }
2486    
2487        /**
2488         * Contributes:
2489         * <dl>
2490         * <dt>AjaxFormUpdate</dt>
2491         * <dd>{@link AjaxFormUpdateFilter}</dd>
2492         * </dl>
2493         *
2494         * @since 5.2.0
2495         */
2496        public static void contributeAjaxComponentEventRequestHandler(
2497                OrderedConfiguration<ComponentEventRequestFilter> configuration)
2498        {
2499            configuration.addInstance("AjaxFormUpdate", AjaxFormUpdateFilter.class);
2500        }
2501    
2502        /**
2503         * Contributes strategies accessible via the {@link NullFieldStrategySource} service.
2504         * <p/>
2505         * <dl>
2506         * <dt>default</dt>
2507         * <dd>Does nothing, nulls stay null.</dd>
2508         * <dt>zero</dt>
2509         * <dd>Null values are converted to zero.</dd>
2510         * </dl>
2511         */
2512        public static void contributeNullFieldStrategySource(MappedConfiguration<String, NullFieldStrategy> configuration)
2513        {
2514            configuration.add("default", new DefaultNullFieldStrategy());
2515            configuration.add("zero", new ZeroNullFieldStrategy());
2516        }
2517    
2518        /**
2519         * Determines positioning of hidden fields relative to other elements (this
2520         * is needed by {@link org.apache.tapestry5.corelib.components.FormFragment} and others.
2521         * <p/>
2522         * For elements input, select, textarea and label the hidden field is positioned after.
2523         * <p/>
2524         * For elements p, div, li and td, the hidden field is positioned inside.
2525         */
2526        public static void contributeHiddenFieldLocationRules(
2527                MappedConfiguration<String, RelativeElementPosition> configuration)
2528        {
2529            configuration.add("input", RelativeElementPosition.AFTER);
2530            configuration.add("select", RelativeElementPosition.AFTER);
2531            configuration.add("textarea", RelativeElementPosition.AFTER);
2532            configuration.add("label", RelativeElementPosition.AFTER);
2533    
2534            configuration.add("p", RelativeElementPosition.INSIDE);
2535            configuration.add("div", RelativeElementPosition.INSIDE);
2536            configuration.add("td", RelativeElementPosition.INSIDE);
2537            configuration.add("li", RelativeElementPosition.INSIDE);
2538        }
2539    
2540        /**
2541         * @since 5.1.0.0
2542         */
2543        public static LinkCreationHub buildLinkCreationHub(LinkSource source)
2544        {
2545            return source.getLinkCreationHub();
2546        }
2547    
2548        /**
2549         * Exposes the public portion of the internal {@link InternalComponentInvalidationEventHub} service.
2550         *
2551         * @since 5.1.0.0
2552         */
2553        @Marker(ComponentClasses.class)
2554        public static InvalidationEventHub buildComponentClassesInvalidationEventHub(
2555                InternalComponentInvalidationEventHub trueHub)
2556        {
2557            return trueHub;
2558        }
2559    
2560        /**
2561         * @since 5.1.0.0
2562         */
2563        @Marker(ComponentTemplates.class)
2564        public static InvalidationEventHub buildComponentTemplatesInvalidationEventHub(
2565                ComponentTemplateSource templateSource)
2566        {
2567            return templateSource.getInvalidationEventHub();
2568        }
2569    
2570        /**
2571         * @since 5.1.0.0
2572         */
2573        @Marker(ComponentMessages.class)
2574        public static InvalidationEventHub buildComponentMessagesInvalidationEventHub(ComponentMessagesSource messagesSource)
2575        {
2576            return messagesSource.getInvalidationEventHub();
2577        }
2578    
2579        @Scope(ScopeConstants.PERTHREAD)
2580        public Environment buildEnvironment(PerthreadManager perthreadManager)
2581        {
2582            EnvironmentImpl service = new EnvironmentImpl();
2583    
2584            perthreadManager.addThreadCleanupListener(service);
2585    
2586            return service;
2587        }
2588    
2589        /**
2590         * The master SessionPersistedObjectAnalyzer.
2591         *
2592         * @since 5.1.0.0
2593         */
2594        @Marker(Primary.class)
2595        public SessionPersistedObjectAnalyzer buildSessionPersistedObjectAnalyzer(
2596                Map<Class, SessionPersistedObjectAnalyzer> configuration)
2597        {
2598            return strategyBuilder.build(SessionPersistedObjectAnalyzer.class, configuration);
2599        }
2600    
2601        /**
2602         * Identifies String, Number and Boolean as immutable objects, a catch-all
2603         * handler for Object (that understands
2604         * the {@link org.apache.tapestry5.annotations.ImmutableSessionPersistedObject} annotation),
2605         * and a handler for {@link org.apache.tapestry5.OptimizedSessionPersistedObject}.
2606         *
2607         * @since 5.1.0.0
2608         */
2609        public static void contributeSessionPersistedObjectAnalyzer(
2610                MappedConfiguration<Class, SessionPersistedObjectAnalyzer> configuration)
2611        {
2612            configuration.add(Object.class, new DefaultSessionPersistedObjectAnalyzer());
2613    
2614            SessionPersistedObjectAnalyzer<Object> immutable = new SessionPersistedObjectAnalyzer<Object>()
2615            {
2616                public boolean checkAndResetDirtyState(Object sessionPersistedObject)
2617                {
2618                    return false;
2619                }
2620            };
2621    
2622            configuration.add(String.class, immutable);
2623            configuration.add(Number.class, immutable);
2624            configuration.add(Boolean.class, immutable);
2625    
2626            configuration.add(OptimizedSessionPersistedObject.class, new OptimizedSessionPersistedObjectAnalyzer());
2627        }
2628    
2629        /**
2630         * @since 5.1.1.0
2631         */
2632        @Marker(Primary.class)
2633        public StackTraceElementAnalyzer buildMasterStackTraceElementAnalyzer(List<StackTraceElementAnalyzer> configuration)
2634        {
2635            return chainBuilder.build(StackTraceElementAnalyzer.class, configuration);
2636        }
2637    
2638        /**
2639         * Contributes:
2640         * <dl>
2641         * <dt>Application</dt>
2642         * <dd>Checks for classes in the application package</dd>
2643         * <dt>Proxies</dt>
2644         * <dd>Checks for classes that appear to be generated proxies.</dd>
2645         * <dt>SunReflect</dt>
2646         * <dd>Checks for <code>sun.reflect</code> (which are omitted)
2647         * <dt>TapestryAOP</dt>
2648         * <dd>Omits stack frames for classes related to Tapestry AOP (such as advice, etc.)</dd>
2649         * <dt>OperationTracker</dt>
2650         * <dd>Omits stack frames related to {@link OperationTracker}</dd>
2651         * </dl>
2652         *
2653         * @since 5.1.0.0
2654         */
2655        public static void contributeMasterStackTraceElementAnalyzer(
2656                OrderedConfiguration<StackTraceElementAnalyzer> configuration)
2657        {
2658            configuration.addInstance("Application", ApplicationStackTraceElementAnalyzer.class);
2659            configuration.add("Proxies", new ProxiesStackTraceElementAnalyzer(), "before:Application");
2660            configuration.add("Synthetic", new SyntheticStackTraceElementAnalyzer(), "before:Application");
2661            configuration.add("SunReflect", new PrefixCheckStackTraceElementAnalyzer(
2662                    StackTraceElementClassConstants.OMITTED, "sun.reflect."));
2663            configuration.addInstance("TapestryAOP", TapestryAOPStackFrameAnalyzer.class, "before:Application");
2664            configuration.add("OperationTracker", new RegexpStackTraceElementAnalyzer(Pattern.compile("internal\\.(RegistryImpl|PerThreadOperationTracker|OperationTrackerImpl)\\.invoke\\("), StackTraceElementClassConstants.OMITTED));
2665        }
2666    
2667        /**
2668         * Advises the {@link org.apache.tapestry5.services.messages.ComponentMessagesSource} service so
2669         * that the creation
2670         * of {@link org.apache.tapestry5.ioc.Messages} instances can be deferred.
2671         *
2672         * @since 5.1.0.0
2673         */
2674        @Match("ComponentMessagesSource")
2675        public static void adviseLazy(LazyAdvisor advisor, MethodAdviceReceiver receiver)
2676        {
2677            advisor.addLazyMethodInvocationAdvice(receiver);
2678        }
2679    
2680        /**
2681         * @since 5.1.0.0
2682         */
2683        public ComponentRequestHandler buildComponentRequestHandler(List<ComponentRequestFilter> configuration,
2684    
2685                                                                    @Autobuild
2686                                                                    ComponentRequestHandlerTerminator terminator,
2687    
2688                                                                    Logger logger)
2689        {
2690            return pipelineBuilder.build(logger, ComponentRequestHandler.class, ComponentRequestFilter.class,
2691                    configuration, terminator);
2692        }
2693    
2694        /**
2695         * Contributes:
2696         * <dl>
2697         * <dt>InitializeActivePageName
2698         * <dd>{@link InitializeActivePageName}
2699         * </dl>
2700         *
2701         * @since 5.2.0
2702         */
2703        public void contributeComponentRequestHandler(OrderedConfiguration<ComponentRequestFilter> configuration)
2704        {
2705            configuration.addInstance("InitializeActivePageName", InitializeActivePageName.class);
2706        }
2707    
2708        /**
2709         * Decorate FieldValidatorDefaultSource to setup the EnvironmentMessages
2710         * object and place it in the environment.
2711         * Although this could have been implemented directly in the default
2712         * implementation of the service, doing it
2713         * as service decoration ensures that the environment will be properly setup
2714         * even if a user overrides the default
2715         * service implementation.
2716         *
2717         * @param defaultSource
2718         *         The service to decorate
2719         * @param environment
2720         */
2721        public static FieldValidatorDefaultSource decorateFieldValidatorDefaultSource(
2722                final FieldValidatorDefaultSource defaultSource, final Environment environment)
2723        {
2724            return new FieldValidatorDefaultSource()
2725            {
2726    
2727                public FieldValidator createDefaultValidator(Field field, String overrideId, Messages overrideMessages,
2728                                                             Locale locale, Class propertyType, AnnotationProvider propertyAnnotations)
2729                {
2730                    environment.push(EnvironmentMessages.class, new EnvironmentMessages(overrideMessages, overrideId));
2731                    FieldValidator fieldValidator = defaultSource.createDefaultValidator(field, overrideId,
2732                            overrideMessages, locale, propertyType, propertyAnnotations);
2733                    environment.pop(EnvironmentMessages.class);
2734                    return fieldValidator;
2735                }
2736    
2737                public FieldValidator createDefaultValidator(ComponentResources resources, String parameterName)
2738                {
2739    
2740                    EnvironmentMessages em = new EnvironmentMessages(resources.getContainerMessages(), resources.getId());
2741                    environment.push(EnvironmentMessages.class, em);
2742                    FieldValidator fieldValidator = defaultSource.createDefaultValidator(resources, parameterName);
2743                    environment.pop(EnvironmentMessages.class);
2744                    return fieldValidator;
2745                }
2746            };
2747        }
2748    
2749        /**
2750         * Exposes the Environmental {@link Heartbeat} as an injectable service.
2751         *
2752         * @since 5.2.0
2753         */
2754        public Heartbeat buildHeartbeat()
2755        {
2756            return environmentalBuilder.build(Heartbeat.class);
2757        }
2758    
2759        /**
2760         * Contributes the "core" and "core-datefield" {@link JavaScriptStack}s
2761         *
2762         * @since 5.2.0
2763         */
2764        public static void contributeJavaScriptStackSource(MappedConfiguration<String, JavaScriptStack> configuration)
2765        {
2766            configuration.addInstance(InternalConstants.CORE_STACK_NAME, CoreJavaScriptStack.class);
2767            configuration.addInstance("core-datefield", DateFieldStack.class);
2768        }
2769    
2770        public static ComponentMessagesSource buildComponentMessagesSource(UpdateListenerHub updateListenerHub, @Autobuild
2771        ComponentMessagesSourceImpl service)
2772        {
2773            updateListenerHub.addUpdateListener(service);
2774    
2775            return service;
2776        }
2777    
2778        /**
2779         * Contributes:
2780         * <dl>
2781         * <dt>AppCatalog</dt>
2782         * <dd>The Resource defined by {@link SymbolConstants#APPLICATION_CATALOG}</dd>
2783         * <dt>ValidationMessages</dt>
2784         * <dd>Messages used by validators (before:AppCatalog)</dd>
2785         * <dt>
2786         *
2787         * @since 5.2.0
2788         */
2789        public static void contributeComponentMessagesSource(AssetSource assetSource,
2790                                                             @Symbol(SymbolConstants.APPLICATION_CATALOG)
2791                                                             Resource applicationCatalog, OrderedConfiguration<Resource> configuration)
2792        {
2793            configuration.add("ValidationMessages",
2794                    assetSource.resourceForPath("org/apache/tapestry5/internal/ValidationMessages.properties"),
2795                    "before:AppCatalog");
2796            configuration.add("AppCatalog", applicationCatalog);
2797        }
2798    
2799        /**
2800         * Contributes extractors for {@link Meta}, {@link Secure}, {@link ContentType} and {@link WhitelistAccessOnly} annotations.
2801         *
2802         * @since 5.2.0
2803         */
2804        @SuppressWarnings("unchecked")
2805        public static void contributeMetaWorker(MappedConfiguration<Class, MetaDataExtractor> configuration)
2806        {
2807            configuration.addInstance(Meta.class, MetaAnnotationExtractor.class);
2808            configuration.add(Secure.class, new FixedExtractor(MetaDataConstants.SECURE_PAGE));
2809            configuration.addInstance(ContentType.class, ContentTypeExtractor.class);
2810            configuration.add(WhitelistAccessOnly.class, new FixedExtractor(MetaDataConstants.WHITELIST_ONLY_PAGE));
2811        }
2812    
2813        /**
2814         * Builds the {@link ComponentTemplateLocator} as a chain of command.
2815         *
2816         * @since 5.2.0
2817         */
2818        @Marker(Primary.class)
2819        public ComponentTemplateLocator buildComponentTemplateLocator(List<ComponentTemplateLocator> configuration)
2820        {
2821            return chainBuilder.build(ComponentTemplateLocator.class, configuration);
2822        }
2823    
2824        /**
2825         * Contributes two template locators:
2826         * <dl>
2827         * <dt>Default</dt>
2828         * <dd>Searches for the template on the classpath ({@link DefaultTemplateLocator}</dd>
2829         * <dt>Page</dt>
2830         * <dd>Searches for <em>page</em> templates in the context ({@link PageTemplateLocator})</dd>
2831         * </dl>
2832         *
2833         * @since 5.2.0
2834         */
2835        public static void contributeComponentTemplateLocator(OrderedConfiguration<ComponentTemplateLocator> configuration,
2836                                                              @ContextProvider
2837                                                              AssetFactory contextAssetFactory,
2838                                                              @Symbol(SymbolConstants.APPLICATION_FOLDER) String applicationFolder,
2839                                                              ComponentClassResolver componentClassResolver)
2840        {
2841            configuration.add("Default", new DefaultTemplateLocator());
2842            configuration
2843                    .add("Page", new PageTemplateLocator(contextAssetFactory.getRootResource(), componentClassResolver, applicationFolder));
2844    
2845        }
2846    
2847        /**
2848         * Builds {@link ComponentEventLinkTransformer} service as a chain of command.
2849         *
2850         * @since 5.2.0
2851         */
2852        @Marker(Primary.class)
2853        public ComponentEventLinkTransformer buildComponentEventLinkTransformer(
2854                List<ComponentEventLinkTransformer> configuration)
2855        {
2856            return chainBuilder.build(ComponentEventLinkTransformer.class, configuration);
2857        }
2858    
2859        /**
2860         * Builds {@link PageRenderLinkTransformer} service as a chain of command.
2861         *
2862         * @since 5.2.0
2863         */
2864        @Marker(Primary.class)
2865        public PageRenderLinkTransformer buildPageRenderLinkTransformer(List<PageRenderLinkTransformer> configuration)
2866        {
2867            return chainBuilder.build(PageRenderLinkTransformer.class, configuration);
2868        }
2869    
2870        /**
2871         * Provides the "LinkTransformer" interceptor for the {@link ComponentEventLinkEncoder} service.
2872         * Other decorations
2873         * should come after LinkTransformer.
2874         *
2875         * @since 5.2.0
2876         */
2877        @Match("ComponentEventLinkEncoder")
2878        public ComponentEventLinkEncoder decorateLinkTransformer(LinkTransformer linkTransformer,
2879                                                                 ComponentEventLinkEncoder delegate)
2880        {
2881            return new LinkTransformerInterceptor(linkTransformer, delegate);
2882        }
2883    
2884        /**
2885         * In production mode, override {@link UpdateListenerHub} to be an empty placeholder.
2886         */
2887        @Contribute(ServiceOverride.class)
2888        public static void productionModeOverrides(MappedConfiguration<Class, Object> configuration,
2889                                                   @Symbol(SymbolConstants.PRODUCTION_MODE)
2890                                                   boolean productionMode)
2891        {
2892            if (productionMode)
2893            {
2894                configuration.add(UpdateListenerHub.class, new UpdateListenerHub()
2895                {
2896                    public void fireCheckForUpdates()
2897                    {
2898                    }
2899    
2900                    public void addUpdateListener(UpdateListener listener)
2901                    {
2902    
2903                    }
2904                });
2905            }
2906        }
2907    
2908        /**
2909         * Contributes a single default analyzer:
2910         * <dl>
2911         * <dt>LocalhostOnly</dt>
2912         * <dd>Identifies requests from localhost as on client whitelist</dd>
2913         * </dl>
2914         *
2915         * @since 5.3
2916         */
2917        @Contribute(ClientWhitelist.class)
2918        public static void defaultWhitelist(OrderedConfiguration<WhitelistAnalyzer> configuration)
2919        {
2920            configuration.add("LocalhostOnly", new LocalhostOnly());
2921        }
2922    
2923        @Startup
2924        public static void registerToClearPlasticProxyFactoryOnInvalidation(@ComponentClasses InvalidationEventHub hub, @Builtin final PlasticProxyFactory proxyFactory)
2925        {
2926            hub.addInvalidationListener(new InvalidationListener()
2927            {
2928                @Override
2929                public void objectWasInvalidated()
2930                {
2931                    proxyFactory.clearCache();
2932                }
2933            });
2934        }
2935    }