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
* "parse_directive.maxdepth = 10" 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]