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