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}