diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java index a392004fd9e..bb4bf33d18e 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java @@ -221,7 +221,8 @@ public class MockedTrackedRace implements DynamicTrackedRace { } @Override - public void addListener(RaceChangeListener listener, boolean notifyAboutWindFixesAlreadyLoaded) { + public void addListener(RaceChangeListener listener, boolean notifyAboutWindFixesAlreadyLoaded, + boolean notifyAboutGPSFixesAlreadyLoaded) { // TODO Auto-generated method stub } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java index 1dc5f78b580..66280733efb 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java @@ -353,7 +353,8 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace { } @Override - public void addListener(RaceChangeListener listener, boolean notifyAboutWindFixesAlreadyLoaded) { + public void addListener(RaceChangeListener listener, boolean notifyAboutWindFixesAlreadyLoaded, + boolean notifyAboutGPSFixesAlreadyLoaded) { // TODO Auto-generated method stub } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java index 53c3a2a0a02..807f153e77f 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java @@ -443,9 +443,12 @@ public interface TrackedRace extends Serializable { * tracked race. This runs synchronized with the otherwise asynchronous loading of wind tracks, triggered by the * constructor of the {@link TrackedRace} implementation classes. This procedure guarantees that eventually the * listener will have received a notification for all wind fixes, regardless of whether they were already loaded at - * the time the listener is registered or they are loaded after the registration has completed. + * the time the listener is registered or they are loaded after the registration has completed.

+ * + * The same is true for the GPS fixes for marks and competitors. */ - void addListener(RaceChangeListener listener, boolean notifyAboutWindFixesAlreadyLoaded); + void addListener(RaceChangeListener listener, boolean notifyAboutWindFixesAlreadyLoaded, + boolean notifyAboutGPSFixesAlreadyLoaded); void removeListener(RaceChangeListener listener); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java index 73a875f2c77..83b2e3ecf34 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java @@ -44,6 +44,7 @@ import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; import com.sap.sailing.domain.tracking.GPSFix; import com.sap.sailing.domain.tracking.GPSFixMoving; +import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.GPSTrackListener; import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.RaceChangeListener; @@ -249,16 +250,14 @@ DynamicTrackedRace, GPSTrackListener { } } - /** - * If the listener wants to be notified about the wind fixes already loaded, this method will obtain the read lock - * of the {@link #getLoadingFromStoresLock() wind loading lock}, locking against the wind loading thread which uses the - * corresponding write lock. This ensures that either all wind fixes have been loaded already or that wind loading - * hasn't started yet. - */ @Override - public void addListener(RaceChangeListener listener, final boolean notifyAboutWindFixesAlreadyLoaded) { + public void addListener(RaceChangeListener listener, final boolean notifyAboutWindFixesAlreadyLoaded, + final boolean notifyAboutGPSFixesAlreadyLoaded) { if (notifyAboutWindFixesAlreadyLoaded) { - LockUtil.lockForRead(getLoadingFromStoresLock()); + LockUtil.lockForRead(getLoadingFromWindStoreLock()); + } + if (notifyAboutGPSFixesAlreadyLoaded) { + LockUtil.lockForRead(getLoadingFromGPSFixStoreLock()); } try { addListener(listener); @@ -282,9 +281,34 @@ DynamicTrackedRace, GPSTrackListener { } } } + if (notifyAboutGPSFixesAlreadyLoaded) { + for (Mark mark : getMarks()) { + GPSFixTrack markTrack = getOrCreateTrack(mark); + markTrack.lockForRead(); + try { + for (GPSFix fix : markTrack.getRawFixes()) { + listener.markPositionChanged(fix, mark); + } + } finally { + markTrack.unlockAfterRead(); + } + } + + for (Competitor competitor : getRace().getCompetitors()) { + GPSFixTrack competitorTrack = getTrack(competitor); + competitorTrack.lockForRead(); + try { + for (GPSFixMoving fix : competitorTrack.getRawFixes()) { + listener.competitorPositionChanged(fix, competitor); + } + } finally { + competitorTrack.unlockAfterRead(); + } + } + } } finally { if (notifyAboutWindFixesAlreadyLoaded) { - LockUtil.unlockAfterRead(getLoadingFromStoresLock()); + LockUtil.unlockAfterRead(getLoadingFromWindStoreLock()); } } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java index b492c587e8c..cd1e3607968 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java @@ -230,22 +230,31 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl private enum LoadingFromStoresState { NOT_STARTED, RUNNING, FINISHED }; /** - * The constructor loads wind fixes from the {@link #windStore} and GPS fixes from the {@link #gpsFixStore} - * asynchronously. When completed, this flag is set to true, and all threads currently waiting on this - * object are notified. + * The constructor loads wind fixes from the {@link #windStore} asynchronously. + * When completed all threads currently waiting on this object are notified. */ - private LoadingFromStoresState loadingFromStoresCompleted; + private LoadingFromStoresState loadingFromWindStoreState; + + /** + * @see #loadingFromWindStoreState but for GPSFixStore + */ + private LoadingFromStoresState loadingFromGPSFixStoreState; private transient CrossTrackErrorCache crossTrackErrorCache; private final GPSFixStore gpsFixStore; /** - * Wind and GPS fix loading is started in a background thread during object construction. If a client needs to - * ensure that wind / GPS fix loading either has terminated or has not yet begun, it can obtain the read lock of - * this lock. The wind / GPS fix loading procedure will obtain the write lock before it starts loading wind fixes. + * Wind and loading is started in a background thread during object construction. If a client needs to + * ensure that wind loading either has terminated or has not yet begun, it can obtain the read lock of + * this lock. The wind loading procedure will obtain the write lock before it starts loading wind fixes. */ - private final NamedReentrantReadWriteLock loadingFromStoresLock; + private final NamedReentrantReadWriteLock loadingFromWindStoreLock; + + /** + * @see #loadingFromWindStoreLock but for GPSFixStore + */ + private final NamedReentrantReadWriteLock loadingFromGPSFixStoreLock; private final Map, NamedReentrantReadWriteLock> locksForMarkPassings; @@ -257,7 +266,9 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl attachedRaceLogs = new HashMap<>(); this.status = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.PREPARED, 0.0); this.statusNotifier = new Object[0]; - this.loadingFromStoresLock = new NamedReentrantReadWriteLock("Loading from stores lock for tracked race " + this.loadingFromWindStoreLock = new NamedReentrantReadWriteLock("Loading from wind store lock for tracked race " + + race.getName(), /* fair */ false); + this.loadingFromGPSFixStoreLock = new NamedReentrantReadWriteLock("Loading from GPSFix store lock for tracked race " + race.getName(), /* fair */ false); this.cacheInvalidationTimerLock = new Object(); this.updateCount = 0; @@ -314,15 +325,15 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl MarkPassingByTimeComparator.INSTANCE)); } markPassingsTimes = new ArrayList>>(); - loadingFromStoresCompleted = LoadingFromStoresState.NOT_STARTED; + loadingFromWindStoreState = LoadingFromStoresState.NOT_STARTED; // When this tracked race is to be serialized, wait for the loading from stores to complete. new Thread("Mongo wind loader for tracked race " + getRace().getName()) { @Override public void run() { LockUtil.lockForRead(getSerializationLock()); - LockUtil.lockForWrite(getLoadingFromStoresLock()); + LockUtil.lockForWrite(getLoadingFromWindStoreLock()); synchronized (TrackedRaceImpl.this) { - loadingFromStoresCompleted = LoadingFromStoresState.RUNNING; // indicates that the serialization lock is now safely held + loadingFromWindStoreState = LoadingFromStoresState.RUNNING; // indicates that the serialization lock is now safely held TrackedRaceImpl.this.notifyAll(); } try { @@ -335,10 +346,11 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl } finally { synchronized (TrackedRaceImpl.this) { - loadingFromStoresCompleted = LoadingFromStoresState.FINISHED; + loadingFromWindStoreState = LoadingFromStoresState.FINISHED; TrackedRaceImpl.this.notifyAll(); + loadingFromWindStoreState.notifyAll(); } - LockUtil.unlockAfterWrite(getLoadingFromStoresLock()); + LockUtil.unlockAfterWrite(getLoadingFromWindStoreLock()); LockUtil.unlockAfterRead(getSerializationLock()); } } @@ -356,7 +368,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl competitorRankingsLocks = new HashMap(); // now wait until wind loading has at least started; then we know that the serialization lock is safely held by the loader synchronized (this) { - while (loadingFromStoresCompleted != LoadingFromStoresState.FINISHED) { + while (loadingFromWindStoreState != LoadingFromStoresState.FINISHED) { try { this.wait(); } catch (InterruptedException e) { @@ -418,7 +430,8 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl @Override public synchronized void waitUntilLoadingFromStoresComplete() throws InterruptedException { - while (loadingFromStoresCompleted != LoadingFromStoresState.FINISHED) { + while (loadingFromWindStoreState != LoadingFromStoresState.FINISHED || + loadingFromGPSFixStoreState != LoadingFromStoresState.FINISHED) { wait(); } } @@ -2349,15 +2362,15 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl if (raceLog != null) { this.attachedRaceLogs.put(raceLog.getId(), raceLog); // Use the new race log, that possibly contains device mappings, to load GPSFix tracks from the DB - loadingFromStoresCompleted = LoadingFromStoresState.NOT_STARTED; + loadingFromGPSFixStoreState = LoadingFromStoresState.NOT_STARTED; // When this tracked race is to be serialized, wait for the loading from stores to complete. new Thread("Mongo mark and competitor track loader for tracked race " + getRace().getName()) { @Override public void run() { LockUtil.lockForRead(getSerializationLock()); - LockUtil.lockForWrite(getLoadingFromStoresLock()); + LockUtil.lockForWrite(getLoadingFromWindStoreLock()); synchronized (TrackedRaceImpl.this) { - loadingFromStoresCompleted = LoadingFromStoresState.RUNNING; // indicates that the serialization + loadingFromGPSFixStoreState = LoadingFromStoresState.RUNNING; // indicates that the serialization // lock is now safely held TrackedRaceImpl.this.notifyAll(); } @@ -2378,10 +2391,11 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl } finally { synchronized (TrackedRaceImpl.this) { - loadingFromStoresCompleted = LoadingFromStoresState.FINISHED; + loadingFromGPSFixStoreState = LoadingFromStoresState.FINISHED; TrackedRaceImpl.this.notifyAll(); + loadingFromGPSFixStoreState.notifyAll(); } - LockUtil.unlockAfterWrite(getLoadingFromStoresLock()); + LockUtil.unlockAfterWrite(getLoadingFromWindStoreLock()); LockUtil.unlockAfterRead(getSerializationLock()); } } @@ -2544,8 +2558,12 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl return null; } - protected NamedReentrantReadWriteLock getLoadingFromStoresLock() { - return loadingFromStoresLock; + protected NamedReentrantReadWriteLock getLoadingFromWindStoreLock() { + return loadingFromWindStoreLock; + } + + protected NamedReentrantReadWriteLock getLoadingFromGPSFixStoreLock() { + return loadingFromGPSFixStoreLock; } /** diff --git a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java index d6543d99646..be3fa9f41ee 100644 --- a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java +++ b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java @@ -221,7 +221,8 @@ public class MockedTrackedRace implements DynamicTrackedRace { } @Override - public void addListener(RaceChangeListener listener, boolean notifyAboutWindFixesAlreadyLoaded) { + public void addListener(RaceChangeListener listener, boolean notifyAboutWindFixesAlreadyLoaded, + boolean notifyAboutGPSFixesAlreadyLoaded) { // TODO Auto-generated method stub } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index b63997ae0bf..4c1eab9c8c8 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -1159,7 +1159,7 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport } TrackedRaceReplicator trackedRaceReplicator = new TrackedRaceReplicator(trackedRace); trackedRaceReplicators.put(trackedRace, trackedRaceReplicator); - trackedRace.addListener(trackedRaceReplicator, /* fire wind already loaded */ true); + trackedRace.addListener(trackedRaceReplicator, /* fire wind already loaded */ true, true); } } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java index fd5abfa16d5..c8da7fbea47 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java @@ -327,7 +327,8 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials { } @Override - public void addListener(RaceChangeListener listener, boolean notifyAboutWindFixesAlreadyLoaded) { + public void addListener(RaceChangeListener listener, boolean notifyAboutWindFixesAlreadyLoaded, + boolean notifyAboutGPSFixesAlreadyLoaded) { // TODO Auto-generated method stub }