A simple (untested, use at your own risk) solution that could work was to maintain a counter for each method in the stream:
private static final ConcurrentHashMap<String, ConcurrentHashMap<Long, AtomicInteger>> COUNTERS = new ConcurrentHashMap<>(); public static int getInvocationId(String methodName, long threadId) { return counter(methodName, threadId).getAndIncrement(); } private static AtomicInteger counter(String methodName, long threadId) { ConcurrentHashMap<Long, AtomicInteger> map = countersForMethodName(methodName); AtomicInteger counter = map.get(threadId); if (counter == null) { AtomicInteger newCounter = new AtomicInteger(); counter = map.putIfAbsent(threadId, newCounter); if (counter == null) { return newCounter; } } return counter; } private static ConcurrentHashMap<Long, AtomicInteger> countersForMethodName( String methodName) { ConcurrentHashMap<Long, AtomicInteger> map = COUNTERS.get(methodName); if (map == null) { ConcurrentHashMap<Long, AtomicInteger> newMap = new ConcurrentHashMap<>(); map = COUNTERS.putIfAbsent(methodName, newMap); if (map == null) { return newMap; } } return map; }
Then in your advice something like:
int invocationId = getInvocationId(thisJoinPoint.getSignature().getName(), Thread.currentThread().getId());
Note that this depends on the recommendations being executed in the same thread as the target method. Unfortunately, I am not sufficiently aware that AspectJ knows whether this assumption will always remain true.
CAVEAT: If your environment creates and expires new threads all the time, then the above tree will continue to grow (essentially a memory leak). If this is a problem, then you will need to add another code to periodically list all active threads and trim expired entries from the tree. In this case, you can use the map identifier for each stream and then the name for each method to make cropping more efficient.
source share