    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}