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 [1/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...

Author: hlship
Date: Fri Nov 17 11:51:39 2006
New Revision: 476282

URL: http://svn.apache.org/viewvc?view=rev&rev=476282
Log:
Add an explicit, configurable setup and cleanup phase to page response rendering.
Implement the Heartbeat environmental service.
Modify the Loop copmonent to begin and end a heartbeat around each iteration.

Added:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/HeartbeatImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/DefaultPageRenderCommand.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/Heartbeat.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PageRenderCommand.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PageRenderInitializer.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/HeartbeatImplTest.java
Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Loop.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/EnvironmentImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageResponseRendererImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Loop.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Loop.java?view=diff&rev=476282&r1=476281&r2=476282
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Loop.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/Loop.java Fri Nov 17 11:51:39 2006
@@ -14,13 +14,15 @@
 
 package org.apache.tapestry.corelib.components;
 
-import java.util.Iterator;
-
-import org.apache.tapestry.annotations.AfterRender;
-import org.apache.tapestry.annotations.BeginRender;
-import org.apache.tapestry.annotations.ComponentClass;
-import org.apache.tapestry.annotations.Parameter;
-import org.apache.tapestry.annotations.SetupRender;
+import java.util.Iterator;
+
+import org.apache.tapestry.annotations.AfterRender;
+import org.apache.tapestry.annotations.BeginRender;
+import org.apache.tapestry.annotations.ComponentClass;
+import org.apache.tapestry.annotations.Environmental;
+import org.apache.tapestry.annotations.Parameter;
+import org.apache.tapestry.annotations.SetupRender;
+import org.apache.tapestry.services.Heartbeat;
 
 /**
  * Basic looping class; loops over a number of items (provided by its source parameter), rendering
@@ -60,7 +62,10 @@
     private int _index;
 
     private Iterator<?> _iterator;
-
+
+    @Environmental
+    private Heartbeat _heartbeat;
+    
     @SetupRender
     boolean setup()
     {
@@ -75,16 +80,22 @@
 
         return _iterator.hasNext();
     }
-
+
+    /** Begins a new heartbeat. */
     @BeginRender
     void begin()
     {
-        _value = _iterator.next();
+        _value = _iterator.next();
+        
+        _heartbeat.begin();
     }
-
+
+    /**  Ends the current heartbeat. */
     @AfterRender
     boolean after()
-    {
+    {
+        _heartbeat.end();
+        
         _index++;
         
         return _iterator.hasNext();

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/EnvironmentImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/EnvironmentImpl.java?view=diff&rev=476282&r1=476281&r2=476282
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/EnvironmentImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/EnvironmentImpl.java Fri Nov 17 11:51:39 2006
@@ -12,69 +12,67 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.internal.services;
-
-import static org.apache.tapestry.util.CollectionFactory.newLinkedList;
-
-import java.util.LinkedList;
-import java.util.Map;
-
-import org.apache.tapestry.services.Environment;
-import org.apache.tapestry.util.CollectionFactory;
-
-/**
- * A non-threadsafe implementation (expects to use the "perthread" service lifecyle.
- * 
- * 
- */
-public class EnvironmentImpl implements Environment
-{
-    // My generics mojo breaks down when we talk about the key and the value being related
-    // types.
-
-    private final Map<Class, LinkedList> _stacks = CollectionFactory.newMap();
-
-    @SuppressWarnings("unchecked")
-    private <T> LinkedList<T> stackFor(Class<T> type)
-    {
-        LinkedList<T> result = _stacks.get(type);
-
-        if (result == null)
-        {
-            result = newLinkedList();
-            _stacks.put(type, result);
-        }
-
-        return result;
-    }
-
-    public <T> T peek(Class<T> type)
-    {
-        LinkedList<T> stack = stackFor(type);
-
-        return stack.isEmpty() ? null : stack.getFirst();
-    }
-
-    public <T> T pop(Class<T> type)
-    {
-        LinkedList<T> stack = stackFor(type);
-
-        return stack.removeFirst();
-    }
-
-    public <T> T push(Class<T> type, T instance)
-    {
-        LinkedList<T> stack = stackFor(type);
-
-        T result = stack.isEmpty() ? null : stack.getFirst();
-
-        stack.addFirst(instance);
-
-        return result;
-    }
-
-    public void clear()
-    {
-        _stacks.clear();
-    }
-}
+package org.apache.tapestry.internal.services;
+
+import static org.apache.tapestry.util.CollectionFactory.newLinkedList;
+
+import java.util.LinkedList;
+import java.util.Map;
+
+import org.apache.tapestry.services.Environment;
+import org.apache.tapestry.util.CollectionFactory;
+
+/**
+ * A non-threadsafe implementation (expects to use the "perthread" service lifecyle.
+ */
+public class EnvironmentImpl implements Environment
+{
+    // My generics mojo breaks down when we talk about the key and the value being related
+    // types.
+
+    private final Map<Class, LinkedList> _stacks = CollectionFactory.newMap();
+
+    @SuppressWarnings("unchecked")
+    private <T> LinkedList<T> stackFor(Class<T> type)
+    {
+        LinkedList<T> result = _stacks.get(type);
+
+        if (result == null)
+        {
+            result = newLinkedList();
+            _stacks.put(type, result);
+        }
+
+        return result;
+    }
+
+    public <T> T peek(Class<T> type)
+    {
+        LinkedList<T> stack = stackFor(type);
+
+        return stack.isEmpty() ? null : stack.getFirst();
+    }
+
+    public <T> T pop(Class<T> type)
+    {
+        LinkedList<T> stack = stackFor(type);
+
+        return stack.removeFirst();
+    }
+
+    public <T> T push(Class<T> type, T instance)
+    {
+        LinkedList<T> stack = stackFor(type);
+
+        T result = stack.isEmpty() ? null : stack.getFirst();
+
+        stack.addFirst(instance);
+
+        return result;
+    }
+
+    public void clear()
+    {
+        _stacks.clear();
+    }
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/HeartbeatImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/HeartbeatImpl.java?view=auto&rev=476282
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/HeartbeatImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/HeartbeatImpl.java Fri Nov 17 11:51:39 2006
@@ -0,0 +1,53 @@
+// 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 static org.apache.tapestry.util.CollectionFactory.newLinkedList;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.tapestry.services.Heartbeat;
+import org.apache.tapestry.util.CollectionFactory;
+import org.apache.tapestry.util.Defense;
+
+public class HeartbeatImpl implements Heartbeat
+{
+    private final LinkedList<List<Runnable>> _stack = newLinkedList();
+
+    public void begin()
+    {
+        List<Runnable> beat = CollectionFactory.newList();
+
+        _stack.addFirst(beat);
+    }
+
+    public void defer(Runnable command)
+    {
+        Defense.notNull(command, "command");
+
+        _stack.getFirst().add(command);
+
+    }
+
+    public void end()
+    {
+        List<Runnable> beat = _stack.removeFirst();
+
+        for (Runnable r : beat)
+            r.run();
+    }
+
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java?view=diff&rev=476282&r1=476281&r2=476282
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java Fri Nov 17 11:51:39 2006
@@ -12,473 +12,471 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.internal.services;
-
-import static org.apache.tapestry.util.CollectionFactory.newMap;
-
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.commons.logging.Log;
-import org.apache.tapestry.Binding;
-import org.apache.tapestry.ComponentResources;
-import org.apache.tapestry.Location;
-import org.apache.tapestry.events.InvalidationListener;
-import org.apache.tapestry.internal.InternalConstants;
-import org.apache.tapestry.internal.bindings.LiteralBinding;
-import org.apache.tapestry.internal.bindings.PropBindingFactory;
-import org.apache.tapestry.ioc.LogSource;
-import org.apache.tapestry.ioc.OrderedConfiguration;
-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.Match;
-import org.apache.tapestry.ioc.annotations.Order;
-import org.apache.tapestry.ioc.services.ChainBuilder;
-import org.apache.tapestry.ioc.services.ClassFactory;
-import org.apache.tapestry.ioc.services.LoggingDecorator;
-import org.apache.tapestry.ioc.services.PropertyAccess;
-import org.apache.tapestry.ioc.services.ThreadCleanupHub;
-import org.apache.tapestry.ioc.services.TypeCoercer;
-import org.apache.tapestry.services.ApplicationInitializer;
-import org.apache.tapestry.services.ApplicationInitializerFilter;
-import org.apache.tapestry.services.BindingFactory;
-import org.apache.tapestry.services.BindingSource;
-import org.apache.tapestry.services.ComponentClassResolver;
-import org.apache.tapestry.services.ComponentClassTransformWorker;
-import org.apache.tapestry.services.MarkupWriterFactory;
-import org.apache.tapestry.services.PersistentFieldManager;
-import org.apache.tapestry.services.PersistentFieldStrategy;
-import org.apache.tapestry.services.RequestExceptionHandler;
-import org.apache.tapestry.services.WebContext;
-import org.apache.tapestry.services.WebRequest;
-import org.apache.tapestry.services.WebRequestFilter;
-import org.apache.tapestry.services.WebResponse;
-import org.apache.tapestry.util.IntegerRange;
-
-/**
- * 
- */
-@Id("tapestry.internal")
-public final class InternalModule
-{
-    private final ComponentInstantiatorSource _componentInstantiatorSource;
-
-    private final ComponentTemplateSource _componentTemplateSource;
-
-    private final UpdateListenerHub _updateListenerHub;
-
-    private final ThreadCleanupHub _threadCleanupHub;
-
-    private final ComponentClassResolver _componentClassResolver;
-
-    private final ChainBuilder _chainBuilder;
-
-    private final WebRequest _request;
-
-    private final WebResponse _response;
-
-    public InternalModule(@InjectService("ComponentInstantiatorSource")
-    ComponentInstantiatorSource componentInstantiatorSource, @InjectService("UpdateListenerHub")
-    UpdateListenerHub updateListenerHub, @InjectService("tapestry.ioc.ThreadCleanupHub")
-    ThreadCleanupHub threadCleanupHub, @InjectService("ComponentTemplateSource")
-    ComponentTemplateSource componentTemplateSource,
-            @InjectService("tapestry.ComponentClassResolver")
-            ComponentClassResolver componentClassResolver,
-            @InjectService("tapestry.ioc.ChainBuilder")
-            ChainBuilder chainBuilder, @Inject("infrastructure:request")
-            WebRequest request, @Inject("infrastructure:response")
-            WebResponse response)
-    {
-        _componentInstantiatorSource = componentInstantiatorSource;
-        _updateListenerHub = updateListenerHub;
-        _threadCleanupHub = threadCleanupHub;
-        _componentTemplateSource = componentTemplateSource;
-        _componentClassResolver = componentClassResolver;
-        _chainBuilder = chainBuilder;
-        _request = request;
-        _response = response;
-    }
-
-    public ComponentClassTransformer buildComponentClassTransformer(
-            @InjectService("tapestry.ComponentClassTransformWorker")
-            ComponentClassTransformWorker workerChain, @InjectService("tapestry.ioc.LogSource")
-            LogSource logSource)
-    {
-        ComponentClassTransformerImpl transformer = new ComponentClassTransformerImpl(workerChain,
-                logSource);
-
-        _componentInstantiatorSource.addInvalidationListener(transformer);
-
-        return transformer;
-    }
-
-    public ComponentInstantiatorSource buildComponentInstantiatorSource(
-            @InjectService("tapestry.ioc.ClassFactory")
-            ClassFactory classFactory, @InjectService("ComponentClassTransformer")
-            ComponentClassTransformer transformer, Log log)
-    {
-        ComponentInstantiatorSourceImpl source = new ComponentInstantiatorSourceImpl(classFactory
-                .getClassLoader(), transformer, log);
-
-        _updateListenerHub.addUpdateListener(source);
-
-        return source;
-    }
-
-    public ComponentTemplateSource buildComponentTemplateSource(@InjectService("TemplateParser")
-    TemplateParser parser)
-    {
-        ComponentTemplateSourceImpl service = new ComponentTemplateSourceImpl(parser);
-
-        _updateListenerHub.addUpdateListener(service);
-
-        return service;
-    }
-
-    @Lifecycle("perthread")
-    public static TemplateParser buildTemplateParser(Log log)
-    {
-        return new TemplateParserImpl(log);
-    }
-
-    public PageElementFactory buildPageElementFactory(@Inject("infrastructure:typeCoercer")
-    TypeCoercer typeCoercer, @InjectService("tapestry.BindingSource")
-    BindingSource bindingSource)
-    {
-        return new PageElementFactoryImpl(_componentInstantiatorSource, _componentClassResolver,
-                typeCoercer, bindingSource);
-    }
-
-    public PageLoader buildPageLoader(@InjectService("PageElementFactory")
-    PageElementFactory pageElementFactory, @InjectService("tapestry.BindingSource")
-    BindingSource bindingSource, @InjectService("LinkFactory")
-    LinkFactory linkFactory, @Inject("infrastructure:persistentFieldManager")
-    PersistentFieldManager persistentFieldManager)
-    {
-        PageLoaderImpl service = new PageLoaderImpl(_componentTemplateSource, pageElementFactory,
-                bindingSource, linkFactory, persistentFieldManager);
-
-        // Recieve invalidations when the class loader is discarded (due to a component class
-        // change).
-        // The notification is forwarded to the page loader's listeners.
-
-        _componentInstantiatorSource.addInvalidationListener(service);
-
-        return service;
-    }
-
-    public PagePool buildPagePool(Log log, @InjectService("PageLoader")
-    PageLoader pageLoader)
-    {
-        PagePoolImpl service = new PagePoolImpl(log, pageLoader);
-
-        // This covers invalidations due to changes to classes
-
-        pageLoader.addInvalidationListener(service);
-
-        // ... and this covers invalidations due to changes to templates
-
-        _componentTemplateSource.addInvalidationListener(service);
-
-        return service;
-    }
-
-    /**
-     * The UpdateListenerHub provides events that other services use to check for invalidations.
-     * Such services usually are {@link org.apache.tapestry.internal.event.InvalidationEventHub}s,
-     * and fire invalidation events to their listeners.
-     */
-    public static UpdateListenerHub buildUpdateListenerHub()
-    {
-        return new UpdateListenerHubImpl();
-    }
-
-    /**
-     * All public services in the tapestry module, and in any sub-module of tapestry will get
-     * logging. This doesn't include the tapesry.ioc module since services of that module can not be
-     * decorated.
-     */
-    @Match(
-    { "tapestry.*", "tapestry.*.*" })
-    @Order("before:*.*")
-    public static <T> T decorateWithLogging(Class<T> serviceInterface, T delegate,
-            String serviceId, Log log, @InjectService("tapestry.ioc.LoggingDecorator")
-            LoggingDecorator loggingDecorator)
-    {
-        return loggingDecorator.build(serviceInterface, delegate, serviceId, log);
-    }
-
-    @Lifecycle("perthread")
-    public RequestPageCache buildRequestPageCache(@InjectService("PagePool")
-    PagePool pagePool)
-    {
-        RequestPageCacheImpl service = new RequestPageCacheImpl(_componentClassResolver, pagePool);
-
-        _threadCleanupHub.addThreadCleanupListener(service);
-
-        return service;
-    }
-
-    public static PageResponseRenderer buildPageResponseRenderer(
-            @InjectService("tapestry.MarkupWriterFactory")
-            MarkupWriterFactory markupWriterFactory,
-            @InjectService("tapestry.PageRenderInitializer")
-            Runnable pageRenderInitializer)
-    {
-        return new PageResponseRendererImpl(markupWriterFactory, pageRenderInitializer);
-    }
-
-    /**
-     * Adds a filter that checks for updates to classes and other resources. It is ordered
-     * before:*.*.
-     */
-    @Contribute("tapestry.WebRequestHandler")
-    public void contributeWebRequestFilters(OrderedConfiguration<WebRequestFilter> configuration)
-    {
-        configuration.add(
-                "CheckForUpdates",
-                new CheckForUpdatesFilter(_updateListenerHub),
-                "before:*.*");
-    }
-
-    /**
-     * Adds a filter that sets the application package (for class loading purposes). The filter is
-     * ordered before:*.*".
-     */
-    @Contribute("tapestry.ApplicationInitializer")
-    public void contributeApplicationInitializerFilters(
-            OrderedConfiguration<ApplicationInitializerFilter> configuration,
-            @InjectService("tapestry.ioc.PropertyAccess")
-            final PropertyAccess propertyAccess, @Inject("infrastructure:typeCoercer")
-            final TypeCoercer typeCoercer)
-    {
-        ApplicationInitializerFilter setApplicationPackage = new ApplicationInitializerFilter()
-        {
-            public void initializeApplication(WebContext context, ApplicationInitializer initializer)
-            {
-                String packageName = context
-                        .getInitParameter(InternalConstants.TAPESTRY_APP_PACKAGE_PARAM);
-
-                _componentClassResolver.setApplicationPackage(packageName);
-
-                initializer.initializeApplication(context);
-            }
-        };
-
-        configuration.add("SetApplicationPackage", setApplicationPackage, "before:*.*");
-
-        final InvalidationListener listener = new InvalidationListener()
-        {
-            public void objectWasInvalidated()
-            {
-                propertyAccess.clearCache();
-                typeCoercer.clearCache();
-            }
-        };
-
-        ApplicationInitializerFilter clearCaches = new ApplicationInitializerFilter()
-        {
-            public void initializeApplication(WebContext context, ApplicationInitializer initializer)
-            {
-                // Snuck in here is the logic to clear the PropertyAccess service's cache whenever
-                // the component class loader is invalidated.
-
-                _componentInstantiatorSource.addInvalidationListener(listener);
-
-                initializer.initializeApplication(context);
-            }
-        };
-
-        configuration.add("ClearCachesOnInvalidation", clearCaches);
-    }
-
-    /**
-     * Builds the PropBindingFactory as a chain of command. The terminator of the chain is
-     * responsible for ordinary property names (and property paths). Contributions to the service
-     * cover additional special cases, such as simple literal values.
-     * 
-     * @param configuration
-     *            contributions of special factories for some constants, each contributed factory
-     *            may return a binding if applicable, or null otherwise
-     * @param propertyAccess
-     * @param classFactory
-     * @return
-     */
-    public BindingFactory buildPropBindingFactory(List<BindingFactory> configuration,
-            @InjectService("tapestry.ioc.PropertyAccess")
-            PropertyAccess propertyAccess, @InjectService("tapestry.ComponentClassFactory")
-            ClassFactory classFactory)
-    {
-        PropBindingFactory service = new PropBindingFactory(propertyAccess, classFactory);
-
-        _componentInstantiatorSource.addInvalidationListener(service);
-
-        configuration.add(service);
-
-        return _chainBuilder.build(BindingFactory.class, configuration);
-    }
-
-    public void contributePropBindingFactory(OrderedConfiguration<BindingFactory> configuration)
-    {
-        BindingFactory keywordFactory = new BindingFactory()
-        {
-            private final Map<String, Object> _keywords = newMap();
-
-            {
-                _keywords.put("true", Boolean.TRUE);
-                _keywords.put("false", Boolean.FALSE);
-                _keywords.put("null", null);
-            }
-
-            public Binding newBinding(String description, ComponentResources component,
-                    String expression, Location location)
-            {
-                String key = expression.trim().toLowerCase();
-
-                if (_keywords.containsKey(key))
-                    return new LiteralBinding(description, _keywords.get(key), location);
-
-                return null;
-            }
-        };
-
-        BindingFactory thisFactory = new BindingFactory()
-        {
-
-            public Binding newBinding(String description, ComponentResources component,
-                    String expression, Location location)
-            {
-                if ("this".equalsIgnoreCase(expression.trim()))
-                    return new LiteralBinding(description, component.getComponent(), location);
-
-                return null;
-            }
-        };
-
-        BindingFactory longFactory = new BindingFactory()
-        {
-            private final Pattern _pattern = Pattern.compile("^\\s*(-?\\d+)\\s*$");
-
-            public Binding newBinding(String description, ComponentResources component,
-                    String expression, Location location)
-            {
-                Matcher matcher = _pattern.matcher(expression);
-
-                if (matcher.matches())
-                {
-                    String value = matcher.group(1);
-
-                    return new LiteralBinding(description, new Long(value), location);
-                }
-
-                return null;
-            }
-        };
-
-        BindingFactory intRangeFactory = new BindingFactory()
-        {
-            private final Pattern _pattern = Pattern
-                    .compile("^\\s*(-?\\d+)\\s*\\.\\.\\s*(-?\\d+)\\s*$");
-
-            public Binding newBinding(String description, ComponentResources component,
-                    String expression, Location location)
-            {
-                Matcher matcher = _pattern.matcher(expression);
-
-                if (matcher.matches())
-                {
-                    int start = Integer.parseInt(matcher.group(1));
-                    int finish = Integer.parseInt(matcher.group(2));
-
-                    IntegerRange range = new IntegerRange(start, finish);
-
-                    return new LiteralBinding(description, range, location);
-                }
-
-                return null;
-            }
-        };
-
-        BindingFactory doubleFactory = new BindingFactory()
-        {
-            // So, either 1234. or 1234.56 or .78
-            private final Pattern _pattern = Pattern
-                    .compile("^\\s*(\\-?((\\d+\\.)|(\\d*\\.\\d+)))\\s*$");
-
-            public Binding newBinding(String description, ComponentResources component,
-                    String expression, Location location)
-            {
-                Matcher matcher = _pattern.matcher(expression);
-
-                if (matcher.matches())
-                {
-                    String value = matcher.group(1);
-
-                    return new LiteralBinding(description, new Double(value), location);
-                }
-
-                return null;
-            }
-        };
-
-        BindingFactory stringFactory = new BindingFactory()
-        {
-            // This will match embedded single quotes as-is, no escaping necessary.
-
-            private final Pattern _pattern = Pattern.compile("^\\s*'(.*)'\\s*$");
-
-            public Binding newBinding(String description, ComponentResources component,
-                    String expression, Location location)
-            {
-                Matcher matcher = _pattern.matcher(expression);
-
-                if (matcher.matches())
-                {
-                    String value = matcher.group(1);
-
-                    return new LiteralBinding(description, value, location);
-                }
-
-                return null;
-            }
-        };
-
-        // To be honest, order probably doesn't matter.
-
-        configuration.add("Keyword", keywordFactory);
-        configuration.add("This", thisFactory);
-        configuration.add("Long", longFactory);
-        configuration.add("IntRange", intRangeFactory);
-        configuration.add("Double", doubleFactory);
-        configuration.add("StringLiteral", stringFactory);
-    }
-
-    public RequestExceptionHandler buildDefaultRequestExceptionHandler(
-            @InjectService("RequestPageCache")
-            RequestPageCache pageCache, @InjectService("PageResponseRenderer")
-            PageResponseRenderer renderer)
-    {
-        return new DefaultRequestExceptionHandler(pageCache, renderer, _response);
-    }
-
-    /** Service used to create links for components and pages. */
-    public LinkFactory buildLinkFactory()
-    {
-        return new LinkFactoryImpl(_request, _response, _componentClassResolver);
-    }
-
-    /**
-     * This exists as its own service just so that we can monitor it using logging.
-     * <p>
-     * TODO: Move this to TapestryModule?
-     */
-    public PersistentFieldStrategy buildSessionPersistentFieldStrategy()
-    {
-        return new SessionPersistentFieldStrategy(_request);
-    }
-}
+package org.apache.tapestry.internal.services;
+
+import static org.apache.tapestry.util.CollectionFactory.newMap;
+
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.logging.Log;
+import org.apache.tapestry.Binding;
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.Location;
+import org.apache.tapestry.events.InvalidationListener;
+import org.apache.tapestry.internal.InternalConstants;
+import org.apache.tapestry.internal.bindings.LiteralBinding;
+import org.apache.tapestry.internal.bindings.PropBindingFactory;
+import org.apache.tapestry.ioc.LogSource;
+import org.apache.tapestry.ioc.OrderedConfiguration;
+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.Match;
+import org.apache.tapestry.ioc.annotations.Order;
+import org.apache.tapestry.ioc.services.ChainBuilder;
+import org.apache.tapestry.ioc.services.ClassFactory;
+import org.apache.tapestry.ioc.services.LoggingDecorator;
+import org.apache.tapestry.ioc.services.PropertyAccess;
+import org.apache.tapestry.ioc.services.ThreadCleanupHub;
+import org.apache.tapestry.ioc.services.TypeCoercer;
+import org.apache.tapestry.services.ApplicationInitializer;
+import org.apache.tapestry.services.ApplicationInitializerFilter;
+import org.apache.tapestry.services.BindingFactory;
+import org.apache.tapestry.services.BindingSource;
+import org.apache.tapestry.services.ComponentClassResolver;
+import org.apache.tapestry.services.ComponentClassTransformWorker;
+import org.apache.tapestry.services.MarkupWriterFactory;
+import org.apache.tapestry.services.PageRenderInitializer;
+import org.apache.tapestry.services.PersistentFieldManager;
+import org.apache.tapestry.services.PersistentFieldStrategy;
+import org.apache.tapestry.services.RequestExceptionHandler;
+import org.apache.tapestry.services.WebContext;
+import org.apache.tapestry.services.WebRequest;
+import org.apache.tapestry.services.WebRequestFilter;
+import org.apache.tapestry.services.WebResponse;
+import org.apache.tapestry.util.IntegerRange;
+
+@Id("tapestry.internal")
+public final class InternalModule
+{
+    private final ComponentInstantiatorSource _componentInstantiatorSource;
+
+    private final ComponentTemplateSource _componentTemplateSource;
+
+    private final UpdateListenerHub _updateListenerHub;
+
+    private final ThreadCleanupHub _threadCleanupHub;
+
+    private final ComponentClassResolver _componentClassResolver;
+
+    private final ChainBuilder _chainBuilder;
+
+    private final WebRequest _request;
+
+    private final WebResponse _response;
+
+    public InternalModule(@InjectService("ComponentInstantiatorSource")
+    ComponentInstantiatorSource componentInstantiatorSource, @InjectService("UpdateListenerHub")
+    UpdateListenerHub updateListenerHub, @InjectService("tapestry.ioc.ThreadCleanupHub")
+    ThreadCleanupHub threadCleanupHub, @InjectService("ComponentTemplateSource")
+    ComponentTemplateSource componentTemplateSource,
+            @InjectService("tapestry.ComponentClassResolver")
+            ComponentClassResolver componentClassResolver,
+            @InjectService("tapestry.ioc.ChainBuilder")
+            ChainBuilder chainBuilder, @Inject("infrastructure:request")
+            WebRequest request, @Inject("infrastructure:response")
+            WebResponse response)
+    {
+        _componentInstantiatorSource = componentInstantiatorSource;
+        _updateListenerHub = updateListenerHub;
+        _threadCleanupHub = threadCleanupHub;
+        _componentTemplateSource = componentTemplateSource;
+        _componentClassResolver = componentClassResolver;
+        _chainBuilder = chainBuilder;
+        _request = request;
+        _response = response;
+    }
+
+    public ComponentClassTransformer buildComponentClassTransformer(
+            @InjectService("tapestry.ComponentClassTransformWorker")
+            ComponentClassTransformWorker workerChain, @InjectService("tapestry.ioc.LogSource")
+            LogSource logSource)
+    {
+        ComponentClassTransformerImpl transformer = new ComponentClassTransformerImpl(workerChain,
+                logSource);
+
+        _componentInstantiatorSource.addInvalidationListener(transformer);
+
+        return transformer;
+    }
+
+    public ComponentInstantiatorSource buildComponentInstantiatorSource(
+            @InjectService("tapestry.ioc.ClassFactory")
+            ClassFactory classFactory, @InjectService("ComponentClassTransformer")
+            ComponentClassTransformer transformer, Log log)
+    {
+        ComponentInstantiatorSourceImpl source = new ComponentInstantiatorSourceImpl(classFactory
+                .getClassLoader(), transformer, log);
+
+        _updateListenerHub.addUpdateListener(source);
+
+        return source;
+    }
+
+    public ComponentTemplateSource buildComponentTemplateSource(@InjectService("TemplateParser")
+    TemplateParser parser)
+    {
+        ComponentTemplateSourceImpl service = new ComponentTemplateSourceImpl(parser);
+
+        _updateListenerHub.addUpdateListener(service);
+
+        return service;
+    }
+
+    @Lifecycle("perthread")
+    public static TemplateParser buildTemplateParser(Log log)
+    {
+        return new TemplateParserImpl(log);
+    }
+
+    public PageElementFactory buildPageElementFactory(@Inject("infrastructure:typeCoercer")
+    TypeCoercer typeCoercer, @InjectService("tapestry.BindingSource")
+    BindingSource bindingSource)
+    {
+        return new PageElementFactoryImpl(_componentInstantiatorSource, _componentClassResolver,
+                typeCoercer, bindingSource);
+    }
+
+    public PageLoader buildPageLoader(@InjectService("PageElementFactory")
+    PageElementFactory pageElementFactory, @InjectService("tapestry.BindingSource")
+    BindingSource bindingSource, @InjectService("LinkFactory")
+    LinkFactory linkFactory, @Inject("infrastructure:persistentFieldManager")
+    PersistentFieldManager persistentFieldManager)
+    {
+        PageLoaderImpl service = new PageLoaderImpl(_componentTemplateSource, pageElementFactory,
+                bindingSource, linkFactory, persistentFieldManager);
+
+        // Recieve invalidations when the class loader is discarded (due to a component class
+        // change).
+        // The notification is forwarded to the page loader's listeners.
+
+        _componentInstantiatorSource.addInvalidationListener(service);
+
+        return service;
+    }
+
+    public PagePool buildPagePool(Log log, @InjectService("PageLoader")
+    PageLoader pageLoader)
+    {
+        PagePoolImpl service = new PagePoolImpl(log, pageLoader);
+
+        // This covers invalidations due to changes to classes
+
+        pageLoader.addInvalidationListener(service);
+
+        // ... and this covers invalidations due to changes to templates
+
+        _componentTemplateSource.addInvalidationListener(service);
+
+        return service;
+    }
+
+    /**
+     * The UpdateListenerHub provides events that other services use to check for invalidations.
+     * Such services usually are {@link org.apache.tapestry.internal.event.InvalidationEventHub}s,
+     * and fire invalidation events to their listeners.
+     */
+    public static UpdateListenerHub buildUpdateListenerHub()
+    {
+        return new UpdateListenerHubImpl();
+    }
+
+    /**
+     * All public services in the tapestry module, and in any sub-module of tapestry will get
+     * logging. This doesn't include the tapesry.ioc module since services of that module can not be
+     * decorated.
+     */
+    @Match(
+    { "tapestry.*", "tapestry.*.*" })
+    @Order("before:*.*")
+    public static <T> T decorateWithLogging(Class<T> serviceInterface, T delegate,
+            String serviceId, Log log, @InjectService("tapestry.ioc.LoggingDecorator")
+            LoggingDecorator loggingDecorator)
+    {
+        return loggingDecorator.build(serviceInterface, delegate, serviceId, log);
+    }
+
+    @Lifecycle("perthread")
+    public RequestPageCache buildRequestPageCache(@InjectService("PagePool")
+    PagePool pagePool)
+    {
+        RequestPageCacheImpl service = new RequestPageCacheImpl(_componentClassResolver, pagePool);
+
+        _threadCleanupHub.addThreadCleanupListener(service);
+
+        return service;
+    }
+
+    public static PageResponseRenderer buildPageResponseRenderer(
+            @InjectService("tapestry.MarkupWriterFactory")
+            MarkupWriterFactory markupWriterFactory,
+            @InjectService("tapestry.PageRenderInitializer")
+            PageRenderInitializer pageRenderInitializer)
+    {
+        return new PageResponseRendererImpl(markupWriterFactory, pageRenderInitializer);
+    }
+
+    /**
+     * Adds a filter that checks for updates to classes and other resources. It is ordered
+     * before:*.*.
+     */
+    @Contribute("tapestry.WebRequestHandler")
+    public void contributeWebRequestFilters(OrderedConfiguration<WebRequestFilter> configuration)
+    {
+        configuration.add(
+                "CheckForUpdates",
+                new CheckForUpdatesFilter(_updateListenerHub),
+                "before:*.*");
+    }
+
+    /**
+     * Adds a filter that sets the application package (for class loading purposes). The filter is
+     * ordered before:*.*".
+     */
+    @Contribute("tapestry.ApplicationInitializer")
+    public void contributeApplicationInitializerFilters(
+            OrderedConfiguration<ApplicationInitializerFilter> configuration,
+            @InjectService("tapestry.ioc.PropertyAccess")
+            final PropertyAccess propertyAccess, @Inject("infrastructure:typeCoercer")
+            final TypeCoercer typeCoercer)
+    {
+        ApplicationInitializerFilter setApplicationPackage = new ApplicationInitializerFilter()
+        {
+            public void initializeApplication(WebContext context, ApplicationInitializer initializer)
+            {
+                String packageName = context
+                        .getInitParameter(InternalConstants.TAPESTRY_APP_PACKAGE_PARAM);
+
+                _componentClassResolver.setApplicationPackage(packageName);
+
+                initializer.initializeApplication(context);
+            }
+        };
+
+        configuration.add("SetApplicationPackage", setApplicationPackage, "before:*.*");
+
+        final InvalidationListener listener = new InvalidationListener()
+        {
+            public void objectWasInvalidated()
+            {
+                propertyAccess.clearCache();
+                typeCoercer.clearCache();
+            }
+        };
+
+        ApplicationInitializerFilter clearCaches = new ApplicationInitializerFilter()
+        {
+            public void initializeApplication(WebContext context, ApplicationInitializer initializer)
+            {
+                // Snuck in here is the logic to clear the PropertyAccess service's cache whenever
+                // the component class loader is invalidated.
+
+                _componentInstantiatorSource.addInvalidationListener(listener);
+
+                initializer.initializeApplication(context);
+            }
+        };
+
+        configuration.add("ClearCachesOnInvalidation", clearCaches);
+    }
+
+    /**
+     * Builds the PropBindingFactory as a chain of command. The terminator of the chain is
+     * responsible for ordinary property names (and property paths). Contributions to the service
+     * cover additional special cases, such as simple literal values.
+     * 
+     * @param configuration
+     *            contributions of special factories for some constants, each contributed factory
+     *            may return a binding if applicable, or null otherwise
+     * @param propertyAccess
+     * @param classFactory
+     * @return
+     */
+    public BindingFactory buildPropBindingFactory(List<BindingFactory> configuration,
+            @InjectService("tapestry.ioc.PropertyAccess")
+            PropertyAccess propertyAccess, @InjectService("tapestry.ComponentClassFactory")
+            ClassFactory classFactory)
+    {
+        PropBindingFactory service = new PropBindingFactory(propertyAccess, classFactory);
+
+        _componentInstantiatorSource.addInvalidationListener(service);
+
+        configuration.add(service);
+
+        return _chainBuilder.build(BindingFactory.class, configuration);
+    }
+
+    public void contributePropBindingFactory(OrderedConfiguration<BindingFactory> configuration)
+    {
+        BindingFactory keywordFactory = new BindingFactory()
+        {
+            private final Map<String, Object> _keywords = newMap();
+
+            {
+                _keywords.put("true", Boolean.TRUE);
+                _keywords.put("false", Boolean.FALSE);
+                _keywords.put("null", null);
+            }
+
+            public Binding newBinding(String description, ComponentResources component,
+                    String expression, Location location)
+            {
+                String key = expression.trim().toLowerCase();
+
+                if (_keywords.containsKey(key))
+                    return new LiteralBinding(description, _keywords.get(key), location);
+
+                return null;
+            }
+        };
+
+        BindingFactory thisFactory = new BindingFactory()
+        {
+
+            public Binding newBinding(String description, ComponentResources component,
+                    String expression, Location location)
+            {
+                if ("this".equalsIgnoreCase(expression.trim()))
+                    return new LiteralBinding(description, component.getComponent(), location);
+
+                return null;
+            }
+        };
+
+        BindingFactory longFactory = new BindingFactory()
+        {
+            private final Pattern _pattern = Pattern.compile("^\\s*(-?\\d+)\\s*$");
+
+            public Binding newBinding(String description, ComponentResources component,
+                    String expression, Location location)
+            {
+                Matcher matcher = _pattern.matcher(expression);
+
+                if (matcher.matches())
+                {
+                    String value = matcher.group(1);
+
+                    return new LiteralBinding(description, new Long(value), location);
+                }
+
+                return null;
+            }
+        };
+
+        BindingFactory intRangeFactory = new BindingFactory()
+        {
+            private final Pattern _pattern = Pattern
+                    .compile("^\\s*(-?\\d+)\\s*\\.\\.\\s*(-?\\d+)\\s*$");
+
+            public Binding newBinding(String description, ComponentResources component,
+                    String expression, Location location)
+            {
+                Matcher matcher = _pattern.matcher(expression);
+
+                if (matcher.matches())
+                {
+                    int start = Integer.parseInt(matcher.group(1));
+                    int finish = Integer.parseInt(matcher.group(2));
+
+                    IntegerRange range = new IntegerRange(start, finish);
+
+                    return new LiteralBinding(description, range, location);
+                }
+
+                return null;
+            }
+        };
+
+        BindingFactory doubleFactory = new BindingFactory()
+        {
+            // So, either 1234. or 1234.56 or .78
+            private final Pattern _pattern = Pattern
+                    .compile("^\\s*(\\-?((\\d+\\.)|(\\d*\\.\\d+)))\\s*$");
+
+            public Binding newBinding(String description, ComponentResources component,
+                    String expression, Location location)
+            {
+                Matcher matcher = _pattern.matcher(expression);
+
+                if (matcher.matches())
+                {
+                    String value = matcher.group(1);
+
+                    return new LiteralBinding(description, new Double(value), location);
+                }
+
+                return null;
+            }
+        };
+
+        BindingFactory stringFactory = new BindingFactory()
+        {
+            // This will match embedded single quotes as-is, no escaping necessary.
+
+            private final Pattern _pattern = Pattern.compile("^\\s*'(.*)'\\s*$");
+
+            public Binding newBinding(String description, ComponentResources component,
+                    String expression, Location location)
+            {
+                Matcher matcher = _pattern.matcher(expression);
+
+                if (matcher.matches())
+                {
+                    String value = matcher.group(1);
+
+                    return new LiteralBinding(description, value, location);
+                }
+
+                return null;
+            }
+        };
+
+        // To be honest, order probably doesn't matter.
+
+        configuration.add("Keyword", keywordFactory);
+        configuration.add("This", thisFactory);
+        configuration.add("Long", longFactory);
+        configuration.add("IntRange", intRangeFactory);
+        configuration.add("Double", doubleFactory);
+        configuration.add("StringLiteral", stringFactory);
+    }
+
+    public RequestExceptionHandler buildDefaultRequestExceptionHandler(
+            @InjectService("RequestPageCache")
+            RequestPageCache pageCache, @InjectService("PageResponseRenderer")
+            PageResponseRenderer renderer)
+    {
+        return new DefaultRequestExceptionHandler(pageCache, renderer, _response);
+    }
+
+    /** Service used to create links for components and pages. */
+    public LinkFactory buildLinkFactory()
+    {
+        return new LinkFactoryImpl(_request, _response, _componentClassResolver);
+    }
+
+    /**
+     * This exists as its own service just so that we can monitor it using logging.
+     * <p>
+     * TODO: Move this to TapestryModule?
+     */
+    public PersistentFieldStrategy buildSessionPersistentFieldStrategy()
+    {
+        return new SessionPersistentFieldStrategy(_request);
+    }
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageResponseRendererImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageResponseRendererImpl.java?view=diff&rev=476282&r1=476281&r2=476282
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageResponseRendererImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageResponseRendererImpl.java Fri Nov 17 11:51:39 2006
@@ -12,51 +12,54 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.internal.services;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-
-import org.apache.tapestry.MarkupWriter;
-import org.apache.tapestry.internal.structure.Page;
-import org.apache.tapestry.services.MarkupWriterFactory;
-import org.apache.tapestry.services.WebResponse;
-
-/**
- * 
- */
-public class PageResponseRendererImpl implements PageResponseRenderer
-{
-    private final Runnable _pageRenderInitializer;
-
-    private final MarkupWriterFactory _markupWriterFactory;
-
-    public PageResponseRendererImpl(MarkupWriterFactory markupWriterFactory,
-            Runnable pageRenderInitializer)
-    {
-        _markupWriterFactory = markupWriterFactory;
-        _pageRenderInitializer = pageRenderInitializer;
-    }
-
-    public void renderPageResponse(Page page, WebResponse response) throws IOException
-    {
-        _pageRenderInitializer.run();
-
-        MarkupWriter writer = _markupWriterFactory.newMarkupWriter();
-
-        RenderQueueImpl queue = new RenderQueueImpl(page.getLog());
-
-        queue.push(page.getRootElement());
-
-        // Run the queue until empty.
-
-        queue.run(writer);
-
-        PrintWriter pw = response.getPrintWriter();
-
-        writer.toXML(pw);
-
-        pw.flush();
-    }
-
-}
+package org.apache.tapestry.internal.services;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import org.apache.tapestry.MarkupWriter;
+import org.apache.tapestry.internal.structure.Page;
+import org.apache.tapestry.services.MarkupWriterFactory;
+import org.apache.tapestry.services.PageRenderInitializer;
+import org.apache.tapestry.services.WebResponse;
+
+public class PageResponseRendererImpl implements PageResponseRenderer
+{
+    private final PageRenderInitializer _pageRenderInitializer;
+
+    private final MarkupWriterFactory _markupWriterFactory;
+
+    public PageResponseRendererImpl(MarkupWriterFactory markupWriterFactory,
+            PageRenderInitializer pageRenderInitializer)
+    {
+        _markupWriterFactory = markupWriterFactory;
+        _pageRenderInitializer = pageRenderInitializer;
+    }
+
+    public void renderPageResponse(Page page, WebResponse response) throws IOException
+    {
+        _pageRenderInitializer.setup();
+
+        // Eventually we'll have to do work to figure out the correct markup type, content type,
+        // whatever. Right now its defaulting to plain HTML.
+
+        MarkupWriter writer = _markupWriterFactory.newMarkupWriter();
+
+        RenderQueueImpl queue = new RenderQueueImpl(page.getLog());
+
+        queue.push(page.getRootElement());
+
+        // Run the queue until empty.
+
+        queue.run(writer);
+
+        _pageRenderInitializer.cleanup();
+
+        PrintWriter pw = response.getPrintWriter();
+
+        writer.toXML(pw);
+
+        pw.flush();
+    }
+
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/DefaultPageRenderCommand.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/DefaultPageRenderCommand.java?view=auto&rev=476282
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/DefaultPageRenderCommand.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/DefaultPageRenderCommand.java Fri Nov 17 11:51:39 2006
@@ -0,0 +1,32 @@
+// 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.services;
+
+/**
+ * Default implementation of {@link PageRenderCommand} that does nothing.
+ */
+public abstract class DefaultPageRenderCommand implements PageRenderCommand
+{
+    /** Does nothing. */
+    public void cleanup(Environment environment)
+    {
+    }
+
+    /** Does nothing. */
+    public void setup(Environment environment)
+    {
+    }
+
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/Heartbeat.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/Heartbeat.java?view=auto&rev=476282
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/Heartbeat.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/Heartbeat.java Fri Nov 17 11:51:39 2006
@@ -0,0 +1,40 @@
+// 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.services;
+
+/**
+ * Allow for deferred execution of logic, useful when trying to get multiple components to
+ * coordinate behavior. A component may add a command to be executed "{@link #end() at the end of the heartbeat}".
+ * The classic example of this is a Label and the field it labels; since we don't know which order
+ * the two will render, we can't tell if the field's id is correct until after both have rendered.
+ */
+public interface Heartbeat
+{
+    /**
+     * Begins a new Heartbeat. Heartbeats nest. Every call to begin() should be matched by a call to
+     * {@link #end()}.
+     */
+    void begin();
+
+    /** Executes all commands since the most recent {@link #begin()}. */
+    void end();
+
+    /**
+     * Adds a new command to the current Heartbeat. The command will be executed by {@link #end()}.
+     * 
+     * @param command
+     */
+    void defer(Runnable command);
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PageRenderCommand.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PageRenderCommand.java?view=auto&rev=476282
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PageRenderCommand.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PageRenderCommand.java Fri Nov 17 11:51:39 2006
@@ -0,0 +1,30 @@
+// 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.services;
+
+/**
+ * Page render commands are invoked at the start of the page render cycle and at the end. This
+ * allows them to perform initial startup or final cleanup (or both).
+ * 
+ * @see PageRenderInitializer
+ */
+public interface PageRenderCommand
+{
+    /** Invoked to perform an initial setup. */
+    void setup(Environment environment);
+
+    /** Invoked to peform final cleanup. */
+    void cleanup(Environment environment);
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PageRenderInitializer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PageRenderInitializer.java?view=auto&rev=476282
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PageRenderInitializer.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PageRenderInitializer.java Fri Nov 17 11:51:39 2006
@@ -0,0 +1,34 @@
+// 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.services;
+
+/**
+ * Responsible for setup and cleanup of the rendering of a page. The implementation of this is based
+ * on an ordered list of {@link PageRenderCommand}s.
+ */
+public interface PageRenderInitializer
+{
+    /**
+     * Perform any initial setup, by invoking {@link PageRenderCommand#setup(Environment)}.
+     * Execution occurs in normal order.
+     */
+    void setup();
+
+    /**
+     * Peform any post-render cleanup, by invoking {@link PageRenderCommand#cleanup(Environment)}.
+     * Execution order is reversed.
+     */
+    void cleanup();
+}