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