aboutsummaryrefslogtreecommitdiff
path: root/velocity-engine-scripting
diff options
context:
space:
mode:
Diffstat (limited to 'velocity-engine-scripting')
-rw-r--r--velocity-engine-scripting/pom.xml87
-rw-r--r--velocity-engine-scripting/src/main/java/org/apache/velocity/script/VelocityCompiledScript.java70
-rw-r--r--velocity-engine-scripting/src/main/java/org/apache/velocity/script/VelocityScriptEngine.java338
-rw-r--r--velocity-engine-scripting/src/main/java/org/apache/velocity/script/VelocityScriptEngineFactory.java231
-rw-r--r--velocity-engine-scripting/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory1
-rw-r--r--velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/AbstractScriptTest.java31
-rw-r--r--velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/ScriptEngineTest.java97
-rw-r--r--velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/VelocityScriptContextTest.java159
-rw-r--r--velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/resources/eventtool.vm5
-rw-r--r--velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/tools/CustomEvent.java50
-rw-r--r--velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/tools/EventToolTest.java61
-rw-r--r--velocity-engine-scripting/src/test/resources/velocity.properties17
12 files changed, 1147 insertions, 0 deletions
diff --git a/velocity-engine-scripting/pom.xml b/velocity-engine-scripting/pom.xml
new file mode 100644
index 00000000..1c6eaf9f
--- /dev/null
+++ b/velocity-engine-scripting/pom.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.velocity</groupId>
+ <artifactId>velocity-engine-parent</artifactId>
+ <version>2.4-SNAPSHOT</version>
+ </parent>
+ <artifactId>velocity-engine-scripting</artifactId>
+ <name>Apache Velocity - JSR 223 Scripting</name>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.velocity</groupId>
+ <artifactId>velocity-engine-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>${junit.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <version>${slf4j.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>${surefire.plugin.version}</version>
+ <configuration>
+ <systemProperties>
+ <property>
+ <name>test.resources.dir</name>
+ <value>${project.build.directory}</value>
+ </property>
+ </systemProperties>
+ </configuration>
+ <executions>
+ <execution>
+ <id>integration-test</id>
+ <phase>integration-test</phase>
+ <goals>
+ <goal>test</goal>
+ </goals>
+ <configuration>
+ <skip>false</skip>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/velocity-engine-scripting/src/main/java/org/apache/velocity/script/VelocityCompiledScript.java b/velocity-engine-scripting/src/main/java/org/apache/velocity/script/VelocityCompiledScript.java
new file mode 100644
index 00000000..f88eb803
--- /dev/null
+++ b/velocity-engine-scripting/src/main/java/org/apache/velocity/script/VelocityCompiledScript.java
@@ -0,0 +1,70 @@
+package org.apache.velocity.script;
+
+/*
+ * 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 org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+
+import javax.script.CompiledScript;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptException;
+import java.io.StringWriter;
+import java.io.Writer;
+
+public class VelocityCompiledScript extends CompiledScript
+{
+ protected VelocityScriptEngine engine;
+ protected Template template;
+
+ public VelocityCompiledScript(VelocityScriptEngine e, Template t)
+ {
+ engine = e;
+ template = t;
+ }
+
+ @Override
+ public Object eval(ScriptContext scriptContext) throws ScriptException
+ {
+ VelocityContext velocityContext = VelocityScriptEngine.getVelocityContext(scriptContext);
+ Writer out = scriptContext.getWriter();
+ if (out == null)
+ {
+ out = new StringWriter();
+ scriptContext.setWriter(out);
+ }
+ try
+ {
+ template.merge(velocityContext, out);
+ out.flush();
+ }
+ catch (Exception exp)
+ {
+ throw new ScriptException(exp);
+ }
+ return out;
+ }
+
+ @Override
+ public ScriptEngine getEngine()
+ {
+ return engine;
+ }
+}
diff --git a/velocity-engine-scripting/src/main/java/org/apache/velocity/script/VelocityScriptEngine.java b/velocity-engine-scripting/src/main/java/org/apache/velocity/script/VelocityScriptEngine.java
new file mode 100644
index 00000000..469ca45d
--- /dev/null
+++ b/velocity-engine-scripting/src/main/java/org/apache/velocity/script/VelocityScriptEngine.java
@@ -0,0 +1,338 @@
+package org.apache.velocity.script;
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met: Redistributions of source code
+ * must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution. Neither the name of the Sun Microsystems nor the names of
+ * is contributors may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Main class for the Velocity script engine.
+ * All the variants of the eval() methods return the writer. The default writer is a PrintWriter towards System.out.
+ * To specify a specific writer, use getContext().setWriter(writer). To get a resulting string, pass a StringWriter.
+ *
+ * You can specify a pathname towards a Velocity properties file using the "org.apache.velocity.script.properties" key,
+ * either as a ScriptContext attribute, or as a System property.
+ *
+ * Example use:
+ * <pre>
+ * ScriptEngine vel = new VelocityScriptEngine();
+ * vel.getContext().setAttribute(VelocityScriptEngine.VELOCITY_PROPERTIES_KEY, "path/to/velocity.properties");
+ * vel.getContext().setWriter(new StringWriter());
+ * vel.put("foo","World");
+ * Object result = vel.eval("Hello $foo !");
+ * String helloWorld = result.toString()
+ * </pre>
+ *
+ * Please refer to the javax.script.ScriptEngine documentation for additional details.
+ *
+ * @author A. Sundararajan
+ * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ * @version $Id: VelocityScriptEngine.java$
+ */
+
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.runtime.RuntimeInstance;
+import org.apache.velocity.runtime.resource.loader.ResourceLoader;
+import org.apache.velocity.runtime.resource.loader.StringResourceLoader;
+
+import javax.script.AbstractScriptEngine;
+import javax.script.Bindings;
+import javax.script.Compilable;
+import javax.script.CompiledScript;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineFactory;
+import javax.script.ScriptException;
+import javax.script.SimpleBindings;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Properties;
+
+public class VelocityScriptEngine extends AbstractScriptEngine implements Compilable
+{
+ /**
+ * Key used to provide this engine with the pathname of the Velocity properties file.
+ * This key is first searched in the ScriptContext attributes, then as a System property
+ */
+ public static final String VELOCITY_PROPERTIES_KEY = "org.apache.velocity.script.properties";
+
+ // my factory, may be null
+ private volatile ScriptEngineFactory factory;
+ private volatile RuntimeInstance velocityEngine;
+
+ /**
+ * constructs a new Velocity script engine, linked to the given factory
+ * @param factory
+ */
+ public VelocityScriptEngine(ScriptEngineFactory factory)
+ {
+ this.factory = factory;
+ }
+
+ /**
+ * constructs a new standalone Velocity script engine
+ */
+ public VelocityScriptEngine()
+ {
+ this(null);
+ }
+
+ /**
+ * get the internal Velocity RuntimeInstance
+ * @return the internal Velocity RuntimeInstance
+ */
+ protected RuntimeInstance getVelocityEngine()
+ {
+ return velocityEngine;
+ }
+
+ /**
+ * Evaluate the given script.
+ * If you wish to get a resulting string, call getContext().setWriter(new StringWriter()) then call toString()
+ * on the returned writer.
+ * @param str script source
+ * @param ctx script context
+ * @return the script context writer (by default a PrintWriter towards System.out)
+ * @throws ScriptException
+ */
+ @Override
+ public Object eval(String str, ScriptContext ctx)
+ throws ScriptException
+ {
+ return eval(new StringReader(str), ctx);
+ }
+
+ /**
+ * Evaluate the given script.
+ * If you wish to get a resulting string, call getContext().setWriter(new StringWriter()) then call toString()
+ * on the returned writer.
+ * @param reader script source reader
+ * @param ctx script context
+ * @return the script context writer (by default a PrintWriter towards System.out)
+ * @throws ScriptException
+ */
+ @Override
+ public Object eval(Reader reader, ScriptContext ctx)
+ throws ScriptException
+ {
+ initVelocityEngine(ctx);
+ String fileName = getFilename(ctx);
+ VelocityContext vctx = getVelocityContext(ctx);
+ Writer out = ctx.getWriter();
+ if (out == null)
+ {
+ out = new StringWriter();
+ ctx.setWriter(out);
+ }
+ try
+ {
+ velocityEngine.evaluate(vctx, out, fileName, reader);
+ out.flush();
+ }
+ catch (Exception exp)
+ {
+ throw new ScriptException(exp);
+ }
+ return out;
+ }
+
+ /**
+ * get the factory used to create this script engine
+ * @return factory
+ */
+ @Override
+ public ScriptEngineFactory getFactory()
+ {
+ if (factory == null)
+ {
+ synchronized (this)
+ {
+ if (factory == null)
+ {
+ factory = new VelocityScriptEngineFactory();
+ }
+ }
+ }
+ return factory;
+ }
+
+ /**
+ * creates a new Bindings to be used with this script
+ * @return new bindings
+ */
+ @Override
+ public Bindings createBindings()
+ {
+ return new SimpleBindings();
+ }
+
+ private void initVelocityEngine(ScriptContext ctx)
+ {
+ if (ctx == null)
+ {
+ ctx = getContext();
+ }
+ if (velocityEngine == null)
+ {
+ synchronized (this)
+ {
+ if (velocityEngine != null) return;
+
+ Properties props = getVelocityProperties(ctx);
+ RuntimeInstance tmpEngine = new RuntimeInstance();
+ try
+ {
+ if (props != null)
+ {
+ tmpEngine.init(props);
+ }
+ else
+ {
+ tmpEngine.init();
+ }
+ }
+ catch (RuntimeException rexp)
+ {
+ throw rexp;
+ }
+ catch (Exception exp)
+ {
+ throw new RuntimeException(exp);
+ }
+ velocityEngine = tmpEngine;
+ }
+ }
+ }
+
+ protected static VelocityContext getVelocityContext(ScriptContext ctx)
+ {
+ ctx.setAttribute("context", ctx, ScriptContext.ENGINE_SCOPE);
+ Bindings globalScope = ctx.getBindings(ScriptContext.GLOBAL_SCOPE);
+ Bindings engineScope = ctx.getBindings(ScriptContext.ENGINE_SCOPE);
+ if (globalScope != null)
+ {
+ return new VelocityContext(engineScope, new VelocityContext(globalScope));
+ }
+ else
+ {
+ return new VelocityContext(engineScope);
+ }
+ }
+
+ protected static String getFilename(ScriptContext ctx)
+ {
+ Object fileName = ctx.getAttribute(ScriptEngine.FILENAME);
+ return fileName != null? fileName.toString() : "<unknown>";
+ }
+
+ protected static Properties getVelocityProperties(ScriptContext ctx)
+ {
+ try
+ {
+ Object props = ctx.getAttribute(VELOCITY_PROPERTIES_KEY);
+ if (props instanceof Properties)
+ {
+ return (Properties) props;
+ }
+ else
+ {
+ String propsName = System.getProperty(VELOCITY_PROPERTIES_KEY);
+ if (propsName != null)
+ {
+ File propsFile = new File(propsName);
+ if (propsFile.exists() && propsFile.canRead())
+ {
+ Properties p = new Properties();
+ p.load(new FileInputStream(propsFile));
+ return p;
+ }
+ }
+ }
+ }
+ catch (Exception exp)
+ {
+ System.err.println(exp);
+ }
+ return null;
+ }
+
+ /**
+ * Compile a template
+ * @param script template source
+ * @return compiled template
+ * @throws ScriptException
+ */
+ @Override
+ public CompiledScript compile(String script) throws ScriptException
+ {
+ return compile(new StringReader(script));
+ }
+
+ /**
+ * Compile a template
+ * @param script template source
+ * @return compiled template
+ * @throws ScriptException
+ */
+ @Override
+ public CompiledScript compile(Reader script) throws ScriptException
+ {
+ initVelocityEngine(null);
+ ResourceLoader resourceLoader = new SingleResourceReader(script);
+ Template template = new Template();
+ template.setRuntimeServices(velocityEngine);
+ template.setResourceLoader(resourceLoader);
+ try
+ {
+ template.process();
+ }
+ catch(Exception e)
+ {
+ // CB TODO - exception may have line/col informations, that ScriptException can exploit
+ throw new ScriptException(e);
+ }
+ return new VelocityCompiledScript(this, template);
+ }
+
+ // a dummy resource reader class, serving a single resource given by the provided resource reader
+ protected static class SingleResourceReader extends StringResourceLoader
+ {
+ private Reader reader;
+
+ public SingleResourceReader(Reader r)
+ {
+ reader = r;
+ }
+
+ @Override
+ public Reader getResourceReader(String source, String encoding) throws ResourceNotFoundException
+ {
+ return reader;
+ }
+ }
+}
diff --git a/velocity-engine-scripting/src/main/java/org/apache/velocity/script/VelocityScriptEngineFactory.java b/velocity-engine-scripting/src/main/java/org/apache/velocity/script/VelocityScriptEngineFactory.java
new file mode 100644
index 00000000..7dd77fc1
--- /dev/null
+++ b/velocity-engine-scripting/src/main/java/org/apache/velocity/script/VelocityScriptEngineFactory.java
@@ -0,0 +1,231 @@
+package org.apache.velocity.script;
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met: Redistributions of source code
+ * must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution. Neither the name of the Sun Microsystems nor the names of
+ * is contributors may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Factory class for the Velocity scripting interface. Please refer to the
+ * javax.script.ScriptEngineFactory documentation for details.
+ *
+ * @author A. Sundararajan
+ * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ * @version $Id: VelocityScriptEngineFactory.java$
+ */
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineFactory;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.velocity.runtime.VelocityEngineVersion;
+
+public class VelocityScriptEngineFactory implements ScriptEngineFactory
+{
+
+ private static final String VELOCITY_NAME = "Velocity";
+ private static final String VELOCITY_VERSION = VelocityEngineVersion.VERSION;
+ private static final String VELOCITY_LANGUAGE = "VTL";
+
+ private static List<String> names;
+ private static List<String> extensions;
+ private static List<String> mimeTypes;
+
+ private static Properties parameters;
+
+ static
+ {
+ names = new ArrayList<>();
+ names.add("velocity");
+ names.add("Velocity");
+ names = Collections.unmodifiableList(names);
+ extensions = new ArrayList<>();
+ extensions.add("vm");
+ extensions.add("vtl");
+ extensions.add("vhtml");
+ extensions = Collections.unmodifiableList(extensions);
+ mimeTypes = new ArrayList<>();
+ mimeTypes.add("text/x-velocity");
+ mimeTypes = Collections.unmodifiableList(mimeTypes);
+ parameters = new Properties();
+ parameters.put(ScriptEngine.NAME, VELOCITY_NAME);
+ parameters.put(ScriptEngine.ENGINE_VERSION, VELOCITY_VERSION);
+ parameters.put(ScriptEngine.ENGINE, VELOCITY_NAME);
+ parameters.put(ScriptEngine.LANGUAGE, VELOCITY_LANGUAGE);
+ parameters.put(ScriptEngine.LANGUAGE_VERSION, VELOCITY_VERSION);
+ parameters.put("THREADING", "MULTITHREADED");
+ }
+
+ /**
+ * get engine name
+ * @return engine name, aka "Velocity"
+ */
+ @Override
+ public String getEngineName()
+ {
+ return VELOCITY_NAME;
+ }
+
+ /**
+ * get engine version
+ * @return engine version string
+ */
+ @Override
+ public String getEngineVersion()
+ {
+ return VELOCITY_VERSION;
+ }
+
+ /**
+ * get the list of file extensions handled by Velocity: vm, vtl, vhtml
+ * @return extensions list
+ */
+ @Override
+ public List<String> getExtensions()
+ {
+ return extensions;
+ }
+
+ /**
+ * get language name
+ * @return language name, aka "VTL"
+ */
+ @Override
+ public String getLanguageName()
+ {
+ return VELOCITY_NAME;
+ }
+
+ /**
+ * get language version (same as engine version)
+ * @return language version string
+ */
+ @Override
+ public String getLanguageVersion()
+ {
+ return VELOCITY_VERSION;
+ }
+
+ /**
+ * get Velocity syntax for calling method 'm' on object 'obj' with provided arguments
+ * @param obj
+ * @param m
+ * @param args
+ * @return VTL call ${obj.m(args...)}
+ */
+ @Override
+ public String getMethodCallSyntax(String obj, String m, String... args)
+ {
+ StringBuilder buf = new StringBuilder();
+ buf.append("${");
+ buf.append(obj);
+ buf.append(".");
+ buf.append(m);
+ buf.append("(");
+ if (args.length != 0)
+ {
+ int i = 0;
+ for (; i < args.length - 1; i++)
+ {
+ buf.append("$").append(args[i]);
+ buf.append(", ");
+ }
+ buf.append("$").append(args[i]);
+ }
+ buf.append(")}");
+ return buf.toString();
+ }
+
+ /**
+ * get the list of Velocity mime types
+ * @return singleton { 'text/x-velocity' }
+ */
+ @Override
+ public List<String> getMimeTypes()
+ {
+ return mimeTypes;
+ }
+
+ /**
+ * get the list of names
+ * @return { 'velocity', 'Velocity' }
+ */
+ @Override
+ public List<String> getNames()
+ {
+ return names;
+ }
+
+ /**
+ * get VTL expression used to display specified string
+ * @param toDisplay
+ * @return escaped string #[[toDisplay]]#
+ */
+ @Override
+ public String getOutputStatement(String toDisplay)
+ {
+ StringBuilder buf = new StringBuilder();
+ buf.append("#[[").append(toDisplay).append("]]#");
+ return buf.toString();
+ }
+
+ /**
+ * get engine parameter for provided key
+ * @param key
+ * @return found parameter, or null
+ */
+ @Override
+ public String getParameter(String key)
+ {
+ return parameters.getProperty(key);
+ }
+
+ /**
+ * get whole VTL program given VTL lines
+ * @param statements VTL lines
+ * @return lines concatenated with carriage returns
+ */
+ @Override
+ public String getProgram(String... statements)
+ {
+ StringBuilder buf = new StringBuilder();
+ for (String statement : statements)
+ {
+ buf.append(statement);
+ buf.append(System.lineSeparator());
+ }
+ return buf.toString();
+ }
+
+ /**
+ * get a Velocity script engine
+ * @return a new Velocity script engine
+ */
+ @Override
+ public ScriptEngine getScriptEngine()
+ {
+ return new VelocityScriptEngine(this);
+ }
+}
diff --git a/velocity-engine-scripting/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory b/velocity-engine-scripting/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory
new file mode 100644
index 00000000..0a4afb48
--- /dev/null
+++ b/velocity-engine-scripting/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory
@@ -0,0 +1 @@
+org.apache.velocity.script.VelocityScriptEngineFactory
diff --git a/velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/AbstractScriptTest.java b/velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/AbstractScriptTest.java
new file mode 100644
index 00000000..b702df78
--- /dev/null
+++ b/velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/AbstractScriptTest.java
@@ -0,0 +1,31 @@
+package org.apache.velocity.script.test;
+
+
+import junit.framework.TestCase;
+import org.apache.velocity.script.VelocityScriptEngineFactory;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineFactory;
+import javax.script.ScriptEngineManager;
+
+public abstract class AbstractScriptTest extends TestCase {
+ protected ScriptEngine engine;
+ protected ScriptEngineFactory engineFactory;
+ protected ScriptEngineManager manager;
+
+ @Override
+ public void setUp() {
+ manager = new ScriptEngineManager();
+ }
+
+ public void setupEngine(ScriptEngineFactory scriptEngineFactory){
+ manager.registerEngineName("velocity", scriptEngineFactory);
+ engine = manager.getEngineByName("velocity");
+ }
+
+ public void setupWithDefaultFactory() {
+ manager.registerEngineName("velocity", new VelocityScriptEngineFactory());
+ engine = manager.getEngineByName("velocity");
+ engineFactory = engine.getFactory();
+ }
+}
diff --git a/velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/ScriptEngineTest.java b/velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/ScriptEngineTest.java
new file mode 100644
index 00000000..f7b9771f
--- /dev/null
+++ b/velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/ScriptEngineTest.java
@@ -0,0 +1,97 @@
+package org.apache.velocity.script.test;
+/*
+ * 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 org.apache.velocity.script.VelocityScriptEngine;
+
+import javax.script.Bindings;
+import javax.script.Compilable;
+import javax.script.CompiledScript;
+import javax.script.ScriptContext;
+import javax.script.ScriptException;
+import java.io.StringWriter;
+
+public class ScriptEngineTest extends AbstractScriptTest {
+
+ @Override
+ public void setUp() {
+ super.setUp();
+ super.setupWithDefaultFactory();
+ }
+
+ public void testBasicOpe() {
+
+ engine.put("name1", "value1");
+ Bindings engineScope = engine.getContext().getBindings(ScriptContext.ENGINE_SCOPE);
+ assertEquals("Engine#put should have same effect as context.put ", engineScope.get("name1"), "value1");
+
+ String val = engine.get("name1").toString();
+ assertEquals("Engine#get should have same effect as context.get ", engineScope.get("name1"), val);
+ }
+
+
+ public void testJSR223tException(){
+ try {
+ engine.get("");
+ fail("Cannot pass empty name");
+ } catch (IllegalArgumentException n) {
+ //Success
+ }
+
+ try {
+ engine.setContext(null);
+ fail("Cannot pass null to context");
+ } catch (NullPointerException n) {
+ //Success
+ }
+
+ }
+
+ public void testEngineEvals() throws ScriptException {
+ String path = System.getProperty("test.resources.dir");
+ engine.getContext().setWriter(new StringWriter());
+ engine.getContext().setAttribute(VelocityScriptEngine.VELOCITY_PROPERTIES_KEY, path + "/test-classes/velocity.properties", ScriptContext.ENGINE_SCOPE);
+ String script = "<html><body>#set( $foo = 'Velocity' )Hello $foo World!</body><html>";
+ Object result = engine.eval(script);
+ assertEquals(result.toString(), "<html><body>Hello Velocity World!</body><html>");
+ }
+
+ public void testCompilable() throws ScriptException
+ {
+ String path = System.getProperty("test.resources.dir");
+ engine.getContext().setWriter(new StringWriter());
+ engine.getContext().setAttribute(VelocityScriptEngine.VELOCITY_PROPERTIES_KEY, path + "/test-classes/velocity.properties", ScriptContext.ENGINE_SCOPE);
+ String script = "$foo";
+ engine.put("foo", "bar");
+ CompiledScript compiled = ((Compilable)engine).compile(script);
+ Object result = compiled.eval();
+ assertEquals(result.toString(), "bar");
+ }
+
+ public void testContext() throws ScriptException
+ {
+ String path = System.getProperty("test.resources.dir");
+ engine.getContext().setWriter(new StringWriter());
+ engine.getContext().setAttribute(VelocityScriptEngine.VELOCITY_PROPERTIES_KEY, path + "/test-classes/velocity.properties", ScriptContext.ENGINE_SCOPE);
+ String script = "$context.class.name $context.writer.class.name $context.reader.class.name $context.errorWriter.class.name";
+ String result = engine.eval(script).toString();
+ assertEquals("javax.script.SimpleScriptContext java.io.StringWriter java.io.InputStreamReader java.io.PrintWriter", result);
+ }
+
+}
diff --git a/velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/VelocityScriptContextTest.java b/velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/VelocityScriptContextTest.java
new file mode 100644
index 00000000..8031cd0e
--- /dev/null
+++ b/velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/VelocityScriptContextTest.java
@@ -0,0 +1,159 @@
+package org.apache.velocity.script.test;
+
+/*
+* 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 javax.script.Bindings;
+import javax.script.ScriptContext;
+import javax.script.SimpleBindings;
+import java.util.List;
+
+
+public class VelocityScriptContextTest extends AbstractScriptTest {
+
+ @Override
+ public void setUp() {
+ super.setUp();
+ super.setupWithDefaultFactory();
+ }
+
+ public void testInitialScopes() {
+ List<Integer> defaultScopes = engine.getContext().getScopes();
+ assertEquals(defaultScopes.size(), 2);
+ assertTrue(defaultScopes.contains(ScriptContext.ENGINE_SCOPE));
+ assertTrue(defaultScopes.contains(ScriptContext.GLOBAL_SCOPE));
+ }
+
+ public void testScopes() {
+
+ Bindings velocityBindings = new SimpleBindings();
+ engine.getContext().setBindings(velocityBindings, ScriptContext.ENGINE_SCOPE);
+ assertNotNull(engine.getBindings(ScriptContext.ENGINE_SCOPE));
+ assertNotNull("Engines Registered through manager sets the global scope", engine.getBindings(ScriptContext.GLOBAL_SCOPE));
+ }
+
+ public void testEngineScopeAttributes() {
+
+ ScriptContext context = engine.getContext();
+ context.setAttribute("engine-prop1", "engine-value1", ScriptContext.ENGINE_SCOPE);
+
+ assertEquals(context.getAttribute("engine-prop1", ScriptContext.ENGINE_SCOPE), "engine-value1");
+ assertNull(context.getAttribute("engine-prop1", ScriptContext.GLOBAL_SCOPE));
+
+ context.removeAttribute("engine-prop1", ScriptContext.ENGINE_SCOPE);
+ assertNull(context.getAttribute("engine-prop1", ScriptContext.ENGINE_SCOPE));
+
+
+ }
+
+ public void testGlobalScopeAttributes() {
+
+ ScriptContext context = engine.getContext();
+ context.setAttribute("global-prop1", "global-value1", ScriptContext.GLOBAL_SCOPE);
+
+ assertEquals(context.getAttribute("global-prop1", ScriptContext.GLOBAL_SCOPE), "global-value1");
+ assertNull(context.getAttribute("global-prop1", ScriptContext.ENGINE_SCOPE));
+
+ context.removeAttribute("global-prop1", ScriptContext.GLOBAL_SCOPE);
+ assertNull(context.getAttribute("global-prop1", ScriptContext.GLOBAL_SCOPE));
+
+ }
+
+ public void testJSRExceptions() {
+
+ ScriptContext context = engine.getContext();
+ context.setAttribute("global-prop", "global-value", ScriptContext.GLOBAL_SCOPE);
+ int invalidScope = 99;
+
+ try {
+ context.setBindings(null, ScriptContext.ENGINE_SCOPE);
+ fail("Cannot pass null binding for engine scope");
+ } catch (NullPointerException n) {
+ //Success
+ }
+
+ try {
+ context.setBindings(new SimpleBindings(), invalidScope);
+ fail("Cannot pass invalid scope");
+ } catch (IllegalArgumentException n) {
+ //Success
+ }
+ try {
+ context.getBindings(invalidScope);
+ fail("Cannot pass invalid scope");
+ } catch (IllegalArgumentException n) {
+ //Success
+ }
+
+
+ try {
+ context.setAttribute(null, "value", ScriptContext.ENGINE_SCOPE);
+
+ fail("Name cannot be null");
+ } catch (NullPointerException n) {
+ //Success
+ }
+ try {
+ context.setAttribute("name1", "value", invalidScope);
+
+ fail("Cannot pass invalid scope");
+ } catch (IllegalArgumentException n) {
+ //Success
+ }
+
+
+
+ try {
+ context.getAttribute(null, ScriptContext.ENGINE_SCOPE);
+
+ fail("Name cannot be null");
+ } catch (NullPointerException n) {
+ //Success
+ }
+ try {
+ context.getAttribute("name1", invalidScope);
+
+ fail("Cannot pass invalid scope");
+ } catch (IllegalArgumentException n) {
+ //Success
+ }
+
+
+ try {
+ context.removeAttribute(null, ScriptContext.ENGINE_SCOPE);
+
+ fail("Name cannot be null");
+ } catch (NullPointerException n) {
+ //Success
+ }
+ try {
+ context.removeAttribute("name1", invalidScope);
+
+ fail("Cannot pass invalid scope");
+ } catch (IllegalArgumentException n) {
+ //Success
+ }
+
+
+ context.setAttribute("prop2","engine-value2",ScriptContext.ENGINE_SCOPE);
+ context.setAttribute("prop2","global-value2",ScriptContext.GLOBAL_SCOPE);
+ assertEquals("Should return lowest scope value binded value",context.getAttribute("prop2"),"engine-value2");
+
+ }
+}
diff --git a/velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/resources/eventtool.vm b/velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/resources/eventtool.vm
new file mode 100644
index 00000000..68e33fe8
--- /dev/null
+++ b/velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/resources/eventtool.vm
@@ -0,0 +1,5 @@
+$event;
+
+Event Created by $event.getName()
+Event Created on $event.getDate()
+Event ID is $event.getID()
diff --git a/velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/tools/CustomEvent.java b/velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/tools/CustomEvent.java
new file mode 100644
index 00000000..b76970ee
--- /dev/null
+++ b/velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/tools/CustomEvent.java
@@ -0,0 +1,50 @@
+package org.apache.velocity.script.test.tools;
+/*
+ * 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.util.Date;
+
+public class CustomEvent {
+
+ String name = "";
+ Date date;
+
+ public CustomEvent(String name) {
+ this.name = name;
+ this.date = new Date();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public long getID(){
+ return date.getTime();
+ }
+
+ @Override
+ public String toString(){
+ return "This is a test event template: created by " + name + " on " + date.toString();
+ }
+
+} \ No newline at end of file
diff --git a/velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/tools/EventToolTest.java b/velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/tools/EventToolTest.java
new file mode 100644
index 00000000..eef11eb6
--- /dev/null
+++ b/velocity-engine-scripting/src/test/java/org/apache/velocity/script/test/tools/EventToolTest.java
@@ -0,0 +1,61 @@
+package org.apache.velocity.script.test.tools;
+/*
+ * 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 org.apache.velocity.script.VelocityScriptEngine;
+import org.apache.velocity.script.test.AbstractScriptTest;
+
+import javax.script.ScriptContext;
+import javax.script.ScriptException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Properties;
+
+public class EventToolTest extends AbstractScriptTest {
+
+ @Override
+ public void setUp() {
+ super.setUp();
+ super.setupWithDefaultFactory();
+ }
+
+ public void testHelloWorldTool() throws ScriptException {
+ ScriptContext context = engine.getContext();
+ Properties properties = new Properties();
+ properties.put("resource.loader", "class");
+ properties.put("class.resource.loader.description", "Template Class Loader");
+ properties.put("class.resource.loader.class",
+ "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
+ CustomEvent event = new CustomEvent("MyEvent");
+ context.getBindings(ScriptContext.ENGINE_SCOPE).put("event", event);
+ context.setAttribute(VelocityScriptEngine.VELOCITY_PROPERTIES_KEY, properties, ScriptContext.ENGINE_SCOPE);
+ context.setAttribute(VelocityScriptEngine.FILENAME, "eventtool.vm", ScriptContext.ENGINE_SCOPE);
+ Writer writer = new StringWriter();
+ context.setWriter(writer);
+ engine.eval("$event;\n" +
+ "Event Created by $event.getName()\n" +
+ "Event Created on $event.getDate()\n" +
+ "Event ID is $event.getID()");
+ // check string start
+ String check = "This is a test event template: created by MyEvent on ";
+ assertEquals(writer.toString().substring(0, check.length()), check);
+ }
+
+
+}
diff --git a/velocity-engine-scripting/src/test/resources/velocity.properties b/velocity-engine-scripting/src/test/resources/velocity.properties
new file mode 100644
index 00000000..d4f683db
--- /dev/null
+++ b/velocity-engine-scripting/src/test/resources/velocity.properties
@@ -0,0 +1,17 @@
+# 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.
+runtime.log = velocity_example.log