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    }