001// Copyright 2006-2013 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.ComponentResources; 018import org.apache.tapestry5.MarkupWriter; 019import org.apache.tapestry5.TapestryMarkers; 020import org.apache.tapestry5.ioc.internal.util.CollectionFactory; 021import org.apache.tapestry5.ioc.util.ExceptionUtils; 022import org.apache.tapestry5.ioc.util.Stack; 023import org.apache.tapestry5.runtime.RenderCommand; 024import org.apache.tapestry5.runtime.RenderQueue; 025import org.slf4j.Logger; 026 027public class RenderQueueImpl implements RenderQueue 028{ 029 private static final int INITIAL_QUEUE_DEPTH = 200; 030 031 private final Stack<RenderCommand> queue = new Stack<RenderCommand>(INITIAL_QUEUE_DEPTH); 032 033 private final Stack<ComponentResources> renderingComponents = CollectionFactory.newStack(); 034 035 private final Logger logger; 036 037 public RenderQueueImpl(Logger logger) 038 { 039 this.logger = logger; 040 } 041 042 public void push(RenderCommand command) 043 { 044 assert command != null; 045 queue.push(command); 046 } 047 048 public void run(MarkupWriter writer) 049 { 050 RenderCommand command = null; 051 052 boolean traceEnabled = logger.isTraceEnabled(TapestryMarkers.RENDER_COMMANDS); 053 054 long startNanos = System.nanoTime(); 055 int commandCount = 0; 056 int maxDepth = 0; 057 058 // Seems to make sense to use one try/finally around the whole processInbound, rather than 059 // around each call to render() since the end result (in a failure scenario) is the same. 060 061 try 062 { 063 while (!queue.isEmpty()) 064 { 065 maxDepth = Math.max(maxDepth, queue.getDepth()); 066 067 command = queue.pop(); 068 069 commandCount++; 070 071 if (traceEnabled) logger.trace(TapestryMarkers.RENDER_COMMANDS, "Executing: {}", command); 072 073 command.render(writer, this); 074 } 075 } catch (RuntimeException ex) 076 { 077 String message = String.format("Render queue error in %s: %s", command, ExceptionUtils.toMessage(ex)); 078 079 logger.error(message, ex); 080 081 throw new RenderQueueException(message, renderingComponents.getSnapshot(), ex); 082 } 083 084 long endNanos = System.nanoTime(); 085 086 long elapsedNanos = endNanos - startNanos; 087 double elapsedSeconds = ((double) elapsedNanos) / 1000000000d; 088 089 logger.debug(TapestryMarkers.RENDER_COMMANDS, 090 String.format("Executed %,d rendering commands (max queue depth: %,d) in %.3f seconds", 091 commandCount, 092 maxDepth, 093 elapsedSeconds)); 094 } 095 096 public void startComponent(ComponentResources resources) 097 { 098 assert resources != null; 099 renderingComponents.push(resources); 100 } 101 102 public void endComponent() 103 { 104 renderingComponents.pop(); 105 } 106}