Add debugging hooks to #parse
-----------------------------

                 Key: VELOCITY-586
                 URL: https://issues.apache.org/jira/browse/VELOCITY-586
             Project: Velocity
          Issue Type: Improvement
          Components: Engine
    Affects Versions: 1.5
            Reporter: Tim White
            Priority: Minor


When you have a page that is made up of a complex web of templates and #parsed 
includes, it is critical to be able to quickly see what parts of the page are 
really in what file.

To do this, I've hacked #parse to inject debugging information into the 
template when a 'debug mode' is engaged.  This debug mode is engaged via a URL 
parameter.

I also inject this information in the VVS right before the main template is 
rendered.  In this way, you can create a debug overlay on top of the page in 
your browser that shows you where each piece of text is coming from.

I think this probably could better be implemented using hooks, so that a user 
could trigger when the debug information was being injected.

package org.apache.velocity.runtime.directive;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.    
 */

import java.io.IOException;
import java.io.Writer;

import javax.servlet.http.HttpSession;

import org.apache.velocity.Template;
import org.apache.velocity.app.event.EventHandlerUtil;
import org.apache.velocity.context.InternalContextAdapter;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.parser.node.Node;
import org.apache.velocity.runtime.parser.node.SimpleNode;

import com.qwest.velocity.VelocityConstants;

/**
 * Pluggable directive that handles the <code>#parse()</code> statement in VTL.
 * 
 * <pre>
 *  Notes:
 *  -----
 *   1) The parsed source material can only come from somewhere in
 *     the TemplateRoot tree for security reasons. There is no way
 *     around this.  If you want to include content from elsewhere on
 *     your disk, use a link from somwhere under Template Root to that
 *     content.
 * 
 *   2) There is a limited parse depth.  It is set as a property
 *     &quot;parse_directive.maxdepth = 10&quot;  for example.  There is a 20 
iteration
 *     safety in the event that the parameter isn't set.
 * </pre>
 * 
 * @author <a href="mailto:[EMAIL PROTECTED]">Geir Magnusson Jr.</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Jason van Zyl</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Christoph Reck</a>
 * @version $Id: Parse.java 463298 2006-10-12 16:10:32Z henning $
 */
public class Parse extends InputBase {
        /**
         * Return name of this directive.
         * 
         * @return The name of this directive.
         */
        public String getName() {
                return "parse";
        }

        /**
         * Return type of this directive.
         * 
         * @return The type of this directive.
         */
        public int getType() {
                return LINE;
        }

