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