1 package org.apache.velocity.script; 2 3 /* 4 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 5 * Use is subject to license terms. 6 * 7 * Redistribution and use in source and binary forms, with or without modification, are 8 * permitted provided that the following conditions are met: Redistributions of source code 9 * must retain the above copyright notice, this list of conditions and the following disclaimer. 10 * Redistributions in binary form must reproduce the above copyright notice, this list of 11 * conditions and the following disclaimer in the documentation and/or other materials 12 * provided with the distribution. Neither the name of the Sun Microsystems nor the names of 13 * is contributors may be used to endorse or promote products derived from this software 14 * without specific prior written permission. 15 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS 17 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 18 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 19 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 22 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 23 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 * POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 /* 28 * Main class for the Velocity script engine. 29 * All the variants of the eval() methods return the writer. The default writer is a PrintWriter towards System.out. 30 * To specify a specific writer, use getContext().setWriter(writer). To get a resulting string, pass a StringWriter. 31 * 32 * You can specify a pathname towards a Velocity properties file using the "org.apache.velocity.script.properties" key, 33 * either as a ScriptContext attribute, or as a System property. 34 * 35 * Example use: 36 * <pre> 37 * ScriptEngine vel = new VelocityScriptEngine(); 38 * vel.getContext().setAttribute(VelocityScriptEngine.VELOCITY_PROPERTIES_KEY, "path/to/velocity.properties"); 39 * vel.getContext().setWriter(new StringWriter()); 40 * vel.put("foo","World"); 41 * Object result = vel.eval("Hello $foo !"); 42 * String helloWorld = result.toString() 43 * </pre> 44 * 45 * Please refer to the javax.script.ScriptEngine documentation for additional details. 46 * 47 * @author A. Sundararajan 48 * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a> 49 * @version $Id: VelocityScriptEngine.java$ 50 */ 51 52 import org.apache.velocity.Template; 53 import org.apache.velocity.VelocityContext; 54 import org.apache.velocity.exception.ResourceNotFoundException; 55 import org.apache.velocity.runtime.RuntimeInstance; 56 import org.apache.velocity.runtime.resource.loader.ResourceLoader; 57 import org.apache.velocity.runtime.resource.loader.StringResourceLoader; 58 59 import javax.script.AbstractScriptEngine; 60 import javax.script.Bindings; 61 import javax.script.Compilable; 62 import javax.script.CompiledScript; 63 import javax.script.ScriptContext; 64 import javax.script.ScriptEngine; 65 import javax.script.ScriptEngineFactory; 66 import javax.script.ScriptException; 67 import javax.script.SimpleBindings; 68 import java.io.File; 69 import java.io.FileInputStream; 70 import java.io.Reader; 71 import java.io.StringReader; 72 import java.io.StringWriter; 73 import java.io.Writer; 74 import java.util.Properties; 75 76 public class VelocityScriptEngine extends AbstractScriptEngine implements Compilable 77 { 78 /** 79 * Key used to provide this engine with the pathname of the Velocity properties file. 80 * This key is first searched in the ScriptContext attributes, then as a System property 81 */ 82 public static final String VELOCITY_PROPERTIES_KEY = "org.apache.velocity.script.properties"; 83 84 // my factory, may be null 85 private volatile ScriptEngineFactory factory; 86 private volatile RuntimeInstance velocityEngine; 87 88 /** 89 * constructs a new Velocity script engine, linked to the given factory 90 * @param factory 91 */ VelocityScriptEngine(ScriptEngineFactory factory)92 public VelocityScriptEngine(ScriptEngineFactory factory) 93 { 94 this.factory = factory; 95 } 96 97 /** 98 * constructs a new standalone Velocity script engine 99 */ VelocityScriptEngine()100 public VelocityScriptEngine() 101 { 102 this(null); 103 } 104 105 /** 106 * get the internal Velocity RuntimeInstance 107 * @return the internal Velocity RuntimeInstance 108 */ getVelocityEngine()109 protected RuntimeInstance getVelocityEngine() 110 { 111 return velocityEngine; 112 } 113 114 /** 115 * Evaluate the given script. 116 * If you wish to get a resulting string, call getContext().setWriter(new StringWriter()) then call toString() 117 * on the returned writer. 118 * @param str script source 119 * @param ctx script context 120 * @return the script context writer (by default a PrintWriter towards System.out) 121 * @throws ScriptException 122 */ 123 @Override eval(String str, ScriptContext ctx)124 public Object eval(String str, ScriptContext ctx) 125 throws ScriptException 126 { 127 return eval(new StringReader(str), ctx); 128 } 129 130 /** 131 * Evaluate the given script. 132 * If you wish to get a resulting string, call getContext().setWriter(new StringWriter()) then call toString() 133 * on the returned writer. 134 * @param reader script source reader 135 * @param ctx script context 136 * @return the script context writer (by default a PrintWriter towards System.out) 137 * @throws ScriptException 138 */ 139 @Override eval(Reader reader, ScriptContext ctx)140 public Object eval(Reader reader, ScriptContext ctx) 141 throws ScriptException 142 { 143 initVelocityEngine(ctx); 144 String fileName = getFilename(ctx); 145 VelocityContext vctx = getVelocityContext(ctx); 146 Writer out = ctx.getWriter(); 147 if (out == null) 148 { 149 out = new StringWriter(); 150 ctx.setWriter(out); 151 } 152 try 153 { 154 velocityEngine.evaluate(vctx, out, fileName, reader); 155 out.flush(); 156 } 157 catch (Exception exp) 158 { 159 throw new ScriptException(exp); 160 } 161 return out; 162 } 163 164 /** 165 * get the factory used to create this script engine 166 * @return factory 167 */ 168 @Override getFactory()169 public ScriptEngineFactory getFactory() 170 { 171 if (factory == null) 172 { 173 synchronized (this) 174 { 175 if (factory == null) 176 { 177 factory = new VelocityScriptEngineFactory(); 178 } 179 } 180 } 181 return factory; 182 } 183 184 /** 185 * creates a new Bindings to be used with this script 186 * @return new bindings 187 */ 188 @Override createBindings()189 public Bindings createBindings() 190 { 191 return new SimpleBindings(); 192 } 193 initVelocityEngine(ScriptContext ctx)194 private void initVelocityEngine(ScriptContext ctx) 195 { 196 if (ctx == null) 197 { 198 ctx = getContext(); 199 } 200 if (velocityEngine == null) 201 { 202 synchronized (this) 203 { 204 if (velocityEngine != null) return; 205 206 Properties props = getVelocityProperties(ctx); 207 RuntimeInstance tmpEngine = new RuntimeInstance(); 208 try 209 { 210 if (props != null) 211 { 212 tmpEngine.init(props); 213 } 214 else 215 { 216 tmpEngine.init(); 217 } 218 } 219 catch (RuntimeException rexp) 220 { 221 throw rexp; 222 } 223 catch (Exception exp) 224 { 225 throw new RuntimeException(exp); 226 } 227 velocityEngine = tmpEngine; 228 } 229 } 230 } 231 getVelocityContext(ScriptContext ctx)232 protected static VelocityContext getVelocityContext(ScriptContext ctx) 233 { 234 ctx.setAttribute("context", ctx, ScriptContext.ENGINE_SCOPE); 235 Bindings globalScope = ctx.getBindings(ScriptContext.GLOBAL_SCOPE); 236 Bindings engineScope = ctx.getBindings(ScriptContext.ENGINE_SCOPE); 237 if (globalScope != null) 238 { 239 return new VelocityContext(engineScope, new VelocityContext(globalScope)); 240 } 241 else 242 { 243 return new VelocityContext(engineScope); 244 } 245 } 246 getFilename(ScriptContext ctx)247 protected static String getFilename(ScriptContext ctx) 248 { 249 Object fileName = ctx.getAttribute(ScriptEngine.FILENAME); 250 return fileName != null? fileName.toString() : "<unknown>"; 251 } 252 getVelocityProperties(ScriptContext ctx)253 protected static Properties getVelocityProperties(ScriptContext ctx) 254 { 255 try 256 { 257 Object props = ctx.getAttribute(VELOCITY_PROPERTIES_KEY); 258 if (props instanceof Properties) 259 { 260 return (Properties) props; 261 } 262 else 263 { 264 String propsName = System.getProperty(VELOCITY_PROPERTIES_KEY); 265 if (propsName != null) 266 { 267 File propsFile = new File(propsName); 268 if (propsFile.exists() && propsFile.canRead()) 269 { 270 Properties p = new Properties(); 271 p.load(new FileInputStream(propsFile)); 272 return p; 273 } 274 } 275 } 276 } 277 catch (Exception exp) 278 { 279 System.err.println(exp); 280 } 281 return null; 282 } 283 284 /** 285 * Compile a template 286 * @param script template source 287 * @return compiled template 288 * @throws ScriptException 289 */ 290 @Override compile(String script)291 public CompiledScript compile(String script) throws ScriptException 292 { 293 return compile(new StringReader(script)); 294 } 295 296 /** 297 * Compile a template 298 * @param script template source 299 * @return compiled template 300 * @throws ScriptException 301 */ 302 @Override compile(Reader script)303 public CompiledScript compile(Reader script) throws ScriptException 304 { 305 initVelocityEngine(null); 306 ResourceLoader resourceLoader = new SingleResourceReader(script); 307 Template template = new Template(); 308 template.setRuntimeServices(velocityEngine); 309 template.setResourceLoader(resourceLoader); 310 try 311 { 312 template.process(); 313 } 314 catch(Exception e) 315 { 316 // CB TODO - exception may have line/col informations, that ScriptException can exploit 317 throw new ScriptException(e); 318 } 319 return new VelocityCompiledScript(this, template); 320 } 321 322 // a dummy resource reader class, serving a single resource given by the provided resource reader 323 protected static class SingleResourceReader extends StringResourceLoader 324 { 325 private Reader reader; 326 SingleResourceReader(Reader r)327 public SingleResourceReader(Reader r) 328 { 329 reader = r; 330 } 331 332 @Override getResourceReader(String source, String encoding)333 public Reader getResourceReader(String source, String encoding) throws ResourceNotFoundException 334 { 335 return reader; 336 } 337 } 338 } 339