001// Licensed under the Apache License, Version 2.0 (the "License"); 002// you may not use this file except in compliance with the License. 003// You may obtain a copy of the License at 004// 005// http://www.apache.org/licenses/LICENSE-2.0 006// 007// Unless required by applicable law or agreed to in writing, software 008// distributed under the License is distributed on an "AS IS" BASIS, 009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 010// See the License for the specific language governing permissions and 011// limitations under the License. 012 013package org.apache.tapestry5.modules; 014 015import org.apache.tapestry5.*; 016import org.apache.tapestry5.ajax.MultiZoneUpdate; 017import org.apache.tapestry5.alerts.AlertManager; 018import org.apache.tapestry5.annotations.*; 019import org.apache.tapestry5.annotations.ContentType; 020import org.apache.tapestry5.beaneditor.DataTypeConstants; 021import org.apache.tapestry5.beaneditor.Validate; 022import org.apache.tapestry5.corelib.data.SecureOption; 023import org.apache.tapestry5.grid.GridConstants; 024import org.apache.tapestry5.grid.GridDataSource; 025import org.apache.tapestry5.internal.*; 026import org.apache.tapestry5.internal.alerts.AlertManagerImpl; 027import org.apache.tapestry5.internal.beaneditor.EnvironmentMessages; 028import org.apache.tapestry5.internal.beaneditor.MessagesConstraintGenerator; 029import org.apache.tapestry5.internal.beaneditor.PrimitiveFieldConstraintGenerator; 030import org.apache.tapestry5.internal.beaneditor.ValidateAnnotationConstraintGenerator; 031import org.apache.tapestry5.internal.bindings.*; 032import org.apache.tapestry5.internal.dynamic.DynamicTemplateParserImpl; 033import org.apache.tapestry5.internal.grid.CollectionGridDataSource; 034import org.apache.tapestry5.internal.grid.NullDataSource; 035import org.apache.tapestry5.internal.gzip.GZipFilter; 036import org.apache.tapestry5.internal.renderers.*; 037import org.apache.tapestry5.internal.services.*; 038import org.apache.tapestry5.internal.services.ajax.AjaxFormUpdateFilter; 039import org.apache.tapestry5.internal.services.ajax.AjaxResponseRendererImpl; 040import org.apache.tapestry5.internal.services.ajax.MultiZoneUpdateEventResultProcessor; 041import org.apache.tapestry5.internal.services.exceptions.ExceptionReportWriterImpl; 042import org.apache.tapestry5.internal.services.exceptions.ExceptionReporterImpl; 043import org.apache.tapestry5.internal.services.linktransform.LinkTransformerImpl; 044import org.apache.tapestry5.internal.services.linktransform.LinkTransformerInterceptor; 045import org.apache.tapestry5.internal.services.messages.PropertiesFileParserImpl; 046import org.apache.tapestry5.internal.services.meta.ContentTypeExtractor; 047import org.apache.tapestry5.internal.services.meta.MetaAnnotationExtractor; 048import org.apache.tapestry5.internal.services.meta.MetaWorkerImpl; 049import org.apache.tapestry5.internal.services.meta.UnknownActivationContextExtractor; 050import org.apache.tapestry5.internal.services.security.ClientWhitelistImpl; 051import org.apache.tapestry5.internal.services.security.LocalhostOnly; 052import org.apache.tapestry5.internal.services.templates.DefaultTemplateLocator; 053import org.apache.tapestry5.internal.services.templates.PageTemplateLocator; 054import org.apache.tapestry5.internal.transform.*; 055import org.apache.tapestry5.internal.translator.NumericTranslator; 056import org.apache.tapestry5.internal.translator.NumericTranslatorSupport; 057import org.apache.tapestry5.internal.translator.StringTranslator; 058import org.apache.tapestry5.internal.util.RenderableAsBlock; 059import org.apache.tapestry5.internal.util.StringRenderable; 060import org.apache.tapestry5.internal.validator.ValidatorMacroImpl; 061import org.apache.tapestry5.ioc.*; 062import org.apache.tapestry5.ioc.annotations.*; 063import org.apache.tapestry5.ioc.internal.BasicDataTypeAnalyzers; 064import org.apache.tapestry5.ioc.internal.util.CollectionFactory; 065import org.apache.tapestry5.ioc.services.*; 066import org.apache.tapestry5.ioc.util.AvailableValues; 067import org.apache.tapestry5.ioc.util.StrategyRegistry; 068import org.apache.tapestry5.json.JSONArray; 069import org.apache.tapestry5.json.JSONObject; 070import org.apache.tapestry5.plastic.MethodAdvice; 071import org.apache.tapestry5.plastic.MethodDescription; 072import org.apache.tapestry5.plastic.MethodInvocation; 073import org.apache.tapestry5.runtime.Component; 074import org.apache.tapestry5.runtime.ComponentResourcesAware; 075import org.apache.tapestry5.runtime.RenderCommand; 076import org.apache.tapestry5.runtime.RenderQueue; 077import org.apache.tapestry5.services.*; 078import org.apache.tapestry5.services.ajax.AjaxResponseRenderer; 079import org.apache.tapestry5.services.dynamic.DynamicTemplate; 080import org.apache.tapestry5.services.dynamic.DynamicTemplateParser; 081import org.apache.tapestry5.services.javascript.JavaScriptSupport; 082import org.apache.tapestry5.services.javascript.ModuleManager; 083import org.apache.tapestry5.services.linktransform.ComponentEventLinkTransformer; 084import org.apache.tapestry5.services.linktransform.LinkTransformer; 085import org.apache.tapestry5.services.linktransform.PageRenderLinkTransformer; 086import org.apache.tapestry5.services.messages.ComponentMessagesSource; 087import org.apache.tapestry5.services.messages.PropertiesFileParser; 088import org.apache.tapestry5.services.meta.FixedExtractor; 089import org.apache.tapestry5.services.meta.MetaDataExtractor; 090import org.apache.tapestry5.services.meta.MetaWorker; 091import org.apache.tapestry5.services.pageload.PreloaderMode; 092import org.apache.tapestry5.services.security.ClientWhitelist; 093import org.apache.tapestry5.services.security.WhitelistAnalyzer; 094import org.apache.tapestry5.services.templates.ComponentTemplateLocator; 095import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2; 096import org.apache.tapestry5.services.transform.InjectionProvider2; 097import org.apache.tapestry5.validator.*; 098import org.slf4j.Logger; 099 100import javax.servlet.ServletContext; 101import javax.servlet.http.HttpServletRequest; 102import javax.servlet.http.HttpServletResponse; 103import java.io.IOException; 104import java.io.InputStream; 105import java.lang.annotation.Annotation; 106import java.math.BigDecimal; 107import java.math.BigInteger; 108import java.net.URL; 109import java.text.DateFormat; 110import java.text.SimpleDateFormat; 111import java.util.*; 112import java.util.regex.Pattern; 113 114/** 115 * The root module for Tapestry. 116 */ 117@Marker(Core.class) 118@ImportModule( 119 {InternalModule.class, AssetsModule.class, PageLoadModule.class, JavaScriptModule.class, CompatibilityModule.class, DashboardModule.class}) 120public final class TapestryModule 121{ 122 private final PipelineBuilder pipelineBuilder; 123 124 private final ApplicationGlobals applicationGlobals; 125 126 private final PropertyShadowBuilder shadowBuilder; 127 128 private final Environment environment; 129 130 private final StrategyBuilder strategyBuilder; 131 132 private final PropertyAccess propertyAccess; 133 134 private final ChainBuilder chainBuilder; 135 136 private final Request request; 137 138 private final Response response; 139 140 private final RequestGlobals requestGlobals; 141 142 private final EnvironmentalShadowBuilder environmentalBuilder; 143 144 private final EndOfRequestEventHub endOfRequestEventHub; 145 146 /** 147 * We inject all sorts of common dependencies (including builders) into the 148 * module itself (note: even though some of 149 * these service are defined by the module itself, that's ok because 150 * services are always lazy proxies). This isn't 151 * about efficiency (it may be slightly more efficient, but not in any 152 * noticeable way), it's about eliminating the 153 * need to keep injecting these dependencies into individual service builder 154 * and contribution methods. 155 */ 156 public TapestryModule(PipelineBuilder pipelineBuilder, 157 158 PropertyShadowBuilder shadowBuilder, 159 160 RequestGlobals requestGlobals, 161 162 ApplicationGlobals applicationGlobals, 163 164 ChainBuilder chainBuilder, 165 166 Environment environment, 167 168 StrategyBuilder strategyBuilder, 169 170 PropertyAccess propertyAccess, 171 172 Request request, 173 174 Response response, 175 176 EnvironmentalShadowBuilder environmentalBuilder, 177 178 EndOfRequestEventHub endOfRequestEventHub) 179 { 180 this.pipelineBuilder = pipelineBuilder; 181 this.shadowBuilder = shadowBuilder; 182 this.requestGlobals = requestGlobals; 183 this.applicationGlobals = applicationGlobals; 184 this.chainBuilder = chainBuilder; 185 this.environment = environment; 186 this.strategyBuilder = strategyBuilder; 187 this.propertyAccess = propertyAccess; 188 this.request = request; 189 this.response = response; 190 this.environmentalBuilder = environmentalBuilder; 191 this.endOfRequestEventHub = endOfRequestEventHub; 192 } 193 194 // A bunch of classes "promoted" from inline inner class to nested classes, 195 // just so that the stack trace would be more readable. Most of these 196 // are terminators for pipeline services. 197 198 /** 199 * @since 5.1.0.0 200 */ 201 private class ApplicationInitializerTerminator implements ApplicationInitializer 202 { 203 public void initializeApplication(Context context) 204 { 205 applicationGlobals.storeContext(context); 206 } 207 } 208 209 /** 210 * @since 5.1.0.0 211 */ 212 private class HttpServletRequestHandlerTerminator implements HttpServletRequestHandler 213 { 214 private final RequestHandler handler; 215 private final String applicationCharset; 216 private final TapestrySessionFactory sessionFactory; 217 218 public HttpServletRequestHandlerTerminator(RequestHandler handler, String applicationCharset, 219 TapestrySessionFactory sessionFactory) 220 { 221 this.handler = handler; 222 this.applicationCharset = applicationCharset; 223 this.sessionFactory = sessionFactory; 224 } 225 226 public boolean service(HttpServletRequest servletRequest, HttpServletResponse servletResponse) 227 throws IOException 228 { 229 requestGlobals.storeServletRequestResponse(servletRequest, servletResponse); 230 231 // Should have started doing this a long time ago: recoding attributes into 232 // the request for things that may be needed downstream, without having to extend 233 // Request. 234 235 servletRequest.setAttribute("servletAPI.protocol", servletRequest.getProtocol()); 236 servletRequest.setAttribute("servletAPI.characterEncoding", servletRequest.getCharacterEncoding()); 237 servletRequest.setAttribute("servletAPI.contentLength", servletRequest.getContentLength()); 238 servletRequest.setAttribute("servletAPI.authType", servletRequest.getAuthType()); 239 servletRequest.setAttribute("servletAPI.contentType", servletRequest.getContentType()); 240 servletRequest.setAttribute("servletAPI.scheme", servletRequest.getScheme()); 241 242 Request request = new RequestImpl(servletRequest, applicationCharset, sessionFactory); 243 Response response = new ResponseImpl(servletRequest, servletResponse); 244 245 // TAP5-257: Make sure that the "initial guess" for request/response 246 // is available, even ifsome filter in the RequestHandler pipeline replaces them. 247 // Which just goes to show that there should have been only one way to access the Request/Response: 248 // either functionally (via parameters) or global (via ReqeuestGlobals) but not both. 249 // That ship has sailed. 250 251 requestGlobals.storeRequestResponse(request, response); 252 253 // Transition from the Servlet API-based pipeline, to the 254 // Tapestry-based pipeline. 255 256 return handler.service(request, response); 257 } 258 } 259 260 /** 261 * @since 5.1.0.0 262 */ 263 private class ServletApplicationInitializerTerminator implements ServletApplicationInitializer 264 { 265 private final ApplicationInitializer initializer; 266 267 public ServletApplicationInitializerTerminator(ApplicationInitializer initializer) 268 { 269 this.initializer = initializer; 270 } 271 272 public void initializeApplication(ServletContext servletContext) 273 { 274 applicationGlobals.storeServletContext(servletContext); 275 276 // And now, down the (Web) ApplicationInitializer pipeline ... 277 278 ContextImpl context = new ContextImpl(servletContext); 279 280 applicationGlobals.storeContext(context); 281 282 initializer.initializeApplication(context); 283 } 284 } 285 286 /** 287 * @since 5.1.0.0 288 */ 289 private class RequestHandlerTerminator implements RequestHandler 290 { 291 private final Dispatcher masterDispatcher; 292 293 public RequestHandlerTerminator(Dispatcher masterDispatcher) 294 { 295 this.masterDispatcher = masterDispatcher; 296 } 297 298 public boolean service(Request request, Response response) throws IOException 299 { 300 // Update RequestGlobals with the current request/response (in case 301 // some filter replaced the 302 // normal set). 303 requestGlobals.storeRequestResponse(request, response); 304 305 return masterDispatcher.dispatch(request, response); 306 } 307 } 308 309 public static void bind(ServiceBinder binder) 310 { 311 binder.bind(PersistentLocale.class, PersistentLocaleImpl.class); 312 binder.bind(ApplicationStateManager.class, ApplicationStateManagerImpl.class); 313 binder.bind(ApplicationStatePersistenceStrategySource.class, 314 ApplicationStatePersistenceStrategySourceImpl.class); 315 binder.bind(BindingSource.class, BindingSourceImpl.class); 316 binder.bind(FieldValidatorSource.class, FieldValidatorSourceImpl.class); 317 binder.bind(ApplicationGlobals.class, ApplicationGlobalsImpl.class); 318 binder.bind(Cookies.class, CookiesImpl.class); 319 binder.bind(FieldValidatorDefaultSource.class, FieldValidatorDefaultSourceImpl.class); 320 binder.bind(RequestGlobals.class, RequestGlobalsImpl.class); 321 binder.bind(ResourceDigestGenerator.class, ResourceDigestGeneratorImpl.class); // Remove in 5.5 322 binder.bind(ValidationConstraintGenerator.class, ValidationConstraintGeneratorImpl.class); 323 binder.bind(EnvironmentalShadowBuilder.class, EnvironmentalShadowBuilderImpl.class); 324 binder.bind(ComponentSource.class, ComponentSourceImpl.class); 325 binder.bind(BeanModelSource.class, BeanModelSourceImpl.class); 326 binder.bind(BeanBlockSource.class, BeanBlockSourceImpl.class); 327 binder.bind(ComponentDefaultProvider.class, ComponentDefaultProviderImpl.class); 328 binder.bind(MarkupWriterFactory.class, MarkupWriterFactoryImpl.class); 329 binder.bind(FieldValidationSupport.class, FieldValidationSupportImpl.class); 330 binder.bind(ObjectRenderer.class, LocationRenderer.class).withSimpleId(); 331 binder.bind(ObjectProvider.class, AssetObjectProvider.class).withSimpleId(); 332 binder.bind(RequestExceptionHandler.class, DefaultRequestExceptionHandler.class); 333 binder.bind(ComponentEventResultProcessor.class, ComponentInstanceResultProcessor.class).withSimpleId(); 334 binder.bind(NullFieldStrategySource.class, NullFieldStrategySourceImpl.class); 335 binder.bind(HttpServletRequestFilter.class, IgnoredPathsFilter.class).withSimpleId(); 336 binder.bind(ContextValueEncoder.class, ContextValueEncoderImpl.class); 337 binder.bind(BaseURLSource.class, BaseURLSourceImpl.class); 338 binder.bind(BeanBlockOverrideSource.class, BeanBlockOverrideSourceImpl.class); 339 binder.bind(HiddenFieldLocationRules.class, HiddenFieldLocationRulesImpl.class); 340 binder.bind(PageDocumentGenerator.class, PageDocumentGeneratorImpl.class); 341 binder.bind(ResponseRenderer.class, ResponseRendererImpl.class); 342 binder.bind(FieldTranslatorSource.class, FieldTranslatorSourceImpl.class); 343 binder.bind(BindingFactory.class, MessageBindingFactory.class).withSimpleId(); 344 binder.bind(BindingFactory.class, ValidateBindingFactory.class).withSimpleId(); 345 binder.bind(BindingFactory.class, TranslateBindingFactory.class).withSimpleId(); 346 binder.bind(BindingFactory.class, AssetBindingFactory.class).withSimpleId(); 347 binder.bind(BindingFactory.class, ContextBindingFactory.class).withSimpleId(); 348 binder.bind(BindingFactory.class, NullFieldStrategyBindingFactory.class).withSimpleId(); 349 binder.bind(BindingFactory.class, SymbolBindingFactory.class).withSimpleId(); 350 binder.bind(URLEncoder.class, URLEncoderImpl.class); 351 binder.bind(ContextPathEncoder.class, ContextPathEncoderImpl.class); 352 binder.bind(ApplicationStatePersistenceStrategy.class, SessionApplicationStatePersistenceStrategy.class).withSimpleId(); 353 binder.bind(TapestrySessionFactory.class, TapestrySessionFactoryImpl.class); 354 binder.bind(NumericTranslatorSupport.class); 355 binder.bind(ClientDataEncoder.class, ClientDataEncoderImpl.class); 356 binder.bind(ComponentEventLinkEncoder.class, ComponentEventLinkEncoderImpl.class); 357 binder.bind(PageRenderLinkSource.class, PageRenderLinkSourceImpl.class); 358 binder.bind(ValidatorMacro.class, ValidatorMacroImpl.class); 359 binder.bind(PropertiesFileParser.class, PropertiesFileParserImpl.class); 360 binder.bind(PageActivator.class, PageActivatorImpl.class); 361 binder.bind(Dispatcher.class, AssetDispatcher.class).withSimpleId(); 362 binder.bind(TranslatorAlternatesSource.class, TranslatorAlternatesSourceImpl.class); 363 binder.bind(MetaWorker.class, MetaWorkerImpl.class); 364 binder.bind(LinkTransformer.class, LinkTransformerImpl.class); 365 binder.bind(SelectModelFactory.class, SelectModelFactoryImpl.class); 366 binder.bind(DynamicTemplateParser.class, DynamicTemplateParserImpl.class); 367 binder.bind(AjaxResponseRenderer.class, AjaxResponseRendererImpl.class); 368 binder.bind(AlertManager.class, AlertManagerImpl.class); 369 binder.bind(ValidationDecoratorFactory.class, ValidationDecoratorFactoryImpl.class); 370 binder.bind(PropertyConduitSource.class, PropertyConduitSourceImpl.class); 371 binder.bind(ClientWhitelist.class, ClientWhitelistImpl.class); 372 binder.bind(MetaDataLocator.class, MetaDataLocatorImpl.class); 373 binder.bind(ComponentClassCache.class, ComponentClassCacheImpl.class); 374 binder.bind(PageActivationContextCollector.class, PageActivationContextCollectorImpl.class); 375 binder.bind(StringInterner.class, StringInternerImpl.class); 376 binder.bind(ValueEncoderSource.class, ValueEncoderSourceImpl.class); 377 binder.bind(PathConstructor.class, PathConstructorImpl.class); 378 binder.bind(DateUtilities.class, DateUtilitiesImpl.class); 379 binder.bind(PartialTemplateRenderer.class, PartialTemplateRendererImpl.class); 380 binder.bind(ExceptionReporter.class, ExceptionReporterImpl.class); 381 binder.bind(ExceptionReportWriter.class, ExceptionReportWriterImpl.class); 382 binder.bind(ComponentOverride.class, ComponentOverrideImpl.class).eagerLoad(); 383 binder.bind(Html5Support.class, Html5SupportImpl.class); 384 } 385 386 // ======================================================================== 387 // 388 // Service Builder Methods (static) 389 // 390 // ======================================================================== 391 392 // ======================================================================== 393 // 394 // Service Contribution Methods (static) 395 // 396 // ======================================================================== 397 398 /** 399 * Contributes the factory for several built-in binding prefixes ("asset", 400 * "block", "component", "literal", prop", 401 * "nullfieldstrategy", "message", "validate", "translate", "var"). 402 */ 403 public static void contributeBindingSource(MappedConfiguration<String, BindingFactory> configuration, 404 405 @InjectService("PropBindingFactory") 406 BindingFactory propBindingFactory, 407 408 @InjectService("MessageBindingFactory") 409 BindingFactory messageBindingFactory, 410 411 @InjectService("ValidateBindingFactory") 412 BindingFactory validateBindingFactory, 413 414 @InjectService("TranslateBindingFactory") 415 BindingFactory translateBindingFactory, 416 417 @InjectService("AssetBindingFactory") 418 BindingFactory assetBindingFactory, 419 420 @InjectService("NullFieldStrategyBindingFactory") 421 BindingFactory nullFieldStrategyBindingFactory, 422 423 @InjectService("ContextBindingFactory") 424 BindingFactory contextBindingFactory, 425 426 @InjectService("SymbolBindingFactory") 427 BindingFactory symbolBindingFactory) 428 { 429 configuration.add(BindingConstants.LITERAL, new LiteralBindingFactory()); 430 configuration.add(BindingConstants.COMPONENT, new ComponentBindingFactory()); 431 configuration.add(BindingConstants.VAR, new RenderVariableBindingFactory()); 432 configuration.add(BindingConstants.BLOCK, new BlockBindingFactory()); 433 434 configuration.add(BindingConstants.PROP, propBindingFactory); 435 configuration.add(BindingConstants.MESSAGE, messageBindingFactory); 436 configuration.add(BindingConstants.VALIDATE, validateBindingFactory); 437 configuration.add(BindingConstants.TRANSLATE, translateBindingFactory); 438 configuration.add(BindingConstants.ASSET, assetBindingFactory); 439 configuration.add(BindingConstants.NULLFIELDSTRATEGY, nullFieldStrategyBindingFactory); 440 configuration.add(BindingConstants.CONTEXT, contextBindingFactory); 441 configuration.add(BindingConstants.SYMBOL, symbolBindingFactory); 442 } 443 444 445 @Contribute(ComponentClassResolver.class) 446 public static void provideCoreAndAppLibraries(Configuration<LibraryMapping> configuration, 447 @Symbol(InternalConstants.TAPESTRY_APP_PACKAGE_PARAM) 448 String appRootPackage) 449 { 450 configuration.add(new LibraryMapping(InternalConstants.CORE_LIBRARY, "org.apache.tapestry5.corelib")); 451 configuration.add(new LibraryMapping("", appRootPackage)); 452 } 453 454 /** 455 * Adds a number of standard component class transform workers: 456 * <dl> 457 * <dt>Parameter</dt> 458 * <dd>Identifies parameters based on the {@link org.apache.tapestry5.annotations.Parameter} annotation</dd> 459 * <dt>BindParameter</dt> 460 * <dd>Support for the {@link BindParameter} annotation</dd> 461 * <dt>Property</dt> 462 * <dd>Generates accessor methods if {@link org.apache.tapestry5.annotations.Property} annotation is present</dd> 463 * <dt>Import</dt> 464 * <dd>Supports the {@link Import} annotation</dd> 465 * <dt>UnclaimedField</dt> 466 * <dd>Manages unclaimed fields, storing their value in a {@link PerThreadValue}</dd> 467 * <dt>OnEvent</dt> 468 * <dd>Handle the @OnEvent annotation, and related naming convention</dd> 469 * <dt>RenderCommand</dt> 470 * <dd>Ensures all components also implement {@link org.apache.tapestry5.runtime.RenderCommand}</dd> 471 * <dt>SupportsInformalParameters</dt> 472 * <dd>Checks for the annotation</dd> 473 * <dt>RenderPhase</dt> 474 * <dd>Link in render phase methods</dd> 475 * <dt>Retain</dt> 476 * <dd>Allows fields to retain their values between requests</dd> 477 * <dt>Meta</dt> 478 * <dd>Checks for meta data annotations and adds it to the component model</dd> 479 * <dt>PageActivationContext</dt> <dd>Support for {@link PageActivationContext} annotation</dd> 480 * <dt>DiscardAfter</dt> <dd>Support for {@link DiscardAfter} method annotation </dd> 481 * <dt>MixinAfter</dt> <dd>Support for the {@link MixinAfter} mixin class annotation</dd> 482 * <dt>PageReset</dt> 483 * <dd>Checks for the {@link PageReset} annotation</dd> 484 * <dt>Mixin</dt> 485 * <dd>Adds a mixin as part of a component's implementation</dd> 486 * <dt>Cached</dt> 487 * <dd>Checks for the {@link org.apache.tapestry5.annotations.Cached} annotation</dd> 488 * <dt>ActivationRequestParameter</dt> 489 * <dd>Support for the {@link ActivationRequestParameter} annotation</dd> 490 * <dt>PageLoaded, PageAttached, PageDetached</dt> 491 * <dd>Support for annotations {@link PageLoaded}, {@link PageAttached}, {@link PageDetached}</dd> 492 * <dt>InjectService</dt> 493 * <dd>Handles the {@link org.apache.tapestry5.ioc.annotations.InjectService} annotation</dd> 494 * <dt>Component</dt> 495 * <dd>Defines embedded components based on the {@link org.apache.tapestry5.annotations.Component} annotation</dd> 496 * <dt>Environment</dt> 497 * <dd>Allows fields to contain values extracted from the {@link org.apache.tapestry5.services.Environment} service</dd> 498 * <dt>ApplicationState</dt> 499 * <dd>Converts fields that reference application state objects</dd> 500 * <dt>Persist</dt> 501 * <dd>Allows fields to store their their value persistently between requests via {@link Persist}</dd> 502 * <dt>SessionAttribute</dt> 503 * <dd>Support for the {@link SessionAttribute}</dd> 504 * <dt>Log</dt> 505 * <dd>Checks for the {@link org.apache.tapestry5.annotations.Log} annotation</dd> 506 * <dt>HeartbeatDeferred 507 * <dd>Support for the {@link HeartbeatDeferred} annotation, which defers method invocation to the end of the {@link Heartbeat} 508 * <dt>Inject</dt> 509 * <dd>Used with the {@link org.apache.tapestry5.ioc.annotations.Inject} annotation, when a value is supplied</dd> 510 * </dl> 511 * <dd>Operation</dt> <dd>Support for the {@link Operation} method annotation</dd></dd> 512 */ 513 @Contribute(ComponentClassTransformWorker2.class) 514 @Primary 515 public static void provideTransformWorkers( 516 OrderedConfiguration<ComponentClassTransformWorker2> configuration, 517 MetaWorker metaWorker, 518 ComponentClassResolver resolver) 519 { 520 configuration.add("Property", new PropertyWorker()); 521 522 // Order this one pretty early: 523 524 configuration.addInstance("Operation", OperationWorker.class); 525 526 configuration.add("RenderCommand", new RenderCommandWorker()); 527 528 configuration.addInstance("OnEvent", OnEventWorker.class); 529 530 configuration.add("MixinAfter", new MixinAfterWorker()); 531 532 // These must come after Property, since they actually delete fields 533 // that may still have the annotation 534 configuration.addInstance("ApplicationState", ApplicationStateWorker.class); 535 configuration.addInstance("Environment", EnvironmentalWorker.class); 536 537 configuration.add("Component", new ComponentWorker(resolver)); 538 configuration.add("Mixin", new MixinWorker(resolver)); 539 configuration.addInstance("InjectPage", InjectPageWorker.class); 540 configuration.addInstance("InjectComponent", InjectComponentWorker.class); 541 configuration.addInstance("InjectContainer", InjectContainerWorker.class); 542 543 // Default values for parameters are often some form of injection, so 544 // make sure that Parameter fields are processed after injections. 545 546 configuration.addInstance("Parameter", ParameterWorker.class); 547 548 // bind parameter should always go after parameter to make sure all 549 // parameters have been properly setup. 550 configuration.addInstance("BindParameter", BindParameterWorker.class); 551 552 configuration.add("SupportsInformalParameters", new SupportsInformalParametersWorker()); 553 554 configuration.addInstance("RenderPhase", RenderPhaseMethodWorker.class); 555 556 // Import advises methods, usually render phase methods, so it must come after RenderPhase. 557 558 configuration.addInstance("Import", ImportWorker.class); 559 560 configuration.add("Meta", metaWorker.getWorker()); 561 562 configuration.add("Retain", new RetainWorker()); 563 564 configuration.add("PageActivationContext", new PageActivationContextWorker()); 565 configuration 566 .addInstance("ActivationRequestParameter", ActivationRequestParameterWorker.class); 567 568 configuration.addInstance("Cached", CachedWorker.class); 569 570 configuration.addInstance("DiscardAfter", DiscardAfterWorker.class); 571 572 add(configuration, PageLoaded.class, TransformConstants.CONTAINING_PAGE_DID_LOAD_DESCRIPTION); 573 add(configuration, PageAttached.class, TransformConstants.CONTAINING_PAGE_DID_ATTACH_DESCRIPTION); 574 add(configuration, PageDetached.class, TransformConstants.CONTAINING_PAGE_DID_DETACH_DESCRIPTION); 575 576 configuration.addInstance("PageReset", PageResetAnnotationWorker.class); 577 configuration.addInstance("InjectService", InjectServiceWorker.class); 578 579 configuration.addInstance("Inject", InjectWorker.class); 580 581 configuration.addInstance("Persist", PersistWorker.class); 582 583 configuration.addInstance("SessionAttribute", SessionAttributeWorker.class); 584 585 configuration.addInstance("Log", LogWorker.class); 586 587 configuration.addInstance("HeartbeatDeferred", HeartbeatDeferredWorker.class); 588 589 // This one is always last. Any additional private fields that aren't 590 // annotated will 591 // be converted to clear out at the end of the request. 592 593 configuration.addInstance("UnclaimedField", UnclaimedFieldWorker.class, "after:*"); 594 } 595 596 /** 597 * <dl> 598 * <dt>Annotation</dt> 599 * <dd>Checks for {@link org.apache.tapestry5.beaneditor.DataType} annotation</dd> 600 * <dt>Default (ordered last)</dt> 601 * <dd> 602 * {@link org.apache.tapestry5.internal.services.DefaultDataTypeAnalyzer} service ( 603 * {@link #contributeDefaultDataTypeAnalyzer(org.apache.tapestry5.ioc.MappedConfiguration)} )</dd> 604 * </dl> 605 */ 606 public static void contributeDataTypeAnalyzer(OrderedConfiguration<DataTypeAnalyzer> configuration, 607 @InjectService("DefaultDataTypeAnalyzer") 608 DataTypeAnalyzer defaultDataTypeAnalyzer) 609 { 610 configuration.add("Annotation", new AnnotationDataTypeAnalyzer()); 611 configuration.add("Default", defaultDataTypeAnalyzer, "after:*"); 612 } 613 614 /** 615 * Maps property types to data type names: 616 * <ul> 617 * <li>String --> text 618 * <li>Number --> number 619 * <li>Enum --> enum 620 * <li>Boolean --> boolean 621 * <li>Date --> date 622 * </ul> 623 */ 624 public static void contributeDefaultDataTypeAnalyzer(MappedConfiguration<Class, String> configuration) 625 { 626 BasicDataTypeAnalyzers.provideDefaultDataTypeAnalyzers(configuration); 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, Html5Support html5Support) 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, html5Support)); 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 the "core" stack (necessary to get the Bootstrap CSS, if nothing else).</dd> 1711 * </dl> 1712 * 1713 * @see org.apache.tapestry5.SymbolConstants#OMIT_GENERATOR_META 1714 * @see org.apache.tapestry5.SymbolConstants#PRODUCTION_MODE 1715 * @see org.apache.tapestry5.SymbolConstants#INCLUDE_CORE_STACK 1716 * @see org.apache.tapestry5.SymbolConstants#ENABLE_PAGELOADING_MASK 1717 */ 1718 public void contributeMarkupRenderer(OrderedConfiguration<MarkupRendererFilter> configuration, 1719 1720 final ModuleManager moduleManager, 1721 1722 @Symbol(SymbolConstants.OMIT_GENERATOR_META) 1723 final boolean omitGeneratorMeta, 1724 1725 @Symbol(SymbolConstants.TAPESTRY_VERSION) 1726 final String tapestryVersion, 1727 1728 @Symbol(SymbolConstants.PRODUCTION_MODE) 1729 boolean productionMode, 1730 1731 @Symbol(SymbolConstants.INCLUDE_CORE_STACK) 1732 final boolean includeCoreStack, 1733 1734 @Symbol(SymbolConstants.ENABLE_PAGELOADING_MASK) 1735 final boolean enablePageloadingMask, 1736 1737 final ValidationDecoratorFactory validationDecoratorFactory) 1738 { 1739 MarkupRendererFilter documentLinker = new MarkupRendererFilter() 1740 { 1741 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer) 1742 { 1743 DocumentLinkerImpl linker = new DocumentLinkerImpl(moduleManager, omitGeneratorMeta, enablePageloadingMask, tapestryVersion); 1744 1745 environment.push(DocumentLinker.class, linker); 1746 1747 renderer.renderMarkup(writer); 1748 1749 environment.pop(DocumentLinker.class); 1750 1751 linker.updateDocument(writer.getDocument()); 1752 } 1753 }; 1754 1755 1756 MarkupRendererFilter clientBehaviorSupport = new MarkupRendererFilter() 1757 { 1758 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer) 1759 { 1760 ClientBehaviorSupportImpl clientBehaviorSupport = new ClientBehaviorSupportImpl(); 1761 1762 environment.push(ClientBehaviorSupport.class, clientBehaviorSupport); 1763 1764 renderer.renderMarkup(writer); 1765 1766 environment.pop(ClientBehaviorSupport.class); 1767 } 1768 }; 1769 1770 MarkupRendererFilter heartbeat = new MarkupRendererFilter() 1771 { 1772 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer) 1773 { 1774 Heartbeat heartbeat = new HeartbeatImpl(); 1775 1776 heartbeat.begin(); 1777 1778 environment.push(Heartbeat.class, heartbeat); 1779 1780 renderer.renderMarkup(writer); 1781 1782 environment.pop(Heartbeat.class); 1783 1784 heartbeat.end(); 1785 } 1786 }; 1787 1788 MarkupRendererFilter defaultValidationDecorator = new MarkupRendererFilter() 1789 { 1790 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer) 1791 { 1792 ValidationDecorator decorator = validationDecoratorFactory.newInstance(writer); 1793 1794 environment.push(ValidationDecorator.class, decorator); 1795 1796 renderer.renderMarkup(writer); 1797 1798 environment.pop(ValidationDecorator.class); 1799 } 1800 }; 1801 1802 MarkupRendererFilter importCoreStack = new MarkupRendererFilter() 1803 { 1804 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer) 1805 { 1806 renderer.renderMarkup(writer); 1807 1808 environment.peekRequired(JavaScriptSupport.class).importStack(InternalConstants.CORE_STACK_NAME); 1809 } 1810 }; 1811 1812 configuration.add("DocumentLinker", documentLinker); 1813 configuration.add("ClientBehaviorSupport", clientBehaviorSupport, "after:JavaScriptSupport"); 1814 configuration.add("Heartbeat", heartbeat); 1815 configuration.add("ValidationDecorator", defaultValidationDecorator); 1816 1817 if (includeCoreStack) 1818 { 1819 configuration.add("ImportCoreStack", importCoreStack); 1820 } 1821 1822 if (productionMode) 1823 { 1824 configuration.add("PageNameMeta", null); 1825 } else 1826 { 1827 configuration.addInstance("PageNameMeta", PageNameMetaInjector.class); 1828 } 1829 } 1830 1831 /** 1832 * Contributes {@link PartialMarkupRendererFilter}s used when rendering a 1833 * partial Ajax response. 1834 * <dl> 1835 * <dt>DocumentLinker 1836 * <dd>Provides {@link org.apache.tapestry5.internal.services.DocumentLinker} 1837 * <dt>ClientBehaviorSupport</dt> 1838 * <dd>Provides {@link ClientBehaviorSupport}</dd> 1839 * <dt>Heartbeat</dt> 1840 * <dd>Provides {@link org.apache.tapestry5.services.Heartbeat}</dd> 1841 * <dt>DefaultValidationDecorator</dt> 1842 * <dt>ValidationDecorator</dt> 1843 * <dd>Provides {@link org.apache.tapestry5.ValidationDecorator} (via {@link ValidationDecoratorFactory#newInstance(org.apache.tapestry5.MarkupWriter)})</dd> 1844 * </dl> 1845 */ 1846 public void contributePartialMarkupRenderer(OrderedConfiguration<PartialMarkupRendererFilter> configuration, 1847 1848 final ValidationDecoratorFactory validationDecoratorFactory) 1849 { 1850 PartialMarkupRendererFilter documentLinker = new PartialMarkupRendererFilter() 1851 { 1852 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer) 1853 { 1854 PartialMarkupDocumentLinker linker = new PartialMarkupDocumentLinker(); 1855 1856 environment.push(DocumentLinker.class, linker); 1857 1858 renderer.renderMarkup(writer, reply); 1859 1860 environment.pop(DocumentLinker.class); 1861 1862 linker.commit(reply); 1863 } 1864 }; 1865 1866 1867 PartialMarkupRendererFilter clientBehaviorSupport = new PartialMarkupRendererFilter() 1868 { 1869 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer) 1870 { 1871 ClientBehaviorSupportImpl support = new ClientBehaviorSupportImpl(); 1872 1873 environment.push(ClientBehaviorSupport.class, support); 1874 1875 renderer.renderMarkup(writer, reply); 1876 1877 environment.pop(ClientBehaviorSupport.class); 1878 } 1879 }; 1880 1881 PartialMarkupRendererFilter heartbeat = new PartialMarkupRendererFilter() 1882 { 1883 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer) 1884 { 1885 Heartbeat heartbeat = new HeartbeatImpl(); 1886 1887 heartbeat.begin(); 1888 1889 environment.push(Heartbeat.class, heartbeat); 1890 1891 renderer.renderMarkup(writer, reply); 1892 1893 environment.pop(Heartbeat.class); 1894 1895 heartbeat.end(); 1896 } 1897 }; 1898 1899 PartialMarkupRendererFilter defaultValidationDecorator = new PartialMarkupRendererFilter() 1900 { 1901 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer) 1902 { 1903 ValidationDecorator decorator = validationDecoratorFactory.newInstance(writer); 1904 1905 environment.push(ValidationDecorator.class, decorator); 1906 1907 renderer.renderMarkup(writer, reply); 1908 1909 environment.pop(ValidationDecorator.class); 1910 } 1911 }; 1912 1913 configuration.add("DocumentLinker", documentLinker); 1914 configuration.add("ClientBehaviorSupport", clientBehaviorSupport, "after:JavaScriptSupport"); 1915 configuration.add("Heartbeat", heartbeat); 1916 configuration.add("ValidationDecorator", defaultValidationDecorator); 1917 } 1918 1919 /** 1920 * Contributes several strategies: 1921 * <dl> 1922 * <dt>session 1923 * <dd>Values are stored in the {@link Session} 1924 * <dt>flash 1925 * <dd>Values are stored in the {@link Session}, until the next request (for the page) 1926 * <dt>client 1927 * <dd>Values are encoded into URLs (or hidden form fields) 1928 * </dl> 1929 */ 1930 public void contributePersistentFieldManager(MappedConfiguration<String, PersistentFieldStrategy> configuration, 1931 1932 Request request, 1933 1934 @InjectService("ClientPersistentFieldStrategy") 1935 PersistentFieldStrategy clientStrategy) 1936 { 1937 configuration.add(PersistenceConstants.SESSION, new SessionPersistentFieldStrategy(request)); 1938 configuration.add(PersistenceConstants.FLASH, new FlashPersistentFieldStrategy(request)); 1939 configuration.add(PersistenceConstants.CLIENT, clientStrategy); 1940 } 1941 1942 /** 1943 * Contributes {@link ValueEncoder}s or {@link ValueEncoderFactory}s for types: 1944 * <ul> 1945 * <li>Object 1946 * <li>String 1947 * <li>Enum 1948 * </ul> 1949 */ 1950 @SuppressWarnings("all") 1951 public static void contributeValueEncoderSource(MappedConfiguration<Class, Object> configuration) 1952 { 1953 configuration.addInstance(Object.class, TypeCoercedValueEncoderFactory.class); 1954 configuration.add(String.class, new StringValueEncoder()); 1955 configuration.addInstance(Enum.class, EnumValueEncoderFactory.class); 1956 } 1957 1958 /** 1959 * Contributes a single filter, "Secure", which checks for non-secure 1960 * requests that access secure pages. 1961 */ 1962 public void contributePageRenderRequestHandler(OrderedConfiguration<PageRenderRequestFilter> configuration, 1963 final RequestSecurityManager securityManager) 1964 { 1965 PageRenderRequestFilter secureFilter = new PageRenderRequestFilter() 1966 { 1967 public void handle(PageRenderRequestParameters parameters, PageRenderRequestHandler handler) 1968 throws IOException 1969 { 1970 1971 if (securityManager.checkForInsecurePageRenderRequest(parameters)) 1972 return; 1973 1974 handler.handle(parameters); 1975 } 1976 }; 1977 1978 configuration.add("Secure", secureFilter); 1979 } 1980 1981 public static void contributeTemplateParser(MappedConfiguration<String, URL> config) 1982 { 1983 // Any class inside the internal module would do. Or we could move all 1984 // these 1985 // files to o.a.t.services. 1986 1987 Class c = TemplateParserImpl.class; 1988 1989 config.add("-//W3C//DTD XHTML 1.0 Strict//EN", c.getResource("xhtml1-strict.dtd")); 1990 config.add("-//W3C//DTD XHTML 1.0 Transitional//EN", c.getResource("xhtml1-transitional.dtd")); 1991 config.add("-//W3C//DTD XHTML 1.0 Frameset//EN", c.getResource("xhtml1-frameset.dtd")); 1992 config.add("-//W3C//DTD HTML 4.01//EN", c.getResource("xhtml1-strict.dtd")); 1993 config.add("-//W3C//DTD HTML 4.01 Transitional//EN", c.getResource("xhtml1-transitional.dtd")); 1994 config.add("-//W3C//DTD HTML 4.01 Frameset//EN", c.getResource("xhtml1-frameset.dtd")); 1995 config.add("-//W3C//ENTITIES Latin 1 for XHTML//EN", c.getResource("xhtml-lat1.ent")); 1996 config.add("-//W3C//ENTITIES Symbols for XHTML//EN", c.getResource("xhtml-symbol.ent")); 1997 config.add("-//W3C//ENTITIES Special for XHTML//EN", c.getResource("xhtml-special.ent")); 1998 } 1999 2000 /** 2001 * Contributes factory defaults that may be overridden. 2002 */ 2003 public static void contributeFactoryDefaults(MappedConfiguration<String, Object> configuration) 2004 { 2005 // Remember this is request-to-request time, presumably it'll take the 2006 // developer more than 2007 // one second to make a change, save it, and switch back to the browser. 2008 2009 configuration.add(SymbolConstants.FILE_CHECK_INTERVAL, "1 s"); 2010 configuration.add(SymbolConstants.FILE_CHECK_UPDATE_TIMEOUT, "50 ms"); 2011 2012 // This should be overridden for particular applications. These are the 2013 // locales for which we have (at least some) localized messages. 2014 configuration.add(SymbolConstants.SUPPORTED_LOCALES, 2015 "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"); 2016 2017 configuration.add(SymbolConstants.TAPESTRY_VERSION, 2018 VersionUtils.readVersionNumber("META-INF/gradle/org.apache.tapestry/tapestry-core/project.properties")); 2019 2020 configuration.add(SymbolConstants.COOKIE_MAX_AGE, "7 d"); 2021 2022 configuration.add(SymbolConstants.START_PAGE_NAME, "start"); 2023 2024 configuration.add(SymbolConstants.DEFAULT_STYLESHEET, ""); 2025 2026 configuration.add(SymbolConstants.PRODUCTION_MODE, true); 2027 2028 configuration.add(SymbolConstants.CLUSTERED_SESSIONS, true); 2029 2030 configuration.add(SymbolConstants.COMPRESS_WHITESPACE, true); 2031 2032 configuration.add(MetaDataConstants.SECURE_PAGE, false); 2033 2034 configuration.add(SymbolConstants.FORM_CLIENT_LOGIC_ENABLED, true); 2035 2036 // This is designed to make it easy to keep synchronized with 2037 // script.aculo.ous. As we support a new version, we create a new folder, and update the 2038 // path entry. We can then delete the old version folder (or keep it around). This should 2039 // be more manageable than overwriting the local copy with updates (it's too easy for 2040 // files deleted between scriptaculous releases to be accidentally left lying around). 2041 // There's also a ClasspathAliasManager contribution based on the path. 2042 2043 configuration.add(SymbolConstants.SCRIPTACULOUS, "${tapestry.asset.root}/scriptaculous_1_9_0"); 2044 2045 // Likewise for WebFX DatePicker, currently version 1.0.6 2046 2047 configuration.add(SymbolConstants.DATEPICKER, "${tapestry.asset.root}/datepicker_106"); 2048 2049 configuration.add(SymbolConstants.PERSISTENCE_STRATEGY, PersistenceConstants.SESSION); 2050 2051 configuration.add(MetaDataConstants.RESPONSE_CONTENT_TYPE, "text/html"); 2052 2053 configuration.add(SymbolConstants.CHARSET, "UTF-8"); 2054 2055 configuration.add(SymbolConstants.APPLICATION_CATALOG, 2056 String.format("context:WEB-INF/${%s}.properties", InternalSymbols.APP_NAME)); 2057 2058 configuration.add(SymbolConstants.EXCEPTION_REPORT_PAGE, "ExceptionReport"); 2059 2060 configuration.add(SymbolConstants.MIN_GZIP_SIZE, 100); 2061 2062 configuration.add(SymbolConstants.APPLICATION_VERSION, "0.0.1"); 2063 2064 configuration.add(SymbolConstants.OMIT_GENERATOR_META, false); 2065 2066 configuration.add(SymbolConstants.SECURE_ENABLED, SymbolConstants.PRODUCTION_MODE_VALUE); 2067 configuration.add(SymbolConstants.COMPACT_JSON, SymbolConstants.PRODUCTION_MODE_VALUE); 2068 2069 configuration.add(SymbolConstants.ENCODE_LOCALE_INTO_PATH, true); 2070 2071 configuration.add(InternalSymbols.RESERVED_FORM_CONTROL_NAMES, "reset,submit,select,id,method,action,onsubmit," + InternalConstants.CANCEL_NAME); 2072 2073 configuration.add(SymbolConstants.COMPONENT_RENDER_TRACING_ENABLED, false); 2074 2075 // The default values denote "use values from request" 2076 configuration.add(SymbolConstants.HOSTNAME, ""); 2077 configuration.add(SymbolConstants.HOSTPORT, 0); 2078 configuration.add(SymbolConstants.HOSTPORT_SECURE, 0); 2079 2080 configuration.add(SymbolConstants.APPLICATION_FOLDER, ""); 2081 2082 // Grid component parameter defaults 2083 configuration.add(ComponentParameterConstants.GRID_ROWS_PER_PAGE, GridConstants.ROWS_PER_PAGE); 2084 configuration.add(ComponentParameterConstants.GRID_PAGER_POSITION, GridConstants.PAGER_POSITION); 2085 configuration.add(ComponentParameterConstants.GRID_EMPTY_BLOCK, GridConstants.EMPTY_BLOCK); 2086 configuration.add(ComponentParameterConstants.GRID_TABLE_CSS_CLASS, GridConstants.TABLE_CLASS); 2087 configuration.add(ComponentParameterConstants.GRIDPAGER_PAGE_RANGE, GridConstants.PAGER_PAGE_RANGE); 2088 configuration.add(ComponentParameterConstants.GRIDCOLUMNS_SORTABLE_ASSET, GridConstants.COLUMNS_SORTABLE); 2089 configuration.add(ComponentParameterConstants.GRIDCOLUMNS_ASCENDING_ASSET, GridConstants.COLUMNS_ASCENDING); 2090 configuration.add(ComponentParameterConstants.GRIDCOLUMNS_DESCENDING_ASSET, GridConstants.COLUMNS_DESCENDING); 2091 2092 // FormInjector component parameter defaults 2093 configuration.add(ComponentParameterConstants.FORMINJECTOR_INSERT_POSITION, "above"); 2094 configuration.add(ComponentParameterConstants.FORMINJECTOR_SHOW_FUNCTION, "highlight"); 2095 2096 // Palette component parameter defaults 2097 configuration.add(ComponentParameterConstants.PALETTE_ROWS_SIZE, 10); 2098 2099 // Defaults for components that use a SelectModel 2100 configuration.add(ComponentParameterConstants.VALIDATE_WITH_MODEL, SecureOption.AUTO); 2101 2102 // Zone component parameters defaults 2103 configuration.add(ComponentParameterConstants.ZONE_SHOW_METHOD, "show"); 2104 configuration.add(ComponentParameterConstants.ZONE_UPDATE_METHOD, "highlight"); 2105 2106 // By default, no page is on the whitelist unless it has the @WhitelistAccessOnly annotation 2107 configuration.add(MetaDataConstants.WHITELIST_ONLY_PAGE, false); 2108 2109 configuration.add(SymbolConstants.CONTEXT_PATH, ""); 2110 2111 // Leaving this as the default results in a runtime error logged to the console (and a default password is used); 2112 // you are expected to override this symbol. 2113 configuration.add(SymbolConstants.HMAC_PASSPHRASE, ""); 2114 2115 configuration.add(SymbolConstants.SESSION_LOCKING_ENABLED, true); 2116 2117 // TAP5-2070 keep the old behavior, defaults to false 2118 configuration.add(MetaDataConstants.UNKNOWN_ACTIVATION_CONTEXT_CHECK, false); 2119 2120 // TAP5-2197 2121 configuration.add(SymbolConstants.INCLUDE_CORE_STACK, true); 2122 2123 // TAP5-2182 2124 configuration.add(SymbolConstants.FORM_GROUP_WRAPPER_CSS_CLASS, "form-group"); 2125 configuration.add(SymbolConstants.FORM_GROUP_LABEL_CSS_CLASS, "control-label"); 2126 configuration.add(SymbolConstants.FORM_GROUP_FORM_FIELD_WRAPPER_ELEMENT_NAME, ""); 2127 configuration.add(SymbolConstants.FORM_GROUP_FORM_FIELD_WRAPPER_ELEMENT_CSS_CLASS, ""); 2128 configuration.add(SymbolConstants.FORM_FIELD_CSS_CLASS, "form-control"); 2129 2130 // TAP5-1998 2131 configuration.add(SymbolConstants.LENIENT_DATE_FORMAT, false); 2132 2133 // TAP5-2187 2134 configuration.add(SymbolConstants.STRICT_CSS_URL_REWRITING, false); 2135 2136 configuration.add(SymbolConstants.EXCEPTION_REPORTS_DIR, "build/exceptions"); 2137 2138 // TAP5-1815 2139 configuration.add(SymbolConstants.ENABLE_HTML5_SUPPORT, false); 2140 2141 configuration.add(SymbolConstants.RESTRICTIVE_ENVIRONMENT, false); 2142 2143 configuration.add(SymbolConstants.ENABLE_PAGELOADING_MASK, true); 2144 configuration.add(SymbolConstants.PRELOADER_MODE, PreloaderMode.PRODUCTION); 2145 } 2146 2147 /** 2148 * Adds a listener to the {@link org.apache.tapestry5.internal.services.ComponentInstantiatorSource} that clears the 2149 * {@link PropertyAccess} and {@link TypeCoercer} caches on 2150 * a class loader invalidation. In addition, forces the 2151 * realization of {@link ComponentClassResolver} at startup. 2152 */ 2153 public void contributeApplicationInitializer(OrderedConfiguration<ApplicationInitializerFilter> configuration, 2154 final TypeCoercer typeCoercer, final ComponentClassResolver componentClassResolver, @ComponentClasses 2155 final InvalidationEventHub invalidationEventHub, final @Autobuild 2156 RestoreDirtySessionObjects restoreDirtySessionObjects) 2157 { 2158 final Runnable callback = new Runnable() 2159 { 2160 public void run() 2161 { 2162 propertyAccess.clearCache(); 2163 2164 typeCoercer.clearCache(); 2165 } 2166 }; 2167 2168 ApplicationInitializerFilter clearCaches = new ApplicationInitializerFilter() 2169 { 2170 public void initializeApplication(Context context, ApplicationInitializer initializer) 2171 { 2172 // Snuck in here is the logic to clear the PropertyAccess 2173 // service's cache whenever 2174 // the component class loader is invalidated. 2175 2176 invalidationEventHub.addInvalidationCallback(callback); 2177 2178 endOfRequestEventHub.addEndOfRequestListener(restoreDirtySessionObjects); 2179 2180 // Perform other pending initialization 2181 2182 initializer.initializeApplication(context); 2183 2184 // We don't care about the result, but this forces a load of the 2185 // service 2186 // at application startup, rather than on first request. 2187 2188 componentClassResolver.isPageName("ForceLoadAtStartup"); 2189 } 2190 }; 2191 2192 configuration.add("ClearCachesOnInvalidation", clearCaches); 2193 } 2194 2195 /** 2196 * Contributes filters: 2197 * <dl> 2198 * <dt>Ajax</dt> 2199 * <dd>Determines if the request is Ajax oriented, and redirects to an alternative handler if so</dd> 2200 * <dt>Secure</dt> 2201 * <dd>Sends a redirect if an non-secure request accesses a secure page</dd> 2202 * </dl> 2203 */ 2204 public void contributeComponentEventRequestHandler(OrderedConfiguration<ComponentEventRequestFilter> configuration, 2205 final RequestSecurityManager requestSecurityManager, @Ajax 2206 ComponentEventRequestHandler ajaxHandler) 2207 { 2208 ComponentEventRequestFilter secureFilter = new ComponentEventRequestFilter() 2209 { 2210 public void handle(ComponentEventRequestParameters parameters, ComponentEventRequestHandler handler) 2211 throws IOException 2212 { 2213 if (requestSecurityManager.checkForInsecureComponentEventRequest(parameters)) 2214 return; 2215 2216 handler.handle(parameters); 2217 } 2218 }; 2219 configuration.add("Secure", secureFilter); 2220 2221 configuration.add("Ajax", new AjaxFilter(request, ajaxHandler)); 2222 } 2223 2224 /** 2225 * Contributes: 2226 * <dl> 2227 * <dt>AjaxFormUpdate</dt> 2228 * <dd>{@link AjaxFormUpdateFilter}</dd> 2229 * </dl> 2230 * 2231 * @since 5.2.0 2232 */ 2233 public static void contributeAjaxComponentEventRequestHandler( 2234 OrderedConfiguration<ComponentEventRequestFilter> configuration) 2235 { 2236 configuration.addInstance("AjaxFormUpdate", AjaxFormUpdateFilter.class); 2237 } 2238 2239 /** 2240 * Contributes strategies accessible via the {@link NullFieldStrategySource} service. 2241 * <p/> 2242 * <dl> 2243 * <dt>default</dt> 2244 * <dd>Does nothing, nulls stay null.</dd> 2245 * <dt>zero</dt> 2246 * <dd>Null values are converted to zero.</dd> 2247 * </dl> 2248 */ 2249 public static void contributeNullFieldStrategySource(MappedConfiguration<String, NullFieldStrategy> configuration) 2250 { 2251 configuration.add("default", new DefaultNullFieldStrategy()); 2252 configuration.add("zero", new ZeroNullFieldStrategy()); 2253 } 2254 2255 /** 2256 * Determines positioning of hidden fields relative to other elements (this 2257 * is needed by {@link org.apache.tapestry5.corelib.components.FormFragment} and others. 2258 * <p/> 2259 * For elements input, select, textarea and label the hidden field is positioned after. 2260 * <p/> 2261 * For elements p, div, li and td, the hidden field is positioned inside. 2262 */ 2263 public static void contributeHiddenFieldLocationRules( 2264 MappedConfiguration<String, RelativeElementPosition> configuration) 2265 { 2266 configuration.add("input", RelativeElementPosition.AFTER); 2267 configuration.add("select", RelativeElementPosition.AFTER); 2268 configuration.add("textarea", RelativeElementPosition.AFTER); 2269 configuration.add("label", RelativeElementPosition.AFTER); 2270 2271 configuration.add("p", RelativeElementPosition.INSIDE); 2272 configuration.add("div", RelativeElementPosition.INSIDE); 2273 configuration.add("td", RelativeElementPosition.INSIDE); 2274 configuration.add("li", RelativeElementPosition.INSIDE); 2275 } 2276 2277 /** 2278 * @since 5.1.0.0 2279 */ 2280 public static LinkCreationHub buildLinkCreationHub(LinkSource source) 2281 { 2282 return source.getLinkCreationHub(); 2283 } 2284 2285 /** 2286 * Exposes the public portion of the internal {@link InternalComponentInvalidationEventHub} service. 2287 * 2288 * @since 5.1.0.0 2289 */ 2290 @Marker(ComponentClasses.class) 2291 public static InvalidationEventHub buildComponentClassesInvalidationEventHub( 2292 InternalComponentInvalidationEventHub trueHub) 2293 { 2294 return trueHub; 2295 } 2296 2297 /** 2298 * @since 5.1.0.0 2299 */ 2300 @Marker(ComponentTemplates.class) 2301 public static InvalidationEventHub buildComponentTemplatesInvalidationEventHub( 2302 ComponentTemplateSource templateSource) 2303 { 2304 return templateSource.getInvalidationEventHub(); 2305 } 2306 2307 /** 2308 * @since 5.1.0.0 2309 */ 2310 @Marker(ComponentMessages.class) 2311 public static InvalidationEventHub buildComponentMessagesInvalidationEventHub(ComponentMessagesSource messagesSource) 2312 { 2313 return messagesSource.getInvalidationEventHub(); 2314 } 2315 2316 @Scope(ScopeConstants.PERTHREAD) 2317 public Environment buildEnvironment(PerthreadManager perthreadManager) 2318 { 2319 final EnvironmentImpl service = new EnvironmentImpl(); 2320 2321 perthreadManager.addThreadCleanupCallback(new Runnable() 2322 { 2323 public void run() 2324 { 2325 service.threadDidCleanup(); 2326 } 2327 }); 2328 2329 return service; 2330 } 2331 2332 /** 2333 * The master SessionPersistedObjectAnalyzer. 2334 * 2335 * @since 5.1.0.0 2336 */ 2337 @Marker(Primary.class) 2338 public SessionPersistedObjectAnalyzer buildSessionPersistedObjectAnalyzer( 2339 Map<Class, SessionPersistedObjectAnalyzer> configuration) 2340 { 2341 return strategyBuilder.build(SessionPersistedObjectAnalyzer.class, configuration); 2342 } 2343 2344 /** 2345 * Identifies String, Number and Boolean as immutable objects, a catch-all 2346 * handler for Object (that understands 2347 * the {@link org.apache.tapestry5.annotations.ImmutableSessionPersistedObject} annotation), 2348 * and a handler for {@link org.apache.tapestry5.OptimizedSessionPersistedObject}. 2349 * 2350 * @since 5.1.0.0 2351 */ 2352 public static void contributeSessionPersistedObjectAnalyzer( 2353 MappedConfiguration<Class, SessionPersistedObjectAnalyzer> configuration) 2354 { 2355 configuration.add(Object.class, new DefaultSessionPersistedObjectAnalyzer()); 2356 2357 SessionPersistedObjectAnalyzer<Object> immutable = new SessionPersistedObjectAnalyzer<Object>() 2358 { 2359 public boolean checkAndResetDirtyState(Object sessionPersistedObject) 2360 { 2361 return false; 2362 } 2363 }; 2364 2365 configuration.add(String.class, immutable); 2366 configuration.add(Number.class, immutable); 2367 configuration.add(Boolean.class, immutable); 2368 2369 configuration.add(OptimizedSessionPersistedObject.class, new OptimizedSessionPersistedObjectAnalyzer()); 2370 } 2371 2372 /** 2373 * @since 5.1.1.0 2374 */ 2375 @Marker(Primary.class) 2376 public StackTraceElementAnalyzer buildMasterStackTraceElementAnalyzer(List<StackTraceElementAnalyzer> configuration) 2377 { 2378 return chainBuilder.build(StackTraceElementAnalyzer.class, configuration); 2379 } 2380 2381 /** 2382 * Contributes: 2383 * <dl> 2384 * <dt>Application</dt> 2385 * <dd>Checks for classes in the application package</dd> 2386 * <dt>Proxies</dt> 2387 * <dd>Checks for classes that appear to be generated proxies.</dd> 2388 * <dt>SunReflect</dt> 2389 * <dd>Checks for <code>sun.reflect</code> (which are omitted) 2390 * <dt>TapestryAOP</dt> 2391 * <dd>Omits stack frames for classes related to Tapestry AOP (such as advice, etc.)</dd> 2392 * <dt>OperationTracker</dt> 2393 * <dd>Omits stack frames related to {@link OperationTracker}</dd> 2394 * <dt>Access</dt> 2395 * <dd>Omits stack frames used to provide access to container class private members</dd> 2396 * </dl> 2397 * 2398 * @since 5.1.0.0 2399 */ 2400 public static void contributeMasterStackTraceElementAnalyzer( 2401 OrderedConfiguration<StackTraceElementAnalyzer> configuration) 2402 { 2403 configuration.addInstance("TapestryAOP", TapestryAOPStackFrameAnalyzer.class); 2404 configuration.add("Proxies", new ProxiesStackTraceElementAnalyzer()); 2405 configuration.add("Synthetic", new SyntheticStackTraceElementAnalyzer()); 2406 configuration.add("SunReflect", new PrefixCheckStackTraceElementAnalyzer( 2407 StackTraceElementClassConstants.OMITTED, "sun.reflect.")); 2408 configuration.add("OperationTracker", new RegexpStackTraceElementAnalyzer(Pattern.compile("internal\\.(RegistryImpl|PerThreadOperationTracker|OperationTrackerImpl).*(run|invoke|perform)\\("), StackTraceElementClassConstants.OMITTED)); 2409 configuration.add("Access", new RegexpStackTraceElementAnalyzer(Pattern.compile("\\.access\\$\\d+\\("), StackTraceElementClassConstants.OMITTED)); 2410 2411 configuration.addInstance("Application", ApplicationStackTraceElementAnalyzer.class); 2412 2413 } 2414 2415 2416 /** 2417 * Advises the {@link org.apache.tapestry5.services.messages.ComponentMessagesSource} service so 2418 * that the creation 2419 * of {@link org.apache.tapestry5.ioc.Messages} instances can be deferred. 2420 * 2421 * @since 5.1.0.0 2422 */ 2423 @Match("ComponentMessagesSource") 2424 public static void adviseLazy(LazyAdvisor advisor, MethodAdviceReceiver receiver) 2425 { 2426 advisor.addLazyMethodInvocationAdvice(receiver); 2427 } 2428 2429 /** 2430 * @since 5.1.0.0 2431 */ 2432 public ComponentRequestHandler buildComponentRequestHandler(List<ComponentRequestFilter> configuration, 2433 2434 @Autobuild 2435 ComponentRequestHandlerTerminator terminator, 2436 2437 Logger logger) 2438 { 2439 return pipelineBuilder.build(logger, ComponentRequestHandler.class, ComponentRequestFilter.class, 2440 configuration, terminator); 2441 } 2442 2443 /** 2444 * Contributes: 2445 * <dl> 2446 * <dt>OperationTracker</dt> 2447 * <dd>Tracks general information about the request using {@link OperationTracker}</dd> 2448 * <dt>UnknownComponentFilter (production mode only)</dt> 2449 * <dd>{@link org.apache.tapestry5.internal.services.ProductionModeUnknownComponentFilter} - Detects request with unknown component and aborts handling to ultimately deliver a 404 response</dd> 2450 * <dt>InitializeActivePageName 2451 * <dd>{@link InitializeActivePageName} 2452 * <dt>DeferredResponseRenderer</dt> 2453 * <dd>{@link DeferredResponseRenderer}</dd> 2454 * </dl> 2455 * 2456 * @since 5.2.0 2457 */ 2458 public void contributeComponentRequestHandler(OrderedConfiguration<ComponentRequestFilter> configuration, @Symbol(SymbolConstants.PRODUCTION_MODE) boolean productionMode) 2459 { 2460 configuration.addInstance("OperationTracker", RequestOperationTracker.class); 2461 2462 if (productionMode) 2463 { 2464 configuration.addInstance("UnknownComponentFilter", ProductionModeUnknownComponentFilter.class); 2465 } 2466 2467 configuration.addInstance("InitializeActivePageName", InitializeActivePageName.class); 2468 configuration.addInstance("DeferredResponseRenderer", DeferredResponseRenderer.class); 2469 } 2470 2471 /** 2472 * Decorate FieldValidatorDefaultSource to setup the EnvironmentMessages 2473 * object and place it in the environment. 2474 * Although this could have been implemented directly in the default 2475 * implementation of the service, doing it 2476 * as service decoration ensures that the environment will be properly setup 2477 * even if a user overrides the default 2478 * service implementation. 2479 * 2480 * @param defaultSource 2481 * The service to decorate 2482 * @param environment 2483 */ 2484 public static FieldValidatorDefaultSource decorateFieldValidatorDefaultSource( 2485 final FieldValidatorDefaultSource defaultSource, final Environment environment) 2486 { 2487 return new FieldValidatorDefaultSource() 2488 { 2489 2490 public FieldValidator createDefaultValidator(Field field, String overrideId, Messages overrideMessages, 2491 Locale locale, Class propertyType, AnnotationProvider propertyAnnotations) 2492 { 2493 environment.push(EnvironmentMessages.class, new EnvironmentMessages(overrideMessages, overrideId)); 2494 FieldValidator fieldValidator = defaultSource.createDefaultValidator(field, overrideId, 2495 overrideMessages, locale, propertyType, propertyAnnotations); 2496 environment.pop(EnvironmentMessages.class); 2497 return fieldValidator; 2498 } 2499 2500 public FieldValidator createDefaultValidator(ComponentResources resources, String parameterName) 2501 { 2502 2503 EnvironmentMessages em = new EnvironmentMessages(resources.getContainerMessages(), resources.getId()); 2504 environment.push(EnvironmentMessages.class, em); 2505 FieldValidator fieldValidator = defaultSource.createDefaultValidator(resources, parameterName); 2506 environment.pop(EnvironmentMessages.class); 2507 return fieldValidator; 2508 } 2509 }; 2510 } 2511 2512 /** 2513 * Exposes the Environmental {@link Heartbeat} as an injectable service. 2514 * 2515 * @since 5.2.0 2516 */ 2517 public Heartbeat buildHeartbeat() 2518 { 2519 return environmentalBuilder.build(Heartbeat.class); 2520 } 2521 2522 public static ComponentMessagesSource buildComponentMessagesSource(UpdateListenerHub updateListenerHub, @Autobuild 2523 ComponentMessagesSourceImpl service) 2524 { 2525 updateListenerHub.addUpdateListener(service); 2526 2527 return service; 2528 } 2529 2530 /** 2531 * Contributes extractors for {@link Meta}, {@link Secure}, {@link ContentType} and {@link WhitelistAccessOnly} annotations. 2532 * 2533 * @since 5.2.0 2534 */ 2535 @SuppressWarnings("unchecked") 2536 public static void contributeMetaWorker(MappedConfiguration<Class, MetaDataExtractor> configuration) 2537 { 2538 configuration.addInstance(Meta.class, MetaAnnotationExtractor.class); 2539 configuration.add(Secure.class, new FixedExtractor(MetaDataConstants.SECURE_PAGE)); 2540 configuration.addInstance(ContentType.class, ContentTypeExtractor.class); 2541 configuration.add(WhitelistAccessOnly.class, new FixedExtractor(MetaDataConstants.WHITELIST_ONLY_PAGE)); 2542 configuration.addInstance(UnknownActivationContextCheck.class, UnknownActivationContextExtractor.class); 2543 } 2544 2545 /** 2546 * Builds the {@link ComponentTemplateLocator} as a chain of command. 2547 * 2548 * @since 5.2.0 2549 */ 2550 @Marker(Primary.class) 2551 public ComponentTemplateLocator buildComponentTemplateLocator(List<ComponentTemplateLocator> configuration) 2552 { 2553 return chainBuilder.build(ComponentTemplateLocator.class, configuration); 2554 } 2555 2556 /** 2557 * Contributes two template locators: 2558 * <dl> 2559 * <dt>Default</dt> 2560 * <dd>Searches for the template on the classpath ({@link DefaultTemplateLocator}</dd> 2561 * <dt>Page</dt> 2562 * <dd>Searches for <em>page</em> templates in the context ({@link PageTemplateLocator})</dd> 2563 * </dl> 2564 * 2565 * @since 5.2.0 2566 */ 2567 public static void contributeComponentTemplateLocator(OrderedConfiguration<ComponentTemplateLocator> configuration, 2568 @ContextProvider 2569 AssetFactory contextAssetFactory, 2570 @Symbol(SymbolConstants.APPLICATION_FOLDER) String applicationFolder, 2571 ComponentClassResolver componentClassResolver) 2572 { 2573 configuration.add("Default", new DefaultTemplateLocator()); 2574 configuration 2575 .add("Page", new PageTemplateLocator(contextAssetFactory.getRootResource(), componentClassResolver, applicationFolder)); 2576 2577 } 2578 2579 /** 2580 * Builds {@link ComponentEventLinkTransformer} service as a chain of command. 2581 * 2582 * @since 5.2.0 2583 */ 2584 @Marker(Primary.class) 2585 public ComponentEventLinkTransformer buildComponentEventLinkTransformer( 2586 List<ComponentEventLinkTransformer> configuration) 2587 { 2588 return chainBuilder.build(ComponentEventLinkTransformer.class, configuration); 2589 } 2590 2591 /** 2592 * Builds {@link PageRenderLinkTransformer} service as a chain of command. 2593 * 2594 * @since 5.2.0 2595 */ 2596 @Marker(Primary.class) 2597 public PageRenderLinkTransformer buildPageRenderLinkTransformer(List<PageRenderLinkTransformer> configuration) 2598 { 2599 return chainBuilder.build(PageRenderLinkTransformer.class, configuration); 2600 } 2601 2602 /** 2603 * Provides the "LinkTransformer" interceptor for the {@link ComponentEventLinkEncoder} service. 2604 * Other decorations 2605 * should come after LinkTransformer. 2606 * 2607 * @since 5.2.0 2608 */ 2609 @Match("ComponentEventLinkEncoder") 2610 public ComponentEventLinkEncoder decorateLinkTransformer(LinkTransformer linkTransformer, 2611 ComponentEventLinkEncoder delegate) 2612 { 2613 return new LinkTransformerInterceptor(linkTransformer, delegate); 2614 } 2615 2616 /** 2617 * In production mode, override {@link UpdateListenerHub} to be an empty placeholder. 2618 */ 2619 @Contribute(ServiceOverride.class) 2620 public static void productionModeOverrides(MappedConfiguration<Class, Object> configuration, 2621 @Symbol(SymbolConstants.PRODUCTION_MODE) 2622 boolean productionMode) 2623 { 2624 if (productionMode) 2625 { 2626 configuration.add(UpdateListenerHub.class, new UpdateListenerHub() 2627 { 2628 public void fireCheckForUpdates() 2629 { 2630 } 2631 2632 public void addUpdateListener(UpdateListener listener) 2633 { 2634 2635 } 2636 }); 2637 } 2638 } 2639 2640 /** 2641 * Contributes a single default analyzer: 2642 * <dl> 2643 * <dt>LocalhostOnly</dt> 2644 * <dd>Identifies requests from localhost as on client whitelist</dd> 2645 * </dl> 2646 * 2647 * @since 5.3 2648 */ 2649 @Contribute(ClientWhitelist.class) 2650 public static void defaultWhitelist(OrderedConfiguration<WhitelistAnalyzer> configuration) 2651 { 2652 configuration.add("LocalhostOnly", new LocalhostOnly()); 2653 } 2654 2655 @Startup 2656 public static void registerToClearPlasticProxyFactoryOnInvalidation(@ComponentClasses InvalidationEventHub hub, @Builtin final PlasticProxyFactory proxyFactory) 2657 { 2658 hub.addInvalidationCallback(new Runnable() 2659 { 2660 public void run() 2661 { 2662 proxyFactory.clearCache(); 2663 } 2664 }); 2665 } 2666 2667 /** 2668 * @since 5.4 2669 */ 2670 @Contribute(ValueLabelProvider.class) 2671 public void defaultValueLabelProviders(MappedConfiguration<Class, ValueLabelProvider> configuration) 2672 { 2673 configuration.addInstance(Object.class, DefaultValueLabelProvider.class); 2674 configuration.addInstance(Enum.class, EnumValueLabelProvider.class); 2675 } 2676 2677 /** 2678 * @since 5.4 2679 */ 2680 public ValueLabelProvider<?> buildValueLabelProvider(Map<Class, ValueLabelProvider> configuration) 2681 { 2682 return strategyBuilder.build(ValueLabelProvider.class, configuration); 2683 } 2684 2685 @Advise(serviceInterface = ComponentInstantiatorSource.class) 2686 public static void componentReplacer(MethodAdviceReceiver methodAdviceReceiver, 2687 final ComponentOverride componentReplacer) throws NoSuchMethodException, SecurityException 2688 { 2689 2690 if (componentReplacer.hasReplacements()) 2691 { 2692 2693 MethodAdvice advice = new MethodAdvice() 2694 { 2695 @Override 2696 public void advise(MethodInvocation invocation) 2697 { 2698 String className = (String) invocation.getParameter(0); 2699 final Class<?> replacement = componentReplacer.getReplacement(className); 2700 if (replacement != null) 2701 { 2702 invocation.setParameter(0, replacement.getName()); 2703 } 2704 invocation.proceed(); 2705 } 2706 }; 2707 2708 methodAdviceReceiver.adviseMethod( 2709 ComponentInstantiatorSource.class.getMethod("getInstantiator", String.class), advice); 2710 2711 } 2712 } 2713 2714 public static ComponentLibraryInfoSource buildComponentLibraryInfoSource(List<ComponentLibraryInfoSource> configuration, 2715 ChainBuilder chainBuilder) 2716 { 2717 return chainBuilder.build(ComponentLibraryInfoSource.class, configuration); 2718 } 2719 2720 @Contribute(ComponentLibraryInfoSource.class) 2721 public static void addBuiltInComponentLibraryInfoSources(OrderedConfiguration<ComponentLibraryInfoSource> configuration) 2722 { 2723 configuration.addInstance("Maven", MavenComponentLibraryInfoSource.class); 2724 configuration.add("TapestryCore", new TapestryCoreComponentLibraryInfoSource()); 2725 } 2726 2727 private static final class TapestryCoreComponentLibraryInfoSource implements 2728 ComponentLibraryInfoSource 2729 { 2730 @Override 2731 public ComponentLibraryInfo find(LibraryMapping libraryMapping) 2732 { 2733 ComponentLibraryInfo info = null; 2734 if (libraryMapping.libraryName.equals("core")) 2735 { 2736 2737 info = new ComponentLibraryInfo(); 2738 2739 // the information above will probably not change in the future, or change very 2740 // infrequently, so I see no problem in hardwiring them here. 2741 info.setArtifactId("tapestry-core"); 2742 info.setGroupId("org.apache.tapestry"); 2743 info.setName("Tapestry 5 core component library"); 2744 info.setDescription("Components provided out-of-the-box by Tapestry"); 2745 info.setDocumentationUrl("http://tapestry.apache.org/component-reference.html"); 2746 info.setJavadocUrl("http://tapestry.apache.org/current/apidocs/"); 2747 info.setSourceBrowseUrl("https://git-wip-us.apache.org/repos/asf?p=tapestry-5.git;a=summary"); 2748 info.setSourceRootUrl("https://git-wip-us.apache.org/repos/asf?p=tapestry-5.git;a=blob;f=tapestry-core/src/main/java/"); 2749 info.setIssueTrackerUrl("https://issues.apache.org/jira/browse/TAP5"); 2750 info.setHomepageUrl("http://tapestry.apache.org"); 2751 info.setLibraryMapping(libraryMapping); 2752 2753 final InputStream inputStream = TapestryModule.class.getResourceAsStream( 2754 "/META-INF/gradle/org.apache.tapestry/tapestry-core/project.properties"); 2755 2756 if (inputStream != null) 2757 { 2758 Properties properties = new Properties(); 2759 try 2760 { 2761 properties.load(inputStream); 2762 } catch (IOException e) 2763 { 2764 throw new RuntimeException(e); 2765 } 2766 info.setVersion(properties.getProperty("version")); 2767 } 2768 } 2769 return info; 2770 } 2771 } 2772 2773}