        /**
         * iterates through the argument list and renders every argument that 
is appropriate. Any non appropriate arguments are logged, but render() 
continues.
         * 
         * @param context
         * @param writer
         * @param node
         * @return True if the directive rendered successfully.
         * @throws IOException
         * @throws ResourceNotFoundException
         * @throws ParseErrorException
         * @throws MethodInvocationException
         */
        public boolean render(InternalContextAdapter context, Writer writer, 
Node node) throws IOException, ResourceNotFoundException, ParseErrorException, 
MethodInvocationException {
                /*
                 * if rendering is no longer allowed (after a stop), we can 
safely skip execution of all the parse directives.
                 */
                if (!context.getAllowRendering()) {
                        return true;
                }

                /*
                 * did we get an argument?
                 */
                if (node.jjtGetChild(0) == null) {
                        rsvc.getLog().error("Portal #parse() null argument");
                        return false;
                }

                /*
                 * does it have a value? If you have a null reference, then no.
                 */
                Object value = node.jjtGetChild(0).value(context);

                if (value == null) {
                        rsvc.getLog().error("Portal #parse() null argument");
                        return false;
                }

                /*
                 * get the path
                 */
                String sourcearg = value.toString();

                /*
                 * check to see if the argument will be changed by the event 
cartridge
                 */

                String arg = EventHandlerUtil.includeEvent(rsvc, context, 
sourcearg, context.getCurrentTemplateName(), getName());

                /*
                 * a null return value from the event cartridge indicates we 
should not input a resource.
                 */
                boolean blockinput = false;
                if (arg == null)
                        blockinput = true;

                /*
                 * see if we have exceeded the configured depth. If it isn't 
configured, put a stop at 20 just in case.
                 */

                Object[] templateStack = context.getTemplateNameStack();

                if (templateStack.length >= 
rsvc.getInt(RuntimeConstants.PARSE_DIRECTIVE_MAXDEPTH, 20)) {
                        StringBuffer path = new StringBuffer();

                        for (int i = 0; i < templateStack.length; ++i) {
                                path.append(" > " + templateStack[i]);
                        }

                        rsvc.getLog().error("Portal #parse: Max recursion depth 
reached (" + templateStack.length + ')' + " File stack:" + path);
                        return false;
                }

                /*
                 * now use the Runtime resource loader to get the template
                 */

                Template t = null;

                try {
                        if (!blockinput)
                                t = rsvc.getTemplate(arg, 
getInputEncoding(context));
                } catch (ResourceNotFoundException rnfe) {
                        /*
                         * the arg wasn't found. Note it and throw
                         */
                        rsvc.getLog().error("Portal #parse(): cannot find 
template '" + arg + "', called from template " + 
context.getCurrentTemplateName() + " at (" + getLine() + ", " + getColumn() + 
")");
                        throw rnfe;
                } catch (ParseErrorException pee) {
                        /*
                         * the arg was found, but didn't parse - syntax error 
note it and throw
                         */

                        rsvc.getLog().error(
                                        "Portal #parse(): syntax error in 
#parse()-ed template '" + arg + "', called from template " + 
context.getCurrentTemplateName() + " at (" + getLine() + ", " + getColumn() + 
")");

                        throw pee;
                }
                /**
                 * pass through application level runtime exceptions
                 */
                catch (RuntimeException e) {
                        throw e;
                } catch (Exception e) {
                        rsvc.getLog().error("Portal #parse() : arg = " + arg + 
'.', e);
                        return false;
                }

                /*
                 * and render it
                 */
                try {
                        if (rsvc.getLog().isDebugEnabled()) {
                                rsvc.getLog().debug("*** Portal #Parse Template 
Path: " + t.getName());
                        }
                        
                        /* Save the name of the main template before resetting 
templateName for this include */
                        String mainTemplateName = "";
                        if (context.get(VelocityConstants.TEMPLATE_NAME_VAR) != 
null) {
                                mainTemplateName = 
context.get(VelocityConstants.TEMPLATE_NAME_VAR).toString();
                        }

                        /* Set the name of the current template in the context 
for general use */
                        context.put(VelocityConstants.TEMPLATE_NAME_VAR, 
t.getName());

                        /* Check various places to see if debug mode is enabled 
*/
                        boolean debugMode = false;
                        /* This supports PortalVelocityViewServlet */
                        if (context.get(VelocityConstants.DEBUG_ATTRIBUTE) != 
null) {
                                debugMode = true;
                        }
                        /* This supports qcms.getMessage() */
                        if (context.get("session") != null) {
                                HttpSession session = (HttpSession) 
context.get("session");
                                if (session.getAttribute("qCmsDebugger") != 
null) {
                                        debugMode = true;
                                }
                        }

                        /* Print Debug start output to browser if debug mode is 
on */
                        if (debugMode) {
                                Template startTemplate = 
rsvc.getTemplate(VelocityConstants.DEBUG_OUTLINE_START_TEMPLATE, 
getInputEncoding(context));
                                startTemplate.merge(context, writer);
                                Template nameTemplate = 
rsvc.getTemplate(VelocityConstants.DEBUG_NAME_TEMPLATE, 
getInputEncoding(context));
                                nameTemplate.merge(context, writer);
                        }
                        
                        if (!blockinput) {
                                context.pushCurrentTemplateName(arg);
                                ((SimpleNode) t.getData()).render(context, 
writer);
                        }
                        
                        /* Print Debug end output to browser if debug mode is 
on */
                        if (debugMode) {
                                Template endTemplate = 
rsvc.getTemplate(VelocityConstants.DEBUG_OUTLINE_END_TEMPLATE, 
getInputEncoding(context));
                                endTemplate.merge(context, writer);
                        }

                        /* Restore the templateName variable to match the main 
template name */
                        context.put(VelocityConstants.TEMPLATE_NAME_VAR, 
mainTemplateName);
                }

                /*
                 * if it's a MIE, it came from the render.... throw it...
                 */
                catch (MethodInvocationException e) {
                        throw e;
                }

                /**
                 * pass through application level runtime exceptions
                 */
                catch (RuntimeException e) {
                        throw e;
                }

                catch (Exception e) {
                        rsvc.getLog().error("Portal #parse: Exception rendering 
#parse(" + arg + ')', e);
                        return false;
                } finally {
                        if (!blockinput)
                                context.popCurrentTemplateName();
                }

                /*
                 * note - a blocked input is still a successful operation as 
this is expected behavior.
                 */

                return true;
        }

}


-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to