You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@velocity.apache.org by "Nathan Bubna (JIRA)" <de...@velocity.apache.org> on 2008/08/21 02:24:44 UTC

[jira] Commented: (VELOCITY-586) Add debugging hooks to #parse

    [ https://issues.apache.org/jira/browse/VELOCITY-586?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12624208#action_12624208 ] 

Nathan Bubna commented on VELOCITY-586:
---------------------------------------

Tim, could you submit this as a diff -u?  It's really hard to tell what it is you've changed here.

> 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.
> I also suggest that the name of the current template, it's resource loader, etc. be passed into the context, so it can be used to see what template is where and what loader it came from.
> Finally, if there could be a way to conditionally prepend or append a template to each merged or #parse template, that could also help.
> Thanks!!!
> 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:geirm@optonline.net">Geir Magnusson Jr.</a>
>  * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
>  * @author <a href="mailto:Christoph.Reck@dlr.de">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: dev-unsubscribe@velocity.apache.org
For additional commands, e-mail: dev-help@velocity.apache.org