You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2008/01/01 00:46:18 UTC

svn commit: r607784 - in /tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry: internal/ internal/services/ internal/test/ services/

Author: hlship
Date: Mon Dec 31 15:46:11 2007
New Revision: 607784

URL: http://svn.apache.org/viewvc?rev=607784&view=rev
Log:
TAPESTRY-1948: Null pointer exception when performing a partial page render

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageContentTypeAnalyzer.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageContentTypeAnalyzerImpl.java
Removed:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/MarkupRendererPipelineImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PartialMarkupRendererPipelineImpl.java
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/InternalConstants.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AjaxComponentActionRequestHandler.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AjaxPartialResponseRendererImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageResponseRendererImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/RequestImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/TestableRequestImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/MarkupWriterFactory.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/Request.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/InternalConstants.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/InternalConstants.java?rev=607784&r1=607783&r2=607784&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/InternalConstants.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/InternalConstants.java Mon Dec 31 15:46:11 2007
@@ -55,6 +55,14 @@
     public static final String PAGES_SUBPACKAGE = "pages";
     public static final String BASE_SUBPACKAGE = "base";
 
+
+    /**
+     * Used in some Ajax scenarios to set the content type for the response early, when the Page instance
+     * (the authority on content types) is known. The value is of type {@link org.apache.tapestry.internal.util.ContentType}.
+     */
+    public static final String CONTENT_TYPE_ATTRIBUTE_NAME = "content-type";
+    public static final String CHARSET_CONTENT_TYPE_PARAMETER = "charset";
+
     private InternalConstants()
     {
     }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AjaxComponentActionRequestHandler.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AjaxComponentActionRequestHandler.java?rev=607784&r1=607783&r2=607784&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AjaxComponentActionRequestHandler.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AjaxComponentActionRequestHandler.java Mon Dec 31 15:46:11 2007
@@ -16,8 +16,10 @@
 
 import org.apache.tapestry.ComponentEventHandler;
 import org.apache.tapestry.TapestryConstants;
+import org.apache.tapestry.internal.InternalConstants;
 import org.apache.tapestry.internal.structure.ComponentPageElement;
 import org.apache.tapestry.internal.structure.Page;
+import org.apache.tapestry.internal.util.ContentType;
 import org.apache.tapestry.internal.util.Holder;
 import org.apache.tapestry.json.JSONObject;
 import org.apache.tapestry.runtime.Component;
@@ -37,15 +39,20 @@
 
     private final AjaxPartialResponseRenderer _renderer;
 
+    private final Request _request;
+
     private final Response _response;
 
     private final PageRenderQueue _queue;
 
     private final ComponentEventResultProcessor _resultProcessor;
 
