You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2006/11/17 20:51:40 UTC
svn commit: r476282 [2/2] - in /tapestry/tapestry5/tapestry-core/trunk/src:
main/java/org/apache/tapestry/corelib/components/
main/java/org/apache/tapestry/internal/services/
main/java/org/apache/tapestry/services/
test/java/org/apache/tapestry/interna...
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.
- */
-@Id("tapestry")
-@SubModule(
-{ 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 {@link ObjectProvider} provided by {@link 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 {@link 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 {@link 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
- * {@link org.apache.tapestry.annotations.Parameter} annotation</li>
- * <li>Component -- identifies embedded components based on the
- * {@link 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 {@link 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
- * {@link 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.
+ */
+@Id("tapestry")
+@SubModule(
+{ 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 {@link ObjectProvider} provided by {@link 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 {@link 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 {@link 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
+ * {@link org.apache.tapestry.annotations.Parameter} annotation</li>
+ * <li>Component -- identifies embedded components based on the
+ * {@link 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 {@link 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();
+ }
+}