    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