+    private final PageContentTypeAnalyzer _pageContentTypeAnalyzer;
+
     public AjaxComponentActionRequestHandler(RequestPageCache cache, MarkupWriterFactory factory,
-                                             AjaxPartialResponseRenderer renderer, Response response,
-                                             PageRenderQueue queue, @Ajax ComponentEventResultProcessor resultProcessor)
+                                             AjaxPartialResponseRenderer renderer, Request request, Response response,
+                                             PageRenderQueue queue, @Ajax ComponentEventResultProcessor resultProcessor,
+                                             PageContentTypeAnalyzer pageContentTypeAnalyzer)
     {
         _cache = cache;
         _factory = factory;
@@ -53,6 +60,8 @@
         _response = response;
         _queue = queue;
         _resultProcessor = resultProcessor;
+        _pageContentTypeAnalyzer = pageContentTypeAnalyzer;
+        _request = request;
     }
 
     public void handle(String logicalPageName, String nestedComponentId, String eventType, String[] context,
@@ -64,6 +73,10 @@
         // page that will be rendered (for logging purposes, if nothing else).
 
         _queue.initializeForCompletePage(page);
+
+        ContentType contentType = _pageContentTypeAnalyzer.findContentType(page);
+
+        _request.setAttribute(InternalConstants.CONTENT_TYPE_ATTRIBUTE_NAME, contentType);
 
         ComponentPageElement element = page.getComponentElementByNestedId(nestedComponentId);
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AjaxPartialResponseRendererImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AjaxPartialResponseRendererImpl.java?rev=607784&r1=607783&r2=607784&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AjaxPartialResponseRendererImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/AjaxPartialResponseRendererImpl.java Mon Dec 31 15:46:11 2007
@@ -15,13 +15,11 @@
 package org.apache.tapestry.internal.services;
 
 import org.apache.tapestry.MarkupWriter;
+import org.apache.tapestry.internal.InternalConstants;
 import org.apache.tapestry.internal.util.ContentType;
 import org.apache.tapestry.json.JSONObject;
 import org.apache.tapestry.runtime.RenderCommand;
-import org.apache.tapestry.services.Environment;
-import org.apache.tapestry.services.MarkupWriterFactory;
-import org.apache.tapestry.services.PartialMarkupRenderer;
-import org.apache.tapestry.services.Response;
+import org.apache.tapestry.services.*;
 
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -32,18 +30,20 @@
 
     private final MarkupWriterFactory _factory;
 
+    private final Request _request;
     private final Response _response;
 
     private final PartialMarkupRenderer _partialMarkupRenderer;
 
     private final PageRenderQueue _pageRenderQueue;
 
-
-    public AjaxPartialResponseRendererImpl(Environment environment, MarkupWriterFactory factory, Response response,
-                                           PartialMarkupRenderer partialMarkupRenderer, PageRenderQueue pageRenderQueue)
+    public AjaxPartialResponseRendererImpl(Environment environment, MarkupWriterFactory factory, Request request,
+                                           Response response, PartialMarkupRenderer partialMarkupRenderer,
+                                           PageRenderQueue pageRenderQueue)
     {
         _environment = environment;
         _factory = factory;
+        _request = request;
         _response = response;
         _partialMarkupRenderer = partialMarkupRenderer;
         _pageRenderQueue = pageRenderQueue;
@@ -59,12 +59,14 @@
 
         _pageRenderQueue.initializeForPartialPageRender(rootRenderCommand);
 
-        // This may be problematic as the charset of the response is not
-        // going to be set properly I think.  We'll loop back to that.
+        ContentType pageContentType = (ContentType) _request.getAttribute(
+                InternalConstants.CONTENT_TYPE_ATTRIBUTE_NAME);
+        String charset = pageContentType.getParameter(InternalConstants.CHARSET_CONTENT_TYPE_PARAMETER);
 
         ContentType contentType = new ContentType("text/javascript");
+        contentType.setParameter(InternalConstants.CHARSET_CONTENT_TYPE_PARAMETER, charset);
 
-        MarkupWriter writer = _factory.newMarkupWriter(null);
+        MarkupWriter writer = _factory.newMarkupWriter(pageContentType);
 
         JSONObject reply = new JSONObject();
 

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageContentTypeAnalyzer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageContentTypeAnalyzer.java?rev=607784&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageContentTypeAnalyzer.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageContentTypeAnalyzer.java Mon Dec 31 15:46:11 2007
@@ -0,0 +1,30 @@
+// Copyright 2007 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.structure.Page;
+import org.apache.tapestry.internal.util.ContentType;
+
+public interface PageContentTypeAnalyzer
+{
+    /**
+     * Analyzes the meta-data for the page and identifies the correct
+     * {@link org.apache.tapestry.internal.util.ContentType} (including encoding).
+     *
+     * @param page to be rendered
+     * @return the content type
+     */
+    ContentType findContentType(Page page);
+}

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageContentTypeAnalyzerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageContentTypeAnalyzerImpl.java?rev=607784&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageContentTypeAnalyzerImpl.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageContentTypeAnalyzerImpl.java Mon Dec 31 15:46:11 2007
@@ -0,0 +1,53 @@
+// Copyright 2007 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.ComponentResources;
+import org.apache.tapestry.TapestryConstants;
+import org.apache.tapestry.internal.InternalConstants;
+import org.apache.tapestry.internal.structure.Page;
+import org.apache.tapestry.internal.util.ContentType;
+import org.apache.tapestry.services.MetaDataLocator;
+
+public class PageContentTypeAnalyzerImpl implements PageContentTypeAnalyzer
+{
+    private final MetaDataLocator _metaDataLocator;
+
+    public PageContentTypeAnalyzerImpl(MetaDataLocator metaDataLocator)
+    {
+        _metaDataLocator = metaDataLocator;
+    }
+
+    public ContentType findContentType(Page page)
+    {
+        ComponentResources pageResources = page.getRootComponent().getComponentResources();
+
+        String contentTypeString = _metaDataLocator.findMeta(TapestryConstants.RESPONSE_CONTENT_TYPE, pageResources);
+        ContentType contentType = new ContentType(contentTypeString);
+
+        // Make sure thre's always a charset specified.
+
+        String encoding = contentType.getParameter(InternalConstants.CHARSET_CONTENT_TYPE_PARAMETER);
+
+        if (encoding == null)
+        {
+            encoding = _metaDataLocator
+                    .findMeta(TapestryConstants.RESPONSE_ENCODING, pageResources);
+            contentType.setParameter(InternalConstants.CHARSET_CONTENT_TYPE_PARAMETER, encoding);
+        }
+
+        return contentType;
+    }
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageResponseRendererImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageResponseRendererImpl.java?rev=607784&r1=607783&r2=607784&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageResponseRendererImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/PageResponseRendererImpl.java Mon Dec 31 15:46:11 2007
@@ -14,13 +14,10 @@
 
 package org.apache.tapestry.internal.services;
 
-import org.apache.tapestry.ComponentResources;
 import org.apache.tapestry.MarkupWriter;
-import org.apache.tapestry.TapestryConstants;
 import org.apache.tapestry.internal.structure.Page;
 import org.apache.tapestry.internal.util.ContentType;
 import org.apache.tapestry.services.MarkupWriterFactory;
-import org.apache.tapestry.services.MetaDataLocator;
 import org.apache.tapestry.services.Response;
 
 import java.io.IOException;
@@ -28,28 +25,26 @@
 
 public class PageResponseRendererImpl implements PageResponseRenderer
 {
-    public static final String CHARSET = "charset";
-
     private final PageMarkupRenderer _markupRenderer;
 
     private final MarkupWriterFactory _markupWriterFactory;
 
-    private final MetaDataLocator _metaDataLocator;
+    private final PageContentTypeAnalyzer _pageContentTypeAnalyzer;
 
     public PageResponseRendererImpl(MarkupWriterFactory markupWriterFactory, PageMarkupRenderer markupRenderer,
-                                    MetaDataLocator metaDataLocator)
+                                    PageContentTypeAnalyzer pageContentTypeAnalyzer)
     {
         _markupWriterFactory = markupWriterFactory;
         _markupRenderer = markupRenderer;
-        _metaDataLocator = metaDataLocator;
+        _pageContentTypeAnalyzer = pageContentTypeAnalyzer;
     }
 
     public void renderPageResponse(Page page, Response response) throws IOException
     {
-        ContentType contentType = findResponseContentType(page);
+        ContentType contentType = _pageContentTypeAnalyzer.findContentType(page);
 
-        // Eventually we'll have to do work to figure out the correct markup type, content type,
-        // whatever. Right now its defaulting to plain HTML.
+        // For the moment, the content type is all that's used determine the model for the markup writer.
+        // It's something of a can of worms.
 
         MarkupWriter writer = _markupWriterFactory.newMarkupWriter(contentType);
 
@@ -61,25 +56,4 @@
 
         pw.flush();
     }
-
-    private ContentType findResponseContentType(Page page)
-    {
-        ComponentResources pageResources = page.getRootComponent().getComponentResources();
-
-        String contentTypeString = _metaDataLocator.findMeta(TapestryConstants.RESPONSE_CONTENT_TYPE, pageResources);
-        ContentType contentType = new ContentType(contentTypeString);
-
-        // Make sure thre's always a charset specified.
-
-        String encoding = contentType.getParameter(CHARSET);
-        if (encoding == null)
-        {
-            encoding = _metaDataLocator
-                    .findMeta(TapestryConstants.RESPONSE_ENCODING, pageResources);
-            contentType.setParameter(CHARSET, encoding);
-        }
-
-        return contentType;
-    }
-
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/RequestImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/RequestImpl.java?rev=607784&r1=607783&r2=607784&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/RequestImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/services/RequestImpl.java Mon Dec 31 15:46:11 2007
@@ -114,4 +114,14 @@
     {
         return _request.isRequestedSessionIdValid();
     }
+
+    public Object getAttribute(String name)
+    {
+        return _request.getAttribute(name);
+    }
+
+    public void setAttribute(String name, Object value)
+    {
+        _request.setAttribute(name, value);
+    }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/TestableRequestImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/TestableRequestImpl.java?rev=607784&r1=607783&r2=607784&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/TestableRequestImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/internal/test/TestableRequestImpl.java Mon Dec 31 15:46:11 2007
@@ -28,6 +28,8 @@
 
     private final Map<String, String> _parameters = newMap();
 
+    private final Map<String, Object> _attributes = newMap();
+
     private Session _session;
 
     public TestableRequestImpl()
@@ -145,5 +147,15 @@
     public boolean isRequestedSessionIdValid()
     {
         return true;
+    }
+
+    public Object getAttribute(String name)
+    {
+        return _attributes.get(name);
+    }
+
+    public void setAttribute(String name, Object value)
+    {
+        _attributes.put(name, value);
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/MarkupWriterFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/MarkupWriterFactory.java?rev=607784&r1=607783&r2=607784&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/MarkupWriterFactory.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/MarkupWriterFactory.java Mon Dec 31 15:46:11 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 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.
@@ -9,7 +9,7 @@
 // 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
+// See the License for the specific language governing permissions andand
 // limitations under the License.
 
 package org.apache.tapestry.services;

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/Request.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/Request.java?rev=607784&r1=607783&r2=607784&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/Request.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/Request.java Mon Dec 31 15:46:11 2007
@@ -119,5 +119,28 @@
      *
      * @return true if the request included a session id that is still active, false if the included session id has expired
      */
-    public boolean isRequestedSessionIdValid();
+    boolean isRequestedSessionIdValid();
+
+
+    /**
+     * Returns the value of the named attribute as an <code>Object</code>,
+     * or <code>null</code> if no attribute of the given name exists.  Because this method is a wrapper
+     * around {@link javax.servlet.ServletRequest#getAttribute(String)}, it is case <em>sensitive</em> (unlike most of Tapestry).
+     *
+     * @param name a <code>String</code> specifying the name of
+     *             the attribute
+     * @return an <code>Object</code> containing the value
+     *         of the attribute, or <code>null</code> if the attribute does not exist
+     */
+    Object getAttribute(String name);
+
+    /**
+     * Stores an attribute in this request.
+     * Attributes are reset between requests (and remember that in Tapestry, there is usually
+     * two requests per operation: the action request that redirects to a render request).
+     *
+     * @param name  a <code>String</code> specifying the name of the attribute
+     * @param value the <code>Object</code> to be stored, or null to remove the attribute
+     */
+    void setAttribute(String name, Object value);
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java?rev=607784&r1=607783&r2=607784&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry/services/TapestryModule.java Mon Dec 31 15:46:11 2007
@@ -111,9 +111,8 @@
         binder.bind(ComponentEventResultProcessor.class, ComponentInstanceResultProcessor.class).withId(
                 "ComponentInstanceResultProcessor");
         binder.bind(PageRenderQueue.class, PageRenderQueueImpl.class);
-        binder.bind(MarkupRenderer.class, MarkupRendererPipelineImpl.class);
         binder.bind(AjaxPartialResponseRenderer.class, AjaxPartialResponseRendererImpl.class);
-        binder.bind(PartialMarkupRenderer.class, PartialMarkupRendererPipelineImpl.class);
+        binder.bind(PageContentTypeAnalyzer.class, PageContentTypeAnalyzerImpl.class);
     }
 
     public static Alias build(Logger logger,
@@ -1245,8 +1244,36 @@
         configuration.add(ClassTransformation.class, preformatted);
     }
 
+
+    /**
+     * The MarkupRenderer service is used to render a full page as markup.  Supports an ordered
+     * configuration of {@link org.apache.tapestry.services.MarkupRendererFilter}s.
+     *
+     * @param pageRenderQueue handles the bulk of the work
+     * @param logger          used to log errors building the pipeline
+     * @param configuration   filters on this service
+     * @return the service
+     * @see #contributeMarkupRenderer(org.apache.tapestry.ioc.OrderedConfiguration, org.apache.tapestry.Asset, org.apache.tapestry.Asset, ValidationMessagesSource, org.apache.tapestry.ioc.services.SymbolSource, AssetSource)
+     */
+    public MarkupRenderer buildMarkupRenderer(final PageRenderQueue pageRenderQueue, Logger logger,
+                                              List<MarkupRendererFilter> configuration)
+    {
+        MarkupRenderer terminator = new MarkupRenderer()
+        {
+            public void renderMarkup(MarkupWriter writer)
+            {
+                pageRenderQueue.render(writer);
+            }
+        };
+
+        return _pipelineBuilder.build(logger, MarkupRenderer.class, MarkupRendererFilter.class, configuration,
+                                      terminator);
+    }
+
+
     /**
-     * Adds page render filters, each of which provides an {@link org.apache.tapestry.annotations.Environmental} service:
+     * Adds page render filters, each of which provides an {@link org.apache.tapestry.annotations.Environmental} service.  Filters often
+     * provide {@link Environmental} services needed by components as they render.
      * <dl>
      * <dt>PageRenderSupport</dt>  <dd>Provides {@link PageRenderSupport}</dd>
      * <dt>ZoneSetup</dt> <dd>Provides {@link ZoneSetup}</dd>
@@ -1361,6 +1388,33 @@
 
 
     /**
+     * A wrapper around {@link org.apache.tapestry.internal.services.PageRenderQueue} used for partial page renders.
+     * Supports an ordered configuration of {@link org.apache.tapestry.services.PartialMarkupRendererFilter}s.
+     *
+     * @param logger        used to log warnings creating the pipeline
+     * @param configuration filters for the service
+     * @param renderQueue   does most of the work
+     * @return the service
+     * @see #contributePartialMarkupRenderer(org.apache.tapestry.ioc.OrderedConfiguration, org.apache.tapestry.Asset, ValidationMessagesSource, org.apache.tapestry.ioc.services.SymbolSource, AssetSource)
+     */
+    public PartialMarkupRenderer buildPartialMarkupRenderer(Logger logger,
+                                                            List<PartialMarkupRendererFilter> configuration,
+                                                            final PageRenderQueue renderQueue)
+    {
+
+        PartialMarkupRenderer terminator = new PartialMarkupRenderer()
+        {
+            public void renderMarkup(MarkupWriter writer, JSONObject reply)
+            {
+                renderQueue.renderPartial(writer, reply);
+            }
+        };
+
+        return _pipelineBuilder.build(logger, PartialMarkupRenderer.class, PartialMarkupRendererFilter.class,
+                                      configuration, terminator);
+    }
+
+    /**
      * Contributes {@link PartialMarkupRendererFilter}s used when rendering a partial Ajax response.  This
      * is an analog to {@link #contributeMarkupRenderer(org.apache.tapestry.ioc.OrderedConfiguration, org.apache.tapestry.Asset, org.apache.tapestry.Asset, ValidationMessagesSource, org.apache.tapestry.ioc.services.SymbolSource, AssetSource)} }
      * and overlaps it to some degree.
@@ -1563,7 +1617,7 @@
         // Java class files always require a digest.
         configuration.add("class");
 
-        // Likewise, we don't want people fishing for templates.
+// Likewise, we don't want people fishing for templates.
         configuration.add(InternalConstants.TEMPLATE_EXTENSION);
     }
 
@@ -1599,7 +1653,7 @@
         configuration.add("tapestry.file-check-interval", "1 s");
         configuration.add("tapestry.file-check-update-timeout", "50 ms");
 
-        // This should be overridden for particular applications.
+// This should be overridden for particular applications.
         configuration.add("tapestry.supported-locales", "en");
 
         configuration.add("tapestry.default-cookie-max-age", "7 d");
@@ -1608,17 +1662,17 @@
 
         configuration.add("tapestry.default-stylesheet", "org/apache/tapestry/default.css");
 
-        // This is designed to make it easy to keep synchronized with script.aculo.ous. As we
-        // support a new version, we create a new folder, and update the path entry. We can then
-        // delete the old version folder (or keep it around). This should be more manageable than
-        // overwriting the local copy with updates (it's too easy for files deleted between scriptaculous
-        // releases to be accidentally left lying around). There's also a ClasspathAliasManager
-        // contribution based on the path.
+// This is designed to make it easy to keep synchronized with script.aculo.ous. As we
+// support a new version, we create a new folder, and update the path entry. We can then
+// delete the old version folder (or keep it around). This should be more manageable than
+// overwriting the local copy with updates (it's too easy for files deleted between scriptaculous
+// releases to be accidentally left lying around). There's also a ClasspathAliasManager
+// contribution based on the path.
 
         configuration.add("tapestry.scriptaculous", "classpath:${tapestry.scriptaculous.path}");
         configuration.add("tapestry.scriptaculous.path", "org/apache/tapestry/scriptaculous_1_8");
 
-        // Likewise for jscalendar, currently version 1.0
+// Likewise for jscalendar, currently version 1.0
 
         configuration.add("tapestry.jscalendar.path", "org/apache/tapestry/jscalendar-1.0");
         configuration.add("tapestry.jscalendar", "classpath:${tapestry.jscalendar.path}");
@@ -1660,15 +1714,15 @@
     {
         PagePoolImpl service = new PagePoolImpl(logger, pageLoader, _threadLocale, resolver);
 
-        // This covers invalidations due to changes to classes
+// This covers invalidations due to changes to classes
 
         pageLoader.addInvalidationListener(service);
 
-        // This covers invalidation due to changes to message catalogs (properties files)
+// This covers invalidation due to changes to message catalogs (properties files)
 
         componentMessagesSource.addInvalidationListener(service);
 
-        // ... and this covers invalidations due to changes to templates
+// ... and this covers invalidations due to changes to templates
 
         _componentTemplateSource.addInvalidationListener(service);
 
@@ -1679,8 +1733,8 @@
     {
         PageLoaderImpl service = resources.autobuild(PageLoaderImpl.class);
 
-        // Recieve invalidations when the class loader is discarded (due to a component class
-        // change). The notification is forwarded to the page loader's listeners.
+// 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);
 
@@ -1820,8 +1874,8 @@
 
                 initializer.initializeApplication(context);
 
-                // We don't care about the result, but this forces a load of the service
-                // at application startup, rather than on first request.
+// We don't care about the result, but this forces a load of the service
+// at application startup, rather than on first request.
 
                 componentClassResolver.isPageName("ForceLoadAtStartup");
             }
@@ -1934,7 +1988,7 @@
 
         BindingFactory stringFactory = new BindingFactory()
         {
-            // This will match embedded single quotes as-is, no escaping necessary.
+// This will match embedded single quotes as-is, no escaping necessary.
 
             private final Pattern _pattern = Pattern.compile("^\\s*'(.*)'\\s*$");
 
@@ -1954,7 +2008,7 @@
             }
         };
 
-        // To be honest, order probably doesn't matter.
+// To be honest, order probably doesn't matter.
 
         configuration.add("Keyword", keywordFactory);
         configuration.add("This", thisFactory);