From 7959860be8bb0ac8aa172e1de8bb140b65afdae6 Mon Sep 17 00:00:00 2001 From: crawshaw Date: Sun, 5 Dec 2004 22:32:27 +0000 Subject: [PATCH] update to handle returning old version from put() function in JS and functional rm command for shell darcs-hash:20041205223227-2eb37-0de3f6e68ae8768e94680c9346ed6f74fb4a30b2.gz --- src/java/org/ibex/xt/Servlet.java | 20 +++--- src/java/org/ibex/xt/shell/Command.java | 102 +++++++++++++++++++++++++----- src/java/org/ibex/xt/shell/JSRemote.java | 95 +++++++++++++++++++++++++--- src/java/org/ibex/xt/shell/Servlet.java | 2 +- src/java/org/ibex/xt/shell/Shell.java | 24 +++++-- 5 files changed, 201 insertions(+), 42 deletions(-) diff --git a/src/java/org/ibex/xt/Servlet.java b/src/java/org/ibex/xt/Servlet.java index 71eedbb..78d1d91 100644 --- a/src/java/org/ibex/xt/Servlet.java +++ b/src/java/org/ibex/xt/Servlet.java @@ -96,13 +96,13 @@ public class Servlet extends HttpServlet { //#end return super.get(key); } - public void put(Object key, Object val) throws JSExn { + public Object put(Object key, Object val) throws JSExn { //#switch(JS.toString(key)) case "created": throw new JSExn("can not set session.created"); case "accessed": throw new JSExn("can not set session.accessed"); case "invalidate": throw new JSExn("can not set session.invalidate"); //#end - super.put(key, val); + return super.put(key, val); } public Object callMethod(Object method, final Object a, final Object b, Object c, Object[] rest, int nargs) throws JSExn { @@ -128,9 +128,10 @@ public class Servlet extends HttpServlet { private List keys = null; public Object get(Object key) { return request.getSession(true).getAttribute(JS.toString(key)); } - public void put(Object key, Object val) { + public Object put(Object key, Object val) { if (val == null) request.getSession(true).removeAttribute(JS.toString(key)); - else request.setAttribute(JS.toString(key), val); } + else request.setAttribute(JS.toString(key), val); + return null; } public Collection keys() { return keys == null ? keys = Collections.list(request.getSession(true).getAttributeNames()) : keys; } }; @@ -141,8 +142,8 @@ public class Servlet extends HttpServlet { return keys == null ? keys = Collections.list(request.getHeaderNames()) : keys; } }; private JS responseHeader = new JS() { - public void put(Object key, Object val) { - response.setHeader(JS.toString(key), JS.toString(val)); } + public Object put(Object key, Object val) { + response.setHeader(JS.toString(key), JS.toString(val)); return null; } }; @@ -150,8 +151,8 @@ public class Servlet extends HttpServlet { private class Sub extends JS { Object key; Sub(Object key) { this.key = key; } - public void put(Object key, Object val) throws JSExn { - Scope.this.put(JS.toString(this.key) + "." + JS.toString(key), val); } + public Object put(Object key, Object val) throws JSExn { + return Scope.this.put(JS.toString(this.key) + "." + JS.toString(key), val); } public Object get(Object key) throws JSExn { return Scope.this.get(JS.toString(this.key) + "." + JS.toString(key)); } public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn { @@ -206,13 +207,14 @@ public class Servlet extends HttpServlet { //#end return null; } - public void put(Object key, Object val) throws JSExn { + public Object put(Object key, Object val) throws JSExn { try { //#switch(JS.toString(key)) case "response.code": response.setStatus(JS.toInt(val)); case "response.redirect": response.sendRedirect(JS.toString(val)); case "response.contentType": response.setContentType(JS.toString(val)); //#end + return null; } catch (IOException e) { throw new JSExn(e); } diff --git a/src/java/org/ibex/xt/shell/Command.java b/src/java/org/ibex/xt/shell/Command.java index 48fb329..04ca7b1 100644 --- a/src/java/org/ibex/xt/shell/Command.java +++ b/src/java/org/ibex/xt/shell/Command.java @@ -1,5 +1,6 @@ package org.ibex.xt.shell; +import java.io.StringReader; import java.io.Writer; import java.io.IOException; @@ -39,14 +40,14 @@ public abstract class Command { w.write("\n"); } - public List fromShellPath(String s) throws Shell.NoSuchPathException { + public List fromShellPath(String s) throws Shell.BadPathException { 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 { + public List fromShellPath(String s, List l) throws Shell.BadPathException { if (s == null) return null; if (l == null) l = new ArrayList(); @@ -124,7 +125,7 @@ public abstract class Command { Object key = path.remove(path.size() - 1); Object po = shell.getFromPath(path.toArray()); if (po == null || !(po instanceof JS)) - throw new Shell.NoSuchPathException(); + throw new Shell.BadPathException(); JS cur = (JS)po; if (key instanceof String && @@ -145,7 +146,7 @@ public abstract class Command { w.write(key.toString()); w.write("\n"); } - } catch (Shell.NoSuchPathException e) { + } catch (Shell.BadPathException e) { w.write("error: no such path: "); w.write(p); w.write("\n"); @@ -159,6 +160,86 @@ public abstract class Command { } } + public static class Rm extends Command { + public Rm(Shell s) { super(s); } + public String name() { return "rm"; } + public String params() { return "[options] [path]"; } + public String summary() { return "Removes objects."; } + public String help() { return "Removes objects."; } // FIXME info + public void execute(Writer w, String[] c) throws IOException { + if (c.length == 1) { usage(w); return; } + + boolean force = false; // FIXME provide ability to set + + StringBuffer func = new StringBuffer(); + + for (int ic=1; ic < c.length; ic++) { + String p = c.length == 1 ? "*" : c[ic]; + if (p.endsWith("/")) p += "*"; + + try { + // get the base of the path + 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.BadPathException(); + JS cur = (JS)po; + + if (cur.containsKey(key)) { + Object o = cur.get(key); + if (!force && o != null && o instanceof JS && ((JS)o).keys().size() > 0) + throw new Shell.BadPathException("key is not empty"); + func.append("prevalent."); + for(int i=0; i < path.size(); i++) { + func.append(path.get(i)); func.append('.'); } + func.append("Delete(\""); + func.append(key.toString()); + func.append("\");\n"); + } else if (key instanceof String && ((String)key).indexOf('*') >= 0) { + String last = (String)key; + last = last.replaceAll("\\.", "\\."); + last = last.replaceAll("\\*", ".*"); + Pattern pat = Pattern.compile(last); + Collection curkeys = cur.keys(); + if (curkeys.size() == 0) throw new Shell.BadPathException(); + Iterator it = curkeys.iterator(); while (it.hasNext()) { + Object o = it.next(); + if (o == null || !(o instanceof String)) continue; + String s = (String)o; + if (!pat.matcher(s).matches()) continue; + + func.append("prevalent."); + for(int i=0; i < path.size(); i++) { + func.append(path.get(i)); func.append('.'); } + func.append("Delete(\""); + func.append(s); + func.append("\");\n"); + } + } else throw new Shell.BadPathException(); + + } catch (JSExn e) { + w.write("error: cannot remove '"); + w.write(p); + w.write("': "); + w.write(e.getMessage()); + w.write("\n"); + return; + } catch (Shell.BadPathException e) { + w.write("error: cannot remove '"); + w.write(p); + w.write("': "); + w.write(e.getMessage() != null ? e.getMessage() : "no such path"); + w.write("\n"); + return; + } + } + + shell.transaction(JS.fromReader( + "rm-transaction", 0, new StringReader(func.toString()))); + } + } + public static class Pwd extends Command { public Pwd(Shell s) { super(s); } public String name() { return "pwd"; } @@ -194,7 +275,7 @@ public abstract class Command { String path = c.length == 1 ? "/" : c[1]; try { shell.setPath(fromShellPath(path).toArray()); } - catch (Shell.NoSuchPathException e) { + catch (Shell.BadPathException e) { w.write("error: no such path: "); w.write(path); w.write("\n"); @@ -202,15 +283,4 @@ public abstract class Command { } } - public static class Rm extends Command { - public Rm(Shell s) { super(s); } - public String name() { return "rm"; } - 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) throws IOException { - if (c.length == 1) { usage(w); return; } - - } - } } diff --git a/src/java/org/ibex/xt/shell/JSRemote.java b/src/java/org/ibex/xt/shell/JSRemote.java index f7886d8..4b3e475 100644 --- a/src/java/org/ibex/xt/shell/JSRemote.java +++ b/src/java/org/ibex/xt/shell/JSRemote.java @@ -8,6 +8,9 @@ import org.ibex.js.*; import org.ibex.util.*; import org.ibex.util.Collections; +import org.ibex.xt.Prevalence; +import org.prevayler.*; + public class JSRemote extends JS { public static final int VERSION = 1; @@ -22,7 +25,7 @@ public class JSRemote extends JS { /** 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) + public static void receive(Prevayler p, JS root, ObjectInputStream in, ObjectOutputStream out) throws IOException { out.writeInt(VERSION); if (in.readInt() != VERSION) return; @@ -37,7 +40,7 @@ public class JSRemote extends JS { throw new IOException("unexpected class not found: " + e.getMessage()); } - r.execute(root, out); + r.execute(p, root, out); } /** Sends a request to the server. */ @@ -63,6 +66,28 @@ public class JSRemote extends JS { return in; } + /** FEATURE: It's questionable as to whether this belongs here. JSRemote shouldn't + * really know anything about prevayler, but that rquires another layer between + * this class and the http layer. Maybe not a bad idea. */ + public void transaction(final JS t) { + Object resp; + try { + resp = send(new Request(null) { + protected void execute() throws JSExn, IOException { + try { + prevayler.execute(new Prevalence.JSTransaction(t)); + out.writeObject(null); + } catch (Exception e) { e.printStackTrace(); out.writeObject(e); } + } + }).readObject(); + } catch (Exception e) { + throw new RuntimeException("transaction failed", e); + } + + if (resp != null && resp instanceof Exception) + throw new RuntimeException("transaction failed", (Exception)resp); + } + public Collection keys() { return keys == null ? keys = new Keys() : keys.update(); } public Object get(Object k) throws JSExn { @@ -82,6 +107,7 @@ public class JSRemote extends JS { Object o = in.readObject(); in.close(); if (o == null) return null; + else if (o instanceof JS) return new JSImmutable((JS)o); else if (o instanceof Exception) throw (Exception)o; else return o; } catch (JSExn e) { throw e; @@ -89,12 +115,12 @@ public class JSRemote extends JS { } // FIXME: unroll JSRemote if it is a value - public void put(Object k, final Object value) throws JSExn { - try { send(new Request(path, k) { + public Object put(Object k, final Object value) throws JSExn { + try { return send(new Request(path, k) { protected void execute() throws JSExn, IOException { - scope.put(key, value); + out.writeObject(scope.put(key, value)); } - }); } catch (Exception e) { throw new RuntimeException("JSRemote", e); } + }).readObject(); } catch (Exception e) { throw new RuntimeException("JSRemote", e); } } public Object remove(Object k) throws JSExn { @@ -105,7 +131,7 @@ public class JSRemote extends JS { }).readObject(); } catch (Exception e) { throw new RuntimeException("JSRemote", e); } } - public boolean containsKey(Object k) throws JSExn { + public boolean containsKey(Object k) { try { return send(new Request(path, k) { protected void execute() throws JSExn, IOException { out.writeBoolean(scope.containsKey(key)); @@ -134,6 +160,7 @@ public class JSRemote extends JS { // FIXME: map trap functions to server public static abstract class Request implements Serializable { + protected Prevayler prevayler; protected ObjectOutputStream out; protected JS scope; protected String path; @@ -157,7 +184,8 @@ public class JSRemote extends JS { this.path = path; } - public void execute(JS r, ObjectOutputStream o) throws IOException { + public void execute(Prevayler p, JS r, ObjectOutputStream o) throws IOException { + prevayler = p; out = o; scope = r; try { @@ -175,21 +203,66 @@ public class JSRemote extends JS { } execute(); } catch(JSExn e) { out.writeObject(e); } + prevayler = null; out = null; scope = null; } protected abstract void execute() throws JSExn, IOException; } + public static class JSImmutable extends JS { + private final JS wrapped; + public JSImmutable(JS toWrap) { wrapped = toWrap; } + + public Collection keys() throws JSExn { + return Collections.unmodifiableCollection(wrapped.keys()); } + public Object get(Object key) throws JSExn { return wrapped.get(key); } + public boolean containsKey(Object key) { return wrapped.containsKey(key); } + public Object call(Object a0, Object a1, Object a2, Object[] r, int n) + throws JSExn { return wrapped.call(a0, a1, a2, r, n); } + + public Object callMethod(Object m, Object a0, Object a1, Object a2, Object[] r, int n) + throws JSExn { throw new JSExn("immutable JS"); } + public Object put(Object k, Object v) throws JSExn { + throw new JSExn("immutable JS"); } + public Object remove(Object k) throws JSExn { + throw new JSExn("immutable JS"); } + public void putAndTriggerTraps(Object k, Object v) throws JSExn { + throw new JSExn("immutable JS"); } + public Object getAndTriggerTraps(Object k) throws JSExn { + throw new JSExn("immutable JS"); } + protected boolean isTrappable(Object n, boolean i) { return false; } + } + private class Keys extends AbstractCollection implements Serializable { + private transient boolean updating = false; private transient final List items = Collections.synchronizedList(new ArrayList()); - private int modCount = 0, size = -1; + private transient int modCount = 0; + + private int size = -1; - public Iterator iterator() { update(); return new KeyIt(); } public int size() { if (size == -1) update(); return size; } + public Iterator iterator() { updateList(); return new KeyIt(); } private synchronized Keys update() { + if (updating) return this; + 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()); + } + }); + size = in.readInt(); + } catch (Exception e) { throw new RuntimeException("JSRemote", e); } + + return this; + } + + private synchronized Keys updateList() { + if (updating) return this; + updating = true; modCount++; items.clear(); try { final ObjectInputStream in = send(new Request(path) { @@ -214,6 +287,8 @@ public class JSRemote extends JS { } catch (Exception e) { size = -1; items.clear(); throw new RuntimeException("JSRemote", e); + } finally { + synchronized (Keys.this) { Keys.this.updating = false; } } }}.start(); } catch (Exception e) { throw new RuntimeException("JSRemote", e); } diff --git a/src/java/org/ibex/xt/shell/Servlet.java b/src/java/org/ibex/xt/shell/Servlet.java index 70b89d9..fc4cbb4 100644 --- a/src/java/org/ibex/xt/shell/Servlet.java +++ b/src/java/org/ibex/xt/shell/Servlet.java @@ -24,7 +24,7 @@ public class Servlet extends HttpServlet { public void doPost(HttpServletRequest rq, HttpServletResponse rs) throws IOException { ObjectInputStream in = new ObjectInputStream(rq.getInputStream()); ObjectOutputStream out = new ObjectOutputStream(rs.getOutputStream()); - JSRemote.receive(prevalent, in, out); + JSRemote.receive(prevayler, 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 f677f28..da77e94 100644 --- a/src/java/org/ibex/xt/shell/Shell.java +++ b/src/java/org/ibex/xt/shell/Shell.java @@ -40,7 +40,7 @@ public class Shell { /** Returns the object represented by the given path, * ignoring the current shell path context.*/ - public Object getFromPath(Object[] path) throws NoSuchPathException, JSExn { + public Object getFromPath(Object[] path) throws BadPathException, JSExn { if (path.length == 0) return root; if (root instanceof JSRemote) { @@ -54,21 +54,30 @@ public class Shell { 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(); + if (o == null || !(o instanceof JS)) throw new Shell.BadPathException(); cur = (JS)o; } return cur.get(path[path.length - 1]); } } + public void transaction(JS t) { + if (root instanceof JSRemote) { + ((JSRemote)root).transaction( + JS.cloneWithNewParentScope(t, new JSScope(null))); + } else { + // FIXME JS.eval(JS.cloneWithNewParentScope(t, root)); + } + } + /** Set the current path of the shell, modifiying the result of getScope(). */ - public void setPath(Object[] s) throws NoSuchPathException { + public void setPath(Object[] s) throws BadPathException { 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(); + try { o = cur.get(s[i]); } catch (JSExn e) { throw new BadPathException(); } + if (o == null || !(o instanceof JS)) throw new BadPathException(); cur = (JS)o; } scope = cur; @@ -174,5 +183,8 @@ public class Shell { } } - public static class NoSuchPathException extends Exception {} + public static class BadPathException extends Exception { + public BadPathException() {} + public BadPathException(String msg) { super(msg); } + } } -- 1.7.10.4