    001// Licensed under the Apache License, Version 2.0 (the "License");
    002// you may not use this file except in compliance with the License.
    003// You may obtain a copy of the License at
    004//
    005// http://www.apache.org/licenses/LICENSE-2.0
    006//
    007// Unless required by applicable law or agreed to in writing, software
    008// distributed under the License is distributed on an "AS IS" BASIS,
    009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    010// See the License for the specific language governing permissions and
    011// limitations under the License.
    012
    013package org.apache.tapestry5.internal.services;
    014
    015import org.apache.tapestry5.MarkupWriter;
    016import org.apache.tapestry5.commons.util.CollectionFactory;
    017import org.apache.tapestry5.commons.util.Stack;
    018import org.apache.tapestry5.internal.structure.Page;
    019import org.apache.tapestry5.ioc.LoggerSource;
    020import org.apache.tapestry5.ioc.ScopeConstants;
    021import org.apache.tapestry5.ioc.annotations.Scope;
    022import org.apache.tapestry5.json.JSONObject;
    023import org.apache.tapestry5.runtime.RenderCommand;
    024import org.apache.tapestry5.services.PartialMarkupRenderer;
    025import org.apache.tapestry5.services.PartialMarkupRendererFilter;
    026import org.slf4j.Logger;
    027
    028/**
    029 * This services keeps track of the page being rendered and the root command for the partial render, it is therefore
    030 * request/thread scoped. There's a filter pipeline around the rendering, and that gets to be stateless because this
    031 * service, at the end of the pipeline, is stateful.
    032 */
    033@Scope(ScopeConstants.PERTHREAD)
    034public class PageRenderQueueImpl implements PageRenderQueue
    035{
    036    private final LoggerSource loggerSource;
    037
    038    private Page page;
    039
    040    private boolean partialRenderInitialized;
    041
    042    private final Stack<PartialMarkupRendererFilter> filters = CollectionFactory.newStack();
    043
    044    private RenderQueueImpl queue;
    045
    046    private static class Bridge implements PartialMarkupRenderer
    047    {
    048        private final PartialMarkupRendererFilter filter;
    049
    050        private final PartialMarkupRenderer delegate;
    051
    052        private Bridge(PartialMarkupRendererFilter filter, PartialMarkupRenderer delegate)
    053        {
    054            this.filter = filter;
    055            this.delegate = delegate;
    056        }
    057
    058        public void renderMarkup(MarkupWriter writer, JSONObject reply)
    059        {
    060            filter.renderMarkup(writer, reply, delegate);
    061        }
    062    }
    063
    064    public PageRenderQueueImpl(LoggerSource loggerSource)
    065    {
    066        this.loggerSource = loggerSource;
    067    }
    068
    069    public void initializeForCompletePage(Page page)
    070    {
    071        setRenderingPage(page);
    072
    073        queue.push(page.getRootElement());
    074    }
    075
    076    public void setRenderingPage(Page page)
    077    {
    078        assert page != null;
    079
    080        this.page = page;
    081
    082        String name = "tapestry.render." + page.getLogger().getName();
    083
    084        Logger logger = loggerSource.getLogger(name);
    085
    086        queue = new RenderQueueImpl(logger);
    087    }
    088
    089    public boolean isPartialRenderInitialized()
    090    {
    091        return partialRenderInitialized;
    092    }
    093
    094    public void addPartialRenderer(RenderCommand renderer)
    095    {
    096        assert renderer != null;
    097
    098        checkQueue();
    099
    100        partialRenderInitialized = true;
    101
    102        queue.push(renderer);
    103    }
    104
    105    private void checkQueue()
    106    {
    107        if (queue == null) {
    108            throw new IllegalStateException("The page used as the basis for partial rendering has not been set.");
    109        }
    110    }
    111
    112    public Page getRenderingPage()
    113    {
    114        return page;
    115    }
    116
    117    public void render(MarkupWriter writer)
    118    {
    119        // Run the queue until empty.
    120
    121        queue.run(writer);
    122    }
    123
    124    public void addPartialMarkupRendererFilter(PartialMarkupRendererFilter filter)
    125    {
    126        assert filter != null;
    127
    128        partialRenderInitialized = true;
    129
    130        filters.push(filter);
    131    }
    132
    133    public void renderPartial(MarkupWriter writer, JSONObject reply)
    134    {
    135        checkQueue();
    136
    137        PartialMarkupRenderer terminator = new PartialMarkupRenderer()
    138        {
    139            public void renderMarkup(MarkupWriter writer, JSONObject reply)
    140            {
    141                render(writer);
    142            }
    143        };
    144
    145        PartialMarkupRenderer delegate = terminator;
    146
    147        while (!filters.isEmpty())
    148        {
    149            PartialMarkupRendererFilter filter = filters.pop();
    150
    151            PartialMarkupRenderer bridge = new Bridge(filter, delegate);
    152
    153            delegate = bridge;
    154        }
    155
    156        // The initialize methods will already have been invoked.
    157
    158        delegate.renderMarkup(writer, reply);
    159    }
    160}