X-Git-Url: http://git.megacz.com/?p=org.ibex.xt-crawshaw.git;a=blobdiff_plain;f=src%2Fjava%2Forg%2Fibex%2Fxt%2Fshell%2FJSRemote.java;fp=src%2Fjava%2Forg%2Fibex%2Fxt%2Fshell%2FJSRemote.java;h=4b3e47584b209c0b128fcd9ba77bbcd6d2c881f4;hp=f7886d83a3db06e565275495f38f9edf0bba7686;hb=7959860be8bb0ac8aa172e1de8bb140b65afdae6;hpb=e2e46233d9db6fe8728421016a41d5bf79db86e5 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); }