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