001package com.hammurapi.common; 002 003import java.io.ByteArrayInputStream; 004import java.io.IOException; 005import java.io.InputStream; 006import java.io.Reader; 007import java.io.StringWriter; 008import java.net.URL; 009import java.net.URLConnection; 010import java.net.URLStreamHandler; 011import java.util.Arrays; 012import java.util.HashMap; 013import java.util.Map; 014 015import javax.script.Bindings; 016import javax.script.ScriptEngine; 017import javax.script.ScriptEngineManager; 018import javax.script.ScriptException; 019import javax.script.SimpleBindings; 020 021/** 022 * This class handles <code>script</code> protocol/schema and evaluates expressions using Java Scripting framework. 023 * Expression language extension is passed as authority and expression is passed as path, e.g. <code>script://java/myObject.toString()</code>. 024 * Evaluation result is converted to input stream in the following way: 025 * a) If it is input stream then it is returned as is. 026 * b) If it is Reader then it is wrapped into input stream. 027 * c) If it is null, then null is returned. 028 * d) Result is converted to String with toString() method and string bytes are returned as input stream. 029 * @author Pavel Vlasov 030 * 031 */ 032public class ScriptURLStreamHandler extends URLStreamHandler { 033 034 public static final String SCRIPT_PROTOCOL = "script"; 035 private Bindings bindings; 036 private ScriptEngineManager scriptEngineManager; 037 038 /** 039 * 040 * @param classLoader ClassLoader to use to load script engines and other classes. 041 * @param bindings Bindings for evaluating script expression. 042 */ 043 public ScriptURLStreamHandler(ClassLoader classLoader, Map<String, Object> bindings) { 044 this.bindings = bindings==null ? new SimpleBindings() : new SimpleBindings(bindings); 045 this.scriptEngineManager = new ScriptEngineManager(classLoader); 046 } 047 048 /** 049 * 050 * @param bindings Bindings for evaluating script expression. 051 */ 052 public ScriptURLStreamHandler(Map<String, Object> bindings) { 053 this.bindings = bindings==null ? new SimpleBindings() : new SimpleBindings(bindings); 054 this.scriptEngineManager = new ScriptEngineManager(); 055 } 056 057 // For testing. 058 public static void main(String[] args) throws Exception { 059 Map<String, Object> env = new HashMap<String, Object>(); 060 env.put("z", "366"); 061 ScriptURLStreamHandler suh = new ScriptURLStreamHandler(null, env); 062 URL testURL = new URL(null, "script://js/z.length", suh); 063 InputStream is = testURL.openStream(); 064 byte[] buf = new byte[40]; 065 is.read(buf); 066 System.out.println(Arrays.toString(buf)); 067 068 } 069 @Override 070 protected URLConnection openConnection(final URL u) throws IOException { 071 if (u==null || !SCRIPT_PROTOCOL.equals(u.getProtocol())) { 072 return null; 073 } 074 return new URLConnection(u) { 075 076 @Override 077 public void connect() throws IOException { 078 // Nothing to do - everything is done in "getInputStream()". 079 080 } 081 082 @Override 083 public InputStream getInputStream() throws IOException { 084 String authority = u.getHost(); 085 if (authority==null) { 086 return null; 087 } 088 089 ScriptEngine scriptEngine = scriptEngineManager.getEngineByExtension(authority); 090 if (scriptEngine==null) { 091 throw new IOException("Cannot create script engine for extension "+authority); 092 } 093 094 String expr = u.getPath(); 095 if (expr.startsWith("/")) { 096 expr = expr.substring(1); 097 } 098 try { 099 Object res = scriptEngine.eval(expr, bindings); 100 if (res==null) { 101 return null; 102 } 103 if (res instanceof InputStream) { 104 return (InputStream) res; 105 } 106 107 if (res instanceof Reader) { 108 StringWriter sw = new StringWriter(); 109 char[] cbuf = new char[4096]; 110 int l; 111 while ((l=((Reader) res).read(cbuf))!=-1) { 112 sw.write(cbuf, 0, l); 113 } 114 ((Reader) res).close(); 115 sw.close(); 116 res = sw.toString(); 117 } 118 119 return new ByteArrayInputStream(res.toString().getBytes()); 120 } catch (ScriptException e) { 121 throw new IOException("Cannot evaluate "+expr, e); 122 } 123 } 124 125 126 }; 127 } 128 129} 130 131