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 }