001 // Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.tapestry5.services;
016
017 import org.apache.tapestry5.*;
018 import org.apache.tapestry5.ajax.MultiZoneUpdate;
019 import org.apache.tapestry5.annotations.*;
020 import org.apache.tapestry5.beaneditor.Validate;
021 import org.apache.tapestry5.corelib.LoopFormState;
022 import org.apache.tapestry5.corelib.data.BlankOption;
023 import org.apache.tapestry5.corelib.data.GridPagerPosition;
024 import org.apache.tapestry5.corelib.data.InsertPosition;
025 import org.apache.tapestry5.grid.GridDataSource;
026 import org.apache.tapestry5.internal.*;
027 import org.apache.tapestry5.internal.beaneditor.PrimitiveFieldConstraintGenerator;
028 import org.apache.tapestry5.internal.beaneditor.ValidateAnnotationConstraintGenerator;
029 import org.apache.tapestry5.internal.bindings.*;
030 import org.apache.tapestry5.internal.grid.CollectionGridDataSource;
031 import org.apache.tapestry5.internal.grid.NullDataSource;
032 import org.apache.tapestry5.internal.gzip.GZipFilter;
033 import org.apache.tapestry5.internal.renderers.*;
034 import org.apache.tapestry5.internal.services.*;
035 import org.apache.tapestry5.internal.transform.*;
036 import org.apache.tapestry5.internal.translator.NumericTranslator;
037 import org.apache.tapestry5.internal.translator.NumericTranslatorSupport;
038 import org.apache.tapestry5.internal.translator.StringTranslator;
039 import org.apache.tapestry5.internal.util.PrimaryKeyEncoder2ValueEncoder;
040 import org.apache.tapestry5.internal.util.RenderableAsBlock;
041 import org.apache.tapestry5.internal.util.StringRenderable;
042 import org.apache.tapestry5.ioc.*;
043 import org.apache.tapestry5.ioc.annotations.*;
044 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
045 import org.apache.tapestry5.ioc.internal.util.IdAllocator;
046 import org.apache.tapestry5.ioc.services.*;
047 import org.apache.tapestry5.ioc.util.StrategyRegistry;
048 import org.apache.tapestry5.ioc.util.TimeInterval;
049 import org.apache.tapestry5.json.JSONArray;
050 import org.apache.tapestry5.json.JSONObject;
051 import org.apache.tapestry5.runtime.Component;
052 import org.apache.tapestry5.runtime.ComponentResourcesAware;
053 import org.apache.tapestry5.runtime.RenderCommand;
054 import org.apache.tapestry5.runtime.RenderQueue;
055 import org.apache.tapestry5.services.ajax.MultiZoneUpdateEventResultProcessor;
056 import org.apache.tapestry5.util.StringToEnumCoercion;
057 import org.apache.tapestry5.validator.*;
058 import org.slf4j.Logger;
059
060 import javax.servlet.ServletContext;
061 import javax.servlet.http.HttpServletRequest;
062 import javax.servlet.http.HttpServletResponse;
063 import java.io.IOException;
064 import java.lang.annotation.Annotation;
065 import java.lang.reflect.Method;
066 import java.math.BigDecimal;
067 import java.math.BigInteger;
068 import java.net.URL;
069 import java.text.DateFormat;
070 import java.text.SimpleDateFormat;
071 import java.util.*;
072 import java.util.regex.Pattern;
073
074 /**
075 * The root module for Tapestry.
076 */
077 @SuppressWarnings({ "JavaDoc" })
078 @Marker(Core.class)
079 @SubModule(InternalModule.class)
080 public final class TapestryModule
081 {
082 private final PipelineBuilder pipelineBuilder;
083
084 private final ApplicationGlobals applicationGlobals;
085
086 private final PropertyShadowBuilder shadowBuilder;
087
088 private final Environment environment;
089
090 private final StrategyBuilder strategyBuilder;
091
092 private final PropertyAccess propertyAccess;
093
094 private final ChainBuilder chainBuilder;
095
096 private final Request request;
097
098 private final Response response;
099
100 private final RequestGlobals requestGlobals;
101
102 private final EnvironmentalShadowBuilder environmentalBuilder;
103
104 private final EndOfRequestEventHub endOfRequestEventHub;
105
106 /**
107 * We inject all sorts of common dependencies (including builders) into the module itself (note: even though some of
108 * these service are defined by the module itself, that's ok because services are always lazy proxies). This isn't
109 * about efficiency (it may be slightly more efficient, but not in any noticable way), it's about eliminating the
110 * need to keep injecting these dependencies into invividual service builder and contribution methods.
111 */
112 public TapestryModule(PipelineBuilder pipelineBuilder,
113
114 PropertyShadowBuilder shadowBuilder,
115
116 RequestGlobals requestGlobals,
117
118 ApplicationGlobals applicationGlobals,
119
120 ChainBuilder chainBuilder,
121
122 Environment environment,
123
124 StrategyBuilder strategyBuilder,
125
126 PropertyAccess propertyAccess,
127
128 Request request,
129
130 Response response,
131
132 EnvironmentalShadowBuilder environmentalBuilder,
133
134 EndOfRequestEventHub endOfRequestEventHub)
135 {
136 this.pipelineBuilder = pipelineBuilder;
137 this.shadowBuilder = shadowBuilder;
138 this.requestGlobals = requestGlobals;
139 this.applicationGlobals = applicationGlobals;
140 this.chainBuilder = chainBuilder;
141 this.environment = environment;
142 this.strategyBuilder = strategyBuilder;
143 this.propertyAccess = propertyAccess;
144 this.request = request;
145 this.response = response;
146 this.environmentalBuilder = environmentalBuilder;
147 this.endOfRequestEventHub = endOfRequestEventHub;
148 }
149
150 // A bunch of classes "promoted" from inline inner class to nested classes,
151 // just so that the stack trace would be more readable. Most of these
152 // are teminators for pipeline services.
153
154 /**
155 * @since 5.1.0.0
156 */
157 private class ApplicationInitializerTerminator implements ApplicationInitializer
158 {
159 public void initializeApplication(Context context)
160 {
161 applicationGlobals.storeContext(context);
162 }
163 }
164
165 /**
166 * @since 5.1.0.0
167 */
168 private class HttpServletRequestHandlerTerminator implements HttpServletRequestHandler
169 {
170 private final RequestHandler handler;
171 private final String applicationCharset;
172 private final SessionPersistedObjectAnalyzer analyzer;
173
174 public HttpServletRequestHandlerTerminator(RequestHandler handler, String applicationCharset,
175 SessionPersistedObjectAnalyzer analyzer)
176 {
177 this.handler = handler;
178 this.applicationCharset = applicationCharset;
179 this.analyzer = analyzer;
180 }
181
182 public boolean service(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
183 throws IOException
184 {
185 requestGlobals.storeServletRequestResponse(servletRequest, servletResponse);
186
187 Request request = new RequestImpl(servletRequest, applicationCharset, analyzer);
188 Response response = new ResponseImpl(servletResponse);
189
190 // TAP5-257: Make sure that the "initial guess" for request/response is available, even if
191 // some filter in the RequestHandler pipeline replaces them.
192
193 requestGlobals.storeRequestResponse(request, response);
194
195 // Transition from the Servlet API-based pipeline, to the Tapestry-based pipeline.
196
197 return handler.service(request, response);
198 }
199 }
200
201 /**
202 * @since 5.1.0.0
203 */
204 private class ServletApplicationInitializerTerminator implements ServletApplicationInitializer
205 {
206 private final ApplicationInitializer initializer;
207
208 public ServletApplicationInitializerTerminator(ApplicationInitializer initializer)
209 {
210 this.initializer = initializer;
211 }
212
213 public void initializeApplication(ServletContext servletContext)
214 {
215 applicationGlobals.storeServletContext(servletContext);
216
217 // And now, down the (Web) ApplicationInitializer pipeline ...
218
219 ContextImpl context = new ContextImpl(servletContext);
220
221 applicationGlobals.storeContext(context);
222
223 initializer.initializeApplication(context);
224 }
225 }
226
227 /**
228 * @since 5.1.0.0
229 */
230 private class RequestHandlerTerminator implements RequestHandler
231 {
232 private final Dispatcher masterDispatcher;
233
234 public RequestHandlerTerminator(Dispatcher masterDispatcher)
235 {
236 this.masterDispatcher = masterDispatcher;
237 }
238
239 public boolean service(Request request, Response response) throws IOException
240 {
241 // Update RequestGlobals with the current request/response (in case some filter replaced the
242 // normal set).
243 requestGlobals.storeRequestResponse(request, response);
244
245 return masterDispatcher.dispatch(request, response);
246 }
247 }
248
249 public static void bind(ServiceBinder binder)
250 {
251 binder.bind(ClasspathAssetAliasManager.class, ClasspathAssetAliasManagerImpl.class);
252 binder.bind(PersistentLocale.class, PersistentLocaleImpl.class);
253 binder.bind(ApplicationStateManager.class, ApplicationStateManagerImpl.class);
254 binder.bind(ApplicationStatePersistenceStrategySource.class,
255 ApplicationStatePersistenceStrategySourceImpl.class);
256 binder.bind(BindingSource.class, BindingSourceImpl.class);
257 binder.bind(FieldValidatorSource.class, FieldValidatorSourceImpl.class);
258 binder.bind(ApplicationGlobals.class, ApplicationGlobalsImpl.class);
259 binder.bind(AssetSource.class, AssetSourceImpl.class);
260 binder.bind(Cookies.class, CookiesImpl.class);
261 binder.bind(FieldValidatorDefaultSource.class, FieldValidatorDefaultSourceImpl.class);
262 binder.bind(RequestGlobals.class, RequestGlobalsImpl.class);
263 binder.bind(ResourceDigestGenerator.class, ResourceDigestGeneratorImpl.class);
264 binder.bind(ValidationConstraintGenerator.class, ValidationConstraintGeneratorImpl.class);
265 binder.bind(EnvironmentalShadowBuilder.class, EnvironmentalShadowBuilderImpl.class);
266 binder.bind(ComponentSource.class, ComponentSourceImpl.class);
267 binder.bind(BeanModelSource.class, BeanModelSourceImpl.class);
268 binder.bind(BeanBlockSource.class, BeanBlockSourceImpl.class);
269 binder.bind(ComponentDefaultProvider.class, ComponentDefaultProviderImpl.class);
270 binder.bind(MarkupWriterFactory.class, MarkupWriterFactoryImpl.class);
271 binder.bind(FieldValidationSupport.class, FieldValidationSupportImpl.class);
272 binder.bind(ObjectRenderer.class, LocationRenderer.class).withId("LocationRenderer");
273 binder.bind(ObjectProvider.class, AssetObjectProvider.class).withId("AssetObjectProvider");
274 binder.bind(RequestExceptionHandler.class, DefaultRequestExceptionHandler.class);
275 binder.bind(ComponentEventResultProcessor.class, ComponentInstanceResultProcessor.class).withId(
276 "ComponentInstanceResultProcessor");
277 binder.bind(NullFieldStrategySource.class, NullFieldStrategySourceImpl.class);
278 binder.bind(HttpServletRequestFilter.class, IgnoredPathsFilter.class).withId("IgnoredPathsFilter");
279 binder.bind(ContextValueEncoder.class, ContextValueEncoderImpl.class);
280 binder.bind(BaseURLSource.class, BaseURLSourceImpl.class);
281 binder.bind(BeanBlockOverrideSource.class, BeanBlockOverrideSourceImpl.class);
282 binder.bind(AliasManager.class, AliasManagerImpl.class).withId("AliasOverrides");
283 binder.bind(HiddenFieldLocationRules.class, HiddenFieldLocationRulesImpl.class);
284 binder.bind(PageDocumentGenerator.class, PageDocumentGeneratorImpl.class);
285 binder.bind(ResponseRenderer.class, ResponseRendererImpl.class);
286 binder.bind(FieldTranslatorSource.class, FieldTranslatorSourceImpl.class);
287 binder.bind(BindingFactory.class, MessageBindingFactory.class).withId("MessageBindingFactory");
288 binder.bind(BindingFactory.class, ValidateBindingFactory.class).withId("ValidateBindingFactory");
289 binder.bind(BindingFactory.class, TranslateBindingFactory.class).withId("TranslateBindingFactory");
290 binder.bind(BindingFactory.class, AssetBindingFactory.class).withId("AssetBindingFactory");
291 binder.bind(BindingFactory.class, ContextBindingFactory.class).withId("ContextBindingFactory");
292 binder.bind(BindingFactory.class, NullFieldStrategyBindingFactory.class).withId(
293 "NullFieldStrategyBindingFactory");
294 binder.bind(URLEncoder.class, URLEncoderImpl.class);
295 binder.bind(ContextPathEncoder.class, ContextPathEncoderImpl.class);
296 binder.bind(UpdateListenerHub.class, UpdateListenerHubImpl.class);
297 binder.bind(ApplicationStatePersistenceStrategy.class, SessionApplicationStatePersistenceStrategy.class).withId(
298 "SessionApplicationStatePersistenceStrategy");
299 binder.bind(AssetPathConverter.class, IdentityAssetPathConverter.class);
300 binder.bind(NumericTranslatorSupport.class);
301 binder.bind(ClientDataEncoder.class, ClientDataEncoderImpl.class);
302 binder.bind(ComponentEventLinkEncoder.class, ComponentEventLinkEncoderImpl.class);
303 binder.bind(PageRenderLinkSource.class, PageRenderLinkSourceImpl.class);
304 binder.bind(ClientInfrastructure.class, ClientInfrastructureImpl.class);
305 binder.bind(URLRewriter.class, URLRewriterImpl.class);
306 }
307
308 // ========================================================================
309 //
310 // Service Builder Methods (static)
311 //
312 // ========================================================================
313
314 @PreventServiceDecoration
315 public static Alias buildAlias(Logger logger,
316
317 @Inject @Symbol(InternalSymbols.ALIAS_MODE)
318 String mode,
319
320 @InjectService("AliasOverrides")
321 AliasManager overridesManager,
322
323 Collection<AliasContribution> configuration)
324 {
325 AliasManager manager = new AliasManagerImpl(logger, configuration);
326
327 return new AliasImpl(manager, mode, overridesManager);
328 }
329
330 // ========================================================================
331 //
332 // Service Contribution Methods (static)
333 //
334 // ========================================================================
335
336 /**
337 * Contributes the factory for serveral built-in binding prefixes ("asset", "block", "component", "literal", prop",
338 * "nullfieldstrategy", "message", "validate", "translate", "var").
339 */
340 public static void contributeBindingSource(MappedConfiguration<String, BindingFactory> configuration,
341
342 @InjectService("PropBindingFactory")
343 BindingFactory propBindingFactory,
344
345 @InjectService("MessageBindingFactory")
346 BindingFactory messageBindingFactory,
347
348 @InjectService("ValidateBindingFactory")
349 BindingFactory validateBindingFactory,
350
351 @InjectService("TranslateBindingFactory")
352 BindingFactory translateBindingFactory,
353
354 @InjectService("AssetBindingFactory")
355 BindingFactory assetBindingFactory,
356
357 @InjectService("NullFieldStrategyBindingFactory")
358 BindingFactory nullFieldStrategyBindingFactory,
359
360 @InjectService("ContextBindingFactory")
361 BindingFactory contextBindingFactory)
362 {
363 configuration.add(BindingConstants.LITERAL, new LiteralBindingFactory());
364 configuration.add(BindingConstants.COMPONENT, new ComponentBindingFactory());
365 configuration.add(BindingConstants.VAR, new RenderVariableBindingFactory());
366 configuration.add(BindingConstants.BLOCK, new BlockBindingFactory());
367
368 configuration.add(BindingConstants.PROP, propBindingFactory);
369 configuration.add(BindingConstants.MESSAGE, messageBindingFactory);
370 configuration.add(BindingConstants.VALIDATE, validateBindingFactory);
371 configuration.add(BindingConstants.TRANSLATE, translateBindingFactory);
372 configuration.add(BindingConstants.ASSET, assetBindingFactory);
373 configuration.add(BindingConstants.NULLFIELDSTRATEGY, nullFieldStrategyBindingFactory);
374 configuration.add(BindingConstants.CONTEXT, contextBindingFactory);
375 }
376
377 public static void contributeClasspathAssetAliasManager(MappedConfiguration<String, String> configuration,
378
379 @Symbol(SymbolConstants.TAPESTRY_VERSION)
380 String tapestryVersion,
381
382 @Symbol(SymbolConstants.APPLICATION_VERSION)
383 String applicationVersion,
384
385 @Symbol(InternalConstants.TAPESTRY_APP_PACKAGE_PARAM)
386 String appPackage,
387
388 // @Inject not needed, because this isn't a service builder method
389 @Symbol("tapestry.scriptaculous.path")
390 String scriptaculousPath,
391
392 @Symbol("tapestry.datepicker.path")
393 String datepickerPath,
394
395 @Symbol("tapestry.blackbird.path")
396 String blackbirdPath)
397 {
398 // TAPESTRY-2159: All the classpath assets are inside a version numbered folder (i.e., 5.0.12).
399 // For scriptaculous, etc., this version is not the version of the library, but the version
400 // of Tapestry.
401
402 configuration.add("tapestry/" + tapestryVersion, "org/apache/tapestry5");
403
404 configuration.add("scriptaculous/" + tapestryVersion, scriptaculousPath);
405
406 configuration.add("datepicker/" + tapestryVersion, datepickerPath);
407
408 configuration.add("blackbird/" + tapestryVersion, blackbirdPath);
409
410 configuration.add("app/" + applicationVersion, appPackage.replace('.', '/'));
411
412 configuration.add("classpath/" + applicationVersion, "");
413 }
414
415 public static void contributeComponentClassResolver(Configuration<LibraryMapping> configuration)
416 {
417 configuration.add(new LibraryMapping("core", "org.apache.tapestry5.corelib"));
418 }
419
420 /**
421 * Adds a number of standard component class transform workers: <dl> <dt>Retain </dt> <dd>Allows fields to retain
422 * their values between requests</dd> <dt>Persist </dt> <dd>Allows fields to store their their value persistently
423 * between requests</dd> <dt>Parameter </dt> <dd>Identifies parameters based on the {@link
424 * org.apache.tapestry5.annotations.Parameter} annotation</dd> <dt>Component </dt> <dd>Defines embedded components
425 * based on the {@link org.apache.tapestry5.annotations.Component} annotation</dd> <dt>Mixin </dt> <dd>Adds a mixin
426 * as part of a component's implementation</dd> <dt>Environment </dt> <dd>Allows fields to contain values extracted
427 * from the {@link org.apache.tapestry5.services.Environment} service</dd> <dt>Inject </dt> <dd>Used with the {@link
428 * org.apache.tapestry5.ioc.annotations.Inject} annotation, when a value is supplied</dd> <dt>InjectService</dt>
429 * <dd>Handles the {@link org.apache.tapestry5.ioc.annotations.InjectService} annotation</dd> <dt>InjectPage</dt>
430 * <dd>Adds code to allow access to other pages via the {@link org.apache.tapestry5.annotations.InjectPage} field
431 * annotation</dd> <dt>InjectBlock </dt> <dd>Allows a block from the template to be injected into a field</dd>
432 * <dt>IncludeStylesheet </dt> <dd>Supports the {@link org.apache.tapestry5.annotations.IncludeStylesheet}
433 * annotation</dd> <dt>IncludeJavaScriptLibrary </dt> <dd>Supports the {@link org.apache.tapestry5.annotations.IncludeJavaScriptLibrary}
434 * annotation</dd> <dt>SupportsInformalParameters </dt> <dd>Checks for the annotation</dd> <dt>Meta </dt> <dd>Checks
435 * for meta data and adds it to the component model</dd> <dt>ApplicationState </dt> <dd>Converts fields that
436 * reference application state objects <dt>UnclaimedField </dt> <dd>Identifies unclaimed fields and resets them to
437 * null/0/false at the end of the request</dd> <dt>RenderCommand </dt> <dd>Ensures all components also implement
438 * {@link org.apache.tapestry5.runtime.RenderCommand}</dd> <dt>SetupRender, BeginRender, etc. </dt> <dd>Correspond
439 * to component render phases and annotations</dd> <dt>InvokePostRenderCleanupOnResources </dt> <dd>Makes sure
440 * {@link org.apache.tapestry5.internal.InternalComponentResources#postRenderCleanup()} is invoked after a component
441 * finishes rendering</dd> <dt>Secure</dt> <dd>Checks for the {@link org.apache.tapestry5.annotations.Secure}
442 * annotation</dd> <dt>ContentType</dt> <dd>Checks for {@link org.apache.tapestry5.annotations.ContentType}
443 * annotation</dd> <dt>GenerateAccessors</dt> <dd>Generates accessor methods if {@link
444 * org.apache.tapestry5.annotations.Property} annotation is present </dd> <dt>Cached</dt> <dd>Checks for the {@link
445 * org.apache.tapestry5.annotations.Cached} annotation</dd><dt>Log</dt> <dd>Checks for the {@link
446 * org.apache.tapestry5.annotations.Log} annotation</dd></dl>
447 */
448 public static void contributeComponentClassTransformWorker(
449 OrderedConfiguration<ComponentClassTransformWorker> configuration,
450
451 ObjectLocator locator,
452
453 InjectionProvider injectionProvider,
454
455 ComponentClassResolver resolver)
456 {
457 // TODO: Proper scheduling of all of this. Since a given field or method should
458 // only have a single annotation, the order doesn't matter so much, as long as
459 // UnclaimedField is last.
460
461 configuration.addInstance("Cached", CachedWorker.class);
462
463 configuration.add("Meta", new MetaWorker());
464
465 configuration.add("Inject", new InjectWorker(locator, injectionProvider));
466 configuration.addInstance("InjectService", InjectServiceWorker.class);
467
468 configuration.add("Secure", new SecureWorker());
469
470 configuration.add("MixinAfter", new MixinAfterWorker());
471 configuration.add("Component", new ComponentWorker(resolver));
472 configuration.add("Mixin", new MixinWorker(resolver));
473 configuration.add("OnEvent", new OnEventWorker());
474 configuration.add("SupportsInformalParameters", new SupportsInformalParametersWorker());
475 configuration.addInstance("InjectPage", InjectPageWorker.class);
476 configuration.add("InjectContainer", new InjectContainerWorker());
477 configuration.add("InjectComponent", new InjectComponentWorker());
478 configuration.add("RenderCommand", new RenderCommandWorker());
479
480 // Default values for parameters are often some form of injection, so make sure
481 // that Parameter fields are processed after injections.
482
483 configuration.addInstance("Parameter", ParameterWorker.class, "after:Inject*");
484
485 // Workers for the component rendering state machine methods; this is in typical
486 // execution order.
487
488 add(configuration, TransformConstants.SETUP_RENDER_SIGNATURE, SetupRender.class, false);
489 add(configuration, TransformConstants.BEGIN_RENDER_SIGNATURE, BeginRender.class, false);
490 add(configuration, TransformConstants.BEFORE_RENDER_TEMPLATE_SIGNATURE, BeforeRenderTemplate.class, false);
491 add(configuration, TransformConstants.BEFORE_RENDER_BODY_SIGNATURE, BeforeRenderBody.class, false);
492
493 // These phases operate in reverse order.
494
495 add(configuration, TransformConstants.AFTER_RENDER_BODY_SIGNATURE, AfterRenderBody.class, true);
496 add(configuration, TransformConstants.AFTER_RENDER_TEMPLATE_SIGNATURE, AfterRenderTemplate.class, true);
497 add(configuration, TransformConstants.AFTER_RENDER_SIGNATURE, AfterRender.class, true);
498 add(configuration, TransformConstants.CLEANUP_RENDER_SIGNATURE, CleanupRender.class, true);
499
500 // Ideally, these should be ordered pretty late in the process to make sure there are no
501 // side effects with other workers that do work inside the page lifecycle methods.
502
503 add(configuration, PageLoaded.class, TransformConstants.CONTAINING_PAGE_DID_LOAD_SIGNATURE, "pageLoaded");
504 add(configuration, PageAttached.class, TransformConstants.CONTAINING_PAGE_DID_ATTACH_SIGNATURE, "pageAttached");
505 add(configuration, PageDetached.class, TransformConstants.CONTAINING_PAGE_DID_DETACH_SIGNATURE, "pageDetached");
506
507 configuration.add("Retain", new RetainWorker());
508 configuration.add("Persist", new PersistWorker());
509
510 configuration.addInstance("IncludeStylesheet", IncludeStylesheetWorker.class, "after:SetupRender");
511 configuration.addInstance("IncludeJavaScriptLibrary", IncludeJavaScriptLibraryWorker.class,
512 "after:SetupRender");
513
514 configuration.add("InvokePostRenderCleanupOnResources", new InvokePostRenderCleanupOnResourcesWorker());
515
516 configuration.add("ContentType", new ContentTypeWorker());
517
518 configuration.add("Property", new PropertyWorker());
519
520 // These must come after Property, since they actually delete fields that may still have the annotation
521 configuration.addInstance("ApplicationState", ApplicationStateWorker.class, "after:Property");
522 configuration.addInstance("Environment", EnvironmentalWorker.class, "after:Property");
523
524 configuration.addInstance("Log", LogWorker.class);
525
526 // This one is always last. Any additional private fields that aren't annotated will
527 // be converted to clear out at the end of the request.
528
529 configuration.add("UnclaimedField", new UnclaimedFieldWorker(), "after:*");
530
531 configuration.add("PageActivationContext", new PageActivationContextWorker(), "before:OnEvent");
532 }
533
534 /**
535 * <dl> <dt>Annotation</dt> <dd>Checks for {@link org.apache.tapestry5.beaneditor.DataType} annotation</dd>
536 * <dt>Default (ordered last)</dt> <dd>{@link org.apache.tapestry5.internal.services.DefaultDataTypeAnalyzer}
537 * service ({@link #contributeDefaultDataTypeAnalyzer(org.apache.tapestry5.ioc.MappedConfiguration)} })</dd> </dl>
538 */
539 public static void contributeDataTypeAnalyzer(OrderedConfiguration<DataTypeAnalyzer> configuration,
540 @InjectService("DefaultDataTypeAnalyzer")
541 DataTypeAnalyzer defaultDataTypeAnalyzer)
542 {
543 configuration.add("Annotation", new AnnotationDataTypeAnalyzer());
544 configuration.add("Default", defaultDataTypeAnalyzer, "after:*");
545 }
546
547 /**
548 * Maps property types to data type names: <ul> <li>String --> text <li>Number --> number <li>Enum --> enum
549 * <li>Boolean --> boolean <li>Date --> date </ul>
550 */
551 public static void contributeDefaultDataTypeAnalyzer(MappedConfiguration<Class, String> configuration)
552 {
553 // This is a special case contributed to avoid exceptions when a property type can't be
554 // matched. DefaultDataTypeAnalyzer converts the empty string to null.
555
556 configuration.add(Object.class, "");
557
558 configuration.add(String.class, "text");
559 configuration.add(Number.class, "number");
560 configuration.add(Enum.class, "enum");
561 configuration.add(Boolean.class, "boolean");
562 configuration.add(Date.class, "date");
563 }
564
565 public static void contributeBeanBlockSource(Configuration<BeanBlockContribution> configuration)
566 {
567 addEditBlock(configuration, "text");
568 addEditBlock(configuration, "number");
569 addEditBlock(configuration, "enum");
570 addEditBlock(configuration, "boolean");
571 addEditBlock(configuration, "date");
572 addEditBlock(configuration, "password");
573
574 // longtext uses a text area, not a text field
575
576 addEditBlock(configuration, "longtext");
577
578 addDisplayBlock(configuration, "enum");
579 addDisplayBlock(configuration, "date");
580
581 // Password and long text have special output needs.
582 addDisplayBlock(configuration, "password");
583 addDisplayBlock(configuration, "longtext");
584 }
585
586 private static void addEditBlock(Configuration<BeanBlockContribution> configuration, String dataType)
587 {
588 addEditBlock(configuration, dataType, dataType);
589 }
590
591 private static void addEditBlock(Configuration<BeanBlockContribution> configuration, String dataType,
592 String blockId)
593 {
594 configuration.add(new BeanBlockContribution(dataType, "PropertyEditBlocks", blockId, true));
595 }
596
597 private static void addDisplayBlock(Configuration<BeanBlockContribution> configuration, String dataType)
598 {
599 addDisplayBlock(configuration, dataType, dataType);
600 }
601
602 private static void addDisplayBlock(Configuration<BeanBlockContribution> configuration, String dataType,
603 String blockId)
604 {
605 configuration.add(new BeanBlockContribution(dataType, "PropertyDisplayBlocks", blockId, false));
606 }
607
608 /**
609 * Contributes the basic set of validators: <ul> <li>required</li> <li>minlength</li> <li>maxlength</li>
610 * <li>min</li> <li>max</li> <li>regexp</li> </ul>
611 */
612 public static void contributeFieldValidatorSource(MappedConfiguration<String, Validator> configuration)
613 {
614 configuration.add("required", new Required());
615 configuration.add("minlength", new MinLength());
616 configuration.add("maxlength", new MaxLength());
617 configuration.add("min", new Min());
618 configuration.add("max", new Max());
619 configuration.add("regexp", new Regexp());
620 configuration.add("email", new Email());
621 }
622
623 /**
624 * Contributes the base set of injection providers: <dl> <dt>Default</dt> <dd>based on {@link
625 * MasterObjectProvider}</dd> <dt>Block</dt> <dd>injects fields of type Block</dd> <dt>ComponentResources</dt>
626 * <dd>give component access to its resources</dd> <dt>CommonResources</dt> <dd>access to properties of resources
627 * (log, messages, etc.)</dd> <dt>Asset</dt> <dd>injection of assets (triggered via {@link Path} annotation), with
628 * the path relative to the component class</dd> <dt>Service</dt> <dd>ordered last, for use when Inject is present
629 * and nothing else works, matches field type against Tapestry IoC services</dd> </dl>
630 */
631 public static void contributeInjectionProvider(OrderedConfiguration<InjectionProvider> configuration,
632
633 MasterObjectProvider masterObjectProvider,
634
635 ObjectLocator locator,
636
637 SymbolSource symbolSource,
638
639 AssetSource assetSource)
640 {
641 configuration.add("Default", new DefaultInjectionProvider(masterObjectProvider, locator));
642
643 configuration.add("ComponentResources", new ComponentResourcesInjectionProvider());
644
645 // This comes after default, to deal with conflicts between injecting a String as the
646 // component id, and injecting a string with @Symbol or @Value.
647
648 configuration.add("CommonResources", new CommonResourcesInjectionProvider(), "after:Default");
649
650 configuration.add("Asset", new AssetInjectionProvider(symbolSource, assetSource), "before:Default");
651
652 configuration.add("Block", new BlockInjectionProvider(), "before:Default");
653
654 // This needs to be the last one, since it matches against services
655 // and might blow up if there is no match.
656 configuration.add("Service", new ServiceInjectionProvider(locator), "after:*");
657 }
658
659 /**
660 * Contributes two object providers: <dl> <dt>Alias</dt> <dd> Searches by type among {@linkplain AliasContribution
661 * contributions} to the {@link Alias} service</dd> <dt>Asset<dt> <dd> Checks for the {@link Path} annotation, and
662 * injects an {@link Asset}</dd> <dt>Service</dt> <dd>Injects based on the {@link Service} annotation, if
663 * present</dd> </dl>
664 */
665 public static void contributeMasterObjectProvider(OrderedConfiguration<ObjectProvider> configuration,
666
667 @Local
668 final Alias alias,
669
670 @InjectService("AssetObjectProvider")
671 ObjectProvider assetObjectProvider)
672 {
673 // There's a nasty web of dependencies related to Alias; this wrapper class lets us
674 // defer instantiating the Alias service implementation just long enough to defuse those
675 // dependencies. The @Local annotation prevents a recursive call through the
676 // MasterObjectProvider to resolve the Alias service itself; that is MasterObjectProvider
677 // gets built using this proxy, then the proxy will trigger the construction of AliasImpl
678 // (which itself needs MasterObjectProvider to resolve some dependencies).
679
680 ObjectProvider wrapper = new ObjectProvider()
681 {
682 public <T> T provide(Class<T> objectType, AnnotationProvider annotationProvider, ObjectLocator locator)
683 {
684 return alias.getObjectProvider().provide(objectType, annotationProvider, locator);
685 }
686 };
687
688 configuration.add("Alias", wrapper, "after:ServiceOverride");
689
690 configuration.add("Asset", assetObjectProvider, "before:AnnotationBasedContributions");
691
692 configuration.add("Service", new ServiceAnnotationObjectProvider(), "before:AnnotationBasedContributions");
693 }
694
695
696 /**
697 * <dl> <dt>StoreIntoGlobals</dt> <dd>Stores the request and response into {@link
698 * org.apache.tapestry5.services.RequestGlobals} at the start of the pipeline</dd> <dt>IgnoredPaths</dt>
699 * <dd>Identifies requests that are known (via the IgnoredPathsFilter service's configuration) to be mapped to other
700 * applications</dd> <dt>GZip</dt> <dd>Handles GZIP compression of response streams (if supported by client)</dd>
701 */
702 public void contributeHttpServletRequestHandler(OrderedConfiguration<HttpServletRequestFilter> configuration,
703
704 @Symbol(SymbolConstants.GZIP_COMPRESSION_ENABLED)
705 boolean gzipCompressionEnabled,
706
707 @Autobuild GZipFilter gzipFilter,
708
709 @InjectService("IgnoredPathsFilter")
710 HttpServletRequestFilter ignoredPathsFilter)
711 {
712 configuration.add("IgnoredPaths", ignoredPathsFilter);
713
714 configuration.add("GZIP",
715 gzipCompressionEnabled ? gzipFilter : null,
716 "after:IgnoredPaths");
717
718 HttpServletRequestFilter storeIntoGlobals = new HttpServletRequestFilter()
719 {
720 public boolean service(HttpServletRequest request, HttpServletResponse response,
721 HttpServletRequestHandler handler)
722 throws IOException
723 {
724 requestGlobals.storeServletRequestResponse(request, response);
725
726 return handler.service(request, response);
727 }
728 };
729
730 configuration.add("StoreIntoGlobals", storeIntoGlobals, "before:*");
731 }
732
733 /**
734 * Continues a number of filters into the RequestHandler service: <dl> <dt>StaticFiles</dt> <dd>Checks to see if the
735 * request is for an actual file, if so, returns true to let the servlet container process the request</dd>
736 * <dt>CheckForUpdates</dt> <dd>Periodically fires events that checks to see if the file system sources for any
737 * cached data has changed (see {@link org.apache.tapestry5.internal.services.CheckForUpdatesFilter}).
738 * <dt>ErrorFilter</dt> <dd>Catches request errors and lets the {@link org.apache.tapestry5.services.RequestExceptionHandler}
739 * handle them</dd> <dt>StoreIntoGlobals</dt> <dd>Stores the request and response into the {@link
740 * org.apache.tapestry5.services.RequestGlobals} service (this is repeated at the end of the pipeline, in case any
741 * filter substitutes the request or response). </dl>
742 */
743 public void contributeRequestHandler(OrderedConfiguration<RequestFilter> configuration, Context context,
744
745 // @Inject not needed because its a long, not a String
746 @Symbol(SymbolConstants.FILE_CHECK_INTERVAL)
747 @IntermediateType(TimeInterval.class)
748 long checkInterval,
749
750 @Symbol(SymbolConstants.FILE_CHECK_UPDATE_TIMEOUT)
751 @IntermediateType(TimeInterval.class)
752 long updateTimeout,
753
754 UpdateListenerHub updateListenerHub,
755
756 URLRewriter urlRewriter)
757 {
758 RequestFilter staticFilesFilter = new StaticFilesFilter(context);
759
760 RequestFilter storeIntoGlobals = new RequestFilter()
761 {
762 public boolean service(Request request, Response response, RequestHandler handler)
763 throws IOException
764 {
765 requestGlobals.storeRequestResponse(request, response);
766
767 return handler.service(request, response);
768 }
769 };
770
771 RequestFilter fireEndOfRequestEvent = new RequestFilter()
772 {
773 public boolean service(Request request, Response response, RequestHandler handler)
774 throws IOException
775 {
776 try
777 {
778 return handler.service(request, response);
779 }
780 finally
781 {
782 endOfRequestEventHub.fire();
783 }
784 }
785 };
786
787 configuration.add("CheckForUpdates", new CheckForUpdatesFilter(updateListenerHub,
788 checkInterval, updateTimeout), "before:*");
789
790 // we just need the URLRewriterRequestFilter if we have URL rewriter rules, of course.
791 if (urlRewriter.hasRequestRules())
792 {
793
794 URLRewriterRequestFilter urlRewriterRequestFilter = new URLRewriterRequestFilter(
795 urlRewriter);
796 configuration.add("URLRewriter", urlRewriterRequestFilter, "before:StaticFiles");
797
798 }
799
800 configuration.add("StaticFiles", staticFilesFilter);
801
802 configuration.addInstance("ErrorFilter", RequestErrorFilter.class);
803
804 configuration.add(
805 "StoreIntoGlobals",
806 storeIntoGlobals,
807 "after:StaticFiles",
808 "before:ErrorFilter");
809
810 configuration.add(
811 "EndOfRequest",
812 fireEndOfRequestEvent,
813 "after:StoreIntoGlobals",
814 "before:ErrorFilter");
815
816 }
817
818 /**
819 * Contributes the basic set of translators: <ul> <li>string</li> <li>byte</li> <li>short</li> <li>integer</li>
820 * <li>long</li> <li>float</li> <li>double</li> <li>BigInteger</li> <li>BigDecimal</li></ul>
821 */
822 public static void contributeTranslatorSource(Configuration<Translator> configuration,
823 NumericTranslatorSupport support)
824 {
825
826 configuration.add(new StringTranslator());
827
828 Class[] types = new Class[] { Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class,
829 BigInteger.class, BigDecimal.class };
830
831 for (Class type : types)
832 {
833 String name = type.getSimpleName().toLowerCase();
834
835 configuration.add(new NumericTranslator(name, type, support));
836 }
837 }
838
839
840 /**
841 * Adds coercions: <ul> <li>String to {@link org.apache.tapestry5.SelectModel} <li>String to {@link
842 * org.apache.tapestry5.corelib.data.InsertPosition} <li>Map to {@link org.apache.tapestry5.SelectModel}
843 * <li>Collection to {@link GridDataSource} <li>null to {@link org.apache.tapestry5.grid.GridDataSource} <li>String
844 * to {@link org.apache.tapestry5.corelib.data.GridPagerPosition} <li>List to {@link
845 * org.apache.tapestry5.SelectModel} <li>{@link org.apache.tapestry5.runtime.ComponentResourcesAware} (typically, a
846 * component) to {@link org.apache.tapestry5.ComponentResources} <li>String to {@link
847 * org.apache.tapestry5.corelib.data.BlankOption} <li> {@link org.apache.tapestry5.ComponentResources} to {@link
848 * org.apache.tapestry5.PropertyOverrides} <li>String to {@link org.apache.tapestry5.Renderable} <li>{@link
849 * org.apache.tapestry5.Renderable} to {@link org.apache.tapestry5.Block} <li>String to {@link java.text.DateFormat}
850 * <li>{@link org.apache.tapestry5.PrimaryKeyEncoder} to {@link org.apache.tapestry5.ValueEncoder} <li>String to
851 * {@link org.apache.tapestry5.ioc.Resource} (via {@link org.apache.tapestry5.services.AssetSource#resourceForPath(String)})
852 * <li>{@link org.apache.tapestry5.Renderable} to {@link org.apache.tapestry5.runtime.RenderCommand}</li> </ul>
853 */
854 public static void contributeTypeCoercer(Configuration<CoercionTuple> configuration,
855
856 @Builtin
857 TypeCoercer coercer,
858
859 @Builtin
860 final ThreadLocale threadLocale,
861
862 @Core
863 final AssetSource assetSource)
864 {
865 add(configuration, ComponentResources.class, PropertyOverrides.class,
866 new Coercion<ComponentResources, PropertyOverrides>()
867 {
868 public PropertyOverrides coerce(ComponentResources input)
869 {
870 return new PropertyOverridesImpl(input);
871 }
872 });
873
874 add(configuration, String.class, SelectModel.class, new Coercion<String, SelectModel>()
875 {
876 public SelectModel coerce(String input)
877 {
878 return TapestryInternalUtils.toSelectModel(input);
879 }
880 });
881
882 add(configuration, Map.class, SelectModel.class, new Coercion<Map, SelectModel>()
883 {
884 @SuppressWarnings("unchecked")
885 public SelectModel coerce(Map input)
886 {
887 return TapestryInternalUtils.toSelectModel(input);
888 }
889 });
890
891 add(configuration, Collection.class, GridDataSource.class, new Coercion<Collection, GridDataSource>()
892 {
893 public GridDataSource coerce(Collection input)
894 {
895 return new CollectionGridDataSource(input);
896 }
897 });
898
899 add(configuration, void.class, GridDataSource.class, new Coercion<Void, GridDataSource>()
900 {
901 private final GridDataSource source = new NullDataSource();
902
903 public GridDataSource coerce(Void input)
904 {
905 return source;
906 }
907 });
908
909 add(configuration, String.class, GridPagerPosition.class,
910 StringToEnumCoercion.create(GridPagerPosition.class));
911
912 add(configuration, String.class, InsertPosition.class, StringToEnumCoercion.create(InsertPosition.class));
913
914 add(configuration, String.class, BlankOption.class, StringToEnumCoercion.create(BlankOption.class));
915
916 add(configuration, String.class, LoopFormState.class, StringToEnumCoercion.create(LoopFormState.class));
917
918 add(configuration, List.class, SelectModel.class, new Coercion<List, SelectModel>()
919 {
920 @SuppressWarnings("unchecked")
921 public SelectModel coerce(List input)
922 {
923 return TapestryInternalUtils.toSelectModel(input);
924 }
925 });
926
927 add(configuration, String.class, Pattern.class, new Coercion<String, Pattern>()
928 {
929 public Pattern coerce(String input)
930 {
931 return Pattern.compile(input);
932 }
933 });
934
935 add(configuration, ComponentResourcesAware.class, ComponentResources.class,
936 new Coercion<ComponentResourcesAware, ComponentResources>()
937 {
938
939 public ComponentResources coerce(ComponentResourcesAware input)
940 {
941 return input.getComponentResources();
942 }
943 });
944
945 add(configuration, String.class, Renderable.class, new Coercion<String, Renderable>()
946 {
947 public Renderable coerce(String input)
948 {
949 return new StringRenderable(input);
950 }
951 });
952
953 add(configuration, Renderable.class, Block.class, new Coercion<Renderable, Block>()
954 {
955 public Block coerce(Renderable input)
956 {
957 return new RenderableAsBlock(input);
958 }
959 });
960
961 add(configuration, String.class, DateFormat.class, new Coercion<String, DateFormat>()
962 {
963 public DateFormat coerce(String input)
964 {
965 return new SimpleDateFormat(input, threadLocale.getLocale());
966 }
967 });
968
969
970 add(configuration, String.class, Resource.class, new Coercion<String, Resource>()
971 {
972 public Resource coerce(String input)
973 {
974 return assetSource.resourceForPath(input);
975 }
976 });
977
978 add(configuration, Renderable.class, RenderCommand.class, new Coercion<Renderable, RenderCommand>()
979 {
980 public RenderCommand coerce(final Renderable input)
981 {
982 return new RenderCommand()
983 {
984 public void render(MarkupWriter writer, RenderQueue queue)
985 {
986 input.render(writer);
987 }
988 };
989 }
990 });
991
992 add(configuration, PrimaryKeyEncoder.class, ValueEncoder.class, new PrimaryKeyEncoder2ValueEncoder(coercer));
993 }
994
995 /**
996 * Adds built-in constraint generators: <ul> <li>PrimtiveField -- primitive fields are always required
997 * <li>ValidateAnnotation -- adds constraints from a {@link Validate} annotation </ul>
998 */
999 public static void contributeValidationConstraintGenerator(
1000 OrderedConfiguration<ValidationConstraintGenerator> configuration)
1001 {
1002 configuration.add("PrimitiveField", new PrimitiveFieldConstraintGenerator());
1003 configuration.add("ValidateAnnotation", new ValidateAnnotationConstraintGenerator());
1004 }
1005
1006 private static <S, T> void add(Configuration<CoercionTuple> configuration, Class<S> sourceType, Class<T> targetType,
1007 Coercion<S, T> coercion)
1008 {
1009 CoercionTuple<S, T> tuple = new CoercionTuple<S, T>(sourceType, targetType, coercion);
1010
1011 configuration.add(tuple);
1012 }
1013
1014 private static void add(OrderedConfiguration<ComponentClassTransformWorker> configuration,
1015 Class<? extends Annotation> annotationClass,
1016 TransformMethodSignature lifecycleMethodSignature, String methodAlias)
1017 {
1018 ComponentClassTransformWorker worker = new PageLifecycleAnnotationWorker(annotationClass,
1019 lifecycleMethodSignature, methodAlias);
1020
1021 String name = TapestryInternalUtils.lastTerm(annotationClass.getName());
1022
1023 configuration.add(name, worker);
1024 }
1025
1026 private static void add(OrderedConfiguration<ComponentClassTransformWorker> configuration,
1027 TransformMethodSignature signature, Class<? extends Annotation> annotationClass,
1028 boolean reverse)
1029 {
1030 // make the name match the annotation class name.
1031
1032 String name = annotationClass.getSimpleName();
1033
1034 configuration.add(name, new RenderPhaseMethodWorker(signature, annotationClass, reverse));
1035 }
1036
1037 // ========================================================================
1038 //
1039 // Service Builder Methods (instance)
1040 //
1041 // ========================================================================
1042
1043 public Context buildContext(ApplicationGlobals globals)
1044 {
1045 return shadowBuilder.build(globals, "context", Context.class);
1046 }
1047
1048 public static ComponentClassResolver buildComponentClassResolver(@Autobuild ComponentClassResolverImpl service,
1049 @ComponentClasses InvalidationEventHub hub)
1050 {
1051 // Allow the resolver to clean its cache when the component classes change
1052
1053 hub.addInvalidationListener(service);
1054
1055 return service;
1056 }
1057
1058 @Marker(ClasspathProvider.class)
1059 public AssetFactory buildClasspathAssetFactory(ResourceCache resourceCache, ClasspathAssetAliasManager aliasManager,
1060 AssetPathConverter converter)
1061 {
1062 ClasspathAssetFactory factory = new ClasspathAssetFactory(resourceCache, aliasManager, converter);
1063
1064 resourceCache.addInvalidationListener(factory);
1065
1066 return factory;
1067 }
1068
1069 @Marker(ContextProvider.class)
1070 public AssetFactory buildContextAssetFactory(ApplicationGlobals globals,
1071
1072 @Inject @Symbol(SymbolConstants.APPLICATION_VERSION)
1073 String applicationVersion,
1074
1075 AssetPathConverter converter)
1076 {
1077 return new ContextAssetFactory(request, globals.getContext(), applicationVersion, converter);
1078 }
1079
1080 /**
1081 * Builds the PropBindingFactory as a chain of command. The terminator of the chain is responsible for ordinary
1082 * property names (and property paths).
1083 * <p/>
1084 * This mechanism has been replaced in 5.1 with a more sophisticated parser based on ANTLR. See <a
1085 * href="https://issues.apache.org/jira/browse/TAP5-79">TAP5-79</a> for details. There are no longer any built-in
1086 * contributions to the configuration.
1087 *
1088 * @param configuration contributions of special factories for some constants, each contributed factory may return a
1089 * binding if applicable, or null otherwise
1090 */
1091 public BindingFactory buildPropBindingFactory(List<BindingFactory> configuration,
1092 @Autobuild PropBindingFactory service)
1093 {
1094 configuration.add(service);
1095
1096 return chainBuilder.build(BindingFactory.class, configuration);
1097 }
1098
1099 /**
1100 * Builds the source of {@link Messages} containing validation messages. The contributions are paths to message
1101 * bundles (resource paths within the classpath); the default contribution is "org/apache/tapestry5/internal/ValidationMessages".
1102 */
1103 public ValidationMessagesSource buildValidationMessagesSource(List<String> configuration,
1104
1105 UpdateListenerHub updateListenerHub,
1106
1107 @ClasspathProvider AssetFactory classpathAssetFactory,
1108
1109 ClasspathURLConverter classpathURLConverter)
1110 {
1111 ValidationMessagesSourceImpl service = new ValidationMessagesSourceImpl(configuration,
1112 classpathAssetFactory.getRootResource(),
1113 classpathURLConverter);
1114 updateListenerHub.addUpdateListener(service);
1115
1116 return service;
1117 }
1118
1119 public static MetaDataLocator buildMetaDataLocator(@Autobuild MetaDataLocatorImpl service,
1120 @ComponentClasses InvalidationEventHub hub)
1121 {
1122 hub.addInvalidationListener(service);
1123
1124 return service;
1125 }
1126
1127 public PersistentFieldStrategy buildClientPersistentFieldStrategy(LinkCreationHub linkCreationHub,
1128 @Autobuild ClientPersistentFieldStrategy service)
1129 {
1130 linkCreationHub.addListener(service);
1131
1132 return service;
1133 }
1134
1135 /**
1136 * Builds a proxy to the current {@link org.apache.tapestry5.RenderSupport} inside this thread's {@link
1137 * org.apache.tapestry5.services.Environment}.
1138 */
1139 public RenderSupport buildRenderSupport()
1140 {
1141 return environmentalBuilder.build(RenderSupport.class);
1142 }
1143
1144 /**
1145 * Builds a proxy to the current {@link org.apache.tapestry5.services.ClientBehaviorSupport} inside this thread's
1146 * {@link org.apache.tapestry5.services.Environment}.
1147 *
1148 * @since 5.1.0.1
1149 */
1150
1151 public ClientBehaviorSupport buildClientBehaviorSupport()
1152 {
1153 return environmentalBuilder.build(ClientBehaviorSupport.class);
1154 }
1155
1156 /**
1157 * Builds a proxy to the current {@link org.apache.tapestry5.services.FormSupport} inside this thread's {@link
1158 * org.apache.tapestry5.services.Environment}.
1159 */
1160 public FormSupport buildFormSupport()
1161 {
1162 return environmentalBuilder.build(FormSupport.class);
1163 }
1164
1165 /**
1166 * Allows the exact steps in the component class transformation process to be defined.
1167 */
1168 public ComponentClassTransformWorker buildComponentClassTransformWorker(
1169 List<ComponentClassTransformWorker> configuration)
1170 {
1171 return chainBuilder.build(ComponentClassTransformWorker.class, configuration);
1172 }
1173
1174 /**
1175 * Analyzes properties to determine the data types, used to {@linkplain #contributeBeanBlockSource(org.apache.tapestry5.ioc.Configuration)}
1176 * locale display and edit blocks} for properties. The default behaviors look for a {@link
1177 * org.apache.tapestry5.beaneditor.DataType} annotation before deriving the data type from the property type.
1178 */
1179 @Marker(Primary.class)
1180 public DataTypeAnalyzer buildDataTypeAnalyzer(List<DataTypeAnalyzer> configuration)
1181 {
1182 return chainBuilder.build(DataTypeAnalyzer.class, configuration);
1183 }
1184
1185 /**
1186 * A chain of command for providing values for {@link Inject}-ed fields in component classes. The service's
1187 * configuration can be extended to allow for different automatic injections (based on some combination of field
1188 * type and field name).
1189 */
1190
1191 public InjectionProvider buildInjectionProvider(List<InjectionProvider> configuration)
1192 {
1193 return chainBuilder.build(InjectionProvider.class, configuration);
1194 }
1195
1196
1197 /**
1198 * Initializes the application, using a pipeline of {@link org.apache.tapestry5.services.ApplicationInitializer}s.
1199 */
1200 @Marker(Primary.class)
1201 public ApplicationInitializer buildApplicationInitializer(Logger logger,
1202 List<ApplicationInitializerFilter> configuration)
1203 {
1204 ApplicationInitializer terminator = new ApplicationInitializerTerminator();
1205
1206 return pipelineBuilder.build(logger, ApplicationInitializer.class, ApplicationInitializerFilter.class,
1207 configuration, terminator);
1208 }
1209
1210 public HttpServletRequestHandler buildHttpServletRequestHandler(Logger logger,
1211
1212 List<HttpServletRequestFilter> configuration,
1213
1214 @Primary
1215 RequestHandler handler,
1216
1217 @Inject @Symbol(SymbolConstants.CHARSET)
1218 String applicationCharset,
1219
1220 @Primary
1221 SessionPersistedObjectAnalyzer analyzer)
1222 {
1223 HttpServletRequestHandler terminator = new HttpServletRequestHandlerTerminator(handler, applicationCharset,
1224 analyzer);
1225
1226 return pipelineBuilder.build(logger, HttpServletRequestHandler.class, HttpServletRequestFilter.class,
1227 configuration, terminator);
1228 }
1229
1230 @Marker(Primary.class)
1231 public RequestHandler buildRequestHandler(Logger logger, List<RequestFilter> configuration,
1232
1233 @Primary
1234 Dispatcher masterDispatcher)
1235 {
1236 RequestHandler terminator = new RequestHandlerTerminator(masterDispatcher);
1237
1238 return pipelineBuilder.build(logger, RequestHandler.class, RequestFilter.class, configuration, terminator);
1239 }
1240
1241 public ServletApplicationInitializer buildServletApplicationInitializer(Logger logger,
1242 List<ServletApplicationInitializerFilter> configuration,
1243
1244 @Primary
1245 ApplicationInitializer initializer)
1246 {
1247 ServletApplicationInitializer terminator = new ServletApplicationInitializerTerminator(initializer);
1248
1249 return pipelineBuilder.build(logger, ServletApplicationInitializer.class,
1250 ServletApplicationInitializerFilter.class, configuration, terminator);
1251 }
1252
1253 /**
1254 * The component event result processor used for normal component requests.
1255 */
1256 @Marker({ Primary.class, Traditional.class })
1257 public ComponentEventResultProcessor buildComponentEventResultProcessor(
1258 Map<Class, ComponentEventResultProcessor> configuration)
1259 {
1260 return constructComponentEventResultProcessor(configuration);
1261 }
1262
1263 /**
1264 * The component event result processor used for Ajax-oriented component requests.
1265 */
1266 @Marker(Ajax.class)
1267 public ComponentEventResultProcessor buildAjaxComponentEventResultProcessor(
1268 Map<Class, ComponentEventResultProcessor> configuration)
1269 {
1270 return constructComponentEventResultProcessor(configuration);
1271 }
1272
1273 private ComponentEventResultProcessor constructComponentEventResultProcessor(
1274 Map<Class, ComponentEventResultProcessor> configuration)
1275 {
1276 Set<Class> handledTypes = CollectionFactory.newSet(configuration.keySet());
1277
1278 // A slight hack!
1279
1280 configuration.put(Object.class, new ObjectComponentEventResultProcessor(handledTypes));
1281
1282 StrategyRegistry<ComponentEventResultProcessor> registry = StrategyRegistry.newInstance(
1283 ComponentEventResultProcessor.class, configuration);
1284
1285 return strategyBuilder.build(registry);
1286 }
1287
1288 /**
1289 * The default data type analyzer is the final analyzer consulted and identifies the type entirely pased on the
1290 * property type, working against its own configuration (mapping property type class to data type).
1291 */
1292 public static DataTypeAnalyzer buildDefaultDataTypeAnalyzer(@Autobuild DefaultDataTypeAnalyzer service,
1293 @ComponentClasses InvalidationEventHub hub)
1294 {
1295 hub.addInvalidationListener(service);
1296
1297 return service;
1298 }
1299
1300 public static TranslatorSource buildTranslatorSource(@Autobuild TranslatorSourceImpl service,
1301 @ComponentClasses InvalidationEventHub hub)
1302 {
1303 hub.addInvalidationListener(service);
1304
1305 return service;
1306 }
1307
1308 @Marker(Primary.class)
1309 public ObjectRenderer buildObjectRenderer(Map<Class, ObjectRenderer> configuration)
1310 {
1311 return strategyBuilder.build(ObjectRenderer.class, configuration);
1312 }
1313
1314
1315 /**
1316 * Returns a {@link org.apache.tapestry5.ioc.services.ClassFactory} that can be used to create extra classes around
1317 * component classes. This ClassFactory will be cleared whenever an underlying component class is discovered to have
1318 * changed. Use of this class factory implies that your code will become aware of this (if necessary) to discard any
1319 * cached object (alas, this currently involves dipping into the internals side to register for the correct
1320 * notifications). Failure to properly clean up can result in really nasty PermGen space memory leaks.
1321 */
1322 @Marker(ComponentLayer.class)
1323 public ClassFactory buildComponentClassFactory(ComponentInstantiatorSource source)
1324 {
1325 return shadowBuilder.build(source, "classFactory", ClassFactory.class);
1326 }
1327
1328
1329 /**
1330 * Ordered contributions to the MasterDispatcher service allow different URL matching strategies to occur.
1331 */
1332 @Marker(Primary.class)
1333 public Dispatcher buildMasterDispatcher(List<Dispatcher> configuration)
1334 {
1335 return chainBuilder.build(Dispatcher.class, configuration);
1336 }
1337
1338 public PropertyConduitSource buildPropertyConduitSource(@Autobuild PropertyConduitSourceImpl service,
1339 @ComponentClasses InvalidationEventHub hub)
1340 {
1341 hub.addInvalidationListener(service);
1342
1343 return service;
1344 }
1345
1346 /**
1347 * Builds a shadow of the RequestGlobals.request property. Note again that the shadow can be an ordinary singleton,
1348 * even though RequestGlobals is perthread.
1349 */
1350 public Request buildRequest()
1351 {
1352 return shadowBuilder.build(requestGlobals, "request", Request.class);
1353 }
1354
1355 /**
1356 * Builds a shadow of the RequestGlobals.HTTPServletRequest property. Generally, you should inject the {@link
1357 * Request} service instead, as future version of Tapestry may operate beyond just the servlet API.
1358 */
1359 public HttpServletRequest buildHttpServletRequest()
1360 {
1361 return shadowBuilder.build(requestGlobals, "HTTPServletRequest", HttpServletRequest.class);
1362 }
1363
1364 /**
1365 * @since 5.1.0.0
1366 */
1367 public HttpServletResponse buildHttpServletResponse()
1368 {
1369 return shadowBuilder.build(requestGlobals, "HTTPServletResponse", HttpServletResponse.class);
1370 }
1371
1372 /**
1373 * Builds a shadow of the RequestGlobals.response property. Note again that the shadow can be an ordinary singleton,
1374 * even though RequestGlobals is perthread.
1375 */
1376 public Response buildResponse()
1377 {
1378 return shadowBuilder.build(requestGlobals, "response", Response.class);
1379 }
1380
1381
1382 /**
1383 * The MarkupRenderer service is used to render a full page as markup. Supports an ordered configuration of {@link
1384 * org.apache.tapestry5.services.MarkupRendererFilter}s.
1385 */
1386 public MarkupRenderer buildMarkupRenderer(Logger logger,
1387 @Autobuild MarkupRendererTerminator terminator,
1388 List<MarkupRendererFilter> configuration)
1389 {
1390 return pipelineBuilder.build(logger, MarkupRenderer.class, MarkupRendererFilter.class, configuration,
1391 terminator);
1392 }
1393
1394 /**
1395 * A wrapper around {@link org.apache.tapestry5.internal.services.PageRenderQueue} used for partial page renders.
1396 * Supports an ordered configuration of {@link org.apache.tapestry5.services.PartialMarkupRendererFilter}s.
1397 *
1398 * @see #contributePartialMarkupRenderer(org.apache.tapestry5.ioc.OrderedConfiguration, org.apache.tapestry5.Asset,
1399 * org.apache.tapestry5.ioc.services.SymbolSource, AssetSource, ValidationMessagesSource)
1400 */
1401 public PartialMarkupRenderer buildPartialMarkupRenderer(Logger logger,
1402 List<PartialMarkupRendererFilter> configuration,
1403 @Autobuild PartialMarkupRendererTerminator terminator)
1404 {
1405
1406 return pipelineBuilder.build(logger, PartialMarkupRenderer.class, PartialMarkupRendererFilter.class,
1407 configuration, terminator);
1408 }
1409
1410 public PageRenderRequestHandler buildPageRenderRequestHandler(
1411 List<PageRenderRequestFilter> configuration,
1412 Logger logger,
1413 @Autobuild PageRenderRequestHandlerImpl terminator)
1414 {
1415 return pipelineBuilder.build(logger, PageRenderRequestHandler.class, PageRenderRequestFilter.class,
1416 configuration, terminator);
1417 }
1418
1419
1420 /**
1421 * Builds the component action request handler for traditional (non-Ajax) requests. These typically result in a
1422 * redirect to a Tapestry render URL.
1423 */
1424 @Marker({ Traditional.class, Primary.class })
1425 public ComponentEventRequestHandler buildComponentEventRequestHandler(
1426 List<ComponentEventRequestFilter> configuration, Logger logger,
1427 @Autobuild ComponentEventRequestHandlerImpl terminator)
1428 {
1429 return pipelineBuilder.build(logger, ComponentEventRequestHandler.class, ComponentEventRequestFilter.class,
1430 configuration, terminator);
1431 }
1432
1433 /**
1434 * Builds the action request handler for Ajax requests, based on a {@linkplain org.apache.tapestry5.ioc.services.PipelineBuilder
1435 * pipeline} around {@link org.apache.tapestry5.internal.services.AjaxComponentEventRequestHandler}. Filters on the
1436 * request handler are supported here as well.
1437 */
1438 @Marker({ Ajax.class, Primary.class })
1439 public ComponentEventRequestHandler buildAjaxComponentEventRequestHandler(
1440 List<ComponentEventRequestFilter> configuration, Logger logger,
1441 @Autobuild AjaxComponentEventRequestHandler terminator)
1442 {
1443 return pipelineBuilder.build(logger, ComponentEventRequestHandler.class, ComponentEventRequestFilter.class,
1444 configuration, terminator);
1445 }
1446
1447 // ========================================================================
1448 //
1449 // Service Contribution Methods (instance)
1450 //
1451 // ========================================================================
1452
1453 /**
1454 * Contributes the default "session" strategy.
1455 */
1456 public void contributeApplicationStatePersistenceStrategySource(
1457 MappedConfiguration<String, ApplicationStatePersistenceStrategy> configuration,
1458
1459 @Local
1460 ApplicationStatePersistenceStrategy sessionStategy)
1461 {
1462 configuration.add("session", sessionStategy);
1463 }
1464
1465 public void contributeAssetSource(MappedConfiguration<String, AssetFactory> configuration,
1466 @ContextProvider AssetFactory contextAssetFactory,
1467
1468 @ClasspathProvider AssetFactory classpathAssetFactory)
1469 {
1470 configuration.add("context", contextAssetFactory);
1471 configuration.add("classpath", classpathAssetFactory);
1472 }
1473
1474 /**
1475 * Contributes handlers for the following types: <dl> <dt>Object</dt> <dd>Failure case, added to provide a more
1476 * useful exception message</dd> <dt>{@link Link}</dt> <dd>Sends a redirect to the link (which is typically a page
1477 * render link)</dd> <dt>String</dt> <dd>Sends a page render redirect</dd> <dt>Class</dt> <dd>Interpreted as the
1478 * class name of a page, sends a page render render redirect (this is more refactoring safe than the page name)</dd>
1479 * <dt>{@link Component}</dt> <dd>A page's root component (though a non-root component will work, but will generate
1480 * a warning). A direct to the containing page is sent.</dd> <dt>{@link org.apache.tapestry5.StreamResponse}</dt>
1481 * <dd>The stream response is sent as the actual reply.</dd> <dt>URL</dt> <dd>Sends a redirect to a (presumably)
1482 * external URL</dd> </dl>
1483 */
1484 public void contributeComponentEventResultProcessor(
1485 @Traditional @ComponentInstanceProcessor
1486 ComponentEventResultProcessor componentInstanceProcessor,
1487
1488 MappedConfiguration<Class, ComponentEventResultProcessor> configuration)
1489 {
1490 configuration.add(Link.class, new ComponentEventResultProcessor<Link>()
1491 {
1492 public void processResultValue(Link value) throws IOException
1493 {
1494 response.sendRedirect(value);
1495 }
1496 });
1497
1498 configuration.add(URL.class, new ComponentEventResultProcessor<URL>()
1499 {
1500 public void processResultValue(URL value) throws IOException
1501 {
1502 response.sendRedirect(value.toExternalForm());
1503 }
1504 });
1505
1506 configuration.addInstance(String.class, PageNameComponentEventResultProcessor.class);
1507
1508 configuration.addInstance(Class.class, ClassResultProcessor.class);
1509
1510 configuration.add(Component.class, componentInstanceProcessor);
1511
1512 configuration.addInstance(StreamResponse.class, StreamResponseResultProcessor.class);
1513 }
1514
1515
1516 /**
1517 * Contributes handlers for the following types: <dl> <dt>Object</dt> <dd>Failure case, added to provide more useful
1518 * exception message</dd> <dt>{@link RenderCommand}</dt> <dd>Typically, a {@link org.apache.tapestry5.Block}</dd>
1519 * <dt>{@link org.apache.tapestry5.annotations.Component}</dt> <dd>Renders the component and its body (unless its a
1520 * page, in which case a redirect JSON response is sent)</dd> <dt>{@link org.apache.tapestry5.json.JSONObject} or
1521 * {@link org.apache.tapestry5.json.JSONArray}</dt> <dd>The JSONObject is returned as a text/javascript
1522 * response</dd> <dt>{@link org.apache.tapestry5.StreamResponse}</dt> <dd>The stream response is sent as the actual
1523 * response</dd> <dt>String</dt> <dd>Interprets the value as a logical page name and sends a client response to
1524 * redirect to that page</dd> <dt>{@link org.apache.tapestry5.Link}</dt> <dd>Sends a JSON response to redirect to
1525 * the link</dd> <dt>{@link Class}</dt> <dd>Treats the class as a page class and sends a redirect for a page render
1526 * for that page</dd> <dt>{@link org.apache.tapestry5.ajax.MultiZoneUpdate}</dt> <dd>Sends a single JSON response to
1527 * update the content of multiple zones</dl>
1528 */
1529 public static void contributeAjaxComponentEventResultProcessor(
1530 MappedConfiguration<Class, ComponentEventResultProcessor> configuration)
1531 {
1532 configuration.addInstance(RenderCommand.class, RenderCommandComponentEventResultProcessor.class);
1533 configuration.addInstance(Component.class, AjaxComponentInstanceEventResultProcessor.class);
1534 configuration.addInstance(JSONObject.class, JSONObjectEventResultProcessor.class);
1535 configuration.addInstance(JSONArray.class, JSONArrayEventResultProcessor.class);
1536 configuration.addInstance(StreamResponse.class, StreamResponseResultProcessor.class);
1537 configuration.addInstance(String.class, AjaxPageNameComponentEventResultProcessor.class);
1538 configuration.addInstance(Link.class, AjaxLinkComponentEventResultProcessor.class);
1539 configuration.addInstance(Class.class, AjaxPageClassComponentEventResultProcessor.class);
1540 configuration.addInstance(MultiZoneUpdate.class, MultiZoneUpdateEventResultProcessor.class);
1541 }
1542
1543 /**
1544 * The MasterDispatcher is a chain-of-command of individual Dispatchers, each handling (like a servlet) a particular
1545 * kind of incoming request. <dl> <dt>RootPath</dt> <dd>Renders the start page for the "/" request</dd>
1546 * <dt>Asset</dt> <dd>Provides access to classpath assets</dd> <dt>VirtualAsset</dt> <dd>Provides access to combined
1547 * scripts</dd> <dt>PageRender</dt> <dd>Identifies the {@link org.apache.tapestry5.services.PageRenderRequestParameters}
1548 * and forwards onto {@link PageRenderRequestHandler}</dd> <dt>ComponentEvent</dt> <dd>Identifies the {@link
1549 * ComponentEventRequestParameters} and forwards onto the {@link ComponentEventRequestHandler}</dd> </dl>
1550 */
1551 public static void contributeMasterDispatcher(OrderedConfiguration<Dispatcher> configuration)
1552 {
1553 // Looks for the root path and renders the start page. This is maintained for compatibility
1554 // with earlier versions of Tapestry 5, it is recommended that an Index page be used instead.
1555
1556 configuration.addInstance("RootPath", RootPathDispatcher.class, "before:Asset");
1557
1558 // This goes first because an asset to be streamed may have an file extension, such as
1559 // ".html", that will confuse the later dispatchers.
1560
1561 configuration.addInstance("Asset", AssetDispatcher.class, "before:ComponentEvent");
1562
1563 configuration.addInstance("VirtualAsset", VirtualAssetDispatcher.class, "before:Asset");
1564
1565 configuration.addInstance("ComponentEvent", ComponentEventDispatcher.class, "before:PageRender");
1566
1567 configuration.addInstance("PageRender", PageRenderDispatcher.class);
1568 }
1569
1570 /**
1571 * Contributes a default object renderer for type Object, plus specialized renderers for {@link
1572 * org.apache.tapestry5.services.Request}, {@link org.apache.tapestry5.ioc.Location}, {@link
1573 * org.apache.tapestry5.ComponentResources}, {@link org.apache.tapestry5.EventContext}, List, and Object[].
1574 */
1575 public void contributeObjectRenderer(MappedConfiguration<Class, ObjectRenderer> configuration,
1576
1577 @InjectService("LocationRenderer")
1578 ObjectRenderer locationRenderer,
1579
1580 final TypeCoercer typeCoercer)
1581 {
1582 configuration.add(Object.class, new ObjectRenderer()
1583 {
1584 public void render(Object object, MarkupWriter writer)
1585 {
1586 writer.write(String.valueOf(object));
1587 }
1588 });
1589
1590 configuration.addInstance(Request.class, RequestRenderer.class);
1591
1592 configuration.add(Location.class, locationRenderer);
1593
1594 ObjectRenderer preformatted = new ObjectRenderer<Object>()
1595 {
1596 public void render(Object object, MarkupWriter writer)
1597 {
1598 writer.element("pre");
1599 writer.write(typeCoercer.coerce(object, String.class));
1600 writer.end();
1601 }
1602 };
1603
1604 configuration.add(ClassTransformation.class, preformatted);
1605
1606 configuration.addInstance(List.class, ListRenderer.class);
1607 configuration.addInstance(Object[].class, ObjectArrayRenderer.class);
1608 configuration.addInstance(ComponentResources.class, ComponentResourcesRenderer.class);
1609 configuration.addInstance(EventContext.class, EventContextRenderer.class);
1610 }
1611
1612
1613 /**
1614 * Adds page render filters, each of which provides an {@link org.apache.tapestry5.annotations.Environmental}
1615 * service. Filters often provide {@link org.apache.tapestry5.annotations.Environmental} services needed by
1616 * components as they render. <dl> <dt>DocumentLinker</dt> <dd>Provides {@link org.apache.tapestry5.internal.services.DocumentLinker}
1617 * <dt>RenderSupport</dt> <dd>Provides {@link org.apache.tapestry5.RenderSupport}</dd>
1618 * <dt>InjectDefaultStyleheet</dt> <dd>Injects the default stylesheet</dd></dt> <dt>ClientBehaviorSupport</dt>
1619 * <dd>Provides {@link ClientBehaviorSupport}</dd> <dt>Heartbeat</dt> <dd>Provides {@link
1620 * org.apache.tapestry5.services.Heartbeat}</dd> <dt>DefaultValidationDecorator</dt> <dd>Provides {@link
1621 * org.apache.tapestry5.ValidationDecorator} (as an instance of {@link org.apache.tapestry5.internal.DefaultValidationDecorator})</dd>
1622 * </dl>
1623 */
1624 public void contributeMarkupRenderer(OrderedConfiguration<MarkupRendererFilter> configuration,
1625
1626 @Symbol(SymbolConstants.PRODUCTION_MODE)
1627 final boolean productionMode,
1628
1629 @Path("${tapestry.spacer-image}")
1630 final Asset spacerImage,
1631
1632 @Symbol(SymbolConstants.OMIT_GENERATOR_META)
1633 final boolean omitGeneratorMeta,
1634
1635 @Inject @Symbol(SymbolConstants.TAPESTRY_VERSION)
1636 final String tapestryVersion,
1637
1638 @Symbol(SymbolConstants.COMBINE_SCRIPTS)
1639 final boolean combineScripts,
1640
1641 final SymbolSource symbolSource,
1642
1643 final AssetSource assetSource,
1644
1645 final ClientDataEncoder clientDataEncoder,
1646
1647 final ClientInfrastructure clientInfrastructure)
1648 {
1649 MarkupRendererFilter documentLinker = new MarkupRendererFilter()
1650 {
1651 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1652 {
1653 DocumentLinkerImpl linker = new DocumentLinkerImpl(productionMode,
1654 omitGeneratorMeta,
1655 tapestryVersion,
1656 combineScripts,
1657 request.getContextPath(),
1658 clientDataEncoder);
1659
1660 environment.push(DocumentLinker.class, linker);
1661
1662 renderer.renderMarkup(writer);
1663
1664 environment.pop(DocumentLinker.class);
1665
1666 linker.updateDocument(writer.getDocument());
1667 }
1668 };
1669
1670 MarkupRendererFilter renderSupport = new MarkupRendererFilter()
1671 {
1672 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1673 {
1674 DocumentLinker linker = environment.peekRequired(DocumentLinker.class);
1675
1676 RenderSupportImpl support = new RenderSupportImpl(linker, symbolSource, assetSource,
1677 clientInfrastructure);
1678
1679 environment.push(RenderSupport.class, support);
1680
1681 renderer.renderMarkup(writer);
1682
1683 environment.pop(RenderSupport.class);
1684
1685 support.commit();
1686 }
1687 };
1688
1689 MarkupRendererFilter injectDefaultStylesheet = new MarkupRendererFilter()
1690 {
1691 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1692 {
1693 RenderSupport renderSupport = environment.peek(RenderSupport.class);
1694
1695 for (Asset stylesheet : clientInfrastructure.getStylesheetStack())
1696 {
1697 renderSupport.addStylesheetLink(stylesheet, null);
1698 }
1699
1700 renderer.renderMarkup(writer);
1701 }
1702 };
1703
1704 MarkupRendererFilter clientBehaviorSupport = new MarkupRendererFilter()
1705 {
1706 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1707 {
1708 RenderSupport renderSupport = environment.peekRequired(RenderSupport.class);
1709
1710 ClientBehaviorSupportImpl clientBehaviorSupport = new ClientBehaviorSupportImpl(renderSupport);
1711
1712 environment.push(ClientBehaviorSupport.class, clientBehaviorSupport);
1713
1714 renderer.renderMarkup(writer);
1715
1716 environment.pop(ClientBehaviorSupport.class);
1717
1718 clientBehaviorSupport.commit();
1719 }
1720 };
1721
1722 MarkupRendererFilter heartbeat = new MarkupRendererFilter()
1723 {
1724 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1725 {
1726 Heartbeat heartbeat = new HeartbeatImpl();
1727
1728 heartbeat.begin();
1729
1730 environment.push(Heartbeat.class, heartbeat);
1731
1732 renderer.renderMarkup(writer);
1733
1734 environment.pop(Heartbeat.class);
1735
1736 heartbeat.end();
1737 }
1738 };
1739
1740 MarkupRendererFilter defaultValidationDecorator = new MarkupRendererFilter()
1741 {
1742 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1743 {
1744 ValidationDecorator decorator = new DefaultValidationDecorator(environment, spacerImage, writer);
1745
1746 environment.push(ValidationDecorator.class, decorator);
1747
1748 renderer.renderMarkup(writer);
1749
1750 environment.pop(ValidationDecorator.class);
1751 }
1752 };
1753
1754
1755 configuration.add("DocumentLinker", documentLinker, "before:RenderSupport");
1756 configuration.add("RenderSupport", renderSupport);
1757 configuration.add("InjectDefaultStyleheet", injectDefaultStylesheet, "after:RenderSupport");
1758 configuration.add("ClientBehaviorSupport", clientBehaviorSupport, "after:RenderSupport");
1759 configuration.add("Heartbeat", heartbeat, "after:RenderSupport");
1760 configuration.add("DefaultValidationDecorator", defaultValidationDecorator, "after:Heartbeat");
1761 }
1762
1763
1764 /**
1765 * Contributes {@link PartialMarkupRendererFilter}s used when rendering a partial Ajax response. <dl>
1766 * <dt>DocumentLinker <dd>Provides {@link org.apache.tapestry5.internal.services.DocumentLinker} <dt>
1767 * PageRenderSupport </dt> <dd>Provides {@link org.apache.tapestry5.RenderSupport}</dd>
1768 * <dt>ClientBehaviorSupport</dt> <dd>Provides {@link ClientBehaviorSupport}</dd> <dt>Heartbeat</dt> <dd>Provides
1769 * {@link org.apache.tapestry5.services.Heartbeat}</dd> <dt>DefaultValidationDecorator</dt> <dd>Provides {@link
1770 * org.apache.tapestry5.ValidationDecorator} (as an instance of {@link org.apache.tapestry5.internal.DefaultValidationDecorator})</dd>
1771 * </dl>
1772 */
1773 public void contributePartialMarkupRenderer(OrderedConfiguration<PartialMarkupRendererFilter> configuration,
1774
1775 @Path("${tapestry.spacer-image}")
1776 final Asset spacerImage,
1777
1778 final SymbolSource symbolSource,
1779
1780 final AssetSource assetSource)
1781 {
1782 PartialMarkupRendererFilter documentLinker = new PartialMarkupRendererFilter()
1783 {
1784 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
1785 {
1786 PartialMarkupDocumentLinker linker = new PartialMarkupDocumentLinker();
1787
1788 environment.push(DocumentLinker.class, linker);
1789
1790 renderer.renderMarkup(writer, reply);
1791
1792 environment.pop(DocumentLinker.class);
1793
1794 linker.commit(reply);
1795 }
1796 };
1797
1798
1799 PartialMarkupRendererFilter renderSupport = new PartialMarkupRendererFilter()
1800 {
1801 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
1802 {
1803 String uid = Long.toHexString(System.currentTimeMillis());
1804
1805 String namespace = "-" + uid;
1806
1807 IdAllocator idAllocator = new IdAllocator(namespace);
1808
1809 DocumentLinker linker = environment.peekRequired(DocumentLinker.class);
1810
1811 RenderSupportImpl support = new RenderSupportImpl(linker, symbolSource, assetSource,
1812 idAllocator, new EmptyClientInfrastructure());
1813
1814 environment.push(RenderSupport.class, support);
1815
1816 renderer.renderMarkup(writer, reply);
1817
1818 support.commit();
1819
1820 environment.pop(RenderSupport.class);
1821 }
1822 };
1823
1824 PartialMarkupRendererFilter clientBehaviorSupport = new PartialMarkupRendererFilter()
1825 {
1826 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
1827 {
1828 RenderSupport renderSupport = environment.peekRequired(RenderSupport.class);
1829
1830 ClientBehaviorSupportImpl support = new ClientBehaviorSupportImpl(renderSupport);
1831
1832 environment.push(ClientBehaviorSupport.class, support);
1833
1834 renderer.renderMarkup(writer, reply);
1835
1836 environment.pop(ClientBehaviorSupport.class);
1837
1838 support.commit();
1839 }
1840 };
1841
1842 PartialMarkupRendererFilter heartbeat = new PartialMarkupRendererFilter()
1843 {
1844 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
1845 {
1846 Heartbeat heartbeat = new HeartbeatImpl();
1847
1848 heartbeat.begin();
1849
1850 environment.push(Heartbeat.class, heartbeat);
1851
1852 renderer.renderMarkup(writer, reply);
1853
1854 environment.pop(Heartbeat.class);
1855
1856 heartbeat.end();
1857 }
1858 };
1859
1860 PartialMarkupRendererFilter defaultValidationDecorator = new PartialMarkupRendererFilter()
1861 {
1862 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
1863 {
1864 ValidationDecorator decorator = new DefaultValidationDecorator(environment, spacerImage, writer);
1865
1866 environment.push(ValidationDecorator.class, decorator);
1867
1868 renderer.renderMarkup(writer, reply);
1869
1870 environment.pop(ValidationDecorator.class);
1871 }
1872 };
1873
1874
1875 configuration.add("DocumentLinker", documentLinker, "before:RenderSupport");
1876 configuration.add("RenderSupport", renderSupport);
1877 configuration.add("ClientBehaviorSupport", clientBehaviorSupport, "after:RenderSupport");
1878 configuration.add("Heartbeat", heartbeat, "after:RenderSupport");
1879 configuration.add("DefaultValidationDecorator", defaultValidationDecorator, "after:Heartbeat");
1880 }
1881
1882 /**
1883 * Contributes several strategies: <dl> <dt>session <dd>Values are stored in the {@link Session} <dt>flash
1884 * <dd>Values are stored in the {@link Session}, until the next request (for the page) <dt>client <dd>Values are
1885 * encoded into URLs (or hidden form fields) </dl>
1886 */
1887 public void contributePersistentFieldManager(MappedConfiguration<String, PersistentFieldStrategy> configuration,
1888
1889 Request request,
1890
1891 @InjectService("ClientPersistentFieldStrategy")
1892 PersistentFieldStrategy clientStrategy)
1893 {
1894 configuration.add(PersistenceConstants.SESSION, new SessionPersistentFieldStrategy(request));
1895 configuration.add(PersistenceConstants.FLASH, new FlashPersistentFieldStrategy(request));
1896 configuration.add(PersistenceConstants.CLIENT, clientStrategy);
1897 }
1898
1899 /**
1900 * Contributes org/apache/tapestry5/internal/ValidationMessages as "Default", ordered first.
1901 */
1902 public void contributeValidationMessagesSource(OrderedConfiguration<String> configuration)
1903 {
1904 configuration.add("Default", "org/apache/tapestry5/internal/ValidationMessages", "before:*");
1905 }
1906
1907 public static ValueEncoderSource buildValueEncoderSource(Map<Class, ValueEncoderFactory> configuration,
1908 @ComponentClasses InvalidationEventHub hub)
1909 {
1910 ValueEncoderSourceImpl service = new ValueEncoderSourceImpl(configuration);
1911
1912 hub.addInvalidationListener(service);
1913
1914 return service;
1915 }
1916
1917 /**
1918 * Contributes {@link ValueEncoderFactory}s for types: <ul> <li>Object <li>String <li>Enum </ul>
1919 */
1920 @SuppressWarnings("unchecked")
1921 public static void contributeValueEncoderSource(MappedConfiguration<Class, ValueEncoderFactory> configuration)
1922 {
1923 configuration.addInstance(Object.class, TypeCoercedValueEncoderFactory.class);
1924 configuration.add(String.class, GenericValueEncoderFactory.create(new StringValueEncoder()));
1925 configuration.add(Enum.class, new EnumValueEncoderFactory());
1926 }
1927
1928
1929 /**
1930 * Contributes a single filter, "Secure", which checks for non-secure requests that access secure pages.
1931 */
1932 public void contributePageRenderRequestHandler(OrderedConfiguration<PageRenderRequestFilter> configuration,
1933 final RequestSecurityManager securityManager)
1934 {
1935 PageRenderRequestFilter secureFilter = new PageRenderRequestFilter()
1936 {
1937 public void handle(PageRenderRequestParameters parameters, PageRenderRequestHandler handler) throws
1938 IOException
1939 {
1940
1941 if (securityManager.checkForInsecureRequest(parameters.getLogicalPageName())) return;
1942
1943 handler.handle(parameters);
1944 }
1945 };
1946
1947 configuration.add("Secure", secureFilter);
1948 }
1949
1950
1951 /**
1952 * Configures the extensions that will require a digest to be downloaded via the asset dispatcher. Most resources
1953 * are "safe", they don't require a digest. For unsafe resources, the digest is incorporated into the URL to ensure
1954 * that the client side isn't just "fishing".
1955 * <p/>
1956 * The extensions must be all lower case.
1957 * <p/>
1958 * This contributes "class" and "tml" (the template extension).
1959 *
1960 * @param configuration collection of extensions
1961 */
1962 public static void contributeResourceDigestGenerator(Configuration<String> configuration)
1963 {
1964 // Java class files always require a digest.
1965 configuration.add("class");
1966
1967 // Likewise, we don't want people fishing for templates.
1968 configuration.add(InternalConstants.TEMPLATE_EXTENSION);
1969 }
1970
1971 public static void contributeTemplateParser(MappedConfiguration<String, URL> config)
1972 {
1973 // Any class inside the internal module would do. Or we could move all these
1974 // files to o.a.t.services.
1975
1976 Class c = UpdateListenerHubImpl.class;
1977
1978 config.add("-//W3C//DTD XHTML 1.0 Strict//EN", c.getResource("xhtml1-strict.dtd"));
1979 config.add("-//W3C//DTD XHTML 1.0 Transitional//EN", c
1980 .getResource("xhtml1-transitional.dtd"));
1981 config.add("-//W3C//DTD XHTML 1.0 Frameset//EN", c.getResource("xhtml1-frameset.dtd"));
1982 config.add("-//W3C//DTD HTML 4.01//EN", c.getResource("xhtml1-strict.dtd"));
1983 config.add("-//W3C//DTD HTML 4.01 Transitional//EN", c
1984 .getResource("xhtml1-transitional.dtd"));
1985 config.add("-//W3C//DTD HTML 4.01 Frameset//EN", c.getResource("xhtml1-frameset.dtd"));
1986 config.add("-//W3C//ENTITIES Latin 1 for XHTML//EN", c.getResource("xhtml-lat1.ent"));
1987 config.add("-//W3C//ENTITIES Symbols for XHTML//EN", c.getResource("xhtml-symbol.ent"));
1988 config.add("-//W3C//ENTITIES Special for XHTML//EN", c.getResource("xhtml-special.ent"));
1989 }
1990
1991 /**
1992 * Contributes factory defaults that may be overridden.
1993 */
1994 public static void contributeFactoryDefaults(MappedConfiguration<String, String> configuration)
1995 {
1996 // Remember this is request-to-request time, presumably it'll take the developer more than
1997 // one second to make a change, save it, and switch back to the browser.
1998
1999 configuration.add(SymbolConstants.FILE_CHECK_INTERVAL, "1 s");
2000 configuration.add(SymbolConstants.FILE_CHECK_UPDATE_TIMEOUT, "50 ms");
2001
2002 // This should be overridden for particular applications. These are the locales for
2003 // which we have (at least some) localized messages.
2004 configuration.add(SymbolConstants.SUPPORTED_LOCALES,
2005 "en,it,es,zh_CN,pt_PT,de,ru,hr,fi_FI,sv_SE,fr_FR,da,pt_BR,ja,el");
2006
2007 configuration.add(SymbolConstants.TAPESTRY_VERSION,
2008 VersionUtils.readVersionNumber(
2009 "META-INF/maven/org.apache.tapestry/tapestry-core/pom.properties"));
2010
2011 configuration.add("tapestry.default-cookie-max-age", "7 d");
2012
2013 configuration.add("tapestry.start-page-name", "start");
2014
2015 configuration.add("tapestry.default-stylesheet", "classpath:/org/apache/tapestry5/default.css");
2016 configuration.add("tapestry.spacer-image", "classpath:/org/apache/tapestry5/spacer.gif");
2017
2018 configuration.add("tapestry.page-pool.soft-limit", "5");
2019 configuration.add("tapestry.page-pool.soft-wait", "10 ms");
2020 configuration.add("tapestry.page-pool.hard-limit", "20");
2021 configuration.add("tapestry.page-pool.active-window", "10 m");
2022
2023 configuration.add(SymbolConstants.SUPPRESS_REDIRECT_FROM_ACTION_REQUESTS, "false");
2024
2025 configuration.add(SymbolConstants.FORCE_ABSOLUTE_URIS, "false");
2026
2027 configuration.add(SymbolConstants.PRODUCTION_MODE, "true");
2028
2029 configuration.add(SymbolConstants.COMPRESS_WHITESPACE, "true");
2030
2031 configuration.add(MetaDataConstants.SECURE_PAGE, "false");
2032
2033 configuration.add(SymbolConstants.FORM_CLIENT_LOGIC_ENABLED, "true");
2034
2035 // This is designed to make it easy to keep synchronized with script.aculo.ous. As we
2036 // support a new version, we create a new folder, and update the path entry. We can then
2037 // delete the old version folder (or keep it around). This should be more manageable than
2038 // overwriting the local copy with updates (it's too easy for files deleted between scriptaculous
2039 // releases to be accidentally left lying around). There's also a ClasspathAliasManager
2040 // contribution based on the path.
2041
2042 configuration.add("tapestry.scriptaculous", "classpath:${tapestry.scriptaculous.path}");
2043 configuration.add("tapestry.scriptaculous.path", "org/apache/tapestry5/scriptaculous_1_8_2");
2044
2045 // Likewise for WebFX DatePicker, currently version 1.0.6
2046
2047 configuration.add("tapestry.datepicker.path", "org/apache/tapestry5/datepicker_106");
2048 configuration.add("tapestry.datepicker", "classpath:${tapestry.datepicker.path}");
2049
2050 configuration.add("tapestry.blackbird.path", "org/apache/tapestry5/blackbird_1_0");
2051 configuration.add("tapestry.blackbird", "classpath:${tapestry.blackbird.path}");
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 Random random = new Random(System.currentTimeMillis());
2067
2068 configuration.add(SymbolConstants.APPLICATION_VERSION, Long.toHexString(random.nextLong()));
2069
2070 configuration.add(SymbolConstants.OMIT_GENERATOR_META, "false");
2071 configuration.add(SymbolConstants.GZIP_COMPRESSION_ENABLED, "true");
2072
2073 String matchProductionMode = String.format("${%s}", SymbolConstants.PRODUCTION_MODE);
2074
2075 configuration.add(SymbolConstants.SECURE_ENABLED, matchProductionMode);
2076 configuration.add(SymbolConstants.COMBINE_SCRIPTS, matchProductionMode);
2077
2078 configuration.add(SymbolConstants.ENCODE_LOCALE_INTO_PATH, "true");
2079 }
2080
2081
2082 /**
2083 * Adds content types: <dl> <dt>css</dt> <dd>text/css</dd> <dt>js</dt> <dd>text/javascript</dd> <dt>jpg, jpeg</dt>
2084 * <dd>image/jpeg</dd> <dt>gif</dt> <dd>image/gif</dd> <dt>png</dtt> <dd>image/png</dd>
2085 * <p/>
2086 * </dl>
2087 */
2088 public void contributeResourceStreamer(MappedConfiguration<String, String> configuration)
2089 {
2090 configuration.add("css", "text/css");
2091 configuration.add("js", "text/javascript");
2092 configuration.add("gif", "image/gif");
2093 configuration.add("jpg", "image/jpeg");
2094 configuration.add("jpeg", "image/jpeg");
2095 configuration.add("png", "image/png");
2096 }
2097
2098 /**
2099 * Adds a listener to the {@link org.apache.tapestry5.internal.services.ComponentInstantiatorSource} that clears the
2100 * {@link PropertyAccess} and {@link TypeCoercer} caches on a class loader invalidation. In addition, forces the
2101 * realization of {@link ComponentClassResolver} at startup.
2102 */
2103 public void contributeApplicationInitializer(OrderedConfiguration<ApplicationInitializerFilter> configuration,
2104 final TypeCoercer typeCoercer,
2105 final ComponentClassResolver componentClassResolver,
2106 @ComponentClasses final InvalidationEventHub invalidationEventHub,
2107 final @Autobuild RestoreDirtySessionObjects restoreDirtySessionObjects)
2108 {
2109 final InvalidationListener listener = new InvalidationListener()
2110 {
2111 public void objectWasInvalidated()
2112 {
2113 propertyAccess.clearCache();
2114
2115 typeCoercer.clearCache();
2116 }
2117 };
2118
2119 ApplicationInitializerFilter clearCaches = new ApplicationInitializerFilter()
2120 {
2121 public void initializeApplication(Context context, ApplicationInitializer initializer)
2122 {
2123 // Snuck in here is the logic to clear the PropertyAccess service's cache whenever
2124 // the component class loader is invalidated.
2125
2126 invalidationEventHub.addInvalidationListener(listener);
2127
2128 endOfRequestEventHub.addEndOfRequestListener(restoreDirtySessionObjects);
2129
2130 // Perform other pending initialization
2131
2132 initializer.initializeApplication(context);
2133
2134 // We don't care about the result, but this forces a load of the service
2135 // at application startup, rather than on first request.
2136
2137 componentClassResolver.isPageName("ForceLoadAtStartup");
2138 }
2139 };
2140
2141 configuration.add("ClearCachesOnInvalidation", clearCaches);
2142 }
2143
2144
2145 /**
2146 * Contributes filters: <dl> <dt>Ajax</dt> <dd>Determines if the request is Ajax oriented, and redirects to an
2147 * alternative handler if so</dd> <dt>ImmediateRender</dt> <dd>When {@linkplain
2148 * SymbolConstants#SUPPRESS_REDIRECT_FROM_ACTION_REQUESTS immediate action response rendering} is enabled, generates
2149 * the markup response (instead of a page redirect response, which is the normal behavior) </dd> <dt>Secure</dt>
2150 * <dd>Sends a redirect if an non-secure request accesses a secure page</dd></dl>
2151 */
2152 public void contributeComponentEventRequestHandler(OrderedConfiguration<ComponentEventRequestFilter> configuration,
2153 final RequestSecurityManager requestSecurityManager,
2154 @Ajax ComponentEventRequestHandler ajaxHandler
2155 )
2156 {
2157 ComponentEventRequestFilter secureFilter = new ComponentEventRequestFilter()
2158 {
2159 public void handle(ComponentEventRequestParameters parameters, ComponentEventRequestHandler handler)
2160 throws IOException
2161 {
2162 if (requestSecurityManager.checkForInsecureRequest(parameters.getActivePageName())) return;
2163
2164 handler.handle(parameters);
2165 }
2166 };
2167
2168 configuration.add("Ajax", new AjaxFilter(request, ajaxHandler));
2169
2170 configuration.addInstance("ImmediateRender", ImmediateActionRenderResponseFilter.class);
2171
2172 configuration.add("Secure", secureFilter, "before:Ajax");
2173 }
2174
2175
2176 /**
2177 * Contributes strategies accessible via the {@link NullFieldStrategySource} service.
2178 * <p/>
2179 * <dl> <dt>default</dt> <dd>Does nothing, nulls stay null.</dd> <dt>zero</dt> <dd>Null values are converted to
2180 * zero.</dd> </dl>
2181 */
2182 public static void contributeNullFieldStrategySource(MappedConfiguration<String, NullFieldStrategy> configuration)
2183 {
2184 configuration.add("default", new DefaultNullFieldStrategy());
2185 configuration.add("zero", new ZeroNullFieldStrategy());
2186 }
2187
2188
2189 /**
2190 * Determines positioning of hidden fields relative to other elements (this is needed by {@link
2191 * org.apache.tapestry5.corelib.components.FormFragment} and others.
2192 * <p/>
2193 * For elements input, select, textarea and label the hidden field is positioned after.
2194 * <p/>
2195 * For elements p, div, li and td, the hidden field is positioned inside.
2196 */
2197 public static void contributeHiddenFieldLocationRules(
2198 MappedConfiguration<String, RelativeElementPosition> configuration)
2199 {
2200 configuration.add("input", RelativeElementPosition.AFTER);
2201 configuration.add("select", RelativeElementPosition.AFTER);
2202 configuration.add("textarea", RelativeElementPosition.AFTER);
2203 configuration.add("label", RelativeElementPosition.AFTER);
2204
2205 configuration.add("p", RelativeElementPosition.INSIDE);
2206 configuration.add("div", RelativeElementPosition.INSIDE);
2207 configuration.add("td", RelativeElementPosition.INSIDE);
2208 configuration.add("li", RelativeElementPosition.INSIDE);
2209 }
2210
2211
2212 /**
2213 * @since 5.1.0.0
2214 */
2215 public static LinkCreationHub buildLinkCreationHub(LinkSource source)
2216 {
2217 return source.getLinkCreationHub();
2218 }
2219
2220 /**
2221 * @since 5.1.0.0
2222 */
2223 @Marker(ComponentClasses.class)
2224 public static InvalidationEventHub buildComponentClassesInvalidationEventHub(ComponentInstantiatorSource source)
2225 {
2226 return source.getInvalidationEventHub();
2227 }
2228
2229 /**
2230 * @since 5.1.0.0
2231 */
2232 @Marker(ComponentTemplates.class)
2233 public static InvalidationEventHub buildComponentTemplatesInvalidationEventHub(
2234 ComponentTemplateSource templateSource)
2235 {
2236 return templateSource.getInvalidationEventHub();
2237 }
2238
2239 /**
2240 * @since 5.1.0.0
2241 */
2242 @Marker(ComponentMessages.class)
2243 public static InvalidationEventHub buildComponentMessagesInvalidationEventHub(
2244 ComponentMessagesSource messagesSource)
2245 {
2246 return messagesSource.getInvalidationEventHub();
2247 }
2248
2249 @Scope(ScopeConstants.PERTHREAD)
2250 public Environment buildEnvironment(PerthreadManager perthreadManager)
2251 {
2252 EnvironmentImpl service = new EnvironmentImpl();
2253
2254 perthreadManager.addThreadCleanupListener(service);
2255
2256 return service;
2257 }
2258
2259 /**
2260 * The master Sessi`onPesistedObjectAnalyzer.
2261 *
2262 * @since 5.1.0.0
2263 */
2264 @Marker(Primary.class)
2265 public SessionPersistedObjectAnalyzer buildSessionPersistedObjectAnalyzer(
2266 Map<Class, SessionPersistedObjectAnalyzer> configuration)
2267 {
2268 return strategyBuilder.build(SessionPersistedObjectAnalyzer.class, configuration);
2269 }
2270
2271 /**
2272 * Identifies String, Number and Boolean as immutable objects, a catch-all handler for Object (that understands
2273 * {@link org.apache.tapestry5.annotations.ImmutableSessionPersistedObject}, and handlers for {@link
2274 * org.apache.tapestry5.OptimizedSessionPersistedObject} and {@link org.apache.tapestry5.OptimizedApplicationStateObject}.
2275 *
2276 * @since 5.1.0.0
2277 */
2278 public static void contributeSessionPersistedObjectAnalyzer(
2279 MappedConfiguration<Class, SessionPersistedObjectAnalyzer> configuration)
2280 {
2281 configuration.add(Object.class, new DefaultSessionPersistedObjectAnalyzer());
2282
2283 SessionPersistedObjectAnalyzer<Object> immutable = new SessionPersistedObjectAnalyzer<Object>()
2284 {
2285 public boolean isDirty(Object object)
2286 {
2287 return false;
2288 }
2289 };
2290
2291 configuration.add(String.class, immutable);
2292 configuration.add(Number.class, immutable);
2293 configuration.add(Boolean.class, immutable);
2294
2295 configuration.add(OptimizedSessionPersistedObject.class, new OptimizedSessionPersistedObjectAnalyzer());
2296 configuration.add(OptimizedApplicationStateObject.class, new OptimizedApplicationStateObjectAnalyzer());
2297 }
2298
2299 /**
2300 * Contibutions are content types that do not benefit from compression. Adds the following content types: <ul>
2301 * <li>image/jpeg</li> <li>image/gif <li>image/png <li>application/json (see https://issues.apache.org/jira/browse/TAP5-469)</ul>
2302 *
2303 * @since 5.1.0.0
2304 */
2305 public static void contributeResponseCompressionAnalyzer(Configuration<String> configuration)
2306 {
2307 configuration.add("image/jpeg");
2308 configuration.add("image/gif");
2309 configuration.add("image/png");
2310 configuration.add("application/json");
2311 }
2312
2313 /**
2314 * @since 5.1.1.0
2315 */
2316 @Marker(Primary.class)
2317 public StackTraceElementAnalyzer buildMasterStackTraceElementAnalyzer(List<StackTraceElementAnalyzer> configuration)
2318 {
2319 return chainBuilder.build(StackTraceElementAnalyzer.class, configuration);
2320 }
2321
2322 /**
2323 * Adds two analyzers: <dl> <dt>Application</dt> <dd>Checks for classes in the application package</dd>
2324 * <dt>Proxies</dt> <dd>Checks for classes that appear to be generated proxies.</dd> <dt>SunReflect</dt> <dd>Checks
2325 * for <code>sun.reflect</code> (which are omitted)</dl>
2326 *
2327 * @since 5.1.0.0
2328 */
2329 public static void contributeMasterStackTraceElementAnalyzer(
2330 OrderedConfiguration<StackTraceElementAnalyzer> configuration)
2331 {
2332 configuration.addInstance("Application", ApplicationStackTraceElementAnalyzer.class);
2333 configuration.addInstance("Proxies", ProxiesStackTraceElementAnalyzer.class, "before:Application");
2334 configuration.add("SunReflect",
2335 new PrefixCheckStackTraceElementAnalyzer(StackTraceElementClassConstants.OMITTED,
2336 "sun.reflect."
2337 ));
2338 }
2339
2340 /**
2341 * Advises the {@link org.apache.tapestry5.internal.services.ComponentMessagesSource} service so that the creation
2342 * of {@link org.apache.tapestry5.ioc.Messages} instances can be deferred.
2343 *
2344 * @since 5.1.0.0
2345 */
2346 @Match("ComponentMessagesSource")
2347 public static void adviseLazy(LazyAdvisor advisor, MethodAdviceReceiver receiver)
2348 {
2349 advisor.addLazyMethodInvocationAdvice(receiver);
2350 }
2351
2352 /**
2353 * @since 5.1.0.0
2354 */
2355 public ComponentRequestHandler buildComponentRequestHandler(
2356 List<ComponentRequestFilter> configuration,
2357
2358 @Autobuild ComponentRequestHandlerTerminator terminator,
2359
2360 Logger logger)
2361 {
2362 return pipelineBuilder.build(logger, ComponentRequestHandler.class, ComponentRequestFilter.class,
2363 configuration, terminator);
2364 }
2365
2366 /**
2367 * @throws Exception
2368 * @since 5.1.0.2
2369 */
2370 public static ComponentEventLinkEncoder decorateComponentEventLinkEncoder(
2371 ComponentEventLinkEncoder encoder, URLRewriter urlRewriter,
2372 Request request, HttpServletRequest httpServletRequest, Response response,
2373 AspectDecorator aspectDecorator) throws Exception
2374 {
2375
2376 // no rules, no link rewriting.
2377 if (!urlRewriter.hasLinkRules())
2378 {
2379 return null;
2380 }
2381
2382 ComponentEventLinkEncoderMethodAdvice pageLinkAdvice =
2383 new ComponentEventLinkEncoderMethodAdvice(urlRewriter, request, httpServletRequest, response, true);
2384
2385 ComponentEventLinkEncoderMethodAdvice eventLinkAdvice =
2386 new ComponentEventLinkEncoderMethodAdvice(urlRewriter, request, httpServletRequest, response, false);
2387
2388
2389 Class<ComponentEventLinkEncoder> clasz = ComponentEventLinkEncoder.class;
2390
2391 Method createPageRenderLink =
2392 clasz.getMethod("createPageRenderLink", PageRenderRequestParameters.class);
2393
2394 Method createComponentEventLink =
2395 clasz.getMethod("createComponentEventLink", ComponentEventRequestParameters.class, boolean.class);
2396
2397
2398 final AspectInterceptorBuilder<ComponentEventLinkEncoder> builder =
2399 aspectDecorator.createBuilder(clasz, encoder, "Link rewriting");
2400
2401 builder.adviseMethod(createComponentEventLink, eventLinkAdvice);
2402 builder.adviseMethod(createPageRenderLink, pageLinkAdvice);
2403
2404 return builder.build();
2405
2406 }
2407
2408 }