mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-11 23:00:59 +00:00
apply competitor listener pattern to leaderboard caching infrastructure; competitor change will invalidate leaderboard cache
This commit is contained in:
1 parent
dc688e857e
commit
48cb36ab60
1 file changed
+77
-10
+77
-10
@@ -7,11 +7,15 @@ import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.CompetitorChangeListener;
|
||||
import com.sap.sailing.domain.base.Fleet;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.Nationality;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.base.RaceColumnListener;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.base.WithNationality;
|
||||
import com.sap.sailing.domain.common.Color;
|
||||
import com.sap.sailing.domain.common.MaxPointsReason;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
@@ -39,7 +43,8 @@ import com.sap.sailing.util.impl.NamedReentrantReadWriteLock;
|
||||
*/
|
||||
public class LeaderboardCacheManager {
|
||||
private final WeakHashMap<Leaderboard, CacheInvalidationUponScoreCorrectionListener> scoreCorrectionListeners;
|
||||
private final NamedReentrantReadWriteLock scoreCorrectionListenersLock;
|
||||
private final NamedReentrantReadWriteLock scoreCorrectionAndCompetitorChangeListenersLock;
|
||||
private final WeakHashMap<Leaderboard, CacheInvalidationUponCompetitorChangeListener> competitorChangeListeners;
|
||||
private final WeakHashMap<Leaderboard, Map<TrackedRace, Set<CacheInvalidationListener>>> invalidationListenersPerLeaderboard;
|
||||
private final WeakHashMap<Leaderboard, RaceColumnListener> raceColumnListeners;
|
||||
|
||||
@@ -113,6 +118,53 @@ public class LeaderboardCacheManager {
|
||||
}
|
||||
}
|
||||
|
||||
private class CacheInvalidationUponCompetitorChangeListener implements CompetitorChangeListener {
|
||||
private static final long serialVersionUID = 8308312509904366143L;
|
||||
private final Leaderboard leaderboard;
|
||||
private final Set<Competitor> observedCompetitors;
|
||||
|
||||
public CacheInvalidationUponCompetitorChangeListener(Leaderboard leaderboard) {
|
||||
this.leaderboard = leaderboard;
|
||||
observedCompetitors = new HashSet<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sailIdChanged(String oldSailId, String newSailId) {
|
||||
removeFromCache(leaderboard);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nationalityChanged(WithNationality what, Nationality oldNationality, Nationality newNationality) {
|
||||
removeFromCache(leaderboard);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void colorChanged(Color oldColor, Color newColor) {
|
||||
removeFromCache(leaderboard);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nameChanged(String oldName, String newName) {
|
||||
removeFromCache(leaderboard);
|
||||
}
|
||||
|
||||
public synchronized void updateCompetitorListeners() {
|
||||
Set<Competitor> competitorsToStopObserving = new HashSet<>(observedCompetitors);
|
||||
for (Competitor competitor : leaderboard.getCompetitors()) {
|
||||
if (!observedCompetitors.contains(competitor)) {
|
||||
competitor.addCompetitorChangeListener(this); // start observing competitor
|
||||
} else {
|
||||
competitorsToStopObserving.remove(competitor); // keep observing competitor
|
||||
}
|
||||
}
|
||||
for (Competitor competitorToStopObserving : competitorsToStopObserving) {
|
||||
competitorToStopObserving.removeCompetitorChangeListener(this);
|
||||
observedCompetitors.remove(competitorToStopObserving);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class CacheInvalidationUponScoreCorrectionListener implements ScoreCorrectionListener {
|
||||
private final Leaderboard leaderboard;
|
||||
|
||||
@@ -146,10 +198,12 @@ public class LeaderboardCacheManager {
|
||||
|
||||
public LeaderboardCacheManager(LeaderboardCache leaderboardCache) {
|
||||
this.leaderboardCache = leaderboardCache;
|
||||
this.invalidationListenersPerLeaderboard = new WeakHashMap<Leaderboard, Map<TrackedRace, Set<CacheInvalidationListener>>>();
|
||||
this.invalidationListenersPerLeaderboard = new WeakHashMap<>();
|
||||
this.raceColumnListeners = new WeakHashMap<Leaderboard, RaceColumnListener>();
|
||||
this.scoreCorrectionListeners = new WeakHashMap<Leaderboard, CacheInvalidationUponScoreCorrectionListener>();
|
||||
this.scoreCorrectionListenersLock = new NamedReentrantReadWriteLock("Score correction listeners", /* fair */ false);
|
||||
this.scoreCorrectionListeners = new WeakHashMap<>();
|
||||
this.scoreCorrectionAndCompetitorChangeListenersLock = new NamedReentrantReadWriteLock(
|
||||
"Score correction and competitor change listeners", /* fair */false);
|
||||
this.competitorChangeListeners = new WeakHashMap<>();
|
||||
}
|
||||
|
||||
private void removeFromCache(Leaderboard leaderboard) {
|
||||
@@ -167,14 +221,19 @@ public class LeaderboardCacheManager {
|
||||
synchronized (raceColumnListeners) {
|
||||
leaderboard.removeRaceColumnListener(raceColumnListeners.remove(leaderboard));
|
||||
}
|
||||
LockUtil.lockForWrite(scoreCorrectionListenersLock);
|
||||
LockUtil.lockForWrite(scoreCorrectionAndCompetitorChangeListenersLock);
|
||||
final CacheInvalidationUponScoreCorrectionListener removedScoreCorrectionListener;
|
||||
final CacheInvalidationUponCompetitorChangeListener removedCompetitorChangeListener;
|
||||
try {
|
||||
removedScoreCorrectionListener = scoreCorrectionListeners.remove(leaderboard);
|
||||
removedCompetitorChangeListener = competitorChangeListeners.remove(leaderboard);
|
||||
} finally {
|
||||
LockUtil.unlockAfterWrite(scoreCorrectionListenersLock);
|
||||
LockUtil.unlockAfterWrite(scoreCorrectionAndCompetitorChangeListenersLock);
|
||||
}
|
||||
leaderboard.getScoreCorrection().removeScoreCorrectionListener(removedScoreCorrectionListener);
|
||||
for (Competitor competitor : leaderboard.getCompetitors()) {
|
||||
competitor.removeCompetitorChangeListener(removedCompetitorChangeListener);
|
||||
}
|
||||
// invalidate after the listeners have been removed; this is important because invalidation may trigger a
|
||||
// re-calculation which then in turn may call add(leaderboard) asynchronously again which may be executed
|
||||
// before the listener removal happens here. This could lead to a race condition where listeners are
|
||||
@@ -195,22 +254,28 @@ public class LeaderboardCacheManager {
|
||||
private void registerAsListener(final Leaderboard leaderboard) {
|
||||
// only add as listener again if not yet added
|
||||
final boolean containsKey;
|
||||
LockUtil.lockForRead(scoreCorrectionListenersLock);
|
||||
LockUtil.lockForRead(scoreCorrectionAndCompetitorChangeListenersLock);
|
||||
try {
|
||||
containsKey = scoreCorrectionListeners.containsKey(leaderboard);
|
||||
} finally {
|
||||
LockUtil.unlockAfterRead(scoreCorrectionListenersLock);
|
||||
LockUtil.unlockAfterRead(scoreCorrectionAndCompetitorChangeListenersLock);
|
||||
}
|
||||
if (!containsKey) {
|
||||
LockUtil.lockForWrite(scoreCorrectionListenersLock);
|
||||
LockUtil.lockForWrite(scoreCorrectionAndCompetitorChangeListenersLock);
|
||||
try {
|
||||
for (TrackedRace trackedRace : leaderboard.getTrackedRaces()) {
|
||||
registerListener(leaderboard, trackedRace);
|
||||
}
|
||||
final CacheInvalidationUponScoreCorrectionListener scoreCorrectionListener = new CacheInvalidationUponScoreCorrectionListener(
|
||||
leaderboard);
|
||||
final CacheInvalidationUponCompetitorChangeListener competitorChangeListener = new CacheInvalidationUponCompetitorChangeListener(
|
||||
leaderboard);
|
||||
leaderboard.getScoreCorrection().addScoreCorrectionListener(scoreCorrectionListener);
|
||||
for (Competitor competitor : leaderboard.getCompetitors()) {
|
||||
competitor.addCompetitorChangeListener(competitorChangeListener);
|
||||
}
|
||||
scoreCorrectionListeners.put(leaderboard, scoreCorrectionListener);
|
||||
competitorChangeListeners.put(leaderboard, competitorChangeListener);
|
||||
final RaceColumnListener raceColumnListener = new RaceColumnListener() {
|
||||
private static final long serialVersionUID = 8165124797028386317L;
|
||||
|
||||
@@ -218,6 +283,7 @@ public class LeaderboardCacheManager {
|
||||
public void trackedRaceLinked(RaceColumn raceColumn, Fleet fleet, TrackedRace trackedRace) {
|
||||
removeFromCache(leaderboard);
|
||||
registerListener(leaderboard, trackedRace);
|
||||
competitorChangeListeners.get(leaderboard).updateCompetitorListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -231,6 +297,7 @@ public class LeaderboardCacheManager {
|
||||
@Override
|
||||
public void trackedRaceUnlinked(RaceColumn raceColumn, Fleet fleet, TrackedRace trackedRace) {
|
||||
removeFromCache(leaderboard);
|
||||
competitorChangeListeners.get(leaderboard).updateCompetitorListeners();
|
||||
Map<TrackedRace, Set<CacheInvalidationListener>> listenersMap = invalidationListenersPerLeaderboard
|
||||
.get(leaderboard);
|
||||
if (listenersMap != null) {
|
||||
@@ -312,7 +379,7 @@ public class LeaderboardCacheManager {
|
||||
raceColumnListeners.put(leaderboard, raceColumnListener);
|
||||
}
|
||||
} finally {
|
||||
LockUtil.unlockAfterWrite(scoreCorrectionListenersLock);
|
||||
LockUtil.unlockAfterWrite(scoreCorrectionAndCompetitorChangeListenersLock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user