Merge branch 'bug3236'

This commit is contained in:
Axel Uhl committed 2015-08-18 15:56:03 +02:00
commit 77ee20db15
8 files changed
+269 -65

No files matched your search

@@ -191,12 +191,38 @@ public class MongoGPSFixStoreImpl implements MongoGPSFixStore {
loadTrack(track, mapping.getDevice(), mapping.getTimeRange().from(), mapping.getTimeRange().to(), true /*inclusive*/);
}
@Override
public void loadCompetitorTrack(DynamicGPSFixTrack<Competitor, GPSFixMoving> track, AbstractLog<?, ?> log,
Competitor competitor, TimePoint start, TimePoint end) throws TransformationException {
List<DeviceMapping<Competitor>> mappings = new DeviceCompetitorMappingFinder<>(log).analyze().get(competitor);
if (mappings != null) {
for (DeviceMapping<Competitor> mapping : mappings) {
final TimePoint from = Util.getLatestOfTimePoints(start, mapping.getTimeRange().from());
final TimePoint to = Util.getEarliestOfTimePoints(end, mapping.getTimeRange().to());
loadTrack(track, mapping.getDevice(), from, to, true /*inclusive*/);
}
}
}
@Override
public void loadMarkTrack(DynamicGPSFixTrack<Mark, GPSFix> track, DeviceMapping<Mark> mapping)
throws TransformationException, NoCorrespondingServiceRegisteredException {
loadTrack(track, mapping.getDevice(), mapping.getTimeRange().from(), mapping.getTimeRange().to(), true /*inclusive*/);
}
@Override
public void loadMarkTrack(DynamicGPSFixTrack<Mark, GPSFix> track, AbstractLog<?, ?> log, Mark mark,
TimePoint start, TimePoint end) throws TransformationException, NoCorrespondingServiceRegisteredException {
List<DeviceMapping<Mark>> mappings = new DeviceMarkMappingFinder<>(log).analyze().get(mark);
if (mappings != null) {
for (DeviceMapping<Mark> mapping : mappings) {
final TimePoint from = Util.getLatestOfTimePoints(start, mapping.getTimeRange().from());
final TimePoint to = Util.getEarliestOfTimePoints(end, mapping.getTimeRange().to());
loadTrack(track, mapping.getDevice(), from, to, true /*inclusive*/);
}
}
}
@Override
public void loadTrack(DynamicGPSFixTrack<WithID, ?> track, DeviceMapping<WithID> mapping)
throws NoCorrespondingServiceRegisteredException, TransformationException {
@@ -18,4 +18,5 @@ Import-Package: com.sap.sailing.domain.abstractlog.regatta.events,
com.sap.sailing.domain.persistence.racelog.tracking.impl,
com.sap.sailing.domain.tractracadapter,
com.sap.sailing.server.impl,
com.sap.sse.concurrent,
javax.servlet.http;version="2.5.0"
@@ -79,7 +79,7 @@ public class AbstractGPSFixStoreTest {
mark, 0, new MillisecondsTimePoint(from), new MillisecondsTimePoint(to)));
}
protected void testLength(Track<?> track, long expected) {
protected void testNumberOfRawFixes(Track<?> track, long expected) {
track.lockForRead();
assertEquals(expected, size(track.getRawFixes()));
track.unlockAfterRead();
@@ -40,6 +40,7 @@ import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.common.impl.TimeRangeImpl;
public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
@@ -49,6 +50,49 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
private Integer previouslyLoading = null;
private int numFixesReceived = 0;
@Test
public void doesRaceLoadOnlyBetweenStartAndEndOfTracking() throws TransformationException,
NoCorrespondingServiceRegisteredException, InterruptedException {
Competitor comp2 = DomainFactory.INSTANCE.getOrCreateCompetitor("comp2", "comp2", null, null, null, null, null,
/* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null);
Mark mark2 = DomainFactory.INSTANCE.getOrCreateMark("mark2");
DeviceIdentifier markDevice = new SmartphoneImeiIdentifier("imei2");
Course course = new CourseImpl("course", Arrays.asList(new Waypoint[] { new WaypointImpl(mark),
new WaypointImpl(mark2) }));
RaceDefinition race = new RaceDefinitionImpl("race", course, boatClass, Arrays.asList(new Competitor[] { comp, comp2 }));
map(comp, device, 0, 10000);
map(mark, markDevice, 0, 10000);
store.storeFix(device, createFix(100, 10, 20, 30, 40)); // before
store.storeFix(device, createFix(1100, 10, 20, 30, 40)); // in
store.storeFix(device, createFix(2100, 10, 20, 30, 40)); // after
store.storeFix(markDevice, createFix(100, 10, 20, 30, 40));
store.storeFix(markDevice, createFix(1100, 10, 20, 30, 40));
store.storeFix(markDevice, createFix(2100, 10, 20, 30, 40));
TrackedRegatta regatta = new DynamicTrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE,
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, /* startDate */
null, /* endDate */null, null, null, "a", null));
final DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race,
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, store, 0, 0, 0, /*useMarkPassingCalculator*/ false,
OneDesignRankingMetric::new, mock(RaceLogResolver.class));
trackedRace.setStartOfTrackingReceived(new MillisecondsTimePoint(1000));
trackedRace.setEndOfTrackingReceived(new MillisecondsTimePoint(2000));
trackedRace.attachRaceLog(raceLog);
trackedRace.waitForLoadingFromGPSFixStoreToFinishRunning(raceLog);
testNumberOfRawFixes(trackedRace.getTrack(comp), 1);
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
// now extend the tracking interval of the tracked race and assert that the additional fixes are loaded
trackedRace.setEndOfTrackingReceived(new MillisecondsTimePoint(2500), /* wait for fixes to load */ true);
testNumberOfRawFixes(trackedRace.getTrack(comp), 2);
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 2);
trackedRace.setStartOfTrackingReceived(new MillisecondsTimePoint(0), /* wait for fixes to load */ true);
testNumberOfRawFixes(trackedRace.getTrack(comp), 3);
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 3);
}
@Test
public void areFixesStoredInDb() throws TransformationException, NoCorrespondingServiceRegisteredException,
InterruptedException {
@@ -88,10 +132,10 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
trackedRace.attachRaceLog(raceLog);
trackedRace.waitForLoadingFromGPSFixStoreToFinishRunning(raceLog);
testLength(trackedRace.getTrack(comp), 10002);
testLength(trackedRace.getTrack(comp2), 1);
testLength(trackedRace.getOrCreateTrack(mark), 1);
testLength(trackedRace.getOrCreateTrack(mark2), 1);
testNumberOfRawFixes(trackedRace.getTrack(comp), 10002);
testNumberOfRawFixes(trackedRace.getTrack(comp2), 1);
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark2), 1);
}
@Test
@@ -10,6 +10,7 @@ import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.TimeRange;
import com.sap.sse.common.WithID;
@@ -64,6 +65,15 @@ public enum EmptyGPSFixStore implements GPSFixStore {
@Override
public void loadTrack(DynamicGPSFixTrack<WithID, ?> track, DeviceMapping<WithID> mapping)
throws NoCorrespondingServiceRegisteredException, TransformationException {
}
@Override
public void loadCompetitorTrack(DynamicGPSFixTrack<Competitor, GPSFixMoving> track, AbstractLog<?, ?> log,
Competitor competitor, TimePoint start, TimePoint end) throws TransformationException {
}
@Override
public void loadMarkTrack(DynamicGPSFixTrack<Mark, GPSFix> track, AbstractLog<?, ?> log, Mark mark,
TimePoint start, TimePoint end) throws TransformationException, NoCorrespondingServiceRegisteredException {
}
}
@@ -12,6 +12,7 @@ import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.TimeRange;
import com.sap.sse.common.WithID;
@@ -23,12 +24,40 @@ public interface GPSFixStore {
void loadCompetitorTrack(DynamicGPSFixTrack<Competitor, GPSFixMoving> track, AbstractLog<?, ?> log,
Competitor competitor) throws TransformationException;
/**
* Load all fixes within the start and end time point (inclusive) that correspond to the
* {@link DeviceCompetitorMappingEvent}s found in the {@code raceLog}.
*
* @param start
* if <code>null</code>, the start of the time range for which to load fixes is only constrained by the
* device mapping intervals
* @param end
* if <code>null</code>, the end of the time range for which to load fixes is only constrained by the
* device mapping intervals
*/
void loadCompetitorTrack(DynamicGPSFixTrack<Competitor, GPSFixMoving> track, AbstractLog<?, ?> log,
Competitor competitor, TimePoint start, TimePoint end) throws TransformationException;
/**
* Load all fixes that correspond to the {@link DeviceMarkMappingEvent}s found in the {@code raceLog}.
*/
void loadMarkTrack(DynamicGPSFixTrack<Mark, GPSFix> track, AbstractLog<?, ?> log, Mark mark) throws TransformationException,
NoCorrespondingServiceRegisteredException;
/**
* Load all fixes within the start and end time point (inclusive) that correspond to the
* {@link DeviceMarkMappingEvent}s found in the {@code raceLog}.
*
* @param start
* if <code>null</code>, the start of the time range for which to load fixes is only constrained by the
* device mapping intervals
* @param end
* if <code>null</code>, the end of the time range for which to load fixes is only constrained by the
* device mapping intervals
*/
void loadMarkTrack(DynamicGPSFixTrack<Mark, GPSFix> track, AbstractLog<?, ?> log, Mark mark, TimePoint start,
TimePoint end) throws TransformationException, NoCorrespondingServiceRegisteredException;
/**
* Load all fixes that correspond to the {@code mapping}.
*/
@@ -143,12 +143,14 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
@Override
public void recordFix(Competitor competitor, GPSFixMoving fix) {
DynamicGPSFixTrack<Competitor, GPSFixMoving> track = getTrack(competitor);
if (track != null) {
if (logger != null && logger.getLevel() != null && logger.getLevel().equals(Level.FINEST)) {
logger.finest(""+competitor.getName() + ": " + fix);
if (isFixWithinStartAndEndOfTracking(fix.getTimePoint())) {
DynamicGPSFixTrack<Competitor, GPSFixMoving> track = getTrack(competitor);
if (track != null) {
if (logger != null && logger.getLevel() != null && logger.getLevel().equals(Level.FINEST)) {
logger.finest(""+competitor.getName() + ": " + fix);
}
track.addGPSFix(fix); // the track notifies this tracked race which in turn notifies its listeners
}
track.addGPSFix(fix); // the track notifies this tracked race which in turn notifies its listeners
}
}
@@ -161,12 +163,21 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
@Override
public void recordFix(Mark mark, GPSFix fix) {
if ((getStartOfTracking() == null || getStartOfTracking().compareTo(fix.getTimePoint()) <= 0) &&
(getEndOfTracking() == null || getEndOfTracking().compareTo(fix.getTimePoint()) >= 0)) {
final TimePoint fixTimePoint = fix.getTimePoint();
if (isFixWithinStartAndEndOfTracking(fixTimePoint)) {
getOrCreateTrack(mark).addGPSFix(fix);
}
}
/**
* A time point is considered "in" if it is (inclusively) between {@link #getStartOfTracking()} and {@link #getEndOfTracking()}.
* A <code>null</code> value for one of the two interval demarcations means an open-ended interval.
*/
private boolean isFixWithinStartAndEndOfTracking(final TimePoint fixTimePoint) {
return (getStartOfTracking() == null || getStartOfTracking().compareTo(fixTimePoint) <= 0) &&
(getEndOfTracking() == null || getEndOfTracking().compareTo(fixTimePoint) >= 0);
}
@Override
public void setMillisecondsOverWhichToAverageSpeed(long millisecondsOverWhichToAverageSpeed) {
this.millisecondsOverWhichToAverageSpeed = millisecondsOverWhichToAverageSpeed;
@@ -613,16 +624,28 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
@Override
public void setStartOfTrackingReceived(TimePoint startOfTrackingReceived) {
setStartOfTrackingReceived(startOfTrackingReceived, /* waitForGPSFixesToLoad */ false);
}
public void setStartOfTrackingReceived(TimePoint startOfTrackingReceived, final boolean waitForGPSFixesToLoad) {
if (!Util.equalsWithNull(startOfTrackingReceived, getStartOfTracking())) {
super.setStartOfTrackingReceived(startOfTrackingReceived);
super.setStartOfTrackingReceived(startOfTrackingReceived, waitForGPSFixesToLoad);
notifyListenersStartOfTrackingChanged(getStartOfTracking());
}
}
@Override
public void setEndOfTrackingReceived(TimePoint endOfTrackingReceived) {
setEndOfTrackingReceived(endOfTrackingReceived, /* waitForGPSFixesToLoad */ false);
}
/**
* Non-interface method, mainly for testing purposes; callers can ask to wait for the loading of fixes in the
* potentially extended tracking interval to finish before returning from this method.
*/
public void setEndOfTrackingReceived(final TimePoint endOfTrackingReceived, final boolean waitForGPSFixesToLoad) {
if (!Util.equalsWithNull(endOfTrackingReceived, getEndOfTracking())) {
super.setEndOfTrackingReceived(endOfTrackingReceived);
super.setEndOfTrackingReceived(endOfTrackingReceived, waitForGPSFixesToLoad);
notifyListenersEndOfTrackingChanged(getEndOfTracking());
}
}
@@ -1795,12 +1795,35 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
return startTimeReceived;
}
protected void setStartOfTrackingReceived(TimePoint startOfTracking) {
protected void setStartOfTrackingReceived(final TimePoint startOfTracking, final boolean waitForGPSFixesToLoad) {
final TimePoint oldStartOfTracking = this.startOfTrackingReceived;
this.startOfTrackingReceived = startOfTracking;
if (!Util.equalsWithNull(oldStartOfTracking, startOfTracking) &&
(startOfTracking == null || (oldStartOfTracking != null && startOfTracking.before(oldStartOfTracking)))) {
logger.info("Loading fixes after start of tracking for "+getRace()+" has been extended from "+oldStartOfTracking+" to "+startOfTracking);
loadGPSFixesForExtendedTimeRange(startOfTracking, oldStartOfTracking, waitForGPSFixesToLoad);
}
}
protected void setEndOfTrackingReceived(TimePoint endOfTracking) {
private void loadGPSFixesForExtendedTimeRange(final TimePoint start, final TimePoint end, final boolean waitForGPSFixesToLoad) {
for (final RaceLog raceLog : attachedRaceLogs.values()) {
loadFixesForLog(raceLog, /* no need to addLogToMap because log has already been added during attachRaceLog */ null,
/* startOfTimeWindowToLoad */ start, /* endOfTimeWindowToLoad */ end, waitForGPSFixesToLoad);
}
for (final RegattaLog regattaLog : attachedRegattaLogs.values()) {
loadFixesForLog(regattaLog, /* no need to addLogToMap because log has already been added during attachRaceLog */ null,
/* startOfTimeWindowToLoad */ start, /* endOfTimeWindowToLoad */ end, waitForGPSFixesToLoad);
}
}
protected void setEndOfTrackingReceived(final TimePoint endOfTracking, final boolean waitForGPSFixesToLoad) {
final TimePoint oldEndOfTracking = this.endOfTrackingReceived;
this.endOfTrackingReceived = endOfTracking;
if (!Util.equalsWithNull(oldEndOfTracking, endOfTracking) &&
(endOfTracking == null || (oldEndOfTracking != null && endOfTracking.after(oldEndOfTracking)))) {
logger.info("Loading fixes after end of tracking for "+getRace()+" has been extended from "+oldEndOfTracking+" to "+endOfTracking);
loadGPSFixesForExtendedTimeRange(oldEndOfTracking, endOfTracking, waitForGPSFixesToLoad);
}
}
/**
@@ -3135,62 +3158,110 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
}
/**
* Loads the GPS fixes as per the device registrations in the <code>log</code> into this race. The <code>log</code> is added to the
* <code>addLogToMap</code> data structure while holding this object's monitor, and all waiters on this object are notified when done
* with adding the <code>log</code> to <code>addLogToMap</code>.
* Loads the GPS fixes as per the device registrations in the <code>log</code> into this race. The <code>log</code>
* is added to the <code>addLogToMap</code> data structure while holding <code>this</code> object's monitor, and all
* waiters on <code>this</code> object are notified when done with adding the <code>log</code> to
* <code>addLogToMap</code>.
*/
private <LogT extends AbstractLog<EventT, VisitorT>, EventT extends AbstractLogEvent<VisitorT>, VisitorT> void loadFixesForLog(
final LogT log, ConcurrentHashMap<Serializable, LogT> addLogToMap) {
final LogT log, ConcurrentHashMap<Serializable, LogT> addLogToMap, final boolean waitForGPSFixesToLoad) {
if (log != null) {
// Use the new log, that possibly contains device mappings, to load GPSFix tracks from the DB
// 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() + ", log "
+ log.getId()) {
@Override
public void run() {
LockUtil.lockForRead(getSerializationLock());
LockUtil.lockForWrite(getLoadingFromGPSFixStoreLock());
synchronized (TrackedRaceImpl.this) {
loadingFromGPSFixStore = true; // indicates that the serialization lock is now safely held
addLogToMap.put(log.getId(), log);
TrackedRaceImpl.this.notifyAll();
}
try {
logger.info("Started loading competitor tracks for " + getRace().getName() + " for log " + log.getId());
for (Competitor competitor : race.getCompetitors()) {
try {
gpsFixStore.loadCompetitorTrack(
(DynamicGPSFixTrack<Competitor, GPSFixMoving>) tracks.get(competitor), log,
competitor);
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
logger.log(Level.WARNING, "Could not load track for " + competitor, e);
}
}
logger.info("Finished loading competitor tracks for " + getRace().getName());
logger.info("Started loading mark tracks for " + getRace().getName());
for (Mark mark : getMarksFromRaceAndLogs()) {
try {
gpsFixStore.loadMarkTrack((DynamicGPSFixTrack<Mark, GPSFix>) getOrCreateTrack(mark), log, mark);
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
logger.log(Level.WARNING, "Could not load track for " + mark, e);
}
}
logger.info("Finished loading mark tracks for " + getRace().getName());
} finally {
synchronized (TrackedRaceImpl.this) {
loadingFromGPSFixStore = false;
TrackedRaceImpl.this.notifyAll();
}
LockUtil.unlockAfterWrite(getLoadingFromGPSFixStoreLock());
LockUtil.unlockAfterRead(getSerializationLock());
}
}
}.start();
final TimePoint startOfTimeWindowToLoad = getStartOfTracking();
final TimePoint endOfTimeWindowToLoad = getEndOfTracking();
loadFixesForLog(log, addLogToMap, startOfTimeWindowToLoad, endOfTimeWindowToLoad, waitForGPSFixesToLoad);
} else {
logger.severe("Got a request to attach log for an empty log!");
}
}
/**
* Loads the GPS fixes as per the device registrations in the <code>log</code> into this race, cropped to the time
* interval specified by <code>startOfTimeWindowToLoad</code> and </code>endOfTimeWindowToLoad</code>. The
* <code>log</code> is added to the <code>addLogToMap</code> data structure (if <code>addLogToMap</code> is not
* <code>null</code>) while holding <code>this</code> object's monitor, and all waiters on <code>this</code> object
* are notified when done with adding the <code>log</code> to <code>addLogToMap</code>.
*
* @param addLogToMap
* the log will be added to this map after the {@link #loadingFromGPSFixStore} flag has been set and only when the
* loading thread has successfully acquired the write lock of {@link #getLoadingFromGPSFixStoreLock()}. When done loading,
* all waiters on <code>this</code> object will be notified. If the map is not <code>null</code>, the key used to enter
* the log is the {@link AbstractLog#getId() log's ID}.
* @param startOfTimeWindowToLoad
* the time to use to crop the beginning of the time range for which to load fixes; if <code>null</code>,
* the start of the time range will be restricted only by the device mapping intervals
* @param endOfTimeWindowToLoad
* the time to use to crop the end of the time range for which to load fixes; if <code>null</code>,
* the end of the time range will be restricted only by the device mapping intervals
*/
private <LogT extends AbstractLog<EventT, VisitorT>, EventT extends AbstractLogEvent<VisitorT>, VisitorT> void loadFixesForLog(final LogT log,
ConcurrentHashMap<Serializable, LogT> addLogToMap, final TimePoint startOfTimeWindowToLoad,
final TimePoint endOfTimeWindowToLoad, final boolean waitForGPSFixesToLoad) {
Thread t = new Thread("Mongo mark and competitor track loader for tracked race " + getRace().getName() + ", log "
+ log.getId()+" from "+startOfTimeWindowToLoad+" to "+endOfTimeWindowToLoad) {
@Override
public void run() {
LockUtil.lockForRead(getSerializationLock());
LockUtil.lockForWrite(getLoadingFromGPSFixStoreLock());
synchronized (TrackedRaceImpl.this) {
loadingFromGPSFixStore = true; // indicates that the serialization lock is now safely held
if (addLogToMap != null) {
addLogToMap.put(log.getId(), log);
}
TrackedRaceImpl.this.notifyAll();
}
try {
logger.info("Started loading competitor tracks for " + getRace().getName() + " for log " + log.getId());
for (Competitor competitor : race.getCompetitors()) {
try {
gpsFixStore.loadCompetitorTrack(
(DynamicGPSFixTrack<Competitor, GPSFixMoving>) tracks.get(competitor), log,
competitor, startOfTimeWindowToLoad, endOfTimeWindowToLoad);
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
logger.log(Level.WARNING, "Could not load track for " + competitor, e);
}
}
logger.info("Finished loading competitor tracks for " + getRace().getName());
logger.info("Started loading mark tracks for " + getRace().getName());
for (Mark mark : getMarksFromRaceAndLogs()) {
try {
gpsFixStore.loadMarkTrack((DynamicGPSFixTrack<Mark, GPSFix>) getOrCreateTrack(mark),
log, mark, startOfTimeWindowToLoad, endOfTimeWindowToLoad);
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
logger.log(Level.WARNING, "Could not load track for " + mark, e);
}
}
logger.info("Finished loading mark tracks for " + getRace().getName());
} finally {
synchronized (TrackedRaceImpl.this) {
loadingFromGPSFixStore = false;
TrackedRaceImpl.this.notifyAll();
}
LockUtil.unlockAfterWrite(getLoadingFromGPSFixStoreLock());
LockUtil.unlockAfterRead(getSerializationLock());
logger.info("Thread "+getName()+" done.");
}
}
};
t.start();
if (waitForGPSFixesToLoad) {
try {
t.join();
} catch (InterruptedException e) {
logger.log(Level.WARNING, "Got interrupted while waiting for loading of GPS fixes from log "+log+" to finish", e);
}
}
}
/**
* Tells if currently the race is loading GPS fixes from the {@link GPSFixStore}. Clients may {@link Object#wait()} on <code>this</code>
* object and will be notified whenever a change of this flag's value occurs.
*/
public boolean isLoadingFromGPSFixStore() {
return loadingFromGPSFixStore;
}
/**
* This can trigger fixes to be loaded, if there are {@link DeviceMapping}s in the {@code RaceLog}.
* If multiple race logs are attached, the threads that are spawned to load fixes will do so one
@@ -3198,7 +3269,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
*/
@Override
public void attachRaceLog(final RaceLog raceLog) {
loadFixesForLog(raceLog, attachedRaceLogs);
loadFixesForLog(raceLog, attachedRaceLogs, /* wait for fixes to load */ false);
}
@Override
@@ -3243,7 +3314,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
@Override
public void attachRegattaLog(RegattaLog regattaLog) {
loadFixesForLog(regattaLog, attachedRegattaLogs);
loadFixesForLog(regattaLog, attachedRegattaLogs, /* wait for fixes to load */ false);
}
@Override