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 contributions of special factories for some constants, each
1238 * contributed factory may return a
1239 * binding if applicable, or null otherwise
1240 */
1241 public BindingFactory buildPropBindingFactory(List<BindingFactory> configuration, @Autobuild
1242 PropBindingFactory service)
1243 {
1244 configuration.add(service);
1245
1246 return chainBuilder.build(BindingFactory.class, configuration);
1247 }
1248
1249 public static MetaDataLocator buildMetaDataLocator(@Autobuild
1250 MetaDataLocatorImpl service, @ComponentClasses
1251 InvalidationEventHub hub)
1252 {
1253 hub.addInvalidationListener(service);
1254
1255 return service;
1256 }
1257
1258 public PersistentFieldStrategy buildClientPersistentFieldStrategy(LinkCreationHub linkCreationHub, @Autobuild
1259 ClientPersistentFieldStrategy service)
1260 {
1261 linkCreationHub.addListener(service);
1262
1263 return service;
1264 }
1265
1266 /**
1267 * Builds a proxy to the current {@link org.apache.tapestry5.RenderSupport} inside this thread's
1268 * {@link org.apache.tapestry5.services.Environment}.
1269 */
1270 public RenderSupport buildRenderSupport()
1271 {
1272 return environmentalBuilder.build(RenderSupport.class);
1273 }
1274
1275 /**
1276 * Builds a proxy to the current {@link JavaScriptSupport} inside this thread's {@link Environment}.
1277 *
1278 * @since 5.2.0
1279 */
1280 public JavaScriptSupport buildJavaScriptSupport()
1281 {
1282 return environmentalBuilder.build(JavaScriptSupport.class);
1283 }
1284
1285 /**
1286 * Builds a proxy to the current {@link org.apache.tapestry5.services.ClientBehaviorSupport} inside this
1287 * thread's {@link org.apache.tapestry5.services.Environment}.
1288 *
1289 * @since 5.1.0.1
1290 */
1291
1292 public ClientBehaviorSupport buildClientBehaviorSupport()
1293 {
1294 return environmentalBuilder.build(ClientBehaviorSupport.class);
1295 }
1296
1297 /**
1298 * Builds a proxy to the current {@link org.apache.tapestry5.services.FormSupport} inside this
1299 * thread's {@link org.apache.tapestry5.services.Environment}.
1300 */
1301 public FormSupport buildFormSupport()
1302 {
1303 return environmentalBuilder.build(FormSupport.class);
1304 }
1305
1306 /**
1307 * Allows the exact steps in the component class transformation process to
1308 * be defined.
1309 */
1310 @Marker(Primary.class)
1311 public ComponentClassTransformWorker2 buildComponentClassTransformWorker(
1312 List<ComponentClassTransformWorker2> configuration)
1313
1314 {
1315 return chainBuilder.build(ComponentClassTransformWorker2.class, configuration);
1316 }
1317
1318 /**
1319 * Analyzes properties to determine the data types, used to
1320 * {@linkplain #provideDefaultBeanBlocks(org.apache.tapestry5.ioc.Configuration)} locale
1321 * display and edit blocks for properties. The default behaviors
1322 * look for a {@link org.apache.tapestry5.beaneditor.DataType} annotation
1323 * before deriving the data type from the property type.
1324 */
1325 @Marker(Primary.class)
1326 public DataTypeAnalyzer buildDataTypeAnalyzer(List<DataTypeAnalyzer> configuration)
1327 {
1328 return chainBuilder.build(DataTypeAnalyzer.class, configuration);
1329 }
1330
1331 /**
1332 * A chain of command for providing values for {@link Inject}-ed fields in
1333 * component classes. The service's
1334 * configuration can be extended to allow for different automatic injections
1335 * (based on some combination of field
1336 * type and field name).
1337 * <p/>
1338 * Note that contributions to this service may be old-style {@link InjectionProvider}, which will
1339 * be coerced to {@link InjectionProvider2}.
1340 */
1341 public InjectionProvider2 buildInjectionProvider(List<InjectionProvider2> configuration)
1342 {
1343 return chainBuilder.build(InjectionProvider2.class, configuration);
1344 }
1345
1346 /**
1347 * Initializes the application, using a pipeline of {@link org.apache.tapestry5.services.ApplicationInitializer}s.
1348 */
1349 @Marker(Primary.class)
1350 public ApplicationInitializer buildApplicationInitializer(Logger logger,
1351 List<ApplicationInitializerFilter> configuration)
1352 {
1353 ApplicationInitializer terminator = new ApplicationInitializerTerminator();
1354
1355 return pipelineBuilder.build(logger, ApplicationInitializer.class, ApplicationInitializerFilter.class,
1356 configuration, terminator);
1357 }
1358
1359 public HttpServletRequestHandler buildHttpServletRequestHandler(Logger logger,
1360
1361 List<HttpServletRequestFilter> configuration,
1362
1363 @Primary
1364 RequestHandler handler,
1365
1366 @Symbol(SymbolConstants.CHARSET)
1367 String applicationCharset,
1368
1369 TapestrySessionFactory sessionFactory)
1370 {
1371 HttpServletRequestHandler terminator = new HttpServletRequestHandlerTerminator(handler, applicationCharset,
1372 sessionFactory);
1373
1374 return pipelineBuilder.build(logger, HttpServletRequestHandler.class, HttpServletRequestFilter.class,
1375 configuration, terminator);
1376 }
1377
1378 @Marker(Primary.class)
1379 public RequestHandler buildRequestHandler(Logger logger, List<RequestFilter> configuration,
1380
1381 @Primary
1382 Dispatcher masterDispatcher)
1383 {
1384 RequestHandler terminator = new RequestHandlerTerminator(masterDispatcher);
1385
1386 return pipelineBuilder.build(logger, RequestHandler.class, RequestFilter.class, configuration, terminator);
1387 }
1388
1389 public ServletApplicationInitializer buildServletApplicationInitializer(Logger logger,
1390 List<ServletApplicationInitializerFilter> configuration,
1391
1392 @Primary
1393 ApplicationInitializer initializer)
1394 {
1395 ServletApplicationInitializer terminator = new ServletApplicationInitializerTerminator(initializer);
1396
1397 return pipelineBuilder.build(logger, ServletApplicationInitializer.class,
1398 ServletApplicationInitializerFilter.class, configuration, terminator);
1399 }
1400
1401 /**
1402 * The component event result processor used for normal component requests.
1403 */
1404 @Marker(
1405 {Primary.class, Traditional.class})
1406 public ComponentEventResultProcessor buildComponentEventResultProcessor(
1407 Map<Class, ComponentEventResultProcessor> configuration)
1408 {
1409 return constructComponentEventResultProcessor(configuration);
1410 }
1411
1412 /**
1413 * The component event result processor used for Ajax-oriented component
1414 * requests.
1415 */
1416 @Marker(Ajax.class)
1417 public ComponentEventResultProcessor buildAjaxComponentEventResultProcessor(
1418 Map<Class, ComponentEventResultProcessor> configuration)
1419 {
1420 return constructComponentEventResultProcessor(configuration);
1421 }
1422
1423 private ComponentEventResultProcessor constructComponentEventResultProcessor(
1424 Map<Class, ComponentEventResultProcessor> configuration)
1425 {
1426 Set<Class> handledTypes = CollectionFactory.newSet(configuration.keySet());
1427
1428 // A slight hack!
1429
1430 configuration.put(Object.class, new ObjectComponentEventResultProcessor(handledTypes));
1431
1432 StrategyRegistry<ComponentEventResultProcessor> registry = StrategyRegistry.newInstance(
1433 ComponentEventResultProcessor.class, configuration);
1434
1435 return strategyBuilder.build(registry);
1436 }
1437
1438 /**
1439 * The default data type analyzer is the final analyzer consulted and
1440 * identifies the type entirely pased on the
1441 * property type, working against its own configuration (mapping property
1442 * type class to data type).
1443 */
1444 public static DataTypeAnalyzer buildDefaultDataTypeAnalyzer(@Autobuild
1445 DefaultDataTypeAnalyzer service, @ComponentClasses
1446 InvalidationEventHub hub)
1447 {
1448 hub.addInvalidationListener(service);
1449
1450 return service;
1451 }
1452
1453 public static TranslatorSource buildTranslatorSource(Map<Class, Translator> configuration,
1454 TranslatorAlternatesSource alternatesSource, @ComponentClasses
1455 InvalidationEventHub hub)
1456 {
1457 TranslatorSourceImpl service = new TranslatorSourceImpl(configuration,
1458 alternatesSource.getTranslatorAlternates());
1459
1460 hub.addInvalidationListener(service);
1461
1462 return service;
1463 }
1464
1465 @Marker(Primary.class)
1466 public ObjectRenderer buildObjectRenderer(Map<Class, ObjectRenderer> configuration)
1467 {
1468 return strategyBuilder.build(ObjectRenderer.class, configuration);
1469 }
1470
1471 /**
1472 * Returns a {@link org.apache.tapestry5.ioc.services.ClassFactory} that can
1473 * be used to create extra classes around
1474 * component classes. This ClassFactory will be cleared whenever an
1475 * underlying component class is discovered to have
1476 * changed. Use of this class factory implies that your code will become
1477 * aware of this (if necessary) to discard any
1478 * cached object (alas, this currently involves dipping into the internals
1479 * side to register for the correct
1480 * notifications). Failure to properly clean up can result in really nasty
1481 * PermGen space memory leaks.
1482 */
1483 @Marker(ComponentLayer.class)
1484 public ClassFactory buildComponentClassFactory(ComponentInstantiatorSource source)
1485 {
1486 return shadowBuilder.build(source, "classFactory", ClassFactory.class);
1487 }
1488
1489 /**
1490 * Returns a {@link PlasticProxyFactory} that can be used to create extra classes around component classes. This
1491 * factory will be cleared whenever an underlying component class is discovered to have changed. Use of this
1492 * factory implies that your code will become aware of this (if necessary) to discard any cached object (alas,
1493 * this currently involves dipping into the internals side to register for the correct notifications). Failure to
1494 * properly clean up can result in really nasty PermGen space memory leaks.
1495 */
1496 @Marker(ComponentLayer.class)
1497 public PlasticProxyFactory buildComponentProxyFactory(ComponentInstantiatorSource source)
1498 {
1499 return shadowBuilder.build(source, "proxyFactory", PlasticProxyFactory.class);
1500 }
1501
1502 /**
1503 * Ordered contributions to the MasterDispatcher service allow different URL
1504 * matching strategies to occur.
1505 */
1506 @Marker(Primary.class)
1507 public Dispatcher buildMasterDispatcher(List<Dispatcher> configuration)
1508 {
1509 return chainBuilder.build(Dispatcher.class, configuration);
1510 }
1511
1512 /**
1513 * Builds a shadow of the RequestGlobals.request property. Note again that
1514 * the shadow can be an ordinary singleton,
1515 * even though RequestGlobals is perthread.
1516 */
1517 public Request buildRequest()
1518 {
1519 return shadowBuilder.build(requestGlobals, "request", Request.class);
1520 }
1521
1522 /**
1523 * Builds a shadow of the RequestGlobals.HTTPServletRequest property.
1524 * Generally, you should inject the {@link Request} service instead, as
1525 * future version of Tapestry may operate beyond just the servlet API.
1526 */
1527 public HttpServletRequest buildHttpServletRequest()
1528 {
1529 return shadowBuilder.build(requestGlobals, "HTTPServletRequest", HttpServletRequest.class);
1530 }
1531
1532 /**
1533 * @since 5.1.0.0
1534 */
1535 public HttpServletResponse buildHttpServletResponse()
1536 {
1537 return shadowBuilder.build(requestGlobals, "HTTPServletResponse", HttpServletResponse.class);
1538 }
1539
1540 /**
1541 * Builds a shadow of the RequestGlobals.response property. Note again that
1542 * the shadow can be an ordinary singleton,
1543 * even though RequestGlobals is perthread.
1544 */
1545 public Response buildResponse()
1546 {
1547 return shadowBuilder.build(requestGlobals, "response", Response.class);
1548 }
1549
1550 /**
1551 * The MarkupRenderer service is used to render a full page as markup.
1552 * Supports an ordered configuration of {@link org.apache.tapestry5.services.MarkupRendererFilter}s.
1553 */
1554 public MarkupRenderer buildMarkupRenderer(Logger logger, @Autobuild
1555 MarkupRendererTerminator terminator, List<MarkupRendererFilter> configuration)
1556 {
1557 return pipelineBuilder.build(logger, MarkupRenderer.class, MarkupRendererFilter.class, configuration,
1558 terminator);
1559 }
1560
1561 /**
1562 * A wrapper around {@link org.apache.tapestry5.internal.services.PageRenderQueue} used for
1563 * partial page renders.
1564 * Supports an ordered configuration of {@link org.apache.tapestry5.services.PartialMarkupRendererFilter}s.
1565 *
1566 * @see #contributePartialMarkupRenderer(org.apache.tapestry5.ioc.OrderedConfiguration, org.apache.tapestry5.Asset, org.apache.tapestry5.services.javascript.JavaScriptStackSource, org.apache.tapestry5.internal.services.javascript.JavaScriptStackPathConstructor, org.apache.tapestry5.ioc.services.SymbolSource, AssetSource)
1567 */
1568 public PartialMarkupRenderer buildPartialMarkupRenderer(Logger logger,
1569 List<PartialMarkupRendererFilter> configuration, @Autobuild
1570 PartialMarkupRendererTerminator terminator)
1571 {
1572
1573 return pipelineBuilder.build(logger, PartialMarkupRenderer.class, PartialMarkupRendererFilter.class,
1574 configuration, terminator);
1575 }
1576
1577 public PageRenderRequestHandler buildPageRenderRequestHandler(List<PageRenderRequestFilter> configuration,
1578 Logger logger, @Autobuild
1579 PageRenderRequestHandlerImpl terminator)
1580 {
1581 return pipelineBuilder.build(logger, PageRenderRequestHandler.class, PageRenderRequestFilter.class,
1582 configuration, terminator);
1583 }
1584
1585 /**
1586 * Builds the component action request handler for traditional (non-Ajax)
1587 * requests. These typically result in a
1588 * redirect to a Tapestry render URL.
1589 */
1590 @Marker(
1591 {Traditional.class, Primary.class})
1592 public ComponentEventRequestHandler buildComponentEventRequestHandler(
1593 List<ComponentEventRequestFilter> configuration, Logger logger, @Autobuild
1594 ComponentEventRequestHandlerImpl terminator)
1595 {
1596 return pipelineBuilder.build(logger, ComponentEventRequestHandler.class, ComponentEventRequestFilter.class,
1597 configuration, terminator);
1598 }
1599
1600 /**
1601 * Builds the action request handler for Ajax requests, based on a
1602 * {@linkplain org.apache.tapestry5.ioc.services.PipelineBuilder
1603 * pipeline} around {@link org.apache.tapestry5.internal.services.AjaxComponentEventRequestHandler} . Filters on
1604 * the
1605 * request handler are supported here as well.
1606 */
1607 @Marker(
1608 {Ajax.class, Primary.class})
1609 public ComponentEventRequestHandler buildAjaxComponentEventRequestHandler(
1610 List<ComponentEventRequestFilter> configuration, Logger logger, @Autobuild
1611 AjaxComponentEventRequestHandler terminator)
1612 {
1613 return pipelineBuilder.build(logger, ComponentEventRequestHandler.class, ComponentEventRequestFilter.class,
1614 configuration, terminator);
1615 }
1616
1617 // ========================================================================
1618 //
1619 // Service Contribution Methods (instance)
1620 //
1621 // ========================================================================
1622
1623 /**
1624 * Contributes the default "session" strategy.
1625 */
1626 public void contributeApplicationStatePersistenceStrategySource(
1627 MappedConfiguration<String, ApplicationStatePersistenceStrategy> configuration,
1628
1629 @Local
1630 ApplicationStatePersistenceStrategy sessionStategy)
1631 {
1632 configuration.add("session", sessionStategy);
1633 }
1634
1635 public void contributeAssetSource(MappedConfiguration<String, AssetFactory> configuration, @ContextProvider
1636 AssetFactory contextAssetFactory,
1637
1638 @ClasspathProvider
1639 AssetFactory classpathAssetFactory)
1640 {
1641 configuration.add(AssetConstants.CONTEXT, contextAssetFactory);
1642 configuration.add(AssetConstants.CLASSPATH, classpathAssetFactory);
1643 }
1644
1645 /**
1646 * Contributes handlers for the following types:
1647 * <dl>
1648 * <dt>Object</dt>
1649 * <dd>Failure case, added to provide a more useful exception message</dd>
1650 * <dt>{@link Link}</dt>
1651 * <dd>Sends a redirect to the link (which is typically a page render link)</dd>
1652 * <dt>String</dt>
1653 * <dd>Sends a page render redirect</dd>
1654 * <dt>Class</dt>
1655 * <dd>Interpreted as the class name of a page, sends a page render render redirect (this is more refactoring safe
1656 * than the page name)</dd>
1657 * <dt>{@link Component}</dt>
1658 * <dd>A page's root component (though a non-root component will work, but will generate a warning). A direct to the
1659 * containing page is sent.</dd>
1660 * <dt>{@link org.apache.tapestry5.StreamResponse}</dt>
1661 * <dd>The stream response is sent as the actual reply.</dd>
1662 * <dt>URL</dt>
1663 * <dd>Sends a redirect to a (presumably) external URL</dd>
1664 * </dl>
1665 */
1666 public void contributeComponentEventResultProcessor(@Traditional
1667 @ComponentInstanceProcessor
1668 ComponentEventResultProcessor componentInstanceProcessor,
1669
1670 MappedConfiguration<Class, ComponentEventResultProcessor> configuration)
1671 {
1672 configuration.add(Link.class, new ComponentEventResultProcessor<Link>()
1673 {
1674 public void processResultValue(Link value) throws IOException
1675 {
1676 response.sendRedirect(value);
1677 }
1678 });
1679
1680 configuration.add(URL.class, new ComponentEventResultProcessor<URL>()
1681 {
1682 public void processResultValue(URL value) throws IOException
1683 {
1684 response.sendRedirect(value.toExternalForm());
1685 }
1686 });
1687
1688 configuration.addInstance(HttpError.class, HttpErrorComponentEventResultProcessor.class);
1689
1690 configuration.addInstance(String.class, PageNameComponentEventResultProcessor.class);
1691
1692 configuration.addInstance(Class.class, ClassResultProcessor.class);
1693
1694 configuration.add(Component.class, componentInstanceProcessor);
1695
1696 configuration.addInstance(StreamResponse.class, StreamResponseResultProcessor.class);
1697
1698 configuration.addInstance(StreamPageContent.class, StreamPageContentResultProcessor.class);
1699 }
1700
1701 /**
1702 * Contributes handlers for the following types:
1703 * <dl>
1704 * <dt>Object</dt>
1705 * <dd>Failure case, added to provide more useful exception message</dd>
1706 * <dt>{@link RenderCommand}</dt>
1707 * <dd>Typically, a {@link org.apache.tapestry5.Block}</dd>
1708 * <dt>{@link org.apache.tapestry5.annotations.Component}</dt>
1709 * <dd>Renders the component and its body (unless its a page, in which case a redirect JSON response is sent)</dd>
1710 * <dt>{@link org.apache.tapestry5.json.JSONObject} or {@link org.apache.tapestry5.json.JSONArray}</dt>
1711 * <dd>The JSONObject is returned as a text/javascript response</dd>
1712 * <dt>{@link org.apache.tapestry5.StreamResponse}</dt>
1713 * <dd>The stream response is sent as the actual response</dd>
1714 * <dt>String</dt>
1715 * <dd>Interprets the value as a logical page name and sends a client response to redirect to that page</dd>
1716 * <dt>{@link org.apache.tapestry5.Link}</dt>
1717 * <dd>Sends a JSON response to redirect to the link</dd>
1718 * <dt>{@link Class}</dt>
1719 * <dd>Treats the class as a page class and sends a redirect for a page render for that page</dd>
1720 * <dt>{@link org.apache.tapestry5.ajax.MultiZoneUpdate}</dt>
1721 * <dd>Sends a single JSON response to update the content of multiple zones
1722 * </dl>
1723 * <p/>
1724 * In most cases, when you want to support a new type, you should convert it to one of the built-in supported types
1725 * (such as {@link RenderCommand}. You can then inject the master AjaxComponentEventResultProcessor (use the
1726 * {@link Ajax} marker annotation) and delegate to it.
1727 */
1728 @Contribute(ComponentEventResultProcessor.class)
1729 @Ajax
1730 public static void provideBaseAjaxComponentEventResultProcessors(
1731 MappedConfiguration<Class, ComponentEventResultProcessor> configuration)
1732 {
1733 configuration.addInstance(RenderCommand.class, RenderCommandComponentEventResultProcessor.class);
1734 configuration.addInstance(Component.class, AjaxComponentInstanceEventResultProcessor.class);
1735 configuration.addInstance(JSONObject.class, JSONObjectEventResultProcessor.class);
1736 configuration.addInstance(JSONArray.class, JSONArrayEventResultProcessor.class);
1737 configuration.addInstance(StreamResponse.class, StreamResponseResultProcessor.class);
1738 configuration.addInstance(String.class, AjaxPageNameComponentEventResultProcessor.class);
1739 configuration.addInstance(Link.class, AjaxLinkComponentEventResultProcessor.class);
1740 configuration.addInstance(Class.class, AjaxPageClassComponentEventResultProcessor.class);
1741 configuration.addInstance(MultiZoneUpdate.class, MultiZoneUpdateEventResultProcessor.class);
1742 configuration.addInstance(HttpError.class, HttpErrorComponentEventResultProcessor.class);
1743 }
1744
1745 /**
1746 * The MasterDispatcher is a chain-of-command of individual Dispatchers,
1747 * each handling (like a servlet) a particular
1748 * kind of incoming request.
1749 * <dl>
1750 * <dt>RootPath</dt>
1751 * <dd>Renders the start page for the "/" request (outdated)</dd>
1752 * <dt>Asset</dt>
1753 * <dd>Provides access to assets (context, classpath and virtual) via {@link AssetDispatcher}</dd>
1754 * <dt>PageRender</dt>
1755 * <dd>Identifies the {@link org.apache.tapestry5.services.PageRenderRequestParameters} and forwards onto
1756 * {@link PageRenderRequestHandler}</dd>
1757 * <dt>ComponentEvent</dt>
1758 * <dd>Identifies the {@link ComponentEventRequestParameters} and forwards onto the
1759 * {@link ComponentEventRequestHandler}</dd>
1760 * </dl>
1761 */
1762 public static void contributeMasterDispatcher(OrderedConfiguration<Dispatcher> configuration,
1763
1764 @InjectService("AssetDispatcher")
1765 Dispatcher assetDispatcher)
1766 {
1767 // Looks for the root path and renders the start page. This is
1768 // maintained for compatibility
1769 // with earlier versions of Tapestry 5, it is recommended that an Index
1770 // page be used instead.
1771
1772 configuration.addInstance("RootPath", RootPathDispatcher.class, "before:Asset");
1773
1774 // This goes first because an asset to be streamed may have an file
1775 // extension, such as
1776 // ".html", that will confuse the later dispatchers.
1777
1778 configuration.add("Asset", assetDispatcher, "before:ComponentEvent");
1779
1780 configuration.addInstance("ComponentEvent", ComponentEventDispatcher.class, "before:PageRender");
1781
1782 configuration.addInstance("PageRender", PageRenderDispatcher.class);
1783 }
1784
1785 /**
1786 * Contributes a default object renderer for type Object, plus specialized
1787 * renderers for {@link org.apache.tapestry5.services.Request}, {@link org.apache.tapestry5.ioc.Location},
1788 * {@link org.apache.tapestry5.ComponentResources}, {@link org.apache.tapestry5.EventContext},
1789 * {@link AvailableValues},
1790 * List, and Object[].
1791 */
1792 @SuppressWarnings("unchecked")
1793 public void contributeObjectRenderer(MappedConfiguration<Class, ObjectRenderer> configuration,
1794
1795 @InjectService("LocationRenderer")
1796 ObjectRenderer locationRenderer,
1797
1798 final TypeCoercer typeCoercer)
1799 {
1800 configuration.add(Object.class, new DefaultObjectRenderer());
1801
1802 configuration.addInstance(Request.class, RequestRenderer.class);
1803
1804 configuration.add(Location.class, locationRenderer);
1805
1806 ObjectRenderer preformatted = new ObjectRenderer<Object>()
1807 {
1808 public void render(Object object, MarkupWriter writer)
1809 {
1810 writer.element("pre");
1811 writer.write(typeCoercer.coerce(object, String.class));
1812 writer.end();
1813 }
1814 };
1815
1816 configuration.add(ClassTransformation.class, preformatted);
1817
1818 configuration.addInstance(List.class, ListRenderer.class);
1819 configuration.addInstance(Object[].class, ObjectArrayRenderer.class);
1820 configuration.addInstance(ComponentResources.class, ComponentResourcesRenderer.class);
1821 configuration.addInstance(EventContext.class, EventContextRenderer.class);
1822 configuration.add(AvailableValues.class, new AvailableValuesRenderer());
1823 }
1824
1825 /**
1826 * Adds page render filters, each of which provides an {@link org.apache.tapestry5.annotations.Environmental}
1827 * service. Filters
1828 * often provide {@link org.apache.tapestry5.annotations.Environmental} services needed by
1829 * components as they render.
1830 * <dl>
1831 * <dt>DocumentLinker</dt>
1832 * <dd>Provides {@link org.apache.tapestry5.internal.services.DocumentLinker}</dd>
1833 * <dt>JavascriptSupport</dt>
1834 * <dd>Provides {@link JavaScriptSupport}</dd>
1835 * <dt>RenderSupport</dt>
1836 * <dd>Provides {@link org.apache.tapestry5.RenderSupport}</dd>
1837 * <dt>InjectDefaultStylesheet</dt>
1838 * <dd>Injects the default stylesheet into all pages</dd></dt>
1839 * <dt>ClientBehaviorSupport</dt>
1840 * <dd>Provides {@link ClientBehaviorSupport}</dd>
1841 * <dt>Heartbeat</dt>
1842 * <dd>Provides {@link org.apache.tapestry5.services.Heartbeat}</dd>
1843 * <dt>ValidationDecorator</dt>
1844 * <dd>Provides {@link org.apache.tapestry5.ValidationDecorator} (via {@link ValidationDecoratorFactory#newInstance(org.apache.tapestry5.MarkupWriter)})</dd>
1845 * </dl>
1846 */
1847 public void contributeMarkupRenderer(OrderedConfiguration<MarkupRendererFilter> configuration,
1848
1849 @Symbol(SymbolConstants.OMIT_GENERATOR_META)
1850 final boolean omitGeneratorMeta,
1851
1852 @Symbol(SymbolConstants.TAPESTRY_VERSION)
1853 final String tapestryVersion,
1854
1855 @Symbol(SymbolConstants.COMPACT_JSON)
1856 final boolean compactJSON,
1857
1858 final SymbolSource symbolSource,
1859
1860 final AssetSource assetSource,
1861
1862 final JavaScriptStackSource javascriptStackSource,
1863
1864 final JavaScriptStackPathConstructor javascriptStackPathConstructor,
1865
1866 final ValidationDecoratorFactory validationDecoratorFactory,
1867
1868 @Path("${tapestry.default-stylesheet}")
1869 final Asset defaultStylesheet)
1870 {
1871 MarkupRendererFilter documentLinker = new MarkupRendererFilter()
1872 {
1873 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1874 {
1875 DocumentLinkerImpl linker = new DocumentLinkerImpl(omitGeneratorMeta, tapestryVersion, compactJSON);
1876
1877 environment.push(DocumentLinker.class, linker);
1878
1879 renderer.renderMarkup(writer);
1880
1881 environment.pop(DocumentLinker.class);
1882
1883 linker.updateDocument(writer.getDocument());
1884 }
1885 };
1886
1887 MarkupRendererFilter javaScriptSupport = new MarkupRendererFilter()
1888 {
1889 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1890 {
1891 DocumentLinker linker = environment.peekRequired(DocumentLinker.class);
1892
1893 JavaScriptSupportImpl support = new JavaScriptSupportImpl(linker, javascriptStackSource,
1894 javascriptStackPathConstructor);
1895
1896 environment.push(JavaScriptSupport.class, support);
1897
1898 renderer.renderMarkup(writer);
1899
1900 environment.pop(JavaScriptSupport.class);
1901
1902 support.commit();
1903 }
1904 };
1905
1906 MarkupRendererFilter renderSupport = new MarkupRendererFilter()
1907 {
1908 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1909 {
1910 JavaScriptSupport javascriptSupport = environment.peekRequired(JavaScriptSupport.class);
1911
1912 RenderSupportImpl support = new RenderSupportImpl(symbolSource, assetSource, javascriptSupport);
1913
1914 environment.push(RenderSupport.class, support);
1915
1916 renderer.renderMarkup(writer);
1917
1918 environment.pop(RenderSupport.class);
1919 }
1920 };
1921
1922 MarkupRendererFilter injectDefaultStylesheet = new MarkupRendererFilter()
1923 {
1924 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1925 {
1926 DocumentLinker linker = environment.peekRequired(DocumentLinker.class);
1927
1928 linker.addStylesheetLink(new StylesheetLink(defaultStylesheet.toClientURL()));
1929
1930 renderer.renderMarkup(writer);
1931 }
1932 };
1933
1934 MarkupRendererFilter clientBehaviorSupport = new MarkupRendererFilter()
1935 {
1936 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1937 {
1938 JavaScriptSupport javascriptSupport = environment.peekRequired(JavaScriptSupport.class);
1939
1940 ClientBehaviorSupportImpl clientBehaviorSupport = new ClientBehaviorSupportImpl(javascriptSupport,
1941 environment);
1942
1943 environment.push(ClientBehaviorSupport.class, clientBehaviorSupport);
1944
1945 renderer.renderMarkup(writer);
1946
1947 environment.pop(ClientBehaviorSupport.class);
1948
1949 clientBehaviorSupport.commit();
1950 }
1951 };
1952
1953 MarkupRendererFilter heartbeat = new MarkupRendererFilter()
1954 {
1955 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1956 {
1957 Heartbeat heartbeat = new HeartbeatImpl();
1958
1959 heartbeat.begin();
1960
1961 environment.push(Heartbeat.class, heartbeat);
1962
1963 renderer.renderMarkup(writer);
1964
1965 environment.pop(Heartbeat.class);
1966
1967 heartbeat.end();
1968 }
1969 };
1970
1971 MarkupRendererFilter defaultValidationDecorator = new MarkupRendererFilter()
1972 {
1973 public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
1974 {
1975 ValidationDecorator decorator = validationDecoratorFactory.newInstance(writer);
1976
1977 environment.push(ValidationDecorator.class, decorator);
1978
1979 renderer.renderMarkup(writer);
1980
1981 environment.pop(ValidationDecorator.class);
1982 }
1983 };
1984
1985 configuration.add("DocumentLinker", documentLinker);
1986 configuration.add("JavaScriptSupport", javaScriptSupport);
1987 configuration.add("RenderSupport", renderSupport);
1988 configuration.add("InjectDefaultStylesheet", injectDefaultStylesheet);
1989 configuration.add("ClientBehaviorSupport", clientBehaviorSupport);
1990 configuration.add("Heartbeat", heartbeat);
1991 configuration.add("ValidationDecorator", defaultValidationDecorator);
1992 }
1993
1994 /**
1995 * Contributes {@link PartialMarkupRendererFilter}s used when rendering a
1996 * partial Ajax response.
1997 * <dl>
1998 * <dt>DocumentLinker
1999 * <dd>Provides {@link org.apache.tapestry5.internal.services.DocumentLinker}
2000 * <dt>JavaScriptSupport
2001 * <dd>Provides {@link JavaScriptSupport}</dd>
2002 * <dt>PageRenderSupport</dt>
2003 * <dd>Provides {@link org.apache.tapestry5.RenderSupport}</dd>
2004 * <dt>ClientBehaviorSupport</dt>
2005 * <dd>Provides {@link ClientBehaviorSupport}</dd>
2006 * <dt>Heartbeat</dt>
2007 * <dd>Provides {@link org.apache.tapestry5.services.Heartbeat}</dd>
2008 * <dt>DefaultValidationDecorator</dt>
2009 * <dt>ValidationDecorator</dt>
2010 * <dd>Provides {@link org.apache.tapestry5.ValidationDecorator} (via {@link ValidationDecoratorFactory#newInstance(org.apache.tapestry5.MarkupWriter)})</dd>
2011 * </dl>
2012 */
2013 public void contributePartialMarkupRenderer(OrderedConfiguration<PartialMarkupRendererFilter> configuration,
2014
2015 final ValidationDecoratorFactory validationDecoratorFactory,
2016
2017 final JavaScriptStackSource javascriptStackSource,
2018
2019 final JavaScriptStackPathConstructor javascriptStackPathConstructor,
2020
2021 final SymbolSource symbolSource,
2022
2023 final AssetSource assetSource)
2024 {
2025 PartialMarkupRendererFilter documentLinker = new PartialMarkupRendererFilter()
2026 {
2027 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2028 {
2029 PartialMarkupDocumentLinker linker = new PartialMarkupDocumentLinker();
2030
2031 environment.push(DocumentLinker.class, linker);
2032
2033 renderer.renderMarkup(writer, reply);
2034
2035 environment.pop(DocumentLinker.class);
2036
2037 linker.commit(reply);
2038 }
2039 };
2040
2041 PartialMarkupRendererFilter javascriptSupport = new PartialMarkupRendererFilter()
2042 {
2043 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2044 {
2045 String uid = Long.toHexString(System.currentTimeMillis());
2046
2047 String namespace = "_" + uid;
2048
2049 IdAllocator idAllocator = new IdAllocator(namespace);
2050
2051 DocumentLinker linker = environment.peekRequired(DocumentLinker.class);
2052
2053 JavaScriptSupportImpl support = new JavaScriptSupportImpl(linker, javascriptStackSource,
2054 javascriptStackPathConstructor, idAllocator, true);
2055
2056 environment.push(JavaScriptSupport.class, support);
2057
2058 renderer.renderMarkup(writer, reply);
2059
2060 environment.pop(JavaScriptSupport.class);
2061
2062 support.commit();
2063 }
2064 };
2065
2066 PartialMarkupRendererFilter renderSupport = new PartialMarkupRendererFilter()
2067 {
2068 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2069 {
2070 JavaScriptSupport javascriptSupport = environment.peekRequired(JavaScriptSupport.class);
2071
2072 RenderSupportImpl support = new RenderSupportImpl(symbolSource, assetSource, javascriptSupport);
2073
2074 environment.push(RenderSupport.class, support);
2075
2076 renderer.renderMarkup(writer, reply);
2077
2078 environment.pop(RenderSupport.class);
2079 }
2080 };
2081
2082 PartialMarkupRendererFilter clientBehaviorSupport = new PartialMarkupRendererFilter()
2083 {
2084 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2085 {
2086 JavaScriptSupport javascriptSupport = environment.peekRequired(JavaScriptSupport.class);
2087
2088 ClientBehaviorSupportImpl support = new ClientBehaviorSupportImpl(javascriptSupport, environment);
2089
2090 environment.push(ClientBehaviorSupport.class, support);
2091
2092 renderer.renderMarkup(writer, reply);
2093
2094 environment.pop(ClientBehaviorSupport.class);
2095
2096 support.commit();
2097 }
2098 };
2099
2100 PartialMarkupRendererFilter heartbeat = new PartialMarkupRendererFilter()
2101 {
2102 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2103 {
2104 Heartbeat heartbeat = new HeartbeatImpl();
2105
2106 heartbeat.begin();
2107
2108 environment.push(Heartbeat.class, heartbeat);
2109
2110 renderer.renderMarkup(writer, reply);
2111
2112 environment.pop(Heartbeat.class);
2113
2114 heartbeat.end();
2115 }
2116 };
2117
2118 PartialMarkupRendererFilter defaultValidationDecorator = new PartialMarkupRendererFilter()
2119 {
2120 public void renderMarkup(MarkupWriter writer, JSONObject reply, PartialMarkupRenderer renderer)
2121 {
2122 ValidationDecorator decorator = validationDecoratorFactory.newInstance(writer);
2123
2124 environment.push(ValidationDecorator.class, decorator);
2125
2126 renderer.renderMarkup(writer, reply);
2127
2128 environment.pop(ValidationDecorator.class);
2129 }
2130 };
2131
2132 configuration.add("DocumentLinker", documentLinker);
2133 configuration.add("JavaScriptSupport", javascriptSupport);
2134 configuration.add("RenderSupport", renderSupport);
2135 configuration.add("ClientBehaviorSupport", clientBehaviorSupport);
2136 configuration.add("Heartbeat", heartbeat);
2137 configuration.add("ValidationDecorator", defaultValidationDecorator);
2138 }
2139
2140 /**
2141 * Contributes several strategies:
2142 * <dl>
2143 * <dt>session
2144 * <dd>Values are stored in the {@link Session}
2145 * <dt>flash
2146 * <dd>Values are stored in the {@link Session}, until the next request (for the page)
2147 * <dt>client
2148 * <dd>Values are encoded into URLs (or hidden form fields)
2149 * </dl>
2150 */
2151 public void contributePersistentFieldManager(MappedConfiguration<String, PersistentFieldStrategy> configuration,
2152
2153 Request request,
2154
2155 @InjectService("ClientPersistentFieldStrategy")
2156 PersistentFieldStrategy clientStrategy)
2157 {
2158 configuration.add(PersistenceConstants.SESSION, new SessionPersistentFieldStrategy(request));
2159 configuration.add(PersistenceConstants.FLASH, new FlashPersistentFieldStrategy(request));
2160 configuration.add(PersistenceConstants.CLIENT, clientStrategy);
2161 }
2162
2163 @SuppressWarnings("rawtypes")
2164 public static ValueEncoderSource buildValueEncoderSource(Map<Class, ValueEncoderFactory> configuration,
2165 @ComponentClasses
2166 InvalidationEventHub hub)
2167 {
2168 ValueEncoderSourceImpl service = new ValueEncoderSourceImpl(configuration);
2169
2170 hub.addInvalidationListener(service);
2171
2172 return service;
2173 }
2174
2175 /**
2176 * Contributes {@link ValueEncoder}s or {@link ValueEncoderFactory}s for types:
2177 * <ul>
2178 * <li>Object
2179 * <li>String
2180 * <li>Enum
2181 * </ul>
2182 */
2183 @SuppressWarnings("all")
2184 public static void contributeValueEncoderSource(MappedConfiguration<Class, Object> configuration)
2185 {
2186 configuration.addInstance(Object.class, TypeCoercedValueEncoderFactory.class);
2187 configuration.add(String.class, new StringValueEncoder());
2188 configuration.addInstance(Enum.class, EnumValueEncoderFactory.class);
2189 }
2190
2191 /**
2192 * Contributes a single filter, "Secure", which checks for non-secure
2193 * requests that access secure pages.
2194 */
2195 public void contributePageRenderRequestHandler(OrderedConfiguration<PageRenderRequestFilter> configuration,
2196 final RequestSecurityManager securityManager)
2197 {
2198 PageRenderRequestFilter secureFilter = new PageRenderRequestFilter()
2199 {
2200 public void handle(PageRenderRequestParameters parameters, PageRenderRequestHandler handler)
2201 throws IOException
2202 {
2203
2204 if (securityManager.checkForInsecurePageRenderRequest(parameters))
2205 return;
2206
2207 handler.handle(parameters);
2208 }
2209 };
2210
2211 configuration.add("Secure", secureFilter);
2212 }
2213
2214 /**
2215 * Configures the extensions that will require a digest to be downloaded via
2216 * the asset dispatcher. Most resources
2217 * are "safe", they don't require a digest. For unsafe resources, the digest
2218 * is incorporated into the URL to ensure
2219 * that the client side isn't just "fishing".
2220 * <p/>
2221 * The extensions must be all lower case.
2222 * <p/>
2223 * This contributes "class", "properties" and "tml" (the template extension).
2224 *
2225 * @param configuration collection of extensions
2226 */
2227 public static void contributeResourceDigestGenerator(Configuration<String> configuration)
2228 {
2229 // Java class files always require a digest.
2230 configuration.add("class");
2231
2232 // Even though properties don't contain sensible data we should protect
2233 // them.
2234 configuration.add("properties");
2235
2236 // Likewise, we don't want people fishing for templates.
2237 configuration.add(TapestryConstants.TEMPLATE_EXTENSION);
2238 }
2239
2240 public static void contributeTemplateParser(MappedConfiguration<String, URL> config)
2241 {
2242 // Any class inside the internal module would do. Or we could move all
2243 // these
2244 // files to o.a.t.services.
2245
2246 Class c = TemplateParserImpl.class;
2247
2248 config.add("-//W3C//DTD XHTML 1.0 Strict//EN", c.getResource("xhtml1-strict.dtd"));
2249 config.add("-//W3C//DTD XHTML 1.0 Transitional//EN", c.getResource("xhtml1-transitional.dtd"));
2250 config.add("-//W3C//DTD XHTML 1.0 Frameset//EN", c.getResource("xhtml1-frameset.dtd"));
2251 config.add("-//W3C//DTD HTML 4.01//EN", c.getResource("xhtml1-strict.dtd"));
2252 config.add("-//W3C//DTD HTML 4.01 Transitional//EN", c.getResource("xhtml1-transitional.dtd"));
2253 config.add("-//W3C//DTD HTML 4.01 Frameset//EN", c.getResource("xhtml1-frameset.dtd"));
2254 config.add("-//W3C//ENTITIES Latin 1 for XHTML//EN", c.getResource("xhtml-lat1.ent"));
2255 config.add("-//W3C//ENTITIES Symbols for XHTML//EN", c.getResource("xhtml-symbol.ent"));
2256 config.add("-//W3C//ENTITIES Special for XHTML//EN", c.getResource("xhtml-special.ent"));
2257 }
2258
2259 /**
2260 * Contributes factory defaults that may be overridden.
2261 */
2262 public static void contributeFactoryDefaults(MappedConfiguration<String, Object> configuration)
2263 {
2264 // Remember this is request-to-request time, presumably it'll take the
2265 // developer more than
2266 // one second to make a change, save it, and switch back to the browser.
2267
2268 configuration.add(SymbolConstants.FILE_CHECK_INTERVAL, "1 s");
2269 configuration.add(SymbolConstants.FILE_CHECK_UPDATE_TIMEOUT, "50 ms");
2270
2271 // This should be overridden for particular applications. These are the
2272 // locales for which we have (at least some) localized messages.
2273 configuration.add(SymbolConstants.SUPPORTED_LOCALES,
2274 "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");
2275
2276 configuration.add(SymbolConstants.TAPESTRY_VERSION,
2277 VersionUtils.readVersionNumber("META-INF/gradle/org.apache.tapestry/tapestry-core/project.properties"));
2278
2279 configuration.add(SymbolConstants.COOKIE_MAX_AGE, "7 d");
2280
2281 configuration.add(SymbolConstants.START_PAGE_NAME, "start");
2282
2283 configuration.add(SymbolConstants.DEFAULT_STYLESHEET, "classpath:/org/apache/tapestry5/default.css");
2284 configuration.add("tapestry.spacer-image", "classpath:/org/apache/tapestry5/spacer.gif");
2285
2286 configuration.add(SymbolConstants.SUPPRESS_REDIRECT_FROM_ACTION_REQUESTS, false);
2287
2288 configuration.add(SymbolConstants.PRODUCTION_MODE, true);
2289
2290 configuration.add(SymbolConstants.CLUSTERED_SESSIONS, true);
2291
2292 configuration.add(SymbolConstants.ASSET_PATH_PREFIX, "/assets/");
2293
2294 configuration.add(SymbolConstants.COMPRESS_WHITESPACE, true);
2295
2296 configuration.add(MetaDataConstants.SECURE_PAGE, false);
2297
2298 configuration.add(SymbolConstants.FORM_CLIENT_LOGIC_ENABLED, true);
2299
2300 // This is designed to make it easy to keep synchronized with
2301 // script.aculo.ous. As we support a new version, we create a new folder, and update the
2302 // path entry. We can then delete the old version folder (or keep it around). This should
2303 // be more manageable than overwriting the local copy with updates (it's too easy for
2304 // files deleted between scriptaculous releases to be accidentally left lying around).
2305 // There's also a ClasspathAliasManager contribution based on the path.
2306
2307 configuration.add(SymbolConstants.SCRIPTACULOUS, "classpath:${tapestry.scriptaculous.path}");
2308 configuration.add("tapestry.scriptaculous.path", "org/apache/tapestry5/scriptaculous_1_9_0");
2309
2310 // Likewise for WebFX DatePicker, currently version 1.0.6
2311
2312 configuration.add("tapestry.datepicker.path", "org/apache/tapestry5/datepicker_106");
2313 configuration.add(SymbolConstants.DATEPICKER, "classpath:${tapestry.datepicker.path}");
2314
2315 configuration.add("tapestry.underscore", "classpath:org/apache/tapestry5/underscore_1_3_3.js");
2316
2317 configuration.add(SymbolConstants.BLACKBIRD, "");
2318
2319 configuration.add(SymbolConstants.PERSISTENCE_STRATEGY, PersistenceConstants.SESSION);
2320
2321 configuration.add(MetaDataConstants.RESPONSE_CONTENT_TYPE, "text/html");
2322
2323 configuration.add(SymbolConstants.CHARSET, "UTF-8");
2324
2325 configuration.add(SymbolConstants.APPLICATION_CATALOG,
2326 String.format("context:WEB-INF/${%s}.properties", InternalSymbols.APP_NAME));
2327
2328 configuration.add(SymbolConstants.EXCEPTION_REPORT_PAGE, "ExceptionReport");
2329
2330 configuration.add(SymbolConstants.MIN_GZIP_SIZE, 100);
2331
2332 Random random = new Random(System.currentTimeMillis());
2333
2334 configuration.add(SymbolConstants.APPLICATION_VERSION, Long.toHexString(random.nextLong()));
2335
2336 configuration.add(SymbolConstants.OMIT_GENERATOR_META, false);
2337
2338 configuration.add(SymbolConstants.SECURE_ENABLED, SymbolConstants.PRODUCTION_MODE_VALUE);
2339 configuration.add(SymbolConstants.COMPACT_JSON, SymbolConstants.PRODUCTION_MODE_VALUE);
2340
2341 configuration.add(SymbolConstants.ENCODE_LOCALE_INTO_PATH, true);
2342
2343 configuration.add(SymbolConstants.BLACKBIRD_ENABLED, false);
2344
2345 configuration.add(InternalSymbols.PRE_SELECTED_FORM_NAMES, "reset,submit,select,id,method,action,onsubmit," + InternalConstants.CANCEL_NAME);
2346
2347 configuration.add(SymbolConstants.COMPONENT_RENDER_TRACING_ENABLED, false);
2348
2349 // The default values denote "use values from request"
2350 configuration.add(SymbolConstants.HOSTNAME, "");
2351 configuration.add(SymbolConstants.HOSTPORT, 0);
2352 configuration.add(SymbolConstants.HOSTPORT_SECURE, 0);
2353
2354 configuration.add(SymbolConstants.UNKNOWN_COMPONENT_ID_CHECK_ENABLED, true);
2355
2356 configuration.add(SymbolConstants.APPLICATION_FOLDER, "");
2357
2358 // Grid component parameters defaults
2359 configuration.add(ComponentParameterConstants.GRID_ROWS_PER_PAGE, GridConstants.ROWS_PER_PAGE);
2360 configuration.add(ComponentParameterConstants.GRID_PAGER_POSITION, GridConstants.PAGER_POSITION);
2361 configuration.add(ComponentParameterConstants.GRID_EMPTY_BLOCK, GridConstants.EMPTY_BLOCK);
2362 configuration.add(ComponentParameterConstants.GRID_TABLE_CSS_CLASS, GridConstants.TABLE_CLASS);
2363 configuration.add(ComponentParameterConstants.GRIDPAGER_PAGE_RANGE, GridConstants.PAGER_PAGE_RANGE);
2364 configuration.add(ComponentParameterConstants.GRIDCOLUMNS_SORTABLE_ASSET, GridConstants.COLUMNS_SORTABLE);
2365 configuration.add(ComponentParameterConstants.GRIDCOLUMNS_ASCENDING_ASSET, GridConstants.COLUMNS_ASCENDING);
2366 configuration.add(ComponentParameterConstants.GRIDCOLUMNS_DESCENDING_ASSET, GridConstants.COLUMNS_DESCENDING);
2367
2368 // FormInjector component parameters defaults
2369 configuration.add(ComponentParameterConstants.FORMINJECTOR_INSERT_POSITION, "above");
2370 configuration.add(ComponentParameterConstants.FORMINJECTOR_SHOW_FUNCTION, "highlight");
2371
2372 // Palette component parameters defaults
2373 configuration.add(ComponentParameterConstants.PALETTE_ROWS_SIZE, 10);
2374
2375 // Zone component parameters defaults
2376 configuration.add(ComponentParameterConstants.ZONE_SHOW_METHOD, "show");
2377 configuration.add(ComponentParameterConstants.ZONE_UPDATE_METHOD, "highlight");
2378
2379 // By default, no page is on the whitelist unless it has the @WhitelistAccessOnly annotation
2380 configuration.add(MetaDataConstants.WHITELIST_ONLY_PAGE, false);
2381 }
2382
2383 /**
2384 * Adds a listener to the {@link org.apache.tapestry5.internal.services.ComponentInstantiatorSource} that clears the
2385 * {@link PropertyAccess} and {@link TypeCoercer} caches on
2386 * a class loader invalidation. In addition, forces the
2387 * realization of {@link ComponentClassResolver} at startup.
2388 */
2389 public void contributeApplicationInitializer(OrderedConfiguration<ApplicationInitializerFilter> configuration,
2390 final TypeCoercer typeCoercer, final ComponentClassResolver componentClassResolver, @ComponentClasses
2391 final InvalidationEventHub invalidationEventHub, final @Autobuild
2392 RestoreDirtySessionObjects restoreDirtySessionObjects)
2393 {
2394 final InvalidationListener listener = new InvalidationListener()
2395 {
2396 public void objectWasInvalidated()
2397 {
2398 propertyAccess.clearCache();
2399
2400 typeCoercer.clearCache();
2401 }
2402 };
2403
2404 ApplicationInitializerFilter clearCaches = new ApplicationInitializerFilter()
2405 {
2406 public void initializeApplication(Context context, ApplicationInitializer initializer)
2407 {
2408 // Snuck in here is the logic to clear the PropertyAccess
2409 // service's cache whenever
2410 // the component class loader is invalidated.
2411
2412 invalidationEventHub.addInvalidationListener(listener);
2413
2414 endOfRequestEventHub.addEndOfRequestListener(restoreDirtySessionObjects);
2415
2416 // Perform other pending initialization
2417
2418 initializer.initializeApplication(context);
2419
2420 // We don't care about the result, but this forces a load of the
2421 // service
2422 // at application startup, rather than on first request.
2423
2424 componentClassResolver.isPageName("ForceLoadAtStartup");
2425 }
2426 };
2427
2428 configuration.add("ClearCachesOnInvalidation", clearCaches);
2429 }
2430
2431 /**
2432 * Contributes filters:
2433 * <dl>
2434 * <dt>Ajax</dt>
2435 * <dd>Determines if the request is Ajax oriented, and redirects to an alternative handler if so</dd>
2436 * <dt>ImmediateRender</dt>
2437 * <dd>When {@linkplain SymbolConstants#SUPPRESS_REDIRECT_FROM_ACTION_REQUESTS
2438 * immediate action response rendering} is enabled, generates the markup response (instead of a page redirect
2439 * response, which is the normal behavior)</dd>
2440 * <dt>Secure</dt>
2441 * <dd>Sends a redirect if an non-secure request accesses a secure page</dd>
2442 * </dl>
2443 */
2444 public void contributeComponentEventRequestHandler(OrderedConfiguration<ComponentEventRequestFilter> configuration,
2445 final RequestSecurityManager requestSecurityManager, @Ajax
2446 ComponentEventRequestHandler ajaxHandler)
2447 {
2448 ComponentEventRequestFilter secureFilter = new ComponentEventRequestFilter()
2449 {
2450 public void handle(ComponentEventRequestParameters parameters, ComponentEventRequestHandler handler)
2451 throws IOException
2452 {
2453 if (requestSecurityManager.checkForInsecureComponentEventRequest(parameters))
2454 return;
2455
2456 handler.handle(parameters);
2457 }
2458 };
2459
2460 configuration.add("Ajax", new AjaxFilter(request, ajaxHandler));
2461
2462 configuration.addInstance("ImmediateRender", ImmediateActionRenderResponseFilter.class);
2463
2464 configuration.add("Secure", secureFilter, "before:Ajax");
2465 }
2466
2467 /**
2468 * Contributes:
2469 * <dl>
2470 * <dt>AjaxFormUpdate</dt>
2471 * <dd>{@link AjaxFormUpdateFilter}</dd>
2472 * </dl>
2473 *
2474 * @since 5.2.0
2475 */
2476 public static void contributeAjaxComponentEventRequestHandler(
2477 OrderedConfiguration<ComponentEventRequestFilter> configuration)
2478 {
2479 configuration.addInstance("AjaxFormUpdate", AjaxFormUpdateFilter.class);
2480 }
2481
2482 /**
2483 * Contributes strategies accessible via the {@link NullFieldStrategySource} service.
2484 * <p/>
2485 * <dl>
2486 * <dt>default</dt>
2487 * <dd>Does nothing, nulls stay null.</dd>
2488 * <dt>zero</dt>
2489 * <dd>Null values are converted to zero.</dd>
2490 * </dl>
2491 */
2492 public static void contributeNullFieldStrategySource(MappedConfiguration<String, NullFieldStrategy> configuration)
2493 {
2494 configuration.add("default", new DefaultNullFieldStrategy());
2495 configuration.add("zero", new ZeroNullFieldStrategy());
2496 }
2497
2498 /**
2499 * Determines positioning of hidden fields relative to other elements (this
2500 * is needed by {@link org.apache.tapestry5.corelib.components.FormFragment} and others.
2501 * <p/>
2502 * For elements input, select, textarea and label the hidden field is positioned after.
2503 * <p/>
2504 * For elements p, div, li and td, the hidden field is positioned inside.
2505 */
2506 public static void contributeHiddenFieldLocationRules(
2507 MappedConfiguration<String, RelativeElementPosition> configuration)
2508 {
2509 configuration.add("input", RelativeElementPosition.AFTER);
2510 configuration.add("select", RelativeElementPosition.AFTER);
2511 configuration.add("textarea", RelativeElementPosition.AFTER);
2512 configuration.add("label", RelativeElementPosition.AFTER);
2513
2514 configuration.add("p", RelativeElementPosition.INSIDE);
2515 configuration.add("div", RelativeElementPosition.INSIDE);
2516 configuration.add("td", RelativeElementPosition.INSIDE);
2517 configuration.add("li", RelativeElementPosition.INSIDE);
2518 }
2519
2520 /**
2521 * @since 5.1.0.0
2522 */
2523 public static LinkCreationHub buildLinkCreationHub(LinkSource source)
2524 {
2525 return source.getLinkCreationHub();
2526 }
2527
2528 /**
2529 * Exposes the public portion of the internal {@link InternalComponentInvalidationEventHub} service.
2530 *
2531 * @since 5.1.0.0
2532 */
2533 @Marker(ComponentClasses.class)
2534 public static InvalidationEventHub buildComponentClassesInvalidationEventHub(
2535 InternalComponentInvalidationEventHub trueHub)
2536 {
2537 return trueHub;
2538 }
2539
2540 /**
2541 * @since 5.1.0.0
2542 */
2543 @Marker(ComponentTemplates.class)
2544 public static InvalidationEventHub buildComponentTemplatesInvalidationEventHub(
2545 ComponentTemplateSource templateSource)
2546 {
2547 return templateSource.getInvalidationEventHub();
2548 }
2549
2550 /**
2551 * @since 5.1.0.0
2552 */
2553 @Marker(ComponentMessages.class)
2554 public static InvalidationEventHub buildComponentMessagesInvalidationEventHub(ComponentMessagesSource messagesSource)
2555 {
2556 return messagesSource.getInvalidationEventHub();
2557 }
2558
2559 @Scope(ScopeConstants.PERTHREAD)
2560 public Environment buildEnvironment(PerthreadManager perthreadManager)
2561 {
2562 EnvironmentImpl service = new EnvironmentImpl();
2563
2564 perthreadManager.addThreadCleanupListener(service);
2565
2566 return service;
2567 }
2568
2569 /**
2570 * The master SessionPersistedObjectAnalyzer.
2571 *
2572 * @since 5.1.0.0
2573 */
2574 @Marker(Primary.class)
2575 public SessionPersistedObjectAnalyzer buildSessionPersistedObjectAnalyzer(
2576 Map<Class, SessionPersistedObjectAnalyzer> configuration)
2577 {
2578 return strategyBuilder.build(SessionPersistedObjectAnalyzer.class, configuration);
2579 }
2580
2581 /**
2582 * Identifies String, Number and Boolean as immutable objects, a catch-all
2583 * handler for Object (that understands
2584 * the {@link org.apache.tapestry5.annotations.ImmutableSessionPersistedObject} annotation),
2585 * and a handler for {@link org.apache.tapestry5.OptimizedSessionPersistedObject}.
2586 *
2587 * @since 5.1.0.0
2588 */
2589 public static void contributeSessionPersistedObjectAnalyzer(
2590 MappedConfiguration<Class, SessionPersistedObjectAnalyzer> configuration)
2591 {
2592 configuration.add(Object.class, new DefaultSessionPersistedObjectAnalyzer());
2593
2594 SessionPersistedObjectAnalyzer<Object> immutable = new SessionPersistedObjectAnalyzer<Object>()
2595 {
2596 public boolean checkAndResetDirtyState(Object sessionPersistedObject)
2597 {
2598 return false;
2599 }
2600 };
2601
2602 configuration.add(String.class, immutable);
2603 configuration.add(Number.class, immutable);
2604 configuration.add(Boolean.class, immutable);
2605
2606 configuration.add(OptimizedSessionPersistedObject.class, new OptimizedSessionPersistedObjectAnalyzer());
2607 }
2608
2609 /**
2610 * @since 5.1.1.0
2611 */
2612 @Marker(Primary.class)
2613 public StackTraceElementAnalyzer buildMasterStackTraceElementAnalyzer(List<StackTraceElementAnalyzer> configuration)
2614 {
2615 return chainBuilder.build(StackTraceElementAnalyzer.class, configuration);
2616 }
2617
2618 /**
2619 * Contributes:
2620 * <dl>
2621 * <dt>Application</dt>
2622 * <dd>Checks for classes in the application package</dd>
2623 * <dt>Proxies</dt>
2624 * <dd>Checks for classes that appear to be generated proxies.</dd>
2625 * <dt>SunReflect</dt>
2626 * <dd>Checks for <code>sun.reflect</code> (which are omitted)
2627 * <dt>TapestryAOP</dt>
2628 * <dd>Omits stack frames for classes related to Tapestry AOP (such as advice, etc.)</dd>
2629 * <dt>OperationTracker</dt>
2630 * <dd>Omits stack frames related to {@link OperationTracker}</dd>
2631 * </dl>
2632 *
2633 * @since 5.1.0.0
2634 */
2635 public static void contributeMasterStackTraceElementAnalyzer(
2636 OrderedConfiguration<StackTraceElementAnalyzer> configuration)
2637 {
2638 configuration.addInstance("Application", ApplicationStackTraceElementAnalyzer.class);
2639 configuration.add("Proxies", new ProxiesStackTraceElementAnalyzer(), "before:Application");
2640 configuration.add("Synthetic", new SyntheticStackTraceElementAnalyzer(), "before:Application");
2641 configuration.add("SunReflect", new PrefixCheckStackTraceElementAnalyzer(
2642 StackTraceElementClassConstants.OMITTED, "sun.reflect."));
2643 configuration.addInstance("TapestryAOP", TapestryAOPStackFrameAnalyzer.class, "before:Application");
2644 configuration.add("OperationTracker", new RegexpStackTraceElementAnalyzer(Pattern.compile("internal\\.(RegistryImpl|PerThreadOperationTracker|OperationTrackerImpl)\\.invoke\\("), StackTraceElementClassConstants.OMITTED));
2645 }
2646
2647 /**
2648 * Advises the {@link org.apache.tapestry5.services.messages.ComponentMessagesSource} service so
2649 * that the creation
2650 * of {@link org.apache.tapestry5.ioc.Messages} instances can be deferred.
2651 *
2652 * @since 5.1.0.0
2653 */
2654 @Match("ComponentMessagesSource")
2655 public static void adviseLazy(LazyAdvisor advisor, MethodAdviceReceiver receiver)
2656 {
2657 advisor.addLazyMethodInvocationAdvice(receiver);
2658 }
2659
2660 /**
2661 * @since 5.1.0.0
2662 */
2663 public ComponentRequestHandler buildComponentRequestHandler(List<ComponentRequestFilter> configuration,
2664
2665 @Autobuild
2666 ComponentRequestHandlerTerminator terminator,
2667
2668 Logger logger)
2669 {
2670 return pipelineBuilder.build(logger, ComponentRequestHandler.class, ComponentRequestFilter.class,
2671 configuration, terminator);
2672 }
2673
2674 /**
2675 * Contributes:
2676 * <dl>
2677 * <dt>InitializeActivePageName
2678 * <dd>{@link InitializeActivePageName}
2679 * </dl>
2680 *
2681 * @since 5.2.0
2682 */
2683 public void contributeComponentRequestHandler(OrderedConfiguration<ComponentRequestFilter> configuration)
2684 {
2685 configuration.addInstance("InitializeActivePageName", InitializeActivePageName.class);
2686 }
2687
2688 /**
2689 * Decorate FieldValidatorDefaultSource to setup the EnvironmentMessages
2690 * object and place it in the environment.
2691 * Although this could have been implemented directly in the default
2692 * implementation of the service, doing it
2693 * as service decoration ensures that the environment will be properly setup
2694 * even if a user overrides the default
2695 * service implementation.
2696 *
2697 * @param defaultSource The service to decorate
2698 * @param environment
2699 */
2700 public static FieldValidatorDefaultSource decorateFieldValidatorDefaultSource(
2701 final FieldValidatorDefaultSource defaultSource, final Environment environment)
2702 {
2703 return new FieldValidatorDefaultSource()
2704 {
2705
2706 public FieldValidator createDefaultValidator(Field field, String overrideId, Messages overrideMessages,
2707 Locale locale, Class propertyType, AnnotationProvider propertyAnnotations)
2708 {
2709 environment.push(EnvironmentMessages.class, new EnvironmentMessages(overrideMessages, overrideId));
2710 FieldValidator fieldValidator = defaultSource.createDefaultValidator(field, overrideId,
2711 overrideMessages, locale, propertyType, propertyAnnotations);
2712 environment.pop(EnvironmentMessages.class);
2713 return fieldValidator;
2714 }
2715
2716 public FieldValidator createDefaultValidator(ComponentResources resources, String parameterName)
2717 {
2718
2719 EnvironmentMessages em = new EnvironmentMessages(resources.getContainerMessages(), resources.getId());
2720 environment.push(EnvironmentMessages.class, em);
2721 FieldValidator fieldValidator = defaultSource.createDefaultValidator(resources, parameterName);
2722 environment.pop(EnvironmentMessages.class);
2723 return fieldValidator;
2724 }
2725 };
2726 }
2727
2728 /**
2729 * Exposes the Environmental {@link Heartbeat} as an injectable service.
2730 *
2731 * @since 5.2.0
2732 */
2733 public Heartbeat buildHeartbeat()
2734 {
2735 return environmentalBuilder.build(Heartbeat.class);
2736 }
2737
2738 /**
2739 * Contributes the "core" and "core-datefield" {@link JavaScriptStack}s
2740 *
2741 * @since 5.2.0
2742 */
2743 public static void contributeJavaScriptStackSource(MappedConfiguration<String, JavaScriptStack> configuration)
2744 {
2745 configuration.addInstance(InternalConstants.CORE_STACK_NAME, CoreJavaScriptStack.class);
2746 configuration.addInstance("core-datefield", DateFieldStack.class);
2747 }
2748
2749 public static ComponentMessagesSource buildComponentMessagesSource(UpdateListenerHub updateListenerHub, @Autobuild
2750 ComponentMessagesSourceImpl service)
2751 {
2752 updateListenerHub.addUpdateListener(service);
2753
2754 return service;
2755 }
2756
2757 /**
2758 * Contributes:
2759 * <dl>
2760 * <dt>AppCatalog</dt>
2761 * <dd>The Resource defined by {@link SymbolConstants#APPLICATION_CATALOG}</dd>
2762 * <dt>ValidationMessages</dt>
2763 * <dd>Messages used by validators (before:AppCatalog)</dd>
2764 * <dt>
2765 *
2766 * @since 5.2.0
2767 */
2768 public static void contributeComponentMessagesSource(AssetSource assetSource,
2769 @Symbol(SymbolConstants.APPLICATION_CATALOG)
2770 Resource applicationCatalog, OrderedConfiguration<Resource> configuration)
2771 {
2772 configuration.add("ValidationMessages",
2773 assetSource.resourceForPath("org/apache/tapestry5/internal/ValidationMessages.properties"),
2774 "before:AppCatalog");
2775 configuration.add("AppCatalog", applicationCatalog);
2776 }
2777
2778 /**
2779 * Contributes extractors for {@link Meta}, {@link Secure}, {@link ContentType} and {@link WhitelistAccessOnly} annotations.
2780 *
2781 * @since 5.2.0
2782 */
2783 @SuppressWarnings("unchecked")
2784 public static void contributeMetaWorker(MappedConfiguration<Class, MetaDataExtractor> configuration)
2785 {
2786 configuration.addInstance(Meta.class, MetaAnnotationExtractor.class);
2787 configuration.add(Secure.class, new FixedExtractor(MetaDataConstants.SECURE_PAGE));
2788 configuration.addInstance(ContentType.class, ContentTypeExtractor.class);
2789 configuration.add(WhitelistAccessOnly.class, new FixedExtractor(MetaDataConstants.WHITELIST_ONLY_PAGE));
2790 }
2791
2792 /**
2793 * Builds the {@link ComponentTemplateLocator} as a chain of command.
2794 *
2795 * @since 5.2.0
2796 */
2797 @Marker(Primary.class)
2798 public ComponentTemplateLocator buildComponentTemplateLocator(List<ComponentTemplateLocator> configuration)
2799 {
2800 return chainBuilder.build(ComponentTemplateLocator.class, configuration);
2801 }
2802
2803 /**
2804 * Contributes two template locators:
2805 * <dl>
2806 * <dt>Default</dt>
2807 * <dd>Searches for the template on the classpath ({@link DefaultTemplateLocator}</dd>
2808 * <dt>Page</dt>
2809 * <dd>Searches for <em>page</em> templates in the context ({@link PageTemplateLocator})</dd>
2810 * </dl>
2811 *
2812 * @since 5.2.0
2813 */
2814 public static void contributeComponentTemplateLocator(OrderedConfiguration<ComponentTemplateLocator> configuration,
2815 @ContextProvider
2816 AssetFactory contextAssetFactory,
2817 @Symbol(SymbolConstants.APPLICATION_FOLDER) String applicationFolder,
2818 ComponentClassResolver componentClassResolver)
2819 {
2820 configuration.add("Default", new DefaultTemplateLocator());
2821 configuration
2822 .add("Page", new PageTemplateLocator(contextAssetFactory.getRootResource(), componentClassResolver, applicationFolder));
2823
2824 }
2825
2826 /**
2827 * Builds {@link ComponentEventLinkTransformer} service as a chain of command.
2828 *
2829 * @since 5.2.0
2830 */
2831 @Marker(Primary.class)
2832 public ComponentEventLinkTransformer buildComponentEventLinkTransformer(
2833 List<ComponentEventLinkTransformer> configuration)
2834 {
2835 return chainBuilder.build(ComponentEventLinkTransformer.class, configuration);
2836 }
2837
2838 /**
2839 * Builds {@link PageRenderLinkTransformer} service as a chain of command.
2840 *
2841 * @since 5.2.0
2842 */
2843 @Marker(Primary.class)
2844 public PageRenderLinkTransformer buildPageRenderLinkTransformer(List<PageRenderLinkTransformer> configuration)
2845 {
2846 return chainBuilder.build(PageRenderLinkTransformer.class, configuration);
2847 }
2848
2849 /**
2850 * Provides the "LinkTransformer" interceptor for the {@link ComponentEventLinkEncoder} service.
2851 * Other decorations
2852 * should come after LinkTransformer.
2853 *
2854 * @since 5.2.0
2855 */
2856 @Match("ComponentEventLinkEncoder")
2857 public ComponentEventLinkEncoder decorateLinkTransformer(LinkTransformer linkTransformer,
2858 ComponentEventLinkEncoder delegate)
2859 {
2860 return new LinkTransformerInterceptor(linkTransformer, delegate);
2861 }
2862
2863 /**
2864 * In production mode, override {@link UpdateListenerHub} to be an empty placeholder.
2865 */
2866 @Contribute(ServiceOverride.class)
2867 public static void productionModeOverrides(MappedConfiguration<Class, Object> configuration,
2868 @Symbol(SymbolConstants.PRODUCTION_MODE)
2869 boolean productionMode)
2870 {
2871 if (productionMode)
2872 {
2873 configuration.add(UpdateListenerHub.class, new UpdateListenerHub()
2874 {
2875 public void fireCheckForUpdates()
2876 {
2877 }
2878
2879 public void addUpdateListener(UpdateListener listener)
2880 {
2881
2882 }
2883 });
2884 }
2885 }
2886
2887 /**
2888 * Contributes a single default analyzer:
2889 * <dl>
2890 * <dt>LocalhostOnly</dt>
2891 * <dd>Identifies requests from localhost as on client whitelist</dd>
2892 * </dl>
2893 *
2894 * @since 5.3
2895 */
2896 @Contribute(ClientWhitelist.class)
2897 public static void defaultWhitelist(OrderedConfiguration<WhitelistAnalyzer> configuration)
2898 {
2899 configuration.add("LocalhostOnly", new LocalhostOnly());
2900 }
2901 }