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}