package tracematches; import java.util.*; public class LeakingSync { static aspect SyncCheck{ /* * Those tracechecks check that one does not directly access a Map or Collection * that has been put into Collections.synchronized*(..). */ //most common supertype of Map anc Collection is Object unfortunately pointcut createSync(Object toSync): call(* Collections.synchr*(..)) && args(toSync); //tracematch for intercepting calls from a caller which is //not the synchronized map/collection instance tracematch(Object orig, Object sync, Object caller) { sym sync after returning(sync): createSync(orig); sym asyncCall before: (call(* Collection+.*(..)) || call(* Map+.*(..))) && target(orig) && this(caller); sync asyncCall { if(caller!=sync) { if(System.getProperty("TMTEST_ACTIVE")!=null) { throw new IllegalStateException("Collection "+System.identityHashCode(orig)+ " was synchronized. Use instance "+System.identityHashCode(sync)+" instead!"); } } } } //tracematch for intercepting calls from a static context tracematch(Object orig, Object sync) { sym sync after returning(sync): createSync(orig); sym asyncStaticCall before: (call(* Collection+.*(..)) || call(* Map+.*(..))) && target(orig) && !this(Object); sync asyncStaticCall { if(System.getProperty("TMTEST_ACTIVE")!=null) { throw new IllegalStateException("Collection "+System.identityHashCode(orig)+ " was synchronized. Use instance "+System.identityHashCode(sync)+" instead!"); } } } } public static void main(String[] args) { final Collection c = new ArrayList(); Collection sc = Collections.synchronizedCollection(c); //this is an ok call sc.add("bla"); System.err.println("added bla"); //illegal call from static context c.clear(); new Runnable() { public void run() { //illegal call from an instance context c.clear(); } }.run(); //safe final Collection c2 = new ArrayList(); c2.add("bla"); Collection sc2 = Collections.synchronizedCollection(c2); sc2.add("bla"); } }