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