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 * <dt>Operation</dt> <dd>Support for the {@link Operation} method annotation</dd> 511 * </dl> 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 * </dl> 773 */ 774 public void contributeHttpServletRequestHandler(OrderedConfiguration<HttpServletRequestFilter> configuration, 775 776 @Symbol(SymbolConstants.GZIP_COMPRESSION_ENABLED) 777 boolean gzipCompressionEnabled, 778 779 @Autobuild 780 GZipFilter gzipFilter, 781 782 @InjectService("IgnoredPathsFilter") 783 HttpServletRequestFilter ignoredPathsFilter) 784 { 785 configuration.add("IgnoredPaths", ignoredPathsFilter); 786 787 configuration.add("GZIP", gzipCompressionEnabled ? gzipFilter : null); 788 789 HttpServletRequestFilter storeIntoGlobals = new HttpServletRequestFilter() 790 { 791 public boolean service(HttpServletRequest request, HttpServletResponse response, 792 HttpServletRequestHandler handler) throws IOException 793 { 794 requestGlobals.storeServletRequestResponse(request, response); 795 796 return handler.service(request, response); 797 } 798 }; 799 800 configuration.add("StoreIntoGlobals", storeIntoGlobals, "before:*"); 801 } 802 803 /** 804 * Continues a number of filters into the RequestHandler service: 805 * <dl> 806 * <dt>StaticFiles</dt> 807 * <dd>Checks to see if the request is for an actual file, if so, returns true to let the servlet container process 808 * the request</dd> 809 * <dt>CheckForUpdates</dt> 810 * <dd>Periodically fires events that checks to see if the file system sources for any cached data has changed (see 811 * {@link org.apache.tapestry5.internal.services.CheckForUpdatesFilter}). Starting in 5.3, this filter will be null 812 * in production mode (it will only be active in development mode). 813 * <dt>ErrorFilter</dt> 814 * <dd>Catches request errors and lets the {@link org.apache.tapestry5.services.RequestExceptionHandler} handle them 815 * </dd> 816 * <dt>StoreIntoGlobals</dt> 817 * <dd>Stores the request and response into the {@link org.apache.tapestry5.services.RequestGlobals} service (this 818 * is repeated at the end of the pipeline, in case any filter substitutes the request or response). 819 * <dt>EndOfRequest</dt> 820 * <dd>Notifies internal services that the request has ended</dd> 821 * </dl> 822 */ 823 public void contributeRequestHandler(OrderedConfiguration<RequestFilter> configuration, Context context, 824 825 @Symbol(SymbolConstants.PRODUCTION_MODE) 826 boolean productionMode) 827 { 828 RequestFilter staticFilesFilter = new StaticFilesFilter(context); 829 830 RequestFilter storeIntoGlobals = new RequestFilter() 831 { 832 public boolean service(Request request, Response response, RequestHandler handler) throws IOException 833 { 834 requestGlobals.storeRequestResponse(request, response); 835 836 return handler.service(request, response); 837 } 838 }; 839 840 RequestFilter fireEndOfRequestEvent = new RequestFilter() 841 { 842 public boolean service(Request request, Response response, RequestHandler handler) throws IOException 843 { 844 try 845 { 846 return handler.service(request, response); 847 } finally 848 { 849 endOfRequestEventHub.fire(); 850 } 851 } 852 }; 853 854 if (productionMode) 855 { 856 configuration.add("CheckForUpdates", null, "before:*"); 857 } else 858 { 859 configuration.addInstance("CheckForUpdates", CheckForUpdatesFilter.class, "before:*"); 860 } 861 862 configuration.add("StaticFiles", staticFilesFilter); 863 864 configuration.add("StoreIntoGlobals", storeIntoGlobals); 865 866 configuration.add("EndOfRequest", fireEndOfRequestEvent); 867 868 configuration.addInstance("ErrorFilter", RequestErrorFilter.class); 869 } 870 871 /** 872 * Contributes the basic set of translators: 873 * <ul> 874 * <li>string</li> 875 * <li>byte</li> 876 * <li>short</li> 877 * <li>integer</li> 878 * <li>long</li> 879 * <li>float</li> 880 * <li>double</li> 881 * <li>BigInteger</li> 882 * <li>BigDecimal</li> 883 * </ul> 884 */ 885 public static void contributeTranslatorSource(MappedConfiguration<Class, Translator> configuration, 886 NumericTranslatorSupport support, Html5Support html5Support) 887 { 888 889 configuration.add(String.class, new StringTranslator()); 890 891 Class[] types = new Class[] 892 {Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, BigInteger.class, 893 BigDecimal.class}; 894 895 for (Class type : types) 896 { 897 String name = type.getSimpleName().toLowerCase(); 898 899 configuration.add(type, new NumericTranslator(name, type, support, html5Support)); 900 } 901 } 902 903 /** 904 * Adds coercions: 905 * <ul> 906 * <li>String to {@link SelectModel} 907 * <li>Map to {@link SelectModel} 908 * <li>Collection to {@link GridDataSource} 909 * <li>null to {@link GridDataSource} 910 * <li>List to {@link SelectModel} 911 * <li>{@link ComponentResourcesAware} (typically, a component) to {@link ComponentResources} 912 * <li>{@link ComponentResources} to {@link PropertyOverrides} 913 * <li>String to {@link Renderable} 914 * <li>{@link Renderable} to {@link Block} 915 * <li>String to {@link DateFormat} 916 * <li>String to {@link Resource} (via {@link AssetSource#resourceForPath(String)}) 917 * <li>{@link Renderable} to {@link RenderCommand}</li> 918 * <li>String to {@link Pattern}</li> 919 * <li>String to {@link DateFormat}</li> 920 * <li>{@link Resource} to {@link DynamicTemplate}</li> 921 * <li>{@link Asset} to {@link Resource}</li> 922 * <li>{@link ValueEncoder} to {@link ValueEncoderFactory}</li> 923 * </ul> 924 */ 925 public static void contributeTypeCoercer(Configuration<CoercionTuple> configuration, 926 927 final ObjectLocator objectLocator, 928 929 @Builtin 930 final ThreadLocale threadLocale, 931 932 @Core 933 final AssetSource assetSource, 934 935 @Core 936 final DynamicTemplateParser dynamicTemplateParser) 937 { 938 configuration.add(CoercionTuple.create(ComponentResources.class, PropertyOverrides.class, 939 new Coercion<ComponentResources, PropertyOverrides>() 940 { 941 public PropertyOverrides coerce(ComponentResources input) 942 { 943 return new PropertyOverridesImpl(input); 944 } 945 })); 946 947 948 // See TAP5-2184 for why this causes some trouble! 949 configuration.add(CoercionTuple.create(String.class, SelectModel.class, new Coercion<String, SelectModel>() 950 { 951 public SelectModel coerce(String input) 952 { 953 return TapestryInternalUtils.toSelectModel(input); 954 } 955 })); 956 957 configuration.add(CoercionTuple.create(Map.class, SelectModel.class, new Coercion<Map, SelectModel>() 958 { 959 @SuppressWarnings("unchecked") 960 public SelectModel coerce(Map input) 961 { 962 return TapestryInternalUtils.toSelectModel(input); 963 } 964 })); 965 966 configuration.add(CoercionTuple.create(Collection.class, GridDataSource.class, 967 new Coercion<Collection, GridDataSource>() 968 { 969 public GridDataSource coerce(Collection input) 970 { 971 return new CollectionGridDataSource(input); 972 } 973 })); 974 975 configuration.add(CoercionTuple.create(void.class, GridDataSource.class, new Coercion<Void, GridDataSource>() 976 { 977 private final GridDataSource source = new NullDataSource(); 978 979 public GridDataSource coerce(Void input) 980 { 981 return source; 982 } 983 })); 984 985 configuration.add(CoercionTuple.create(List.class, SelectModel.class, new Coercion<List, SelectModel>() 986 { 987 private SelectModelFactory selectModelFactory; 988 989 @SuppressWarnings("unchecked") 990 public SelectModel coerce(List input) 991 { 992 // This doesn't look thread safe, but it is because its a one-time transition from null 993 // to another value, and a race condition is harmless. 994 if (selectModelFactory == null) 995 { 996 selectModelFactory = objectLocator.getService(SelectModelFactory.class); 997 } 998 999 return selectModelFactory.create(input); 1000 } 1001 })); 1002 1003 configuration.add(CoercionTuple.create(String.class, Pattern.class, new Coercion<String, Pattern>() 1004 { 1005 public Pattern coerce(String input) 1006 { 1007 return Pattern.compile(input); 1008 } 1009 })); 1010 1011 configuration.add(CoercionTuple.create(ComponentResourcesAware.class, ComponentResources.class, 1012 new Coercion<ComponentResourcesAware, ComponentResources>() 1013 { 1014 1015 public ComponentResources coerce(ComponentResourcesAware input) 1016 { 1017 return input.getComponentResources(); 1018 } 1019 })); 1020 1021 configuration.add(CoercionTuple.create(String.class, Renderable.class, new Coercion<String, Renderable>() 1022 { 1023 public Renderable coerce(String input) 1024 { 1025 return new StringRenderable(input); 1026 } 1027 })); 1028 1029 configuration.add(CoercionTuple.create(Renderable.class, Block.class, new Coercion<Renderable, Block>() 1030 { 1031 public Block coerce(Renderable input) 1032 { 1033 return new RenderableAsBlock(input); 1034 } 1035 })); 1036 1037 configuration.add(CoercionTuple.create(String.class, DateFormat.class, new Coercion<String, DateFormat>() 1038 { 1039 public DateFormat coerce(String input) 1040 { 1041 final SimpleDateFormat dateFormat = new SimpleDateFormat(input, threadLocale.getLocale()); 1042 final String lenient = objectLocator.getService(SymbolSource.class).valueForSymbol(SymbolConstants.LENIENT_DATE_FORMAT); 1043 dateFormat.setLenient(Boolean.parseBoolean(lenient)); 1044 return dateFormat; 1045 } 1046 })); 1047 1048 configuration.add(CoercionTuple.create(String.class, Resource.class, new Coercion<String, Resource>() 1049 { 1050 public Resource coerce(String input) 1051 { 1052 return assetSource.resourceForPath(input); 1053 } 1054 })); 1055 1056 configuration.add(CoercionTuple.create(Renderable.class, RenderCommand.class, 1057 new Coercion<Renderable, RenderCommand>() 1058 { 1059 public RenderCommand coerce(final Renderable input) 1060 { 1061 return new RenderCommand() 1062 { 1063 public void render(MarkupWriter writer, RenderQueue queue) 1064 { 1065 input.render(writer); 1066 } 1067 }; 1068 } 1069 })); 1070 1071 configuration.add(CoercionTuple.create(Date.class, Calendar.class, new Coercion<Date, Calendar>() 1072 { 1073 public Calendar coerce(Date input) 1074 { 1075 Calendar calendar = Calendar.getInstance(threadLocale.getLocale()); 1076 calendar.setTime(input); 1077 return calendar; 1078 } 1079 })); 1080 1081 configuration.add(CoercionTuple.create(Resource.class, DynamicTemplate.class, 1082 new Coercion<Resource, DynamicTemplate>() 1083 { 1084 public DynamicTemplate coerce(Resource input) 1085 { 1086 return dynamicTemplateParser.parseTemplate(input); 1087 } 1088 })); 1089 1090 configuration.add(CoercionTuple.create(Asset.class, Resource.class, new Coercion<Asset, Resource>() 1091 { 1092 public Resource coerce(Asset input) 1093 { 1094 return input.getResource(); 1095 } 1096 })); 1097 1098 configuration.add(CoercionTuple.create(ValueEncoder.class, ValueEncoderFactory.class, new Coercion<ValueEncoder, ValueEncoderFactory>() 1099 { 1100 public ValueEncoderFactory coerce(ValueEncoder input) 1101 { 1102 return new GenericValueEncoderFactory(input); 1103 } 1104 })); 1105 } 1106 1107 /** 1108 * Adds built-in constraint generators: 1109 * <ul> 1110 * <li>PrimtiveField -- primitive fields are always required 1111 * <li>ValidateAnnotation -- adds constraints from a {@link Validate} annotation 1112 * </ul> 1113 */ 1114 public static void contributeValidationConstraintGenerator( 1115 OrderedConfiguration<ValidationConstraintGenerator> configuration) 1116 { 1117 configuration.add("PrimitiveField", new PrimitiveFieldConstraintGenerator()); 1118 configuration.add("ValidateAnnotation", new ValidateAnnotationConstraintGenerator()); 1119 configuration.addInstance("Messages", MessagesConstraintGenerator.class); 1120 } 1121 1122 private static void add(OrderedConfiguration<ComponentClassTransformWorker2> configuration, 1123 Class<? extends Annotation> annotationClass, MethodDescription description) 1124 { 1125 String name = TapestryInternalUtils.lastTerm(annotationClass.getName()); 1126 1127 ComponentClassTransformWorker2 worker = new PageLifecycleAnnotationWorker(annotationClass, 1128 description, name); 1129 1130 configuration.add(name, worker); 1131 } 1132 1133 // ======================================================================== 1134 // 1135 // Service Builder Methods (instance) 1136 // 1137 // ======================================================================== 1138 1139 public Context buildContext(ApplicationGlobals globals) 1140 { 1141 return shadowBuilder.build(globals, "context", Context.class); 1142 } 1143 1144 public static ComponentClassResolver buildComponentClassResolver(@Autobuild 1145 ComponentClassResolverImpl service, @ComponentClasses 1146 InvalidationEventHub hub) 1147 { 1148 // Allow the resolver to clean its cache when the component classes 1149 // change 1150 1151 hub.addInvalidationListener(service); 1152 1153 return service; 1154 } 1155 1156 1157 /** 1158 * Builds the PropBindingFactory as a chain of command. The terminator of 1159 * the chain is responsible for ordinary 1160 * property names (and property paths). 1161 * 1162 * This mechanism has been replaced in 5.1 with a more sophisticated parser based on ANTLR. See <a 1163 * href="https://issues.apache.org/jira/browse/TAP5-79">TAP5-79</a> for details. There are no longer any built-in 1164 * contributions to the configuration. 1165 * 1166 * @param configuration 1167 * contributions of special factories for some constants, each 1168 * contributed factory may return a 1169 * binding if applicable, or null otherwise 1170 */ 1171 public BindingFactory buildPropBindingFactory(List<BindingFactory> configuration, @Autobuild 1172 PropBindingFactory service) 1173 { 1174 configuration.add(service); 1175 1176 return chainBuilder.build(BindingFactory.class, configuration); 1177 } 1178 1179 public PersistentFieldStrategy buildClientPersistentFieldStrategy(LinkCreationHub linkCreationHub, @Autobuild 1180 ClientPersistentFieldStrategy service) 1181 { 1182 linkCreationHub.addListener(service); 1183 1184 return service; 1185 } 1186 1187 /** 1188 * Builds a proxy to the current {@link org.apache.tapestry5.services.ClientBehaviorSupport} inside this 1189 * thread's {@link org.apache.tapestry5.services.Environment}. 1190 * 1191 * @since 5.1.0.1 1192 */ 1193 1194 public ClientBehaviorSupport buildClientBehaviorSupport() 1195 { 1196 return environmentalBuilder.build(ClientBehaviorSupport.class); 1197 } 1198 1199 /** 1200 * Builds a proxy to the current {@link org.apache.tapestry5.services.FormSupport} inside this 1201 * thread's {@link org.apache.tapestry5.services.Environment}. 1202 */ 1203 public FormSupport buildFormSupport() 1204 { 1205 return environmentalBuilder.build(FormSupport.class); 1206 } 1207 1208 /** 1209 * Allows the exact steps in the component class transformation process to 1210 * be defined. 1211 */ 1212 @Marker(Primary.class) 1213 public ComponentClassTransformWorker2 buildComponentClassTransformWorker( 1214 List<ComponentClassTransformWorker2> configuration) 1215 1216 { 1217 return chainBuilder.build(ComponentClassTransformWorker2.class, configuration); 1218 } 1219 1220 /** 1221 * Analyzes properties to determine the data types, used to 1222 * {@linkplain #provideDefaultBeanBlocks(org.apache.tapestry5.ioc.Configuration)} locale 1223 * display and edit blocks for properties. The default behaviors 1224 * look for a {@link org.apache.tapestry5.beaneditor.DataType} annotation 1225 * before deriving the data type from the property type. 1226 */ 1227 @Marker(Primary.class) 1228 public DataTypeAnalyzer buildDataTypeAnalyzer(List<DataTypeAnalyzer> configuration) 1229 { 1230 return chainBuilder.build(DataTypeAnalyzer.class, configuration); 1231 } 1232 1233 /** 1234 * A chain of command for providing values for {@link Inject}-ed fields in 1235 * component classes. The service's 1236 * configuration can be extended to allow for different automatic injections 1237 * (based on some combination of field 1238 * type and field name). 1239 */ 1240 public InjectionProvider2 buildInjectionProvider(List<InjectionProvider2> configuration) 1241 { 1242 return chainBuilder.build(InjectionProvider2.class, configuration); 1243 } 1244 1245 /** 1246 * Initializes the application, using a pipeline of {@link org.apache.tapestry5.services.ApplicationInitializer}s. 1247 */ 1248 @Marker(Primary.class) 1249 public ApplicationInitializer buildApplicationInitializer(Logger logger, 1250 List<ApplicationInitializerFilter> configuration) 1251 { 1252 ApplicationInitializer terminator = new ApplicationInitializerTerminator(); 1253 1254 return pipelineBuilder.build(logger, ApplicationInitializer.class, ApplicationInitializerFilter.class, 1255 configuration, terminator); 1256 } 1257 1258 public HttpServletRequestHandler buildHttpServletRequestHandler(Logger logger, 1259 1260 List<HttpServletRequestFilter> configuration, 1261 1262 @Primary 1263 RequestHandler handler, 1264 1265 @Symbol(SymbolConstants.CHARSET) 1266 String applicationCharset, 1267 1268 TapestrySessionFactory sessionFactory) 1269 { 1270 HttpServletRequestHandler terminator = new HttpServletRequestHandlerTerminator(handler, applicationCharset, 1271 sessionFactory); 1272 1273 return pipelineBuilder.build(logger, HttpServletRequestHandler.class, HttpServletRequestFilter.class, 1274 configuration, terminator); 1275 } 1276 1277 @Marker(Primary.class) 1278 public RequestHandler buildRequestHandler(Logger logger, List<RequestFilter> configuration, 1279 1280 @Primary 1281 Dispatcher masterDispatcher) 1282 { 1283 RequestHandler terminator = new RequestHandlerTerminator(masterDispatcher); 1284 1285 return pipelineBuilder.build(logger, RequestHandler.class, RequestFilter.class, configuration, terminator); 1286 } 1287 1288 public ServletApplicationInitializer buildServletApplicationInitializer(Logger logger, 1289 List<ServletApplicationInitializerFilter> configuration, 1290 1291 @Primary 1292 ApplicationInitializer initializer) 1293 { 1294 ServletApplicationInitializer terminator = new ServletApplicationInitializerTerminator(initializer); 1295 1296 return pipelineBuilder.build(logger, ServletApplicationInitializer.class, 1297 ServletApplicationInitializerFilter.class, configuration, terminator); 1298 } 1299 1300 /** 1301 * The component event result processor used for normal component requests. 1302 */ 1303 @Marker( 1304 {Primary.class, Traditional.class}) 1305 public ComponentEventResultProcessor buildComponentEventResultProcessor( 1306 Map<Class, ComponentEventResultProcessor> configuration, @ComponentClasses 1307 InvalidationEventHub hub) 1308 { 1309 return constructComponentEventResultProcessor(configuration, hub); 1310 } 1311 1312 /** 1313 * The component event result processor used for Ajax-oriented component 1314 * requests. 1315 */ 1316 @Marker(Ajax.class) 1317 public ComponentEventResultProcessor buildAjaxComponentEventResultProcessor( 1318 Map<Class, ComponentEventResultProcessor> configuration, @ComponentClasses 1319 InvalidationEventHub hub) 1320 { 1321 return constructComponentEventResultProcessor(configuration, hub); 1322 } 1323 1324 private ComponentEventResultProcessor constructComponentEventResultProcessor( 1325 Map<Class, ComponentEventResultProcessor> configuration, InvalidationEventHub hub) 1326 { 1327 Set<Class> handledTypes = CollectionFactory.newSet(configuration.keySet()); 1328 1329 // A slight hack! 1330 1331 configuration.put(Object.class, new ObjectComponentEventResultProcessor(handledTypes)); 1332 1333 final StrategyRegistry<ComponentEventResultProcessor> registry = StrategyRegistry.newInstance( 1334 ComponentEventResultProcessor.class, configuration); 1335 1336 //As the registry will cache component classes, we need to clear the cache when we reload components to avoid memory leaks in permgen 1337 hub.addInvalidationCallback(new Runnable() 1338 { 1339 public void run() 1340 { 1341 registry.clearCache(); 1342 } 1343 }); 1344 1345 return strategyBuilder.build(registry); 1346 } 1347 1348 /** 1349 * The default data type analyzer is the final analyzer consulted and 1350 * identifies the type entirely pased on the 1351 * property type, working against its own configuration (mapping property 1352 * type class to data type). 1353 */ 1354 public static DataTypeAnalyzer buildDefaultDataTypeAnalyzer(@Autobuild 1355 DefaultDataTypeAnalyzer service, @ComponentClasses 1356 InvalidationEventHub hub) 1357 { 1358 hub.addInvalidationCallback(service); 1359 1360 return service; 1361 } 1362 1363 public static TranslatorSource buildTranslatorSource(Map<Class, Translator> configuration, 1364 TranslatorAlternatesSource alternatesSource, 1365 @ComponentClasses 1366 InvalidationEventHub hub) 1367 { 1368 TranslatorSourceImpl service = new TranslatorSourceImpl(configuration, 1369 alternatesSource.getTranslatorAlternates()); 1370 1371 hub.addInvalidationCallback(service); 1372 1373 return service; 1374 } 1375 1376 @Marker(Primary.class) 1377 public ObjectRenderer buildObjectRenderer(Map<Class, ObjectRenderer> configuration) 1378 { 1379 return strategyBuilder.build(ObjectRenderer.class, configuration); 1380 } 1381 1382 /** 1383 * Returns a {@link PlasticProxyFactory} that can be used to create extra classes around component classes. This 1384 * factory will be cleared whenever an underlying component class is discovered to have changed. Use of this 1385 * factory implies that your code will become aware of this (if necessary) to discard any cached object (alas, 1386 * this currently involves dipping into the internals side to register for the correct notifications). Failure to 1387 * properly clean up can result in really nasty PermGen space memory leaks. 1388 */ 1389 @Marker(ComponentLayer.class) 1390 public PlasticProxyFactory buildComponentProxyFactory(ComponentInstantiatorSource source) 1391 { 1392 return shadowBuilder.build(source, "proxyFactory", PlasticProxyFactory.class); 1393 } 1394 1395 /** 1396 * Ordered contributions to the MasterDispatcher service allow different URL 1397 * matching strategies to occur. 1398 */ 1399 @Marker(Primary.class) 1400 public Dispatcher buildMasterDispatcher(List<Dispatcher> configuration) 1401 { 1402 return chainBuilder.build(Dispatcher.class, configuration); 1403 } 1404 1405 /** 1406 * Builds a shadow of the RequestGlobals.request property. Note again that 1407 * the shadow can be an ordinary singleton, 1408 * even though RequestGlobals is perthread. 1409 */ 1410 public Request buildRequest() 1411 { 1412 return shadowBuilder.build(requestGlobals, "request", Request.class); 1413 } 1414 1415 /** 1416 * Builds a shadow of the RequestGlobals.HTTPServletRequest property. 1417 * Generally, you should inject the {@link Request} service instead, as 1418 * future version of Tapestry may operate beyond just the servlet API. 1419 */ 1420 public HttpServletRequest buildHttpServletRequest() 1421 { 1422 return shadowBuilder.build(requestGlobals, "HTTPServletRequest", HttpServletRequest.class); 1423 } 1424 1425 /** 1426 * @since 5.1.0.0 1427 */ 1428 public HttpServletResponse buildHttpServletResponse() 1429 { 1430 return shadowBuilder.build(requestGlobals, "HTTPServletResponse", HttpServletResponse.class); 1431 } 1432 1433 /** 1434 * Builds a shadow of the RequestGlobals.response property. Note again that 1435 * the shadow can be an ordinary singleton, 1436 * even though RequestGlobals is perthread. 1437 */ 1438 public Response buildResponse() 1439 { 1440 return shadowBuilder.build(requestGlobals, "response", Response.class); 1441 } 1442 1443 /** 1444 * The MarkupRenderer service is used to render a full page as markup. 1445 * Supports an ordered configuration of {@link org.apache.tapestry5.services.MarkupRendererFilter}s. 1446 */ 1447 public MarkupRenderer buildMarkupRenderer(Logger logger, @Autobuild 1448 MarkupRendererTerminator terminator, List<MarkupRendererFilter> configuration) 1449 { 1450 return pipelineBuilder.build(logger, MarkupRenderer.class, MarkupRendererFilter.class, configuration, 1451 terminator); 1452 } 1453 1454 /** 1455 * A wrapper around {@link org.apache.tapestry5.internal.services.PageRenderQueue} used for 1456 * partial page renders. 1457 * Supports an ordered configuration of {@link org.apache.tapestry5.services.PartialMarkupRendererFilter}s. 1458 */ 1459 public PartialMarkupRenderer buildPartialMarkupRenderer(Logger logger, 1460 List<PartialMarkupRendererFilter> configuration, @Autobuild 1461 PartialMarkupRendererTerminator terminator) 1462 { 1463 1464 return pipelineBuilder.build(logger, PartialMarkupRenderer.class, PartialMarkupRendererFilter.class, 1465 configuration, terminator); 1466 } 1467 1468 public PageRenderRequestHandler buildPageRenderRequestHandler(List<PageRenderRequestFilter> configuration, 1469 Logger logger, @Autobuild 1470 PageRenderRequestHandlerImpl terminator) 1471 { 1472 return pipelineBuilder.build(logger, PageRenderRequestHandler.class, PageRenderRequestFilter.class, 1473 configuration, terminator); 1474 } 1475 1476 /** 1477 * Builds the component action request handler for traditional (non-Ajax) 1478 * requests. These typically result in a 1479 * redirect to a Tapestry render URL. 1480 */ 1481 @Marker( 1482 {Traditional.class, Primary.class}) 1483 public ComponentEventRequestHandler buildComponentEventRequestHandler( 1484 List<ComponentEventRequestFilter> configuration, Logger logger, @Autobuild 1485 ComponentEventRequestHandlerImpl terminator) 1486 { 1487 return pipelineBuilder.build(logger, ComponentEventRequestHandler.class, ComponentEventRequestFilter.class, 1488 configuration, terminator); 1489 } 1490 1491 /** 1492 * Builds the action request handler for Ajax requests, based on a 1493 * {@linkplain org.apache.tapestry5.ioc.services.PipelineBuilder 1494 * pipeline} around {@link org.apache.tapestry5.internal.services.AjaxComponentEventRequestHandler} . Filters on 1495 * the 1496 * request handler are supported here as well. 1497 */ 1498 @Marker( 1499 {Ajax.class, Primary.class}) 1500 public ComponentEventRequestHandler buildAjaxComponentEventRequestHandler( 1501 List<ComponentEventRequestFilter> configuration, Logger logger, @Autobuild 1502 AjaxComponentEventRequestHandler terminator) 1503 { 1504 return pipelineBuilder.build(logger, ComponentEventRequestHandler.class, ComponentEventRequestFilter.class, 1505 configuration, terminator); 1506 } 1507 1508 // ======================================================================== 1509 // 1510 // Service Contribution Methods (instance) 1511 // 1512 // ======================================================================== 1513 1514 /** 1515 * Contributes the default "session" strategy. 1516 */ 1517 public void contributeApplicationStatePersistenceStrategySource( 1518 MappedConfiguration<String, ApplicationStatePersistenceStrategy> configuration, 1519 1520 @Local 1521 ApplicationStatePersistenceStrategy sessionStategy) 1522 { 1523 configuration.add("session", sessionStategy); 1524 } 1525 1526 /** 1527 * Contributes handlers for the following types: 1528 * <dl> 1529 * <dt>Object</dt> 1530 * <dd>Failure case, added to provide a more useful exception message</dd> 1531 * <dt>{@link Link}</dt> 1532 * <dd>Sends a redirect to the link (which is typically a page render link)</dd> 1533 * <dt>String</dt> 1534 * <dd>Sends a page render redirect</dd> 1535 * <dt>Class</dt> 1536 * <dd>Interpreted as the class name of a page, sends a page render render redirect (this is more refactoring safe 1537 * than the page name)</dd> 1538 * <dt>{@link Component}</dt> 1539 * <dd>A page's root component (though a non-root component will work, but will generate a warning). A direct to the 1540 * containing page is sent.</dd> 1541 * <dt>{@link org.apache.tapestry5.StreamResponse}</dt> 1542 * <dd>The stream response is sent as the actual reply.</dd> 1543 * <dt>URL</dt> 1544 * <dd>Sends a redirect to a (presumably) external URL</dd> 1545 * </dl> 1546 */ 1547 public void contributeComponentEventResultProcessor(@Traditional 1548 @ComponentInstanceProcessor 1549 ComponentEventResultProcessor componentInstanceProcessor, 1550 1551 MappedConfiguration<Class, ComponentEventResultProcessor> configuration) 1552 { 1553 configuration.add(Link.class, new ComponentEventResultProcessor<Link>() 1554 { 1555 public void processResultValue(Link value) throws IOException 1556 { 1557 response.sendRedirect(value); 1558 } 1559 }); 1560 1561 configuration.add(URL.class, new ComponentEventResultProcessor<URL>() 1562 { 1563 public void processResultValue(URL value) throws IOException 1564 { 1565 response.sendRedirect(value.toExternalForm()); 1566 } 1567 }); 1568 1569 configuration.addInstance(HttpError.class, HttpErrorComponentEventResultProcessor.class); 1570 1571 configuration.addInstance(String.class, PageNameComponentEventResultProcessor.class); 1572 1573 configuration.addInstance(Class.class, ClassResultProcessor.class); 1574 1575 configuration.add(Component.class, componentInstanceProcessor); 1576 1577 configuration.addInstance(StreamResponse.class, StreamResponseResultProcessor.class); 1578 1579 configuration.addInstance(StreamPageContent.class, StreamPageContentResultProcessor.class); 1580 } 1581 1582 /** 1583 * Contributes handlers for the following types: 1584 * <dl> 1585 * <dt>Object</dt> 1586 * <dd>Failure case, added to provide more useful exception message</dd> 1587 * <dt>{@link RenderCommand}</dt> 1588 * <dd>Typically, a {@link org.apache.tapestry5.Block}</dd> 1589 * <dt>{@link org.apache.tapestry5.annotations.Component}</dt> 1590 * <dd>Renders the component and its body (unless its a page, in which case a redirect JSON response is sent)</dd> 1591 * <dt>{@link org.apache.tapestry5.json.JSONObject} or {@link org.apache.tapestry5.json.JSONArray}</dt> 1592 * <dd>The JSONObject is returned as a text/javascript response</dd> 1593 * <dt>{@link org.apache.tapestry5.StreamResponse}</dt> 1594 * <dd>The stream response is sent as the actual response</dd> 1595 * <dt>String</dt> 1596 * <dd>Interprets the value as a logical page name and sends a client response to redirect to that page</dd> 1597 * <dt>{@link org.apache.tapestry5.Link}</dt> 1598 * <dd>Sends a JSON response to redirect to the link</dd> 1599 * <dt>{@link Class}</dt> 1600 * <dd>Treats the class as a page class and sends a redirect for a page render for that page</dd> 1601 * <dt>{@link org.apache.tapestry5.ajax.MultiZoneUpdate}</dt> 1602 * <dd>Sends a single JSON response to update the content of multiple zones 1603 * </dl> 1604 * 1605 * In most cases, when you want to support a new type, you should convert it to one of the built-in supported types 1606 * (such as {@link RenderCommand}. You can then inject the master AjaxComponentEventResultProcessor (use the 1607 * {@link Ajax} marker annotation) and delegate to it. 1608 */ 1609 @Contribute(ComponentEventResultProcessor.class) 1610 @Ajax 1611 public static void provideBaseAjaxComponentEventResultProcessors( 1612 MappedConfiguration<Class, ComponentEventResultProcessor> configuration) 1613 { 1614 configuration.addInstance(RenderCommand.class, RenderCommandComponentEventResultProcessor.class); 1615 configuration.addInstance(Component.class, AjaxComponentInstanceEventResultProcessor.class); 1616 configuration.addInstance(JSONObject.class, JSONObjectEventResultProcessor.class); 1617 configuration.addInstance(JSONArray.class, JSONArrayEventResultProcessor.class); 1618 configuration.addInstance(StreamResponse.class, StreamResponseResultProcessor.class); 1619 configuration.addInstance(String.class, AjaxPageNameComponentEventResultProcessor.class); 1620 configuration.addInstance(Link.class, AjaxLinkComponentEventResultProcessor.class); 1621 configuration.addInstance(URL.class, AjaxURLComponentEventResultProcessor.class); 1622 configuration.addInstance(Class.class, AjaxPageClassComponentEventResultProcessor.class); 1623 configuration.addInstance(MultiZoneUpdate.class, MultiZoneUpdateEventResultProcessor.class); 1624 configuration.addInstance(HttpError.class, HttpErrorComponentEventResultProcessor.class); 1625 } 1626 1627 /** 1628 * The MasterDispatcher is a chain-of-command of individual Dispatchers, 1629 * each handling (like a servlet) a particular 1630 * kind of incoming request. 1631 * <dl> 1632 * <dt>RootPath</dt> 1633 * <dd>Renders the start page for the "/" request (outdated)</dd> 1634 * <dt>PageRender</dt> 1635 * <dd>Identifies the {@link org.apache.tapestry5.services.PageRenderRequestParameters} and forwards onto 1636 * {@link PageRenderRequestHandler}</dd> 1637 * <dt>ComponentEvent</dt> 1638 * <dd>Identifies the {@link ComponentEventRequestParameters} and forwards onto the 1639 * {@link ComponentEventRequestHandler}</dd> 1640 * </dl> 1641 */ 1642 public static void contributeMasterDispatcher(OrderedConfiguration<Dispatcher> configuration) 1643 { 1644 // Looks for the root path and renders the start page. This is 1645 // maintained for compatibility 1646 // with earlier versions of Tapestry 5, it is recommended that an Index 1647 // page be used instead. 1648 1649 configuration.addInstance("RootPath", RootPathDispatcher.class, "before:Asset"); 1650 1651 configuration.addInstance("ComponentEvent", ComponentEventDispatcher.class, "before:PageRender"); 1652 1653 configuration.addInstance("PageRender", PageRenderDispatcher.class); 1654 } 1655 1656 /** 1657 * Contributes a default object renderer for type Object, plus specialized 1658 * renderers for {@link org.apache.tapestry5.services.Request}, {@link org.apache.tapestry5.ioc.Location}, 1659 * {@link org.apache.tapestry5.ComponentResources}, {@link org.apache.tapestry5.EventContext}, 1660 * {@link AvailableValues}, 1661 * List, and Object[]. 1662 */ 1663 @SuppressWarnings("unchecked") 1664 public void contributeObjectRenderer(MappedConfiguration<Class, ObjectRenderer> configuration, 1665 1666 @InjectService("LocationRenderer") 1667 ObjectRenderer locationRenderer, 1668 1669 final TypeCoercer typeCoercer) 1670 { 1671 configuration.add(Object.class, new DefaultObjectRenderer()); 1672 1673 configuration.addInstance(Request.class, RequestRenderer.class); 1674 1675 configuration.add(Location.class, locationRenderer); 1676 1677 ObjectRenderer preformatted = new ObjectRenderer<Object>() 1678 { 1679 public void render(Object object, MarkupWriter writer) 1680 { 1681 writer.element("pre"); 1682 writer.write(typeCoercer.coerce(object, String.class)); 1683 writer.end(); 1684 } 1685 }; 1686 1687 configuration.addInstance(List.class, ListRenderer.class); 1688 configuration.addInstance(Object[].class, ObjectArrayRenderer.class); 1689 configuration.addInstance(ComponentResources.class, ComponentResourcesRenderer.class); 1690 configuration.addInstance(EventContext.class, EventContextRenderer.class); 1691 configuration.add(AvailableValues.class, new AvailableValuesRenderer()); 1692 } 1693 1694 /** 1695 * Adds page render filters, each of which provides an {@link org.apache.tapestry5.annotations.Environmental} 1696 * service. Filters 1697 * often provide {@link org.apache.tapestry5.annotations.Environmental} services needed by 1698 * components as they render. 1699 * <dl> 1700 * <dt>DocumentLinker</dt> 1701 * <dd>Provides {@link org.apache.tapestry5.internal.services.DocumentLinker}</dd> 1702 * <dt>ClientBehaviorSupport (deprecated in 5.4)</dt> 1703 * <dd>Provides {@link ClientBehaviorSupport}</dd> 1704 * <dt>Heartbeat</dt> 1705 * <dd>Provides {@link org.apache.tapestry5.services.Heartbeat}</dd> 1706 * <dt>ValidationDecorator (deprecated in 5.4)</dt> 1707 * <dd>Provides {@link org.apache.tapestry5.ValidationDecorator} (via {@link ValidationDecoratorFactory#newInstance(org.apache.tapestry5.MarkupWriter)})</dd> 1708 * <dt>PageNameMeta (since 5.4)</dt> 1709 * <dd>Renders a {@code <meta/>} tag describing the active page name (development mode only)</dd> 1710 * <dt>ImportCoreStack (since 5.4) </dt> 1711 * <dd>Imports the "core" stack (necessary to get the Bootstrap CSS, if nothing else).</dd> 1712 * </dl> 1713 * 1714 * @see org.apache.tapestry5.SymbolConstants#OMIT_GENERATOR_META 1715 * @see org.apache.tapestry5.SymbolConstants#PRODUCTION_MODE 1716 * @see org.apache.tapestry5.SymbolConstants#INCLUDE_CORE_STACK 1717 * @see org.apache.tapestry5.SymbolConstants#ENABLE_PAGELOADING_MASK 1718 */ 1719 public void contributeMarkupRenderer(OrderedConfiguration<MarkupRendererFilter> configuration, 1720 1721 final ModuleManager moduleManager, 1722 1723 @Symbol(SymbolConstants.OMIT_GENERATOR_META) 1724 final boolean omitGeneratorMeta, 1725 1726 @Symbol(SymbolConstants.TAPESTRY_VERSION) 1727 final String tapestryVersion, 1728 1729 @Symbol(SymbolConstants.PRODUCTION_MODE) 1730 boolean productionMode, 1731 1732 @Symbol(SymbolConstants.INCLUDE_CORE_STACK) 1733 final boolean includeCoreStack, 1734 1735 @Symbol(SymbolConstants.ENABLE_PAGELOADING_MASK) 1736 final boolean enablePageloadingMask, 1737 1738 final ValidationDecoratorFactory validationDecoratorFactory) 1739 { 1740 MarkupRendererFilter documentLinker = new MarkupRendererFilter() 1741 { 1742 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer) 1743 { 1744 DocumentLinkerImpl linker = new DocumentLinkerImpl(moduleManager, omitGeneratorMeta, enablePageloadingMask, tapestryVersion); 1745 1746 environment.push(DocumentLinker.class, linker); 1747 1748 renderer.renderMarkup(writer); 1749 1750 environment.pop(DocumentLinker.class); 1751 1752 linker.updateDocument(writer.getDocument()); 1753 } 1754 }; 1755 1756 1757 MarkupRendererFilter clientBehaviorSupport = new MarkupRendererFilter() 1758 { 1759 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer) 1760 { 1761 ClientBehaviorSupportImpl clientBehaviorSupport = new ClientBehaviorSupportImpl(); 1762 1763 environment.push(ClientBehaviorSupport.class, clientBehaviorSupport); 1764 1765 renderer.renderMarkup(writer); 1766 1767 environment.pop(ClientBehaviorSupport.class); 1768 } 1769 }; 1770 1771 MarkupRendererFilter heartbeat = new MarkupRendererFilter() 1772 { 1773 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer) 1774 { 1775 Heartbeat heartbeat = new HeartbeatImpl(); 1776 1777 heartbeat.begin(); 1778 1779 environment.push(Heartbeat.class, heartbeat); 1780 1781 renderer.renderMarkup(writer); 1782 1783 environment.pop(Heartbeat.class); 1784 1785 heartbeat.end(); 1786 } 1787 }; 1788 1789 MarkupRendererFilter defaultValidationDecorator = new MarkupRendererFilter() 1790 { 1791 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer) 1792 { 1793 ValidationDecorator decorator = validationDecoratorFactory.newInstance(writer); 1794 1795 environment.push(ValidationDecorator.class, decorator); 1796 1797 renderer.renderMarkup(writer); 1798 1799 environment.pop(ValidationDecorator.class); 1800 } 1801 }; 1802 1803 MarkupRendererFilter importCoreStack = new MarkupRendererFilter() 1804 { 1805 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer) 1806 { 1807 renderer.renderMarkup(writer); 1808 1809 environment.peekRequired(JavaScriptSupport.class).importStack(InternalConstants.CORE_STACK_NAME); 1810 } 1811 }; 1812 1813 configuration.add("DocumentLinker", documentLinker); 1814 configuration.add("ClientBehaviorSupport", clientBehaviorSupport, "after:JavaScriptSupport"); 1815 configuration.add("Heartbeat", heartbeat); 1816 configuration.add("ValidationDecorator", defaultValidationDecorator); 1817 1818 if (includeCoreStack) 1819 { 1820 configuration.add("ImportCoreStack", importCoreStack); 1821 } 1822 1823 if (productionMode) 1824 { 1825 configuration.add("PageNameMeta", null); 1826 } else 1827 { 1828 configuration.addInstance("PageNameMeta", PageNameMetaInjector.class); 1829 } 1830 } 1831 1832 /** 1833 * Contributes {@link PartialMarkupRendererFilter}s used when rendering a 1834 * partial Ajax response. 1835 * <dl> 1836 * <dt>DocumentLinker 1837 * <dd>Provides {@link org.apache.tapestry5.internal.services.DocumentLinker} 1838 * <dt>ClientBehaviorSupport</dt> 1839 * <dd>Provides {@link ClientBehaviorSupport}</dd> 1840 * <dt>Heartbeat</dt> 1841 * <dd>Provides {@link org.apache.tapestry5.services.Heartbeat}</dd> 1842 * <dt>DefaultValidationDecorator</dt> 1843 * <dt>ValidationDecorator</dt> 1844 * <dd>Provides {@link org.apache.tapestry5.ValidationDecorator} (via {@link ValidationDecoratorFactory#newInstance(org.apache.tapestry5.MarkupWriter)})</dd> 1845 * </dl> 1846 */ 1847 public void contributePartialMarkupRenderer(OrderedConfiguration<PartialMarkupRendererFilter> configuration, 1848 1849 final ValidationDecoratorFactory validationDecoratorFactory) 1850 { 1851 PartialMarkupRendererFilter documentLinker = new PartialMarkupRendererFilter() 1852 { 1853 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer) 1854 { 1855 PartialMarkupDocumentLinker linker = new PartialMarkupDocumentLinker(); 1856 1857 environment.push(DocumentLinker.class, linker); 1858 1859 renderer.renderMarkup(writer, reply); 1860 1861 environment.pop(DocumentLinker.class); 1862 1863 linker.commit(reply); 1864 } 1865 }; 1866 1867 1868 PartialMarkupRendererFilter clientBehaviorSupport = new PartialMarkupRendererFilter() 1869 { 1870 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer) 1871 { 1872 ClientBehaviorSupportImpl support = new ClientBehaviorSupportImpl(); 1873 1874 environment.push(ClientBehaviorSupport.class, support); 1875 1876 renderer.renderMarkup(writer, reply); 1877 1878 environment.pop(ClientBehaviorSupport.class); 1879 } 1880 }; 1881 1882 PartialMarkupRendererFilter heartbeat = new PartialMarkupRendererFilter() 1883 { 1884 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer) 1885 { 1886 Heartbeat heartbeat = new HeartbeatImpl(); 1887 1888 heartbeat.begin(); 1889 1890 environment.push(Heartbeat.class, heartbeat); 1891 1892 renderer.renderMarkup(writer, reply); 1893 1894 environment.pop(Heartbeat.class); 1895 1896 heartbeat.end(); 1897 } 1898 }; 1899 1900 PartialMarkupRendererFilter defaultValidationDecorator = new PartialMarkupRendererFilter() 1901 { 1902 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer) 1903 { 1904 ValidationDecorator decorator = validationDecoratorFactory.newInstance(writer); 1905 1906 environment.push(ValidationDecorator.class, decorator); 1907 1908 renderer.renderMarkup(writer, reply); 1909 1910 environment.pop(ValidationDecorator.class); 1911 } 1912 }; 1913 1914 configuration.add("DocumentLinker", documentLinker); 1915 configuration.add("ClientBehaviorSupport", clientBehaviorSupport, "after:JavaScriptSupport"); 1916 configuration.add("Heartbeat", heartbeat); 1917 configuration.add("ValidationDecorator", defaultValidationDecorator); 1918 } 1919 1920 /** 1921 * Contributes several strategies: 1922 * <dl> 1923 * <dt>session 1924 * <dd>Values are stored in the {@link Session} 1925 * <dt>flash 1926 * <dd>Values are stored in the {@link Session}, until the next request (for the page) 1927 * <dt>client 1928 * <dd>Values are encoded into URLs (or hidden form fields) 1929 * </dl> 1930 */ 1931 public void contributePersistentFieldManager(MappedConfiguration<String, PersistentFieldStrategy> configuration, 1932 1933 Request request, 1934 1935 @InjectService("ClientPersistentFieldStrategy") 1936 PersistentFieldStrategy clientStrategy) 1937 { 1938 configuration.add(PersistenceConstants.SESSION, new SessionPersistentFieldStrategy(request)); 1939 configuration.add(PersistenceConstants.FLASH, new FlashPersistentFieldStrategy(request)); 1940 configuration.add(PersistenceConstants.CLIENT, clientStrategy); 1941 } 1942 1943 /** 1944 * Contributes {@link ValueEncoder}s or {@link ValueEncoderFactory}s for types: 1945 * <ul> 1946 * <li>Object 1947 * <li>String 1948 * <li>Enum 1949 * </ul> 1950 */ 1951 @SuppressWarnings("all") 1952 public static void contributeValueEncoderSource(MappedConfiguration<Class, Object> configuration) 1953 { 1954 configuration.addInstance(Object.class, TypeCoercedValueEncoderFactory.class); 1955 configuration.add(String.class, new StringValueEncoder()); 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 * 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 * 2259 * For elements input, select, textarea and label the hidden field is positioned after. 2260 * 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}