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 boolean debugEnabled = logger.isDebugEnabled(); 054 055 long startNanos = -1l; 056 057 if (debugEnabled) 058 { 059 startNanos = System.nanoTime(); 060 } 061 int commandCount = 0; 062 int maxDepth = 0; 063 064 // Seems to make sense to use one try/finally around the whole processInbound, rather than 065 // around each call to render() since the end result (in a failure scenario) is the same. 066 067 try 068 { 069 while (!queue.isEmpty()) 070 { 071 maxDepth = Math.max(maxDepth, queue.getDepth()); 072 073 command = queue.pop(); 074 075 commandCount++; 076 077 if (traceEnabled) logger.trace(TapestryMarkers.RENDER_COMMANDS, "Executing: {}", command); 078 079 command.render(writer, this); 080 } 081 } catch (RuntimeException ex) 082 { 083 String message = String.format("Render queue error in %s: %s", command, ExceptionUtils.toMessage(ex)); 084 085 logger.error(message, ex); 086 087 throw new RenderQueueException(message, renderingComponents.getSnapshot(), ex); 088 } 089 090 if (debugEnabled) 091 { 092 long endNanos = System.nanoTime(); 093 094 long elapsedNanos = endNanos - startNanos; 095 double elapsedSeconds = ((double) elapsedNanos) / 1000000000d; 096 097 logger.debug(TapestryMarkers.RENDER_COMMANDS, 098 String.format("Executed %,d rendering commands (max queue depth: %,d) in %.3f seconds", 099 commandCount, 100 maxDepth, 101 elapsedSeconds)); 102 } 103 } 104 105 public void startComponent(ComponentResources resources) 106 { 107 assert resources != null; 108 renderingComponents.push(resources); 109 } 110 111 public void endComponent() 112 { 113 renderingComponents.pop(); 114 } 115}