1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package groovy.util;
35
36 import groovy.lang.Binding;
37 import groovy.lang.GroovyClassLoader;
38 import groovy.lang.Script;
39
40 import java.io.BufferedReader;
41 import java.io.File;
42 import java.io.IOException;
43 import java.io.InputStreamReader;
44 import java.net.MalformedURLException;
45 import java.net.URL;
46 import java.net.URLConnection;
47 import java.security.AccessController;
48 import java.security.PrivilegedAction;
49 import java.util.Collections;
50 import java.util.HashMap;
51 import java.util.Iterator;
52 import java.util.Map;
53
54 import org.codehaus.groovy.control.CompilationFailedException;
55 import org.codehaus.groovy.runtime.InvokerHelper;
56
57 /***
58 * @author sam
59 *
60 * To change the template for this generated type comment go to Window -
61 * Preferences - Java - Code Generation - Code and Comments
62 */
63 public class GroovyScriptEngine implements ResourceConnector {
64
65 /***
66 * Simple testing harness for the GSE. Enter script roots as arguments and
67 * then input script names to run them.
68 *
69 * @param args
70 * @throws Exception
71 */
72 public static void main(String[] args) throws Exception {
73 URL[] roots = new URL[args.length];
74 for (int i = 0; i < roots.length; i++) {
75 roots[i] = new File(args[i]).toURL();
76 }
77 GroovyScriptEngine gse = new GroovyScriptEngine(roots);
78 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
79 String line;
80 while (true) {
81 System.out.print("groovy> ");
82 if ((line = br.readLine()) == null || line.equals("quit"))
83 break;
84 try {
85 System.out.println(gse.run(line, new Binding()));
86 } catch (Exception e) {
87 e.printStackTrace();
88 }
89 }
90 }
91
92 private URL[] roots;
93 private Map scriptCache = Collections.synchronizedMap(new HashMap());
94 private ResourceConnector rc;
95
96 private static class ScriptCacheEntry {
97 private Class scriptClass;
98 private long lastModified;
99 private Map dependencies = new HashMap();
100 }
101
102 public URLConnection getResourceConnection(String resourceName) throws ResourceException {
103
104 URLConnection groovyScriptConn = null;
105
106 ResourceException se = null;
107 for (int i = 0; i < roots.length; i++) {
108 URL scriptURL = null;
109 try {
110 scriptURL = new URL(roots[i], resourceName);
111 groovyScriptConn = scriptURL.openConnection();
112 } catch (MalformedURLException e) {
113 String message = "Malformed URL: " + roots[i] + ", " + resourceName;
114 if (se == null) {
115 se = new ResourceException(message);
116 } else {
117 se = new ResourceException(message, se);
118 }
119 } catch (IOException e1) {
120 String message = "Cannot open URL: " + scriptURL;
121 if (se == null) {
122 se = new ResourceException(message);
123 } else {
124 se = new ResourceException(message, se);
125 }
126 }
127
128 }
129
130
131
132 if (groovyScriptConn == null) {
133 throw se;
134 }
135
136 return groovyScriptConn;
137 }
138
139 /***
140 * The groovy script engine will run groovy scripts and reload them and
141 * their dependencies when they are modified. This is useful for embedding
142 * groovy in other containers like games and application servers.
143 *
144 * @param roots This an array of URLs where Groovy scripts will be stored. They should
145 * be layed out using their package structure like Java classes
146 */
147 public GroovyScriptEngine(URL[] roots) {
148 this.roots = roots;
149 this.rc = this;
150 }
151
152 public GroovyScriptEngine(String[] args) throws IOException {
153 roots = new URL[args.length];
154 for (int i = 0; i < roots.length; i++) {
155 roots[i] = new File(args[i]).toURL();
156 }
157 this.rc = this;
158 }
159
160 public GroovyScriptEngine(String arg) throws IOException {
161 roots = new URL[1];
162 roots[0] = new File(arg).toURL();
163 this.rc = this;
164 }
165
166 public GroovyScriptEngine(ResourceConnector rc) {
167 this.rc = rc;
168 }
169
170 public String run(String script, String argument) throws ResourceException, ScriptException {
171 Binding binding = new Binding();
172 binding.setVariable("arg", argument);
173 Object result = run(script, binding);
174 return result == null ? "" : result.toString();
175 }
176
177 public Object run(String script, Binding binding) throws ResourceException, ScriptException {
178
179 ScriptCacheEntry entry;
180
181 script = script.intern();
182 synchronized (script) {
183
184 URLConnection groovyScriptConn = rc.getResourceConnection(script);
185
186
187 long lastModified = groovyScriptConn.getLastModified();
188
189 entry = (ScriptCacheEntry) scriptCache.get(script);
190
191 boolean dependencyOutOfDate = false;
192 if (entry != null) {
193 for (Iterator i = entry.dependencies.keySet().iterator(); i.hasNext();) {
194 URLConnection urlc = null;
195 URL url = (URL) i.next();
196 try {
197 urlc = url.openConnection();
198 urlc.setDoInput(false);
199 urlc.setDoOutput(false);
200 long dependentLastModified = urlc.getLastModified();
201 if (dependentLastModified > ((Long) entry.dependencies.get(url)).longValue()) {
202 dependencyOutOfDate = true;
203 break;
204 }
205 } catch (IOException ioe) {
206 dependencyOutOfDate = true;
207 break;
208 }
209 }
210 }
211
212 if (entry == null || entry.lastModified < lastModified || dependencyOutOfDate) {
213
214 entry = new ScriptCacheEntry();
215
216
217 final ScriptCacheEntry finalEntry = entry;
218
219
220 GroovyClassLoader groovyLoader =
221 (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
222 public Object run() {
223 return new GroovyClassLoader(getClass().getClassLoader()) {
224 protected Class findClass(String className) throws ClassNotFoundException {
225 String filename = className.replace('.', File.separatorChar) + ".groovy";
226 URLConnection dependentScriptConn = null;
227 try {
228 dependentScriptConn = rc.getResourceConnection(filename);
229 finalEntry.dependencies.put(
230 dependentScriptConn.getURL(),
231 new Long(dependentScriptConn.getLastModified()));
232 } catch (ResourceException e1) {
233 throw new ClassNotFoundException("Could not read " + className + ": " + e1);
234 }
235 try {
236 return parseClass(dependentScriptConn.getInputStream(), filename);
237 } catch (CompilationFailedException e2) {
238 throw new ClassNotFoundException("Syntax error in " + className + ": " + e2);
239 } catch (IOException e2) {
240 throw new ClassNotFoundException("Problem reading " + className + ": " + e2);
241 }
242 }
243 };
244 }
245 });
246
247 try {
248 entry.scriptClass = groovyLoader.parseClass(groovyScriptConn.getInputStream(), script);
249 } catch (Exception e) {
250 throw new ScriptException("Could not parse script: " + script, e);
251 }
252 entry.lastModified = lastModified;
253 scriptCache.put(script, entry);
254 }
255 }
256 Script scriptObject = InvokerHelper.createScript(entry.scriptClass, binding);
257 return scriptObject.run();
258 }
259 }