001    // Copyright 2006, 2007, 2008, 2009 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.annotations.*;
020    import org.apache.tapestry5.beaneditor.Validate;
021    import org.apache.tapestry5.corelib.LoopFormState;
022    import org.apache.tapestry5.corelib.data.BlankOption;
023    import org.apache.tapestry5.corelib.data.GridPagerPosition;
024    import org.apache.tapestry5.corelib.data.InsertPosition;
025    import org.apache.tapestry5.grid.GridDataSource;
026    import org.apache.tapestry5.internal.*;
027    import org.apache.tapestry5.internal.beaneditor.PrimitiveFieldConstraintGenerator;
028    import org.apache.tapestry5.internal.beaneditor.ValidateAnnotationConstraintGenerator;
029    import org.apache.tapestry5.internal.bindings.*;
030    import org.apache.tapestry5.internal.grid.CollectionGridDataSource;
031    import org.apache.tapestry5.internal.grid.NullDataSource;
032    import org.apache.tapestry5.internal.gzip.GZipFilter;
033    import org.apache.tapestry5.internal.renderers.*;
034    import org.apache.tapestry5.internal.services.*;
035    import org.apache.tapestry5.internal.transform.*;
036    import org.apache.tapestry5.internal.translator.NumericTranslator;
037    import org.apache.tapestry5.internal.translator.NumericTranslatorSupport;
038    import org.apache.tapestry5.internal.translator.StringTranslator;
039    import org.apache.tapestry5.internal.util.PrimaryKeyEncoder2ValueEncoder;
040    import org.apache.tapestry5.internal.util.RenderableAsBlock;
041    import org.apache.tapestry5.internal.util.StringRenderable;
042    import org.apache.tapestry5.ioc.*;
043    import org.apache.tapestry5.ioc.annotations.*;
044    import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
045    import org.apache.tapestry5.ioc.internal.util.IdAllocator;
046    import org.apache.tapestry5.ioc.services.*;
047    import org.apache.tapestry5.ioc.util.StrategyRegistry;
048    import org.apache.tapestry5.ioc.util.TimeInterval;
049    import org.apache.tapestry5.json.JSONArray;
050    import org.apache.tapestry5.json.JSONObject;
051    import org.apache.tapestry5.runtime.Component;
052    import org.apache.tapestry5.runtime.ComponentResourcesAware;
053    import org.apache.tapestry5.runtime.RenderCommand;
054    import org.apache.tapestry5.runtime.RenderQueue;
055    import org.apache.tapestry5.services.ajax.MultiZoneUpdateEventResultProcessor;
056    import org.apache.tapestry5.util.StringToEnumCoercion;
057    import org.apache.tapestry5.validator.*;
058    import org.slf4j.Logger;
059    
060    import javax.servlet.ServletContext;
061    import javax.servlet.http.HttpServletRequest;
062    import javax.servlet.http.HttpServletResponse;
063    import java.io.IOException;
064    import java.lang.annotation.Annotation;
065    import java.lang.reflect.Method;
066    import java.math.BigDecimal;
067    import java.math.BigInteger;
068    import java.net.URL;
069    import java.text.DateFormat;
070    import java.text.SimpleDateFormat;
071    import java.util.*;
072    import java.util.regex.Pattern;
073    
074    /**
075     * The root module for Tapestry.
076     */
077    @SuppressWarnings({ "JavaDoc" })
078    @Marker(Core.class)
079    @SubModule(InternalModule.class)
080    public final class TapestryModule
081    {
082        private final PipelineBuilder pipelineBuilder;
083    
084        private final ApplicationGlobals applicationGlobals;
085    
086        private final PropertyShadowBuilder shadowBuilder;
087    
088        private final Environment environment;
089    
090        private final StrategyBuilder strategyBuilder;
091    
092        private final PropertyAccess propertyAccess;
093    
094        private final ChainBuilder chainBuilder;
095    
096        private final Request request;
097    
098        private final Response response;
099    
100        private final RequestGlobals requestGlobals;
101    
102        private final EnvironmentalShadowBuilder environmentalBuilder;
103    
104        private final EndOfRequestEventHub endOfRequestEventHub;
105    
106        /**
107         * We inject all sorts of common dependencies (including builders) into the module itself (note: even though some of
108         * these service are defined by the module itself, that's ok because services are always lazy proxies).  This isn't
109         * about efficiency (it may be slightly more efficient, but not in any noticable way), it's about eliminating the
110         * need to keep injecting these dependencies into invividual service builder and contribution methods.
111         */
112        public TapestryModule(PipelineBuilder pipelineBuilder,
113    
114                              PropertyShadowBuilder shadowBuilder,
115    
116                              RequestGlobals requestGlobals,
117    
118                              ApplicationGlobals applicationGlobals,
119    
120                              ChainBuilder chainBuilder,
121    
122                              Environment environment,
123    
124                              StrategyBuilder strategyBuilder,
125    
126                              PropertyAccess propertyAccess,
127    
128                              Request request,
129    
130                              Response response,
131    
132                              EnvironmentalShadowBuilder environmentalBuilder,
133    
134                              EndOfRequestEventHub endOfRequestEventHub)
135        {
136            this.pipelineBuilder = pipelineBuilder;
137            this.shadowBuilder = shadowBuilder;
138            this.requestGlobals = requestGlobals;
139            this.applicationGlobals = applicationGlobals;
140            this.chainBuilder = chainBuilder;
141            this.environment = environment;
142            this.strategyBuilder = strategyBuilder;
143            this.propertyAccess = propertyAccess;
144            this.request = request;
145            this.response = response;
146            this.environmentalBuilder = environmentalBuilder;
147            this.endOfRequestEventHub = endOfRequestEventHub;
148        }
149    
150        // A bunch of classes "promoted" from inline inner class to nested classes,
151        // just so that the stack trace would be more readable. Most of these
152        // are teminators for pipeline services.
153    
154        /**
155         * @since 5.1.0.0
156         */
157        private class ApplicationInitializerTerminator implements ApplicationInitializer
158        {
159            public void initializeApplication(Context context)
160            {
161                applicationGlobals.storeContext(context);
162            }
163        }
164    
165        /**
166         * @since 5.1.0.0
167         */
168        private class HttpServletRequestHandlerTerminator implements HttpServletRequestHandler
169        {
170            private final RequestHandler handler;
171            private final String applicationCharset;
172            private final SessionPersistedObjectAnalyzer analyzer;
173    
174            public HttpServletRequestHandlerTerminator(RequestHandler handler, String applicationCharset,
175                                                       SessionPersistedObjectAnalyzer analyzer)
176            {
177                this.handler = handler;
178                this.applicationCharset = applicationCharset;
179                this.analyzer = analyzer;
180            }
181    
182            public boolean service(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
183                    throws IOException
184            {
185                requestGlobals.storeServletRequestResponse(servletRequest, servletResponse);
186    
187                Request request = new RequestImpl(servletRequest, applicationCharset, analyzer);
188                Response response = new ResponseImpl(servletResponse);
189    
190                // TAP5-257: Make sure that the "initial guess" for request/response is available, even if
191                // some filter in the RequestHandler pipeline replaces them.
192    
193                requestGlobals.storeRequestResponse(request, response);
194    
195                // Transition from the Servlet API-based pipeline, to the Tapestry-based pipeline.
196    
197                return handler.service(request, response);
198            }
199        }
200    
201        /**
202         * @since 5.1.0.0
203         */
204        private class ServletApplicationInitializerTerminator implements ServletApplicationInitializer
205        {
206            private final ApplicationInitializer initializer;
207    
208            public ServletApplicationInitializerTerminator(ApplicationInitializer initializer)
209            {
210                this.initializer = initializer;
211            }
212    
213            public void initializeApplication(ServletContext servletContext)
214            {
215                applicationGlobals.storeServletContext(servletContext);
216    
217                // And now, down the (Web) ApplicationInitializer pipeline ...
218    
219                ContextImpl context = new ContextImpl(servletContext);
220    
221                applicationGlobals.storeContext(context);
222    
223                initializer.initializeApplication(context);
224            }
225        }
226    
227        /**
228         * @since 5.1.0.0
229         */
230        private class RequestHandlerTerminator implements RequestHandler
231        {
232            private final Dispatcher masterDispatcher;
233    
234            public RequestHandlerTerminator(Dispatcher masterDispatcher)
235            {
236                this.masterDispatcher = masterDispatcher;
237            }
238    
239            public boolean service(Request request, Response response) throws IOException
240            {
241                // Update RequestGlobals with the current request/response (in case some filter replaced the
242                // normal set).
243                requestGlobals.storeRequestResponse(request, response);
244    
245                return masterDispatcher.dispatch(request, response);
246            }
247        }
248    
249        public static void bind(ServiceBinder binder)
250        {
251            binder.bind(ClasspathAssetAliasManager.class, ClasspathAssetAliasManagerImpl.class);
252            binder.bind(PersistentLocale.class, PersistentLocaleImpl.class);
253            binder.bind(ApplicationStateManager.class, ApplicationStateManagerImpl.class);
254            binder.bind(ApplicationStatePersistenceStrategySource.class,
255                        ApplicationStatePersistenceStrategySourceImpl.class);
256            binder.bind(BindingSource.class, BindingSourceImpl.class);
257            binder.bind(FieldValidatorSource.class, FieldValidatorSourceImpl.class);
258            binder.bind(ApplicationGlobals.class, ApplicationGlobalsImpl.class);
259            binder.bind(AssetSource.class, AssetSourceImpl.class);
260            binder.bind(Cookies.class, CookiesImpl.class);
261            binder.bind(FieldValidatorDefaultSource.class, FieldValidatorDefaultSourceImpl.class);
262            binder.bind(RequestGlobals.class, RequestGlobalsImpl.class);
263            binder.bind(ResourceDigestGenerator.class, ResourceDigestGeneratorImpl.class);
264            binder.bind(ValidationConstraintGenerator.class, ValidationConstraintGeneratorImpl.class);
265            binder.bind(EnvironmentalShadowBuilder.class, EnvironmentalShadowBuilderImpl.class);
266            binder.bind(ComponentSource.class, ComponentSourceImpl.class);
267            binder.bind(BeanModelSource.class, BeanModelSourceImpl.class);
268            binder.bind(BeanBlockSource.class, BeanBlockSourceImpl.class);
269            binder.bind(ComponentDefaultProvider.class, ComponentDefaultProviderImpl.class);
270            binder.bind(MarkupWriterFactory.class, MarkupWriterFactoryImpl.class);
271            binder.bind(FieldValidationSupport.class, FieldValidationSupportImpl.class);
272            binder.bind(ObjectRenderer.class, LocationRenderer.class).withId("LocationRenderer");
273            binder.bind(ObjectProvider.class, AssetObjectProvider.class).withId("AssetObjectProvider");
274            binder.bind(RequestExceptionHandler.class, DefaultRequestExceptionHandler.class);
275            binder.bind(ComponentEventResultProcessor.class, ComponentInstanceResultProcessor.class).withId(
276                    "ComponentInstanceResultProcessor");
277            binder.bind(NullFieldStrategySource.class, NullFieldStrategySourceImpl.class);
278            binder.bind(HttpServletRequestFilter.class, IgnoredPathsFilter.class).withId("IgnoredPathsFilter");
279            binder.bind(ContextValueEncoder.class, ContextValueEncoderImpl.class);
280            binder.bind(BaseURLSource.class, BaseURLSourceImpl.class);
281            binder.bind(BeanBlockOverrideSource.class, BeanBlockOverrideSourceImpl.class);
282            binder.bind(AliasManager.class, AliasManagerImpl.class).withId("AliasOverrides");
283            binder.bind(HiddenFieldLocationRules.class, HiddenFieldLocationRulesImpl.class);
284            binder.bind(PageDocumentGenerator.class, PageDocumentGeneratorImpl.class);
285            binder.bind(ResponseRenderer.class, ResponseRendererImpl.class);
286            binder.bind(FieldTranslatorSource.class, FieldTranslatorSourceImpl.class);
287            binder.bind(BindingFactory.class, MessageBindingFactory.class).withId("MessageBindingFactory");
288            binder.bind(BindingFactory.class, ValidateBindingFactory.class).withId("ValidateBindingFactory");
289            binder.bind(BindingFactory.class, TranslateBindingFactory.class).withId("TranslateBindingFactory");
290            binder.bind(BindingFactory.class, AssetBindingFactory.class).withId("AssetBindingFactory");
291            binder.bind(BindingFactory.class, ContextBindingFactory.class).withId("ContextBindingFactory");
292            binder.bind(BindingFactory.class, NullFieldStrategyBindingFactory.class).withId(
293                    "NullFieldStrategyBindingFactory");
294            binder.bind(URLEncoder.class, URLEncoderImpl.class);
295            binder.bind(ContextPathEncoder.class, ContextPathEncoderImpl.class);
296            binder.bind(UpdateListenerHub.class, UpdateListenerHubImpl.class);
297            binder.bind(ApplicationStatePersistenceStrategy.class, SessionApplicationStatePersistenceStrategy.class).withId(
298                    "SessionApplicationStatePersistenceStrategy");
299            binder.bind(AssetPathConverter.class, IdentityAssetPathConverter.class);
300            binder.bind(NumericTranslatorSupport.class);
301            binder.bind(ClientDataEncoder.class, ClientDataEncoderImpl.class);
302            binder.bind(ComponentEventLinkEncoder.class, ComponentEventLinkEncoderImpl.class);
303            binder.bind(PageRenderLinkSource.class, PageRenderLinkSourceImpl.class);
304            binder.bind(ClientInfrastructure.class, ClientInfrastructureImpl.class);
305            binder.bind(URLRewriter.class, URLRewriterImpl.class);
306        }
307    
308        // ========================================================================
309        //
310        // Service Builder Methods (static)
311        //
312        // ========================================================================
313    
314        @PreventServiceDecoration
315        public static Alias buildAlias(Logger logger,
316    
317                                       @Inject @Symbol(InternalSymbols.ALIAS_MODE)
318                                       String mode,
319    
320                                       @InjectService("AliasOverrides")
321                                       AliasManager overridesManager,
322    
323                                       Collection<AliasContribution> configuration)
324        {
325            AliasManager manager = new AliasManagerImpl(logger, configuration);
326    
327            return new AliasImpl(manager, mode, overridesManager);
328        }
329    
330        // ========================================================================
331        //
332        // Service Contribution Methods (static)
333        //
334        // ========================================================================
335    
336        /**
337         * Contributes the factory for serveral built-in binding prefixes ("asset", "block", "component", "literal", prop",
338         * "nullfieldstrategy", "message", "validate", "translate", "var").
339         */
340        public static void contributeBindingSource(MappedConfiguration<String, BindingFactory> configuration,
341    
342                                                   @InjectService("PropBindingFactory")
343                                                   BindingFactory propBindingFactory,
344    
345                                                   @InjectService("MessageBindingFactory")
346                                                   BindingFactory messageBindingFactory,
347    
348                                                   @InjectService("ValidateBindingFactory")
349                                                   BindingFactory validateBindingFactory,
350    
351                                                   @InjectService("TranslateBindingFactory")
352                                                   BindingFactory translateBindingFactory,
353    
354                                                   @InjectService("AssetBindingFactory")
355                                                   BindingFactory assetBindingFactory,
356    
357                                                   @InjectService("NullFieldStrategyBindingFactory")
358                                                   BindingFactory nullFieldStrategyBindingFactory,
359    
360                                                   @InjectService("ContextBindingFactory")
361                                                   BindingFactory contextBindingFactory)
362        {
363            configuration.add(BindingConstants.LITERAL, new LiteralBindingFactory());
364            configuration.add(BindingConstants.COMPONENT, new ComponentBindingFactory());
365            configuration.add(BindingConstants.VAR, new RenderVariableBindingFactory());
366            configuration.add(BindingConstants.BLOCK, new BlockBindingFactory());
367    
368            configuration.add(BindingConstants.PROP, propBindingFactory);
369            configuration.add(BindingConstants.MESSAGE, messageBindingFactory);
370            configuration.add(BindingConstants.VALIDATE, validateBindingFactory);
371            configuration.add(BindingConstants.TRANSLATE, translateBindingFactory);
372            configuration.add(BindingConstants.ASSET, assetBindingFactory);
373            configuration.add(BindingConstants.NULLFIELDSTRATEGY, nullFieldStrategyBindingFactory);
374            configuration.add(BindingConstants.CONTEXT, contextBindingFactory);
375        }
376    
377        public static void contributeClasspathAssetAliasManager(MappedConfiguration<String, String> configuration,
378    
379                                                                @Symbol(SymbolConstants.TAPESTRY_VERSION)
380                                                                String tapestryVersion,
381    
382                                                                @Symbol(SymbolConstants.APPLICATION_VERSION)
383                                                                String applicationVersion,
384    
385                                                                @Symbol(InternalConstants.TAPESTRY_APP_PACKAGE_PARAM)
386                                                                String appPackage,
387    
388                                                                // @Inject not needed, because this isn't a service builder method
389                                                                @Symbol("tapestry.scriptaculous.path")
390                                                                String scriptaculousPath,
391    
392                                                                @Symbol("tapestry.datepicker.path")
393                                                                String datepickerPath,
394    
395                                                                @Symbol("tapestry.blackbird.path")
396                                                                String blackbirdPath)
397        {
398            // TAPESTRY-2159:  All the classpath assets are inside a version numbered folder (i.e., 5.0.12).
399            // For scriptaculous, etc., this version is not the version of the library, but the version
400            // of Tapestry.
401    
402            configuration.add("tapestry/" + tapestryVersion, "org/apache/tapestry5");
403    
404            configuration.add("scriptaculous/" + tapestryVersion, scriptaculousPath);
405    
406            configuration.add("datepicker/" + tapestryVersion, datepickerPath);
407    
408            configuration.add("blackbird/" + tapestryVersion, blackbirdPath);
409    
410            configuration.add("app/" + applicationVersion, appPackage.replace('.', '/'));
411    
412            configuration.add("classpath/" + applicationVersion, "");
413        }
414    
415        public static void contributeComponentClassResolver(Configuration<LibraryMapping> configuration)
416        {
417            configuration.add(new LibraryMapping("core", "org.apache.tapestry5.corelib"));
418        }
419    
420        /**
421         * Adds a number of standard component class transform workers: <dl> <dt>Retain </dt> <dd>Allows fields to retain
422         * their values between requests</dd> <dt>Persist </dt> <dd>Allows fields to store their their value persistently
423         * between requests</dd> <dt>Parameter </dt> <dd>Identifies parameters based on the {@link
424         * org.apache.tapestry5.annotations.Parameter} annotation</dd> <dt>Component </dt> <dd>Defines embedded components
425         * based on the {@link org.apache.tapestry5.annotations.Component} annotation</dd> <dt>Mixin </dt> <dd>Adds a mixin
426         * as part of a component's implementation</dd> <dt>Environment </dt> <dd>Allows fields to contain values extracted
427         * from the {@link org.apache.tapestry5.services.Environment} service</dd> <dt>Inject </dt> <dd>Used with the {@link
428         * org.apache.tapestry5.ioc.annotations.Inject} annotation, when a value is supplied</dd> <dt>InjectService</dt>
429         * <dd>Handles the {@link org.apache.tapestry5.ioc.annotations.InjectService} annotation</dd> <dt>InjectPage</dt>
430         * <dd>Adds code to allow access to other pages via the {@link org.apache.tapestry5.annotations.InjectPage} field
431         * annotation</dd> <dt>InjectBlock </dt> <dd>Allows a block from the template to be injected into a field</dd>
432         * <dt>IncludeStylesheet </dt> <dd>Supports the {@link org.apache.tapestry5.annotations.IncludeStylesheet}
433         * annotation</dd> <dt>IncludeJavaScriptLibrary </dt> <dd>Supports the {@link org.apache.tapestry5.annotations.IncludeJavaScriptLibrary}
434         * annotation</dd> <dt>SupportsInformalParameters </dt> <dd>Checks for the annotation</dd> <dt>Meta </dt> <dd>Checks
435         * for meta data and adds it to the component model</dd> <dt>ApplicationState </dt> <dd>Converts fields that
436         * reference application state objects <dt>UnclaimedField </dt> <dd>Identifies unclaimed fields and resets them to
437         * null/0/false at the end of the request</dd> <dt>RenderCommand </dt> <dd>Ensures all components also implement
438         * {@link org.apache.tapestry5.runtime.RenderCommand}</dd> <dt>SetupRender, BeginRender, etc. </dt> <dd>Correspond
439         * to component render phases and annotations</dd> <dt>InvokePostRenderCleanupOnResources </dt> <dd>Makes sure
440         * {@link org.apache.tapestry5.internal.InternalComponentResources#postRenderCleanup()} is invoked after a component
441         * finishes rendering</dd> <dt>Secure</dt> <dd>Checks for the {@link org.apache.tapestry5.annotations.Secure}
442         * annotation</dd> <dt>ContentType</dt> <dd>Checks for {@link org.apache.tapestry5.annotations.ContentType}
443         * annotation</dd> <dt>GenerateAccessors</dt> <dd>Generates accessor methods if {@link
444         * org.apache.tapestry5.annotations.Property} annotation is present </dd> <dt>Cached</dt> <dd>Checks for the {@link
445         * org.apache.tapestry5.annotations.Cached} annotation</dd><dt>Log</dt> <dd>Checks for the {@link
446         * org.apache.tapestry5.annotations.Log} annotation</dd></dl>
447         */
448        public static void contributeComponentClassTransformWorker(
449                OrderedConfiguration<ComponentClassTransformWorker> configuration,
450    
451                ObjectLocator locator,
452    
453                InjectionProvider injectionProvider,
454    
455                ComponentClassResolver resolver)
456        {
457            // TODO: Proper scheduling of all of this. Since a given field or method should
458            // only have a single annotation, the order doesn't matter so much, as long as
459            // UnclaimedField is last.
460    
461            configuration.addInstance("Cached", CachedWorker.class);
462    
463            configuration.add("Meta", new MetaWorker());
464    
465            configuration.add("Inject", new InjectWorker(locator, injectionProvider));
466            configuration.addInstance("InjectService", InjectServiceWorker.class);
467    
468            configuration.add("Secure", new SecureWorker());
469    
470            configuration.add("MixinAfter", new MixinAfterWorker());
471            configuration.add("Component", new ComponentWorker(resolver));
472            configuration.add("Mixin", new MixinWorker(resolver));
473            configuration.add("OnEvent", new OnEventWorker());
474            configuration.add("SupportsInformalParameters", new SupportsInformalParametersWorker());
475            configuration.addInstance("InjectPage", InjectPageWorker.class);
476            configuration.add("InjectContainer", new InjectContainerWorker());
477            configuration.add("InjectComponent", new InjectComponentWorker());
478            configuration.add("RenderCommand", new RenderCommandWorker());
479    
480            // Default values for parameters are often some form of injection, so make sure
481            // that Parameter fields are processed after injections.
482    
483            configuration.addInstance("Parameter", ParameterWorker.class, "after:Inject*");
484    
485            // Workers for the component rendering state machine methods; this is in typical
486            // execution order.
487    
488            add(configuration, TransformConstants.SETUP_RENDER_SIGNATURE, SetupRender.class, false);
489            add(configuration, TransformConstants.BEGIN_RENDER_SIGNATURE, BeginRender.class, false);
490            add(configuration, TransformConstants.BEFORE_RENDER_TEMPLATE_SIGNATURE, BeforeRenderTemplate.class, false);
491            add(configuration, TransformConstants.BEFORE_RENDER_BODY_SIGNATURE, BeforeRenderBody.class, false);
492    
493            // These phases operate in reverse order.
494    
495            add(configuration, TransformConstants.AFTER_RENDER_BODY_SIGNATURE, AfterRenderBody.class, true);
496            add(configuration, TransformConstants.AFTER_RENDER_TEMPLATE_SIGNATURE, AfterRenderTemplate.class, true);
497            add(configuration, TransformConstants.AFTER_RENDER_SIGNATURE, AfterRender.class, true);
498            add(configuration, TransformConstants.CLEANUP_RENDER_SIGNATURE, CleanupRender.class, true);
499    
500            // Ideally, these should be ordered pretty late in the process to make sure there are no
501            // side effects with other workers that do work inside the page lifecycle methods.
502    
503            add(configuration, PageLoaded.class, TransformConstants.CONTAINING_PAGE_DID_LOAD_SIGNATURE, "pageLoaded");
504            add(configuration, PageAttached.class, TransformConstants.CONTAINING_PAGE_DID_ATTACH_SIGNATURE, "pageAttached");
505            add(configuration, PageDetached.class, TransformConstants.CONTAINING_PAGE_DID_DETACH_SIGNATURE, "pageDetached");
506    
507            configuration.add("Retain", new RetainWorker());
508            configuration.add("Persist", new PersistWorker());
509    
510            configuration.addInstance("IncludeStylesheet", IncludeStylesheetWorker.class, "after:SetupRender");
511            configuration.addInstance("IncludeJavaScriptLibrary", IncludeJavaScriptLibraryWorker.class,
512                                      "after:SetupRender");
513    
514            configuration.add("InvokePostRenderCleanupOnResources", new InvokePostRenderCleanupOnResourcesWorker());
515    
516            configuration.add("ContentType", new ContentTypeWorker());
517    
518            configuration.add("Property", new PropertyWorker());
519    
520            // These must come after Property, since they actually delete fields that may still have the annotation
521            configuration.addInstance("ApplicationState", ApplicationStateWorker.class, "after:Property");
522            configuration.addInstance("Environment", EnvironmentalWorker.class, "after:Property");
523    
524            configuration.addInstance("Log", LogWorker.class);
525    
526            // This one is always last. Any additional private fields that aren't annotated will
527            // be converted to clear out at the end of the request.
528    
529            configuration.add("UnclaimedField", new UnclaimedFieldWorker(), "after:*");
530    
531            configuration.add("PageActivationContext", new PageActivationContextWorker(), "before:OnEvent");
532        }
533    
534        /**
535         * <dl> <dt>Annotation</dt> <dd>Checks for {@link org.apache.tapestry5.beaneditor.DataType} annotation</dd>
536         * <dt>Default  (ordered last)</dt> <dd>{@link org.apache.tapestry5.internal.services.DefaultDataTypeAnalyzer}
537         * service ({@link #contributeDefaultDataTypeAnalyzer(org.apache.tapestry5.ioc.MappedConfiguration)} })</dd> </dl>
538         */
539        public static void contributeDataTypeAnalyzer(OrderedConfiguration<DataTypeAnalyzer> configuration,
540                                                      @InjectService("DefaultDataTypeAnalyzer")
541                                                      DataTypeAnalyzer defaultDataTypeAnalyzer)
542        {
543            configuration.add("Annotation", new AnnotationDataTypeAnalyzer());
544            configuration.add("Default", defaultDataTypeAnalyzer, "after:*");
545        }
546    
547        /**
548         * Maps property types to data type names: <ul> <li>String --&gt; text <li>Number --&gt; number <li>Enum --&gt; enum
549         * <li>Boolean --&gt; boolean <li>Date --&gt; date </ul>
550         */
551        public static void contributeDefaultDataTypeAnalyzer(MappedConfiguration<Class, String> configuration)
552        {
553            // This is a special case contributed to avoid exceptions when a property type can't be
554            // matched. DefaultDataTypeAnalyzer converts the empty string to null.
555    
556            configuration.add(Object.class, "");
557    
558            configuration.add(String.class, "text");
559            configuration.add(Number.class, "number");
560            configuration.add(Enum.class, "enum");
561            configuration.add(Boolean.class, "boolean");
562            configuration.add(Date.class, "date");
563        }
564    
565        public static void contributeBeanBlockSource(Configuration<BeanBlockContribution> configuration)
566        {
567            addEditBlock(configuration, "text");
568            addEditBlock(configuration, "number");
569            addEditBlock(configuration, "enum");
570            addEditBlock(configuration, "boolean");
571            addEditBlock(configuration, "date");
572            addEditBlock(configuration, "password");
573    
574            // longtext uses a text area, not a text field
575    
576            addEditBlock(configuration, "longtext");
577    
578            addDisplayBlock(configuration, "enum");
579            addDisplayBlock(configuration, "date");
580    
581            // Password and long text have special output needs.
582            addDisplayBlock(configuration, "password");
583            addDisplayBlock(configuration, "longtext");
584        }
585    
586        private static void addEditBlock(Configuration<BeanBlockContribution> configuration, String dataType)
587        {
588            addEditBlock(configuration, dataType, dataType);
589        }
590    
591        private static void addEditBlock(Configuration<BeanBlockContribution> configuration, String dataType,
592                                         String blockId)
593        {
594            configuration.add(new BeanBlockContribution(dataType, "PropertyEditBlocks", blockId, true));
595        }
596    
597        private static void addDisplayBlock(Configuration<BeanBlockContribution> configuration, String dataType)
598        {
599            addDisplayBlock(configuration, dataType, dataType);
600        }
601    
602        private static void addDisplayBlock(Configuration<BeanBlockContribution> configuration, String dataType,
603                                            String blockId)
604        {
605            configuration.add(new BeanBlockContribution(dataType, "PropertyDisplayBlocks", blockId, false));
606        }
607    
608        /**
609         * Contributes the basic set of validators: <ul> <li>required</li> <li>minlength</li> <li>maxlength</li>
610         * <li>min</li> <li>max</li> <li>regexp</li> </ul>
611         */
612        public static void contributeFieldValidatorSource(MappedConfiguration<String, Validator> configuration)
613        {
614            configuration.add("required", new Required());
615            configuration.add("minlength", new MinLength());
616            configuration.add("maxlength", new MaxLength());
617            configuration.add("min", new Min());
618            configuration.add("max", new Max());
619            configuration.add("regexp", new Regexp());
620            configuration.add("email", new Email());
621        }
622    
623        /**
624         * Contributes the base set of injection providers: <dl> <dt>Default</dt> <dd>based on {@link
625         * MasterObjectProvider}</dd> <dt>Block</dt> <dd>injects fields of type Block</dd> <dt>ComponentResources</dt>
626         * <dd>give component access to its resources</dd> <dt>CommonResources</dt> <dd>access to properties of resources
627         * (log, messages, etc.)</dd> <dt>Asset</dt> <dd>injection of assets (triggered via {@link Path} annotation), with
628         * the path relative to the component class</dd> <dt>Service</dt> <dd>ordered last, for use when Inject is present
629         * and nothing else works, matches field type against Tapestry IoC services</dd> </dl>
630         */
631        public static void contributeInjectionProvider(OrderedConfiguration<InjectionProvider> configuration,
632    
633                                                       MasterObjectProvider masterObjectProvider,
634    
635                                                       ObjectLocator locator,
636    
637                                                       SymbolSource symbolSource,
638    
639                                                       AssetSource assetSource)
640        {
641            configuration.add("Default", new DefaultInjectionProvider(masterObjectProvider, locator));
642    
643            configuration.add("ComponentResources", new ComponentResourcesInjectionProvider());
644    
645            // This comes after default, to deal with conflicts between injecting a String as the
646            // component id, and injecting a string with @Symbol or @Value.
647    
648            configuration.add("CommonResources", new CommonResourcesInjectionProvider(), "after:Default");
649    
650            configuration.add("Asset", new AssetInjectionProvider(symbolSource, assetSource), "before:Default");
651    
652            configuration.add("Block", new BlockInjectionProvider(), "before:Default");
653    
654            // This needs to be the last one, since it matches against services
655            // and might blow up if there is no match.
656            configuration.add("Service", new ServiceInjectionProvider(locator), "after:*");
657        }
658    
659        /**
660         * Contributes two object providers: <dl> <dt>Alias</dt> <dd> Searches by type among {@linkplain AliasContribution
661         * contributions} to the {@link Alias} service</dd> <dt>Asset<dt> <dd> Checks for the {@link Path} annotation, and
662         * injects an {@link Asset}</dd> <dt>Service</dt> <dd>Injects based on the {@link Service} annotation, if
663         * present</dd> </dl>
664         */
665        public static void contributeMasterObjectProvider(OrderedConfiguration<ObjectProvider> configuration,
666    
667                                                          @Local
668                                                          final Alias alias,
669    
670                                                          @InjectService("AssetObjectProvider")
671                                                          ObjectProvider assetObjectProvider)
672        {
673            // There's a nasty web of dependencies related to Alias; this wrapper class lets us
674            // defer instantiating the Alias service implementation just long enough to defuse those
675            // dependencies. The @Local annotation prevents a recursive call through the
676            // MasterObjectProvider to resolve the Alias service itself; that is MasterObjectProvider
677            // gets built using this proxy, then the proxy will trigger the construction of AliasImpl
678            // (which itself needs MasterObjectProvider to resolve some dependencies).
679    
680            ObjectProvider wrapper = new ObjectProvider()
681            {
682                public <T> T provide(Class<T> objectType, AnnotationProvider annotationProvider, ObjectLocator locator)
683                {
684                    return alias.getObjectProvider().provide(objectType, annotationProvider, locator);
685                }
686            };
687    
688            configuration.add("Alias", wrapper, "after:ServiceOverride");
689    
690            configuration.add("Asset", assetObjectProvider, "before:AnnotationBasedContributions");
691    
692            configuration.add("Service", new ServiceAnnotationObjectProvider(), "before:AnnotationBasedContributions");
693        }
694    
695    
696        /**
697         * <dl> <dt>StoreIntoGlobals</dt> <dd>Stores the request and response into {@link
698         * org.apache.tapestry5.services.RequestGlobals} at the start of the pipeline</dd> <dt>IgnoredPaths</dt>
699         * <dd>Identifies requests that are known (via the IgnoredPathsFilter service's configuration) to be mapped to other
700         * applications</dd> <dt>GZip</dt> <dd>Handles GZIP compression of response streams (if supported by client)</dd>
701         */
702        public void contributeHttpServletRequestHandler(OrderedConfiguration<HttpServletRequestFilter> configuration,
703    
704                                                        @Symbol(SymbolConstants.GZIP_COMPRESSION_ENABLED)
705                                                        boolean gzipCompressionEnabled,
706    
707                                                        @Autobuild GZipFilter gzipFilter,
708    
709                                                        @InjectService("IgnoredPathsFilter")
710                                                        HttpServletRequestFilter ignoredPathsFilter)
711        {
712            configuration.add("IgnoredPaths", ignoredPathsFilter);
713    
714            configuration.add("GZIP",
715                              gzipCompressionEnabled ? gzipFilter : null,
716                              "after:IgnoredPaths");
717    
718            HttpServletRequestFilter storeIntoGlobals = new HttpServletRequestFilter()
719            {
720                public boolean service(HttpServletRequest request, HttpServletResponse response,
721                                       HttpServletRequestHandler handler)
722                        throws IOException
723                {
724                    requestGlobals.storeServletRequestResponse(request, response);
725    
726                    return handler.service(request, response);
727                }
728            };
729    
730            configuration.add("StoreIntoGlobals", storeIntoGlobals, "before:*");
731        }
732    
733        /**
734         * Continues a number of filters into the RequestHandler service: <dl> <dt>StaticFiles</dt> <dd>Checks to see if the
735         * request is for an actual file, if so, returns true to let the servlet container process the request</dd>
736         * <dt>CheckForUpdates</dt> <dd>Periodically fires events that checks to see if the file system sources for any
737         * cached data has changed (see {@link org.apache.tapestry5.internal.services.CheckForUpdatesFilter}).
738         * <dt>ErrorFilter</dt> <dd>Catches request errors and lets the {@link org.apache.tapestry5.services.RequestExceptionHandler}
739         * handle them</dd>  <dt>StoreIntoGlobals</dt> <dd>Stores the request and response into the {@link
740         * org.apache.tapestry5.services.RequestGlobals} service (this is repeated at the end of the pipeline, in case any
741         * filter substitutes the request or response).  </dl>
742         */
743        public void contributeRequestHandler(OrderedConfiguration<RequestFilter> configuration, Context context,
744    
745                                             // @Inject not needed because its a long, not a String
746                                             @Symbol(SymbolConstants.FILE_CHECK_INTERVAL)
747                                             @IntermediateType(TimeInterval.class)
748                                             long checkInterval,
749    
750                                             @Symbol(SymbolConstants.FILE_CHECK_UPDATE_TIMEOUT)
751                                             @IntermediateType(TimeInterval.class)
752                                             long updateTimeout,
753    
754                                             UpdateListenerHub updateListenerHub,
755    
756                                             URLRewriter urlRewriter)
757        {
758            RequestFilter staticFilesFilter = new StaticFilesFilter(context);
759    
760            RequestFilter storeIntoGlobals = new RequestFilter()
761            {
762                public boolean service(Request request, Response response, RequestHandler handler)
763                        throws IOException
764                {
765                    requestGlobals.storeRequestResponse(request, response);
766    
767                    return handler.service(request, response);
768                }
769            };
770    
771            RequestFilter fireEndOfRequestEvent = new RequestFilter()
772            {
773                public boolean service(Request request, Response response, RequestHandler handler)
774                        throws IOException
775                {
776                    try
777                    {
778                        return handler.service(request, response);
779                    }
780                    finally
781                    {
782                        endOfRequestEventHub.fire();
783                    }
784                }
785            };
786    
787            configuration.add("CheckForUpdates", new CheckForUpdatesFilter(updateListenerHub,
788                                                                           checkInterval, updateTimeout), "before:*");
789    
790            // we just need the URLRewriterRequestFilter if we have URL rewriter rules, of course.
791            if (urlRewriter.hasRequestRules())
792            {
793    
794                URLRewriterRequestFilter urlRewriterRequestFilter = new URLRewriterRequestFilter(
795                        urlRewriter);
796                configuration.add("URLRewriter", urlRewriterRequestFilter, "before:StaticFiles");
797    
798            }
799    
800            configuration.add("StaticFiles", staticFilesFilter);
801    
802            configuration.addInstance("ErrorFilter", RequestErrorFilter.class);
803    
804            configuration.add(
805                    "StoreIntoGlobals",
806                    storeIntoGlobals,
807                    "after:StaticFiles",
808                    "before:ErrorFilter");
809    
810            configuration.add(
811                    "EndOfRequest",
812                    fireEndOfRequestEvent,
813                    "after:StoreIntoGlobals",
814                    "before:ErrorFilter");
815    
816        }
817    
818        /**
819         * Contributes the basic set of translators: <ul>  <li>string</li>  <li>byte</li> <li>short</li> <li>integer</li>
820         * <li>long</li> <li>float</li> <li>double</li>  <li>BigInteger</li> <li>BigDecimal</li></ul>
821         */
822        public static void contributeTranslatorSource(Configuration<Translator> configuration,
823                                                      NumericTranslatorSupport support)
824        {
825    
826            configuration.add(new StringTranslator());
827    
828            Class[] types = new Class[] { Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class,
829                    BigInteger.class, BigDecimal.class };
830    
831            for (Class type : types)
832            {
833                String name = type.getSimpleName().toLowerCase();
834    
835                configuration.add(new NumericTranslator(name, type, support));
836            }
837        }
838    
839    
840        /**
841         * Adds coercions: <ul> <li>String to {@link org.apache.tapestry5.SelectModel} <li>String to {@link
842         * org.apache.tapestry5.corelib.data.InsertPosition} <li>Map to {@link org.apache.tapestry5.SelectModel}
843         * <li>Collection to {@link GridDataSource} <li>null to {@link org.apache.tapestry5.grid.GridDataSource} <li>String
844         * to {@link org.apache.tapestry5.corelib.data.GridPagerPosition} <li>List to {@link
845         * org.apache.tapestry5.SelectModel} <li>{@link org.apache.tapestry5.runtime.ComponentResourcesAware} (typically, a
846         * component) to {@link org.apache.tapestry5.ComponentResources} <li>String to {@link
847         * org.apache.tapestry5.corelib.data.BlankOption} <li> {@link org.apache.tapestry5.ComponentResources} to {@link
848         * org.apache.tapestry5.PropertyOverrides} <li>String to {@link org.apache.tapestry5.Renderable} <li>{@link
849         * org.apache.tapestry5.Renderable} to {@link org.apache.tapestry5.Block} <li>String to {@link java.text.DateFormat}
850         * <li>{@link org.apache.tapestry5.PrimaryKeyEncoder} to {@link org.apache.tapestry5.ValueEncoder} <li>String to
851         * {@link org.apache.tapestry5.ioc.Resource} (via {@link org.apache.tapestry5.services.AssetSource#resourceForPath(String)})
852         * <li>{@link org.apache.tapestry5.Renderable} to {@link org.apache.tapestry5.runtime.RenderCommand}</li> </ul>
853         */
854        public static void contributeTypeCoercer(Configuration<CoercionTuple> configuration,
855    
856                                                 @Builtin
857                                                 TypeCoercer coercer,
858    
859                                                 @Builtin
860                                                 final ThreadLocale threadLocale,
861    
862                                                 @Core
863                                                 final AssetSource assetSource)
864        {
865            add(configuration, ComponentResources.class, PropertyOverrides.class,
866                new Coercion<ComponentResources, PropertyOverrides>()
867                {
868                    public PropertyOverrides coerce(ComponentResources input)
869                    {
870                        return new PropertyOverridesImpl(input);
871                    }
872                });
873    
874            add(configuration, String.class, SelectModel.class, new Coercion<String, SelectModel>()
875            {
876                public SelectModel coerce(String input)
877                {
878                    return TapestryInternalUtils.toSelectModel(input);
879                }
880            });
881    
882            add(configuration, Map.class, SelectModel.class, new Coercion<Map, SelectModel>()
883            {
884                @SuppressWarnings("unchecked")
885                public SelectModel coerce(Map input)
886                {
887                    return TapestryInternalUtils.toSelectModel(input);
888                }
889            });
890    
891            add(configuration, Collection.class, GridDataSource.class, new Coercion<Collection, GridDataSource>()
892            {
893                public GridDataSource coerce(Collection input)
894                {
895                    return new CollectionGridDataSource(input);
896                }
897            });
898    
899            add(configuration, void.class, GridDataSource.class, new Coercion<Void, GridDataSource>()
900            {
901                private final GridDataSource source = new NullDataSource();
902    
903                public GridDataSource coerce(Void input)
904                {
905                    return source;
906                }
907            });
908    
909            add(configuration, String.class, GridPagerPosition.class,
910                StringToEnumCoercion.create(GridPagerPosition.class));
911    
912            add(configuration, String.class, InsertPosition.class, StringToEnumCoercion.create(InsertPosition.class));
913    
914            add(configuration, String.class, BlankOption.class, StringToEnumCoercion.create(BlankOption.class));
915    
916            add(configuration, String.class, LoopFormState.class, StringToEnumCoercion.create(LoopFormState.class));
917    
918            add(configuration, List.class, SelectModel.class, new Coercion<List, SelectModel>()
919            {
920                @SuppressWarnings("unchecked")
921                public SelectModel coerce(List input)
922                {
923                    return TapestryInternalUtils.toSelectModel(input);
924                }
925            });
926    
927            add(configuration, String.class, Pattern.class, new Coercion<String, Pattern>()
928            {
929                public Pattern coerce(String input)
930                {
931                    return Pattern.compile(input);
932                }
933            });
934    
935            add(configuration, ComponentResourcesAware.class, ComponentResources.class,
936                new Coercion<ComponentResourcesAware, ComponentResources>()
937                {
938    
939                    public ComponentResources coerce(ComponentResourcesAware input)
940                    {
941                        return input.getComponentResources();
942                    }
943                });
944    
945            add(configuration, String.class, Renderable.class, new Coercion<String, Renderable>()
946            {
947                public Renderable coerce(String input)
948                {
949                    return new StringRenderable(input);
950                }
951            });
952    
953            add(configuration, Renderable.class, Block.class, new Coercion<Renderable, Block>()
954            {
955                public Block coerce(Renderable input)
956                {
957                    return new RenderableAsBlock(input);
958                }
959            });
960    
961            add(configuration, String.class, DateFormat.class, new Coercion<String, DateFormat>()
962            {
963                public DateFormat coerce(String input)
964                {
965                    return new SimpleDateFormat(input, threadLocale.getLocale());
966                }
967            });
968    
969    
970            add(configuration, String.class, Resource.class, new Coercion<String, Resource>()
971            {
972                public Resource coerce(String input)
973                {
974                    return assetSource.resourceForPath(input);
975                }
976            });
977    
978            add(configuration, Renderable.class, RenderCommand.class, new Coercion<Renderable, RenderCommand>()
979            {
980                public RenderCommand coerce(final Renderable input)
981                {
982                    return new RenderCommand()
983                    {
984                        public void render(MarkupWriter writer, RenderQueue queue)
985                        {
986                            input.render(writer);
987                        }
988                    };
989                }
990            });
991    
992            add(configuration, PrimaryKeyEncoder.class, ValueEncoder.class, new PrimaryKeyEncoder2ValueEncoder(coercer));
993        }
994    
995        /**
996         * Adds built-in constraint generators: <ul> <li>PrimtiveField -- primitive fields are always required
997         * <li>ValidateAnnotation -- adds constraints from a {@link Validate} annotation </ul>
998         */
999        public static void contributeValidationConstraintGenerator(
1000                OrderedConfiguration<ValidationConstraintGenerator> configuration)
1001        {
1002            configuration.add("PrimitiveField", new PrimitiveFieldConstraintGenerator());
1003            configuration.add("ValidateAnnotation", new ValidateAnnotationConstraintGenerator());
1004        }
1005    
1006        private static <S, T> void add(Configuration<CoercionTuple> configuration, Class<S> sourceType, Class<T> targetType,
1007                                       Coercion<S, T> coercion)
1008        {
1009            CoercionTuple<S, T> tuple = new CoercionTuple<S, T>(sourceType, targetType, coercion);
1010    
1011            configuration.add(tuple);
1012        }
1013    
1014        private static void add(OrderedConfiguration<ComponentClassTransformWorker> configuration,
1015                                Class<? extends Annotation> annotationClass,
1016                                TransformMethodSignature lifecycleMethodSignature, String methodAlias)
1017        {
1018            ComponentClassTransformWorker worker = new PageLifecycleAnnotationWorker(annotationClass,
1019                                                                                     lifecycleMethodSignature, methodAlias);
1020    
1021            String name = TapestryInternalUtils.lastTerm(annotationClass.getName());
1022    
1023            configuration.add(name, worker);
1024        }
1025    
1026        private static void add(OrderedConfiguration<ComponentClassTransformWorker> configuration,
1027                                TransformMethodSignature signature, Class<? extends Annotation> annotationClass,
1028                                boolean reverse)
1029        {
1030            // make the name match the annotation class name.
1031    
1032            String name = annotationClass.getSimpleName();
1033    
1034            configuration.add(name, new RenderPhaseMethodWorker(signature, annotationClass, reverse));
1035        }
1036    
1037        // ========================================================================
1038        //
1039        // Service Builder Methods (instance)
1040        //
1041        // ========================================================================
1042    
1043        public Context buildContext(ApplicationGlobals globals)
1044        {
1045            return shadowBuilder.build(globals, "context", Context.class);
1046        }
1047    
1048        public static ComponentClassResolver buildComponentClassResolver(@Autobuild ComponentClassResolverImpl service,
1049                                                                         @ComponentClasses InvalidationEventHub hub)
1050        {
1051            // Allow the resolver to clean its cache when the component classes change
1052    
1053            hub.addInvalidationListener(service);
1054    
1055            return service;
1056        }
1057    
1058        @Marker(ClasspathProvider.class)
1059        public AssetFactory buildClasspathAssetFactory(ResourceCache resourceCache, ClasspathAssetAliasManager aliasManager,
1060                                                       AssetPathConverter converter)
1061        {
1062            ClasspathAssetFactory factory = new ClasspathAssetFactory(resourceCache, aliasManager, converter);
1063    
1064            resourceCache.addInvalidationListener(factory);
1065    
1066            return factory;
1067        }
1068    
1069        @Marker(ContextProvider.class)
1070        public AssetFactory buildContextAssetFactory(ApplicationGlobals globals,
1071    
1072                                                     @Inject @Symbol(SymbolConstants.APPLICATION_VERSION)
1073                                                     String applicationVersion,
1074    
1075                                                     AssetPathConverter converter)
1076        {
1077            return new ContextAssetFactory(request, globals.getContext(), applicationVersion, converter);
1078        }
1079    
1080        /**
1081         * Builds the PropBindingFactory as a chain of command. The terminator of the chain is responsible for ordinary
1082         * property names (and property paths).
1083         * <p/>
1084         * This mechanism has been replaced in 5.1 with a more sophisticated parser based on ANTLR. See <a
1085         * href="https://issues.apache.org/jira/browse/TAP5-79">TAP5-79</a> for details.  There are no longer any built-in
1086         * contributions to the configuration.
1087         *
1088         * @param configuration contributions of special factories for some constants, each contributed factory may return a
1089         *                      binding if applicable, or null otherwise
1090         */
1091        public BindingFactory buildPropBindingFactory(List<BindingFactory> configuration,
1092                                                      @Autobuild PropBindingFactory service)
1093        {
1094            configuration.add(service);
1095    
1096            return chainBuilder.build(BindingFactory.class, configuration);
1097        }
1098    
1099        /**
1100         * Builds the source of {@link Messages} containing validation messages. The contributions are paths to message
1101         * bundles (resource paths within the classpath); the default contribution is "org/apache/tapestry5/internal/ValidationMessages".
1102         */
1103        public ValidationMessagesSource buildValidationMessagesSource(List<String> configuration,
1104    
1105                                                                      UpdateListenerHub updateListenerHub,
1106    
1107                                                                      @ClasspathProvider AssetFactory classpathAssetFactory,
1108    
1109                                                                      ClasspathURLConverter classpathURLConverter)
1110        {
1111            ValidationMessagesSourceImpl service = new ValidationMessagesSourceImpl(configuration,
1112                                                                                    classpathAssetFactory.getRootResource(),
1113                                                                                    classpathURLConverter);
1114            updateListenerHub.addUpdateListener(service);
1115    
1116            return service;
1117        }
1118    
1119        public static MetaDataLocator buildMetaDataLocator(@Autobuild MetaDataLocatorImpl service,
1120                                                           @ComponentClasses InvalidationEventHub hub)
1121        {
1122            hub.addInvalidationListener(service);
1123    
1124            return service;
1125        }
1126    
1127        public PersistentFieldStrategy buildClientPersistentFieldStrategy(LinkCreationHub linkCreationHub,
1128                                                                          @Autobuild ClientPersistentFieldStrategy service)
1129        {
1130            linkCreationHub.addListener(service);
1131    
1132            return service;
1133        }
1134    
1135        /**
1136         * Builds a proxy to the current {@link org.apache.tapestry5.RenderSupport} inside this thread's {@link
1137         * org.apache.tapestry5.services.Environment}.
1138         */
1139        public RenderSupport buildRenderSupport()
1140        {
1141            return environmentalBuilder.build(RenderSupport.class);
1142        }
1143    
1144        /**
1145         * Builds a proxy to the current {@link org.apache.tapestry5.services.ClientBehaviorSupport} inside this thread's
1146         * {@link org.apache.tapestry5.services.Environment}.
1147         *
1148         * @since 5.1.0.1
1149         */
1150    
1151        public ClientBehaviorSupport buildClientBehaviorSupport()
1152        {
1153            return environmentalBuilder.build(ClientBehaviorSupport.class);
1154        }
1155    
1156        /**
1157         * Builds a proxy to the current {@link org.apache.tapestry5.services.FormSupport} inside this thread's {@link
1158         * org.apache.tapestry5.services.Environment}.
1159         */
1160        public FormSupport buildFormSupport()
1161        {
1162            return environmentalBuilder.build(FormSupport.class);
1163        }
1164    
1165        /**
1166         * Allows the exact steps in the component class transformation process to be defined.
1167         */
1168        public ComponentClassTransformWorker buildComponentClassTransformWorker(
1169                List<ComponentClassTransformWorker> configuration)
1170        {
1171            return chainBuilder.build(ComponentClassTransformWorker.class, configuration);
1172        }
1173    
1174        /**
1175         * Analyzes properties to determine the data types, used to {@linkplain #contributeBeanBlockSource(org.apache.tapestry5.ioc.Configuration)}
1176         * locale display and edit blocks} for properties.  The default behaviors look for a {@link
1177         * org.apache.tapestry5.beaneditor.DataType} annotation before deriving the data type from the property type.
1178         */
1179        @Marker(Primary.class)
1180        public DataTypeAnalyzer buildDataTypeAnalyzer(List<DataTypeAnalyzer> configuration)
1181        {
1182            return chainBuilder.build(DataTypeAnalyzer.class, configuration);
1183        }
1184    
1185        /**
1186         * A chain of command for providing values for {@link Inject}-ed fields in component classes. The service's
1187         * configuration can be extended to allow for different automatic injections (based on some combination of field
1188         * type and field name).
1189         */
1190    
1191        public InjectionProvider buildInjectionProvider(List<InjectionProvider> configuration)
1192        {
1193            return chainBuilder.build(InjectionProvider.class, configuration);
1194        }
1195    
1196    
1197        /**
1198         * Initializes the application, using a pipeline of {@link org.apache.tapestry5.services.ApplicationInitializer}s.
1199         */
1200        @Marker(Primary.class)
1201        public ApplicationInitializer buildApplicationInitializer(Logger logger,
1202                                                                  List<ApplicationInitializerFilter> configuration)
1203        {
1204            ApplicationInitializer terminator = new ApplicationInitializerTerminator();
1205    
1206            return pipelineBuilder.build(logger, ApplicationInitializer.class, ApplicationInitializerFilter.class,
1207                                         configuration, terminator);
1208        }
1209    
1210        public HttpServletRequestHandler buildHttpServletRequestHandler(Logger logger,
1211    
1212                                                                        List<HttpServletRequestFilter> configuration,
1213    
1214                                                                        @Primary
1215                                                                        RequestHandler handler,
1216    
1217                                                                        @Inject @Symbol(SymbolConstants.CHARSET)
1218                                                                        String applicationCharset,
1219    
1220                                                                        @Primary
1221                                                                        SessionPersistedObjectAnalyzer analyzer)
1222        {
1223            HttpServletRequestHandler terminator = new HttpServletRequestHandlerTerminator(handler, applicationCharset,
1224                                                                                           analyzer);
1225    
1226            return pipelineBuilder.build(logger, HttpServletRequestHandler.class, HttpServletRequestFilter.class,
1227                                         configuration, terminator);
1228        }
1229    
1230        @Marker(Primary.class)
1231        public RequestHandler buildRequestHandler(Logger logger, List<RequestFilter> configuration,
1232    
1233                                                  @Primary
1234                                                  Dispatcher masterDispatcher)
1235        {
1236            RequestHandler terminator = new RequestHandlerTerminator(masterDispatcher);
1237    
1238            return pipelineBuilder.build(logger, RequestHandler.class, RequestFilter.class, configuration, terminator);
1239        }
1240    
1241        public ServletApplicationInitializer buildServletApplicationInitializer(Logger logger,
1242                                                                                List<ServletApplicationInitializerFilter> configuration,
1243    
1244                                                                                @Primary
1245                                                                                ApplicationInitializer initializer)
1246        {
1247            ServletApplicationInitializer terminator = new ServletApplicationInitializerTerminator(initializer);
1248    
1249            return pipelineBuilder.build(logger, ServletApplicationInitializer.class,
1250                                         ServletApplicationInitializerFilter.class, configuration, terminator);
1251        }
1252    
1253        /**
1254         * The component event result processor used for normal component requests.
1255         */
1256        @Marker({ Primary.class, Traditional.class })
1257        public ComponentEventResultProcessor buildComponentEventResultProcessor(
1258                Map<Class, ComponentEventResultProcessor> configuration)
1259        {
1260            return constructComponentEventResultProcessor(configuration);
1261        }
1262    
1263        /**
1264         * The component event result processor used for Ajax-oriented component requests.
1265         */
1266        @Marker(Ajax.class)
1267        public ComponentEventResultProcessor buildAjaxComponentEventResultProcessor(
1268                Map<Class, ComponentEventResultProcessor> configuration)
1269        {
1270            return constructComponentEventResultProcessor(configuration);
1271        }
1272    
1273        private ComponentEventResultProcessor constructComponentEventResultProcessor(
1274                Map<Class, ComponentEventResultProcessor> configuration)
1275        {
1276            Set<Class> handledTypes = CollectionFactory.newSet(configuration.keySet());
1277    
1278            // A slight hack!
1279    
1280            configuration.put(Object.class, new ObjectComponentEventResultProcessor(handledTypes));
1281    
1282            StrategyRegistry<ComponentEventResultProcessor> registry = StrategyRegistry.newInstance(
1283                    ComponentEventResultProcessor.class, configuration);
1284    
1285            return strategyBuilder.build(registry);
1286        }
1287    
1288        /**
1289         * The default data type analyzer is the final analyzer consulted and identifies the type entirely pased on the
1290         * property type, working against its own configuration (mapping property type class to data type).
1291         */
1292        public static DataTypeAnalyzer buildDefaultDataTypeAnalyzer(@Autobuild DefaultDataTypeAnalyzer service,
1293                                                                    @ComponentClasses InvalidationEventHub hub)
1294        {
1295            hub.addInvalidationListener(service);
1296    
1297            return service;
1298        }
1299    
1300        public static TranslatorSource buildTranslatorSource(@Autobuild TranslatorSourceImpl service,
1301                                                             @ComponentClasses InvalidationEventHub hub)
1302        {
1303            hub.addInvalidationListener(service);
1304    
1305            return service;
1306        }
1307    
1308        @Marker(Primary.class)
1309        public ObjectRenderer buildObjectRenderer(Map<Class, ObjectRenderer> configuration)
1310        {
1311            return strategyBuilder.build(ObjectRenderer.class, configuration);
1312        }
1313    
1314    
1315        /**
1316         * Returns a {@link org.apache.tapestry5.ioc.services.ClassFactory} that can be used to create extra classes around
1317         * component classes. This ClassFactory will be cleared whenever an underlying component class is discovered to have
1318         * changed. Use of this class factory implies that your code will become aware of this (if necessary) to discard any
1319         * cached object (alas, this currently involves dipping into the internals side to register for the correct
1320         * notifications). Failure to properly clean up can result in really nasty PermGen space memory leaks.
1321         */
1322        @Marker(ComponentLayer.class)
1323        public ClassFactory buildComponentClassFactory(ComponentInstantiatorSource source)
1324        {
1325            return shadowBuilder.build(source, "classFactory", ClassFactory.class);
1326        }
1327    
1328    
1329        /**
1330         * Ordered contributions to the MasterDispatcher service allow different URL matching strategies to occur.
1331         */
1332        @Marker(Primary.class)
1333        public Dispatcher buildMasterDispatcher(List<Dispatcher> configuration)
1334        {
1335            return chainBuilder.build(Dispatcher.class, configuration);
1336        }
1337    
1338        public PropertyConduitSource buildPropertyConduitSource(@Autobuild PropertyConduitSourceImpl service,
1339                                                                @ComponentClasses InvalidationEventHub hub)
1340        {
1341            hub.addInvalidationListener(service);
1342    
1343            return service;
1344        }
1345    
1346        /**
1347         * Builds a shadow of the RequestGlobals.request property. Note again that the shadow can be an ordinary singleton,
1348         * even though RequestGlobals is perthread.
1349         */
1350        public Request buildRequest()
1351        {
1352            return shadowBuilder.build(requestGlobals, "request", Request.class);
1353        }
1354    
1355        /**
1356         * Builds a shadow of the RequestGlobals.HTTPServletRequest property.  Generally, you should inject the {@link
1357         * Request} service instead, as future version of Tapestry may operate beyond just the servlet API.
1358         */
1359        public HttpServletRequest buildHttpServletRequest()
1360        {
1361            return shadowBuilder.build(requestGlobals, "HTTPServletRequest", HttpServletRequest.class);
1362        }
1363    
1364        /**
1365         * @since 5.1.0.0
1366         */
1367        public HttpServletResponse buildHttpServletResponse()
1368        {
1369            return shadowBuilder.build(requestGlobals, "HTTPServletResponse", HttpServletResponse.class);
1370        }
1371    
1372        /**
1373         * Builds a shadow of the RequestGlobals.response property. Note again that the shadow can be an ordinary singleton,
1374         * even though RequestGlobals is perthread.
1375         */
1376        public Response buildResponse()
1377        {
1378            return shadowBuilder.build(requestGlobals, "response", Response.class);
1379        }
1380    
1381    
1382        /**
1383         * The MarkupRenderer service is used to render a full page as markup.  Supports an ordered configuration of {@link
1384         * org.apache.tapestry5.services.MarkupRendererFilter}s.
1385         */
1386        public MarkupRenderer buildMarkupRenderer(Logger logger,
1387                                                  @Autobuild MarkupRendererTerminator terminator,
1388                                                  List<MarkupRendererFilter> configuration)
1389        {
1390            return pipelineBuilder.build(logger, MarkupRenderer.class, MarkupRendererFilter.class, configuration,
1391                                         terminator);
1392        }
1393    
1394        /**
1395         * A wrapper around {@link org.apache.tapestry5.internal.services.PageRenderQueue} used for partial page renders.
1396         * Supports an ordered configuration of {@link org.apache.tapestry5.services.PartialMarkupRendererFilter}s.
1397         *
1398         * @see #contributePartialMarkupRenderer(org.apache.tapestry5.ioc.OrderedConfiguration, org.apache.tapestry5.Asset,
1399         *      org.apache.tapestry5.ioc.services.SymbolSource, AssetSource, ValidationMessagesSource)
1400         */
1401        public PartialMarkupRenderer buildPartialMarkupRenderer(Logger logger,
1402                                                                List<PartialMarkupRendererFilter> configuration,
1403                                                                @Autobuild PartialMarkupRendererTerminator terminator)
1404        {
1405    
1406            return pipelineBuilder.build(logger, PartialMarkupRenderer.class, PartialMarkupRendererFilter.class,
1407                                         configuration, terminator);
1408        }
1409    
1410        public PageRenderRequestHandler buildPageRenderRequestHandler(
1411                List<PageRenderRequestFilter> configuration,
1412                Logger logger,
1413                @Autobuild PageRenderRequestHandlerImpl terminator)
1414        {
1415            return pipelineBuilder.build(logger, PageRenderRequestHandler.class, PageRenderRequestFilter.class,
1416                                         configuration, terminator);
1417        }
1418    
1419    
1420        /**
1421         * Builds the component action request handler for traditional (non-Ajax) requests. These typically result in a
1422         * redirect to a Tapestry render URL.
1423         */
1424        @Marker({ Traditional.class, Primary.class })
1425        public ComponentEventRequestHandler buildComponentEventRequestHandler(
1426                List<ComponentEventRequestFilter> configuration, Logger logger,
1427                @Autobuild ComponentEventRequestHandlerImpl terminator)
1428        {
1429            return pipelineBuilder.build(logger, ComponentEventRequestHandler.class, ComponentEventRequestFilter.class,
1430                                         configuration, terminator);
1431        }
1432    
1433        /**
1434         * Builds the action request handler for Ajax requests, based on a {@linkplain org.apache.tapestry5.ioc.services.PipelineBuilder
1435         * pipeline} around {@link org.apache.tapestry5.internal.services.AjaxComponentEventRequestHandler}. Filters on the
1436         * request handler are supported here as well.
1437         */
1438        @Marker({ Ajax.class, Primary.class })
1439        public ComponentEventRequestHandler buildAjaxComponentEventRequestHandler(
1440                List<ComponentEventRequestFilter> configuration, Logger logger,
1441                @Autobuild AjaxComponentEventRequestHandler terminator)
1442        {
1443            return pipelineBuilder.build(logger, ComponentEventRequestHandler.class, ComponentEventRequestFilter.class,
1444                                         configuration, terminator);
1445        }
1446    
1447        // ========================================================================
1448        //
1449        // Service Contribution Methods (instance)
1450        //
1451        // ========================================================================
1452    
1453        /**
1454         * Contributes the default "session" strategy.
1455         */
1456        public void contributeApplicationStatePersistenceStrategySource(
1457                MappedConfiguration<String, ApplicationStatePersistenceStrategy> configuration,
1458    
1459                @Local
1460                ApplicationStatePersistenceStrategy sessionStategy)
1461        {
1462            configuration.add("session", sessionStategy);
1463        }
1464    
1465        public void contributeAssetSource(MappedConfiguration<String, AssetFactory> configuration,
1466                                          @ContextProvider AssetFactory contextAssetFactory,
1467    
1468                                          @ClasspathProvider AssetFactory classpathAssetFactory)
1469        {
1470            configuration.add("context", contextAssetFactory);
1471            configuration.add("classpath", classpathAssetFactory);
1472        }
1473    
1474        /**
1475         * Contributes handlers for the following types: <dl> <dt>Object</dt> <dd>Failure case, added to provide a more
1476         * useful exception message</dd> <dt>{@link Link}</dt> <dd>Sends a redirect to the link (which is typically a page
1477         * render link)</dd> <dt>String</dt> <dd>Sends a page render redirect</dd> <dt>Class</dt> <dd>Interpreted as the
1478         * class name of a page, sends a page render render redirect (this is more refactoring safe than the page name)</dd>
1479         * <dt>{@link Component}</dt> <dd>A page's root component (though a non-root component will work, but will generate
1480         * a warning). A direct to the containing page is sent.</dd> <dt>{@link org.apache.tapestry5.StreamResponse}</dt>
1481         * <dd>The stream response is sent as the actual reply.</dd> <dt>URL</dt> <dd>Sends a redirect to a (presumably)
1482         * external URL</dd> </dl>
1483         */
1484        public void contributeComponentEventResultProcessor(
1485                @Traditional @ComponentInstanceProcessor
1486                ComponentEventResultProcessor componentInstanceProcessor,
1487    
1488                MappedConfiguration<Class, ComponentEventResultProcessor> configuration)
1489        {
1490            configuration.add(Link.class, new ComponentEventResultProcessor<Link>()
1491            {
1492                public void processResultValue(Link value) throws IOException
1493                {
1494                    response.sendRedirect(value);
1495                }
1496            });
1497    
1498            configuration.add(URL.class, new ComponentEventResultProcessor<URL>()
1499            {
1500                public void processResultValue(URL value) throws IOException
1501                {
1502                    response.sendRedirect(value.toExternalForm());
1503                }
1504            });
1505    
1506            configuration.addInstance(String.class, PageNameComponentEventResultProcessor.class);
1507    
1508            configuration.addInstance(Class.class, ClassResultProcessor.class);
1509    
1510            configuration.add(Component.class, componentInstanceProcessor);
1511    
1512            configuration.addInstance(StreamResponse.class, StreamResponseResultProcessor.class);
1513        }
1514    
1515    
1516        /**
1517         * Contributes handlers for the following types: <dl> <dt>Object</dt> <dd>Failure case, added to provide more useful
1518         * exception message</dd> <dt>{@link RenderCommand}</dt> <dd>Typically, a {@link org.apache.tapestry5.Block}</dd>
1519         * <dt>{@link org.apache.tapestry5.annotations.Component}</dt> <dd>Renders the component and its body (unless its a
1520         * page, in which case a redirect JSON response is sent)</dd> <dt>{@link org.apache.tapestry5.json.JSONObject} or
1521         * {@link org.apache.tapestry5.json.JSONArray}</dt> <dd>The JSONObject is returned as a text/javascript
1522         * response</dd> <dt>{@link org.apache.tapestry5.StreamResponse}</dt> <dd>The stream response is sent as the actual
1523         * response</dd> <dt>String</dt> <dd>Interprets the value as a logical page name and sends a client response to
1524         * redirect to that page</dd> <dt>{@link org.apache.tapestry5.Link}</dt> <dd>Sends a JSON response to redirect to
1525         * the link</dd> <dt>{@link Class}</dt> <dd>Treats the class as a page class and sends a redirect for a page render
1526         * for that page</dd> <dt>{@link org.apache.tapestry5.ajax.MultiZoneUpdate}</dt> <dd>Sends a single JSON response to
1527         * update the content of multiple zones</dl>
1528         */
1529        public static void contributeAjaxComponentEventResultProcessor(
1530                MappedConfiguration<Class, ComponentEventResultProcessor> configuration)
1531        {
1532            configuration.addInstance(RenderCommand.class, RenderCommandComponentEventResultProcessor.class);
1533            configuration.addInstance(Component.class, AjaxComponentInstanceEventResultProcessor.class);
1534            configuration.addInstance(JSONObject.class, JSONObjectEventResultProcessor.class);
1535            configuration.addInstance(JSONArray.class, JSONArrayEventResultProcessor.class);
1536            configuration.addInstance(StreamResponse.class, StreamResponseResultProcessor.class);
1537            configuration.addInstance(String.class, AjaxPageNameComponentEventResultProcessor.class);
1538            configuration.addInstance(Link.class, AjaxLinkComponentEventResultProcessor.class);
1539            configuration.addInstance(Class.class, AjaxPageClassComponentEventResultProcessor.class);
1540            configuration.addInstance(MultiZoneUpdate.class, MultiZoneUpdateEventResultProcessor.class);
1541        }
1542    
1543        /**
1544         * The MasterDispatcher is a chain-of-command of individual Dispatchers, each handling (like a servlet) a particular
1545         * kind of incoming request. <dl> <dt>RootPath</dt> <dd>Renders the start page for the "/" request</dd>
1546         * <dt>Asset</dt> <dd>Provides access to classpath assets</dd> <dt>VirtualAsset</dt> <dd>Provides access to combined
1547         * scripts</dd> <dt>PageRender</dt> <dd>Identifies the {@link org.apache.tapestry5.services.PageRenderRequestParameters}
1548         * and forwards onto {@link PageRenderRequestHandler}</dd> <dt>ComponentEvent</dt> <dd>Identifies the {@link
1549         * ComponentEventRequestParameters} and forwards onto the {@link ComponentEventRequestHandler}</dd> </dl>
1550         */
1551        public static void contributeMasterDispatcher(OrderedConfiguration<Dispatcher> configuration)
1552        {
1553            // Looks for the root path and renders the start page. This is maintained for compatibility
1554            // with earlier versions of Tapestry 5, it is recommended that an Index page be used instead.
1555    
1556            configuration.addInstance("RootPath", RootPathDispatcher.class, "before:Asset");
1557    
1558            // This goes first because an asset to be streamed may have an file extension, such as
1559            // ".html", that will confuse the later dispatchers.
1560    
1561            configuration.addInstance("Asset", AssetDispatcher.class, "before:ComponentEvent");
1562    
1563            configuration.addInstance("VirtualAsset", VirtualAssetDispatcher.class, "before:Asset");
1564    
1565            configuration.addInstance("ComponentEvent", ComponentEventDispatcher.class, "before:PageRender");
1566    
1567            configuration.addInstance("PageRender", PageRenderDispatcher.class);
1568        }
1569    
1570        /**
1571         * Contributes a default object renderer for type Object, plus specialized renderers for {@link
1572         * org.apache.tapestry5.services.Request}, {@link org.apache.tapestry5.ioc.Location}, {@link
1573         * org.apache.tapestry5.ComponentResources}, {@link org.apache.tapestry5.EventContext}, List, and Object[].
1574         */
1575        public void contributeObjectRenderer(MappedConfiguration<Class, ObjectRenderer> configuration,
1576    
1577                                             @InjectService("LocationRenderer")
1578                                             ObjectRenderer locationRenderer,
1579    
1580                                             final TypeCoercer typeCoercer)
1581        {
1582            configuration.add(Object.class, new ObjectRenderer()
1583            {
1584                public void render(Object object, MarkupWriter writer)
1585                {
1586                    writer.write(String.valueOf(object));
1587                }
1588            });
1589    
1590            configuration.addInstance(Request.class, RequestRenderer.class);
1591    
1592            configuration.add(Location.class, locationRenderer);
1593    
1594            ObjectRenderer preformatted = new ObjectRenderer<Object>()
1595            {
1596                public void render(Object object, MarkupWriter writer)
1597                {
1598                    writer.element("pre");
1599                    writer.write(typeCoercer.coerce(object, String.class));
1600                    writer.end();
1601                }
1602            };
1603    
1604            configuration.add(ClassTransformation.class, preformatted);
1605    
1606            configuration.addInstance(List.class, ListRenderer.class);
1607            configuration.addInstance(Object[].class, ObjectArrayRenderer.class);
1608            configuration.addInstance(ComponentResources.class, ComponentResourcesRenderer.class);
1609            configuration.addInstance(EventContext.class, EventContextRenderer.class);
1610        }
1611    
1612    
1613        /**
1614         * Adds page render filters, each of which provides an {@link org.apache.tapestry5.annotations.Environmental}
1615         * service. Filters often provide {@link org.apache.tapestry5.annotations.Environmental} services needed by
1616         * components as they render. <dl> <dt>DocumentLinker</dt> <dd>Provides {@link org.apache.tapestry5.internal.services.DocumentLinker}
1617         * <dt>RenderSupport</dt>  <dd>Provides {@link org.apache.tapestry5.RenderSupport}</dd>
1618         * <dt>InjectDefaultStyleheet</dt> <dd>Injects the default stylesheet</dd></dt> <dt>ClientBehaviorSupport</dt>
1619         * <dd>Provides {@link ClientBehaviorSupport}</dd> <dt>Heartbeat</dt> <dd>Provides {@link
1620         * org.apache.tapestry5.services.Heartbeat}</dd> <dt>DefaultValidationDecorator</dt> <dd>Provides {@link
1621         * org.apache.tapestry5.ValidationDecorator} (as an instance of {@link org.apache.tapestry5.internal.DefaultValidationDecorator})</dd>
1622         * </dl>
1623         */
1624        public void contributeMarkupRenderer(OrderedConfiguration<MarkupRendererFilter> configuration,
1625    
1626                                             @Symbol(SymbolConstants.PRODUCTION_MODE)
1627                                             final boolean productionMode,
1628    
1629                                             @Path("${tapestry.spacer-image}")
1630                                             final Asset spacerImage,
1631    
1632                                             @Symbol(SymbolConstants.OMIT_GENERATOR_META)
1633                                             final boolean omitGeneratorMeta,
1634    
1635                                             @Inject @Symbol(SymbolConstants.TAPESTRY_VERSION)
1636                                             final String tapestryVersion,
1637    
1638                                             @Symbol(SymbolConstants.COMBINE_SCRIPTS)
1639                                             final boolean combineScripts,
1640    
1641                                             final SymbolSource symbolSource,
1642    
1643                                             final AssetSource assetSource,
1644    
1645                                             final ClientDataEncoder clientDataEncoder,
1646    
1647                                             final ClientInfrastructure clientInfrastructure)
1648        {
1649            MarkupRendererFilter documentLinker = new MarkupRendererFilter()
1650            {
1651                public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1652                {
1653                    DocumentLinkerImpl linker = new DocumentLinkerImpl(productionMode,
1654                                                                       omitGeneratorMeta,
1655                                                                       tapestryVersion,
1656                                                                       combineScripts,
1657                                                                       request.getContextPath(),
1658                                                                       clientDataEncoder);
1659    
1660                    environment.push(DocumentLinker.class, linker);
1661    
1662                    renderer.renderMarkup(writer);
1663    
1664                    environment.pop(DocumentLinker.class);
1665    
1666                    linker.updateDocument(writer.getDocument());
1667                }
1668            };
1669    
1670            MarkupRendererFilter renderSupport = new MarkupRendererFilter()
1671            {
1672                public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1673                {
1674                    DocumentLinker linker = environment.peekRequired(DocumentLinker.class);
1675    
1676                    RenderSupportImpl support = new RenderSupportImpl(linker, symbolSource, assetSource,
1677                                                                      clientInfrastructure);
1678    
1679                    environment.push(RenderSupport.class, support);
1680    
1681                    renderer.renderMarkup(writer);
1682    
1683                    environment.pop(RenderSupport.class);
1684    
1685                    support.commit();
1686                }
1687            };
1688    
1689            MarkupRendererFilter injectDefaultStylesheet = new MarkupRendererFilter()
1690            {
1691                public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1692                {
1693                    RenderSupport renderSupport = environment.peek(RenderSupport.class);
1694    
1695                    for (Asset stylesheet : clientInfrastructure.getStylesheetStack())
1696                    {
1697                        renderSupport.addStylesheetLink(stylesheet, null);
1698                    }
1699    
1700                    renderer.renderMarkup(writer);
1701                }
1702            };
1703    
1704            MarkupRendererFilter clientBehaviorSupport = new MarkupRendererFilter()
1705            {
1706                public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1707                {
1708                    RenderSupport renderSupport = environment.peekRequired(RenderSupport.class);
1709    
1710                    ClientBehaviorSupportImpl clientBehaviorSupport = new ClientBehaviorSupportImpl(renderSupport);
1711    
1712                    environment.push(ClientBehaviorSupport.class, clientBehaviorSupport);
1713    
1714                    renderer.renderMarkup(writer);
1715    
1716                    environment.pop(ClientBehaviorSupport.class);
1717    
1718                    clientBehaviorSupport.commit();
1719                }
1720            };
1721    
1722            MarkupRendererFilter heartbeat = new MarkupRendererFilter()
1723            {
1724                public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1725                {
1726                    Heartbeat heartbeat = new HeartbeatImpl();
1727    
1728                    heartbeat.begin();
1729    
1730                    environment.push(Heartbeat.class, heartbeat);
1731    
1732                    renderer.renderMarkup(writer);
1733    
1734                    environment.pop(Heartbeat.class);
1735    
1736                    heartbeat.end();
1737                }
1738            };
1739    
1740            MarkupRendererFilter defaultValidationDecorator = new MarkupRendererFilter()
1741            {
1742                public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1743                {
1744                    ValidationDecorator decorator = new DefaultValidationDecorator(environment, spacerImage, writer);
1745    
1746                    environment.push(ValidationDecorator.class, decorator);
1747    
1748                    renderer.renderMarkup(writer);
1749    
1750                    environment.pop(ValidationDecorator.class);
1751                }
1752            };
1753    
1754    
1755            configuration.add("DocumentLinker", documentLinker, "before:RenderSupport");
1756            configuration.add("RenderSupport", renderSupport);
1757            configuration.add("InjectDefaultStyleheet", injectDefaultStylesheet, "after:RenderSupport");
1758            configuration.add("ClientBehaviorSupport", clientBehaviorSupport, "after:RenderSupport");
1759            configuration.add("Heartbeat", heartbeat, "after:RenderSupport");
1760            configuration.add("DefaultValidationDecorator", defaultValidationDecorator, "after:Heartbeat");
1761        }
1762    
1763    
1764        /**
1765         * Contributes {@link PartialMarkupRendererFilter}s used when rendering a partial Ajax response. <dl>
1766         * <dt>DocumentLinker <dd>Provides {@link org.apache.tapestry5.internal.services.DocumentLinker} <dt>
1767         * PageRenderSupport     </dt> <dd>Provides {@link org.apache.tapestry5.RenderSupport}</dd>
1768         * <dt>ClientBehaviorSupport</dt> <dd>Provides {@link ClientBehaviorSupport}</dd> <dt>Heartbeat</dt> <dd>Provides
1769         * {@link org.apache.tapestry5.services.Heartbeat}</dd> <dt>DefaultValidationDecorator</dt> <dd>Provides {@link
1770         * org.apache.tapestry5.ValidationDecorator} (as an instance of {@link org.apache.tapestry5.internal.DefaultValidationDecorator})</dd>
1771         * </dl>
1772         */
1773        public void contributePartialMarkupRenderer(OrderedConfiguration<PartialMarkupRendererFilter> configuration,
1774    
1775                                                    @Path("${tapestry.spacer-image}")
1776                                                    final Asset spacerImage,
1777    
1778                                                    final SymbolSource symbolSource,
1779    
1780                                                    final AssetSource assetSource)
1781        {
1782            PartialMarkupRendererFilter documentLinker = new PartialMarkupRendererFilter()
1783            {
1784                public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
1785                {
1786                    PartialMarkupDocumentLinker linker = new PartialMarkupDocumentLinker();
1787    
1788                    environment.push(DocumentLinker.class, linker);
1789    
1790                    renderer.renderMarkup(writer, reply);
1791    
1792                    environment.pop(DocumentLinker.class);
1793    
1794                    linker.commit(reply);
1795                }
1796            };
1797    
1798    
1799            PartialMarkupRendererFilter renderSupport = new PartialMarkupRendererFilter()
1800            {
1801                public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
1802                {
1803                    String uid = Long.toHexString(System.currentTimeMillis());
1804    
1805                    String namespace = "-" + uid;
1806    
1807                    IdAllocator idAllocator = new IdAllocator(namespace);
1808    
1809                    DocumentLinker linker = environment.peekRequired(DocumentLinker.class);
1810    
1811                    RenderSupportImpl support = new RenderSupportImpl(linker, symbolSource, assetSource,
1812                                                                      idAllocator, new EmptyClientInfrastructure());
1813    
1814                    environment.push(RenderSupport.class, support);
1815    
1816                    renderer.renderMarkup(writer, reply);
1817    
1818                    support.commit();
1819    
1820                    environment.pop(RenderSupport.class);
1821                }
1822            };
1823    
1824            PartialMarkupRendererFilter clientBehaviorSupport = new PartialMarkupRendererFilter()
1825            {
1826                public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
1827                {
1828                    RenderSupport renderSupport = environment.peekRequired(RenderSupport.class);
1829    
1830                    ClientBehaviorSupportImpl support = new ClientBehaviorSupportImpl(renderSupport);
1831    
1832                    environment.push(ClientBehaviorSupport.class, support);
1833    
1834                    renderer.renderMarkup(writer, reply);
1835    
1836                    environment.pop(ClientBehaviorSupport.class);
1837    
1838                    support.commit();
1839                }
1840            };
1841    
1842            PartialMarkupRendererFilter heartbeat = new PartialMarkupRendererFilter()
1843            {
1844                public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
1845                {
1846                    Heartbeat heartbeat = new HeartbeatImpl();
1847    
1848                    heartbeat.begin();
1849    
1850                    environment.push(Heartbeat.class, heartbeat);
1851    
1852                    renderer.renderMarkup(writer, reply);
1853    
1854                    environment.pop(Heartbeat.class);
1855    
1856                    heartbeat.end();
1857                }
1858            };
1859    
1860            PartialMarkupRendererFilter defaultValidationDecorator = new PartialMarkupRendererFilter()
1861            {
1862                public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
1863                {
1864                    ValidationDecorator decorator = new DefaultValidationDecorator(environment, spacerImage, writer);
1865    
1866                    environment.push(ValidationDecorator.class, decorator);
1867    
1868                    renderer.renderMarkup(writer, reply);
1869    
1870                    environment.pop(ValidationDecorator.class);
1871                }
1872            };
1873    
1874    
1875            configuration.add("DocumentLinker", documentLinker, "before:RenderSupport");
1876            configuration.add("RenderSupport", renderSupport);
1877            configuration.add("ClientBehaviorSupport", clientBehaviorSupport, "after:RenderSupport");
1878            configuration.add("Heartbeat", heartbeat, "after:RenderSupport");
1879            configuration.add("DefaultValidationDecorator", defaultValidationDecorator, "after:Heartbeat");
1880        }
1881    
1882        /**
1883         * Contributes several strategies: <dl> <dt>session <dd>Values are stored in the {@link Session} <dt>flash
1884         * <dd>Values are stored in the {@link Session}, until the next request (for the page) <dt>client <dd>Values are
1885         * encoded into URLs (or hidden form fields) </dl>
1886         */
1887        public void contributePersistentFieldManager(MappedConfiguration<String, PersistentFieldStrategy> configuration,
1888    
1889                                                     Request request,
1890    
1891                                                     @InjectService("ClientPersistentFieldStrategy")
1892                                                     PersistentFieldStrategy clientStrategy)
1893        {
1894            configuration.add(PersistenceConstants.SESSION, new SessionPersistentFieldStrategy(request));
1895            configuration.add(PersistenceConstants.FLASH, new FlashPersistentFieldStrategy(request));
1896            configuration.add(PersistenceConstants.CLIENT, clientStrategy);
1897        }
1898    
1899        /**
1900         * Contributes org/apache/tapestry5/internal/ValidationMessages as "Default", ordered first.
1901         */
1902        public void contributeValidationMessagesSource(OrderedConfiguration<String> configuration)
1903        {
1904            configuration.add("Default", "org/apache/tapestry5/internal/ValidationMessages", "before:*");
1905        }
1906    
1907        public static ValueEncoderSource buildValueEncoderSource(Map<Class, ValueEncoderFactory> configuration,
1908                                                                 @ComponentClasses InvalidationEventHub hub)
1909        {
1910            ValueEncoderSourceImpl service = new ValueEncoderSourceImpl(configuration);
1911    
1912            hub.addInvalidationListener(service);
1913    
1914            return service;
1915        }
1916    
1917        /**
1918         * Contributes {@link ValueEncoderFactory}s for types: <ul> <li>Object <li>String <li>Enum </ul>
1919         */
1920        @SuppressWarnings("unchecked")
1921        public static void contributeValueEncoderSource(MappedConfiguration<Class, ValueEncoderFactory> configuration)
1922        {
1923            configuration.addInstance(Object.class, TypeCoercedValueEncoderFactory.class);
1924            configuration.add(String.class, GenericValueEncoderFactory.create(new StringValueEncoder()));
1925            configuration.add(Enum.class, new EnumValueEncoderFactory());
1926        }
1927    
1928    
1929        /**
1930         * Contributes a single filter, "Secure", which checks for non-secure requests that access secure pages.
1931         */
1932        public void contributePageRenderRequestHandler(OrderedConfiguration<PageRenderRequestFilter> configuration,
1933                                                       final RequestSecurityManager securityManager)
1934        {
1935            PageRenderRequestFilter secureFilter = new PageRenderRequestFilter()
1936            {
1937                public void handle(PageRenderRequestParameters parameters, PageRenderRequestHandler handler) throws
1938                        IOException
1939                {
1940    
1941                    if (securityManager.checkForInsecureRequest(parameters.getLogicalPageName())) return;
1942    
1943                    handler.handle(parameters);
1944                }
1945            };
1946    
1947            configuration.add("Secure", secureFilter);
1948        }
1949    
1950    
1951        /**
1952         * Configures the extensions that will require a digest to be downloaded via the asset dispatcher. Most resources
1953         * are "safe", they don't require a digest. For unsafe resources, the digest is incorporated into the URL to ensure
1954         * that the client side isn't just "fishing".
1955         * <p/>
1956         * The extensions must be all lower case.
1957         * <p/>
1958         * This contributes "class" and "tml" (the template extension).
1959         *
1960         * @param configuration collection of extensions
1961         */
1962        public static void contributeResourceDigestGenerator(Configuration<String> configuration)
1963        {
1964            // Java class files always require a digest.
1965            configuration.add("class");
1966    
1967            // Likewise, we don't want people fishing for templates.
1968            configuration.add(InternalConstants.TEMPLATE_EXTENSION);
1969        }
1970    
1971        public static void contributeTemplateParser(MappedConfiguration<String, URL> config)
1972        {
1973            // Any class inside the internal module would do. Or we could move all these
1974            // files to o.a.t.services.
1975    
1976            Class c = UpdateListenerHubImpl.class;
1977    
1978            config.add("-//W3C//DTD XHTML 1.0 Strict//EN", c.getResource("xhtml1-strict.dtd"));
1979            config.add("-//W3C//DTD XHTML 1.0 Transitional//EN", c
1980                    .getResource("xhtml1-transitional.dtd"));
1981            config.add("-//W3C//DTD XHTML 1.0 Frameset//EN", c.getResource("xhtml1-frameset.dtd"));
1982            config.add("-//W3C//DTD HTML 4.01//EN", c.getResource("xhtml1-strict.dtd"));
1983            config.add("-//W3C//DTD HTML 4.01 Transitional//EN", c
1984                    .getResource("xhtml1-transitional.dtd"));
1985            config.add("-//W3C//DTD HTML 4.01 Frameset//EN", c.getResource("xhtml1-frameset.dtd"));
1986            config.add("-//W3C//ENTITIES Latin 1 for XHTML//EN", c.getResource("xhtml-lat1.ent"));
1987            config.add("-//W3C//ENTITIES Symbols for XHTML//EN", c.getResource("xhtml-symbol.ent"));
1988            config.add("-//W3C//ENTITIES Special for XHTML//EN", c.getResource("xhtml-special.ent"));
1989        }
1990    
1991        /**
1992         * Contributes factory defaults that may be overridden.
1993         */
1994        public static void contributeFactoryDefaults(MappedConfiguration<String, String> configuration)
1995        {
1996            // Remember this is request-to-request time, presumably it'll take the developer more than
1997            // one second to make a change, save it, and switch back to the browser.
1998    
1999            configuration.add(SymbolConstants.FILE_CHECK_INTERVAL, "1 s");
2000            configuration.add(SymbolConstants.FILE_CHECK_UPDATE_TIMEOUT, "50 ms");
2001    
2002            // This should be overridden for particular applications. These are the locales for
2003            // which we have (at least some) localized messages.
2004            configuration.add(SymbolConstants.SUPPORTED_LOCALES,
2005                              "en,it,es,zh_CN,pt_PT,de,ru,hr,fi_FI,sv_SE,fr_FR,da,pt_BR,ja,el");
2006    
2007            configuration.add(SymbolConstants.TAPESTRY_VERSION,
2008                              VersionUtils.readVersionNumber(
2009                                      "META-INF/maven/org.apache.tapestry/tapestry-core/pom.properties"));
2010    
2011            configuration.add("tapestry.default-cookie-max-age", "7 d");
2012    
2013            configuration.add("tapestry.start-page-name", "start");
2014    
2015            configuration.add("tapestry.default-stylesheet", "classpath:/org/apache/tapestry5/default.css");
2016            configuration.add("tapestry.spacer-image", "classpath:/org/apache/tapestry5/spacer.gif");
2017    
2018            configuration.add("tapestry.page-pool.soft-limit", "5");
2019            configuration.add("tapestry.page-pool.soft-wait", "10 ms");
2020            configuration.add("tapestry.page-pool.hard-limit", "20");
2021            configuration.add("tapestry.page-pool.active-window", "10 m");
2022    
2023            configuration.add(SymbolConstants.SUPPRESS_REDIRECT_FROM_ACTION_REQUESTS, "false");
2024    
2025            configuration.add(SymbolConstants.FORCE_ABSOLUTE_URIS, "false");
2026    
2027            configuration.add(SymbolConstants.PRODUCTION_MODE, "true");
2028    
2029            configuration.add(SymbolConstants.COMPRESS_WHITESPACE, "true");
2030    
2031            configuration.add(MetaDataConstants.SECURE_PAGE, "false");
2032    
2033            configuration.add(SymbolConstants.FORM_CLIENT_LOGIC_ENABLED, "true");
2034    
2035            // This is designed to make it easy to keep synchronized with script.aculo.ous. As we
2036            // support a new version, we create a new folder, and update the path entry. We can then
2037            // delete the old version folder (or keep it around). This should be more manageable than
2038            // overwriting the local copy with updates (it's too easy for files deleted between scriptaculous
2039            // releases to be accidentally left lying around). There's also a ClasspathAliasManager
2040            // contribution based on the path.
2041    
2042            configuration.add("tapestry.scriptaculous", "classpath:${tapestry.scriptaculous.path}");
2043            configuration.add("tapestry.scriptaculous.path", "org/apache/tapestry5/scriptaculous_1_8_2");
2044    
2045            // Likewise for WebFX DatePicker, currently version 1.0.6
2046    
2047            configuration.add("tapestry.datepicker.path", "org/apache/tapestry5/datepicker_106");
2048            configuration.add("tapestry.datepicker", "classpath:${tapestry.datepicker.path}");
2049    
2050            configuration.add("tapestry.blackbird.path", "org/apache/tapestry5/blackbird_1_0");
2051            configuration.add("tapestry.blackbird", "classpath:${tapestry.blackbird.path}");
2052    
2053            configuration.add(SymbolConstants.PERSISTENCE_STRATEGY, PersistenceConstants.SESSION);
2054    
2055            configuration.add(MetaDataConstants.RESPONSE_CONTENT_TYPE, "text/html");
2056    
2057            configuration.add(SymbolConstants.CHARSET, "UTF-8");
2058    
2059            configuration.add(SymbolConstants.APPLICATION_CATALOG,
2060                              String.format("context:WEB-INF/${%s}.properties", InternalSymbols.APP_NAME));
2061    
2062            configuration.add(SymbolConstants.EXCEPTION_REPORT_PAGE, "ExceptionReport");
2063    
2064            configuration.add(SymbolConstants.MIN_GZIP_SIZE, "100");
2065    
2066            Random random = new Random(System.currentTimeMillis());
2067    
2068            configuration.add(SymbolConstants.APPLICATION_VERSION, Long.toHexString(random.nextLong()));
2069    
2070            configuration.add(SymbolConstants.OMIT_GENERATOR_META, "false");
2071            configuration.add(SymbolConstants.GZIP_COMPRESSION_ENABLED, "true");
2072    
2073            String matchProductionMode = String.format("${%s}", SymbolConstants.PRODUCTION_MODE);
2074    
2075            configuration.add(SymbolConstants.SECURE_ENABLED, matchProductionMode);
2076            configuration.add(SymbolConstants.COMBINE_SCRIPTS, matchProductionMode);
2077    
2078            configuration.add(SymbolConstants.ENCODE_LOCALE_INTO_PATH, "true");
2079        }
2080    
2081    
2082        /**
2083         * Adds content types: <dl> <dt>css</dt> <dd>text/css</dd> <dt>js</dt> <dd>text/javascript</dd> <dt>jpg, jpeg</dt>
2084         * <dd>image/jpeg</dd> <dt>gif</dt> <dd>image/gif</dd> <dt>png</dtt> <dd>image/png</dd>
2085         * <p/>
2086         * </dl>
2087         */
2088        public void contributeResourceStreamer(MappedConfiguration<String, String> configuration)
2089        {
2090            configuration.add("css", "text/css");
2091            configuration.add("js", "text/javascript");
2092            configuration.add("gif", "image/gif");
2093            configuration.add("jpg", "image/jpeg");
2094            configuration.add("jpeg", "image/jpeg");
2095            configuration.add("png", "image/png");
2096        }
2097    
2098        /**
2099         * Adds a listener to the {@link org.apache.tapestry5.internal.services.ComponentInstantiatorSource} that clears the
2100         * {@link PropertyAccess} and {@link TypeCoercer} caches on a class loader invalidation.  In addition, forces the
2101         * realization of {@link ComponentClassResolver} at startup.
2102         */
2103        public void contributeApplicationInitializer(OrderedConfiguration<ApplicationInitializerFilter> configuration,
2104                                                     final TypeCoercer typeCoercer,
2105                                                     final ComponentClassResolver componentClassResolver,
2106                                                     @ComponentClasses final InvalidationEventHub invalidationEventHub,
2107                                                     final @Autobuild RestoreDirtySessionObjects restoreDirtySessionObjects)
2108        {
2109            final InvalidationListener listener = new InvalidationListener()
2110            {
2111                public void objectWasInvalidated()
2112                {
2113                    propertyAccess.clearCache();
2114    
2115                    typeCoercer.clearCache();
2116                }
2117            };
2118    
2119            ApplicationInitializerFilter clearCaches = new ApplicationInitializerFilter()
2120            {
2121                public void initializeApplication(Context context, ApplicationInitializer initializer)
2122                {
2123                    // Snuck in here is the logic to clear the PropertyAccess service's cache whenever
2124                    // the component class loader is invalidated.
2125    
2126                    invalidationEventHub.addInvalidationListener(listener);
2127    
2128                    endOfRequestEventHub.addEndOfRequestListener(restoreDirtySessionObjects);
2129    
2130                    // Perform other pending initialization
2131    
2132                    initializer.initializeApplication(context);
2133    
2134                    // We don't care about the result, but this forces a load of the service
2135                    // at application startup, rather than on first request.
2136    
2137                    componentClassResolver.isPageName("ForceLoadAtStartup");
2138                }
2139            };
2140    
2141            configuration.add("ClearCachesOnInvalidation", clearCaches);
2142        }
2143    
2144    
2145        /**
2146         * Contributes filters: <dl> <dt>Ajax</dt> <dd>Determines if the request is Ajax oriented, and redirects to an
2147         * alternative handler if so</dd> <dt>ImmediateRender</dt> <dd>When {@linkplain
2148         * SymbolConstants#SUPPRESS_REDIRECT_FROM_ACTION_REQUESTS immediate action response rendering} is enabled, generates
2149         * the markup response (instead of a page redirect response, which is the normal behavior) </dd> <dt>Secure</dt>
2150         * <dd>Sends a redirect if an non-secure request accesses a secure page</dd></dl>
2151         */
2152        public void contributeComponentEventRequestHandler(OrderedConfiguration<ComponentEventRequestFilter> configuration,
2153                                                           final RequestSecurityManager requestSecurityManager,
2154                                                           @Ajax ComponentEventRequestHandler ajaxHandler
2155        )
2156        {
2157            ComponentEventRequestFilter secureFilter = new ComponentEventRequestFilter()
2158            {
2159                public void handle(ComponentEventRequestParameters parameters, ComponentEventRequestHandler handler)
2160                        throws IOException
2161                {
2162                    if (requestSecurityManager.checkForInsecureRequest(parameters.getActivePageName())) return;
2163    
2164                    handler.handle(parameters);
2165                }
2166            };
2167    
2168            configuration.add("Ajax", new AjaxFilter(request, ajaxHandler));
2169    
2170            configuration.addInstance("ImmediateRender", ImmediateActionRenderResponseFilter.class);
2171    
2172            configuration.add("Secure", secureFilter, "before:Ajax");
2173        }
2174    
2175    
2176        /**
2177         * Contributes strategies accessible via the {@link NullFieldStrategySource} service.
2178         * <p/>
2179         * <dl> <dt>default</dt> <dd>Does nothing, nulls stay null.</dd> <dt>zero</dt> <dd>Null values are converted to
2180         * zero.</dd> </dl>
2181         */
2182        public static void contributeNullFieldStrategySource(MappedConfiguration<String, NullFieldStrategy> configuration)
2183        {
2184            configuration.add("default", new DefaultNullFieldStrategy());
2185            configuration.add("zero", new ZeroNullFieldStrategy());
2186        }
2187    
2188    
2189        /**
2190         * Determines positioning of hidden fields relative to other elements (this is needed by {@link
2191         * org.apache.tapestry5.corelib.components.FormFragment} and others.
2192         * <p/>
2193         * For elements input, select, textarea and label the hidden field is positioned after.
2194         * <p/>
2195         * For elements p, div, li and td, the hidden field is positioned inside.
2196         */
2197        public static void contributeHiddenFieldLocationRules(
2198                MappedConfiguration<String, RelativeElementPosition> configuration)
2199        {
2200            configuration.add("input", RelativeElementPosition.AFTER);
2201            configuration.add("select", RelativeElementPosition.AFTER);
2202            configuration.add("textarea", RelativeElementPosition.AFTER);
2203            configuration.add("label", RelativeElementPosition.AFTER);
2204    
2205            configuration.add("p", RelativeElementPosition.INSIDE);
2206            configuration.add("div", RelativeElementPosition.INSIDE);
2207            configuration.add("td", RelativeElementPosition.INSIDE);
2208            configuration.add("li", RelativeElementPosition.INSIDE);
2209        }
2210    
2211    
2212        /**
2213         * @since 5.1.0.0
2214         */
2215        public static LinkCreationHub buildLinkCreationHub(LinkSource source)
2216        {
2217            return source.getLinkCreationHub();
2218        }
2219    
2220        /**
2221         * @since 5.1.0.0
2222         */
2223        @Marker(ComponentClasses.class)
2224        public static InvalidationEventHub buildComponentClassesInvalidationEventHub(ComponentInstantiatorSource source)
2225        {
2226            return source.getInvalidationEventHub();
2227        }
2228    
2229        /**
2230         * @since 5.1.0.0
2231         */
2232        @Marker(ComponentTemplates.class)
2233        public static InvalidationEventHub buildComponentTemplatesInvalidationEventHub(
2234                ComponentTemplateSource templateSource)
2235        {
2236            return templateSource.getInvalidationEventHub();
2237        }
2238    
2239        /**
2240         * @since 5.1.0.0
2241         */
2242        @Marker(ComponentMessages.class)
2243        public static InvalidationEventHub buildComponentMessagesInvalidationEventHub(
2244                ComponentMessagesSource messagesSource)
2245        {
2246            return messagesSource.getInvalidationEventHub();
2247        }
2248    
2249        @Scope(ScopeConstants.PERTHREAD)
2250        public Environment buildEnvironment(PerthreadManager perthreadManager)
2251        {
2252            EnvironmentImpl service = new EnvironmentImpl();
2253    
2254            perthreadManager.addThreadCleanupListener(service);
2255    
2256            return service;
2257        }
2258    
2259        /**
2260         * The master Sessi`onPesistedObjectAnalyzer.
2261         *
2262         * @since 5.1.0.0
2263         */
2264        @Marker(Primary.class)
2265        public SessionPersistedObjectAnalyzer buildSessionPersistedObjectAnalyzer(
2266                Map<Class, SessionPersistedObjectAnalyzer> configuration)
2267        {
2268            return strategyBuilder.build(SessionPersistedObjectAnalyzer.class, configuration);
2269        }
2270    
2271        /**
2272         * Identifies String, Number and Boolean as immutable objects, a catch-all handler for Object (that understands
2273         * {@link org.apache.tapestry5.annotations.ImmutableSessionPersistedObject}, and handlers for {@link
2274         * org.apache.tapestry5.OptimizedSessionPersistedObject} and {@link org.apache.tapestry5.OptimizedApplicationStateObject}.
2275         *
2276         * @since 5.1.0.0
2277         */
2278        public static void contributeSessionPersistedObjectAnalyzer(
2279                MappedConfiguration<Class, SessionPersistedObjectAnalyzer> configuration)
2280        {
2281            configuration.add(Object.class, new DefaultSessionPersistedObjectAnalyzer());
2282    
2283            SessionPersistedObjectAnalyzer<Object> immutable = new SessionPersistedObjectAnalyzer<Object>()
2284            {
2285                public boolean isDirty(Object object)
2286                {
2287                    return false;
2288                }
2289            };
2290    
2291            configuration.add(String.class, immutable);
2292            configuration.add(Number.class, immutable);
2293            configuration.add(Boolean.class, immutable);
2294    
2295            configuration.add(OptimizedSessionPersistedObject.class, new OptimizedSessionPersistedObjectAnalyzer());
2296            configuration.add(OptimizedApplicationStateObject.class, new OptimizedApplicationStateObjectAnalyzer());
2297        }
2298    
2299        /**
2300         * Contibutions are content types that do not benefit from compression. Adds the following content types: <ul>
2301         * <li>image/jpeg</li> <li>image/gif <li>image/png <li>application/json (see https://issues.apache.org/jira/browse/TAP5-469)</ul>
2302         *
2303         * @since 5.1.0.0
2304         */
2305        public static void contributeResponseCompressionAnalyzer(Configuration<String> configuration)
2306        {
2307            configuration.add("image/jpeg");
2308            configuration.add("image/gif");
2309            configuration.add("image/png");
2310            configuration.add("application/json");
2311        }
2312    
2313        /**
2314         * @since 5.1.1.0
2315         */
2316        @Marker(Primary.class)
2317        public StackTraceElementAnalyzer buildMasterStackTraceElementAnalyzer(List<StackTraceElementAnalyzer> configuration)
2318        {
2319            return chainBuilder.build(StackTraceElementAnalyzer.class, configuration);
2320        }
2321    
2322        /**
2323         * Adds two analyzers: <dl> <dt>Application</dt> <dd>Checks for classes in the application package</dd>
2324         * <dt>Proxies</dt> <dd>Checks for classes that appear to be generated proxies.</dd> <dt>SunReflect</dt> <dd>Checks
2325         * for <code>sun.reflect</code> (which are omitted)</dl>
2326         *
2327         * @since 5.1.0.0
2328         */
2329        public static void contributeMasterStackTraceElementAnalyzer(
2330                OrderedConfiguration<StackTraceElementAnalyzer> configuration)
2331        {
2332            configuration.addInstance("Application", ApplicationStackTraceElementAnalyzer.class);
2333            configuration.addInstance("Proxies", ProxiesStackTraceElementAnalyzer.class, "before:Application");
2334            configuration.add("SunReflect",
2335                              new PrefixCheckStackTraceElementAnalyzer(StackTraceElementClassConstants.OMITTED,
2336                                                                       "sun.reflect."
2337                              ));
2338        }
2339    
2340        /**
2341         * Advises the {@link org.apache.tapestry5.internal.services.ComponentMessagesSource} service so that the creation
2342         * of {@link org.apache.tapestry5.ioc.Messages} instances can be deferred.
2343         *
2344         * @since 5.1.0.0
2345         */
2346        @Match("ComponentMessagesSource")
2347        public static void adviseLazy(LazyAdvisor advisor, MethodAdviceReceiver receiver)
2348        {
2349            advisor.addLazyMethodInvocationAdvice(receiver);
2350        }
2351    
2352        /**
2353         * @since 5.1.0.0
2354         */
2355        public ComponentRequestHandler buildComponentRequestHandler(
2356                List<ComponentRequestFilter> configuration,
2357    
2358                @Autobuild ComponentRequestHandlerTerminator terminator,
2359    
2360                Logger logger)
2361        {
2362            return pipelineBuilder.build(logger, ComponentRequestHandler.class, ComponentRequestFilter.class,
2363                                         configuration, terminator);
2364        }
2365    
2366        /**
2367         * @throws Exception
2368         * @since 5.1.0.2
2369         */
2370        public static ComponentEventLinkEncoder decorateComponentEventLinkEncoder(
2371                ComponentEventLinkEncoder encoder, URLRewriter urlRewriter,
2372                Request request, HttpServletRequest httpServletRequest, Response response,
2373                AspectDecorator aspectDecorator) throws Exception
2374        {
2375    
2376            // no rules, no link rewriting.
2377            if (!urlRewriter.hasLinkRules())
2378            {
2379                return null;
2380            }
2381    
2382            ComponentEventLinkEncoderMethodAdvice pageLinkAdvice =
2383                    new ComponentEventLinkEncoderMethodAdvice(urlRewriter, request, httpServletRequest, response, true);
2384    
2385            ComponentEventLinkEncoderMethodAdvice eventLinkAdvice =
2386                    new ComponentEventLinkEncoderMethodAdvice(urlRewriter, request, httpServletRequest, response, false);
2387    
2388    
2389            Class<ComponentEventLinkEncoder> clasz = ComponentEventLinkEncoder.class;
2390    
2391            Method createPageRenderLink =
2392                    clasz.getMethod("createPageRenderLink", PageRenderRequestParameters.class);
2393    
2394            Method createComponentEventLink =
2395                    clasz.getMethod("createComponentEventLink", ComponentEventRequestParameters.class, boolean.class);
2396    
2397    
2398            final AspectInterceptorBuilder<ComponentEventLinkEncoder> builder =
2399                    aspectDecorator.createBuilder(clasz, encoder, "Link rewriting");
2400    
2401            builder.adviseMethod(createComponentEventLink, eventLinkAdvice);
2402            builder.adviseMethod(createPageRenderLink, pageLinkAdvice);
2403    
2404            return builder.build();
2405    
2406        }
2407    
2408    }