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.commons.util.CollectionFactory;
021import org.apache.tapestry5.commons.util.ExceptionUtils;
022import org.apache.tapestry5.commons.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}