public class Tree { public long insert(LeafNode ln, byte[] key, ...) { BottomNode bin = findBINForInsert(key, ...); long position = ln.log(key, ...); bin.updateEntry(ln, position, key); bin.clearKnownDeleted(); traceInsert(Level.FINER, bin, ln, position); //... } }
public class Tree { public long insert(LeafNode ln, byte[] key, ...) { BottomNode bin = findBINForInsert(key, ...); long position = ln.log(key, ...); bin.updateEntry(ln, position, key); bin.clearKnownDeleted(); hook(Level.FINER, bin, ln, position); //... } void hook(Level l, BottomNode b, LeafNode l, long p) { } }
public aspect TreeLogging { before(Level l, BottomNode bin, LeafNode ln, long pos): execution(void Tree.hook(...)) && args(l,bin,ln,pos) { traceInsert(l, bin, ln, pos) } }
joinpointtype Trace { Level logLevel; } joinpointtype TraceInsert extends Trace { BottomNode bin; LeafNode ln; long position; }
public class Tree exhibits TraceInsert { public long insert(LeafNode ln, byte[] key , ... ) { //... bin.clearKnownDeleted(); exhibit new TraceInsert(Level.FINER, bin, ln, position) {}; // ... } }
public aspect TreeLogging advises TraceInsert { after(TraceInsert t){ traceInsert(t.logLevel, t.bin, t.ln, t.position); } //... }
Note the empty block: there is no statement to be advised, only a point between two statements. before, after, and around all have the same effect.
Employing AspectJ's fine grained join point model, it is possible to advise join points that occur in the middle of a method (call, get, and set join points).
Consider the following Java example from Berkeley DB, where the updateMemorySize call belonging to the MemoryBudget Feature should be extracted into an aspect.
public class IN { public int insertEntry1(CR entry) { //... if (nEntries < entryTargets.length) { //... <strong>updateMemorySize(0, getInMemorySize(index)); adjustCursorsForInsert(index); //... } } }
public class IN { public int insertEntry1(CR entry) { //... if (nEntries < entryTargets.length) { //... hook(index); adjustCursorsForInsert(index); //... } } void hook(int index) {} } public aspect MemoryBudget { before(IN in, int index): execution(void IN.hook(int)) && this(in) && args(index) { in.updateMemorySize(0, in.getInMemorySize(index)); } }Second, we can use AspectJ's fine grained join point model to "emulate" a statement extension. Note how this hides the semantics of an aspect and is extremely sensitive to change.
public aspect MemoryBudget { before(IN in, int index): call(void IN.adjustCursorsForInsert(int)) && this(in) && args(index) && withincode(int IN.insertEntry1(CR)) { in.updateMemorySize(0, in.getInMemorySize(index)); } }
joinpointtype INMemoryUpdate { IN internalNode; int index; } public class IN exhibits INMemoryUpdate { public int insertEntry1(CR entry) { // ... if (nEntries < entryTargets.length) { // ... exhibit new INMemoryUpdate(this, index) { adjustCursorsForInsert(index); }; // ... } } aspect MemoryBudget advises INMemoryUpdate { before(INMemoryUpdate m) { m.internalNode.updateMemorySize(0, m.internalNode.getInMemorySize(m.index)); } }
public class IN { public boolean deleteEntry(int index, boolean maybeValidate) throws DatabaseException { if (nEntries == 0) { return false; } /* Check the subtree validation only if maybeValidate is true. */ assert maybeValidate ? validateSubtreeBeforeDelete(index) : true; if (index < nEntries) { int oldLSNArraySize = computeLsnOverhead(); /* LSNArray.setElement can mutate to an array of longs. */ for (int i = index; i < nEntries - 1; i++) { setEntryInternal(i + 1, i); } clearEntry(nEntries - 1); updateMemorySize(oldLSNArraySize, computeLsnOverhead()); nEntries--; setDirty(true); setProhibitNextDelta(); /* * Note that we don't have to adjust cursors for delete, since there * should be nothing pointing at this record. */ traceDelete(Level.FINEST, index); return true; } else { return false; } } //... }
public class IN { public boolean deleteEntry(int index) { if (nEntries == 0 || index >= nEntries) return false; hookr_deleteEntryInternal(index); nEntries--; setDirty(true); return true; public class IN {} private void hookr_deleteEntryInternal(int index) { for (int i = index; i < nEntries - 1; i++) setEntryInternal(i + 1, i); clearEntry(nEntries - 1); } //... }
aspect MemoryBudget { void around(IN in, int index) : execution(void IN.hookr_deleteEntryInternal(int)) && this(in) && args(index) { in.updateMemorySize(in.getEntryInMemorySize(index), 0); int oldSize = in.computeLsnOverhead(); proceed(in); in.changeMemorySize(in.computeLsnOverhead() - oldSize); } }
joinpointtype OverheadSizeChanged { IN in; int index; }
public class IN exhibits OverheadSizeChanged { public boolean deleteEntry(int index) { if (nEntries == 0 || index >= nEntries) return false; exhibit new OverheadSizeChanged(this, index) { for (int i = index; i < nEntries - 1; i++) setEntryInternal(i + 1, i); clearEntry(nEntries - 1); }; nEntries--; setDirty(true); return true; } }
aspect MemoryBudget advises OverheadSizeChanged{ void around(OverheadSizeChanged o){ o.in.updateMemorySize(in.getEntryInMemorySize(o.index), 0); int oldSize = o.in.computeLsnOverhead(); proceed(); o.in.changeMemorySize(o.in.computeLsnOverhead() - oldSize); } }
Back to main page