From e2e46233d9db6fe8728421016a41d5bf79db86e5 Mon Sep 17 00:00:00 2001 From: crawshaw Date: Sun, 5 Dec 2004 14:28:47 +0000 Subject: [PATCH] new xt shell based on JS transparency layer darcs-hash:20041205142847-2eb37-384d49182b364866cf8046df220644309c8cc6ee.gz --- src/java/org/ibex/xt/shell/Command.java | 238 +++++++++++++++-------------- src/java/org/ibex/xt/shell/Env.java | 35 ----- src/java/org/ibex/xt/shell/JSRemote.java | 244 ++++++++++++++++++++++++++++++ src/java/org/ibex/xt/shell/Request.java | 210 ------------------------- src/java/org/ibex/xt/shell/Servlet.java | 32 +--- src/java/org/ibex/xt/shell/Shell.java | 138 +++++++++++------ 6 files changed, 462 insertions(+), 435 deletions(-) delete mode 100644 src/java/org/ibex/xt/shell/Env.java create mode 100644 src/java/org/ibex/xt/shell/JSRemote.java delete mode 100644 src/java/org/ibex/xt/shell/Request.java diff --git a/src/java/org/ibex/xt/shell/Command.java b/src/java/org/ibex/xt/shell/Command.java index 7edc5ac..48fb329 100644 --- a/src/java/org/ibex/xt/shell/Command.java +++ b/src/java/org/ibex/xt/shell/Command.java @@ -1,19 +1,24 @@ package org.ibex.xt.shell; -import java.io.StringReader; import java.io.Writer; import java.io.IOException; import java.util.*; +import java.util.regex.*; import org.ibex.js.*; +import org.ibex.util.*; public abstract class Command { + protected Shell shell; + + protected Command(Shell s) { shell = s; } + /** Returns the command name. */ public abstract String name(); - /** Returns single-line of usage information, eg. pattern] */ - public abstract String usage(); + /** Returns a single-line of parameter information, eg. [pattern] */ + public abstract String params(); /** Returns single-line description of command. */ public abstract String summary(); @@ -22,38 +27,72 @@ public abstract class Command { public abstract String help(); /** Writes result of execution, even if result is an error. */ - public abstract void execute(Writer w, String[] args, Env env) throws IOException; + public abstract void execute(Writer w, String[] args) throws IOException; + + + /** Returns single-line of usage information, eg. usage: ls [pattern] */ + public void usage(Writer w) throws IOException { + w.write("usage: "); + w.write(name()); + w.write(" "); + w.write(params()); + w.write("\n"); + } + + public List fromShellPath(String s) throws Shell.NoSuchPathException { + return fromShellPath(s, null); + } + + /** Converts a shell path "/foo/etc/../bar" to its component form, + * { "foo", "bar" } and loads it into the returned list. + * Handles relative positioning to shell.getPath(). */ + public List fromShellPath(String s, List l) throws Shell.NoSuchPathException { + if (s == null) return null; + if (l == null) l = new ArrayList(); + + s = s.trim().replace("/+", "/"); + if (s.charAt(0) != '/') l.addAll(Arrays.asList(shell.getPath())); + + StringTokenizer st = new StringTokenizer(s, "/"); + while (st.hasMoreTokens()) { + String part = st.nextToken(); + if (part == null || part.equals("") || part.equals(".")) continue; + else if (part.equals("..")) { if (l.size() > 0) l.remove(l.size() - 1); } + else l.add(part); + } + + return l; + } public static class Help extends Command { + public Help(Shell s) { super(s); } public String name() { return "help"; } - public String usage() { return "[command name]"; } + public String params() { return "[command name]"; } public String summary() { return "Lists available commands."; } public String help() { return ""; } - public void execute(Writer w, String[] c, Env env) throws IOException { + public void execute(Writer w, String[] c) throws IOException { if (c.length > 1) { - Command cmd = env.command(c[1]); + Command cmd = shell.getCommand(c[1]); if (c == null) { w.write("help: "); w.write(c[1]); w.write(": command not found\n"); } else { - w.write("usage: "); - w.write(cmd.name()); - w.write(" "); - w.write(cmd.usage()); - w.write("\n\n"); + cmd.usage(w); + w.write("\n"); w.write(cmd.help()); w.write("\n"); } } else { int len = 3; - for (int i=0; i < env.commands.length; i++) - len = Math.max(env.commands[i].name().length(), len); + Command[] cmds = shell.getCommands(); + for (int i=0; i < cmds.length; i++) + len = Math.max(cmds[i].name().length(), len); w.write("Available commands:\n"); - for (int i=0; i < env.commands.length; i++) { - Command cmd = env.commands[i]; + for (int i=0; i < cmds.length; i++) { + Command cmd = cmds[i]; w.write(" "); w.write(cmd.name()); for (int j=len - cmd.name().length(); j >= 0; j--) w.write(" "); @@ -67,47 +106,81 @@ public abstract class Command { } public static class Ls extends Command { + public Ls(Shell s) { super(s); } public String name() { return "ls"; } - public String usage() { return "[path]"; } + public String params() { return "[path]"; } public String summary() { return "List object entries."; } public String help() { return "Lists the keys in an object. Modelled after the UNIX ls command."; } - public void execute(Writer w, String[] c, Env env) throws IOException { - if (c.length > 2) { w.write(usage()); return; } - String p = env.path(c.length == 1 ? "*" : c[1]); - - Request.Response ret = env.send(new Request.Key(p)); - if (!(ret instanceof Request.Key.Res)) { - w.write("error: "); - w.write(ret.error().getMessage()); - w.write("\n"); - } else { - List l = ((Request.Key.Res)ret).keys(); - Iterator i = l.iterator(); while (i.hasNext()) { - w.write(i.next().toString()); + public void execute(Writer w, String[] c) throws IOException { + if (c.length > 2) { usage(w); return; } + String p = c.length == 1 ? "*" : c[1]; + if (p.endsWith("/")) p += "*"; + + try { + List path = fromShellPath(p); + Object key = path.remove(path.size() - 1); + Object po = shell.getFromPath(path.toArray()); + if (po == null || !(po instanceof JS)) + throw new Shell.NoSuchPathException(); + JS cur = (JS)po; + + if (key instanceof String && + ((String)key).indexOf('*') >= 0) { + String last = (String)key; + last = last.replaceAll("\\.", "\\."); + last = last.replaceAll("\\*", ".*"); + Pattern pat = Pattern.compile(last); + Iterator i = cur.keys().iterator(); while (i.hasNext()) { + Object o = i.next(); + if (o == null || !(o instanceof String)) continue; + String s = (String)o; + if (!pat.matcher(s).matches()) continue; + w.write(s); + w.write("\n"); + } + } else if (cur.containsKey(key)) { + w.write(key.toString()); w.write("\n"); } + } catch (Shell.NoSuchPathException e) { + w.write("error: no such path: "); + w.write(p); + w.write("\n"); + } catch (JSExn e) { + w.write("error: no such path: "); + w.write(p); + w.write(" ("); + w.write(e.getMessage()); + w.write(")\n"); } } } public static class Pwd extends Command { + public Pwd(Shell s) { super(s); } public String name() { return "pwd"; } - public String usage() { return ""; } + public String params() { return ""; } public String summary() { return "Path to current object."; } public String help() { return "Print the path to the current object."; } - public void execute(Writer w, String[] c, Env env) throws IOException { - if (c.length != 1) { w.write(usage()); return; } - w.write(env.path.equals("") ? "." : env.path); + public void execute(Writer w, String[] c) throws IOException { + if (c.length != 1) { usage(w); return; } + Object[] path = shell.getPath(); + for (int i=0; i < path.length; i++) { + w.write("/"); + w.write(path[i].toString()); + } + if (path.length == 0) w.write("/"); w.write("\n"); } } public static class Cd extends Command { + public Cd(Shell s) { super(s); } public String name() { return "cd"; } - public String usage() { return "[path]"; } + public String params() { return "[path]"; } public String summary() { return "Change current object."; } public String help() { return "Chnages the current object that all other commands use "+ @@ -116,97 +189,28 @@ public abstract class Command { "or an absolute path (e.g. cd .prevalent.myob).\n\n" + "To go up one level, cd .. can be used."; } - public void execute(Writer w, String[] c, Env env) throws IOException { - if (c.length > 2) w.write(usage()); - else if (c.length == 1 || c[1].equals("") || c[1].equals(".")) env.path = ""; - else if (c[1].equals("..")) { - String n = env.path; - n = n.substring(0, n.lastIndexOf('.')); - env.path = n; - } else { - String n = env.path(c[1]); - - Request.Response ret = env.send(new Request.IsKey(n)); - if (!(ret instanceof Request.IsKey.Res)) { - w.write("error: "); - w.write(ret.error().getMessage()); - w.write("\n"); - } else { - if (((Request.IsKey.Res)ret).exists()) { - w.write("error: "); - w.write(c[1]); - w.write(": no such path\n"); - } else { - env.path = n; - } - } + public void execute(Writer w, String[] c) throws IOException { + if (c.length > 2) { usage(w); return; } + String path = c.length == 1 ? "/" : c[1]; + + try { shell.setPath(fromShellPath(path).toArray()); } + catch (Shell.NoSuchPathException e) { + w.write("error: no such path: "); + w.write(path); + w.write("\n"); } } } public static class Rm extends Command { + public Rm(Shell s) { super(s); } public String name() { return "rm"; } - public String usage() { return "[options] [path]"; } + public String params() { return "[options] [path]"; } public String summary() { return "Removes objects."; } public String help() { return "Removes objects."; } // FIXME - public void execute(Writer w, String[] c, Env env) throws IOException { - if (c.length == 1) { w.write(usage()); } - - Request.Key[] r = new Request.Key[c.length - 1]; - for (int i=0; i < r.length; i++) r[i] = new Request.RemoveKey(env.path(c[i + 1])); + public void execute(Writer w, String[] c) throws IOException { + if (c.length == 1) { usage(w); return; } - Request.Response ret = env.send(new Request.Composite(r, false)); - if (!(ret instanceof Request.Composite.Res)) { - w.write("error: "); - w.write(ret.error().getMessage()); - w.write("\n"); - } else { - Request.Response[] res = ((Request.Composite.Res)ret).responses(); - for (int i=0; i < res.length; i++) { - if (res[i] instanceof Request.RemoveKey.Res) { - boolean rm = ((Request.RemoveKey.Res)res[i]).removed(); - if (!rm) { - w.write("error: cannot remove '"); - w.write(c[i + 1]); - w.write("': no such key\n"); - } - } else { - w.write("error: cannot remove '"); - w.write(c[i + 1]); - w.write("': "); - w.write(res[i].error().getMessage()); - w.write("\n"); - } - } - } } } - - public static class Set extends Command { - public String name() { return "set"; } - public String usage() { return "[name] [object]"; } - public String summary() { return "Sets ."; } - public String help() { return "Removes objects."; } // FIXME - - public void execute(Writer w, String[] c, Env env) throws IOException { - if (c.length < 3) { w.write(usage()); } - - String m = c[1]; - String s = "return ("; - for (int i=2; i < c.length; i++) s += c[i]; - s += ");"; - - JS js; - try { js = JS.fromReader("input", 0, new StringReader(s)); } - catch (IOException e) { - w.write("error: "); - w.write(e.getMessage()); - w.write("\n"); - return; - } - - Request.Response ret = env.send(new Request.SetKey(env.path, m, js)); - } - } - } diff --git a/src/java/org/ibex/xt/shell/Env.java b/src/java/org/ibex/xt/shell/Env.java deleted file mode 100644 index f09cc95..0000000 --- a/src/java/org/ibex/xt/shell/Env.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.ibex.xt.shell; - -import java.io.IOException; - -public abstract class Env { - /** Current JS path using '.' as a seperator. */ - public String path = ""; - - public Command[] commands = new Command[0]; - - /** Returns the command matching the given name. */ - public Command command(String name) { - for (int i=0; i < commands.length; i++) - if (commands[i].name().equals(name)) return commands[i]; - return null; - } - - /** Returns a path, based on console-style representation. */ - public String path(String c) { - if (c.equals("") || c.equals(".")) { - c = "."; - } else if (c.equals("..")) { - c = path; - c = c.substring(0, c.lastIndexOf('.')); - if (c.equals("")) c = "."; - } else { - if (c.charAt(0) != '.') c = path + "." + c; - if (c.length() > 1 && c.charAt(c.length() - 1) == '.') - c = c.substring(0, c.length() - 1); - } - return c; - } - - public abstract Request.Response send(Request request) throws IOException; -} diff --git a/src/java/org/ibex/xt/shell/JSRemote.java b/src/java/org/ibex/xt/shell/JSRemote.java new file mode 100644 index 0000000..f7886d8 --- /dev/null +++ b/src/java/org/ibex/xt/shell/JSRemote.java @@ -0,0 +1,244 @@ +package org.ibex.xt.shell; + +import java.io.*; +import java.net.*; +import java.util.*; + +import org.ibex.js.*; +import org.ibex.util.*; +import org.ibex.util.Collections; + +public class JSRemote extends JS { + public static final int VERSION = 1; + + private final URL server; + private final String path; + + private transient String cookie = null; + private transient Keys keys = null; + + public JSRemote(URL s) { server = s; path = null; } + public JSRemote(URL s, String p) { server = s; path = p; } + + /** Receives a Request object from a client on in + * and writes a response on out */ + public static void receive(JS root, ObjectInputStream in, ObjectOutputStream out) + throws IOException { + out.writeInt(VERSION); if (in.readInt() != VERSION) return; + + Request r; + try { + Object o = in.readObject(); + if (o == null) throw new IOException("unexpected null request"); + if (!(o instanceof Request)) throw new IOException( + "unexpected request object: "+o.getClass().getName()); + r = (Request)o; + } catch (ClassNotFoundException e) { + throw new IOException("unexpected class not found: " + e.getMessage()); + } + + r.execute(root, out); + } + + /** Sends a request to the server. */ + protected ObjectInputStream send(Request request) throws IOException { + URLConnection c = server.openConnection(); + ((HttpURLConnection)c).setRequestMethod("POST"); + c.setDoOutput(true); + if (cookie != null) c.setRequestProperty("Cookie", cookie); + + c.connect(); + + ObjectOutputStream out = new ObjectOutputStream(c.getOutputStream()); + out.writeInt(VERSION); + out.writeObject(request); + out.close(); + + String cook = c.getHeaderField("Set-Cookie"); + if (cook != null && cook.length() > 0) cookie = cook.substring(0, cook.indexOf(';')); + + ObjectInputStream in = new ObjectInputStream(c.getInputStream()); + int ver = in.readInt(); + if (ver != VERSION) throw new IOException("server version "+ver+", expected "+VERSION); + return in; + } + + public Collection keys() { return keys == null ? keys = new Keys() : keys.update(); } + + public Object get(Object k) throws JSExn { + try { + ObjectInputStream in = send(new Request(path, k) { + protected void execute() throws JSExn, IOException { + Object o = scope.get(key); + if (o == null) + out.writeObject(null); + else if (o instanceof Number || o instanceof Boolean || o instanceof String) + out.writeObject(o); + else if (o instanceof JS) + out.writeObject(new JSRemote(server, (path == null ? "" : path + '.') + key)); + else throw new JSExn("unexpected class type " + o + " ["+o.getClass()+"]"); + } + }); + Object o = in.readObject(); in.close(); + + if (o == null) return null; + else if (o instanceof Exception) throw (Exception)o; + else return o; + } catch (JSExn e) { throw e; + } catch (Exception e) { throw new RuntimeException("JSRemote", e); } + } + + // FIXME: unroll JSRemote if it is a value + public void put(Object k, final Object value) throws JSExn { + try { send(new Request(path, k) { + protected void execute() throws JSExn, IOException { + scope.put(key, value); + } + }); } catch (Exception e) { throw new RuntimeException("JSRemote", e); } + } + + public Object remove(Object k) throws JSExn { + try { return send(new Request(path, k) { + protected void execute() throws JSExn, IOException { + out.writeObject(scope.remove(key)); + } + }).readObject(); } catch (Exception e) { throw new RuntimeException("JSRemote", e); } + } + + public boolean containsKey(Object k) throws JSExn { + try { return send(new Request(path, k) { + protected void execute() throws JSExn, IOException { + out.writeBoolean(scope.containsKey(key)); + } + }).readBoolean(); } catch (Exception e) { throw new RuntimeException("JSRemote", e); } + } + + public Object call(final Object a0, final Object a1, final Object a2, + final Object[] rest, final int nargs) throws JSExn { + try { return send(new Request(path) { + protected void execute() throws JSExn, IOException { + out.writeObject(scope.call(a0, a1, a2, rest, nargs)); + } + }).readObject(); } catch (Exception e) { throw new RuntimeException("JSRemote", e); } + } + + public Object callMethod(final Object m, final Object a0, final Object a1, final Object a2, + final Object[] rest, final int nargs) throws JSExn { + try { return send(new Request(path) { + protected void execute() throws JSExn, IOException { + out.writeObject(scope.callMethod(m, a0, a1, a2, rest, nargs)); + } + }).readObject(); } catch (Exception e) { throw new RuntimeException("JSRemote", e); } + } + + // FIXME: map trap functions to server + + public static abstract class Request implements Serializable { + protected ObjectOutputStream out; + protected JS scope; + protected String path; + protected Object key; + + public Request(String path) { this.path = path; key = null; } + public Request(String path, Object key) { + if (key != null && key instanceof String) { + String k = (String)key; + if (k.length() > 0) { + if (k.charAt(0) == '.') k = k.substring(1); + if (k.charAt(k.length() - 1) == '.') k = k.substring(0, k.length() - 1); + } + int pos = k.lastIndexOf('.'); + if (pos >= 0) { + path += k.substring(0, pos); + k = k.substring(pos + 1); + } + this.key = k; + } else this.key = key; + + this.path = path; + } + public void execute(JS r, ObjectOutputStream o) throws IOException { + out = o; + scope = r; + try { + if (path != null) { + StringTokenizer st = new StringTokenizer(path, "."); + while (st.hasMoreTokens()) { + String s = st.nextToken().trim(); + if (s.length() > 0 && !s.equals(".")) { + Object ob = scope.get(s); + if (ob == null || !(ob instanceof JS)) + throw new JSExn("path not found"); + scope = (JS)ob; + } + } + } + execute(); + } catch(JSExn e) { out.writeObject(e); } + out = null; + scope = null; + } + protected abstract void execute() throws JSExn, IOException; + } + + private class Keys extends AbstractCollection implements Serializable { + private transient final List items = + Collections.synchronizedList(new ArrayList()); + private int modCount = 0, size = -1; + + public Iterator iterator() { update(); return new KeyIt(); } + public int size() { if (size == -1) update(); return size; } + + private synchronized Keys update() { + modCount++; items.clear(); + try { + final ObjectInputStream in = send(new Request(path) { + protected void execute() throws JSExn, IOException { + Collection c = scope.keys(); out.writeInt(c.size()); + Iterator i; + if (c.size() > 1000) { // FIXME: better way to unload server? + i = c.iterator(); + } else { + List l = new ArrayList(c); + Collections.sort(l); + i = l.listIterator(); + } + while (i.hasNext()) out.writeObject(i.next()); + } + }); + size = in.readInt(); + new Thread() { public void run() { + try { + for (int i=0; i < size; i++) items.add(in.readObject()); + in.close(); + } catch (Exception e) { + size = -1; items.clear(); + throw new RuntimeException("JSRemote", e); + } + }}.start(); + } catch (Exception e) { throw new RuntimeException("JSRemote", e); } + + return this; + } + + private class KeyIt implements Iterator { + private final int mod; + private int pos = 0; + + KeyIt() { mod = modCount; } + public boolean hasNext() { + if (modCount != mod) throw new ConcurrentModificationException(); + return pos < size; + } + public Object next() { + if (modCount != mod) throw new ConcurrentModificationException(); + while (pos >= items.size()) { + if (pos >= size) throw new NoSuchElementException(); + try { Thread.sleep(50); } catch (InterruptedException e) {} + } + return items.get(pos++); + } + public void remove() { throw new UnsupportedOperationException(); } // FIXME + } + } +} diff --git a/src/java/org/ibex/xt/shell/Request.java b/src/java/org/ibex/xt/shell/Request.java deleted file mode 100644 index 8cb2be6..0000000 --- a/src/java/org/ibex/xt/shell/Request.java +++ /dev/null @@ -1,210 +0,0 @@ -package org.ibex.xt.shell; - -import java.io.*; -import java.util.*; -import java.util.regex.*; - -import org.ibex.js.*; - -public abstract class Request implements Serializable { - - public abstract Response process(JSScope root) throws JSExn; - - public static class Response implements Serializable { - protected Exception ex; - public Response() { ex = null; } - public Response(Exception e) { ex = e; } - public Exception error() { return ex; } - } - - public static class Key extends Request { - protected String path, matcher; - public Key() {} - - /** Expects a shell-path that uses '.' as a seperator and * for wildcard. */ - public Key(String c) { - int pos = c.lastIndexOf('.'); - path = c.substring(0, pos); - matcher = c.substring(pos + 1).replaceAll("\\*+", ".*"); - } - - public Key(String path, String matcher) { - this.path = path; this.matcher = matcher; - } - - /** Returns the object referenced by path. */ - protected Object path(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); - - return p.equals("") ? root : root.get(p); - } - - /** Returns the keys in js that match matcher. */ - protected List matches(JS js) throws JSExn { - String m = matcher; - - Pattern pat = Pattern.compile(m); - List keys = new ArrayList(); - - Iterator i = js.keys().iterator(); while(i.hasNext()) { - String k = i.next().toString(); - if (pat.matcher(k).matches()) keys.add(k); - } - - return keys; - } - - /** Returns o cast as a JS if it is such and has keys, - * otherwise returns null. */ - protected JS keyed(Object o) { - // FIXME: replace this with a canHaveKeys() function in org.ibex.js.JS - return o == null || !(o instanceof JS) || - o instanceof JSDate || o instanceof Directory || - o instanceof Grammar || o instanceof JSMath || - o instanceof JSReflection || o instanceof JSRegexp ? - null : (JS)o; - } - - public Response process(JSScope root) throws JSExn { - JS js = keyed(path(root)); - - // if matcher is exact and a keyed object, return its keys - JS o = keyed(js.get(matcher)); - if (o != null) { js = o; matcher = ".*"; } - - return js == null ? new Res() : new Res(matches(js)); - } - - public static class Res extends Response { - private List keys; - public Res() { keys = null; } - public Res(List l) { keys = l; } - public List keys() { return keys; } - public boolean isPath() { return keys != null; } - } - } - - public static class IsKey extends Key { - public IsKey() {} - public IsKey(String c) { super(c); } - public Response process(JSScope root) throws JSExn { - JS js = keyed(path(root)); - return js == null ? new Res(false) : new Res(js.get(matcher) != null); - } - - public static class Res extends Response { - private boolean exists; - public Res() {} - public Res(boolean e) { exists = e; } - public boolean exists() { return exists; } - } - } - - public static class RemoveKey extends Key { - public RemoveKey(String c) { super(c); } - public RemoveKey(String p, String m) { super(p, m); } - public Response process(JSScope root) throws JSExn { - JS js = keyed(path(root)); - if (js == null) throw new JSExn("no such path"); - boolean rm = js.containsKey(matcher); - if (rm) js.remove(matcher); - return new Res(rm); - } - - public static class Res extends Response { - private boolean removed; - public Res() { } - public Res(boolean rm) { removed = rm; } - public boolean removed() { return removed; } - } - } - - public static class SetKey extends Key { - protected JS value; - public SetKey() {} - public SetKey(String p, String m, JS val) { super(p, m); this.value = val; } - - public Response process(JSScope root) throws JSExn { - JS js = keyed(path(root)); - if (js == null) throw new JSExn("no such path"); - - js.put(matcher, JS.eval(JS.cloneWithNewParentScope( - value, new JSResolve(root, js)))); - return new Res(); // TODO: return put() value when it has one - } - - public class Res extends Response { - } - - public class JSResolve extends JSScope { - private JS path; - public JSResolve(JSScope parent, JS path) { super(parent); this.path = path; } - public Object get(Object key) throws JSExn { - if (key != null && key instanceof String) { - String k = (String)key; - if (k.startsWith(".")) k = k.substring(1); - else return path.get(k); - } - return super.get(key); - } - public void put(Object key, Object val) throws JSExn { - if (key != null && key instanceof String) { - String k = (String)key; - if (k.startsWith(".")) k = k.substring(1); - else { path.put(key, val); return; } - } - super.put(key, val); - } - } - } - - /** Runs a series of requests. */ - public static class Composite extends Request { - private boolean breakOnError = false; - private Request[] requests; - public Composite() {} - public Composite(Request[] r, boolean breakOnError) { - requests = r; - this.breakOnError = breakOnError; - } - public Composite(List r, boolean breakOnError) { - Request[] req = new Request[r.size()]; - r.toArray(req); - requests = req; - this.breakOnError = breakOnError; - } - - public Response process(JSScope root) { - Response[] res = new Response[requests.length]; - - for (int i=0; i < requests.length; i++) { - try { res[i] = requests[i].process(root); } - catch (JSExn e) { - res[i] = new Response(e); - - if (breakOnError) { - Response[] newres = new Response[i + 1]; - System.arraycopy(res, 0, newres, 0, newres.length); - res = newres; - break; - } - } - } - - return new Res(res); - } - - public static class Res extends Response { - private Response[] responses; - public Res() {} - public Res(Response[] r) { responses = r; } - public Response get(int i) { return responses[i]; } - public int size() { return responses.length; } - public Response[] responses() { return responses; } - } - } -} diff --git a/src/java/org/ibex/xt/shell/Servlet.java b/src/java/org/ibex/xt/shell/Servlet.java index 44cc346..70b89d9 100644 --- a/src/java/org/ibex/xt/shell/Servlet.java +++ b/src/java/org/ibex/xt/shell/Servlet.java @@ -22,33 +22,9 @@ public class Servlet extends HttpServlet { } public void doPost(HttpServletRequest rq, HttpServletResponse rs) throws IOException { - Request r; - try { r = (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) { - try { - scope = new JSScope(null) { - { super.put("prevalent", prevalent); } - - public void put(String k, String v) throws JSExn { - if (k != null && k.equals("prevalent")) throw new JSExn( - "can not replace key prevalent"); - super.put(k, v); - } - }; - } catch (JSExn e) { - e.printStackTrace(); - throw new IOException("unexpected JSExn"); - } - rq.getSession().setAttribute("scope", scope); - } - Request.Response ret; - try { ret = r.process(scope); } catch (JSExn e) { ret = new Request.Response(e); } - new ObjectOutputStream(rs.getOutputStream()).writeObject(ret); + ObjectInputStream in = new ObjectInputStream(rq.getInputStream()); + ObjectOutputStream out = new ObjectOutputStream(rs.getOutputStream()); + JSRemote.receive(prevalent, in, out); + out.flush(); } } diff --git a/src/java/org/ibex/xt/shell/Shell.java b/src/java/org/ibex/xt/shell/Shell.java index 17a2f57..f677f28 100644 --- a/src/java/org/ibex/xt/shell/Shell.java +++ b/src/java/org/ibex/xt/shell/Shell.java @@ -3,7 +3,10 @@ package org.ibex.xt.shell; import java.io.*; import java.net.*; -public class Shell extends Env { +import org.ibex.js.*; +import org.ibex.util.*; + +public class Shell { public static void main(String[] args) throws Exception { if (args.length == 0 || args.length > 2|| !args[0].startsWith("http://")) { @@ -22,22 +25,95 @@ public class Shell extends Env { System.out.println("Usage: xish url [command]"); } - /** URL of server. */ - protected URL server; - /** Server cookie. Reduces server load. */ - private String cookie = null; + /** Supported commands. */ + private Command[] commands; + + /** Root context. */ + private final JS root; + + /** Current JS context for the shell. */ + private JS scope; + + /** Current path to scope in root. */ + private Object[] path; + + /** Returns the object represented by the given path, + * ignoring the current shell path context.*/ + public Object getFromPath(Object[] path) throws NoSuchPathException, JSExn { + if (path.length == 0) return root; + + if (root instanceof JSRemote) { + // JSRemote will automatically process its keys for '.' seperators + StringBuffer sb = new StringBuffer(path[0].toString()); + for (int i=1; i < path.length; i++) { + sb.append('.'); sb.append(path[i].toString()); + } + return root.get(sb.toString()); + } else { + JS cur = root; + for (int i=0; i < path.length - 1; i++) { + Object o = cur.get(path[i]); + if (o == null || !(o instanceof JS)) throw new Shell.NoSuchPathException(); + cur = (JS)o; + } + return cur.get(path[path.length - 1]); + } + } + + /** Set the current path of the shell, modifiying the result of getScope(). */ + public void setPath(Object[] s) throws NoSuchPathException { + JS cur = root; + if (s == null) s = new Object[0]; + for (int i=0; i < s.length; i++) { + Object o; + try { o = cur.get(s[i]); } catch (JSExn e) { throw new NoSuchPathException(); } + if (o == null || !(o instanceof JS)) throw new NoSuchPathException(); + cur = (JS)o; + } + scope = cur; + path = s; + } + + /** Returns the current path of the shell. */ + public Object[] getPath() { return path; } + + /** Returns String represntation of path, using '/' as a seperator. */ + public String getPathAsString() { + if (path.length == 0) return "/"; + StringBuffer sb = new StringBuffer(); + for (int i=0; i < path.length; i++) { + sb.append('/'); sb.append(path[i].toString()); + } + return sb.toString(); + } + + /** Returns the context matching the current path. */ + public JS getScope() { return scope; } + + /** Returns the context matching the current path. */ + public JS getRootScope() { return root; } + + /** Returns all supported commands. */ + public Command[] getCommands() { return commands; } + + /** Returns the command matching the given name. */ + public Command getCommand(String name) { + for (int i=0; i < commands.length; i++) + if (commands[i].name().equals(name)) return commands[i]; + return null; + } /** Create a new Shell using the given url for the server. */ public Shell(URL url) { - server = url; + root = scope = new JSRemote(url); + path = new String[0]; commands = new Command[] { - new Command.Ls(), - new Command.Pwd(), - new Command.Cd(), - new Command.Rm(), - new Command.Set(), - new Command.Help() + new Command.Ls(this), + new Command.Pwd(this), + new Command.Cd(this), + new Command.Rm(this), + new Command.Help(this) }; } @@ -46,7 +122,7 @@ public class Shell extends Env { PrintWriter out = new PrintWriter(w); out.println("ibex xt shell: type help or exit"); - out.print("xt: "); + out.print("xt:"); out.print(getPathAsString()); out.print("# "); out.flush(); String line; @@ -84,47 +160,19 @@ public class Shell extends Env { if (buffer.length() > 0) { String[] c = buffer.split(" "); - Command cmd = command(c[0]); + Command cmd = getCommand(c[0]); if (cmd == null) { out.write(c[0]); w.write(": command not found\n"); - } else cmd.execute(out, c, this); + } else cmd.execute(out, c); buffer = ""; } - out.print("xt: "); + out.print("xt:"); out.print(getPathAsString()); out.print("# "); out.flush(); } } - public Request.Response send(Request request) throws IOException { - URLConnection c = server.openConnection(); - ((HttpURLConnection)c).setRequestMethod("POST"); - c.setDoOutput(true); - if (cookie != null) c.setRequestProperty("Cookie", cookie); - - c.connect(); - - ObjectOutputStream out = new ObjectOutputStream(c.getOutputStream()); - out.writeObject(request); - out.close(); - - String cook = c.getHeaderField("Set-Cookie"); - if (cook != null && !cook.equals("")) cookie = cook.substring(0, cook.indexOf(';')); - - try { - Object o = new ObjectInputStream(c.getInputStream()).readObject(); - if (o == null) { - throw new IOException("unexpected null object returned"); - } else if (!(o instanceof Request.Response)) { - throw new IOException("unexpected object returned: "+o.getClass().getName()); - } else { - return (Request.Response)o; - } - } catch (ClassNotFoundException e) { - e.printStackTrace(); - throw new IOException("unexpected ClassNotFoundException"); - } - } + public static class NoSuchPathException extends Exception {} } -- 1.7.10.4