001// Copyright 2006, 2007, 2008, 2010, 2011 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.Stack;
022import org.apache.tapestry5.runtime.RenderCommand;
023import org.apache.tapestry5.runtime.RenderQueue;
024import org.slf4j.Logger;
025
026public class RenderQueueImpl implements RenderQueue
027{
028    private static final int INITIAL_QUEUE_DEPTH = 200;
029
030    private final Stack<RenderCommand> queue = CollectionFactory.newStack(INITIAL_QUEUE_DEPTH);
031
032    private final Stack<ComponentResources> renderingComponents = CollectionFactory.newStack();
033
034    private final Logger logger;
035
036    public RenderQueueImpl(Logger logger)
037    {
038        this.logger = logger;
039    }
040
041    public void push(RenderCommand command)
042    {
043        assert command != null;
044        queue.push(command);
045    }
046
047    public void run(MarkupWriter writer)
048    {
049        RenderCommand command = null;
050
051        boolean traceEnabled = logger.isTraceEnabled(TapestryMarkers.RENDER_COMMANDS);
052
053        long startNanos = System.nanoTime();
054        int commandCount = 0;
055        int maxDepth = 0;
056
057        // Seems to make sense to use one try/finally around the whole processInbound, rather than
058        // around each call to render() since the end result (in a failure scenario) is the same.
059
060        try
061        {
062            while (!queue.isEmpty())
063            {
064                maxDepth = Math.max(maxDepth, queue.getDepth());
065
066                command = queue.pop();
067
068                commandCount++;
069
070                if (traceEnabled) logger.trace(TapestryMarkers.RENDER_COMMANDS, "Executing: {}", command);
071
072                command.render(writer, this);
073            }
074        }
075        catch (RuntimeException ex)
076        {
077            String message = ServicesMessages.renderQueueError(command, 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}