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. <tt>pattern]</tt> */
- public abstract String usage();
+ /** Returns a single-line of parameter information, eg. <tt>[pattern]</tt> */
+ public abstract String params();
/** Returns single-line description of command. */
public abstract String summary();
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. <tt>usage: ls [pattern]</tt> */
+ 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(" ");
}
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 "+
"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));
- }
- }
-
}
+++ /dev/null
-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;
-}
--- /dev/null
+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 <tt>in</tt>
+ * and writes a response on <tt>out</tt> */
+ 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
+ }
+ }
+}
+++ /dev/null
-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 <tt>js</tt> that match <tt>matcher</tt>. */
- 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 <tt>o</tt> 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; }
- }
- }
-}
}
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();
}
}
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://")) {
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 <tt>scope</tt> in <tt>root</tt>. */
+ 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)
};
}
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;
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 {}
}