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