initial framwork for xt shell
authorcrawshaw <crawshaw@ibex.org>
Fri, 26 Nov 2004 10:14:23 +0000 (10:14 +0000)
committercrawshaw <crawshaw@ibex.org>
Fri, 26 Nov 2004 10:14:23 +0000 (10:14 +0000)
darcs-hash:20041126101423-2eb37-4342908ba113868713de465f55a9221672810f49.gz

src/java/org/ibex/xt/Shell.java [new file with mode: 0644]
src/java/org/ibex/xt/ShellServlet.java [new file with mode: 0644]

diff --git a/src/java/org/ibex/xt/Shell.java b/src/java/org/ibex/xt/Shell.java
new file mode 100644 (file)
index 0000000..0f228ab
--- /dev/null
@@ -0,0 +1,218 @@
+package org.ibex.xt;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.regex.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+import org.ibex.util.*;
+import org.ibex.util.Collections;
+import org.ibex.js.*;
+
+import org.prevayler.*;
+
+public class Shell {
+
+    public static void main(String[] args) throws Exception {
+        if (args.length == 0 || args.length > 2||  !args[0].startsWith("http://")) {
+            printUsage(); return;
+        }
+        Shell shell = new Shell(new URL(args[0]));
+
+        if (args.length == 2) {
+            System.out.println(shell.execute(args[1]));
+        } else {
+            shell.listen(new InputStreamReader(System.in), new OutputStreamWriter(System.out));
+        }
+    }
+
+    private static void printUsage() {
+        System.out.println("Usage: xish url [command]");
+    }
+
+    /** URL of server. */
+    private URL server;
+
+    /** Current JS path using '.' as a seperator. */
+    private String pwd = ".";
+
+    /** Create a new Shell using the given url for the server. */
+    public Shell(URL url) { server = url; }
+
+    public void listen(Reader r, Writer w) throws IOException {
+        LineNumberReader in = new LineNumberReader(r);
+        PrintWriter out = new PrintWriter(w);
+
+        out.println("Ibex xt shell. Type help or exit.");
+        out.print("xt: ");
+        out.flush();
+
+        String line;
+        String buffer = "";
+        while ((line = in.readLine()) != null) {
+            if (line.length() > 0) {
+                if (line.startsWith("exit")) return;
+                if (line.charAt(line.length() - 1) == '\\') {
+                    buffer += line.substring(0, line.length() - 1);
+                    out.print('>');
+                    out.flush(); continue;
+                }
+
+                buffer += line;
+            }
+
+            if (buffer.length() > 0) {
+                execute(buffer, out);
+                buffer = "";
+            } else {
+                out.println("");
+            }
+            out.print("xt: ");
+            out.flush();
+        }
+    }
+
+    public String execute(String cmd) throws IOException {
+        StringWriter s = new StringWriter();
+        execute(cmd, s);
+        return s.toString();
+    }
+
+    public void execute(String cmd, Writer w) throws IOException {
+        if (cmd == null || cmd.length() == 0) return;
+        String[] c = cmd.split(" ");
+
+        //#switch(c[0])
+        case "ls":
+            if (c.length > 2) { w.write("usage: ls [path]\n"); return; }
+            String path = pwd;
+            String matcher = ".*";
+            if (c.length == 2) {
+                int pos = c[1].lastIndexOf('/');
+                path += c[1].substring(0, pos);
+                path.replaceAll("/", "\\.");
+                if (pos + 1 < c[1].length()) {
+                    matcher = c[1].substring(pos + 1);
+                    matcher.replaceAll("\\.", "\\.");
+                    matcher.replaceAll("\\*", ".*");
+                }
+            }
+
+            Object ret = send(new KeyRequest(path, matcher));
+            if (ret == null) {
+                error(w, "returned object is null"); return;
+            } else if (ret instanceof JSExn) {
+                error(w, (JSExn)ret); return;
+            } else if (ret instanceof List) {
+                List l = (List)ret; Collections.sort(l);
+                Iterator i = l.iterator(); while (i.hasNext()) {
+                    w.write("  ");
+                    w.write(i.next().toString());
+                    w.write("\n");
+                }
+                return;
+            } else {
+                error(w, "returned object of unknown type: "+ret.getClass().getName());return;
+            }
+        case "pwd":
+            w.write(pwd.replace('.', '/'));
+            w.write("\n");
+        case "cd":
+            throw new Error("not yet implemented");
+        case "help":
+            w.write("Available commands:\n");
+            w.write("  ls    - list entries in current object\n");
+            w.write("  pwd   - path of current object\n");
+            w.write("  help  - this message\n");
+            w.write("  exit  - close xt shell\n");
+        //#end
+    }
+
+    public void error(Writer w, String s) throws IOException {
+        w.write("Unexpected error: ");
+        w.write(s);
+        w.write("\n");
+    }
+
+    public void error(Writer w, JSExn e) throws IOException {
+        w.write("\nerror: ");
+        w.write(e.getMessage());
+        w.write('\n');
+    }
+
+    public Object send(Request request) throws IOException {
+        URLConnection c = server.openConnection();
+        ((HttpURLConnection)c).setRequestMethod("POST");
+        c.setDoOutput(true);
+        c.connect();
+
+        ObjectOutputStream out = new ObjectOutputStream(c.getOutputStream());
+        out.writeObject(request);
+        out.close();
+
+        try {
+            return new ObjectInputStream(c.getInputStream()).readObject();
+        } catch (ClassNotFoundException e) {
+            e.printStackTrace();
+            throw new IOException("unexpected ClassNotFoundException");
+        }
+    }
+
+    public static abstract class Request implements Serializable {
+        public abstract Object process(JSScope root) throws JSExn;
+    }
+
+    public static class KeyRequest extends Request {
+        private String path, matcher;
+        public KeyRequest() {}
+        public KeyRequest(String path, String matcher) {
+            this.path = path; this.matcher = matcher;
+        }
+
+        /** Returns a List. */
+        public Object process(JSScope root) throws JSExn {
+            String p = path == null ? "" : path.replaceAll("\\.+", "\\.");
+            if (p.length() > 0 && p.charAt(0) == '.') p = p.substring(1);
+            if (p.length() > 0 && p.charAt(p.length() - 1) == '.') p = p.substring(0, p.length() - 1);
+            System.out.println("searching path '"+p+"' for pattern '"+matcher+"'");
+
+            Object o = p.equals("") ? root : root.get(p);
+            if (o == null || o instanceof JSDate ||
+                             o instanceof JSArray ||
+                             !(o instanceof JS)) {
+                System.out.println("hit bad object: "+o+", class="+
+                    (o == null ? null : o.getClass().getName()));
+                throw new JSExn("path /" + p + " does not exist");
+            } else {
+                Pattern pat = Pattern.compile(matcher);
+                List keys = new ArrayList();
+
+                Iterator i = ((JS)o).keys().iterator(); while(i.hasNext()) {
+                    String k = i.next().toString();
+                    if (pat.matcher(k).matches()) keys.add(k);
+                }
+
+                return keys;
+            }
+        }
+    }
+
+    public static class ExecRequest extends Request {
+        private JS exec;
+        public ExecRequest() {}
+        public ExecRequest(JS exec) { this.exec = exec; }
+        public ExecRequest(String source) throws IOException, JSExn {
+            this(new StringReader(source));
+        }
+        public ExecRequest(Reader source) throws IOException, JSExn {
+            exec = JS.fromReader("xsh", 0, source);
+        }
+
+        /** Returns the result of <tt>JS.eval()</tt>. */
+        public Object process(JSScope root) throws JSExn {
+            return JS.eval(JS.cloneWithNewParentScope(exec, root));
+        }
+    }
+}
diff --git a/src/java/org/ibex/xt/ShellServlet.java b/src/java/org/ibex/xt/ShellServlet.java
new file mode 100644 (file)
index 0000000..bc53366
--- /dev/null
@@ -0,0 +1,53 @@
+package org.ibex.xt;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.regex.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+import org.ibex.util.*;
+import org.ibex.util.Collections;
+import org.ibex.js.*;
+
+import org.prevayler.*;
+
+
+public class ShellServlet extends HttpServlet {
+    private ServletContext cx = null;
+    private Prevayler prevayler;
+    private JS prevalent;
+
+    // FIXME: destroy() counter with normal Servlet
+    public void init(ServletConfig sc) throws ServletException {
+        cx = sc.getServletContext();
+        prevayler = Prevalence.getPrevayler(cx);
+        prevalent = (JS)prevayler.prevalentSystem();
+    }
+
+    public void doPost(HttpServletRequest rq, HttpServletResponse rs) throws IOException {
+        Shell.Request r;
+        try { r = (Shell.Request)new ObjectInputStream(rq.getInputStream()).readObject(); }
+        catch (ClassNotFoundException e) {
+            e.printStackTrace();
+            throw new IOException("exception receiving request, class not found");
+        }
+
+        JSScope scope = (JSScope)rq.getSession().getAttribute("scope");
+        if (scope == null) {
+            System.out.println("creating new scope");
+            try {
+                scope = new JSScope(null);
+                scope.put("prevalent", prevalent);
+            } catch (JSExn e) {
+                e.printStackTrace();
+                throw new IOException("unexpected JSExn");
+            }
+            rq.getSession().setAttribute("scope", scope);
+        }
+        Object ret;
+        try { ret = r.process(scope); } catch (JSExn e) { ret = e; }
+        new ObjectOutputStream(rs.getOutputStream()).writeObject(ret);
+    }
+}