1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.asteriskjava.fastagi;
18
19 import org.asteriskjava.util.LogFactory;
20 import org.asteriskjava.util.Log;
21
22 import javax.script.ScriptEngine;
23 import javax.script.ScriptEngineManager;
24 import javax.script.Bindings;
25 import javax.script.ScriptException;
26 import java.io.*;
27 import java.util.Arrays;
28 import java.util.regex.Matcher;
29 import java.util.List;
30 import java.util.ArrayList;
31 import java.net.URLClassLoader;
32 import java.net.URL;
33 import java.net.MalformedURLException;
34
35
36
37
38
39
40
41 public class ScriptEngineMappingStrategy implements MappingStrategy
42 {
43 protected final Log logger = LogFactory.getLog(getClass());
44
45
46
47
48 public static final String REQUEST = "request";
49
50
51
52
53 public static final String CHANNEL = "channel";
54
55 private static final String[] DEFAULT_SCRIPT_PATH = new String[]{"agi"};
56 private static final String[] DEFAULT_LIB_PATH = new String[]{"lib"};
57
58 protected String[] scriptPath;
59 protected String[] libPath;
60 protected ScriptEngineManager scriptEngineManager = null;
61
62
63
64
65 public ScriptEngineMappingStrategy()
66 {
67 this(DEFAULT_SCRIPT_PATH, DEFAULT_LIB_PATH);
68 }
69
70
71
72
73
74
75
76 public ScriptEngineMappingStrategy(String[] scriptPath, String[] libPath)
77 {
78 setScriptPath(scriptPath);
79 setLibPath(libPath);
80 }
81
82
83
84
85
86
87
88 public void setScriptPath(String[] scriptPath)
89 {
90 this.scriptPath = Arrays.copyOf(scriptPath, scriptPath.length);;
91 }
92
93
94
95
96
97
98
99 public void setLibPath(String[] libPath)
100 {
101 this.libPath = Arrays.copyOf(libPath, libPath.length);
102 }
103
104 @Override
105 public AgiScript determineScript(AgiRequest request, AgiChannel channel)
106 {
107
108 final File file = searchFile(request.getScript(), scriptPath);
109 if (file == null)
110 {
111 return null;
112 }
113
114
115 final ScriptEngine scriptEngine = getScriptEngine(file);
116 if (scriptEngine == null)
117 {
118 logger.debug("No ScriptEngine found that can handle '" + file.getPath() + "'");
119 return null;
120 }
121
122 return new ScriptEngineAgiScript(file, scriptEngine);
123 }
124
125
126
127
128
129
130
131 protected ScriptEngine getScriptEngine(File file)
132 {
133 final String extension = getExtension(file.getName());
134 if (extension == null)
135 {
136 return null;
137 }
138
139 return getScriptEngineManager().getEngineByExtension(extension);
140 }
141
142
143
144
145
146
147
148
149 protected synchronized ScriptEngineManager getScriptEngineManager()
150 {
151 if (scriptEngineManager == null)
152 {
153 this.scriptEngineManager = new ScriptEngineManager(getClassLoader());
154 }
155 return scriptEngineManager;
156 }
157
158
159
160
161
162
163
164
165 protected ClassLoader getClassLoader()
166 {
167 final ClassLoader parentClassLoader = Thread.currentThread().getContextClassLoader();
168 final List<URL> jarFileUrls = new ArrayList<URL>();
169
170 if (libPath == null || libPath.length == 0)
171 {
172 return parentClassLoader;
173 }
174
175 for (String libPathEntry : libPath)
176 {
177 final File libDir = new File(libPathEntry);
178 if (!libDir.isDirectory())
179 {
180 continue;
181 }
182
183 final File[] jarFiles = libDir.listFiles(new FilenameFilter()
184 {
185 public boolean accept(File dir, String name)
186 {
187 return name.endsWith(".jar");
188 }
189 });
190
191 for (File jarFile : jarFiles)
192 {
193 try
194 {
195 jarFileUrls.add(jarFile.toURI().toURL());
196 }
197 catch (MalformedURLException e)
198 {
199
200 }
201 }
202 }
203
204 if (jarFileUrls.size() == 0)
205 {
206 return parentClassLoader;
207 }
208
209 return new URLClassLoader(jarFileUrls.toArray(new URL[jarFileUrls.size()]), parentClassLoader);
210 }
211
212
213
214
215
216
217
218
219 protected File searchFile(String scriptName, String[] path)
220 {
221 if (scriptName == null || path == null)
222 {
223 return null;
224 }
225
226 for (String pathElement : path)
227 {
228 final File pathElementDir = new File(pathElement);
229
230 if (!pathElementDir.isDirectory())
231 {
232 continue;
233 }
234
235 final File file = new File(pathElementDir, scriptName.replaceAll("/", Matcher.quoteReplacement(File.separator)));
236 if (!file.exists())
237 {
238 continue;
239 }
240
241 try
242 {
243
244 if (!isInside(file, pathElementDir))
245 {
246 return null;
247 }
248 }
249 catch (IOException e)
250 {
251 logger.warn("Unable to check whether '" + file.getPath() + "' is below '" + pathElementDir.getPath() + "'");
252 continue;
253 }
254
255 try
256 {
257 return file.getCanonicalFile();
258 }
259 catch (IOException e)
260 {
261 logger.error("Unable to get canonical file for '" + file.getPath() + "'", e);
262 }
263 }
264 return null;
265 }
266
267
268
269
270
271
272
273
274
275 protected final boolean isInside(File file, File dir) throws IOException
276 {
277 return file.getCanonicalPath().startsWith(dir.getCanonicalPath());
278 }
279
280
281
282
283
284
285
286 protected static String getExtension(String scriptName)
287 {
288 if (scriptName == null)
289 {
290 return null;
291 }
292
293 int filePosition = scriptName.lastIndexOf("/");
294 String fileName;
295
296 if (scriptName.lastIndexOf("\\") > filePosition)
297 {
298 filePosition = scriptName.lastIndexOf("\\");
299 }
300
301 if (filePosition >= 0)
302 {
303 fileName = scriptName.substring(filePosition + 1);
304 }
305 else
306 {
307 fileName = scriptName;
308 }
309
310 final int extensionPosition = fileName.lastIndexOf(".");
311 if (extensionPosition >= 0)
312 {
313 return fileName.substring(extensionPosition + 1);
314 }
315
316 return null;
317 }
318
319 protected static Reader getReader(File file) throws FileNotFoundException
320 {
321 final InputStream is = new FileInputStream(file);
322 return new InputStreamReader(is);
323 }
324
325 protected class ScriptEngineAgiScript implements NamedAgiScript
326 {
327 final File file;
328 final ScriptEngine scriptEngine;
329
330
331
332
333
334
335
336 public ScriptEngineAgiScript(File file, ScriptEngine scriptEngine)
337 {
338 this.file = file;
339 this.scriptEngine = scriptEngine;
340 }
341
342 public String getName()
343 {
344 return file == null ? null : file.getName();
345 }
346
347 public void service(AgiRequest request, AgiChannel channel) throws AgiException
348 {
349 final Bindings bindings = scriptEngine.createBindings();
350
351 bindings.put(ScriptEngine.FILENAME, file.getPath());
352 bindings.put(REQUEST, request);
353 bindings.put(CHANNEL, channel);
354
355
356 populateBindings(file, request, channel, bindings);
357
358 try
359 {
360 scriptEngine.eval(getReader(file), bindings);
361 }
362 catch (ScriptException e)
363 {
364 throw new AgiException("Execution of script '" + file.getPath() + "' with ScriptEngine failed", e);
365 }
366 catch (FileNotFoundException e)
367 {
368 throw new AgiException("Script '" + file.getPath() + "' not found", e);
369 }
370 }
371 }
372
373
374
375
376
377
378
379
380
381
382
383 protected void populateBindings(File file, AgiRequest request, AgiChannel channel, Bindings bindings)
384 {
385
386 }
387 }