• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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