Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [aspectj-users] An unclarity in prog guide about precedences and a problem in using perthis

>   Now, if I want A to dominate B, do I simply say
> declare precedence : A, B; 

Yes, that's correct. Does it help with the super-aspect/sub-aspect thing 
if I say "all advice from an instance of A has precedence over advice from 
an instance of B?". For any combination of before and around advice, the 
advice with the highest precedence gets to run first. For after advice the 
advice with the highest precedence gets to run last.

And for the second question...

>   This unit test passes fine if I use the association
> perthis(this(CacheTestHelper)) that I have commented out in the test
> helper class. However, I also tried associating this aspect with
> perthis(cachedOperations()) and then the test fails. This is consistent
> as that is the association the aspect inherits from its abstract parent
> aspect.

With the caveat that I haven't run your code, here's what I see going on: 
the difference between the perthis statement in the abstract aspect 
(perthis(cachedOperation())  and the perthis statement in the subaspect 
(perthis(this(CacheTestHelper)) is that the latter will match any join 
point where 'this' is bound to an instance of CacheTestHelper, but the 
former only matches "cachedOperation()" join points where "this" is bound 
to an instance of CacheTestHelper. Now cachedOperation is defined in the 
concrete aspect to be simply the execution of methodToBeCached, so I was 
looking for a join point other than this execution that will make the test 
pass in the latter case but not in the former. My guess is that your 
testEqualsCaching() test fails because in this test you do encounter a 
join point where 'this' is a CacheTestHelper, but not a join point for the 
execution of methodToBeCached.

A long winded way of saying that if you change the cachedOperation() 
pointcut definition in the concrete aspect to:

pointcut cachedOperation() :  execution(* methodToBeCached(..)) || 
execution(* equals(..))
   && this(CacheTestHelper);

then I think the test will start passing again.

-- Adrian
Adrian_Colyer@xxxxxxxxxx



"Antti Karanta" <antti.karanta@xxxxxxx> 
Sent by: aspectj-users-admin@xxxxxxxxxxx
13/09/2004 12:48
Please respond to
aspectj-users@xxxxxxxxxxx


To
<aspectj-users@xxxxxxxxxxx>
cc

Subject
[aspectj-users] An unclarity in prog guide about precedences and a problem 
in using perthis








                   Hi!

  I had some difficulty figuring out what declare precedence does in a
specific case. Here is a rough sketch of what I have:


abstract aspect AbstractA {

  abstract protected pointcut aPointcut();

  around() : aPointcut() {
    // do something, call proceed
  }

}

aspect A extends AbstractA {
  protected pointcut aPointcut() : // some definition here
}

aspect B {
  // some advice here that binds to (at least some of) the same join
points as A
}


  Now, if I want A to dominate B, do I simply say
declare precedence : A, B; 
  ?

  This is a little unclear to interpret from the programming guide, as
it says: 
"If aspect A is matched earlier than aspect B in some declare precedence
form, then all advice in concrete aspect A has precedence over all
advice in concrete aspect B when they are on the same join point."
http://dev.eclipse.org/viewcvs/indextech.cgi/~checkout~/aspectj-home/doc
/progguide/semantics-advice.html#d0e5915

  Now, the advice is not defined in the concrete aspect A but in its
abstract super aspect AbstractA. 

  I tried it out and it seems the in relation to the above quotation on
precedence, the advice in parent aspect are considered to be advice in
the inheriting concrete aspect. I.e. the above declare precedence clause
causes the advice inherited from AbstractA to A to dominate advice in B.


  I think the programming guide requires a little clarification here. Or
maybe I missed something there?

///////////////////////////////////////

  Another thing: In the programming guide it says:
"The criteria used to determine how an aspect is instantiated is
inherited from its parent aspect."
http://dev.eclipse.org/viewcvs/indextech.cgi/~checkout~/aspectj-home/doc
/progguide/semantics-aspects.html#d0e6619

  Now here's what I have:

  First, a rather simple aspect to cache the values of heavy
calculations: 

import java.util.Map;
import java.util.HashMap;

import org.aspectj.lang.Signature;

// NOTE THE perthis ASSOCIATION: 
public abstract aspect CachingAspect perthis(cachedOperations()) {
 
  private Map _cachedReturnValues = new HashMap();
  private Map _cachedEqualities   = new HashMap();
 
  protected pointcut mutatorOperations();
  abstract protected pointcut cachedOperations();

  protected pointcut equalsOperation(Object other);
 
  // after returning advice to mutator operations that invalidates the
cache 
  after() returning: mutatorOperations() {
    _cachedReturnValues.clear();
    _cachedEqualities.clear();
  }
 
  // around advice to cached operations that checks if the value is 
  // already in the cache. If not, calculate it.
  Object around(): cachedOperations() {
    Signature method = thisJoinPointStaticPart.getSignature();
    if(_cachedReturnValues.containsKey(method))
      return _cachedReturnValues.get(method);
    Object retVal = proceed();
    _cachedReturnValues.put(method, retVal);
    return retVal;
                   }

  boolean around(Object other) : equalsOperation(other) {
    if(_cachedEqualities.containsKey(other))
      // simplify this once autoboxing is available
      return ((Boolean)_cachedEqualities.get(other)).booleanValue();
    boolean isEqual = proceed(other);
    _cachedEqualities.put(other, Boolean.valueOf(isEqual));
    return isEqual;
  }
 
}

  Here's the unit test (that fails):

import junit.framework.Assert;
import junit.framework.TestCase;


public class CachingAspectTest extends TestCase {
 
  private CacheTestHelper _helper = new CacheTestHelper();

  private CacheTestHelper _helper;
 
  protected void setUp() throws Exception {
    super.setUp();
    _helper = new CacheTestHelper();
  }
 
  public void testReturnValueCaching() {
    int valueReturned = _helper.methodToBeCached();
    Assert.assertEquals(1, _helper._numberOfTimesMethodHasBeenExecuted);
    Assert.assertEquals(valueReturned, _helper.methodToBeCached());
    Assert.assertEquals(1, _helper._numberOfTimesMethodHasBeenExecuted);
    _helper.mutatorOperation();
    valueReturned = _helper.methodToBeCached();
    Assert.assertEquals(2, _helper._numberOfTimesMethodHasBeenExecuted);
    Assert.assertEquals(valueReturned, _helper.methodToBeCached());
    Assert.assertEquals(2, _helper._numberOfTimesMethodHasBeenExecuted);

  }
 
  public void testEqualsCaching() {
    CacheTestHelper other1 = null;
    CacheTestHelper other2 = new CacheTestHelper();
    CacheTestHelper other3 = new CacheTestHelper();
    other2.mutatorOperation(); // make this unequal to the _helper

    Assert.assertFalse(_helper.equals(other1));
    Assert.assertFalse(_helper.equals(other2));
    Assert.assertTrue( _helper.equals(other3));
    Assert.assertEquals(3, _helper._numberOfTimesEqualsHasBeenExecuted);
    // now the return values should come from the cache
    Assert.assertFalse(_helper.equals(other1));
    Assert.assertFalse(_helper.equals(other2));
    Assert.assertTrue( _helper.equals(other3));
    Assert.assertEquals(3, _helper._numberOfTimesEqualsHasBeenExecuted);

    _helper.mutatorOperation();
    Assert.assertFalse(_helper.equals(other1));
    Assert.assertTrue( _helper.equals(other2));
    Assert.assertFalse(_helper.equals(other3));
    Assert.assertEquals(6, _helper._numberOfTimesEqualsHasBeenExecuted);
    // check that the caching still works
    Assert.assertFalse(_helper.equals(other1));
    Assert.assertTrue( _helper.equals(other2));
    Assert.assertFalse(_helper.equals(other3));
    Assert.assertFalse(_helper.equals(other1));
    Assert.assertTrue( _helper.equals(other2));
    Assert.assertFalse(_helper.equals(other3));
    Assert.assertEquals(6, _helper._numberOfTimesEqualsHasBeenExecuted);
 
    // check that the cache is really per object
    Assert.assertFalse(other2.equals(other1));
    Assert.assertTrue( other2.equals(other2));
    Assert.assertFalse(other2.equals(other3));
    Assert.assertTrue( other2.equals(_helper));
    Assert.assertEquals(4, other2._numberOfTimesEqualsHasBeenExecuted);
    Assert.assertFalse(other2.equals(other1));
    Assert.assertTrue( other2.equals(other2));
    Assert.assertFalse(other2.equals(other3));
    Assert.assertTrue( other2.equals(_helper));
    Assert.assertEquals(4, other2._numberOfTimesEqualsHasBeenExecuted);
    Assert.assertEquals(6, _helper._numberOfTimesEqualsHasBeenExecuted);
 
  }
 
}

class CacheTestHelper {
 
  int _numberOfTimesMethodHasBeenExecuted = 0;
  int _numberOfTimesEqualsHasBeenExecuted = 0;
  int _currentReturnValue               = 0;
 
  int methodToBeCached() {
    _numberOfTimesMethodHasBeenExecuted++;
    return _currentReturnValue;
  }
 
  void mutatorOperation() {
    _currentReturnValue++;
  }
 
  public boolean equals(Object other) {
    _numberOfTimesEqualsHasBeenExecuted++;
    if(other == null || !(other instanceof CacheTestHelper)) return
false;
    return this == other || 
            ((CacheTestHelper)other)._currentReturnValue ==
_currentReturnValue;
  }

  private static aspect Cache extends CachingAspect { //
perthis(this(CacheTestHelper))
    protected pointcut mutatorOperations() : execution(void
CacheTestHelper.mutatorOperation());
    protected pointcut cachedOperations() :  execution(int
CacheTestHelper.methodToBeCached());
    protected pointcut equalsOperation(Object other) :
      execution(boolean CacheTestHelper.equals(Object)) && args(other);
  }
 
}

  This unit test passes fine if I use the association
perthis(this(CacheTestHelper)) that I have commented out in the test
helper class. However, I also tried associating this aspect with
perthis(cachedOperations()) and then the test fails. This is consistent
as that is the association the aspect inherits from its abstract parent
aspect.

  So, the fault is propably not in the compiler but in my limited
understanding: why doesn't the per object instantiation
perthis(cachedOperations()) associate one aspect instance per one object
whose return values are to be cached? What am I missing here?
  Is this an issue with aspect inheritance or perthis in general?


        -Antti-



_______________________________________________
aspectj-users mailing list
aspectj-users@xxxxxxxxxxx
http://dev.eclipse.org/mailman/listinfo/aspectj-users




Back to the top