1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.scxml2.env.groovy;
18
19 import java.io.IOException;
20 import java.io.ObjectInputStream;
21 import java.io.Serializable;
22 import java.security.AccessController;
23 import java.security.PrivilegedAction;
24 import java.util.LinkedHashMap;
25
26 import org.codehaus.groovy.control.CompilerConfiguration;
27
28 import groovy.lang.GroovyClassLoader;
29 import groovy.lang.GroovyCodeSource;
30 import groovy.lang.GroovyRuntimeException;
31 import groovy.lang.Script;
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 public class GroovyExtendableScriptCache implements Serializable {
72
73 private static final long serialVersionUID = 1L;
74
75
76
77
78
79 public interface ParentClassLoaderFactory extends Serializable {
80 ClassLoader getClassLoader();
81 }
82
83
84
85
86
87 public interface CompilerConfigurationFactory extends Serializable {
88 CompilerConfiguration getCompilerConfiguration();
89 }
90
91 public interface ScriptPreProcessor extends Serializable {
92 String preProcess(String script);
93 }
94
95
96 public static final String DEFAULT_SCRIPT_CODE_BASE = "/groovy/scxml/script";
97
98
99 public static final ParentClassLoaderFactory DEFAULT_PARENT_CLASS_LOADER_FACTORY = new ParentClassLoaderFactory() {
100 public ClassLoader getClassLoader() {
101 return GroovyExtendableScriptCache.class.getClassLoader();
102 }
103 };
104
105
106 public static final CompilerConfigurationFactory DEFAULT_COMPILER_CONFIGURATION_FACTORY = new CompilerConfigurationFactory() {
107 public CompilerConfiguration getCompilerConfiguration() {
108 return new CompilerConfiguration();
109 }
110 };
111
112 protected static class ScriptCacheElement implements Serializable {
113 private static final long serialVersionUID = 1L;
114
115 protected final String baseClass;
116 protected final String scriptSource;
117 protected String scriptName;
118 protected transient Class<? extends Script> scriptClass;
119
120 public ScriptCacheElement(String baseClass, String scriptSource) {
121 this.baseClass = baseClass;
122 this.scriptSource = scriptSource;
123 }
124
125 public String getBaseClass() {
126 return baseClass;
127 }
128
129 public String getScriptSource() {
130 return scriptSource;
131 }
132
133 public String getScriptName() {
134 return scriptName;
135 }
136
137 public void setScriptName(String scriptName) {
138 this.scriptName = scriptName;
139 }
140
141 public Class<? extends Script> getScriptClass() {
142 return scriptClass;
143 }
144
145 public void setScriptClass(Class<? extends Script> scriptClass) {
146 this.scriptClass = scriptClass;
147 }
148
149 @Override
150 public boolean equals(final Object o) {
151 if (this == o) {
152 return true;
153 }
154 if (o == null || getClass() != o.getClass()) {
155 return false;
156 }
157
158 final ScriptCacheElement that = (ScriptCacheElement) o;
159
160 return !(baseClass != null ? !baseClass.equals(that.baseClass) : that.baseClass != null) &&
161 scriptSource.equals(that.scriptSource);
162
163 }
164
165 @Override
166 public int hashCode() {
167 int result = baseClass != null ? baseClass.hashCode() : 0;
168 result = 31 * result + scriptSource.hashCode();
169 return result;
170 }
171 }
172
173 private final LinkedHashMap<ScriptCacheElement, ScriptCacheElement> scriptCache = new LinkedHashMap<ScriptCacheElement, ScriptCacheElement>();
174
175 private String scriptCodeBase = DEFAULT_SCRIPT_CODE_BASE;
176 private String scriptBaseClass;
177 private ParentClassLoaderFactory parentClassLoaderFactory = DEFAULT_PARENT_CLASS_LOADER_FACTORY;
178 private CompilerConfigurationFactory compilerConfigurationFactory = DEFAULT_COMPILER_CONFIGURATION_FACTORY;
179 private ScriptPreProcessor scriptPreProcessor;
180
181
182 private transient GroovyClassLoader groovyClassLoader;
183 private transient CompilerConfiguration compilerConfiguration;
184
185 public GroovyExtendableScriptCache() {
186 }
187
188
189
190
191
192 private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException {
193 in.defaultReadObject();
194 ensureInitializedOrReloaded();
195 }
196
197 public ClassLoader getGroovyClassLoader() {
198 return groovyClassLoader;
199 }
200
201
202
203
204
205
206
207 public Script getScript(String scriptSource) {
208 return getScript(null, scriptSource);
209 }
210
211 public Script getScript(String scriptBaseClass, String scriptSource) {
212 Class<? extends Script> scriptClass;
213 synchronized (scriptCache) {
214 ensureInitializedOrReloaded();
215 ScriptCacheElement cacheKey = new ScriptCacheElement(scriptBaseClass, scriptSource);
216 ScriptCacheElement cacheElement = scriptCache.get(cacheKey);
217 if (cacheElement != null) {
218 scriptClass = cacheElement.getScriptClass();
219 }
220 else {
221 String scriptName = generatedScriptName(scriptSource, scriptCache.size());
222 scriptClass = compileScript(scriptBaseClass, scriptSource, scriptName);
223 cacheKey.setScriptName(scriptName);
224 cacheKey.setScriptClass(scriptClass);
225 scriptCache.put(cacheKey, cacheKey);
226 }
227 }
228 try {
229 return scriptClass.newInstance();
230 } catch (Exception e) {
231 throw new GroovyRuntimeException("Failed to create Script instance for class: "+ scriptClass + ". Reason: " + e, e);
232 }
233 }
234
235 protected void ensureInitializedOrReloaded() {
236 if (groovyClassLoader == null) {
237 compilerConfiguration = new CompilerConfiguration(getCompilerConfigurationFactory().getCompilerConfiguration());
238 if (getScriptBaseClass() != null) {
239 compilerConfiguration.setScriptBaseClass(getScriptBaseClass());
240 }
241
242 groovyClassLoader = AccessController.doPrivileged(new PrivilegedAction<GroovyClassLoader>() {
243 public GroovyClassLoader run() {
244 return new GroovyClassLoader(getParentClassLoaderFactory().getClassLoader(), compilerConfiguration);
245 }
246 });
247 if (!scriptCache.isEmpty()) {
248
249 for (ScriptCacheElement element : scriptCache.keySet()) {
250 element.setScriptClass(compileScript(element.getBaseClass(), element.getScriptSource(), element.getScriptName()));
251 }
252 }
253 }
254 }
255
256 @SuppressWarnings("unchecked")
257 protected Class<Script> compileScript(final String scriptBaseClass, String scriptSource, final String scriptName) {
258 final String script = preProcessScript(scriptSource);
259
260 GroovyCodeSource codeSource = AccessController.doPrivileged(new PrivilegedAction<GroovyCodeSource>() {
261 public GroovyCodeSource run() {
262 return new GroovyCodeSource(script, scriptName, getScriptCodeBase());
263 }
264 });
265
266 String currentScriptBaseClass = compilerConfiguration.getScriptBaseClass();
267 try {
268 if (scriptBaseClass != null) {
269 compilerConfiguration.setScriptBaseClass(scriptBaseClass);
270 }
271 return groovyClassLoader.parseClass(codeSource, false);
272 }
273 finally {
274 compilerConfiguration.setScriptBaseClass(currentScriptBaseClass);
275 }
276 }
277
278 protected String preProcessScript(String scriptSource) {
279 return getScriptPreProcessor() != null ? getScriptPreProcessor().preProcess(scriptSource) : scriptSource;
280 }
281
282 protected String generatedScriptName(String scriptSource, int seed) {
283 return "script"+seed+"_"+Math.abs(scriptSource.hashCode())+".groovy";
284 }
285
286
287 public String getScriptCodeBase() {
288 return scriptCodeBase;
289 }
290
291
292
293
294
295
296 @SuppressWarnings("unused")
297 public void setScriptCodeBase(String scriptCodeBase) {
298 if (scriptCodeBase != null && scriptCodeBase.length() > 0 && scriptCodeBase.charAt(0) == '/') {
299 this.scriptCodeBase = scriptCodeBase;
300 }
301 else {
302 this.scriptCodeBase = DEFAULT_SCRIPT_CODE_BASE;
303 }
304 }
305
306 public String getScriptBaseClass() {
307 return scriptBaseClass;
308 }
309
310 public void setScriptBaseClass(String scriptBaseClass) {
311 this.scriptBaseClass = scriptBaseClass;
312 }
313
314 public ParentClassLoaderFactory getParentClassLoaderFactory() {
315 return parentClassLoaderFactory;
316 }
317
318 @SuppressWarnings("unused")
319 public void setParentClassLoaderFactory(ParentClassLoaderFactory parentClassLoaderFactory) {
320 this.parentClassLoaderFactory = parentClassLoaderFactory != null ? parentClassLoaderFactory : DEFAULT_PARENT_CLASS_LOADER_FACTORY;
321 }
322
323 public CompilerConfigurationFactory getCompilerConfigurationFactory() {
324 return compilerConfigurationFactory;
325 }
326
327 @SuppressWarnings("unused")
328 public void setCompilerConfigurationFactory(CompilerConfigurationFactory compilerConfigurationFactory) {
329 this.compilerConfigurationFactory = compilerConfigurationFactory != null ? compilerConfigurationFactory : DEFAULT_COMPILER_CONFIGURATION_FACTORY;
330 }
331
332 public ScriptPreProcessor getScriptPreProcessor() {
333 return scriptPreProcessor;
334 }
335
336 public void setScriptPreProcessor(final ScriptPreProcessor scriptPreProcessor) {
337 this.scriptPreProcessor = scriptPreProcessor;
338 }
339
340 public boolean isEmpty() {
341 synchronized (scriptCache) {
342 return scriptCache.isEmpty();
343 }
344 }
345 public void clearCache() {
346 synchronized (scriptCache) {
347 scriptCache.clear();
348 if (groovyClassLoader != null) {
349 groovyClassLoader.clearCache();
350 }
351 }
352 }
353 }