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 public GroovyScriptEngine(URL[] roots) {
145 this.roots = roots;
146 this.rc = this;
147 }
148
149 public GroovyScriptEngine(String[] args) throws IOException {
150 URL[] roots = new URL[args.length];
151 for (int i = 0; i < roots.length; i++) {
152 roots[i] = new File(args[i]).toURL();
153 }
154 this.rc = this;
155 }
156
157 public GroovyScriptEngine(String arg) throws IOException {
158 this.roots = new URL[1];
159 roots[0] = new File(arg).toURL();
160 this.rc = this;
161 }
162
163 public GroovyScriptEngine(ResourceConnector rc) {
164 this.rc = rc;
165 }
166
167 public String run(String script, String argument) throws ResourceException, ScriptException {
168 Binding binding = new Binding();
169 binding.setVariable("arg", argument);
170 Object result = run(script, binding);
171 return result == null ? "" : result.toString();
172 }
173
174 public Object run(String script, Binding binding) throws ResourceException, ScriptException {
175
176 ScriptCacheEntry entry;
177
178 script = script.intern();
179 synchronized (script) {
180
181 URLConnection groovyScriptConn = rc.getResourceConnection(script);
182
183
184 long lastModified = groovyScriptConn.getLastModified();
185
186 entry = (ScriptCacheEntry) scriptCache.get(script);
187
188 boolean dependencyOutOfDate = false;
189 if (entry != null) {
190 for (Iterator i = entry.dependencies.keySet().iterator(); i.hasNext();) {
191 URLConnection urlc = null;
192 URL url = (URL) i.next();
193 try {
194 urlc = url.openConnection();
195 urlc.setDoInput(false);
196 urlc.setDoOutput(false);
197 long dependentLastModified = urlc.getLastModified();
198 if (dependentLastModified > ((Long) entry.dependencies.get(url)).longValue()) {
199 dependencyOutOfDate = true;
200 break;
201 }
202 } catch (IOException ioe) {
203 dependencyOutOfDate = true;
204 break;
205 }
206 }
207 }
208
209 if (entry == null || entry.lastModified < lastModified || dependencyOutOfDate) {
210
211 entry = new ScriptCacheEntry();
212
213
214 final ScriptCacheEntry finalEntry = entry;
215
216
217 GroovyClassLoader groovyLoader =
218 (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
219 public Object run() {
220 return new GroovyClassLoader(getClass().getClassLoader()) {
221 protected Class findClass(String className) throws ClassNotFoundException {
222 String filename = className.replace('.', File.separatorChar) + ".groovy";
223 URLConnection dependentScriptConn = null;
224 try {
225 dependentScriptConn = rc.getResourceConnection(filename);
226 finalEntry.dependencies.put(
227 dependentScriptConn.getURL(),
228 new Long(dependentScriptConn.getLastModified()));
229 } catch (ResourceException e1) {
230 throw new ClassNotFoundException("Could not read " + className + ": " + e1);
231 }
232 try {
233 return parseClass(dependentScriptConn.getInputStream(), filename);
234 } catch (CompilationFailedException e2) {
235 throw new ClassNotFoundException("Syntax error in " + className + ": " + e2);
236 } catch (IOException e2) {
237 throw new ClassNotFoundException("Problem reading " + className + ": " + e2);
238 }
239 }
240 };
241 }
242 });
243
244 try {
245 entry.scriptClass = groovyLoader.parseClass(groovyScriptConn.getInputStream(), script);
246 } catch (Exception e) {
247 throw new ScriptException("Could not parse script: " + script, e);
248 }
249 entry.lastModified = lastModified;
250 scriptCache.put(script, entry);
251 }
252 }
253 Script scriptObject = InvokerHelper.createScript(entry.scriptClass, binding);
254 return scriptObject.run();
255 }
256 }