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 }