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