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 015package org.apache.tapestry5.internal.services; 016 017import org.apache.tapestry5.MarkupWriter; 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.ioc.internal.util.CollectionFactory; 023import org.apache.tapestry5.ioc.util.Stack; 024import org.apache.tapestry5.json.JSONObject; 025import org.apache.tapestry5.runtime.RenderCommand; 026import org.apache.tapestry5.services.PartialMarkupRenderer; 027import org.apache.tapestry5.services.PartialMarkupRendererFilter; 028import 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) 036public 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 public void addPartialRenderer(RenderCommand renderer) 097 { 098 assert renderer != null; 099 100 partialRenderInitialized = true; 101 102 queue.push(renderer); 103 } 104 105 public Page getRenderingPage() 106 { 107 return page; 108 } 109 110 public void render(MarkupWriter writer) 111 { 112 // Run the queue until empty. 113 114 queue.run(writer); 115 } 116 117 public void addPartialMarkupRendererFilter(PartialMarkupRendererFilter filter) 118 { 119 assert filter != null; 120 121 partialRenderInitialized = true; 122 123 filters.push(filter); 124 } 125 126 public void renderPartial(MarkupWriter writer, JSONObject reply) 127 { 128 PartialMarkupRenderer terminator = new PartialMarkupRenderer() 129 { 130 public void renderMarkup(MarkupWriter writer, JSONObject reply) 131 { 132 render(writer); 133 } 134 }; 135 136 PartialMarkupRenderer delegate = terminator; 137 138 while (!filters.isEmpty()) 139 { 140 PartialMarkupRendererFilter filter = filters.pop(); 141 142 PartialMarkupRenderer bridge = new Bridge(filter, delegate); 143 144 delegate = bridge; 145 } 146 147 // The initialize methods will already have been invoked. 148 149 delegate.renderMarkup(writer, reply); 150 } 151}