Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java?view=diff&rev=476282&r1=476281&r2=476282 ============================================================================== --- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java (original) +++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java Fri Nov 17 11:51:39 2006 @@ -12,635 +12,670 @@ // See the License for the specific language governing permissions and // limitations under the License. -package org.apache.tapestry.services; - -import java.io.IOException; -import java.lang.annotation.Annotation; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.logging.Log; -import org.apache.tapestry.MarkupWriter; -import org.apache.tapestry.annotations.AfterRender; -import org.apache.tapestry.annotations.AfterRenderBody; -import org.apache.tapestry.annotations.AfterRenderTemplate; -import org.apache.tapestry.annotations.BeforeRenderBody; -import org.apache.tapestry.annotations.BeforeRenderTemplate; -import org.apache.tapestry.annotations.BeginRender; -import org.apache.tapestry.annotations.CleanupRender; -import org.apache.tapestry.annotations.PostBeginRender; -import org.apache.tapestry.annotations.PreBeginRender; -import org.apache.tapestry.annotations.SetupRender; -import org.apache.tapestry.internal.InternalConstants; -import org.apache.tapestry.internal.bindings.LiteralBindingFactory; -import org.apache.tapestry.internal.services.ApplicationGlobalsImpl; -import org.apache.tapestry.internal.services.BindingSourceImpl; -import org.apache.tapestry.internal.services.ComponentClassFactoryImpl; -import org.apache.tapestry.internal.services.ComponentClassResolverImpl; -import org.apache.tapestry.internal.services.ComponentEventDispatcher; -import org.apache.tapestry.internal.services.ComponentInstantiatorSource; -import org.apache.tapestry.internal.services.ComponentLifecycleMethodWorker; -import org.apache.tapestry.internal.services.ComponentResourcesInjectionProvider; -import org.apache.tapestry.internal.services.ComponentSourceImpl; -import org.apache.tapestry.internal.services.ComponentWorker; -import org.apache.tapestry.internal.services.DefaultInjectionProvider; -import org.apache.tapestry.internal.services.EnvironmentImpl; -import org.apache.tapestry.internal.services.EnvironmentalWorker; -import org.apache.tapestry.internal.services.InfrastructureImpl; -import org.apache.tapestry.internal.services.InfrastructureManagerImpl; -import org.apache.tapestry.internal.services.InjectWorker; -import org.apache.tapestry.internal.services.InjectionProvider; -import org.apache.tapestry.internal.services.InternalModule; -import org.apache.tapestry.internal.services.LinkFactory; -import org.apache.tapestry.internal.services.MarkupWriterImpl; -import org.apache.tapestry.internal.services.MixinWorker; -import org.apache.tapestry.internal.services.OnEventWorker; -import org.apache.tapestry.internal.services.PageRenderDispatcher; -import org.apache.tapestry.internal.services.PageRenderSupportImpl; -import org.apache.tapestry.internal.services.PageResponseRenderer; -import org.apache.tapestry.internal.services.ParameterWorker; -import org.apache.tapestry.internal.services.PersistWorker; -import org.apache.tapestry.internal.services.PersistentFieldManagerImpl; -import org.apache.tapestry.internal.services.RequestGlobalsImpl; -import org.apache.tapestry.internal.services.RequestPageCache; -import org.apache.tapestry.internal.services.RetainWorker; -import org.apache.tapestry.internal.services.SessionPersistentFieldStrategy; -import org.apache.tapestry.internal.services.StaticFilesFilter; -import org.apache.tapestry.internal.services.SupportsInformalParametersWorker; -import org.apache.tapestry.internal.services.UnclaimedFieldWorker; -import org.apache.tapestry.internal.services.WebContextImpl; -import org.apache.tapestry.internal.services.WebRequestImpl; -import org.apache.tapestry.internal.services.WebResponseImpl; -import org.apache.tapestry.ioc.Configuration; -import org.apache.tapestry.ioc.IOCUtilities; -import org.apache.tapestry.ioc.MappedConfiguration; -import org.apache.tapestry.ioc.ObjectProvider; -import org.apache.tapestry.ioc.OrderedConfiguration; -import org.apache.tapestry.ioc.ServiceLocator; -import org.apache.tapestry.ioc.annotations.Contribute; -import org.apache.tapestry.ioc.annotations.Id; -import org.apache.tapestry.ioc.annotations.Inject; -import org.apache.tapestry.ioc.annotations.InjectService; -import org.apache.tapestry.ioc.annotations.Lifecycle; -import org.apache.tapestry.ioc.annotations.SubModule; -import org.apache.tapestry.ioc.services.ChainBuilder; -import org.apache.tapestry.ioc.services.ClassFactory; -import org.apache.tapestry.ioc.services.PipelineBuilder; -import org.apache.tapestry.ioc.services.PropertyShadowBuilder; -import org.apache.tapestry.ioc.services.TypeCoercer; - -/** - * The root module for Tapestry. - */ [EMAIL PROTECTED]("tapestry") [EMAIL PROTECTED]( -{ InternalModule.class }) -public final class TapestryModule -{ - private final ChainBuilder _chainBuilder; - - private final PipelineBuilder _pipelineBuilder; - - private final RequestGlobals _requestGlobals; - - private final ApplicationGlobals _applicationGlobals; - - private final PropertyShadowBuilder _shadowBuilder; - - private final RequestPageCache _requestPageCache; - - private final PageResponseRenderer _pageResponseRenderer; - - private final WebRequest _request; - - private final Environment _environment; - - // Yes, you can inject services defined by this module into this module. The service proxy is - // created without instantiating the module itself. We're careful about making as many - // service builder and contributor methods static as possible to avoid recursive build - // exceptions. - - public TapestryModule(@InjectService("tapestry.ioc.PipelineBuilder") - PipelineBuilder pipelineBuilder, @InjectService("tapestry.ioc.PropertyShadowBuilder") - PropertyShadowBuilder shadowBuilder, @InjectService("RequestGlobals") - RequestGlobals requestGlobals, @InjectService("ApplicationGlobals") - ApplicationGlobals applicationGlobals, @InjectService("tapestry.ioc.ChainBuilder") - ChainBuilder chainBuilder, @InjectService("tapestry.internal.RequestPageCache") - RequestPageCache requestPageCache, @InjectService("tapestry.internal.PageResponseRenderer") - PageResponseRenderer pageResponseRenderer, @Inject("infrastructure:request") - WebRequest request, @InjectService("Environment") - Environment environment) - { - _pipelineBuilder = pipelineBuilder; - _shadowBuilder = shadowBuilder; - _requestGlobals = requestGlobals; - _applicationGlobals = applicationGlobals; - _chainBuilder = chainBuilder; - _requestPageCache = requestPageCache; - _pageResponseRenderer = pageResponseRenderer; - _request = request; - _environment = environment; - } - - private static <T> void add(Configuration<InfrastructureContribution> configuration, - ServiceLocator locator, Class<T> serviceInterface) - { - String className = serviceInterface.getName(); - int dotx = className.lastIndexOf('.'); - String serviceId = className.substring(dotx + 1); - - // Convert first character to lower case to form the property name. - - String propertyName = serviceId.substring(0, 1).toLowerCase() + serviceId.substring(1); - - T service = locator.getService(serviceId, serviceInterface); - - InfrastructureContribution contribution = new InfrastructureContribution(propertyName, - service); - - configuration.add(contribution); - } - - public static ApplicationGlobals buildApplicationGlobals() - { - return new ApplicationGlobalsImpl(); - } - - public WebContext buildWebContext(@InjectService("ApplicationGlobals") - ApplicationGlobals globals) - { - return _shadowBuilder.build(globals, "webContext", WebContext.class); - } - - public ServletApplicationInitializer buildServletApplicationInitializer(Log log, - List<ServletApplicationInitializerFilter> configuration, - @InjectService("ApplicationInitializer") - final ApplicationInitializer initializer) - { - ServletApplicationInitializer terminator = new ServletApplicationInitializer() - { - public void initializeApplication(ServletContext context) - { - _applicationGlobals.store(context); - - // And now, down the (Web) ApplicationInitializer pipeline ... - - initializer.initializeApplication(new WebContextImpl(context)); - } - }; - - return _pipelineBuilder.build( - log, - ServletApplicationInitializer.class, - ServletApplicationInitializerFilter.class, - configuration, - terminator); - } - - /** Initializes the application. */ - public ApplicationInitializer buildApplicationInitializer(Log log, - List<ApplicationInitializerFilter> configuration) - { - ApplicationInitializer terminator = new ApplicationInitializer() - { - public void initializeApplication(WebContext context) - { - _applicationGlobals.store(context); - } - }; - - return _pipelineBuilder.build( - log, - ApplicationInitializer.class, - ApplicationInitializerFilter.class, - configuration, - terminator); - } - - /** - * Allows the exact steps in the component class transformation process to be defined. - */ - public ComponentClassTransformWorker buildComponentClassTransformWorker( - List<ComponentClassTransformWorker> configuration) - { - return _chainBuilder.build(ComponentClassTransformWorker.class, configuration); - } - - public HttpServletRequestHandler buildHttpServletRequestHandler(Log log, - List<HttpServletRequestFilter> configuration, @InjectService("WebRequestHandler") - final WebRequestHandler handler) - { - HttpServletRequestHandler terminator = new HttpServletRequestHandler() - { - public boolean service(HttpServletRequest request, HttpServletResponse response) - throws IOException - { - _requestGlobals.store(request, response); - - return handler.service(new WebRequestImpl(request), new WebResponseImpl(response)); - } - }; - - return _pipelineBuilder.build( - log, - HttpServletRequestHandler.class, - HttpServletRequestFilter.class, - configuration, - terminator); - } - - public static Infrastructure buildInfrastructure(Log log, - Collection<InfrastructureContribution> configuration) - { - InfrastructureManager manager = new InfrastructureManagerImpl(log, configuration); - - return new InfrastructureImpl(manager); - } - - public static MarkupWriterFactory buildMarkupWriterFactory() - { - // Temporary ... - return new MarkupWriterFactory() - { - public MarkupWriter newMarkupWriter() - { - return new MarkupWriterImpl(); - } - }; - } - - /** - * Ordered contributions to the MasterDispatcher service allow different URL matching strategies - * to occur. - */ - public Dispatcher buildMasterDispatcher(List<Dispatcher> configuration) - { - return _chainBuilder.build(Dispatcher.class, configuration); - } - - @Lifecycle("perthread") - public static RequestGlobals buildRequestGlobals() - { - return new RequestGlobalsImpl(); - } - - /** - * Builds a shadow of the RequestGlobals.request property. Note again that the shadow can be an - * ordinary singleton, even though RequestGlobals is perthread. - */ - public WebRequest buildWebRequest() - { - return _shadowBuilder.build(_requestGlobals, "request", WebRequest.class); - } - - /** - * Builds a shadow of the RequestGlobals.response property. Note again that the shadow can be an - * ordinary singleton, even though RequestGlobals is perthread. - */ - public WebResponse buildWebResponse() - { - return _shadowBuilder.build(_requestGlobals, "response", WebResponse.class); - } - - public WebRequestHandler buildWebRequestHandler(Log log, List<WebRequestFilter> configuration, - @InjectService("MasterDispatcher") - final Dispatcher masterDispatcher) - { - WebRequestHandler terminator = new WebRequestHandler() - { - public boolean service(WebRequest request, WebResponse response) throws IOException - { - _requestGlobals.store(request, response); - - return masterDispatcher.dispatch(request, response); - } - }; - - return _pipelineBuilder.build( - log, - WebRequestHandler.class, - WebRequestFilter.class, - configuration, - terminator); - } - - /** - * Contributes filter "tapestry.StaticFilesFilter" that idenfies requests for static resources - * and terminates the pipeline by returning false. Generally, most filters should be ordered - * after this filter. - */ - public static void contributeWebRequestHandler( - OrderedConfiguration<WebRequestFilter> configuration, @InjectService("WebContext") - WebContext webContext, - @InjectService("tapestry.internal.DefaultRequestExceptionHandler") - final RequestExceptionHandler exceptionHandler) - { - WebRequestFilter staticFilesFilter = new StaticFilesFilter(webContext); - - configuration.add("StaticFilesFilter", staticFilesFilter); - - WebRequestFilter errorFilter = new WebRequestFilter() - { - public boolean service(WebRequest request, WebResponse response, - WebRequestHandler handler) throws IOException - { - try - { - return handler.service(request, response); - } - catch (RuntimeException ex) - { - exceptionHandler.handleRequestException(ex); - - // We assume a reponse has been sent and there's no need to handle the request - // further. - - return true; - } - } - }; - - configuration.add("ErrorFilter", errorFilter); - } - - /** - * Contributes properties: componentNameExpander, markupWriterFactory, request - */ - public static void contributeInfrastructure( - Configuration<InfrastructureContribution> configuration, ServiceLocator locator, - @InjectService("WebRequest") - WebRequest request, @InjectService("WebResponse") - WebResponse response, @InjectService("tapestry.ioc.TypeCoercer") - TypeCoercer typeCoercer) - { - add(configuration, locator, MarkupWriterFactory.class); - add(configuration, locator, PersistentFieldManager.class); - add(configuration, locator, Environment.class); - add(configuration, locator, ComponentSource.class); - - configuration.add(new InfrastructureContribution("request", request)); - configuration.add(new InfrastructureContribution("response", response)); - configuration.add(new InfrastructureContribution("typeCoercer", typeCoercer)); - } - - /** - * Contributes the [EMAIL PROTECTED] ObjectProvider} provided by [EMAIL PROTECTED] Infrastructure#getObjectProvider()} - * mapped to the provider prefix "infrastructure". - */ - @Contribute("tapestry.ioc.MasterObjectProvider") - public static void contributeInfrastructureToMasterObjectProvider( - MappedConfiguration<String, ObjectProvider> configuration, - @InjectService("Infrastructure") - Infrastructure infrastructure) - { - configuration.add("infrastructure", infrastructure.getObjectProvider()); - } - - public void contributeMasterDispatcher(OrderedConfiguration<Dispatcher> configuration, - @InjectService("tapestry.internal.LinkFactory") - LinkFactory linkFactory) - { - configuration.add( - "HTML", - new PageRenderDispatcher(_pageResponseRenderer, _requestPageCache)); - - // This goes after the HTML one so that the "." in ".html" doesn't confuse it. - - configuration.add("ComponentEvent", new ComponentEventDispatcher(_requestPageCache, - linkFactory), "after:HTML"); - } - - public static ComponentClassResolver buildComponentClassResolver( - @InjectService("tapestry.internal.ComponentInstantiatorSource") - ComponentInstantiatorSource source, Collection<LibraryMapping> configuration) - { - ComponentClassResolverImpl service = new ComponentClassResolverImpl(source, configuration); - - // Allow the resolver to clean its cache when the source is invalidated - - source.addInvalidationListener(service); - - return service; - } - - public static void contributeComponentClassResolver(Configuration<LibraryMapping> configuration) - { - configuration.add(new LibraryMapping("core", "org.apache.tapestry.corelib")); - } - - public static BindingSource buildBindingSource(Map<String, BindingFactory> configuration) - { - return new BindingSourceImpl(configuration); - } - - /** Contributes the factory for "literal:" bindings. */ - public static void contributeBindingSource( - MappedConfiguration<String, BindingFactory> configuration, - @InjectService("tapestry.internal.PropBindingFactory") - BindingFactory propBindingFactory) - { - configuration.add(InternalConstants.LITERAL_BINDING_PREFIX, new LiteralBindingFactory()); - configuration.add(InternalConstants.PROP_BINDING_PREFIX, propBindingFactory); - } - - /** - * Returns a [EMAIL PROTECTED] ClassFactory} that can be used to create extra classes around component - * classes. - */ - public static ClassFactory buildComponentClassFactory(Log log, - @InjectService("tapestry.internal.ComponentInstantiatorSource") - ComponentInstantiatorSource source) - { - return new ComponentClassFactoryImpl(log, source); - } - - /** - * A chain of command for providing values for [EMAIL PROTECTED] org.apache.tapestry.annotations.Inject}-ed - * fields in component classes. The service's configuration can be extended to allow for - * different automatic injections (based on some combination of field type and field name). - */ - - public InjectionProvider buildInjectionProvider(List<InjectionProvider> configuration) - { - return _chainBuilder.build(InjectionProvider.class, configuration); - } - - /** - * Contributes the elemental providers: - * <ul> - * <li>ComponentResources -- give component access to its resources</li> - * <li>Default -- looks for a unique IoC service that matches the field type</li> - * </ul> - */ - public static void contributeInjectionProvider( - OrderedConfiguration<InjectionProvider> configuration) - { - configuration.add("ComponentResources", new ComponentResourcesInjectionProvider()); - configuration.add("Default", new DefaultInjectionProvider(), "after:*.*"); - } - - /** - * Adds a number of standard component class transform workers: - * <ul> - * <li>Retain -- allows fields to retain their values between requests</li> - * <li>Persist -- allows fields to store their their value persistently between requests</li> - * <li>Parameter -- identifies parameters based on the - * [EMAIL PROTECTED] org.apache.tapestry.annotations.Parameter} annotation</li> - * <li>Component -- identifies embedded components based on the - * [EMAIL PROTECTED] org.apache.tapestry.annotations.Component} annotation</li> - * <li>Mixin -- adds a mixin as part of a component's implementation</li> - * <li>Environment -- allows fields to contain values extracted from the [EMAIL PROTECTED] Environment} - * service</li> - * <li>SupportsInformalParameters -- checks for the annotation</li> - * <li>UnclaimedField -- identifies unclaimed fields and resets them to null/0/false at the end - * of the request</li> - * <li>SetupRender, BeginRender, etc. -- correspond to component render phases and annotations</li> - * </ul> - */ - public static void contributeComponentClassTransformWorker( - OrderedConfiguration<ComponentClassTransformWorker> configuration, - ServiceLocator locator, @InjectService("tapestry.ioc.MasterObjectProvider") - ObjectProvider objectProvider, @InjectService("InjectionProvider") - InjectionProvider injectionProvider, @InjectService("Environment") - Environment environment, @InjectService("tapestry.ComponentClassResolver") - ComponentClassResolver resolver) - { - // TODO: Proper scheduling of all of this. Since a given field or method should - // only have a single annotation, the order doesn't matter so much, as long as - // UnclaimedField is last. - - configuration.add("Inject", new InjectWorker(objectProvider, locator, injectionProvider)); - configuration.add("Parameter", new ParameterWorker()); - configuration.add("Component", new ComponentWorker(resolver)); - configuration.add("Environment", new EnvironmentalWorker(environment)); - configuration.add("Mixin", new MixinWorker(resolver)); - configuration.add("OnEvent", new OnEventWorker()); - configuration.add("SupportsInformalParameters", new SupportsInformalParametersWorker()); - - // Workers for the component rendering state machine methods; this is in typical - // execution order. - - add(configuration, TransformConstants.SETUP_RENDER_SIGNATURE, SetupRender.class, false); - add( - configuration, - TransformConstants.PRE_BEGIN_RENDER_SIGNATURE, - PreBeginRender.class, - false); - add(configuration, TransformConstants.BEGIN_RENDER_SIGNATURE, BeginRender.class, false); - add( - configuration, - TransformConstants.POST_BEGIN_RENDER_SIGNATURE, - PostBeginRender.class, - false); - add( - configuration, - TransformConstants.BEFORE_RENDER_TEMPLATE_SIGNATURE, - BeforeRenderTemplate.class, - false); - add( - configuration, - TransformConstants.BEFORE_RENDER_BODY_SIGNATURE, - BeforeRenderBody.class, - false); - - // These phases operate in reverse order. - - add( - configuration, - TransformConstants.AFTER_RENDER_BODY_SIGNATURE, - AfterRenderBody.class, - true); - add( - configuration, - TransformConstants.AFTER_RENDER_TEMPLATE_SIGNATURE, - AfterRenderTemplate.class, - true); - add(configuration, TransformConstants.AFTER_RENDER_SIGNATURE, AfterRender.class, true); - add(configuration, TransformConstants.CLEANUP_RENDER_SIGNATURE, CleanupRender.class, true); - - configuration.add("Retain", new RetainWorker()); - configuration.add("Persist", new PersistWorker()); - configuration.add("UnclaimedField", new UnclaimedFieldWorker(), "after:*.*"); - } - - private static void add(OrderedConfiguration<ComponentClassTransformWorker> configuration, - MethodSignature signature, Class<? extends Annotation> annotationClass, boolean reverse) - { - // make the name match the annotation class name. - - String name = IOCUtilities.toSimpleId(annotationClass.getName()); - - configuration.add(name, new ComponentLifecycleMethodWorker(signature, annotationClass, - reverse)); - } - - @Lifecycle("perthread") - public static Environment buildEnvironment() - { - return new EnvironmentImpl(); - } - - /** - * A service that acts primarily as a configuration point for render initializers (each - * implements Runnable). All of these initializers are invoked at the start of the page - * rendering process. The primary use of this is to initialize environmental services inside the - * [EMAIL PROTECTED] Environment}. The Environment is cleared before any of the contributions are - * executed. - */ - public Runnable buildPageRenderInitializer(final Collection<Runnable> configuration) - { - return new Runnable() - { - public void run() - { - _environment.clear(); - - for (Runnable r : configuration) - r.run(); - } - }; - } - - public void contributePageRenderInitializer(Configuration<Runnable> configuration) - { - // I'm not happy with this lifecycle, per-se. Instead of Runnable, I think we need an - // interface more tailored to Environment, with perhaps a second method for the end of - // the page render. - - configuration.add(new Runnable() - { - public void run() - { - _environment.push(PageRenderSupport.class, new PageRenderSupportImpl()); - } - }); - } - - /** A public service since extensions may provide new persistent strategies. */ - public static PersistentFieldManager buildPersistentFieldManager( - Map<String, PersistentFieldStrategy> configuration) - { - return new PersistentFieldManagerImpl(configuration); - } - - /** - * Contributes the "session" strategy. - */ - public void contributePersistentFieldManager( - MappedConfiguration<String, PersistentFieldStrategy> configuration) - { - configuration.add("session", new SessionPersistentFieldStrategy(_request)); - } - - public ComponentSource buildComponentSource( - @InjectService("tapestry.internal.RequestPageCache") - RequestPageCache pageCache) - { - return new ComponentSourceImpl(pageCache); - } -} +package org.apache.tapestry.services; + +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.logging.Log; +import org.apache.tapestry.MarkupWriter; +import org.apache.tapestry.annotations.AfterRender; +import org.apache.tapestry.annotations.AfterRenderBody; +import org.apache.tapestry.annotations.AfterRenderTemplate; +import org.apache.tapestry.annotations.BeforeRenderBody; +import org.apache.tapestry.annotations.BeforeRenderTemplate; +import org.apache.tapestry.annotations.BeginRender; +import org.apache.tapestry.annotations.CleanupRender; +import org.apache.tapestry.annotations.PostBeginRender; +import org.apache.tapestry.annotations.PreBeginRender; +import org.apache.tapestry.annotations.SetupRender; +import org.apache.tapestry.internal.InternalConstants; +import org.apache.tapestry.internal.bindings.LiteralBindingFactory; +import org.apache.tapestry.internal.services.ApplicationGlobalsImpl; +import org.apache.tapestry.internal.services.BindingSourceImpl; +import org.apache.tapestry.internal.services.ComponentClassFactoryImpl; +import org.apache.tapestry.internal.services.ComponentClassResolverImpl; +import org.apache.tapestry.internal.services.ComponentEventDispatcher; +import org.apache.tapestry.internal.services.ComponentInstantiatorSource; +import org.apache.tapestry.internal.services.ComponentLifecycleMethodWorker; +import org.apache.tapestry.internal.services.ComponentResourcesInjectionProvider; +import org.apache.tapestry.internal.services.ComponentSourceImpl; +import org.apache.tapestry.internal.services.ComponentWorker; +import org.apache.tapestry.internal.services.DefaultInjectionProvider; +import org.apache.tapestry.internal.services.EnvironmentImpl; +import org.apache.tapestry.internal.services.EnvironmentalWorker; +import org.apache.tapestry.internal.services.HeartbeatImpl; +import org.apache.tapestry.internal.services.InfrastructureImpl; +import org.apache.tapestry.internal.services.InfrastructureManagerImpl; +import org.apache.tapestry.internal.services.InjectWorker; +import org.apache.tapestry.internal.services.InjectionProvider; +import org.apache.tapestry.internal.services.InternalModule; +import org.apache.tapestry.internal.services.LinkFactory; +import org.apache.tapestry.internal.services.MarkupWriterImpl; +import org.apache.tapestry.internal.services.MixinWorker; +import org.apache.tapestry.internal.services.OnEventWorker; +import org.apache.tapestry.internal.services.PageRenderDispatcher; +import org.apache.tapestry.internal.services.PageRenderSupportImpl; +import org.apache.tapestry.internal.services.PageResponseRenderer; +import org.apache.tapestry.internal.services.ParameterWorker; +import org.apache.tapestry.internal.services.PersistWorker; +import org.apache.tapestry.internal.services.PersistentFieldManagerImpl; +import org.apache.tapestry.internal.services.RequestGlobalsImpl; +import org.apache.tapestry.internal.services.RequestPageCache; +import org.apache.tapestry.internal.services.RetainWorker; +import org.apache.tapestry.internal.services.SessionPersistentFieldStrategy; +import org.apache.tapestry.internal.services.StaticFilesFilter; +import org.apache.tapestry.internal.services.SupportsInformalParametersWorker; +import org.apache.tapestry.internal.services.UnclaimedFieldWorker; +import org.apache.tapestry.internal.services.WebContextImpl; +import org.apache.tapestry.internal.services.WebRequestImpl; +import org.apache.tapestry.internal.services.WebResponseImpl; +import org.apache.tapestry.internal.util.InternalUtils; +import org.apache.tapestry.ioc.Configuration; +import org.apache.tapestry.ioc.IOCUtilities; +import org.apache.tapestry.ioc.MappedConfiguration; +import org.apache.tapestry.ioc.ObjectProvider; +import org.apache.tapestry.ioc.OrderedConfiguration; +import org.apache.tapestry.ioc.ServiceLocator; +import org.apache.tapestry.ioc.annotations.Contribute; +import org.apache.tapestry.ioc.annotations.Id; +import org.apache.tapestry.ioc.annotations.Inject; +import org.apache.tapestry.ioc.annotations.InjectService; +import org.apache.tapestry.ioc.annotations.Lifecycle; +import org.apache.tapestry.ioc.annotations.SubModule; +import org.apache.tapestry.ioc.services.ChainBuilder; +import org.apache.tapestry.ioc.services.ClassFactory; +import org.apache.tapestry.ioc.services.PipelineBuilder; +import org.apache.tapestry.ioc.services.PropertyShadowBuilder; +import org.apache.tapestry.ioc.services.TypeCoercer; + +/** + * The root module for Tapestry. + */ [EMAIL PROTECTED]("tapestry") [EMAIL PROTECTED]( +{ InternalModule.class }) +public final class TapestryModule +{ + private final ChainBuilder _chainBuilder; + + private final PipelineBuilder _pipelineBuilder; + + private final RequestGlobals _requestGlobals; + + private final ApplicationGlobals _applicationGlobals; + + private final PropertyShadowBuilder _shadowBuilder; + + private final RequestPageCache _requestPageCache; + + private final PageResponseRenderer _pageResponseRenderer; + + private final WebRequest _request; + + private final Environment _environment; + + // Yes, you can inject services defined by this module into this module. The service proxy is + // created without instantiating the module itself. We're careful about making as many + // service builder and contributor methods static as possible to avoid recursive build + // exceptions. + + public TapestryModule(@InjectService("tapestry.ioc.PipelineBuilder") + PipelineBuilder pipelineBuilder, @InjectService("tapestry.ioc.PropertyShadowBuilder") + PropertyShadowBuilder shadowBuilder, @InjectService("RequestGlobals") + RequestGlobals requestGlobals, @InjectService("ApplicationGlobals") + ApplicationGlobals applicationGlobals, @InjectService("tapestry.ioc.ChainBuilder") + ChainBuilder chainBuilder, @InjectService("tapestry.internal.RequestPageCache") + RequestPageCache requestPageCache, @InjectService("tapestry.internal.PageResponseRenderer") + PageResponseRenderer pageResponseRenderer, @Inject("infrastructure:request") + WebRequest request, @InjectService("Environment") + Environment environment) + { + _pipelineBuilder = pipelineBuilder; + _shadowBuilder = shadowBuilder; + _requestGlobals = requestGlobals; + _applicationGlobals = applicationGlobals; + _chainBuilder = chainBuilder; + _requestPageCache = requestPageCache; + _pageResponseRenderer = pageResponseRenderer; + _request = request; + _environment = environment; + } + + private static <T> void add(Configuration<InfrastructureContribution> configuration, + ServiceLocator locator, Class<T> serviceInterface) + { + String className = serviceInterface.getName(); + int dotx = className.lastIndexOf('.'); + String serviceId = className.substring(dotx + 1); + + // Convert first character to lower case to form the property name. + + String propertyName = serviceId.substring(0, 1).toLowerCase() + serviceId.substring(1); + + T service = locator.getService(serviceId, serviceInterface); + + InfrastructureContribution contribution = new InfrastructureContribution(propertyName, + service); + + configuration.add(contribution); + } + + public static ApplicationGlobals buildApplicationGlobals() + { + return new ApplicationGlobalsImpl(); + } + + public WebContext buildWebContext(@InjectService("ApplicationGlobals") + ApplicationGlobals globals) + { + return _shadowBuilder.build(globals, "webContext", WebContext.class); + } + + public ServletApplicationInitializer buildServletApplicationInitializer(Log log, + List<ServletApplicationInitializerFilter> configuration, + @InjectService("ApplicationInitializer") + final ApplicationInitializer initializer) + { + ServletApplicationInitializer terminator = new ServletApplicationInitializer() + { + public void initializeApplication(ServletContext context) + { + _applicationGlobals.store(context); + + // And now, down the (Web) ApplicationInitializer pipeline ... + + initializer.initializeApplication(new WebContextImpl(context)); + } + }; + + return _pipelineBuilder.build( + log, + ServletApplicationInitializer.class, + ServletApplicationInitializerFilter.class, + configuration, + terminator); + } + + /** Initializes the application. */ + public ApplicationInitializer buildApplicationInitializer(Log log, + List<ApplicationInitializerFilter> configuration) + { + ApplicationInitializer terminator = new ApplicationInitializer() + { + public void initializeApplication(WebContext context) + { + _applicationGlobals.store(context); + } + }; + + return _pipelineBuilder.build( + log, + ApplicationInitializer.class, + ApplicationInitializerFilter.class, + configuration, + terminator); + } + + /** + * Allows the exact steps in the component class transformation process to be defined. + */ + public ComponentClassTransformWorker buildComponentClassTransformWorker( + List<ComponentClassTransformWorker> configuration) + { + return _chainBuilder.build(ComponentClassTransformWorker.class, configuration); + } + + public HttpServletRequestHandler buildHttpServletRequestHandler(Log log, + List<HttpServletRequestFilter> configuration, @InjectService("WebRequestHandler") + final WebRequestHandler handler) + { + HttpServletRequestHandler terminator = new HttpServletRequestHandler() + { + public boolean service(HttpServletRequest request, HttpServletResponse response) + throws IOException + { + _requestGlobals.store(request, response); + + return handler.service(new WebRequestImpl(request), new WebResponseImpl(response)); + } + }; + + return _pipelineBuilder.build( + log, + HttpServletRequestHandler.class, + HttpServletRequestFilter.class, + configuration, + terminator); + } + + public static Infrastructure buildInfrastructure(Log log, + Collection<InfrastructureContribution> configuration) + { + InfrastructureManager manager = new InfrastructureManagerImpl(log, configuration); + + return new InfrastructureImpl(manager); + } + + public static MarkupWriterFactory buildMarkupWriterFactory() + { + // Temporary ... + return new MarkupWriterFactory() + { + public MarkupWriter newMarkupWriter() + { + return new MarkupWriterImpl(); + } + }; + } + + /** + * Ordered contributions to the MasterDispatcher service allow different URL matching strategies + * to occur. + */ + public Dispatcher buildMasterDispatcher(List<Dispatcher> configuration) + { + return _chainBuilder.build(Dispatcher.class, configuration); + } + + @Lifecycle("perthread") + public static RequestGlobals buildRequestGlobals() + { + return new RequestGlobalsImpl(); + } + + /** + * Builds a shadow of the RequestGlobals.request property. Note again that the shadow can be an + * ordinary singleton, even though RequestGlobals is perthread. + */ + public WebRequest buildWebRequest() + { + return _shadowBuilder.build(_requestGlobals, "request", WebRequest.class); + } + + /** + * Builds a shadow of the RequestGlobals.response property. Note again that the shadow can be an + * ordinary singleton, even though RequestGlobals is perthread. + */ + public WebResponse buildWebResponse() + { + return _shadowBuilder.build(_requestGlobals, "response", WebResponse.class); + } + + public WebRequestHandler buildWebRequestHandler(Log log, List<WebRequestFilter> configuration, + @InjectService("MasterDispatcher") + final Dispatcher masterDispatcher) + { + WebRequestHandler terminator = new WebRequestHandler() + { + public boolean service(WebRequest request, WebResponse response) throws IOException + { + _requestGlobals.store(request, response); + + return masterDispatcher.dispatch(request, response); + } + }; + + return _pipelineBuilder.build( + log, + WebRequestHandler.class, + WebRequestFilter.class, + configuration, + terminator); + } + + /** + * Contributes filter "tapestry.StaticFilesFilter" that idenfies requests for static resources + * and terminates the pipeline by returning false. Generally, most filters should be ordered + * after this filter. + */ + public static void contributeWebRequestHandler( + OrderedConfiguration<WebRequestFilter> configuration, @InjectService("WebContext") + WebContext webContext, + @InjectService("tapestry.internal.DefaultRequestExceptionHandler") + final RequestExceptionHandler exceptionHandler) + { + WebRequestFilter staticFilesFilter = new StaticFilesFilter(webContext); + + configuration.add("StaticFilesFilter", staticFilesFilter); + + WebRequestFilter errorFilter = new WebRequestFilter() + { + public boolean service(WebRequest request, WebResponse response, + WebRequestHandler handler) throws IOException + { + try + { + return handler.service(request, response); + } + catch (RuntimeException ex) + { + exceptionHandler.handleRequestException(ex); + + // We assume a reponse has been sent and there's no need to handle the request + // further. + + return true; + } + } + }; + + configuration.add("ErrorFilter", errorFilter); + } + + /** + * Contributes properties: componentNameExpander, markupWriterFactory, request + */ + public static void contributeInfrastructure( + Configuration<InfrastructureContribution> configuration, ServiceLocator locator, + @InjectService("WebRequest") + WebRequest request, @InjectService("WebResponse") + WebResponse response, @InjectService("tapestry.ioc.TypeCoercer") + TypeCoercer typeCoercer) + { + add(configuration, locator, MarkupWriterFactory.class); + add(configuration, locator, PersistentFieldManager.class); + add(configuration, locator, Environment.class); + add(configuration, locator, ComponentSource.class); + + configuration.add(new InfrastructureContribution("request", request)); + configuration.add(new InfrastructureContribution("response", response)); + configuration.add(new InfrastructureContribution("typeCoercer", typeCoercer)); + } + + /** + * Contributes the [EMAIL PROTECTED] ObjectProvider} provided by [EMAIL PROTECTED] Infrastructure#getObjectProvider()} + * mapped to the provider prefix "infrastructure". + */ + @Contribute("tapestry.ioc.MasterObjectProvider") + public static void contributeInfrastructureToMasterObjectProvider( + MappedConfiguration<String, ObjectProvider> configuration, + @InjectService("Infrastructure") + Infrastructure infrastructure) + { + configuration.add("infrastructure", infrastructure.getObjectProvider()); + } + + public void contributeMasterDispatcher(OrderedConfiguration<Dispatcher> configuration, + @InjectService("tapestry.internal.LinkFactory") + LinkFactory linkFactory) + { + configuration.add( + "HTML", + new PageRenderDispatcher(_pageResponseRenderer, _requestPageCache)); + + // This goes after the HTML one so that the "." in ".html" doesn't confuse it. + + configuration.add("ComponentEvent", new ComponentEventDispatcher(_requestPageCache, + linkFactory), "after:HTML"); + } + + public static ComponentClassResolver buildComponentClassResolver( + @InjectService("tapestry.internal.ComponentInstantiatorSource") + ComponentInstantiatorSource source, Collection<LibraryMapping> configuration) + { + ComponentClassResolverImpl service = new ComponentClassResolverImpl(source, configuration); + + // Allow the resolver to clean its cache when the source is invalidated + + source.addInvalidationListener(service); + + return service; + } + + public static void contributeComponentClassResolver(Configuration<LibraryMapping> configuration) + { + configuration.add(new LibraryMapping("core", "org.apache.tapestry.corelib")); + } + + public static BindingSource buildBindingSource(Map<String, BindingFactory> configuration) + { + return new BindingSourceImpl(configuration); + } + + /** Contributes the factory for "literal:" bindings. */ + public static void contributeBindingSource( + MappedConfiguration<String, BindingFactory> configuration, + @InjectService("tapestry.internal.PropBindingFactory") + BindingFactory propBindingFactory) + { + configuration.add(InternalConstants.LITERAL_BINDING_PREFIX, new LiteralBindingFactory()); + configuration.add(InternalConstants.PROP_BINDING_PREFIX, propBindingFactory); + } + + /** + * Returns a [EMAIL PROTECTED] ClassFactory} that can be used to create extra classes around component + * classes. + */ + public static ClassFactory buildComponentClassFactory(Log log, + @InjectService("tapestry.internal.ComponentInstantiatorSource") + ComponentInstantiatorSource source) + { + return new ComponentClassFactoryImpl(log, source); + } + + /** + * A chain of command for providing values for [EMAIL PROTECTED] org.apache.tapestry.annotations.Inject}-ed + * fields in component classes. The service's configuration can be extended to allow for + * different automatic injections (based on some combination of field type and field name). + */ + + public InjectionProvider buildInjectionProvider(List<InjectionProvider> configuration) + { + return _chainBuilder.build(InjectionProvider.class, configuration); + } + + /** + * Contributes the elemental providers: + * <ul> + * <li>ComponentResources -- give component access to its resources</li> + * <li>Default -- looks for a unique IoC service that matches the field type</li> + * </ul> + */ + public static void contributeInjectionProvider( + OrderedConfiguration<InjectionProvider> configuration) + { + configuration.add("ComponentResources", new ComponentResourcesInjectionProvider()); + configuration.add("Default", new DefaultInjectionProvider(), "after:*.*"); + } + + /** + * Adds a number of standard component class transform workers: + * <ul> + * <li>Retain -- allows fields to retain their values between requests</li> + * <li>Persist -- allows fields to store their their value persistently between requests</li> + * <li>Parameter -- identifies parameters based on the + * [EMAIL PROTECTED] org.apache.tapestry.annotations.Parameter} annotation</li> + * <li>Component -- identifies embedded components based on the + * [EMAIL PROTECTED] org.apache.tapestry.annotations.Component} annotation</li> + * <li>Mixin -- adds a mixin as part of a component's implementation</li> + * <li>Environment -- allows fields to contain values extracted from the [EMAIL PROTECTED] Environment} + * service</li> + * <li>SupportsInformalParameters -- checks for the annotation</li> + * <li>UnclaimedField -- identifies unclaimed fields and resets them to null/0/false at the end + * of the request</li> + * <li>SetupRender, BeginRender, etc. -- correspond to component render phases and annotations</li> + * </ul> + */ + public static void contributeComponentClassTransformWorker( + OrderedConfiguration<ComponentClassTransformWorker> configuration, + ServiceLocator locator, @InjectService("tapestry.ioc.MasterObjectProvider") + ObjectProvider objectProvider, @InjectService("InjectionProvider") + InjectionProvider injectionProvider, @InjectService("Environment") + Environment environment, @InjectService("tapestry.ComponentClassResolver") + ComponentClassResolver resolver) + { + // TODO: Proper scheduling of all of this. Since a given field or method should + // only have a single annotation, the order doesn't matter so much, as long as + // UnclaimedField is last. + + configuration.add("Inject", new InjectWorker(objectProvider, locator, injectionProvider)); + configuration.add("Parameter", new ParameterWorker()); + configuration.add("Component", new ComponentWorker(resolver)); + configuration.add("Environment", new EnvironmentalWorker(environment)); + configuration.add("Mixin", new MixinWorker(resolver)); + configuration.add("OnEvent", new OnEventWorker()); + configuration.add("SupportsInformalParameters", new SupportsInformalParametersWorker()); + + // Workers for the component rendering state machine methods; this is in typical + // execution order. + + add(configuration, TransformConstants.SETUP_RENDER_SIGNATURE, SetupRender.class, false); + add( + configuration, + TransformConstants.PRE_BEGIN_RENDER_SIGNATURE, + PreBeginRender.class, + false); + add(configuration, TransformConstants.BEGIN_RENDER_SIGNATURE, BeginRender.class, false); + add( + configuration, + TransformConstants.POST_BEGIN_RENDER_SIGNATURE, + PostBeginRender.class, + false); + add( + configuration, + TransformConstants.BEFORE_RENDER_TEMPLATE_SIGNATURE, + BeforeRenderTemplate.class, + false); + add( + configuration, + TransformConstants.BEFORE_RENDER_BODY_SIGNATURE, + BeforeRenderBody.class, + false); + + // These phases operate in reverse order. + + add( + configuration, + TransformConstants.AFTER_RENDER_BODY_SIGNATURE, + AfterRenderBody.class, + true); + add( + configuration, + TransformConstants.AFTER_RENDER_TEMPLATE_SIGNATURE, + AfterRenderTemplate.class, + true); + add(configuration, TransformConstants.AFTER_RENDER_SIGNATURE, AfterRender.class, true); + add(configuration, TransformConstants.CLEANUP_RENDER_SIGNATURE, CleanupRender.class, true); + + configuration.add("Retain", new RetainWorker()); + configuration.add("Persist", new PersistWorker()); + configuration.add("UnclaimedField", new UnclaimedFieldWorker(), "after:*.*"); + } + + private static void add(OrderedConfiguration<ComponentClassTransformWorker> configuration, + MethodSignature signature, Class<? extends Annotation> annotationClass, boolean reverse) + { + // make the name match the annotation class name. + + String name = IOCUtilities.toSimpleId(annotationClass.getName()); + + configuration.add(name, new ComponentLifecycleMethodWorker(signature, annotationClass, + reverse)); + } + + @Lifecycle("perthread") + public static Environment buildEnvironment() + { + return new EnvironmentImpl(); + } + + /** + * Controls setup and cleanup of the environment during page rendering (the generation of a + * markup stream response for the client web browser). + */ + public PageRenderInitializer buildPageRenderInitializer( + final List<PageRenderCommand> configuration) + { + return new PageRenderInitializer() + { + public void setup() + { + _environment.clear(); + + for (PageRenderCommand command : configuration) + command.setup(_environment); + } + + public void cleanup() + { + Iterator<PageRenderCommand> i = InternalUtils.reverseIterator(configuration); + + while (i.hasNext()) + i.next().cleanup(_environment); + + // Probably not necessary, but what the heck! + _environment.clear(); + } + }; + } + + public static void contributePageRenderInitializer( + OrderedConfiguration<PageRenderCommand> configuration) + { + // I'm not happy with this lifecycle, per-se. Instead of Runnable, I think we need an + // interface more tailored to Environment, with perhaps a second method for the end of + // the page render. + + configuration.add("PageRenderSupport", new PageRenderCommand() + { + public void setup(Environment environment) + { + environment.push(PageRenderSupport.class, new PageRenderSupportImpl()); + } + + public void cleanup(Environment environment) + { + environment.pop(PageRenderSupport.class); + } + }); + + configuration.add("Heartbeat", new PageRenderCommand() + { + public void setup(Environment environment) + { + HeartbeatImpl heartbeat = new HeartbeatImpl(); + + heartbeat.begin(); + + environment.push(Heartbeat.class, heartbeat); + } + + public void cleanup(Environment environment) + { + environment.pop(Heartbeat.class).end(); + } + }); + } + + /** A public service since extensions may provide new persistent strategies. */ + public static PersistentFieldManager buildPersistentFieldManager( + Map<String, PersistentFieldStrategy> configuration) + { + return new PersistentFieldManagerImpl(configuration); + } + + /** + * Contributes the "session" strategy. + */ + public void contributePersistentFieldManager( + MappedConfiguration<String, PersistentFieldStrategy> configuration) + { + configuration.add("session", new SessionPersistentFieldStrategy(_request)); + } + + public ComponentSource buildComponentSource( + @InjectService("tapestry.internal.RequestPageCache") + RequestPageCache pageCache) + { + return new ComponentSourceImpl(pageCache); + } +}
Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/HeartbeatImplTest.java URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/HeartbeatImplTest.java?view=auto&rev=476282 ============================================================================== --- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/HeartbeatImplTest.java (added) +++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/HeartbeatImplTest.java Fri Nov 17 11:51:39 2006 @@ -0,0 +1,89 @@ +// Copyright 2006 The Apache Software Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.apache.tapestry.internal.services; + +import org.apache.tapestry.internal.test.InternalBaseTestCase; +import org.apache.tapestry.services.Heartbeat; +import org.testng.annotations.Test; + +public class HeartbeatImplTest extends InternalBaseTestCase +{ + @Test + public void single_heartbeat() + { + Runnable r1 = newRunnable(); + Runnable r2 = newRunnable(); + + replay(); + + Heartbeat hb = new HeartbeatImpl(); + + hb.begin(); + + hb.defer(r1); + hb.defer(r2); + + verify(); + + r1.run(); + r2.run(); + + replay(); + + hb.end(); + + verify(); + } + + @Test + public void nested_heartbeats() + { + Runnable r1 = newRunnable(); + Runnable r2 = newRunnable(); + Runnable r3 = newRunnable(); + + replay(); + + Heartbeat hb = new HeartbeatImpl(); + + hb.begin(); + + hb.defer(r1); + hb.defer(r2); + + hb.begin(); + + hb.defer(r3); + + verify(); + + r3.run(); + + replay(); + + hb.end(); + + verify(); + + r1.run(); + r2.run(); + + replay(); + + hb.end(); + + verify(); + } +}
