Hacking Velocity – ApacheCon 2004 • • • • • • • What is Velocity Velocity Tools Custom Directives Custom Resourceloaders Custom Introspector Adding Event Handlers Modifying Velocity Syntax Presenter Contact Info: Will Glass-Husain Menlo Park, California.

Download Report

Transcript Hacking Velocity – ApacheCon 2004 • • • • • • • What is Velocity Velocity Tools Custom Directives Custom Resourceloaders Custom Introspector Adding Event Handlers Modifying Velocity Syntax Presenter Contact Info: Will Glass-Husain Menlo Park, California.

Hacking Velocity – ApacheCon 2004
•
•
•
•
•
•
•
What is Velocity
Velocity Tools
Custom Directives
Custom Resourceloaders
Custom Introspector
Adding Event Handlers
Modifying Velocity Syntax
Presenter Contact Info:
Will Glass-Husain
Menlo Park, California USA
[email protected]
+ 1 415 440-7500
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
1
What is Velocity?
• A templating engine that can generate any type of text output,
including HTML, email, SQL, or Java source code.
• Based on a "pull" approach in which a text file includes
"References" to data objects that are pulled from a provided
"Context".
$Date
Dear $Contact.FirstName $Contact.LastName,
Thank you for your recent purchase of
$Order.ProductName. The cost of the item was
$Order.TotalCost#if($Order.Tax > 0) including
a tax of $Order.TaxRate%#end.
Order
Date
Contact
Sincerely,
November 17, 2004
Biggest and Best Book Merchants, Inc.
Dear Jennifer Glass,
Thank you for your recent purchase of Alice in
Wonderland, Annotated Edition. The cost of
the item was $29.95.
Sincerely,
Biggest and Best Book Merchants, Inc.
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
2
Basic Velocity Concepts
• References
– References objects from the Velocity context. Starts with a "$".
– Can have Properties or Methods
– Common practice is to provide two types: data objects (with
properties) and "tools" (with methods).
– Examples:
• $contact.FirstName
• $format("$#,##0.00",$order.price)
• Directives
– Control statements. Starts with a "#"
– Example block directive:
• #if ($columncount > 0)
display a table
#end
– Example line directive:
• #include('header.vm')
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
3
How To Use Velocity
Load a Template From a File
VelocityEngine ve = new VelocityEngine();
ve.setProperty("file.resource.loader.path","templates");
ve.init();
Context c = new VelocityContext();
c.add("order",order);
c.add("contact",contact);
Template t1 = ve.getTemplate("inputfile.vm");
StringWriter writer = new StringWriter();
t1.merge(c,writer);
String result = writer.toString();
Evaluate a Template stored as a String
VelocityEngine ve = new VelocityEngine();
ve.init();
Context c = new VelocityContext().
c.add("order",order);
c.add("contact",contact);
StringWriter writer = new StringWriter();
ve.evaluate(c,writer,"velocity",inputstring);
String result = writer.toString()
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
4
Improve Productivity with Velocity-Tools
from the web site:
http://jakarta.apache.org/velocity/tools/index.html
VelocityTools is a collection of Velocity subprojects with a common goal of creating tools and
infrastructure for building both web and non-web applications using the Velocity template
engine.
•
GenericTools
–
•
VelocityView
–
–
–
•
GenericTools is a group of reuseable and documented tools that can be added to a Velocity context.
A tool is simply a class which can perform various tasks when made available to the Velocity engine.
Most tools are optimized for use with an automatically managed toolbox (see VelocityViewServlet).
VelocityView contains a standalone servlet (VelocityViewServlet) which can render templates for web
applications.
Also included is a Toolbox Manager which can automatically make "view tools" and data available to
the templates. Any class with public methods can be used as a tool in the template.
VelocityLayoutServlet is an extension of the basic VelocityViewServlet that can render screen content
into common layout templates.
VelocityStruts
–
–
VelocityStruts is a set of tools for using the Velocity template engine as the view layer for a web
application built upon the Jakarta Struts framework.
This work leverages the VelocityViewServlet and additional tools which make it easy to integrate the
Velocity with Struts. Several example hybrid applications are included.
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
5
Example Tools
Generic Tools
VelocityView (web) Tools
•
•
•
•
•
•
•
•
•
•
•
DateTool
IteratorTool
MathTool
NumberTool
RenderTool
AbstractSearchTool
CookieTool
ImportTool
LinkTool
ParameterParser
ViewRenderTool
Using a Tool
Context c = new VelocityContext();
c.add("date",new DateTool());
Template t1 = ve.getTemplate("inputfile.vm");
StringWriter writer = new StringWriter();
t1.merge(c,writer);
String result = writer.toString();
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
6
VelocityViewServlet: Serve Velocity Pages Over the Web
• Quick and easy way to build a web app.
• Serves Velocity pages just like HTM or JSP files.
• Tools are automatically placed in the Velocity context
according to "toolbox.xml" config file. Excerpt:
<tool>
<key>math</key>
<scope>application</scope>
<class>org.apache.velocity.tools.generic.MathTool</class>
</tool>
• Any tool that has a scope of session or request is passed session
and/or request info when it is initialized. (if the tool implements
ViewContext).
• VelocityViewServlet can be subclassed to add additional
functionality. Example (in distribution): VelocityLayoutServlet,
which uses a 2 pass render in order to apply a common layout to
all web pages.
Note: VelocityViewServlet replaces deprecated VelocityServlet distributed with Velocity project
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
7
VelocityViewServlet: Configuration Example (web.xml)
WEB-INF/web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>Example application</display-name>
<!-- Define Velocity template compiler -->
<servlet>
<servlet-name>velocity</servlet-name>
<servlet-class>org.apache.velocity.tools.view.servlet.VelocityViewServlet
</servlet-class>
<init-param>
<param-name>org.apache.velocity.toolbox</param-name>
<param-value>/WEB-INF/toolbox.xml</param-value>
</init-param>
<init-param>
<param-name>org.apache.velocity.properties</param-name>
<param-value>/WEB-INF/velocity.properties</param-value>
</init-param>
</servlet>
<!-- Map *.vm files to Velocity -->
<servlet-mapping>
<servlet-name>velocity</servlet-name>
<url-pattern>*.vm</url-pattern>
</servlet-mapping>
</web-app>
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
8
VelocityViewServlet: Configuration Example (toolbox.xml)
WEB-INF/toolbox.xml
<?xml version="1.0"?>
<toolbox>
<tool>
<key>date</key>
<scope>application</scope>
<class>org.apache.velocity.tools.generic.DateTool</class>
</tool>
<tool>
<key>params</key>
<scope>request</scope>
<class>org.apache.velocity.tools.view.tools.ParameterParser</class>
</tool>
<tool>
<key>cookie</key>
<scope>request</scope>
<class>org.apache.velocity.tools.view.tools.CookieTool</class>
</tool>
<data type="number">
<key>app_version</key>
<value>1.5</value>
</data>
<data type="string">
<key>app_name</key>
<value>A Carroll Compendium</value>
</data>
</toolbox>
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
9
VelocityViewServlet: Configuration Example (velocity.properties)
WEB-INF/velocity.properties
# all optional
velocimacro.library = /WEB-INF/VM_global_library.vm
velocimacro.permissions.allow.inline = true
velocimacro.permissions.allow.inline.to.replace.global = false
velocimacro.permissions.allow.inline.local.scope = false
velocimacro.context.localscope = false
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
10
VelocityViewStruts: An Alternative To JSP
VelocityStruts Tools
•
•
•
•
ActionMessagesTool
ErrorsTool
FormTool
MessageTool
November 17, 2004
•
•
•
•
StrutsLinkTool
SecureLinkTool
TilesTool
ValidatorTool
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
11
Controlling Velocity Template Generation with a Custom Directive
• Consider – can the problem you are trying to solve be done
more simply with a Tool or VelocityMacro?
• If not, Velocity allows you to code your own custom directives by
doing the following:
– Create a class that extends
org.apache.velocity.runtime.directive.Directive
– Add the following to velocity.properties
userdirective = yourdirectiveclass
• Review the directives in the velocity source code for good
examples. For example, review
org.apache.velocity.runtime.directive.ForEach
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
12
Custom Directive Example: Generate Endnotes
Usage:
#endNotes
All in the golden afternoon
(*1:In these prefatory verses Carroll recalls that "golden afternoon" in 1862 when he and
his friend the Reverend Robinson Duckworth (then a fellow of Trinity College, Oxford,
later canon of Westminster) took the three charming Liddell sisters on a rowing
expedition up the Thames. *)
Full leisurely we glide;
For both our oars, with little skill,
By little arms are plied,
While little hands make vain pretence
Our wanderings to guide.
(* 2:Note how this stanza puns three times with the word "little." "Liddle" was
pronounced to rhyme with "fiddle." *)
#end
Results:
All in the golden afternoon
Full leisurely we glide;
For both our oars, with little skill,
By little arms are plied,
While little hands make vain pretence
Our wanderings to guide.
NOTES
1: In these prefatory verses Carroll recalls that "golden afternoon" in 1862 when he and
his friend the Reverend Robinson Duckworth (then a fellow of Trinity College, Oxford,
later canon of Westminster) took the three charming Liddell sisters on a rowing
expedition up the Thames.
2:Note how this stanza puns three times with the word "little." "Liddle" was pronounced
to rhyme with "fiddle."
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
13
Custom Directive Example: Code Excerpt
Full code listings available at:
http://jlamp.com/apachecon2004/
public class EndNoteDirective extends Directive {
public String getName() { return "endNotes"; }
public int getType()
{ return BLOCK; }
public boolean render(InternalContextAdapter context, java.io.Writer writer,Node node)
throws ResourceNotFoundException,ParseErrorException,MethodInvocationException
{
// first, parse the entire block and retrieve the content
StringWriter internalwriter = new StringWriter();
// the node's children are the arguments and the body.
// here there should be only one child, since there are no arguments
node.jjtGetChild(0).render(context, internalwriter);
String sourcecontent = internalwriter.toString();
internalwriter.close();
// missing code creates new "finalcontent" based on "sourcecontent"
writer.write(finalcontent.toString());
return true;
}
}
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
14
Custom Directive Example: Configuration
Complete sample app available at:
http://jlamp.com/apachecon2004/
## Load templates from classpath
resource.loader = class
class.resource.loader.class =
org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
## Specify the user directive
userdirective=com.jlamp.directive.EndNoteDirective
## Log messages displayed with log4j
runtime.log.logsystem.class = org.apache.velocity.runtime.log.SimpleLog4JLogSystem
runtime.log.logsystem.log4j.category = com.jlamp.velocity
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
15
Custom ResourceLoaders
• A ResourceLoader is a mechanism to load a template. Velocity
provides these built in ResourceLoaders:
–
–
–
–
ClasspathResourceLoader
DataSourceResourceLoader
FileResourceLoader
JarResourceLoader
• To implement your own resource loader, extend
org.apache.velocity.runtime.resource.loader.ResourceLoader
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
16
Custom ResourceLoader Example: Excerpt (FTP resource loader)
Complete sample app available at:
http://jlamp.com/apachecon2004/
public class FtpResourceLoader extends ResourceLoader {
String username;
String password;
String hostname;
String rootDirectory;
public void init (ExtendedProperties configuration)
{
username = configuration.getString("username");
password = configuration.getString("password");
hostname = configuration.getString("host");
rootDirectory = configuration.getString("root");
}
public long getLastModified (Resource resource)
{
String name = resource.getName();
if (name.startsWith("/")) name = name.substring(1);
return getFileModificationDate(username,password,hostname,rootDirectory + name).getTime();
}
public boolean isSourceModified (Resource resource)
{
long date = getLastModified(resource);
return (date == 0) || (date != resource.getLastModified());
}
public InputStream getResourceStream (String source) throws ResourceNotFoundException
{
if (source.startsWith("/")) source = source.substring(1);
InputStream i = getFileStream(username,password,hostname,rootDirectory + source);
if (i == null)
throw new ResourceNotFoundException(source);
else
return i;
}
}
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
17
Custom ResourceLoader Example: Configuration
Complete sample app available at:
http://jlamp.com/apachecon2004/
## Load templates from FTP site
resource.loader = ftp
ftp.resource.loader.class = com.jlamp.resourceloader.FtpResourceLoader
ftp.resource.loader.username = templateuser
ftp.resource.loader.password = apricots
ftp.resource.loader.host = www.jlamp.com
ftp.resource.loader.root = /app/templates
## Log messages displayed with log4j
runtime.log.logsystem.class = org.apache.velocity.runtime.log.SimpleLog4JLogSystem
runtime.log.logsystem.log4j.category = com.jlamp.velocity
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
18
Adding an Event Handler
• Velocity provides a mechanism to intercept and modify parsing
operation.
• Currently, three event handlers available:
– ReferenceInsertionEventHandler: Modify the text that gets inserted
from a reference (e.g. $date)
– NullSetEventHandler: Prevents logging of null #set operations
– MethodExceptionEventHandler: Intercept a method thrown by a
tool in the context.
• Event handlers are instantiated for each page that is parsed,
and are "attached" to the context before the template is
merged:
EventCartridge ec = new EventCartridge();
ec.addEventHandler(new CustomReferenceInsertionHandler());
ec.attachToContext( context );
// now merge the template
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
19
Event Handler Example: Escape XML Entities (excerpt)
Complete sample app available at:
http://jlamp.com/apachecon2004/
/**
* Escape all XML Entities in the reference insertion. Specifically, the following
* conversions are performed:
* <DL>
* <DT>&amp;</DT><DD>&amp;amp;</DD>
* <DT>&lt;</DT><DD>&amp;lt;</DD>
* <DT>&gt;</DT><DD>&amp;gt;</DD>
* <DT>&quot;</DT><DD>&amp;quot;</DD>
* </DL>
*/
public class EscapeXMLEntities implements ReferenceInsertionEventHandler {
/**
* Escape the XML entities for all inserted references.
*/
public Object referenceInsert(String reference, Object value) {
String val = value.toString();
return escapeText(val);
}
}
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
20
Custom Introspector
• The introspector is used to discover and execute reference
properties and method calls.
• The build in introspector calls get/set methods when accessing
properties, and does all method calls.
• Possible custom introspectors:
– Access public fields directly instead of calling get/set methods
– Prevent access of "dangerous" methods by untrusted template
designer such as getClassLoader() and newInstance.
(dirty little secret – any class can be instantiated in a Velocity
Template).
• A custom introspector is fairly complex to implement. The built-in
introspector includes 9 classes in two packages.
• Introspector needs to implement
org.apache.velocity.util.introspection.Uberspect
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
21
An Evil Velocity Template
Please! Use this knowledge to further the cause of the good, not of those with evil intent.
Today's date: $today
Date class: $today.Class
## Let's do some mischief and instantiate an arbitrary class
#set ($fileclass = $today.Class.forName("java.io.File"))
#set ($stringclass = $today.Class.forName("java.lang.String"))
#set ($paramlist = [".."])
#set ($paramarray = $paramlist.toArray())
#set ($constructors = $fileclass.Constructors)
## Call the constructor java.io.File(String)
#foreach ($c in $constructors)
#if ($c.toString() == "public java.io.File(java.lang.String)")
#set ($file = $c.newInstance($paramarray))
$file
#end
#end
## Now, get the File list.
## Bad, Bad, Bad!
Think about what else you could do!
#set ($files = $file.list())
App Directory contents:
#foreach ($f in $files)
$f
#end
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
22
An Evil Velocity Template: Result (standard introspector)
Today's date: Mon Oct 18 01:26:06 PDT 2004
Date class: class java.util.Date
..
App Directory contents:
bin
build
classes_src
java
lib
results
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
23
An Evil Velocity Template: Result (custom introspector)
Today's date: Mon Oct 18 01:33:35 PDT 2004
Date class: class java.util.Date
App Directory contents:
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
24
Custom Introspector: Code Excerpt
Complete sample app available at:
http://jlamp.com/apachecon2004/
/**
* Property getter
*/
public VelPropertyGet getPropertyGet(Object obj, String identifier, Info i)
throws Exception
{
if (!checkObjectExecutePermission(obj,null))
{
rlog.warn ("Cannot retrieve get method from object of class " +
obj.getClass().getName() +
" due to execute security restrictions. ["
+ i.getLine() + "," + i.getColumn() + "]"
+ " in template " + i.getTemplateName()
+ ".");
return null;
}
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
25
Custom Introspector Example: Configuration
Complete sample app available at:
http://jlamp.com/apachecon2004/
## Custom introspector
runtime.introspector.uberspect = com.jlamp.introspection.CustomIntrospector
## Load templates from classpath
resource.loader = class
class.resource.loader.class =
org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
## Log messages displayed with log4j
runtime.log.logsystem.class = org.apache.velocity.runtime.log.SimpleLog4JLogSystem
runtime.log.logsystem.log4j.category = com.jlamp.velocity
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
26
Modifying Velocity Syntax
• Velocity Template Language (VTL) is specified in a grammar file
designed to be used with JavaCC (javacc.dev.java.net).
• Change the language syntax by editing Parser.jjt.
• Run jjtree and javacc to generate new Parser.java files
• Add new capabilities by creating new Node files and copying
into the package org.apache.velocity.runtime.parser.node
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
27
Modifying Velocity Syntax: Adding #{else}
• Problem: Adding text without a space in #if statement is difficult.
• Bad syntax:
#if($condition)text1#elsetext2#end
• Use block comments #* *# to split #else and text (unreadable)
#if($condition)text1#else#**#text2#end
• Proposed syntax – use optional brackets around directive
#if($condition)text1#{else}text2#end
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
28
Modifying Velocity Syntax: Old Parser.jjt (excerpt)
Full modification available as patch:
http://nagoya.apache.org/bugzilla/show_bug.cgi?id=7189
<PRE_DIRECTIVE>
TOKEN :
{
<END: "end" ( ( " " | "\t" )* ( "\n" | "\r" | "\r\n" ) )? >
{
inDirective = false;
stateStackPop();
}
|
<IF_DIRECTIVE: "if">
{
SwitchTo(DIRECTIVE);
}
|
<ELSEIF_DIRECTIVE: "elseif">
{
SwitchTo(DIRECTIVE);
}
|
<ELSE_DIRECTIVE: "else" ( ( " " | "\t" )* ( "\n" | "\r" | "\r\n" ) )?
{
inDirective = false;
stateStackPop();
}
|
<STOP_DIRECTIVE: "stop">
{
matchedToken.kind = EOF;
fileDepth = 0;
}
>
}
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
29
Modifying Velocity Syntax: Old Parser.jjt (excerpt)
Complete sample app available at:
http://jlamp.com/apachecon2004/
<PRE_DIRECTIVE>
TOKEN :
{
<END: ( "end" ( ( " " | "\t" )* ( "\n" | "\r" | "\r\n" ) )? )
| ("{end}" ( ( " " | "\t" )* ( "\n" | "\r" | "\r\n" ) )? ) >
{
inDirective = false;
stateStackPop();
}
|
<IF_DIRECTIVE: "if" | "{if}">
{
SwitchTo(DIRECTIVE);
}
|
<ELSEIF_DIRECTIVE: "elseif" | "{elseif}">
{
SwitchTo(DIRECTIVE);
}
|
<ELSE_DIRECTIVE:
( "else" ( ( " " | "\t" )* ( "\n" | "\r" | "\r\n" ) )? )
| ( "{else}" ( ( " " | "\t" )* ( "\n" | "\r" | "\r\n" ) )? )
inDirective = false;
stateStackPop();
}
|
}
<STOP_DIRECTIVE: "stop" | "{stop}" >
matchedToken.kind = EOF;
fileDepth = 0;
}
November 17, 2004
>
{
{
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
30
Modifying Velocity Syntax: Summary of Steps
1. Download project "jakarta-velocity" from Jakarta CVS
2. Modify "Parser.jjt"
3. Execute shell script "build":
#!/bin/sh
echo "Running JJTree ..."
jjtree Parser.jjt
echo "Running JavaCC ..."
javacc Parser.jj
# Remove the generated nodes as they are now
# in a package of their own.
rm -f AST*
rm -f Node.java
rm -f SimpleNode.java
rm -f ParserVisitor.java
4. If necessary (new functionality added), add new node files to
package org.apache.velocity.runtime.parser.node.
November 17, 2004
(c) 2004 Will Glass-Husain. Non-commercial redistribution permitted freely.
31