Browse Source

dirty example adapted from May user email

tags/V1_1_1
wisberg 21 years ago
parent
commit
df36570fd7
1 changed files with 191 additions and 0 deletions
  1. 191
    0
      docs/sandbox/common/caching/WatchSetters.java

+ 191
- 0
docs/sandbox/common/caching/WatchSetters.java View File

@@ -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();
}
}

Loading…
Cancel
Save