//#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 {
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; }
};
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; }
};
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 {
//#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);
}
package org.ibex.xt.shell;
+import java.io.StringReader;
import java.io.Writer;
import java.io.IOException;
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();
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 &&
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");
}
}
+ 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"; }
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");
}
}
- 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; }
-
- }
- }
}
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;
/** 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)
+ public static void receive(Prevayler p, JS root, ObjectInputStream in, ObjectOutputStream out)
throws IOException {
out.writeInt(VERSION); if (in.readInt() != VERSION) return;
throw new IOException("unexpected class not found: " + e.getMessage());
}
- r.execute(root, out);
+ r.execute(p, root, out);
}
/** Sends a request to the server. */
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 {
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;
}
// 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 {
}).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));
// 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;
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 {
}
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) {
} 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); }
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();
}
}
/** 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) {
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;
}
}
- public static class NoSuchPathException extends Exception {}
+ public static class BadPathException extends Exception {
+ public BadPathException() {}
+ public BadPathException(String msg) { super(msg); }
+ }
}