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