0001: // Copyright 2006, 2007 The Apache Software Foundation
0002: //
0003: // Licensed under the Apache License, Version 2.0 (the "License");
0004: // you may not use this file except in compliance with the License.
0005: // You may obtain a copy of the License at
0006: //
0007: // http://www.apache.org/licenses/LICENSE-2.0
0008: //
0009: // Unless required by applicable law or agreed to in writing, software
0010: // distributed under the License is distributed on an "AS IS" BASIS,
0011: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0012: // See the License for the specific language governing permissions and
0013: // limitations under the License.
0014:
0015: package org.apache.tapestry.services;
0016:
0017: import java.io.IOException;
0018: import java.lang.annotation.Annotation;
0019: import java.util.Collection;
0020: import java.util.Iterator;
0021: import java.util.List;
0022: import java.util.Map;
0023: import java.util.regex.Pattern;
0024:
0025: import javax.servlet.ServletContext;
0026: import javax.servlet.http.HttpServletRequest;
0027: import javax.servlet.http.HttpServletResponse;
0028:
0029: import org.apache.commons.logging.Log;
0030: import org.apache.tapestry.Asset;
0031: import org.apache.tapestry.Link;
0032: import org.apache.tapestry.MarkupWriter;
0033: import org.apache.tapestry.PageRenderSupport;
0034: import org.apache.tapestry.SelectModel;
0035: import org.apache.tapestry.StreamResponse;
0036: import org.apache.tapestry.TapestryConstants;
0037: import org.apache.tapestry.Translator;
0038: import org.apache.tapestry.Validator;
0039: import org.apache.tapestry.annotations.AfterRender;
0040: import org.apache.tapestry.annotations.AfterRenderBody;
0041: import org.apache.tapestry.annotations.AfterRenderTemplate;
0042: import org.apache.tapestry.annotations.BeforeRenderBody;
0043: import org.apache.tapestry.annotations.BeforeRenderTemplate;
0044: import org.apache.tapestry.annotations.BeginRender;
0045: import org.apache.tapestry.annotations.CleanupRender;
0046: import org.apache.tapestry.annotations.InjectPage;
0047: import org.apache.tapestry.annotations.PageAttached;
0048: import org.apache.tapestry.annotations.PageDetached;
0049: import org.apache.tapestry.annotations.PageLoaded;
0050: import org.apache.tapestry.annotations.Path;
0051: import org.apache.tapestry.annotations.Service;
0052: import org.apache.tapestry.annotations.SetupRender;
0053: import org.apache.tapestry.beaneditor.Validate;
0054: import org.apache.tapestry.corelib.data.GridPagerPosition;
0055: import org.apache.tapestry.dom.Document;
0056: import org.apache.tapestry.grid.GridDataSource;
0057: import org.apache.tapestry.internal.InternalConstants;
0058: import org.apache.tapestry.internal.TapestryInternalUtils;
0059: import org.apache.tapestry.internal.beaneditor.PrimitiveFieldConstraintGenerator;
0060: import org.apache.tapestry.internal.beaneditor.ValidateAnnotationConstraintGenerator;
0061: import org.apache.tapestry.internal.bindings.AssetBindingFactory;
0062: import org.apache.tapestry.internal.bindings.BlockBindingFactory;
0063: import org.apache.tapestry.internal.bindings.ComponentBindingFactory;
0064: import org.apache.tapestry.internal.bindings.LiteralBindingFactory;
0065: import org.apache.tapestry.internal.bindings.MessageBindingFactory;
0066: import org.apache.tapestry.internal.bindings.TranslateBindingFactory;
0067: import org.apache.tapestry.internal.bindings.ValidateBindingFactory;
0068: import org.apache.tapestry.internal.grid.ListGridDataSource;
0069: import org.apache.tapestry.internal.grid.NullDataSource;
0070: import org.apache.tapestry.internal.services.AliasImpl;
0071: import org.apache.tapestry.internal.services.AliasManagerImpl;
0072: import org.apache.tapestry.internal.services.ApplicationGlobalsImpl;
0073: import org.apache.tapestry.internal.services.ApplicationStateManagerImpl;
0074: import org.apache.tapestry.internal.services.ApplicationStatePersistenceStrategySourceImpl;
0075: import org.apache.tapestry.internal.services.ApplicationStateWorker;
0076: import org.apache.tapestry.internal.services.AssetDispatcher;
0077: import org.apache.tapestry.internal.services.AssetInjectionProvider;
0078: import org.apache.tapestry.internal.services.AssetSourceImpl;
0079: import org.apache.tapestry.internal.services.BeanBlockSourceImpl;
0080: import org.apache.tapestry.internal.services.BeanModelSourceImpl;
0081: import org.apache.tapestry.internal.services.BindingSourceImpl;
0082: import org.apache.tapestry.internal.services.ClassResultProcessor;
0083: import org.apache.tapestry.internal.services.ClasspathAssetAliasManagerImpl;
0084: import org.apache.tapestry.internal.services.CommonResourcesInjectionProvider;
0085: import org.apache.tapestry.internal.services.ComponentActionDispatcher;
0086: import org.apache.tapestry.internal.services.ComponentActionRequestHandlerImpl;
0087: import org.apache.tapestry.internal.services.ComponentClassResolverImpl;
0088: import org.apache.tapestry.internal.services.ComponentDefaultProviderImpl;
0089: import org.apache.tapestry.internal.services.ComponentInstanceResultProcessor;
0090: import org.apache.tapestry.internal.services.ComponentInstantiatorSource;
0091: import org.apache.tapestry.internal.services.ComponentLifecycleMethodWorker;
0092: import org.apache.tapestry.internal.services.ComponentMessagesSourceImpl;
0093: import org.apache.tapestry.internal.services.ComponentResourcesInjectionProvider;
0094: import org.apache.tapestry.internal.services.ComponentSourceImpl;
0095: import org.apache.tapestry.internal.services.ComponentWorker;
0096: import org.apache.tapestry.internal.services.ContextImpl;
0097: import org.apache.tapestry.internal.services.CookiesImpl;
0098: import org.apache.tapestry.internal.services.DefaultDataTypeAnalyzer;
0099: import org.apache.tapestry.internal.services.DefaultValidationDelegateCommand;
0100: import org.apache.tapestry.internal.services.DocumentScriptBuilder;
0101: import org.apache.tapestry.internal.services.DocumentScriptBuilderImpl;
0102: import org.apache.tapestry.internal.services.EnumValueEncoderFactory;
0103: import org.apache.tapestry.internal.services.EnvironmentImpl;
0104: import org.apache.tapestry.internal.services.EnvironmentalShadowBuilderImpl;
0105: import org.apache.tapestry.internal.services.EnvironmentalWorker;
0106: import org.apache.tapestry.internal.services.FieldValidatorDefaultSourceImpl;
0107: import org.apache.tapestry.internal.services.FieldValidatorSourceImpl;
0108: import org.apache.tapestry.internal.services.FlashPersistentFieldStrategy;
0109: import org.apache.tapestry.internal.services.GenericValueEncoderFactory;
0110: import org.apache.tapestry.internal.services.HeartbeatImpl;
0111: import org.apache.tapestry.internal.services.InjectBlockWorker;
0112: import org.apache.tapestry.internal.services.InjectComponentWorker;
0113: import org.apache.tapestry.internal.services.InjectPageWorker;
0114: import org.apache.tapestry.internal.services.InjectResourcesWorker;
0115: import org.apache.tapestry.internal.services.InjectStandardStylesheetCommand;
0116: import org.apache.tapestry.internal.services.InjectWorker;
0117: import org.apache.tapestry.internal.services.InternalModule;
0118: import org.apache.tapestry.internal.services.LinkActionResponseGenerator;
0119: import org.apache.tapestry.internal.services.LinkFactory;
0120: import org.apache.tapestry.internal.services.MarkupWriterFactoryImpl;
0121: import org.apache.tapestry.internal.services.MetaDataLocatorImpl;
0122: import org.apache.tapestry.internal.services.MetaWorker;
0123: import org.apache.tapestry.internal.services.MixinAfterWorker;
0124: import org.apache.tapestry.internal.services.MixinWorker;
0125: import org.apache.tapestry.internal.services.ObjectComponentEventResultProcessor;
0126: import org.apache.tapestry.internal.services.OnEventWorker;
0127: import org.apache.tapestry.internal.services.PageLifecycleAnnotationWorker;
0128: import org.apache.tapestry.internal.services.PageRenderDispatcher;
0129: import org.apache.tapestry.internal.services.PageRenderRequestHandlerImpl;
0130: import org.apache.tapestry.internal.services.PageRenderSupportImpl;
0131: import org.apache.tapestry.internal.services.PageResponseRenderer;
0132: import org.apache.tapestry.internal.services.ParameterWorker;
0133: import org.apache.tapestry.internal.services.PersistWorker;
0134: import org.apache.tapestry.internal.services.PersistentFieldManagerImpl;
0135: import org.apache.tapestry.internal.services.PropertyConduitSourceImpl;
0136: import org.apache.tapestry.internal.services.RenderCommandWorker;
0137: import org.apache.tapestry.internal.services.RequestGlobalsImpl;
0138: import org.apache.tapestry.internal.services.RequestImpl;
0139: import org.apache.tapestry.internal.services.RequestPageCache;
0140: import org.apache.tapestry.internal.services.RequestRenderer;
0141: import org.apache.tapestry.internal.services.ResourceCache;
0142: import org.apache.tapestry.internal.services.ResourceDigestGeneratorImpl;
0143: import org.apache.tapestry.internal.services.ResourceStreamer;
0144: import org.apache.tapestry.internal.services.ResponseImpl;
0145: import org.apache.tapestry.internal.services.RetainWorker;
0146: import org.apache.tapestry.internal.services.RootPathDispatcher;
0147: import org.apache.tapestry.internal.services.ServiceAnnotationObjectProvider;
0148: import org.apache.tapestry.internal.services.SessionApplicationStatePersistenceStrategy;
0149: import org.apache.tapestry.internal.services.SessionPersistentFieldStrategy;
0150: import org.apache.tapestry.internal.services.StaticFilesFilter;
0151: import org.apache.tapestry.internal.services.StreamResponseResultProcessor;
0152: import org.apache.tapestry.internal.services.StringResultProcessor;
0153: import org.apache.tapestry.internal.services.StringValueEncoder;
0154: import org.apache.tapestry.internal.services.SupportsInformalParametersWorker;
0155: import org.apache.tapestry.internal.services.TranslatorDefaultSourceImpl;
0156: import org.apache.tapestry.internal.services.TranslatorSourceImpl;
0157: import org.apache.tapestry.internal.services.UnclaimedFieldWorker;
0158: import org.apache.tapestry.internal.services.UpdateListenerHub;
0159: import org.apache.tapestry.internal.services.ValidationConstraintGeneratorImpl;
0160: import org.apache.tapestry.internal.services.ValidationMessagesSourceImpl;
0161: import org.apache.tapestry.internal.services.ValueEncoderSourceImpl;
0162: import org.apache.tapestry.ioc.AnnotationProvider;
0163: import org.apache.tapestry.ioc.Configuration;
0164: import org.apache.tapestry.ioc.Location;
0165: import org.apache.tapestry.ioc.MappedConfiguration;
0166: import org.apache.tapestry.ioc.Messages;
0167: import org.apache.tapestry.ioc.ObjectLocator;
0168: import org.apache.tapestry.ioc.ObjectProvider;
0169: import org.apache.tapestry.ioc.OrderedConfiguration;
0170: import org.apache.tapestry.ioc.ServiceBinder;
0171: import org.apache.tapestry.ioc.ServiceResources;
0172: import org.apache.tapestry.ioc.annotations.Inject;
0173: import org.apache.tapestry.ioc.annotations.InjectService;
0174: import org.apache.tapestry.ioc.annotations.SubModule;
0175: import org.apache.tapestry.ioc.annotations.Symbol;
0176: import org.apache.tapestry.ioc.annotations.Value;
0177: import org.apache.tapestry.ioc.internal.util.InternalUtils;
0178: import org.apache.tapestry.ioc.services.ChainBuilder;
0179: import org.apache.tapestry.ioc.services.ClassFactory;
0180: import org.apache.tapestry.ioc.services.Coercion;
0181: import org.apache.tapestry.ioc.services.CoercionTuple;
0182: import org.apache.tapestry.ioc.services.PipelineBuilder;
0183: import org.apache.tapestry.ioc.services.PropertyAccess;
0184: import org.apache.tapestry.ioc.services.PropertyShadowBuilder;
0185: import org.apache.tapestry.ioc.services.StrategyBuilder;
0186: import org.apache.tapestry.ioc.services.SymbolSource;
0187: import org.apache.tapestry.ioc.services.ThreadLocale;
0188: import org.apache.tapestry.ioc.services.TypeCoercer;
0189: import org.apache.tapestry.ioc.util.StrategyRegistry;
0190: import org.apache.tapestry.runtime.Component;
0191: import org.apache.tapestry.runtime.RenderCommand;
0192: import org.apache.tapestry.translator.DoubleTranslator;
0193: import org.apache.tapestry.translator.IntegerTranslator;
0194: import org.apache.tapestry.translator.LongTranslator;
0195: import org.apache.tapestry.translator.StringTranslator;
0196: import org.apache.tapestry.util.StringToEnumCoercion;
0197: import org.apache.tapestry.validator.Max;
0198: import org.apache.tapestry.validator.MaxLength;
0199: import org.apache.tapestry.validator.Min;
0200: import org.apache.tapestry.validator.MinLength;
0201: import org.apache.tapestry.validator.Regexp;
0202: import org.apache.tapestry.validator.Required;
0203:
0204: /**
0205: * The root module for Tapestry.
0206: */
0207: @SubModule(InternalModule.class)
0208: public final class TapestryModule {
0209: public static void bind(ServiceBinder binder) {
0210: binder.bind(ClasspathAssetAliasManager.class,
0211: ClasspathAssetAliasManagerImpl.class);
0212: binder.bind(PersistentLocale.class, PersistentLocaleImpl.class);
0213: binder.bind(ApplicationStateManager.class,
0214: ApplicationStateManagerImpl.class);
0215: binder.bind(ApplicationStatePersistenceStrategySource.class,
0216: ApplicationStatePersistenceStrategySourceImpl.class);
0217: binder.bind(BindingSource.class, BindingSourceImpl.class);
0218: binder.bind(TranslatorSource.class, TranslatorSourceImpl.class);
0219: binder.bind(PersistentFieldManager.class,
0220: PersistentFieldManagerImpl.class);
0221: binder.bind(FieldValidatorSource.class,
0222: FieldValidatorSourceImpl.class);
0223: binder.bind(ApplicationGlobals.class,
0224: ApplicationGlobalsImpl.class);
0225: binder.bind(AssetSource.class, AssetSourceImpl.class);
0226: binder.bind(Cookies.class, CookiesImpl.class);
0227: binder.bind(Environment.class, EnvironmentImpl.class);
0228: binder.bind(FieldValidatorDefaultSource.class,
0229: FieldValidatorDefaultSourceImpl.class);
0230: binder.bind(RequestGlobals.class, RequestGlobalsImpl.class);
0231: binder.bind(ResourceDigestGenerator.class,
0232: ResourceDigestGeneratorImpl.class);
0233: binder.bind(ValidationConstraintGenerator.class,
0234: ValidationConstraintGeneratorImpl.class);
0235: binder.bind(EnvironmentalShadowBuilder.class,
0236: EnvironmentalShadowBuilderImpl.class);
0237: binder.bind(ComponentSource.class, ComponentSourceImpl.class);
0238: binder.bind(BeanModelSource.class, BeanModelSourceImpl.class);
0239: binder.bind(BeanBlockSource.class, BeanBlockSourceImpl.class);
0240: binder.bind(ComponentDefaultProvider.class,
0241: ComponentDefaultProviderImpl.class);
0242: binder.bind(MarkupWriterFactory.class,
0243: MarkupWriterFactoryImpl.class);
0244: }
0245:
0246: public static Alias build(Log log,
0247:
0248: @Inject
0249: @Symbol(InternalConstants.TAPESTRY_ALIAS_MODE_SYMBOL)
0250: String mode,
0251:
0252: @InjectService("AliasOverrides")
0253: AliasManager overridesManager,
0254:
0255: Collection<AliasContribution> configuration) {
0256: AliasManager manager = new AliasManagerImpl(log, configuration);
0257:
0258: return new AliasImpl(manager, mode, overridesManager);
0259: }
0260:
0261: /**
0262: * A few of the built in services overlap in terms of service interface so we make contributions
0263: * to the Alias service to disambiguate. This ensures that a bare parameter (without an
0264: * InjectService annotation) will chose the correct value without being further qualified.
0265: * <dl>
0266: * <dt>{@link ComponentEventResultProcessor}
0267: * <dd> the master ComponentEventResultProcessor service (rather than one of the other services
0268: * that exist to handle a specific type of result)</li>
0269: * <dt>{@link ObjectRenderer}
0270: * <dd> the master ObjectRenderer service (rather than the one of the other services that
0271: * renders a specific type of object)</li>
0272: * <dt>{@link ClassFactory}
0273: * <dd> the <em>ComponentClassFactory</em> (which will be recreated if the component class
0274: * loader is recreated, on a change to a component class)
0275: * <dt>{@link DataTypeAnalyzer}
0276: * <dd> the <em>DefaultDataTypeAnalyzer</em> service
0277: * </dl>
0278: */
0279: public static void contributeAlias(
0280: Configuration<AliasContribution> configuration,
0281: ObjectLocator locator,
0282:
0283: @InjectService("ComponentClassFactory")
0284: ClassFactory componentClassFactory,
0285:
0286: @InjectService("DefaultDataTypeAnalyzer")
0287: DataTypeAnalyzer dataTypeAnalyzer) {
0288: add(configuration, locator,
0289: ComponentEventResultProcessor.class,
0290: ObjectRenderer.class);
0291:
0292: configuration.add(AliasContribution.create(ClassFactory.class,
0293: componentClassFactory));
0294: configuration.add(AliasContribution.create(
0295: DataTypeAnalyzer.class, dataTypeAnalyzer));
0296: }
0297:
0298: @SuppressWarnings("unchecked")
0299: private static void add(
0300: Configuration<AliasContribution> configuration,
0301: ObjectLocator locator, Class... serviceInterfaces) {
0302: for (Class serviceInterface : serviceInterfaces) {
0303: String name = serviceInterface.getName();
0304: String serviceId = InternalUtils.lastTerm(name);
0305:
0306: AliasContribution contribution = AliasContribution.create(
0307: serviceInterface, locator.getService(serviceId,
0308: serviceInterface));
0309:
0310: configuration.add(contribution);
0311: }
0312: }
0313:
0314: /**
0315: * A companion service to {@linkplain #build(Log, AliasManager, Collection) the Alias service}
0316: * whose configuration contribution define spot overrides to specific services.
0317: */
0318: public static AliasManager buildAliasOverrides(Log log,
0319: Collection<AliasContribution> configuration) {
0320: return new AliasManagerImpl(log, configuration);
0321: }
0322:
0323: /**
0324: * Contributes the factory for serveral built-in binding prefixes ("asset", "literal", prop",
0325: * "block", "component" "message", "validate", "translate").
0326: */
0327: public static void contributeBindingSource(
0328: MappedConfiguration<String, BindingFactory> configuration,
0329:
0330: AssetSource assetSource,
0331:
0332: @InjectService("PropBindingFactory")
0333: BindingFactory propBindingFactory,
0334:
0335: FieldValidatorSource fieldValidatorSource,
0336:
0337: TranslatorSource translatorSource) {
0338: configuration.add(TapestryConstants.LITERAL_BINDING_PREFIX,
0339: new LiteralBindingFactory());
0340: configuration.add(TapestryConstants.PROP_BINDING_PREFIX,
0341: propBindingFactory);
0342: configuration.add("component", new ComponentBindingFactory());
0343: configuration.add("message", new MessageBindingFactory());
0344: configuration.add("validate", new ValidateBindingFactory(
0345: fieldValidatorSource));
0346: configuration.add("translate", new TranslateBindingFactory(
0347: translatorSource));
0348: configuration.add("block", new BlockBindingFactory());
0349: configuration
0350: .add("asset", new AssetBindingFactory(assetSource));
0351: }
0352:
0353: public static void contributeClasspathAssetAliasManager(
0354: MappedConfiguration<String, String> configuration,
0355:
0356: // @Inject not needed, because this isn't a service builder method
0357: @Symbol("tapestry.scriptaculous.path")
0358: String scriptaculousPath) {
0359: configuration.add("tapestry/", "org/apache/tapestry/");
0360:
0361: configuration.add("scriptaculous/", scriptaculousPath + "/");
0362: }
0363:
0364: public static void contributeComponentClassResolver(
0365: Configuration<LibraryMapping> configuration) {
0366: configuration.add(new LibraryMapping("core",
0367: "org.apache.tapestry.corelib"));
0368: }
0369:
0370: /**
0371: * Adds a number of standard component class transform workers:
0372: * <ul>
0373: * <li>Retain -- allows fields to retain their values between requests</li>
0374: * <li>Persist -- allows fields to store their their value persistently between requests</li>
0375: * <li>Parameter -- identifies parameters based on the
0376: * {@link org.apache.tapestry.annotations.Parameter} annotation</li>
0377: * <li>Component -- identifies embedded components based on the
0378: * {@link org.apache.tapestry.annotations.Component} annotation</li>
0379: * <li>Mixin -- adds a mixin as part of a component's implementation</li>
0380: * <li>Environment -- allows fields to contain values extracted from the {@link Environment}
0381: * service</li>
0382: * <li>Inject -- used with the {@link Inject} annotation, when a value is supplied</li>
0383: * <li>InjectResources -- used with the {@link Inject} annotation, when no value is supplied</li>
0384: * <li>InjectPage -- adds code to allow access to other pages via the {@link InjectPage} field
0385: * annotation</li>
0386: * <li>InjectBlock -- allows a block from the template to be injected into a field</li>
0387: * <li>SupportsInformalParameters -- checks for the annotation</li>
0388: * <li>Meta -- checks for meta data and adds it to the component model
0389: * <li>ApplicationState -- converts fields that reference application state objects
0390: * <li>UnclaimedField -- identifies unclaimed fields and resets them to null/0/false at the end
0391: * of the request</li>
0392: * <li>RenderCommand -- ensures all components also implement {@link RenderCommand}</li>
0393: * <li>SetupRender, BeginRender, etc. -- correspond to component render phases and annotations</li>
0394: * </ul>
0395: */
0396: public static void contributeComponentClassTransformWorker(
0397: OrderedConfiguration<ComponentClassTransformWorker> configuration,
0398:
0399: ObjectLocator locator,
0400:
0401: @InjectService("MasterObjectProvider")
0402: ObjectProvider objectProvider,
0403:
0404: InjectionProvider injectionProvider,
0405:
0406: Environment environment,
0407:
0408: ComponentClassResolver resolver,
0409:
0410: RequestPageCache requestPageCache,
0411:
0412: AssetSource assetSource,
0413:
0414: SymbolSource symbolSource,
0415:
0416: BindingSource bindingsource,
0417:
0418: ApplicationStateManager applicationStateManager) {
0419: // TODO: Proper scheduling of all of this. Since a given field or method should
0420: // only have a single annotation, the order doesn't matter so much, as long as
0421: // UnclaimedField is last.
0422:
0423: configuration.add("Meta", new MetaWorker());
0424: configuration.add("ApplicationState",
0425: new ApplicationStateWorker(applicationStateManager));
0426: configuration.add("Inject", new InjectWorker(objectProvider,
0427: locator));
0428:
0429: // These next two "reinterpret" what @Inject does (in the presence of
0430: // a particular field type and must come before the normal Inject annotation
0431: // processing.
0432:
0433: configuration.add("InjectResources", new InjectResourcesWorker(
0434: locator, injectionProvider), "before:Inject");
0435: configuration.add("InjectBlock", new InjectBlockWorker(),
0436: "before:Inject");
0437:
0438: configuration.add("MixinAfter", new MixinAfterWorker());
0439: configuration.add("Component", new ComponentWorker(resolver));
0440: configuration.add("Environment", new EnvironmentalWorker(
0441: environment));
0442: configuration.add("Mixin", new MixinWorker(resolver));
0443: configuration.add("OnEvent", new OnEventWorker());
0444: configuration.add("SupportsInformalParameters",
0445: new SupportsInformalParametersWorker());
0446: configuration.add("InjectPage", new InjectPageWorker(
0447: requestPageCache, resolver));
0448: configuration.add("InjectComponent",
0449: new InjectComponentWorker());
0450: configuration.add("RenderCommand", new RenderCommandWorker());
0451:
0452: // Default values for parameters are often some form of injection, so make sure
0453: // that Parameter fields are processed after injections.
0454:
0455: configuration.add("Parameter", new ParameterWorker(
0456: bindingsource), "after:Inject*");
0457:
0458: // Workers for the component rendering state machine methods; this is in typical
0459: // execution order.
0460:
0461: add(configuration, TransformConstants.SETUP_RENDER_SIGNATURE,
0462: SetupRender.class, false);
0463: add(configuration, TransformConstants.BEGIN_RENDER_SIGNATURE,
0464: BeginRender.class, false);
0465: add(configuration,
0466: TransformConstants.BEFORE_RENDER_TEMPLATE_SIGNATURE,
0467: BeforeRenderTemplate.class, false);
0468: add(configuration,
0469: TransformConstants.BEFORE_RENDER_BODY_SIGNATURE,
0470: BeforeRenderBody.class, false);
0471:
0472: // These phases operate in reverse order.
0473:
0474: add(configuration,
0475: TransformConstants.AFTER_RENDER_BODY_SIGNATURE,
0476: AfterRenderBody.class, true);
0477: add(configuration,
0478: TransformConstants.AFTER_RENDER_TEMPLATE_SIGNATURE,
0479: AfterRenderTemplate.class, true);
0480: add(configuration, TransformConstants.AFTER_RENDER_SIGNATURE,
0481: AfterRender.class, true);
0482: add(configuration, TransformConstants.CLEANUP_RENDER_SIGNATURE,
0483: CleanupRender.class, true);
0484:
0485: // Ideally, these should be ordered pretty late in the process to make sure there are no
0486: // side effects
0487: // with other workers that do work inside the page lifecycle methods.
0488:
0489: add(configuration, PageLoaded.class,
0490: TransformConstants.CONTAINING_PAGE_DID_LOAD_SIGNATURE,
0491: "pageLoaded");
0492: add(
0493: configuration,
0494: PageAttached.class,
0495: TransformConstants.CONTAINING_PAGE_DID_ATTACH_SIGNATURE,
0496: "pageAttached");
0497: add(
0498: configuration,
0499: PageDetached.class,
0500: TransformConstants.CONTAINING_PAGE_DID_DETACH_SIGNATURE,
0501: "pageDetached");
0502:
0503: configuration.add("Retain", new RetainWorker());
0504: configuration.add("Persist", new PersistWorker());
0505: configuration.add("UnclaimedField", new UnclaimedFieldWorker(),
0506: "after:*");
0507: }
0508:
0509: /**
0510: * Adds the {@link #buildDefaultDataTypeAnalyzer(Map) DefaultDatatTypeAnalyzer} to the
0511: * configuration, ordered explicitly last.
0512: */
0513: public static void contributeDataTypeAnalyzer(
0514: OrderedConfiguration<DataTypeAnalyzer> configuration,
0515: @InjectService("DefaultDataTypeAnalyzer")
0516: DataTypeAnalyzer defaultDataTypeAnalyzer) {
0517: configuration
0518: .add("Default", defaultDataTypeAnalyzer, "after:*");
0519: }
0520:
0521: /**
0522: * Maps property types to data type names
0523: * <ul>
0524: * <li>String --> text
0525: * <li>Number --> text
0526: * <li>Enum --> enum
0527: * <li>Boolean --> checkbox
0528: * </ul>
0529: */
0530: public static void contributeDefaultDataTypeAnalyzer(
0531: MappedConfiguration<Class, String> configuration) {
0532: // This is a special case contributed to avoid exceptions when a property type can't be
0533: // matched. DefaultDataTypeAnalyzer converts the empty string to null.
0534:
0535: configuration.add(Object.class, "");
0536:
0537: configuration.add(String.class, "text");
0538:
0539: // This may change; as currently implemented, "text" refers more to the edit component
0540: // (TextField) than to
0541: // the "flavor" of data.
0542:
0543: configuration.add(Number.class, "text");
0544: configuration.add(Enum.class, "enum");
0545: configuration.add(Boolean.class, "checkbox");
0546: }
0547:
0548: public static void contributeBeanBlockSource(
0549: Configuration<BeanBlockContribution> configuration) {
0550: addEditBlock(configuration, "text", "text");
0551: addEditBlock(configuration, "enum", "enum");
0552: addEditBlock(configuration, "checkbox", "checkbox");
0553:
0554: addDisplayBlock(configuration, "enum", "enum");
0555: }
0556:
0557: private static void addEditBlock(
0558: Configuration<BeanBlockContribution> configuration,
0559: String dataType, String blockId) {
0560: configuration.add(new BeanBlockContribution(dataType,
0561: "PropertyEditBlocks", blockId, true));
0562: }
0563:
0564: private static void addDisplayBlock(
0565: Configuration<BeanBlockContribution> configuration,
0566: String dataType, String blockId) {
0567: configuration.add(new BeanBlockContribution(dataType,
0568: "PropertyDisplayBlocks", blockId, false));
0569: }
0570:
0571: /**
0572: * Contributes the basic set of validators:
0573: * <ul>
0574: * <li>required</li>
0575: * <li>minlength</li>
0576: * <li>maxlength</li>
0577: * <li>min</li>
0578: * <li>max</li>
0579: * <li>regexp</li>
0580: * </ul>
0581: */
0582: public static void contributeFieldValidatorSource(
0583: MappedConfiguration<String, Validator> configuration) {
0584: configuration.add("required", new Required());
0585: configuration.add("minlength", new MinLength());
0586: configuration.add("maxlength", new MaxLength());
0587: configuration.add("min", new Min());
0588: configuration.add("max", new Max());
0589: configuration.add("regexp", new Regexp());
0590: }
0591:
0592: /**
0593: * Contributes the elemental providers:
0594: * <ul>
0595: * <li>ComponentResources -- give component access to its resources</li>
0596: * <li>CommonResources -- access to properties of resources (log, messages, etc.)</li>
0597: * <li>Asset -- injection of assets (triggered via {@link Path} annotation), with the path
0598: * relative to the component class</li>
0599: * </ul>
0600: */
0601: public static void contributeInjectionProvider(
0602: OrderedConfiguration<InjectionProvider> configuration,
0603:
0604: SymbolSource symbolSource,
0605:
0606: AssetSource assetSource) {
0607: configuration.add("ComponentResources",
0608: new ComponentResourcesInjectionProvider());
0609: configuration.add("CommonResources",
0610: new CommonResourcesInjectionProvider());
0611: configuration.add("Asset", new AssetInjectionProvider(
0612: symbolSource, assetSource));
0613: }
0614:
0615: /**
0616: * Contributes two object providers:
0617: * <ul>
0618: * <li>Alias: Searches by type among {@linkplain AliasContribution contributions} to the
0619: * {@link Alias} service</li>
0620: * <li>Asset: Checks for the {@link Path} annotation, and injects an {@link Asset}</li>
0621: * <li>Service: Injects based on the {@link Service} annotation, if present</li>
0622: * </ul>
0623: */
0624: public static void contributeMasterObjectProvider(
0625: OrderedConfiguration<ObjectProvider> configuration,
0626:
0627: @InjectService("Alias")
0628: final Alias alias,
0629:
0630: @InjectService("AssetObjectProvider")
0631: ObjectProvider assetObjectProvider) {
0632: // There's a nasty web of dependencies related to Alias; this wrapper class lets us
0633: // defer instantiating the Alias service implementation just long enough to defuse those
0634: // dependencies.
0635:
0636: ObjectProvider wrapper = new ObjectProvider() {
0637: public <T> T provide(Class<T> objectType,
0638: AnnotationProvider annotationProvider,
0639: ObjectLocator locator) {
0640: return alias.getObjectProvider().provide(objectType,
0641: annotationProvider, locator);
0642: }
0643: };
0644:
0645: configuration.add("Alias", wrapper, "after:Value");
0646:
0647: configuration.add("Asset", assetObjectProvider, "before:Alias");
0648:
0649: configuration.add("Service",
0650: new ServiceAnnotationObjectProvider(), "before:Alias");
0651: }
0652:
0653: /**
0654: * Contributes filter "StaticFilesFilter" that identifies requests for static resources and
0655: * terminates the pipeline by returning false. Generally, most filters should be ordered after
0656: * this filter.
0657: */
0658: public static void contributeRequestHandler(
0659: OrderedConfiguration<RequestFilter> configuration,
0660: Context context,
0661: final RequestExceptionHandler exceptionHandler) {
0662: RequestFilter staticFilesFilter = new StaticFilesFilter(context);
0663:
0664: configuration.add("StaticFiles", staticFilesFilter);
0665:
0666: RequestFilter errorFilter = new RequestFilter() {
0667: public boolean service(Request request, Response response,
0668: RequestHandler handler) throws IOException {
0669: try {
0670: return handler.service(request, response);
0671: } catch (IOException ex) {
0672: // Pass it through.
0673: throw ex;
0674: } catch (Throwable ex) {
0675: exceptionHandler.handleRequestException(ex);
0676:
0677: // We assume a reponse has been sent and there's no need to handle the request
0678: // further.
0679:
0680: return true;
0681: }
0682: }
0683: };
0684:
0685: configuration.add("ErrorFilter", errorFilter);
0686: }
0687:
0688: /**
0689: * Contributes the basic set of default translators:
0690: * <ul>
0691: * <li>Integer</li>
0692: * <li>String</li>
0693: * <li>Long</li>
0694: * <li>Double</li>
0695: * </li>
0696: */
0697: public static void contributeTranslatorDefaultSource(
0698: MappedConfiguration<Class, Translator> configuration) {
0699: configuration.add(Integer.class, new IntegerTranslator());
0700: configuration.add(String.class, new StringTranslator());
0701: configuration.add(Long.class, new LongTranslator());
0702: configuration.add(Double.class, new DoubleTranslator());
0703: }
0704:
0705: /**
0706: * Contributes the basic set of named translators:
0707: * <ul>
0708: * <li>integer</li>
0709: * <li>string</li>
0710: * <li>long</li>
0711: * <li>double</li>
0712: * </ul>
0713: */
0714: public static void contributeTranslatorSource(
0715: MappedConfiguration<String, Translator> configuration) {
0716: // Fortunately, the translators are tiny, so we don't have to worry about the slight
0717: // duplication between this and TranslatorDefaultSource, though it is a pain to keep the two
0718: // organized (perhaps they should be joined together into a single service, where we
0719: // identify a name and a match type).
0720:
0721: configuration.add("integer", new IntegerTranslator());
0722: configuration.add("string", new StringTranslator());
0723: configuration.add("long", new LongTranslator());
0724: configuration.add("double", new DoubleTranslator());
0725: }
0726:
0727: /**
0728: * Adds coercions:
0729: * <ul>
0730: * <li>String to {@link SelectModel}
0731: * <li>Map to {@link SelectModel}
0732: * <li>List to {@link GridDataSource}
0733: * <li>null to {@link GridDataSource}
0734: * <li>String to {@link GridPagerPosition}
0735: * <li>List to {@link SelectModel}
0736: * </ul>
0737: */
0738: public static void contributeTypeCoercer(
0739: Configuration<CoercionTuple> configuration) {
0740: add(configuration, String.class, SelectModel.class,
0741: new Coercion<String, SelectModel>() {
0742: public SelectModel coerce(String input) {
0743: return TapestryInternalUtils
0744: .toSelectModel(input);
0745: }
0746: });
0747:
0748: add(configuration, Map.class, SelectModel.class,
0749: new Coercion<Map, SelectModel>() {
0750: @SuppressWarnings("unchecked")
0751: public SelectModel coerce(Map input) {
0752: return TapestryInternalUtils
0753: .toSelectModel(input);
0754: }
0755: });
0756:
0757: add(configuration, List.class, GridDataSource.class,
0758: new Coercion<List, GridDataSource>() {
0759: public GridDataSource coerce(List input) {
0760: return new ListGridDataSource(input);
0761: }
0762: });
0763:
0764: add(configuration, void.class, GridDataSource.class,
0765: new Coercion<Void, GridDataSource>() {
0766: private final GridDataSource _source = new NullDataSource();
0767:
0768: public GridDataSource coerce(Void input) {
0769: return _source;
0770: }
0771: });
0772:
0773: add(configuration, String.class, GridPagerPosition.class,
0774: new StringToEnumCoercion<GridPagerPosition>(
0775: GridPagerPosition.class));
0776:
0777: add(configuration, List.class, SelectModel.class,
0778: new Coercion<List, SelectModel>() {
0779: @SuppressWarnings("unchecked")
0780: public SelectModel coerce(List input) {
0781: return TapestryInternalUtils
0782: .toSelectModel(input);
0783: }
0784: });
0785:
0786: add(configuration, String.class, Pattern.class,
0787: new Coercion<String, Pattern>() {
0788: public Pattern coerce(String input) {
0789: return Pattern.compile(input);
0790: }
0791: });
0792:
0793: }
0794:
0795: /**
0796: * Adds built-in constraint generators:
0797: * <ul>
0798: * <li>PrimtiveField -- primitive fields are always required
0799: * <li>ValidateAnnotation -- adds constraints from a {@link Validate} annotation
0800: * </ul>
0801: */
0802: public static void contributeValidationConstraintGenerator(
0803: OrderedConfiguration<ValidationConstraintGenerator> configuration) {
0804: configuration.add("PrimitiveField",
0805: new PrimitiveFieldConstraintGenerator());
0806: configuration.add("ValidateAnnotation",
0807: new ValidateAnnotationConstraintGenerator());
0808: }
0809:
0810: private static <S, T> void add(
0811: Configuration<CoercionTuple> configuration,
0812: Class<S> sourceType, Class<T> targetType,
0813: Coercion<S, T> coercion) {
0814: CoercionTuple<S, T> tuple = new CoercionTuple<S, T>(sourceType,
0815: targetType, coercion);
0816:
0817: configuration.add(tuple);
0818: }
0819:
0820: private static void add(
0821: OrderedConfiguration<ComponentClassTransformWorker> configuration,
0822: Class<? extends Annotation> annotationClass,
0823: MethodSignature lifecycleMethodSignature, String methodAlias) {
0824: ComponentClassTransformWorker worker = new PageLifecycleAnnotationWorker(
0825: annotationClass, lifecycleMethodSignature, methodAlias);
0826:
0827: String name = TapestryInternalUtils.lastTerm(annotationClass
0828: .getName());
0829:
0830: configuration.add(name, worker);
0831: }
0832:
0833: private static void add(
0834: OrderedConfiguration<ComponentClassTransformWorker> configuration,
0835: MethodSignature signature,
0836: Class<? extends Annotation> annotationClass, boolean reverse) {
0837: // make the name match the annotation class name.
0838:
0839: String name = TapestryInternalUtils.lastTerm(annotationClass
0840: .getName());
0841:
0842: configuration.add(name, new ComponentLifecycleMethodWorker(
0843: signature, annotationClass, reverse));
0844: }
0845:
0846: private final ChainBuilder _chainBuilder;
0847:
0848: private final PipelineBuilder _pipelineBuilder;
0849:
0850: private final RequestGlobals _requestGlobals;
0851:
0852: private final ApplicationGlobals _applicationGlobals;
0853:
0854: private final PropertyShadowBuilder _shadowBuilder;
0855:
0856: private final RequestPageCache _requestPageCache;
0857:
0858: private final PageResponseRenderer _pageResponseRenderer;
0859:
0860: private final Environment _environment;
0861:
0862: private final StrategyBuilder _strategyBuilder;
0863:
0864: private final ComponentInstantiatorSource _componentInstantiatorSource;
0865:
0866: private final LinkFactory _linkFactory;
0867:
0868: private final PropertyAccess _propertyAccess;
0869:
0870: private final ClassFactory _componentClassFactory;
0871:
0872: public TapestryModule(PipelineBuilder pipelineBuilder,
0873:
0874: PropertyShadowBuilder shadowBuilder,
0875:
0876: RequestGlobals requestGlobals,
0877:
0878: ApplicationGlobals applicationGlobals,
0879:
0880: ChainBuilder chainBuilder,
0881:
0882: RequestPageCache requestPageCache,
0883:
0884: PageResponseRenderer pageResponseRenderer,
0885:
0886: Environment environment,
0887:
0888: StrategyBuilder strategyBuilder,
0889:
0890: ComponentInstantiatorSource componentInstantiatorSource,
0891:
0892: LinkFactory linkFactory,
0893:
0894: PropertyAccess propertyAccess,
0895:
0896: @InjectService("ComponentClassFactory")
0897: ClassFactory componentClassFactory) {
0898: _pipelineBuilder = pipelineBuilder;
0899: _shadowBuilder = shadowBuilder;
0900: _requestGlobals = requestGlobals;
0901: _applicationGlobals = applicationGlobals;
0902: _chainBuilder = chainBuilder;
0903: _requestPageCache = requestPageCache;
0904: _pageResponseRenderer = pageResponseRenderer;
0905: _environment = environment;
0906: _strategyBuilder = strategyBuilder;
0907: _componentInstantiatorSource = componentInstantiatorSource;
0908: _linkFactory = linkFactory;
0909: _propertyAccess = propertyAccess;
0910: _componentClassFactory = componentClassFactory;
0911: }
0912:
0913: public Context build(ApplicationGlobals globals) {
0914: return _shadowBuilder.build(globals, "context", Context.class);
0915: }
0916:
0917: public ComponentClassResolver buildComponentClassResolver(
0918: ServiceResources resources) {
0919: ComponentClassResolverImpl service = resources
0920: .autobuild(ComponentClassResolverImpl.class);
0921:
0922: // Allow the resolver to clean its cache when the source is invalidated
0923:
0924: _componentInstantiatorSource.addInvalidationListener(service);
0925:
0926: return service;
0927: }
0928:
0929: /**
0930: * Builds the source of {@link Messages} containing validation messages. The contributions are
0931: * paths to message bundles (resource paths within the classpath); the default contribution is
0932: * "org/apache/tapestry/internal/ValidationMessages".
0933: */
0934: public ValidationMessagesSource build(
0935: Collection<String> configuration,
0936: UpdateListenerHub updateListenerHub,
0937:
0938: @InjectService("ClasspathAssetFactory")
0939: AssetFactory classpathAssetFactory) {
0940: ValidationMessagesSourceImpl service = new ValidationMessagesSourceImpl(
0941: configuration, classpathAssetFactory.getRootResource());
0942:
0943: updateListenerHub.addUpdateListener(service);
0944:
0945: return service;
0946: }
0947:
0948: public MetaDataLocator buildMetaDataLocator(
0949: ServiceResources resources) {
0950: MetaDataLocatorImpl service = resources
0951: .autobuild(MetaDataLocatorImpl.class);
0952:
0953: _componentInstantiatorSource.addInvalidationListener(service);
0954:
0955: return service;
0956: }
0957:
0958: /**
0959: * Builds a proxy to the current PageRenderSupport inside this thread's {@link Environment}.
0960: */
0961: public PageRenderSupport build(EnvironmentalShadowBuilder builder) {
0962: return builder.build(PageRenderSupport.class);
0963: }
0964:
0965: /**
0966: * Allows the exact steps in the component class transformation process to be defined.
0967: */
0968: public ComponentClassTransformWorker build(
0969: List<ComponentClassTransformWorker> configuration) {
0970: return _chainBuilder.build(ComponentClassTransformWorker.class,
0971: configuration);
0972: }
0973:
0974: public DataTypeAnalyzer build(List<DataTypeAnalyzer> configuration) {
0975: return _chainBuilder.build(DataTypeAnalyzer.class,
0976: configuration);
0977: }
0978:
0979: /**
0980: * A chain of command for providing values for {@link org.apache.tapestry.annotations.Inject}-ed
0981: * fields in component classes. The service's configuration can be extended to allow for
0982: * different automatic injections (based on some combination of field type and field name).
0983: */
0984:
0985: public InjectionProvider build(List<InjectionProvider> configuration) {
0986: return _chainBuilder.build(InjectionProvider.class,
0987: configuration);
0988: }
0989:
0990: /**
0991: * Controls setup and cleanup of the environment during page rendering (the generation of a
0992: * markup stream response for the client web browser).
0993: */
0994: public PageRenderInitializer build(
0995: final List<PageRenderCommand> configuration) {
0996: return new PageRenderInitializer() {
0997: public void cleanup(MarkupWriter writer) {
0998: Iterator<PageRenderCommand> i = InternalUtils
0999: .reverseIterator(configuration);
1000:
1001: while (i.hasNext())
1002: i.next().cleanup(_environment);
1003:
1004: _environment.clear();
1005: }
1006:
1007: public void setup(MarkupWriter writer) {
1008: _environment.clear();
1009:
1010: _environment.push(MarkupWriter.class, writer);
1011: _environment.push(Document.class, writer.getDocument());
1012:
1013: for (PageRenderCommand command : configuration)
1014: command.setup(_environment);
1015: }
1016: };
1017: }
1018:
1019: /** Initializes the application. */
1020: public ApplicationInitializer build(Log log,
1021: List<ApplicationInitializerFilter> configuration) {
1022: ApplicationInitializer terminator = new ApplicationInitializer() {
1023: public void initializeApplication(Context context) {
1024: _applicationGlobals.store(context);
1025: }
1026: };
1027:
1028: return _pipelineBuilder.build(log,
1029: ApplicationInitializer.class,
1030: ApplicationInitializerFilter.class, configuration,
1031: terminator);
1032: }
1033:
1034: public HttpServletRequestHandler build(Log log,
1035: List<HttpServletRequestFilter> configuration,
1036:
1037: @InjectService("RequestHandler")
1038: final RequestHandler handler) {
1039: HttpServletRequestHandler terminator = new HttpServletRequestHandler() {
1040: public boolean service(HttpServletRequest request,
1041: HttpServletResponse response) throws IOException {
1042: _requestGlobals.store(request, response);
1043:
1044: return handler.service(new RequestImpl(request),
1045: new ResponseImpl(response));
1046: }
1047: };
1048:
1049: return _pipelineBuilder.build(log,
1050: HttpServletRequestHandler.class,
1051: HttpServletRequestFilter.class, configuration,
1052: terminator);
1053: }
1054:
1055: public RequestHandler build(Log log,
1056: List<RequestFilter> configuration,
1057: @InjectService("MasterDispatcher")
1058: final Dispatcher masterDispatcher) {
1059: RequestHandler terminator = new RequestHandler() {
1060: public boolean service(Request request, Response response)
1061: throws IOException {
1062: _requestGlobals.store(request, response);
1063:
1064: return masterDispatcher.dispatch(request, response);
1065: }
1066: };
1067:
1068: return _pipelineBuilder.build(log, RequestHandler.class,
1069: RequestFilter.class, configuration, terminator);
1070: }
1071:
1072: public ServletApplicationInitializer build(Log log,
1073: List<ServletApplicationInitializerFilter> configuration,
1074: @InjectService("ApplicationInitializer")
1075: final ApplicationInitializer initializer) {
1076: ServletApplicationInitializer terminator = new ServletApplicationInitializer() {
1077: public void initializeApplication(ServletContext context) {
1078: _applicationGlobals.store(context);
1079:
1080: // And now, down the (Web) ApplicationInitializer pipeline ...
1081:
1082: initializer.initializeApplication(new ContextImpl(
1083: context));
1084: }
1085: };
1086:
1087: return _pipelineBuilder.build(log,
1088: ServletApplicationInitializer.class,
1089: ServletApplicationInitializerFilter.class,
1090: configuration, terminator);
1091: }
1092:
1093: public ComponentEventResultProcessor build(
1094: Map<Class, ComponentEventResultProcessor> configuration) {
1095: // A slight hack!
1096:
1097: configuration.put(Object.class,
1098: new ObjectComponentEventResultProcessor(configuration
1099: .keySet()));
1100:
1101: StrategyRegistry<ComponentEventResultProcessor> registry = StrategyRegistry
1102: .newInstance(ComponentEventResultProcessor.class,
1103: configuration);
1104:
1105: return _strategyBuilder.build(registry);
1106: }
1107:
1108: /**
1109: * The default data type analyzer is the final analyzer consulted and identifies the type
1110: * entirely pased on the property type, working against its own configuration (mapping property
1111: * type class to data type).
1112: */
1113: public DataTypeAnalyzer buildDefaultDataTypeAnalyzer(
1114: ServiceResources resources) {
1115: DefaultDataTypeAnalyzer service = resources
1116: .autobuild(DefaultDataTypeAnalyzer.class);
1117:
1118: _componentInstantiatorSource.addInvalidationListener(service);
1119:
1120: return service;
1121: }
1122:
1123: public TranslatorDefaultSource buildTranslatorDefaultSource(
1124: ServiceResources resources) {
1125: TranslatorDefaultSourceImpl service = resources
1126: .autobuild(TranslatorDefaultSourceImpl.class);
1127:
1128: _componentInstantiatorSource.addInvalidationListener(service);
1129:
1130: return service;
1131: }
1132:
1133: public ObjectRenderer build(StrategyBuilder strategyBuilder,
1134: Map<Class, ObjectRenderer> configuration) {
1135: StrategyRegistry<ObjectRenderer> registry = StrategyRegistry
1136: .newInstance(ObjectRenderer.class, configuration);
1137:
1138: return _strategyBuilder.build(registry);
1139: }
1140:
1141: public static ComponentMessagesSource build(
1142: UpdateListenerHub updateListenerHub,
1143:
1144: @InjectService("ContextAssetFactory")
1145: AssetFactory contextAssetFactory,
1146:
1147: @Inject
1148: @Value("WEB-INF/${tapestry.app-name}.properties")
1149: String appCatalog) {
1150: ComponentMessagesSourceImpl service = new ComponentMessagesSourceImpl(
1151: contextAssetFactory.getRootResource(), appCatalog);
1152:
1153: updateListenerHub.addUpdateListener(service);
1154:
1155: return service;
1156: }
1157:
1158: /**
1159: * Returns a {@link ClassFactory} that can be used to create extra classes around component
1160: * classes. This ClassFactory will be cleared whenever an underlying component class is
1161: * discovered to have changed. Use of this class factory implies that your code will become
1162: * aware of this (if necessary) to discard any cached object (alas, this currently involves
1163: * dipping into the internals side to register for the correct notifications). Failure to
1164: * properly clean up can result in really nasty PermGen space memory leaks.
1165: */
1166: public ClassFactory buildComponentClassFactory() {
1167: return _shadowBuilder.build(_componentInstantiatorSource,
1168: "classFactory", ClassFactory.class);
1169: }
1170:
1171: public ComponentEventResultProcessor buildComponentInstanceResultProcessor(
1172: Log log) {
1173: return new ComponentInstanceResultProcessor(_requestPageCache,
1174: _linkFactory, log);
1175: }
1176:
1177: /**
1178: * Ordered contributions to the MasterDispatcher service allow different URL matching strategies
1179: * to occur.
1180: */
1181: public Dispatcher buildMasterDispatcher(
1182: List<Dispatcher> configuration) {
1183: return _chainBuilder.build(Dispatcher.class, configuration);
1184: }
1185:
1186: public PropertyConduitSource buildPropertyConduitSource() {
1187: PropertyConduitSourceImpl service = new PropertyConduitSourceImpl(
1188: _propertyAccess, _componentClassFactory);
1189:
1190: _componentInstantiatorSource.addInvalidationListener(service);
1191:
1192: return service;
1193: }
1194:
1195: /**
1196: * Builds a shadow of the RequestGlobals.request property. Note again that the shadow can be an
1197: * ordinary singleton, even though RequestGlobals is perthread.
1198: */
1199: public Request buildRequest() {
1200: return _shadowBuilder.build(_requestGlobals, "request",
1201: Request.class);
1202: }
1203:
1204: /**
1205: * Builds a shadow of the RequestGlobals.response property. Note again that the shadow can be an
1206: * ordinary singleton, even though RequestGlobals is perthread.
1207: */
1208: public Response buildResponse() {
1209: return _shadowBuilder.build(_requestGlobals, "response",
1210: Response.class);
1211: }
1212:
1213: /** Contributes the default "session" strategy. */
1214: public void contributeApplicationStatePersistenceStrategySource(
1215: MappedConfiguration<String, ApplicationStatePersistenceStrategy> configuration,
1216:
1217: Request request) {
1218: configuration
1219: .add("session",
1220: new SessionApplicationStatePersistenceStrategy(
1221: request));
1222: }
1223:
1224: public void contributeAssetSource(
1225: MappedConfiguration<String, AssetFactory> configuration,
1226: @InjectService("ContextAssetFactory")
1227: AssetFactory contextAssetFactory,
1228:
1229: @InjectService("ClasspathAssetFactory")
1230: AssetFactory classpathAssetFactory) {
1231: configuration.add("context", contextAssetFactory);
1232: configuration.add("classpath", classpathAssetFactory);
1233: }
1234:
1235: /**
1236: * Contributes handlers for the following types:
1237: * <dl>
1238: * <dt>Object</dt>
1239: * <dd>Failure case, added to provide a more useful exception message</dd>
1240: * <dt>ActionResponseGenerator</dt>
1241: * <dd>Returns the ActionResponseGenerator; this sometimes occurs when a component generates
1242: * events whose return values are converted to ActionResponseGenerators (this handles that
1243: * bubble up case).</dd>
1244: * <dt>Link</dt>
1245: * <dd>Wraps the Link to send a redirect</dd>
1246: * <dt>String</dt>
1247: * <dd>The name of the page to render the response (after a redirect)</dd>
1248: * </dl>
1249: */
1250: public void contributeComponentEventResultProcessor(
1251: @InjectService("ComponentInstanceResultProcessor")
1252: ComponentEventResultProcessor componentInstanceProcessor,
1253: ComponentClassResolver componentClassResolver,
1254: MappedConfiguration<Class, ComponentEventResultProcessor> configuration) {
1255: configuration
1256: .add(
1257: ActionResponseGenerator.class,
1258: new ComponentEventResultProcessor<ActionResponseGenerator>() {
1259: public ActionResponseGenerator processComponentEvent(
1260: ActionResponseGenerator value,
1261: Component component,
1262: String methodDescripion) {
1263: return value;
1264: }
1265: });
1266:
1267: configuration.add(Link.class,
1268: new ComponentEventResultProcessor<Link>() {
1269:
1270: public ActionResponseGenerator processComponentEvent(
1271: Link value, Component component,
1272: String methodDescripion) {
1273: return new LinkActionResponseGenerator(value);
1274: }
1275: });
1276:
1277: configuration.add(String.class, new StringResultProcessor(
1278: _requestPageCache, _linkFactory));
1279:
1280: configuration.add(Class.class,
1281: new ClassResultProcessor(componentClassResolver,
1282: _requestPageCache, _linkFactory));
1283:
1284: configuration.add(Component.class, componentInstanceProcessor);
1285:
1286: configuration.add(StreamResponse.class,
1287: new StreamResponseResultProcessor());
1288: }
1289:
1290: public void contributeMasterDispatcher(
1291: OrderedConfiguration<Dispatcher> configuration,
1292:
1293: ClasspathAssetAliasManager aliasManager,
1294:
1295: ResourceCache resourceCache,
1296:
1297: ResourceStreamer streamer,
1298:
1299: PageRenderRequestHandler pageRenderRequestHandler,
1300:
1301: ComponentActionRequestHandler componentActionRequestHandler,
1302:
1303: ComponentClassResolver componentClassResolver,
1304:
1305: @Symbol("tapestry.start-page-name")
1306: String startPageName) {
1307: // Looks for the root path and renders the start page
1308:
1309: configuration.add("RootPath", new RootPathDispatcher(
1310: componentClassResolver, pageRenderRequestHandler,
1311: _pageResponseRenderer, startPageName), "before:Asset");
1312:
1313: // This goes first because an asset to be streamed may have an file extension, such as
1314: // ".html", that will confuse the later dispatchers.
1315:
1316: configuration.add("Asset", new AssetDispatcher(streamer,
1317: aliasManager, resourceCache), "before:PageRender");
1318:
1319: configuration.add("PageRender", new PageRenderDispatcher(
1320: componentClassResolver, pageRenderRequestHandler));
1321:
1322: configuration.add("ComponentAction",
1323: new ComponentActionDispatcher(
1324: componentActionRequestHandler),
1325: "after:PageRender");
1326: }
1327:
1328: /**
1329: * Contributes meta data defaults:
1330: * <dl>
1331: * <dt>{@link PersistentFieldManagerImpl#META_KEY}
1332: * <dd>{@link PersistentFieldManagerImpl#DEFAULT_STRATEGY}
1333: * <dt>{@link TapestryConstants#RESPONSE_CONTENT_TYPE}
1334: * <dd>text/html
1335: * <dt>{@link TapestryConstants#RESPONSE_ENCODING}
1336: * <dd>UTF-8
1337: * </dl>
1338: *
1339: * @param configuration
1340: */
1341: public void contributeMetaDataLocator(
1342: MappedConfiguration<String, String> configuration) {
1343: configuration.add(PersistentFieldManagerImpl.META_KEY,
1344: PersistentFieldManagerImpl.DEFAULT_STRATEGY);
1345:
1346: configuration.add(TapestryConstants.RESPONSE_CONTENT_TYPE,
1347: "text/html");
1348: configuration.add(TapestryConstants.RESPONSE_ENCODING, "UTF-8");
1349: }
1350:
1351: /**
1352: * Contributes a default object renderer for type Object, plus specialized renderers for
1353: * {@link Request} and {@link Location}.
1354: */
1355: public void contributeObjectRenderer(
1356: MappedConfiguration<Class, ObjectRenderer> configuration,
1357:
1358: @InjectService("LocationRenderer")
1359: ObjectRenderer locationRenderer,
1360:
1361: final TypeCoercer typeCoercer) {
1362: configuration.add(Object.class, new ObjectRenderer() {
1363: public void render(Object object, MarkupWriter writer) {
1364: writer.write(String.valueOf(object));
1365: }
1366: });
1367:
1368: configuration.add(Request.class, new RequestRenderer());
1369:
1370: configuration.add(Location.class, locationRenderer);
1371:
1372: ObjectRenderer preformatted = new ObjectRenderer<Object>() {
1373: public void render(Object object, MarkupWriter writer) {
1374: writer.element("pre");
1375: writer.write(typeCoercer.coerce(object, String.class));
1376: writer.end();
1377: }
1378: };
1379:
1380: configuration.add(ClassTransformation.class, preformatted);
1381: }
1382:
1383: public void contributePageRenderInitializer(
1384: OrderedConfiguration<PageRenderCommand> configuration,
1385:
1386: ThreadLocale threadLocale,
1387:
1388: @Path("org/apache/tapestry/default.css")
1389: Asset stylesheetAsset,
1390:
1391: @Path("org/apache/tapestry/field-error-marker.png")
1392: Asset fieldErrorIcon,
1393:
1394: ValidationMessagesSource validationMessagesSource,
1395:
1396: final SymbolSource symbolSource,
1397:
1398: final AssetSource assetSource) {
1399: configuration.add("PageRenderSupport", new PageRenderCommand() {
1400: public void cleanup(Environment environment) {
1401: environment.pop(PageRenderSupport.class);
1402:
1403: Document document = environment.peek(Document.class);
1404:
1405: DocumentScriptBuilder builder = environment
1406: .pop(DocumentScriptBuilder.class);
1407:
1408: builder.updateDocument(document);
1409: }
1410:
1411: public void setup(Environment environment) {
1412: DocumentScriptBuilder builder = new DocumentScriptBuilderImpl();
1413:
1414: environment.push(DocumentScriptBuilder.class, builder);
1415: environment.push(PageRenderSupport.class,
1416: new PageRenderSupportImpl(builder,
1417: symbolSource, assetSource));
1418: }
1419: });
1420:
1421: configuration.add("Heartbeat", new PageRenderCommand() {
1422: public void cleanup(Environment environment) {
1423: environment.pop(Heartbeat.class).end();
1424: }
1425:
1426: public void setup(Environment environment) {
1427: HeartbeatImpl heartbeat = new HeartbeatImpl();
1428:
1429: heartbeat.begin();
1430:
1431: environment.push(Heartbeat.class, heartbeat);
1432: }
1433: });
1434:
1435: configuration.add("InjectStandardStylesheet",
1436: new InjectStandardStylesheetCommand(stylesheetAsset));
1437:
1438: configuration.add("DefaultValidationDelegate",
1439: new DefaultValidationDelegateCommand(threadLocale,
1440: validationMessagesSource, fieldErrorIcon));
1441: }
1442:
1443: /**
1444: * Contributes several strategies:
1445: * <dl>
1446: * <dt>session
1447: * <dd>Values are stored in the {@link Session}
1448: * <dt>flash
1449: * <dd>Values are stored in the {@link Session}, until the next request (for the page)
1450: * <dt>client
1451: * <dd>Values are encoded into URLs (or hidden form fields)
1452: * </dl>
1453: */
1454: public void contributePersistentFieldManager(
1455: MappedConfiguration<String, PersistentFieldStrategy> configuration,
1456:
1457: Request request,
1458:
1459: @InjectService("ClientPersistentFieldStrategy")
1460: PersistentFieldStrategy clientStrategy) {
1461: configuration.add("session",
1462: new SessionPersistentFieldStrategy(request));
1463: configuration.add("flash", new FlashPersistentFieldStrategy(
1464: request));
1465: configuration.add("client", clientStrategy);
1466: }
1467:
1468: public void contributeValidationMessagesSource(
1469: Configuration<String> configuration) {
1470: configuration
1471: .add("org/apache/tapestry/internal/ValidationMessages");
1472: }
1473:
1474: public ValueEncoderSource build(
1475: Map<Class, ValueEncoderFactory> configuration) {
1476: ValueEncoderSourceImpl service = new ValueEncoderSourceImpl(
1477: configuration);
1478:
1479: _componentInstantiatorSource.addInvalidationListener(service);
1480:
1481: return service;
1482: }
1483:
1484: /**
1485: * Contributes {@link ValueEncoderFactory}s for types:
1486: * <ul>
1487: * <li>String
1488: * <li>Enum
1489: * </ul>
1490: *
1491: * @param configuration
1492: */
1493: @SuppressWarnings("unchecked")
1494: public static void contributeValueEncoderSource(
1495: MappedConfiguration<Class, ValueEncoderFactory> configuration) {
1496: configuration.add(String.class, new GenericValueEncoderFactory(
1497: new StringValueEncoder()));
1498: configuration.add(Enum.class, new EnumValueEncoderFactory());
1499: }
1500:
1501: public PageRenderRequestHandler buildPageRenderRequestHandler(
1502: List<PageRenderRequestFilter> configuration, Log log,
1503: ServiceResources resources) {
1504: return _pipelineBuilder.build(log,
1505: PageRenderRequestHandler.class,
1506: PageRenderRequestFilter.class, configuration, resources
1507: .autobuild(PageRenderRequestHandlerImpl.class));
1508: }
1509:
1510: public ComponentActionRequestHandler buildComponentActionRequestHandler(
1511: List<ComponentActionRequestFilter> configuration, Log log,
1512: ServiceResources resources) {
1513: return _pipelineBuilder
1514: .build(
1515: log,
1516: ComponentActionRequestHandler.class,
1517: ComponentActionRequestFilter.class,
1518: configuration,
1519: resources
1520: .autobuild(ComponentActionRequestHandlerImpl.class));
1521: }
1522:
1523: }
|