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.addInstance("checked", Checked.class); 698 configuration.addInstance("unchecked", Unchecked.class); 699 configuration.add("none", new None()); 700 } 701 702 /** 703 * <dl> 704 * <dt>Default</dt> 705 * <dd>based on {@link MasterObjectProvider}</dd> 706 * <dt>Named</dt> <dd>Handles fields with the {@link javax.inject.Named} annotation</dd> 707 * <dt>Block</dt> 708 * <dd>injects fields of type {@link Block}</dd> 709 * <dt>CommonResources</dt> 710 * <dd>Access to properties of resources (log, messages, etc.)</dd> 711 * <dt>Asset</dt> 712 * <dd>injection of assets (triggered via {@link Path} annotation), with the path relative to the component class</dd> 713 * <dt>Service</dt> 714 * <dd>Ordered last, for use when Inject is present and nothing else works, matches field type against Tapestry IoC 715 * services</dd> 716 * </dl> 717 */ 718 @Contribute(InjectionProvider2.class) 719 public static void provideStandardInjectionProviders(OrderedConfiguration<InjectionProvider2> configuration, SymbolSource symbolSource, 720 721 AssetSource assetSource) 722 { 723 configuration.addInstance("Named", InjectNamedProvider.class); 724 configuration.add("Block", new BlockInjectionProvider()); 725 configuration.add("Asset", new AssetInjectionProvider(assetSource)); 726 727 configuration.add("CommonResources", new CommonResourcesInjectionProvider()); 728 729 configuration.addInstance("Default", DefaultInjectionProvider.class); 730 731 // This needs to be the last one, since it matches against services 732 // and might blow up if there is no match. 733 configuration.addInstance("Service", ServiceInjectionProvider.class, "after:*"); 734 } 735 736 /** 737 * Contributes two object providers: 738 * <dl> 739 * <dt>Asset 740 * <dt> 741 * <dd>Checks for the {@link Path} annotation, and injects an {@link Asset}</dd> 742 * <dt>Service</dt> 743 * <dd>Injects based on the {@link Service} annotation, if present</dd> 744 * <dt>ApplicationMessages</dt> 745 * <dd>Injects the global application messages</dd> 746 * </dl> 747 */ 748 public static void contributeMasterObjectProvider(OrderedConfiguration<ObjectProvider> configuration, 749 750 @InjectService("AssetObjectProvider") 751 ObjectProvider assetObjectProvider, 752 753 ObjectLocator locator) 754 { 755 configuration.add("Asset", assetObjectProvider, "before:AnnotationBasedContributions"); 756 757 configuration.add("Service", new ServiceAnnotationObjectProvider(), "before:AnnotationBasedContributions"); 758 759 configuration.add("ApplicationMessages", new ApplicationMessageCatalogObjectProvider(locator), 760 "before:AnnotationBasedContributions"); 761 762 } 763 764 /** 765 * <dl> 766 * <dt>StoreIntoGlobals</dt> 767 * <dd>Stores the request and response into {@link org.apache.tapestry5.services.RequestGlobals} at the start of the 768 * pipeline</dd> 769 * <dt>IgnoredPaths</dt> 770 * <dd>Identifies requests that are known (via the IgnoredPathsFilter service's configuration) to be mapped to other 771 * applications</dd> 772 * <dt>GZip</dt> 773 * <dd>Handles GZIP compression of response streams (if supported by client)</dd> 774 * </dl> 775 */ 776 public void contributeHttpServletRequestHandler(OrderedConfiguration<HttpServletRequestFilter> configuration, 777 778 @Symbol(SymbolConstants.GZIP_COMPRESSION_ENABLED) 779 boolean gzipCompressionEnabled, 780 781 @Autobuild 782 GZipFilter gzipFilter, 783 784 @InjectService("IgnoredPathsFilter") 785 HttpServletRequestFilter ignoredPathsFilter) 786 { 787 configuration.add("IgnoredPaths", ignoredPathsFilter); 788 789 configuration.add("GZIP", gzipCompressionEnabled ? gzipFilter : null); 790 791 HttpServletRequestFilter storeIntoGlobals = new HttpServletRequestFilter() 792 { 793 public boolean service(HttpServletRequest request, HttpServletResponse response, 794 HttpServletRequestHandler handler) throws IOException 795 { 796 requestGlobals.storeServletRequestResponse(request, response); 797 798 return handler.service(request, response); 799 } 800 }; 801 802 configuration.add("StoreIntoGlobals", storeIntoGlobals, "before:*"); 803 } 804 805 /** 806 * Continues a number of filters into the RequestHandler service: 807 * <dl> 808 * <dt>StaticFiles</dt> 809 * <dd>Checks to see if the request is for an actual file, if so, returns true to let the servlet container process 810 * the request</dd> 811 * <dt>CheckForUpdates</dt> 812 * <dd>Periodically fires events that checks to see if the file system sources for any cached data has changed (see 813 * {@link org.apache.tapestry5.internal.services.CheckForUpdatesFilter}). Starting in 5.3, this filter will be null 814 * in production mode (it will only be active in development mode). 815 * <dt>ErrorFilter</dt> 816 * <dd>Catches request errors and lets the {@link org.apache.tapestry5.services.RequestExceptionHandler} handle them 817 * </dd> 818 * <dt>StoreIntoGlobals</dt> 819 * <dd>Stores the request and response into the {@link org.apache.tapestry5.services.RequestGlobals} service (this 820 * is repeated at the end of the pipeline, in case any filter substitutes the request or response). 821 * <dt>EndOfRequest</dt> 822 * <dd>Notifies internal services that the request has ended</dd> 823 * </dl> 824 */ 825 public void contributeRequestHandler(OrderedConfiguration<RequestFilter> configuration, Context context, 826 827 @Symbol(SymbolConstants.PRODUCTION_MODE) 828 boolean productionMode) 829 { 830 RequestFilter staticFilesFilter = new StaticFilesFilter(context); 831 832 RequestFilter storeIntoGlobals = new RequestFilter() 833 { 834 public boolean service(Request request, Response response, RequestHandler handler) throws IOException 835 { 836 requestGlobals.storeRequestResponse(request, response); 837 838 return handler.service(request, response); 839 } 840 }; 841 842 RequestFilter fireEndOfRequestEvent = new RequestFilter() 843 { 844 public boolean service(Request request, Response response, RequestHandler handler) throws IOException 845 { 846 try 847 { 848 return handler.service(request, response); 849 } finally 850 { 851 endOfRequestEventHub.fire(); 852 } 853 } 854 }; 855 856 if (productionMode) 857 { 858 configuration.add("CheckForUpdates", null, "before:*"); 859 } else 860 { 861 configuration.addInstance("CheckForUpdates", CheckForUpdatesFilter.class, "before:*"); 862 } 863 864 configuration.add("StaticFiles", staticFilesFilter); 865 866 configuration.add("StoreIntoGlobals", storeIntoGlobals); 867 868 configuration.add("EndOfRequest", fireEndOfRequestEvent); 869 870 configuration.addInstance("ErrorFilter", RequestErrorFilter.class); 871 } 872 873 /** 874 * Contributes the basic set of translators: 875 * <ul> 876 * <li>string</li> 877 * <li>byte</li> 878 * <li>short</li> 879 * <li>integer</li> 880 * <li>long</li> 881 * <li>float</li> 882 * <li>double</li> 883 * <li>BigInteger</li> 884 * <li>BigDecimal</li> 885 * </ul> 886 */ 887 public static void contributeTranslatorSource(MappedConfiguration<Class, Translator> configuration, 888 NumericTranslatorSupport support, Html5Support html5Support) 889 { 890 891 configuration.add(String.class, new StringTranslator()); 892 893 Class[] types = new Class[] 894 {Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, BigInteger.class, 895 BigDecimal.class}; 896 897 for (Class type : types) 898 { 899 String name = type.getSimpleName().toLowerCase(); 900 901 configuration.add(type, new NumericTranslator(name, type, support, html5Support)); 902 } 903 } 904 905 /** 906 * Adds coercions: 907 * <ul> 908 * <li>String to {@link SelectModel} 909 * <li>Map to {@link SelectModel} 910 * <li>Collection to {@link GridDataSource} 911 * <li>null to {@link GridDataSource} 912 * <li>List to {@link SelectModel} 913 * <li>{@link ComponentResourcesAware} (typically, a component) to {@link ComponentResources} 914 * <li>{@link ComponentResources} to {@link PropertyOverrides} 915 * <li>String to {@link Renderable} 916 * <li>{@link Renderable} to {@link Block} 917 * <li>String to {@link DateFormat} 918 * <li>String to {@link Resource} (via {@link AssetSource#resourceForPath(String)}) 919 * <li>{@link Renderable} to {@link RenderCommand}</li> 920 * <li>String to {@link Pattern}</li> 921 * <li>String to {@link DateFormat}</li> 922 * <li>{@link Resource} to {@link DynamicTemplate}</li> 923 * <li>{@link Asset} to {@link Resource}</li> 924 * <li>{@link ValueEncoder} to {@link ValueEncoderFactory}</li> 925 * </ul> 926 */ 927 public static void contributeTypeCoercer(Configuration<CoercionTuple> configuration, 928 929 final ObjectLocator objectLocator, 930 931 @Builtin 932 final ThreadLocale threadLocale, 933 934 @Core 935 final AssetSource assetSource, 936 937 @Core 938 final DynamicTemplateParser dynamicTemplateParser) 939 { 940 configuration.add(CoercionTuple.create(ComponentResources.class, PropertyOverrides.class, 941 new Coercion<ComponentResources, PropertyOverrides>() 942 { 943 public PropertyOverrides coerce(ComponentResources input) 944 { 945 return new PropertyOverridesImpl(input); 946 } 947 })); 948 949 950 // See TAP5-2184 for why this causes some trouble! 951 configuration.add(CoercionTuple.create(String.class, SelectModel.class, new Coercion<String, SelectModel>() 952 { 953 public SelectModel coerce(String input) 954 { 955 return TapestryInternalUtils.toSelectModel(input); 956 } 957 })); 958 959 configuration.add(CoercionTuple.create(Map.class, SelectModel.class, new Coercion<Map, SelectModel>() 960 { 961 @SuppressWarnings("unchecked") 962 public SelectModel coerce(Map input) 963 { 964 return TapestryInternalUtils.toSelectModel(input); 965 } 966 })); 967 968 configuration.add(CoercionTuple.create(Collection.class, GridDataSource.class, 969 new Coercion<Collection, GridDataSource>() 970 { 971 public GridDataSource coerce(Collection input) 972 { 973 return new CollectionGridDataSource(input); 974 } 975 })); 976 977 configuration.add(CoercionTuple.create(void.class, GridDataSource.class, new Coercion<Void, GridDataSource>() 978 { 979 private final GridDataSource source = new NullDataSource(); 980 981 public GridDataSource coerce(Void input) 982 { 983 return source; 984 } 985 })); 986 987 configuration.add(CoercionTuple.create(List.class, SelectModel.class, new Coercion<List, SelectModel>() 988 { 989 private SelectModelFactory selectModelFactory; 990 991 @SuppressWarnings("unchecked") 992 public SelectModel coerce(List input) 993 { 994 // This doesn't look thread safe, but it is because its a one-time transition from null 995 // to another value, and a race condition is harmless. 996 if (selectModelFactory == null) 997 { 998 selectModelFactory = objectLocator.getService(SelectModelFactory.class); 999 } 1000 1001 return selectModelFactory.create(input); 1002 } 1003 })); 1004 1005 configuration.add(CoercionTuple.create(String.class, Pattern.class, new Coercion<String, Pattern>() 1006 { 1007 public Pattern coerce(String input) 1008 { 1009 return Pattern.compile(input); 1010 } 1011 })); 1012 1013 configuration.add(CoercionTuple.create(ComponentResourcesAware.class, ComponentResources.class, 1014 new Coercion<ComponentResourcesAware, ComponentResources>() 1015 { 1016 1017 public ComponentResources coerce(ComponentResourcesAware input) 1018 { 1019 return input.getComponentResources(); 1020 } 1021 })); 1022 1023 configuration.add(CoercionTuple.create(String.class, Renderable.class, new Coercion<String, Renderable>() 1024 { 1025 public Renderable coerce(String input) 1026 { 1027 return new StringRenderable(input); 1028 } 1029 })); 1030 1031 configuration.add(CoercionTuple.create(Renderable.class, Block.class, new Coercion<Renderable, Block>() 1032 { 1033 public Block coerce(Renderable input) 1034 { 1035 return new RenderableAsBlock(input); 1036 } 1037 })); 1038 1039 configuration.add(CoercionTuple.create(String.class, DateFormat.class, new Coercion<String, DateFormat>() 1040 { 1041 public DateFormat coerce(String input) 1042 { 1043 final SimpleDateFormat dateFormat = new SimpleDateFormat(input, threadLocale.getLocale()); 1044 final String lenient = objectLocator.getService(SymbolSource.class).valueForSymbol(SymbolConstants.LENIENT_DATE_FORMAT); 1045 dateFormat.setLenient(Boolean.parseBoolean(lenient)); 1046 return dateFormat; 1047 } 1048 })); 1049 1050 configuration.add(CoercionTuple.create(String.class, Resource.class, new Coercion<String, Resource>() 1051 { 1052 public Resource coerce(String input) 1053 { 1054 return assetSource.resourceForPath(input); 1055 } 1056 })); 1057 1058 configuration.add(CoercionTuple.create(Renderable.class, RenderCommand.class, 1059 new Coercion<Renderable, RenderCommand>() 1060 { 1061 public RenderCommand coerce(final Renderable input) 1062 { 1063 return new RenderCommand() 1064 { 1065 public void render(MarkupWriter writer, RenderQueue queue) 1066 { 1067 input.render(writer); 1068 } 1069 }; 1070 } 1071 })); 1072 1073 configuration.add(CoercionTuple.create(Date.class, Calendar.class, new Coercion<Date, Calendar>() 1074 { 1075 public Calendar coerce(Date input) 1076 { 1077 Calendar calendar = Calendar.getInstance(threadLocale.getLocale()); 1078 calendar.setTime(input); 1079 return calendar; 1080 } 1081 })); 1082 1083 configuration.add(CoercionTuple.create(Resource.class, DynamicTemplate.class, 1084 new Coercion<Resource, DynamicTemplate>() 1085 { 1086 public DynamicTemplate coerce(Resource input) 1087 { 1088 return dynamicTemplateParser.parseTemplate(input); 1089 } 1090 })); 1091 1092 configuration.add(CoercionTuple.create(Asset.class, Resource.class, new Coercion<Asset, Resource>() 1093 { 1094 public Resource coerce(Asset input) 1095 { 1096 return input.getResource(); 1097 } 1098 })); 1099 1100 configuration.add(CoercionTuple.create(ValueEncoder.class, ValueEncoderFactory.class, new Coercion<ValueEncoder, ValueEncoderFactory>() 1101 { 1102 public ValueEncoderFactory coerce(ValueEncoder input) 1103 { 1104 return new GenericValueEncoderFactory(input); 1105 } 1106 })); 1107 } 1108 1109 /** 1110 * Adds built-in constraint generators: 1111 * <ul> 1112 * <li>PrimtiveField -- primitive fields are always required 1113 * <li>ValidateAnnotation -- adds constraints from a {@link Validate} annotation 1114 * </ul> 1115 */ 1116 public static void contributeValidationConstraintGenerator( 1117 OrderedConfiguration<ValidationConstraintGenerator> configuration) 1118 { 1119 configuration.add("PrimitiveField", new PrimitiveFieldConstraintGenerator()); 1120 configuration.add("ValidateAnnotation", new ValidateAnnotationConstraintGenerator()); 1121 configuration.addInstance("Messages", MessagesConstraintGenerator.class); 1122 } 1123 1124 private static void add(OrderedConfiguration<ComponentClassTransformWorker2> configuration, 1125 Class<? extends Annotation> annotationClass, MethodDescription description) 1126 { 1127 String name = TapestryInternalUtils.lastTerm(annotationClass.getName()); 1128 1129 ComponentClassTransformWorker2 worker = new PageLifecycleAnnotationWorker(annotationClass, 1130 description, name); 1131 1132 configuration.add(name, worker); 1133 } 1134 1135 // ======================================================================== 1136 // 1137 // Service Builder Methods (instance) 1138 // 1139 // ======================================================================== 1140 1141 public Context buildContext(ApplicationGlobals globals) 1142 { 1143 return shadowBuilder.build(globals, "context", Context.class); 1144 } 1145 1146 public static ComponentClassResolver buildComponentClassResolver(@Autobuild 1147 ComponentClassResolverImpl service, @ComponentClasses 1148 InvalidationEventHub hub) 1149 { 1150 // Allow the resolver to clean its cache when the component classes 1151 // change 1152 1153 hub.addInvalidationListener(service); 1154 1155 return service; 1156 } 1157 1158 1159 /** 1160 * Builds the PropBindingFactory as a chain of command. The terminator of 1161 * the chain is responsible for ordinary 1162 * property names (and property paths). 1163 * 1164 * This mechanism has been replaced in 5.1 with a more sophisticated parser based on ANTLR. See <a 1165 * href="https://issues.apache.org/jira/browse/TAP5-79">TAP5-79</a> for details. There are no longer any built-in 1166 * contributions to the configuration. 1167 * 1168 * @param configuration 1169 * contributions of special factories for some constants, each 1170 * contributed factory may return a 1171 * binding if applicable, or null otherwise 1172 */ 1173 public BindingFactory buildPropBindingFactory(List<BindingFactory> configuration, @Autobuild 1174 PropBindingFactory service) 1175 { 1176 configuration.add(service); 1177 1178 return chainBuilder.build(BindingFactory.class, configuration); 1179 } 1180 1181 public PersistentFieldStrategy buildClientPersistentFieldStrategy(LinkCreationHub linkCreationHub, @Autobuild 1182 ClientPersistentFieldStrategy service) 1183 { 1184 linkCreationHub.addListener(service); 1185 1186 return service; 1187 } 1188 1189 /** 1190 * Builds a proxy to the current {@link org.apache.tapestry5.services.ClientBehaviorSupport} inside this 1191 * thread's {@link org.apache.tapestry5.services.Environment}. 1192 * 1193 * @since 5.1.0.1 1194 */ 1195 1196 public ClientBehaviorSupport buildClientBehaviorSupport() 1197 { 1198 return environmentalBuilder.build(ClientBehaviorSupport.class); 1199 } 1200 1201 /** 1202 * Builds a proxy to the current {@link org.apache.tapestry5.services.FormSupport} inside this 1203 * thread's {@link org.apache.tapestry5.services.Environment}. 1204 */ 1205 public FormSupport buildFormSupport() 1206 { 1207 return environmentalBuilder.build(FormSupport.class); 1208 } 1209 1210 /** 1211 * Allows the exact steps in the component class transformation process to 1212 * be defined. 1213 */ 1214 @Marker(Primary.class) 1215 public ComponentClassTransformWorker2 buildComponentClassTransformWorker( 1216 List<ComponentClassTransformWorker2> configuration) 1217 1218 { 1219 return chainBuilder.build(ComponentClassTransformWorker2.class, configuration); 1220 } 1221 1222 /** 1223 * Analyzes properties to determine the data types, used to 1224 * {@linkplain #provideDefaultBeanBlocks(org.apache.tapestry5.ioc.Configuration)} locale 1225 * display and edit blocks for properties. The default behaviors 1226 * look for a {@link org.apache.tapestry5.beaneditor.DataType} annotation 1227 * before deriving the data type from the property type. 1228 */ 1229 @Marker(Primary.class) 1230 public DataTypeAnalyzer buildDataTypeAnalyzer(List<DataTypeAnalyzer> configuration) 1231 { 1232 return chainBuilder.build(DataTypeAnalyzer.class, configuration); 1233 } 1234 1235 /** 1236 * A chain of command for providing values for {@link Inject}-ed fields in 1237 * component classes. The service's 1238 * configuration can be extended to allow for different automatic injections 1239 * (based on some combination of field 1240 * type and field name). 1241 */ 1242 public InjectionProvider2 buildInjectionProvider(List<InjectionProvider2> configuration) 1243 { 1244 return chainBuilder.build(InjectionProvider2.class, configuration); 1245 } 1246 1247 /** 1248 * Initializes the application, using a pipeline of {@link org.apache.tapestry5.services.ApplicationInitializer}s. 1249 */ 1250 @Marker(Primary.class) 1251 public ApplicationInitializer buildApplicationInitializer(Logger logger, 1252 List<ApplicationInitializerFilter> configuration) 1253 { 1254 ApplicationInitializer terminator = new ApplicationInitializerTerminator(); 1255 1256 return pipelineBuilder.build(logger, ApplicationInitializer.class, ApplicationInitializerFilter.class, 1257 configuration, terminator); 1258 } 1259 1260 public HttpServletRequestHandler buildHttpServletRequestHandler(Logger logger, 1261 1262 List<HttpServletRequestFilter> configuration, 1263 1264 @Primary 1265 RequestHandler handler, 1266 1267 @Symbol(SymbolConstants.CHARSET) 1268 String applicationCharset, 1269 1270 TapestrySessionFactory sessionFactory) 1271 { 1272 HttpServletRequestHandler terminator = new HttpServletRequestHandlerTerminator(handler, applicationCharset, 1273 sessionFactory); 1274 1275 return pipelineBuilder.build(logger, HttpServletRequestHandler.class, HttpServletRequestFilter.class, 1276 configuration, terminator); 1277 } 1278 1279 @Marker(Primary.class) 1280 public RequestHandler buildRequestHandler(Logger logger, List<RequestFilter> configuration, 1281 1282 @Primary 1283 Dispatcher masterDispatcher) 1284 { 1285 RequestHandler terminator = new RequestHandlerTerminator(masterDispatcher); 1286 1287 return pipelineBuilder.build(logger, RequestHandler.class, RequestFilter.class, configuration, terminator); 1288 } 1289 1290 public ServletApplicationInitializer buildServletApplicationInitializer(Logger logger, 1291 List<ServletApplicationInitializerFilter> configuration, 1292 1293 @Primary 1294 ApplicationInitializer initializer) 1295 { 1296 ServletApplicationInitializer terminator = new ServletApplicationInitializerTerminator(initializer); 1297 1298 return pipelineBuilder.build(logger, ServletApplicationInitializer.class, 1299 ServletApplicationInitializerFilter.class, configuration, terminator); 1300 } 1301 1302 /** 1303 * The component event result processor used for normal component requests. 1304 */ 1305 @Marker( 1306 {Primary.class, Traditional.class}) 1307 public ComponentEventResultProcessor buildComponentEventResultProcessor( 1308 Map<Class, ComponentEventResultProcessor> configuration, @ComponentClasses 1309 InvalidationEventHub hub) 1310 { 1311 return constructComponentEventResultProcessor(configuration, hub); 1312 } 1313 1314 /** 1315 * The component event result processor used for Ajax-oriented component 1316 * requests. 1317 */ 1318 @Marker(Ajax.class) 1319 public ComponentEventResultProcessor buildAjaxComponentEventResultProcessor( 1320 Map<Class, ComponentEventResultProcessor> configuration, @ComponentClasses 1321 InvalidationEventHub hub) 1322 { 1323 return constructComponentEventResultProcessor(configuration, hub); 1324 } 1325 1326 private ComponentEventResultProcessor constructComponentEventResultProcessor( 1327 Map<Class, ComponentEventResultProcessor> configuration, InvalidationEventHub hub) 1328 { 1329 Set<Class> handledTypes = CollectionFactory.newSet(configuration.keySet()); 1330 1331 // A slight hack! 1332 1333 configuration.put(Object.class, new ObjectComponentEventResultProcessor(handledTypes)); 1334 1335 final StrategyRegistry<ComponentEventResultProcessor> registry = StrategyRegistry.newInstance( 1336 ComponentEventResultProcessor.class, configuration); 1337 1338 //As the registry will cache component classes, we need to clear the cache when we reload components to avoid memory leaks in permgen 1339 hub.addInvalidationCallback(new Runnable() 1340 { 1341 public void run() 1342 { 1343 registry.clearCache(); 1344 } 1345 }); 1346 1347 return strategyBuilder.build(registry); 1348 } 1349 1350 /** 1351 * The default data type analyzer is the final analyzer consulted and 1352 * identifies the type entirely pased on the 1353 * property type, working against its own configuration (mapping property 1354 * type class to data type). 1355 */ 1356 public static DataTypeAnalyzer buildDefaultDataTypeAnalyzer(@Autobuild 1357 DefaultDataTypeAnalyzer service, @ComponentClasses 1358 InvalidationEventHub hub) 1359 { 1360 hub.addInvalidationCallback(service); 1361 1362 return service; 1363 } 1364 1365 public static TranslatorSource buildTranslatorSource(Map<Class, Translator> configuration, 1366 TranslatorAlternatesSource alternatesSource, 1367 @ComponentClasses 1368 InvalidationEventHub hub) 1369 { 1370 TranslatorSourceImpl service = new TranslatorSourceImpl(configuration, 1371 alternatesSource.getTranslatorAlternates()); 1372 1373 hub.addInvalidationCallback(service); 1374 1375 return service; 1376 } 1377 1378 @Marker(Primary.class) 1379 public ObjectRenderer buildObjectRenderer(Map<Class, ObjectRenderer> configuration) 1380 { 1381 return strategyBuilder.build(ObjectRenderer.class, configuration); 1382 } 1383 1384 /** 1385 * Returns a {@link PlasticProxyFactory} that can be used to create extra classes around component classes. This 1386 * factory will be cleared whenever an underlying component class is discovered to have changed. Use of this 1387 * factory implies that your code will become aware of this (if necessary) to discard any cached object (alas, 1388 * this currently involves dipping into the internals side to register for the correct notifications). Failure to 1389 * properly clean up can result in really nasty PermGen space memory leaks. 1390 */ 1391 @Marker(ComponentLayer.class) 1392 public PlasticProxyFactory buildComponentProxyFactory(ComponentInstantiatorSource source) 1393 { 1394 return shadowBuilder.build(source, "proxyFactory", PlasticProxyFactory.class); 1395 } 1396 1397 /** 1398 * Ordered contributions to the MasterDispatcher service allow different URL 1399 * matching strategies to occur. 1400 */ 1401 @Marker(Primary.class) 1402 public Dispatcher buildMasterDispatcher(List<Dispatcher> configuration) 1403 { 1404 return chainBuilder.build(Dispatcher.class, configuration); 1405 } 1406 1407 /** 1408 * Builds a shadow of the RequestGlobals.request property. Note again that 1409 * the shadow can be an ordinary singleton, 1410 * even though RequestGlobals is perthread. 1411 */ 1412 public Request buildRequest() 1413 { 1414 return shadowBuilder.build(requestGlobals, "request", Request.class); 1415 } 1416 1417 /** 1418 * Builds a shadow of the RequestGlobals.HTTPServletRequest property. 1419 * Generally, you should inject the {@link Request} service instead, as 1420 * future version of Tapestry may operate beyond just the servlet API. 1421 */ 1422 public HttpServletRequest buildHttpServletRequest() 1423 { 1424 return shadowBuilder.build(requestGlobals, "HTTPServletRequest", HttpServletRequest.class); 1425 } 1426 1427 /** 1428 * @since 5.1.0.0 1429 */ 1430 public HttpServletResponse buildHttpServletResponse() 1431 { 1432 return shadowBuilder.build(requestGlobals, "HTTPServletResponse", HttpServletResponse.class); 1433 } 1434 1435 /** 1436 * Builds a shadow of the RequestGlobals.response property. Note again that 1437 * the shadow can be an ordinary singleton, 1438 * even though RequestGlobals is perthread. 1439 */ 1440 public Response buildResponse() 1441 { 1442 return shadowBuilder.build(requestGlobals, "response", Response.class); 1443 } 1444 1445 /** 1446 * The MarkupRenderer service is used to render a full page as markup. 1447 * Supports an ordered configuration of {@link org.apache.tapestry5.services.MarkupRendererFilter}s. 1448 */ 1449 public MarkupRenderer buildMarkupRenderer(Logger logger, @Autobuild 1450 MarkupRendererTerminator terminator, List<MarkupRendererFilter> configuration) 1451 { 1452 return pipelineBuilder.build(logger, MarkupRenderer.class, MarkupRendererFilter.class, configuration, 1453 terminator); 1454 } 1455 1456 /** 1457 * A wrapper around {@link org.apache.tapestry5.internal.services.PageRenderQueue} used for 1458 * partial page renders. 1459 * Supports an ordered configuration of {@link org.apache.tapestry5.services.PartialMarkupRendererFilter}s. 1460 */ 1461 public PartialMarkupRenderer buildPartialMarkupRenderer(Logger logger, 1462 List<PartialMarkupRendererFilter> configuration, @Autobuild 1463 PartialMarkupRendererTerminator terminator) 1464 { 1465 1466 return pipelineBuilder.build(logger, PartialMarkupRenderer.class, PartialMarkupRendererFilter.class, 1467 configuration, terminator); 1468 } 1469 1470 public PageRenderRequestHandler buildPageRenderRequestHandler(List<PageRenderRequestFilter> configuration, 1471 Logger logger, @Autobuild 1472 PageRenderRequestHandlerImpl terminator) 1473 { 1474 return pipelineBuilder.build(logger, PageRenderRequestHandler.class, PageRenderRequestFilter.class, 1475 configuration, terminator); 1476 } 1477 1478 /** 1479 * Builds the component action request handler for traditional (non-Ajax) 1480 * requests. These typically result in a 1481 * redirect to a Tapestry render URL. 1482 */ 1483 @Marker( 1484 {Traditional.class, Primary.class}) 1485 public ComponentEventRequestHandler buildComponentEventRequestHandler( 1486 List<ComponentEventRequestFilter> configuration, Logger logger, @Autobuild 1487 ComponentEventRequestHandlerImpl terminator) 1488 { 1489 return pipelineBuilder.build(logger, ComponentEventRequestHandler.class, ComponentEventRequestFilter.class, 1490 configuration, terminator); 1491 } 1492 1493 /** 1494 * Builds the action request handler for Ajax requests, based on a 1495 * {@linkplain org.apache.tapestry5.ioc.services.PipelineBuilder 1496 * pipeline} around {@link org.apache.tapestry5.internal.services.AjaxComponentEventRequestHandler} . Filters on 1497 * the 1498 * request handler are supported here as well. 1499 */ 1500 @Marker( 1501 {Ajax.class, Primary.class}) 1502 public ComponentEventRequestHandler buildAjaxComponentEventRequestHandler( 1503 List<ComponentEventRequestFilter> configuration, Logger logger, @Autobuild 1504 AjaxComponentEventRequestHandler terminator) 1505 { 1506 return pipelineBuilder.build(logger, ComponentEventRequestHandler.class, ComponentEventRequestFilter.class, 1507 configuration, terminator); 1508 } 1509 1510 // ======================================================================== 1511 // 1512 // Service Contribution Methods (instance) 1513 // 1514 // ======================================================================== 1515 1516 /** 1517 * Contributes the default "session" strategy. 1518 */ 1519 public void contributeApplicationStatePersistenceStrategySource( 1520 MappedConfiguration<String, ApplicationStatePersistenceStrategy> configuration, 1521 1522 @Local 1523 ApplicationStatePersistenceStrategy sessionStategy) 1524 { 1525 configuration.add("session", sessionStategy); 1526 } 1527 1528 /** 1529 * Contributes handlers for the following types: 1530 * <dl> 1531 * <dt>Object</dt> 1532 * <dd>Failure case, added to provide a more useful exception message</dd> 1533 * <dt>{@link Link}</dt> 1534 * <dd>Sends a redirect to the link (which is typically a page render link)</dd> 1535 * <dt>String</dt> 1536 * <dd>Sends a page render redirect</dd> 1537 * <dt>Class</dt> 1538 * <dd>Interpreted as the class name of a page, sends a page render render redirect (this is more refactoring safe 1539 * than the page name)</dd> 1540 * <dt>{@link Component}</dt> 1541 * <dd>A page's root component (though a non-root component will work, but will generate a warning). A direct to the 1542 * containing page is sent.</dd> 1543 * <dt>{@link org.apache.tapestry5.StreamResponse}</dt> 1544 * <dd>The stream response is sent as the actual reply.</dd> 1545 * <dt>URL</dt> 1546 * <dd>Sends a redirect to a (presumably) external URL</dd> 1547 * </dl> 1548 */ 1549 public void contributeComponentEventResultProcessor(@Traditional 1550 @ComponentInstanceProcessor 1551 ComponentEventResultProcessor componentInstanceProcessor, 1552 1553 MappedConfiguration<Class, ComponentEventResultProcessor> configuration) 1554 { 1555 configuration.add(Link.class, new ComponentEventResultProcessor<Link>() 1556 { 1557 public void processResultValue(Link value) throws IOException 1558 { 1559 response.sendRedirect(value); 1560 } 1561 }); 1562 1563 configuration.add(URL.class, new ComponentEventResultProcessor<URL>() 1564 { 1565 public void processResultValue(URL value) throws IOException 1566 { 1567 response.sendRedirect(value.toExternalForm()); 1568 } 1569 }); 1570 1571 configuration.addInstance(HttpError.class, HttpErrorComponentEventResultProcessor.class); 1572 1573 configuration.addInstance(String.class, PageNameComponentEventResultProcessor.class); 1574 1575 configuration.addInstance(Class.class, ClassResultProcessor.class); 1576 1577 configuration.add(Component.class, componentInstanceProcessor); 1578 1579 configuration.addInstance(StreamResponse.class, StreamResponseResultProcessor.class); 1580 1581 configuration.addInstance(StreamPageContent.class, StreamPageContentResultProcessor.class); 1582 } 1583 1584 /** 1585 * Contributes handlers for the following types: 1586 * <dl> 1587 * <dt>Object</dt> 1588 * <dd>Failure case, added to provide more useful exception message</dd> 1589 * <dt>{@link RenderCommand}</dt> 1590 * <dd>Typically, a {@link org.apache.tapestry5.Block}</dd> 1591 * <dt>{@link org.apache.tapestry5.annotations.Component}</dt> 1592 * <dd>Renders the component and its body (unless its a page, in which case a redirect JSON response is sent)</dd> 1593 * <dt>{@link org.apache.tapestry5.json.JSONObject} or {@link org.apache.tapestry5.json.JSONArray}</dt> 1594 * <dd>The JSONObject is returned as a text/javascript response</dd> 1595 * <dt>{@link org.apache.tapestry5.StreamResponse}</dt> 1596 * <dd>The stream response is sent as the actual response</dd> 1597 * <dt>String</dt> 1598 * <dd>Interprets the value as a logical page name and sends a client response to redirect to that page</dd> 1599 * <dt>{@link org.apache.tapestry5.Link}</dt> 1600 * <dd>Sends a JSON response to redirect to the link</dd> 1601 * <dt>{@link Class}</dt> 1602 * <dd>Treats the class as a page class and sends a redirect for a page render for that page</dd> 1603 * <dt>{@link org.apache.tapestry5.ajax.MultiZoneUpdate}</dt> 1604 * <dd>Sends a single JSON response to update the content of multiple zones 1605 * </dl> 1606 * 1607 * In most cases, when you want to support a new type, you should convert it to one of the built-in supported types 1608 * (such as {@link RenderCommand}. You can then inject the master AjaxComponentEventResultProcessor (use the 1609 * {@link Ajax} marker annotation) and delegate to it. 1610 */ 1611 @Contribute(ComponentEventResultProcessor.class) 1612 @Ajax 1613 public static void provideBaseAjaxComponentEventResultProcessors( 1614 MappedConfiguration<Class, ComponentEventResultProcessor> configuration) 1615 { 1616 configuration.addInstance(RenderCommand.class, RenderCommandComponentEventResultProcessor.class); 1617 configuration.addInstance(Component.class, AjaxComponentInstanceEventResultProcessor.class); 1618 configuration.addInstance(JSONObject.class, JSONObjectEventResultProcessor.class); 1619 configuration.addInstance(JSONArray.class, JSONArrayEventResultProcessor.class); 1620 configuration.addInstance(StreamResponse.class, StreamResponseResultProcessor.class); 1621 configuration.addInstance(String.class, AjaxPageNameComponentEventResultProcessor.class); 1622 configuration.addInstance(Link.class, AjaxLinkComponentEventResultProcessor.class); 1623 configuration.addInstance(URL.class, AjaxURLComponentEventResultProcessor.class); 1624 configuration.addInstance(Class.class, AjaxPageClassComponentEventResultProcessor.class); 1625 configuration.addInstance(MultiZoneUpdate.class, MultiZoneUpdateEventResultProcessor.class); 1626 configuration.addInstance(HttpError.class, HttpErrorComponentEventResultProcessor.class); 1627 } 1628 1629 /** 1630 * The MasterDispatcher is a chain-of-command of individual Dispatchers, 1631 * each handling (like a servlet) a particular 1632 * kind of incoming request. 1633 * <dl> 1634 * <dt>RootPath</dt> 1635 * <dd>Renders the start page for the "/" request (outdated)</dd> 1636 * <dt>PageRender</dt> 1637 * <dd>Identifies the {@link org.apache.tapestry5.services.PageRenderRequestParameters} and forwards onto 1638 * {@link PageRenderRequestHandler}</dd> 1639 * <dt>ComponentEvent</dt> 1640 * <dd>Identifies the {@link ComponentEventRequestParameters} and forwards onto the 1641 * {@link ComponentEventRequestHandler}</dd> 1642 * </dl> 1643 */ 1644 public static void contributeMasterDispatcher(OrderedConfiguration<Dispatcher> configuration) 1645 { 1646 // Looks for the root path and renders the start page. This is 1647 // maintained for compatibility 1648 // with earlier versions of Tapestry 5, it is recommended that an Index 1649 // page be used instead. 1650 1651 configuration.addInstance("RootPath", RootPathDispatcher.class, "before:Asset"); 1652 1653 configuration.addInstance("ComponentEvent", ComponentEventDispatcher.class, "before:PageRender"); 1654 1655 configuration.addInstance("PageRender", PageRenderDispatcher.class); 1656 } 1657 1658 /** 1659 * Contributes a default object renderer for type Object, plus specialized 1660 * renderers for {@link org.apache.tapestry5.services.Request}, {@link org.apache.tapestry5.ioc.Location}, 1661 * {@link org.apache.tapestry5.ComponentResources}, {@link org.apache.tapestry5.EventContext}, 1662 * {@link AvailableValues}, 1663 * List, and Object[]. 1664 */ 1665 @SuppressWarnings("unchecked") 1666 public void contributeObjectRenderer(MappedConfiguration<Class, ObjectRenderer> configuration, 1667 1668 @InjectService("LocationRenderer") 1669 ObjectRenderer locationRenderer, 1670 1671 final TypeCoercer typeCoercer) 1672 { 1673 configuration.add(Object.class, new DefaultObjectRenderer()); 1674 1675 configuration.addInstance(Request.class, RequestRenderer.class); 1676 1677 configuration.add(Location.class, locationRenderer); 1678 1679 ObjectRenderer preformatted = new ObjectRenderer<Object>() 1680 { 1681 public void render(Object object, MarkupWriter writer) 1682 { 1683 writer.element("pre"); 1684 writer.write(typeCoercer.coerce(object, String.class)); 1685 writer.end(); 1686 } 1687 }; 1688 1689 configuration.addInstance(List.class, ListRenderer.class); 1690 configuration.addInstance(Object[].class, ObjectArrayRenderer.class); 1691 configuration.addInstance(ComponentResources.class, ComponentResourcesRenderer.class); 1692 configuration.addInstance(EventContext.class, EventContextRenderer.class); 1693 configuration.add(AvailableValues.class, new AvailableValuesRenderer()); 1694 } 1695 1696 /** 1697 * Adds page render filters, each of which provides an {@link org.apache.tapestry5.annotations.Environmental} 1698 * service. Filters 1699 * often provide {@link org.apache.tapestry5.annotations.Environmental} services needed by 1700 * components as they render. 1701 * <dl> 1702 * <dt>DocumentLinker</dt> 1703 * <dd>Provides {@link org.apache.tapestry5.internal.services.DocumentLinker}</dd> 1704 * <dt>ClientBehaviorSupport (deprecated in 5.4)</dt> 1705 * <dd>Provides {@link ClientBehaviorSupport}</dd> 1706 * <dt>Heartbeat</dt> 1707 * <dd>Provides {@link org.apache.tapestry5.services.Heartbeat}</dd> 1708 * <dt>ValidationDecorator (deprecated in 5.4)</dt> 1709 * <dd>Provides {@link org.apache.tapestry5.ValidationDecorator} (via {@link ValidationDecoratorFactory#newInstance(org.apache.tapestry5.MarkupWriter)})</dd> 1710 * <dt>PageNameMeta (since 5.4)</dt> 1711 * <dd>Renders a {@code <meta/>} tag describing the active page name (development mode only)</dd> 1712 * <dt>ImportCoreStack (since 5.4) </dt> 1713 * <dd>Imports the "core" stack (necessary to get the Bootstrap CSS, if nothing else).</dd> 1714 * </dl> 1715 * 1716 * @see org.apache.tapestry5.SymbolConstants#OMIT_GENERATOR_META 1717 * @see org.apache.tapestry5.SymbolConstants#PRODUCTION_MODE 1718 * @see org.apache.tapestry5.SymbolConstants#INCLUDE_CORE_STACK 1719 * @see org.apache.tapestry5.SymbolConstants#ENABLE_PAGELOADING_MASK 1720 */ 1721 public void contributeMarkupRenderer(OrderedConfiguration<MarkupRendererFilter> configuration, 1722 1723 final ModuleManager moduleManager, 1724 1725 @Symbol(SymbolConstants.OMIT_GENERATOR_META) 1726 final boolean omitGeneratorMeta, 1727 1728 @Symbol(SymbolConstants.TAPESTRY_VERSION) 1729 final String tapestryVersion, 1730 1731 @Symbol(SymbolConstants.PRODUCTION_MODE) 1732 boolean productionMode, 1733 1734 @Symbol(SymbolConstants.INCLUDE_CORE_STACK) 1735 final boolean includeCoreStack, 1736 1737 @Symbol(SymbolConstants.ENABLE_PAGELOADING_MASK) 1738 final boolean enablePageloadingMask, 1739 1740 final ValidationDecoratorFactory validationDecoratorFactory) 1741 { 1742 MarkupRendererFilter documentLinker = new MarkupRendererFilter() 1743 { 1744 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer) 1745 { 1746 DocumentLinkerImpl linker = new DocumentLinkerImpl(moduleManager, omitGeneratorMeta, enablePageloadingMask, tapestryVersion); 1747 1748 environment.push(DocumentLinker.class, linker); 1749 1750 renderer.renderMarkup(writer); 1751 1752 environment.pop(DocumentLinker.class); 1753 1754 linker.updateDocument(writer.getDocument()); 1755 } 1756 }; 1757 1758 1759 MarkupRendererFilter clientBehaviorSupport = new MarkupRendererFilter() 1760 { 1761 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer) 1762 { 1763 ClientBehaviorSupportImpl clientBehaviorSupport = new ClientBehaviorSupportImpl(); 1764 1765 environment.push(ClientBehaviorSupport.class, clientBehaviorSupport); 1766 1767 renderer.renderMarkup(writer); 1768 1769 environment.pop(ClientBehaviorSupport.class); 1770 } 1771 }; 1772 1773 MarkupRendererFilter heartbeat = new MarkupRendererFilter() 1774 { 1775 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer) 1776 { 1777 Heartbeat heartbeat = new HeartbeatImpl(); 1778 1779 heartbeat.begin(); 1780 1781 environment.push(Heartbeat.class, heartbeat); 1782 1783 renderer.renderMarkup(writer); 1784 1785 environment.pop(Heartbeat.class); 1786 1787 heartbeat.end(); 1788 } 1789 }; 1790 1791 MarkupRendererFilter defaultValidationDecorator = new MarkupRendererFilter() 1792 { 1793 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer) 1794 { 1795 ValidationDecorator decorator = validationDecoratorFactory.newInstance(writer); 1796 1797 environment.push(ValidationDecorator.class, decorator); 1798 1799 renderer.renderMarkup(writer); 1800 1801 environment.pop(ValidationDecorator.class); 1802 } 1803 }; 1804 1805 MarkupRendererFilter importCoreStack = new MarkupRendererFilter() 1806 { 1807 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer) 1808 { 1809 renderer.renderMarkup(writer); 1810 1811 environment.peekRequired(JavaScriptSupport.class).importStack(InternalConstants.CORE_STACK_NAME); 1812 } 1813 }; 1814 1815 configuration.add("DocumentLinker", documentLinker); 1816 configuration.add("ClientBehaviorSupport", clientBehaviorSupport, "after:JavaScriptSupport"); 1817 configuration.add("Heartbeat", heartbeat); 1818 configuration.add("ValidationDecorator", defaultValidationDecorator); 1819 1820 if (includeCoreStack) 1821 { 1822 configuration.add("ImportCoreStack", importCoreStack); 1823 } 1824 1825 if (productionMode) 1826 { 1827 configuration.add("PageNameMeta", null); 1828 } else 1829 { 1830 configuration.addInstance("PageNameMeta", PageNameMetaInjector.class); 1831 } 1832 } 1833 1834 /** 1835 * Contributes {@link PartialMarkupRendererFilter}s used when rendering a 1836 * partial Ajax response. 1837 * <dl> 1838 * <dt>DocumentLinker 1839 * <dd>Provides {@link org.apache.tapestry5.internal.services.DocumentLinker} 1840 * <dt>ClientBehaviorSupport</dt> 1841 * <dd>Provides {@link ClientBehaviorSupport}</dd> 1842 * <dt>Heartbeat</dt> 1843 * <dd>Provides {@link org.apache.tapestry5.services.Heartbeat}</dd> 1844 * <dt>DefaultValidationDecorator</dt> 1845 * <dt>ValidationDecorator</dt> 1846 * <dd>Provides {@link org.apache.tapestry5.ValidationDecorator} (via {@link ValidationDecoratorFactory#newInstance(org.apache.tapestry5.MarkupWriter)})</dd> 1847 * </dl> 1848 */ 1849 public void contributePartialMarkupRenderer(OrderedConfiguration<PartialMarkupRendererFilter> configuration, 1850 1851 final ValidationDecoratorFactory validationDecoratorFactory) 1852 { 1853 PartialMarkupRendererFilter documentLinker = new PartialMarkupRendererFilter() 1854 { 1855 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer) 1856 { 1857 PartialMarkupDocumentLinker linker = new PartialMarkupDocumentLinker(); 1858 1859 environment.push(DocumentLinker.class, linker); 1860 1861 renderer.renderMarkup(writer, reply); 1862 1863 environment.pop(DocumentLinker.class); 1864 1865 linker.commit(reply); 1866 } 1867 }; 1868 1869 1870 PartialMarkupRendererFilter clientBehaviorSupport = new PartialMarkupRendererFilter() 1871 { 1872 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer) 1873 { 1874 ClientBehaviorSupportImpl support = new ClientBehaviorSupportImpl(); 1875 1876 environment.push(ClientBehaviorSupport.class, support); 1877 1878 renderer.renderMarkup(writer, reply); 1879 1880 environment.pop(ClientBehaviorSupport.class); 1881 } 1882 }; 1883 1884 PartialMarkupRendererFilter heartbeat = new PartialMarkupRendererFilter() 1885 { 1886 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer) 1887 { 1888 Heartbeat heartbeat = new HeartbeatImpl(); 1889 1890 heartbeat.begin(); 1891 1892 environment.push(Heartbeat.class, heartbeat); 1893 1894 renderer.renderMarkup(writer, reply); 1895 1896 environment.pop(Heartbeat.class); 1897 1898 heartbeat.end(); 1899 } 1900 }; 1901 1902 PartialMarkupRendererFilter defaultValidationDecorator = new PartialMarkupRendererFilter() 1903 { 1904 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer) 1905 { 1906 ValidationDecorator decorator = validationDecoratorFactory.newInstance(writer); 1907 1908 environment.push(ValidationDecorator.class, decorator); 1909 1910 renderer.renderMarkup(writer, reply); 1911 1912 environment.pop(ValidationDecorator.class); 1913 } 1914 }; 1915 1916 configuration.add("DocumentLinker", documentLinker); 1917 configuration.add("ClientBehaviorSupport", clientBehaviorSupport, "after:JavaScriptSupport"); 1918 configuration.add("Heartbeat", heartbeat); 1919 configuration.add("ValidationDecorator", defaultValidationDecorator); 1920 } 1921 1922 /** 1923 * Contributes several strategies: 1924 * <dl> 1925 * <dt>session 1926 * <dd>Values are stored in the {@link Session} 1927 * <dt>flash 1928 * <dd>Values are stored in the {@link Session}, until the next request (for the page) 1929 * <dt>client 1930 * <dd>Values are encoded into URLs (or hidden form fields) 1931 * </dl> 1932 */ 1933 public void contributePersistentFieldManager(MappedConfiguration<String, PersistentFieldStrategy> configuration, 1934 1935 Request request, 1936 1937 @InjectService("ClientPersistentFieldStrategy") 1938 PersistentFieldStrategy clientStrategy) 1939 { 1940 configuration.add(PersistenceConstants.SESSION, new SessionPersistentFieldStrategy(request)); 1941 configuration.add(PersistenceConstants.FLASH, new FlashPersistentFieldStrategy(request)); 1942 configuration.add(PersistenceConstants.CLIENT, clientStrategy); 1943 } 1944 1945 /** 1946 * Contributes {@link ValueEncoder}s or {@link ValueEncoderFactory}s for types: 1947 * <ul> 1948 * <li>Object 1949 * <li>String 1950 * <li>Enum 1951 * </ul> 1952 */ 1953 @SuppressWarnings("all") 1954 public static void contributeValueEncoderSource(MappedConfiguration<Class, Object> configuration) 1955 { 1956 configuration.addInstance(Object.class, TypeCoercedValueEncoderFactory.class); 1957 configuration.add(String.class, new StringValueEncoder()); 1958 } 1959 1960 /** 1961 * Contributes a single filter, "Secure", which checks for non-secure 1962 * requests that access secure pages. 1963 */ 1964 public void contributePageRenderRequestHandler(OrderedConfiguration<PageRenderRequestFilter> configuration, 1965 final RequestSecurityManager securityManager) 1966 { 1967 PageRenderRequestFilter secureFilter = new PageRenderRequestFilter() 1968 { 1969 public void handle(PageRenderRequestParameters parameters, PageRenderRequestHandler handler) 1970 throws IOException 1971 { 1972 1973 if (securityManager.checkForInsecurePageRenderRequest(parameters)) 1974 return; 1975 1976 handler.handle(parameters); 1977 } 1978 }; 1979 1980 configuration.add("Secure", secureFilter); 1981 } 1982 1983 public static void contributeTemplateParser(MappedConfiguration<String, URL> config) 1984 { 1985 // Any class inside the internal module would do. Or we could move all 1986 // these 1987 // files to o.a.t.services. 1988 1989 Class c = TemplateParserImpl.class; 1990 1991 config.add("-//W3C//DTD XHTML 1.0 Strict//EN", c.getResource("xhtml1-strict.dtd")); 1992 config.add("-//W3C//DTD XHTML 1.0 Transitional//EN", c.getResource("xhtml1-transitional.dtd")); 1993 config.add("-//W3C//DTD XHTML 1.0 Frameset//EN", c.getResource("xhtml1-frameset.dtd")); 1994 config.add("-//W3C//DTD HTML 4.01//EN", c.getResource("xhtml1-strict.dtd")); 1995 config.add("-//W3C//DTD HTML 4.01 Transitional//EN", c.getResource("xhtml1-transitional.dtd")); 1996 config.add("-//W3C//DTD HTML 4.01 Frameset//EN", c.getResource("xhtml1-frameset.dtd")); 1997 config.add("-//W3C//ENTITIES Latin 1 for XHTML//EN", c.getResource("xhtml-lat1.ent")); 1998 config.add("-//W3C//ENTITIES Symbols for XHTML//EN", c.getResource("xhtml-symbol.ent")); 1999 config.add("-//W3C//ENTITIES Special for XHTML//EN", c.getResource("xhtml-special.ent")); 2000 } 2001 2002 /** 2003 * Contributes factory defaults that may be overridden. 2004 */ 2005 public static void contributeFactoryDefaults(MappedConfiguration<String, Object> configuration) 2006 { 2007 // Remember this is request-to-request time, presumably it'll take the 2008 // developer more than 2009 // one second to make a change, save it, and switch back to the browser. 2010 2011 configuration.add(SymbolConstants.FILE_CHECK_INTERVAL, "1 s"); 2012 configuration.add(SymbolConstants.FILE_CHECK_UPDATE_TIMEOUT, "50 ms"); 2013 2014 // This should be overridden for particular applications. These are the 2015 // locales for which we have (at least some) localized messages. 2016 configuration.add(SymbolConstants.SUPPORTED_LOCALES, 2017 "en,it,es,zh_CN,pt_PT,de,ru,hr,fi_FI,sv_SE,fr,da,pt_BR,ja,el,bg,nb,sr_RS,mk_MK"); 2018 2019 configuration.add(SymbolConstants.TAPESTRY_VERSION, 2020 VersionUtils.readVersionNumber("META-INF/gradle/org.apache.tapestry/tapestry-core/project.properties")); 2021 2022 configuration.add(SymbolConstants.COOKIE_MAX_AGE, "7 d"); 2023 2024 configuration.add(SymbolConstants.START_PAGE_NAME, "start"); 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 // Grid's default table CSS class comes from the ComponentParameterConstants.GRID_TABLE_CSS_CLASS symbol 2131 configuration.add(SymbolConstants.BEAN_DISPLAY_CSS_CLASS, "well dl-horizontal"); 2132 configuration.add(SymbolConstants.BEAN_EDITOR_BOOLEAN_PROPERTY_DIV_CSS_CLASS, "input-group"); 2133 configuration.add(SymbolConstants.ERROR_CSS_CLASS, "help-block"); 2134 configuration.add(SymbolConstants.AJAX_FORM_LOOP_ADD_ROW_LINK_CSS_CLASS, "btn btn-default btn-sm"); 2135 configuration.add(SymbolConstants.ERRORS_BASE_CSS_CLASS, "alert-dismissable"); 2136 configuration.add(SymbolConstants.ERRORS_DEFAULT_CLASS_PARAMETER_VALUE, "alert alert-danger"); 2137 configuration.add(SymbolConstants.ERRORS_CLOSE_BUTTON_CSS_CLASS, "close"); 2138 2139 // TAP5-1998 2140 configuration.add(SymbolConstants.LENIENT_DATE_FORMAT, false); 2141 2142 // TAP5-2187 2143 configuration.add(SymbolConstants.STRICT_CSS_URL_REWRITING, false); 2144 2145 configuration.add(SymbolConstants.EXCEPTION_REPORTS_DIR, "build/exceptions"); 2146 2147 // TAP5-1815 2148 configuration.add(SymbolConstants.ENABLE_HTML5_SUPPORT, false); 2149 2150 configuration.add(SymbolConstants.RESTRICTIVE_ENVIRONMENT, false); 2151 2152 configuration.add(SymbolConstants.ENABLE_PAGELOADING_MASK, true); 2153 configuration.add(SymbolConstants.PRELOADER_MODE, PreloaderMode.PRODUCTION); 2154 } 2155 2156 /** 2157 * Adds a listener to the {@link org.apache.tapestry5.internal.services.ComponentInstantiatorSource} that clears the 2158 * {@link PropertyAccess} and {@link TypeCoercer} caches on 2159 * a class loader invalidation. In addition, forces the 2160 * realization of {@link ComponentClassResolver} at startup. 2161 */ 2162 public void contributeApplicationInitializer(OrderedConfiguration<ApplicationInitializerFilter> configuration, 2163 final TypeCoercer typeCoercer, final ComponentClassResolver componentClassResolver, @ComponentClasses 2164 final InvalidationEventHub invalidationEventHub, final @Autobuild 2165 RestoreDirtySessionObjects restoreDirtySessionObjects) 2166 { 2167 final Runnable callback = new Runnable() 2168 { 2169 public void run() 2170 { 2171 propertyAccess.clearCache(); 2172 2173 typeCoercer.clearCache(); 2174 } 2175 }; 2176 2177 ApplicationInitializerFilter clearCaches = new ApplicationInitializerFilter() 2178 { 2179 public void initializeApplication(Context context, ApplicationInitializer initializer) 2180 { 2181 // Snuck in here is the logic to clear the PropertyAccess 2182 // service's cache whenever 2183 // the component class loader is invalidated. 2184 2185 invalidationEventHub.addInvalidationCallback(callback); 2186 2187 endOfRequestEventHub.addEndOfRequestListener(restoreDirtySessionObjects); 2188 2189 // Perform other pending initialization 2190 2191 initializer.initializeApplication(context); 2192 2193 // We don't care about the result, but this forces a load of the 2194 // service 2195 // at application startup, rather than on first request. 2196 2197 componentClassResolver.isPageName("ForceLoadAtStartup"); 2198 } 2199 }; 2200 2201 configuration.add("ClearCachesOnInvalidation", clearCaches); 2202 } 2203 2204 /** 2205 * Contributes filters: 2206 * <dl> 2207 * <dt>Ajax</dt> 2208 * <dd>Determines if the request is Ajax oriented, and redirects to an alternative handler if so</dd> 2209 * <dt>Secure</dt> 2210 * <dd>Sends a redirect if an non-secure request accesses a secure page</dd> 2211 * </dl> 2212 */ 2213 public void contributeComponentEventRequestHandler(OrderedConfiguration<ComponentEventRequestFilter> configuration, 2214 final RequestSecurityManager requestSecurityManager, @Ajax 2215 ComponentEventRequestHandler ajaxHandler) 2216 { 2217 ComponentEventRequestFilter secureFilter = new ComponentEventRequestFilter() 2218 { 2219 public void handle(ComponentEventRequestParameters parameters, ComponentEventRequestHandler handler) 2220 throws IOException 2221 { 2222 if (requestSecurityManager.checkForInsecureComponentEventRequest(parameters)) 2223 return; 2224 2225 handler.handle(parameters); 2226 } 2227 }; 2228 configuration.add("Secure", secureFilter); 2229 2230 configuration.add("Ajax", new AjaxFilter(request, ajaxHandler)); 2231 } 2232 2233 /** 2234 * Contributes: 2235 * <dl> 2236 * <dt>AjaxFormUpdate</dt> 2237 * <dd>{@link AjaxFormUpdateFilter}</dd> 2238 * </dl> 2239 * 2240 * @since 5.2.0 2241 */ 2242 public static void contributeAjaxComponentEventRequestHandler( 2243 OrderedConfiguration<ComponentEventRequestFilter> configuration) 2244 { 2245 configuration.addInstance("AjaxFormUpdate", AjaxFormUpdateFilter.class); 2246 } 2247 2248 /** 2249 * Contributes strategies accessible via the {@link NullFieldStrategySource} service. 2250 * 2251 * <dl> 2252 * <dt>default</dt> 2253 * <dd>Does nothing, nulls stay null.</dd> 2254 * <dt>zero</dt> 2255 * <dd>Null values are converted to zero.</dd> 2256 * </dl> 2257 */ 2258 public static void contributeNullFieldStrategySource(MappedConfiguration<String, NullFieldStrategy> configuration) 2259 { 2260 configuration.add("default", new DefaultNullFieldStrategy()); 2261 configuration.add("zero", new ZeroNullFieldStrategy()); 2262 } 2263 2264 /** 2265 * Determines positioning of hidden fields relative to other elements (this 2266 * is needed by {@link org.apache.tapestry5.corelib.components.FormFragment} and others. 2267 * 2268 * For elements input, select, textarea and label the hidden field is positioned after. 2269 * 2270 * For elements p, div, li and td, the hidden field is positioned inside. 2271 */ 2272 public static void contributeHiddenFieldLocationRules( 2273 MappedConfiguration<String, RelativeElementPosition> configuration) 2274 { 2275 configuration.add("input", RelativeElementPosition.AFTER); 2276 configuration.add("select", RelativeElementPosition.AFTER); 2277 configuration.add("textarea", RelativeElementPosition.AFTER); 2278 configuration.add("label", RelativeElementPosition.AFTER); 2279 2280 configuration.add("p", RelativeElementPosition.INSIDE); 2281 configuration.add("div", RelativeElementPosition.INSIDE); 2282 configuration.add("td", RelativeElementPosition.INSIDE); 2283 configuration.add("li", RelativeElementPosition.INSIDE); 2284 } 2285 2286 /** 2287 * @since 5.1.0.0 2288 */ 2289 public static LinkCreationHub buildLinkCreationHub(LinkSource source) 2290 { 2291 return source.getLinkCreationHub(); 2292 } 2293 2294 /** 2295 * Exposes the public portion of the internal {@link InternalComponentInvalidationEventHub} service. 2296 * 2297 * @since 5.1.0.0 2298 */ 2299 @Marker(ComponentClasses.class) 2300 public static InvalidationEventHub buildComponentClassesInvalidationEventHub( 2301 InternalComponentInvalidationEventHub trueHub) 2302 { 2303 return trueHub; 2304 } 2305 2306 /** 2307 * @since 5.1.0.0 2308 */ 2309 @Marker(ComponentTemplates.class) 2310 public static InvalidationEventHub buildComponentTemplatesInvalidationEventHub( 2311 ComponentTemplateSource templateSource) 2312 { 2313 return templateSource.getInvalidationEventHub(); 2314 } 2315 2316 /** 2317 * @since 5.1.0.0 2318 */ 2319 @Marker(ComponentMessages.class) 2320 public static InvalidationEventHub buildComponentMessagesInvalidationEventHub(ComponentMessagesSource messagesSource) 2321 { 2322 return messagesSource.getInvalidationEventHub(); 2323 } 2324 2325 @Scope(ScopeConstants.PERTHREAD) 2326 public Environment buildEnvironment(PerthreadManager perthreadManager) 2327 { 2328 final EnvironmentImpl service = new EnvironmentImpl(); 2329 2330 perthreadManager.addThreadCleanupCallback(new Runnable() 2331 { 2332 public void run() 2333 { 2334 service.threadDidCleanup(); 2335 } 2336 }); 2337 2338 return service; 2339 } 2340 2341 /** 2342 * The master SessionPersistedObjectAnalyzer. 2343 * 2344 * @since 5.1.0.0 2345 */ 2346 @Marker(Primary.class) 2347 public SessionPersistedObjectAnalyzer buildSessionPersistedObjectAnalyzer( 2348 Map<Class, SessionPersistedObjectAnalyzer> configuration) 2349 { 2350 return strategyBuilder.build(SessionPersistedObjectAnalyzer.class, configuration); 2351 } 2352 2353 /** 2354 * Identifies String, Number and Boolean as immutable objects, a catch-all 2355 * handler for Object (that understands 2356 * the {@link org.apache.tapestry5.annotations.ImmutableSessionPersistedObject} annotation), 2357 * and a handler for {@link org.apache.tapestry5.OptimizedSessionPersistedObject}. 2358 * 2359 * @since 5.1.0.0 2360 */ 2361 public static void contributeSessionPersistedObjectAnalyzer( 2362 MappedConfiguration<Class, SessionPersistedObjectAnalyzer> configuration) 2363 { 2364 configuration.add(Object.class, new DefaultSessionPersistedObjectAnalyzer()); 2365 2366 SessionPersistedObjectAnalyzer<Object> immutable = new SessionPersistedObjectAnalyzer<Object>() 2367 { 2368 public boolean checkAndResetDirtyState(Object sessionPersistedObject) 2369 { 2370 return false; 2371 } 2372 }; 2373 2374 configuration.add(String.class, immutable); 2375 configuration.add(Number.class, immutable); 2376 configuration.add(Boolean.class, immutable); 2377 2378 configuration.add(OptimizedSessionPersistedObject.class, new OptimizedSessionPersistedObjectAnalyzer()); 2379 } 2380 2381 /** 2382 * @since 5.1.1.0 2383 */ 2384 @Marker(Primary.class) 2385 public StackTraceElementAnalyzer buildMasterStackTraceElementAnalyzer(List<StackTraceElementAnalyzer> configuration) 2386 { 2387 return chainBuilder.build(StackTraceElementAnalyzer.class, configuration); 2388 } 2389 2390 /** 2391 * Contributes: 2392 * <dl> 2393 * <dt>Application</dt> 2394 * <dd>Checks for classes in the application package</dd> 2395 * <dt>Proxies</dt> 2396 * <dd>Checks for classes that appear to be generated proxies.</dd> 2397 * <dt>SunReflect</dt> 2398 * <dd>Checks for <code>sun.reflect</code> (which are omitted) 2399 * <dt>TapestryAOP</dt> 2400 * <dd>Omits stack frames for classes related to Tapestry AOP (such as advice, etc.)</dd> 2401 * <dt>OperationTracker</dt> 2402 * <dd>Omits stack frames related to {@link OperationTracker}</dd> 2403 * <dt>Access</dt> 2404 * <dd>Omits stack frames used to provide access to container class private members</dd> 2405 * </dl> 2406 * 2407 * @since 5.1.0.0 2408 */ 2409 public static void contributeMasterStackTraceElementAnalyzer( 2410 OrderedConfiguration<StackTraceElementAnalyzer> configuration) 2411 { 2412 configuration.addInstance("TapestryAOP", TapestryAOPStackFrameAnalyzer.class); 2413 configuration.add("Proxies", new ProxiesStackTraceElementAnalyzer()); 2414 configuration.add("Synthetic", new SyntheticStackTraceElementAnalyzer()); 2415 configuration.add("SunReflect", new PrefixCheckStackTraceElementAnalyzer( 2416 StackTraceElementClassConstants.OMITTED, "sun.reflect.")); 2417 configuration.add("OperationTracker", new RegexpStackTraceElementAnalyzer(Pattern.compile("internal\\.(RegistryImpl|PerThreadOperationTracker|OperationTrackerImpl).*(run|invoke|perform)\\("), StackTraceElementClassConstants.OMITTED)); 2418 configuration.add("Access", new RegexpStackTraceElementAnalyzer(Pattern.compile("\\.access\\$\\d+\\("), StackTraceElementClassConstants.OMITTED)); 2419 2420 configuration.addInstance("Application", ApplicationStackTraceElementAnalyzer.class); 2421 2422 } 2423 2424 2425 /** 2426 * Advises the {@link org.apache.tapestry5.services.messages.ComponentMessagesSource} service so 2427 * that the creation 2428 * of {@link org.apache.tapestry5.ioc.Messages} instances can be deferred. 2429 * 2430 * @since 5.1.0.0 2431 */ 2432 @Match("ComponentMessagesSource") 2433 public static void adviseLazy(LazyAdvisor advisor, MethodAdviceReceiver receiver) 2434 { 2435 advisor.addLazyMethodInvocationAdvice(receiver); 2436 } 2437 2438 /** 2439 * @since 5.1.0.0 2440 */ 2441 public ComponentRequestHandler buildComponentRequestHandler(List<ComponentRequestFilter> configuration, 2442 2443 @Autobuild 2444 ComponentRequestHandlerTerminator terminator, 2445 2446 Logger logger) 2447 { 2448 return pipelineBuilder.build(logger, ComponentRequestHandler.class, ComponentRequestFilter.class, 2449 configuration, terminator); 2450 } 2451 2452 /** 2453 * Contributes: 2454 * <dl> 2455 * <dt>OperationTracker</dt> 2456 * <dd>Tracks general information about the request using {@link OperationTracker}</dd> 2457 * <dt>UnknownComponentFilter (production mode only)</dt> 2458 * <dd>{@link org.apache.tapestry5.internal.services.ProductionModeUnknownComponentFilter} - Detects request with unknown component and aborts handling to ultimately deliver a 404 response</dd> 2459 * <dt>InitializeActivePageName 2460 * <dd>{@link InitializeActivePageName} 2461 * <dt>DeferredResponseRenderer</dt> 2462 * <dd>{@link DeferredResponseRenderer}</dd> 2463 * </dl> 2464 * 2465 * @since 5.2.0 2466 */ 2467 public void contributeComponentRequestHandler(OrderedConfiguration<ComponentRequestFilter> configuration, @Symbol(SymbolConstants.PRODUCTION_MODE) boolean productionMode) 2468 { 2469 configuration.addInstance("OperationTracker", RequestOperationTracker.class); 2470 2471 if (productionMode) 2472 { 2473 configuration.addInstance("UnknownComponentFilter", ProductionModeUnknownComponentFilter.class); 2474 } 2475 2476 configuration.addInstance("InitializeActivePageName", InitializeActivePageName.class); 2477 configuration.addInstance("DeferredResponseRenderer", DeferredResponseRenderer.class); 2478 } 2479 2480 /** 2481 * Decorate FieldValidatorDefaultSource to setup the EnvironmentMessages 2482 * object and place it in the environment. 2483 * Although this could have been implemented directly in the default 2484 * implementation of the service, doing it 2485 * as service decoration ensures that the environment will be properly setup 2486 * even if a user overrides the default 2487 * service implementation. 2488 * 2489 * @param defaultSource 2490 * The service to decorate 2491 * @param environment 2492 */ 2493 public static FieldValidatorDefaultSource decorateFieldValidatorDefaultSource( 2494 final FieldValidatorDefaultSource defaultSource, final Environment environment) 2495 { 2496 return new FieldValidatorDefaultSource() 2497 { 2498 2499 public FieldValidator createDefaultValidator(Field field, String overrideId, Messages overrideMessages, 2500 Locale locale, Class propertyType, AnnotationProvider propertyAnnotations) 2501 { 2502 environment.push(EnvironmentMessages.class, new EnvironmentMessages(overrideMessages, overrideId)); 2503 FieldValidator fieldValidator = defaultSource.createDefaultValidator(field, overrideId, 2504 overrideMessages, locale, propertyType, propertyAnnotations); 2505 environment.pop(EnvironmentMessages.class); 2506 return fieldValidator; 2507 } 2508 2509 public FieldValidator createDefaultValidator(ComponentResources resources, String parameterName) 2510 { 2511 2512 EnvironmentMessages em = new EnvironmentMessages(resources.getContainerMessages(), resources.getId()); 2513 environment.push(EnvironmentMessages.class, em); 2514 FieldValidator fieldValidator = defaultSource.createDefaultValidator(resources, parameterName); 2515 environment.pop(EnvironmentMessages.class); 2516 return fieldValidator; 2517 } 2518 }; 2519 } 2520 2521 /** 2522 * Exposes the Environmental {@link Heartbeat} as an injectable service. 2523 * 2524 * @since 5.2.0 2525 */ 2526 public Heartbeat buildHeartbeat() 2527 { 2528 return environmentalBuilder.build(Heartbeat.class); 2529 } 2530 2531 public static ComponentMessagesSource buildComponentMessagesSource(UpdateListenerHub updateListenerHub, @Autobuild 2532 ComponentMessagesSourceImpl service) 2533 { 2534 updateListenerHub.addUpdateListener(service); 2535 2536 return service; 2537 } 2538 2539 /** 2540 * Contributes extractors for {@link Meta}, {@link Secure}, {@link ContentType} and {@link WhitelistAccessOnly} annotations. 2541 * 2542 * @since 5.2.0 2543 */ 2544 @SuppressWarnings("unchecked") 2545 public static void contributeMetaWorker(MappedConfiguration<Class, MetaDataExtractor> configuration) 2546 { 2547 configuration.addInstance(Meta.class, MetaAnnotationExtractor.class); 2548 configuration.add(Secure.class, new FixedExtractor(MetaDataConstants.SECURE_PAGE)); 2549 configuration.addInstance(ContentType.class, ContentTypeExtractor.class); 2550 configuration.add(WhitelistAccessOnly.class, new FixedExtractor(MetaDataConstants.WHITELIST_ONLY_PAGE)); 2551 configuration.addInstance(UnknownActivationContextCheck.class, UnknownActivationContextExtractor.class); 2552 } 2553 2554 /** 2555 * Builds the {@link ComponentTemplateLocator} as a chain of command. 2556 * 2557 * @since 5.2.0 2558 */ 2559 @Marker(Primary.class) 2560 public ComponentTemplateLocator buildComponentTemplateLocator(List<ComponentTemplateLocator> configuration) 2561 { 2562 return chainBuilder.build(ComponentTemplateLocator.class, configuration); 2563 } 2564 2565 /** 2566 * Contributes two template locators: 2567 * <dl> 2568 * <dt>Default</dt> 2569 * <dd>Searches for the template on the classpath ({@link DefaultTemplateLocator}</dd> 2570 * <dt>Page</dt> 2571 * <dd>Searches for <em>page</em> templates in the context ({@link PageTemplateLocator})</dd> 2572 * </dl> 2573 * 2574 * @since 5.2.0 2575 */ 2576 public static void contributeComponentTemplateLocator(OrderedConfiguration<ComponentTemplateLocator> configuration, 2577 @ContextProvider 2578 AssetFactory contextAssetFactory, 2579 @Symbol(SymbolConstants.APPLICATION_FOLDER) String applicationFolder, 2580 ComponentClassResolver componentClassResolver) 2581 { 2582 configuration.add("Default", new DefaultTemplateLocator()); 2583 configuration 2584 .add("Page", new PageTemplateLocator(contextAssetFactory.getRootResource(), componentClassResolver, applicationFolder)); 2585 2586 } 2587 2588 /** 2589 * Builds {@link ComponentEventLinkTransformer} service as a chain of command. 2590 * 2591 * @since 5.2.0 2592 */ 2593 @Marker(Primary.class) 2594 public ComponentEventLinkTransformer buildComponentEventLinkTransformer( 2595 List<ComponentEventLinkTransformer> configuration) 2596 { 2597 return chainBuilder.build(ComponentEventLinkTransformer.class, configuration); 2598 } 2599 2600 /** 2601 * Builds {@link PageRenderLinkTransformer} service as a chain of command. 2602 * 2603 * @since 5.2.0 2604 */ 2605 @Marker(Primary.class) 2606 public PageRenderLinkTransformer buildPageRenderLinkTransformer(List<PageRenderLinkTransformer> configuration) 2607 { 2608 return chainBuilder.build(PageRenderLinkTransformer.class, configuration); 2609 } 2610 2611 /** 2612 * Provides the "LinkTransformer" interceptor for the {@link ComponentEventLinkEncoder} service. 2613 * Other decorations 2614 * should come after LinkTransformer. 2615 * 2616 * @since 5.2.0 2617 */ 2618 @Match("ComponentEventLinkEncoder") 2619 public ComponentEventLinkEncoder decorateLinkTransformer(LinkTransformer linkTransformer, 2620 ComponentEventLinkEncoder delegate) 2621 { 2622 return new LinkTransformerInterceptor(linkTransformer, delegate); 2623 } 2624 2625 /** 2626 * In production mode, override {@link UpdateListenerHub} to be an empty placeholder. 2627 */ 2628 @Contribute(ServiceOverride.class) 2629 public static void productionModeOverrides(MappedConfiguration<Class, Object> configuration, 2630 @Symbol(SymbolConstants.PRODUCTION_MODE) 2631 boolean productionMode) 2632 { 2633 if (productionMode) 2634 { 2635 configuration.add(UpdateListenerHub.class, new UpdateListenerHub() 2636 { 2637 public void fireCheckForUpdates() 2638 { 2639 } 2640 2641 public void addUpdateListener(UpdateListener listener) 2642 { 2643 2644 } 2645 }); 2646 } 2647 } 2648 2649 /** 2650 * Contributes a single default analyzer: 2651 * <dl> 2652 * <dt>LocalhostOnly</dt> 2653 * <dd>Identifies requests from localhost as on client whitelist</dd> 2654 * </dl> 2655 * 2656 * @since 5.3 2657 */ 2658 @Contribute(ClientWhitelist.class) 2659 public static void defaultWhitelist(OrderedConfiguration<WhitelistAnalyzer> configuration) 2660 { 2661 configuration.add("LocalhostOnly", new LocalhostOnly()); 2662 } 2663 2664 @Startup 2665 public static void registerToClearPlasticProxyFactoryOnInvalidation(@ComponentClasses InvalidationEventHub hub, @Builtin final PlasticProxyFactory proxyFactory) 2666 { 2667 hub.addInvalidationCallback(new Runnable() 2668 { 2669 public void run() 2670 { 2671 proxyFactory.clearCache(); 2672 } 2673 }); 2674 } 2675 2676 /** 2677 * @since 5.4 2678 */ 2679 @Contribute(ValueLabelProvider.class) 2680 public void defaultValueLabelProviders(MappedConfiguration<Class, ValueLabelProvider> configuration) 2681 { 2682 configuration.addInstance(Object.class, DefaultValueLabelProvider.class); 2683 configuration.addInstance(Enum.class, EnumValueLabelProvider.class); 2684 } 2685 2686 /** 2687 * @since 5.4 2688 */ 2689 public ValueLabelProvider<?> buildValueLabelProvider(Map<Class, ValueLabelProvider> configuration) 2690 { 2691 return strategyBuilder.build(ValueLabelProvider.class, configuration); 2692 } 2693 2694 @Advise(serviceInterface = ComponentInstantiatorSource.class) 2695 public static void componentReplacer(MethodAdviceReceiver methodAdviceReceiver, 2696 final ComponentOverride componentReplacer) throws NoSuchMethodException, SecurityException 2697 { 2698 2699 if (componentReplacer.hasReplacements()) 2700 { 2701 2702 MethodAdvice advice = new MethodAdvice() 2703 { 2704 @Override 2705 public void advise(MethodInvocation invocation) 2706 { 2707 String className = (String) invocation.getParameter(0); 2708 final Class<?> replacement = componentReplacer.getReplacement(className); 2709 if (replacement != null) 2710 { 2711 invocation.setParameter(0, replacement.getName()); 2712 } 2713 invocation.proceed(); 2714 } 2715 }; 2716 2717 methodAdviceReceiver.adviseMethod( 2718 ComponentInstantiatorSource.class.getMethod("getInstantiator", String.class), advice); 2719 2720 } 2721 } 2722 2723 public static ComponentLibraryInfoSource buildComponentLibraryInfoSource(List<ComponentLibraryInfoSource> configuration, 2724 ChainBuilder chainBuilder) 2725 { 2726 return chainBuilder.build(ComponentLibraryInfoSource.class, configuration); 2727 } 2728 2729 @Contribute(ComponentLibraryInfoSource.class) 2730 public static void addBuiltInComponentLibraryInfoSources(OrderedConfiguration<ComponentLibraryInfoSource> configuration) 2731 { 2732 configuration.addInstance("Maven", MavenComponentLibraryInfoSource.class); 2733 configuration.add("TapestryCore", new TapestryCoreComponentLibraryInfoSource()); 2734 } 2735 2736 private static final class TapestryCoreComponentLibraryInfoSource implements 2737 ComponentLibraryInfoSource 2738 { 2739 @Override 2740 public ComponentLibraryInfo find(LibraryMapping libraryMapping) 2741 { 2742 ComponentLibraryInfo info = null; 2743 if (libraryMapping.libraryName.equals("core")) 2744 { 2745 2746 info = new ComponentLibraryInfo(); 2747 2748 // the information above will probably not change in the future, or change very 2749 // infrequently, so I see no problem in hardwiring them here. 2750 info.setArtifactId("tapestry-core"); 2751 info.setGroupId("org.apache.tapestry"); 2752 info.setName("Tapestry 5 core component library"); 2753 info.setDescription("Components provided out-of-the-box by Tapestry"); 2754 info.setDocumentationUrl("http://tapestry.apache.org/component-reference.html"); 2755 info.setJavadocUrl("http://tapestry.apache.org/current/apidocs/"); 2756 info.setSourceBrowseUrl("https://git-wip-us.apache.org/repos/asf?p=tapestry-5.git;a=summary"); 2757 info.setSourceRootUrl("https://git-wip-us.apache.org/repos/asf?p=tapestry-5.git;a=blob;f=tapestry-core/src/main/java/"); 2758 info.setIssueTrackerUrl("https://issues.apache.org/jira/browse/TAP5"); 2759 info.setHomepageUrl("http://tapestry.apache.org"); 2760 info.setLibraryMapping(libraryMapping); 2761 2762 final InputStream inputStream = TapestryModule.class.getResourceAsStream( 2763 "/META-INF/gradle/org.apache.tapestry/tapestry-core/project.properties"); 2764 2765 if (inputStream != null) 2766 { 2767 Properties properties = new Properties(); 2768 try 2769 { 2770 properties.load(inputStream); 2771 } catch (IOException e) 2772 { 2773 throw new RuntimeException(e); 2774 } 2775 info.setVersion(properties.getProperty("version")); 2776 } 2777 } 2778 return info; 2779 } 2780 } 2781 2782}