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