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