]> source.dussan.org Git - aspectj.git/commitdiff
dirty example adapted from May user email
authorwisberg <wisberg>
Sun, 7 Sep 2003 00:01:00 +0000 (00:01 +0000)
committerwisberg <wisberg>
Sun, 7 Sep 2003 00:01:00 +0000 (00:01 +0000)
docs/sandbox/common/caching/WatchSetters.java [new file with mode: 0644]

diff --git a/docs/sandbox/common/caching/WatchSetters.java b/docs/sandbox/common/caching/WatchSetters.java
new file mode 100644 (file)
index 0000000..fa280f5
--- /dev/null
@@ -0,0 +1,191 @@
+//@author Ricardo Giacomin, Wes Isberg
+//XXX author n/a at old address for explicit authorization
+
+
+//START-SAMPLE caching-dirty-reflectiveSetters Use getter/setter pattern to track dirtiness
+package caching;
+
+import java.lang.reflect.Method;
+
+/**
+ * Watch setters to skip if new value is same as old
+ * or to set a dirty flag otherwise.
+ * Clients classes opt-in by implementing IWatched,
+ * and anyone can read the dirty and dirty-valid flags.
+ * <pre>
+ * class Foo implements WatchSetters.IWatched {
+ *    ...
+ * }
+ * Foo foo = new Foo();
+ * ...
+ * if (!foo.isDirtyValid() || foo.isDirty()) {
+ *    foo.write();
+ * } 
+ * </pre>
+ * 
+ * (Initial draft was sent to aspectj-users@eclipse.org by
+ * Ricardo on 5/13/2003 
+ * (http://dev.eclipse.org/mhonarc/lists/aspectj-users/msg00482.html)
+ * but his email fails now, so we
+ * did not get explicit authorization to post.)
+ * 
+ * @author Ricardo Giacomin, Wes Isberg
+ */
+public aspect WatchSetters {
+    // just to invoke test code below
+    public static void main(String[] args) {
+        Client.handleTimer(new Timer());
+    }
+
+    private static final Class[] GETTER_ARG_TYPES = new Class[]{};
+    private static final Object[] GETTER_ARGS = new Object[]{};
+    private static final Object NONE = new Object();
+
+    /** maintain dirty flag for any IWatched */
+    public interface IWatched {}
+
+    /** true if new value sent to any setter */
+    private boolean IWatched.dirty;
+
+    /** false if unable to maintain dirty b/c no privileges, no getter... */
+    private boolean IWatched.dirtyValid = true;
+
+    /** clients can use dirty flag */
+    public boolean IWatched.isDirty() {
+        return dirty;
+    }
+
+    /** clients handle case when dirty flag is invalid */
+    public boolean IWatched.isDirtyValid() {
+        return dirtyValid;
+    }
+    
+    /** Setters are instance methods returning void,
+     * prefixed "set..." and taking exactly 1 argument.
+     * Does not use args(id), since that requires the
+     * argument be non-null.
+     */
+    public pointcut setters(IWatched watched) : target(watched)
+        && execution(void IWatched+.set*(*)); // advice uses args[0]
+
+    /**
+     * Skip setter if arg is same as current value;
+     * otherwise, set dirty flag after proceeding with setter.
+     * Skip this advice if we tried it but failed because
+     * there wasn't a corresponding setter, we didn't
+     * have the right security permissions, etc.
+     */
+    void around(IWatched watched) : setters(watched) 
+            && if(watched.dirtyValid) {
+        // get value by invoking getter method
+        Object value = NONE;
+        try {
+            String getterName = "g" +
+                thisJoinPoint.getSignature().getName().substring(1);
+            Method method = watched.getClass()
+                .getMethod(getterName, GETTER_ARG_TYPES);
+            value = method.invoke(watched, GETTER_ARGS);
+        } catch (Throwable t) {
+            // NoSuchMethodException, SecurityException, 
+            // InvocationTargetException...
+        }
+        if (NONE == value) {
+            watched.dirtyValid = false;
+            proceed(watched);
+            return;
+        }
+
+        // compare value with arg being set - pointcut has exactly 1 parm
+        Object arg = thisJoinPoint.getArgs()[0];
+        if (!(null == arg ? value == null : arg.equals(value))) {
+            proceed(watched);
+            watched.dirty = true;
+        }
+    }
+}
+
+// ----------- sample clients of WatchSetter
+// classes may opt in - can also use aspects to declare.
+class Timer implements WatchSetters.IWatched {
+    private static int ID;
+    public final int id = ++ID;
+    private int counter;
+    public int getCounter() { 
+        return counter;
+    }
+    public void setCounter(int i) { 
+        counter = i;
+    }
+    public void write() {
+        System.out.println("writing " + this);
+    }
+    public String toString() {
+        return "Timer " + id + "==" + counter;  
+    }
+}
+
+// clients can use dirty flag directly
+class Client {
+   static void handleTimer(Timer timer) {
+       timer.setCounter(0); // should result in no write
+       if (!timer.isDirtyValid() || timer.isDirty()) {
+           timer.write();
+       }
+       timer.setCounter(2);
+       if (!timer.isDirtyValid() || timer.isDirty()) {
+           timer.write();
+       }
+   }
+}
+// ---- aspects use dirty to implement cache, etc.
+// Volatile things are flushed when dirty
+abstract aspect Volatile {
+    // subaspects declare targets using Volatile.ITag
+    protected interface ITag {}
+    declare precedence : Volatile+, WatchSetters;
+    after(WatchSetters.IWatched watched) returning : 
+            WatchSetters.setters(watched) {
+        if (!watched.isDirtyValid() || watched.isDirty()) {
+            flushCache(watched);
+        }
+    }
+    abstract void flushCache(Object o);
+}
+
+// treat Timer as volatile, write when flushing
+aspect VolatileTimer extends Volatile {
+    declare parents: Timer implements ITag;
+    void flushCache(Object o) {
+        Timer timer = (Timer) o;
+        timer.write();
+    }
+}
+
+//END-SAMPLE caching-dirty-reflectiveSetters
+
+aspect Testing {
+
+    void signal(String s) {
+        org.aspectj.testing.Tester.event(s);
+    }
+    
+    static {
+        org.aspectj.testing.Tester.expectEvent("client-write");
+        org.aspectj.testing.Tester.expectEvent("volatile-write");
+    }
+
+    before() : withincode(void VolatileTimer.flushCache(Object)) 
+        && call(void Timer.write()) {
+        signal("volatile-write");
+    }
+
+    before() : withincode(void Client.handleTimer(Timer)) 
+        && call(void Timer.write()) {
+        signal("client-write");
+    }
+
+    after() returning : execution(void WatchSetters.main(String[])) {
+        org.aspectj.testing.Tester.checkAllEvents();
+    }
+}
\ No newline at end of file