You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

WatchSetters.java 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. //@author Ricardo Giacomin, Wes Isberg
  2. //XXX author n/a at old address for explicit authorization
  3. //START-SAMPLE caching-dirty-reflectiveSetters Use getter/setter pattern to track dirtiness
  4. package caching;
  5. import java.lang.reflect.Method;
  6. /**
  7. * Watch setters to skip if new value is same as old
  8. * or to set a dirty flag otherwise.
  9. * Clients classes opt-in by implementing IWatched,
  10. * and anyone can read the dirty and dirty-valid flags.
  11. * <pre>
  12. * class Foo implements WatchSetters.IWatched {
  13. * ...
  14. * }
  15. * Foo foo = new Foo();
  16. * ...
  17. * if (!foo.isDirtyValid() || foo.isDirty()) {
  18. * foo.write();
  19. * }
  20. * </pre>
  21. *
  22. * (Initial draft was sent to aspectj-users@eclipse.org by
  23. * Ricardo on 5/13/2003
  24. * (https://dev.eclipse.org/mhonarc/lists/aspectj-users/msg00482.html)
  25. * but his email fails now, so we
  26. * did not get explicit authorization to post.)
  27. *
  28. * @author Ricardo Giacomin, Wes Isberg
  29. */
  30. public aspect WatchSetters {
  31. // just to invoke test code below
  32. public static void main(String[] args) {
  33. Client.handleTimer(new Timer());
  34. }
  35. private static final Class[] GETTER_ARG_TYPES = new Class[]{};
  36. private static final Object[] GETTER_ARGS = new Object[]{};
  37. private static final Object NONE = new Object();
  38. /** maintain dirty flag for any IWatched */
  39. public interface IWatched {}
  40. /** true if new value sent to any setter */
  41. private boolean IWatched.dirty;
  42. /** false if unable to maintain dirty b/c no privileges, no getter... */
  43. private boolean IWatched.dirtyValid = true;
  44. /** clients can use dirty flag */
  45. public boolean IWatched.isDirty() {
  46. return dirty;
  47. }
  48. /** clients handle case when dirty flag is invalid */
  49. public boolean IWatched.isDirtyValid() {
  50. return dirtyValid;
  51. }
  52. /** Setters are instance methods returning void,
  53. * prefixed "set..." and taking exactly 1 argument.
  54. * Does not use args(id), since that requires the
  55. * argument be non-null.
  56. */
  57. public pointcut setters(IWatched watched) : target(watched)
  58. && execution(void IWatched+.set*(*)); // advice uses args[0]
  59. /**
  60. * Skip setter if arg is same as current value;
  61. * otherwise, set dirty flag after proceeding with setter.
  62. * Skip this advice if we tried it but failed because
  63. * there wasn't a corresponding setter, we didn't
  64. * have the right security permissions, etc.
  65. */
  66. void around(IWatched watched) : setters(watched)
  67. && if(watched.dirtyValid) {
  68. // get value by invoking getter method
  69. Object value = NONE;
  70. try {
  71. String getterName = "g" +
  72. thisJoinPoint.getSignature().getName().substring(1);
  73. Method method = watched.getClass()
  74. .getMethod(getterName, GETTER_ARG_TYPES);
  75. value = method.invoke(watched, GETTER_ARGS);
  76. } catch (Throwable t) {
  77. // NoSuchMethodException, SecurityException,
  78. // InvocationTargetException...
  79. }
  80. if (NONE == value) {
  81. watched.dirtyValid = false;
  82. proceed(watched);
  83. return;
  84. }
  85. // compare value with arg being set - pointcut has exactly 1 parm
  86. Object arg = thisJoinPoint.getArgs()[0];
  87. if (!(null == arg ? value == null : arg.equals(value))) {
  88. proceed(watched);
  89. watched.dirty = true;
  90. }
  91. }
  92. }
  93. // ----------- sample clients of WatchSetter
  94. // classes may opt in - can also use aspects to declare.
  95. class Timer implements WatchSetters.IWatched {
  96. private static int ID;
  97. public final int id = ++ID;
  98. private int counter;
  99. public int getCounter() {
  100. return counter;
  101. }
  102. public void setCounter(int i) {
  103. counter = i;
  104. }
  105. public void write() {
  106. System.out.println("writing " + this);
  107. }
  108. public String toString() {
  109. return "Timer " + id + "==" + counter;
  110. }
  111. }
  112. // clients can use dirty flag directly
  113. class Client {
  114. static void handleTimer(Timer timer) {
  115. timer.setCounter(0); // should result in no write
  116. if (!timer.isDirtyValid() || timer.isDirty()) {
  117. timer.write();
  118. }
  119. timer.setCounter(2);
  120. if (!timer.isDirtyValid() || timer.isDirty()) {
  121. timer.write();
  122. }
  123. }
  124. }
  125. // ---- aspects use dirty to implement cache, etc.
  126. // Volatile things are flushed when dirty
  127. abstract aspect Volatile {
  128. // subaspects declare targets using Volatile.ITag
  129. protected interface ITag {}
  130. declare precedence : Volatile+, WatchSetters;
  131. after(WatchSetters.IWatched watched) returning :
  132. WatchSetters.setters(watched) {
  133. if (!watched.isDirtyValid() || watched.isDirty()) {
  134. flushCache(watched);
  135. }
  136. }
  137. abstract void flushCache(Object o);
  138. }
  139. // treat Timer as volatile, write when flushing
  140. aspect VolatileTimer extends Volatile {
  141. declare parents: Timer implements ITag;
  142. void flushCache(Object o) {
  143. Timer timer = (Timer) o;
  144. timer.write();
  145. }
  146. }
  147. //END-SAMPLE caching-dirty-reflectiveSetters
  148. aspect Testing {
  149. void signal(String s) {
  150. org.aspectj.testing.Tester.event(s);
  151. }
  152. static {
  153. org.aspectj.testing.Tester.expectEvent("client-write");
  154. org.aspectj.testing.Tester.expectEvent("volatile-write");
  155. }
  156. before() : withincode(void VolatileTimer.flushCache(Object))
  157. && call(void Timer.write()) {
  158. signal("volatile-write");
  159. }
  160. before() : withincode(void Client.handleTimer(Timer))
  161. && call(void Timer.write()) {
  162. signal("client-write");
  163. }
  164. after() returning : execution(void WatchSetters.main(String[])) {
  165. org.aspectj.testing.Tester.checkAllEvents();
  166. }
  167. }