added GPSFixStore concept to TrackedRace

- needs to be passed through RacingEventServiceImpl -> lots of changes in
  test cases
- changes in adapters, as GPSFixStore is also passed around in
  RaceAdapter etc.
This commit is contained in:
Fredrik Teschke committed 2014-02-15 12:20:12 +01:00
1 parent 07f5efe154
commit 78c848e594
67 files changed
+769 -425

No files matched your search

@@ -49,9 +49,8 @@ import com.sap.sailing.domain.persistence.MongoObjectFactory;
import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.domain.racelog.RaceLogEventAuthor;
import com.sap.sailing.domain.racelog.RaceLogStartTimeEvent;
import com.sap.sailing.domain.racelog.tracking.DeviceIdentifier;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.GPSFix;
import com.sap.sailing.domain.tracking.RaceListener;
import com.sap.sailing.domain.tracking.RaceTracker;
import com.sap.sailing.domain.tracking.TrackedRace;
@@ -255,7 +254,7 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch
*/
void updateStoredLeaderboardGroup(LeaderboardGroup leaderboardGroup);
DynamicTrackedRace createTrackedRace(RegattaAndRaceIdentifier raceIdentifier, WindStore windStore,
DynamicTrackedRace createTrackedRace(RegattaAndRaceIdentifier raceIdentifier, WindStore windStore, GPSFixStore gpsFixStore,
long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed);
Regatta getOrCreateDefaultRegatta(String regattaBaseName, String boatClassName, Serializable id);
@@ -481,14 +480,6 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch
DomainObjectFactory getDomainObjectFactory();
WindStore getWindStore();
/**
* Store {@link GPSFix}es, that originate from devices that are not managed by a tracking provider
* who himself persists the tracking data, e.g. in the case of smartphone tracking.
*
* Currently tracked races with a device mapping for this {@code device} are notified of this new fix.
* @param device
* @param fix
*/
void storeGPSFix(DeviceIdentifier device, GPSFix fix);
GPSFixStore getGPSFixStore();
}
@@ -93,6 +93,7 @@ import com.sap.sailing.domain.persistence.PersistenceFactory;
import com.sap.sailing.domain.persistence.media.DBMediaTrack;
import com.sap.sailing.domain.persistence.media.MediaDB;
import com.sap.sailing.domain.persistence.media.MediaDBFactory;
import com.sap.sailing.domain.persistence.racelog.tracking.MongoGPSFixStoreFactory;
import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.racelog.impl.RaceLogEventAuthorImpl;
@@ -101,7 +102,7 @@ import com.sap.sailing.domain.racelog.state.ReadonlyRaceState;
import com.sap.sailing.domain.racelog.state.impl.RaceStateImpl;
import com.sap.sailing.domain.racelog.state.impl.ReadonlyRaceStateImpl;
import com.sap.sailing.domain.racelog.tracking.DeviceIdentifier;
import com.sap.sailing.domain.tracking.DynamicTrack;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.GPSFix;
@@ -247,14 +248,7 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
protected final DeviceConfigurationMapImpl configurationMap;
private final WindStore windStore;
/**
* These tracks are not replicated in any way. They are only loaded on the master instance.
* All management of device mappings has to occur via the master instance anyway, and as soon
* as a portion of the track is mapped to an actual race, all the relevant fixes are replicated
* with the existing replication features through that race.
*/
private final Map<DeviceIdentifier, DynamicTrack<GPSFix>> tracksByDevice;
private final GPSFixStore gpsFixStore;
/**
* If this service runs in the context of an OSGi environment, the activator should {@link #setBundleContext set the bundle context} on this
@@ -275,8 +269,8 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
this(true, null);
}
public RacingEventServiceImpl(WindStore windStore, TypeBasedServiceFinderFactory serviceFinderFactory) {
this(true, windStore, serviceFinderFactory);
public RacingEventServiceImpl(WindStore windStore, GPSFixStore gpsFixStore, TypeBasedServiceFinderFactory serviceFinderFactory) {
this(true, windStore, gpsFixStore, serviceFinderFactory);
}
void setBundleContext(BundleContext bundleContext) {
@@ -296,7 +290,7 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
* created and stored during previous service executions will initially be loaded.
*/
public RacingEventServiceImpl(boolean clearPersistentCompetitorStore, TypeBasedServiceFinderFactory serviceFinderFactory) {
this(new PersistentCompetitorStore(PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(serviceFinderFactory), clearPersistentCompetitorStore, serviceFinderFactory), null, serviceFinderFactory);
this(new PersistentCompetitorStore(PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(serviceFinderFactory), clearPersistentCompetitorStore, serviceFinderFactory), null, null, serviceFinderFactory);
}
/**
@@ -313,9 +307,9 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
* of {@link DeviceIdentifier}s of specific device types or the managing of the device-to-competitor
* associations per race tracked.
*/
private RacingEventServiceImpl(boolean clearPersistentCompetitorStore, WindStore windStore, TypeBasedServiceFinderFactory serviceFinderFactory) {
private RacingEventServiceImpl(boolean clearPersistentCompetitorStore, WindStore windStore, GPSFixStore gpsFixStore, TypeBasedServiceFinderFactory serviceFinderFactory) {
this(new PersistentCompetitorStore(PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(serviceFinderFactory),
clearPersistentCompetitorStore, serviceFinderFactory), windStore, serviceFinderFactory);
clearPersistentCompetitorStore, serviceFinderFactory), windStore, gpsFixStore, serviceFinderFactory);
}
/**
@@ -327,9 +321,9 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
* {@link DeviceIdentifier}s of specific device types or the managing of the device-to-competitor associations
* per race tracked.
*/
private RacingEventServiceImpl(PersistentCompetitorStore persistentCompetitorStore, WindStore windStore, TypeBasedServiceFinderFactory serviceFinderFactory) {
private RacingEventServiceImpl(PersistentCompetitorStore persistentCompetitorStore, WindStore windStore, GPSFixStore gpsFixStore, TypeBasedServiceFinderFactory serviceFinderFactory) {
this(persistentCompetitorStore.getDomainObjectFactory(), persistentCompetitorStore.getMongoObjectFactory(),
persistentCompetitorStore.getBaseDomainFactory(), MediaDBFactory.INSTANCE.getDefaultMediaDB(), persistentCompetitorStore, windStore);
persistentCompetitorStore.getBaseDomainFactory(), MediaDBFactory.INSTANCE.getDefaultMediaDB(), persistentCompetitorStore, windStore, gpsFixStore, serviceFinderFactory);
}
/**
@@ -337,8 +331,8 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
* {@link PersistenceFactory#getDefaultDomainObjectFactory() default domain object factory}. This constructor should
* be used for testing because it provides a transient {@link CompetitorStore} as required for competitor persistence.
*/
public RacingEventServiceImpl(DomainObjectFactory domainObjectFactory, MongoObjectFactory mongoObjectFactory, MediaDB mediaDB, WindStore windStore) {
this(domainObjectFactory, mongoObjectFactory, domainObjectFactory.getBaseDomainFactory(), mediaDB, domainObjectFactory.getBaseDomainFactory().getCompetitorStore(), windStore);
public RacingEventServiceImpl(DomainObjectFactory domainObjectFactory, MongoObjectFactory mongoObjectFactory, MediaDB mediaDB, WindStore windStore, GPSFixStore gpsFixStore) {
this(domainObjectFactory, mongoObjectFactory, domainObjectFactory.getBaseDomainFactory(), mediaDB, domainObjectFactory.getBaseDomainFactory().getCompetitorStore(), windStore, gpsFixStore, null);
}
/**
@@ -351,7 +345,8 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
* per race tracked.
*/
private RacingEventServiceImpl(DomainObjectFactory domainObjectFactory, MongoObjectFactory mongoObjectFactory,
com.sap.sailing.domain.base.DomainFactory baseDomainFactory, MediaDB mediaDb, CompetitorStore competitorStore, WindStore windStore) {
com.sap.sailing.domain.base.DomainFactory baseDomainFactory, MediaDB mediaDb, CompetitorStore competitorStore,
WindStore windStore, GPSFixStore gpsFixStore, TypeBasedServiceFinderFactory serviceFinderFactory) {
logger.info("Created " + this);
this.baseDomainFactory = baseDomainFactory;
this.domainObjectFactory = domainObjectFactory;
@@ -367,7 +362,6 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
operationExecutionListeners = new ConcurrentHashMap<>();
courseListeners = new ConcurrentHashMap<>();
persistentRegattasForRaceIDs = new ConcurrentHashMap<>();
tracksByDevice = new ConcurrentHashMap<>();
this.raceLogReplicator = new RaceLogReplicator(this);
this.raceLogScoringReplicator = new RaceLogScoringReplicator(this);
this.mediaDB = mediaDb;
@@ -380,6 +374,14 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
}
}
this.windStore = windStore;
if (gpsFixStore == null) {
try {
gpsFixStore = MongoGPSFixStoreFactory.INSTANCE.getMongoGPSFixStore(mongoObjectFactory, domainObjectFactory, serviceFinderFactory);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
this.gpsFixStore = gpsFixStore;
this.configurationMap = new DeviceConfigurationMapImpl();
// Add one default leaderboard that aggregates all races currently tracked by this service.
@@ -393,7 +395,6 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
loadStoredLeaderboardsAndGroups();
loadMediaLibary();
loadStoredDeviceConfigurations();
loadStoredTracks();
}
@Override
@@ -459,21 +460,6 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
}
}
}
private void loadStoredTracks() {
/*
* TODO load tracks in seperate thread, or even better: use proxy tracks, that on first load only
* load metadata (beginning and end TimePoints of individual tracking sessions in track, and then
* load the fixes when really needed.
*/
synchronized(tracksByDevice) {
try {
tracksByDevice.putAll(domainObjectFactory.loadAllGPSFixTracks());
} catch (Exception e) {
logger.log(Level.WARNING, "Could not load tracks", e);
}
}
}
@Override
public void addLeaderboard(Leaderboard leaderboard) {
@@ -924,10 +910,10 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
Regatta regatta = regattaToAddTo == null ? null : getRegatta(regattaToAddTo);
if (regatta == null) {
// create tracker and use an existing or create a default regatta
tracker = params.createRaceTracker(this, windStore);
tracker = params.createRaceTracker(this, windStore, gpsFixStore);
} else {
// use the regatta selected by the RaceIdentifier regattaToAddTo
tracker = params.createRaceTracker(regatta, this, windStore);
tracker = params.createRaceTracker(regatta, this, windStore, gpsFixStore);
assert tracker.getRegatta() == regatta;
}
synchronized (raceTrackersByRegatta) {
@@ -962,6 +948,11 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
logger.warning("Wind store mismatch. Requested wind store: " + windStore
+ ". Wind store in use by existing tracker: " + existingTrackersWindStore);
}
GPSFixStore existingTrackersGPSFixStore = tracker.getGPSFixStore();
if (!existingTrackersGPSFixStore.equals(gpsFixStore)) {
logger.warning("GPSFix store mismatch. Requested GPSFix store: " + gpsFixStore
+ ". GPSFix store in use by existing tracker: " + existingTrackersGPSFixStore);
}
}
if (timeoutInMilliseconds != -1) {
scheduleAbortTrackerAfterInitialTimeout(tracker, timeoutInMilliseconds);
@@ -1035,11 +1026,11 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
}
@Override
public DynamicTrackedRace createTrackedRace(RegattaAndRaceIdentifier raceIdentifier, WindStore windStore,
public DynamicTrackedRace createTrackedRace(RegattaAndRaceIdentifier raceIdentifier, WindStore windStore, GPSFixStore gpsFixStore,
long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed) {
DynamicTrackedRegatta trackedRegatta = getOrCreateTrackedRegatta(getRegatta(raceIdentifier));
RaceDefinition race = getRace(raceIdentifier);
return trackedRegatta.createTrackedRace(race, Collections.<Sideline> emptyList(), windStore, delayToLiveInMillis,
return trackedRegatta.createTrackedRace(race, Collections.<Sideline> emptyList(), windStore, gpsFixStore, delayToLiveInMillis,
millisecondsOverWhichToAverageWind, millisecondsOverWhichToAverageSpeed,
/* raceDefinitionSetToUpdate */null);
}
@@ -1092,7 +1083,7 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
public void raceAdded(TrackedRace trackedRace) {
// replicate the addition of the tracked race:
CreateTrackedRace op = new CreateTrackedRace(trackedRace.getRaceIdentifier(), trackedRace.getWindStore(),
trackedRace.getDelayToLiveInMillis(), trackedRace.getMillisecondsOverWhichToAverageWind(),
trackedRace.getGPSFixStore(), trackedRace.getDelayToLiveInMillis(), trackedRace.getMillisecondsOverWhichToAverageWind(),
trackedRace.getMillisecondsOverWhichToAverageSpeed());
replicate(op);
linkRaceToConfiguredLeaderboardColumns(trackedRace);
@@ -2196,29 +2187,9 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
}
return result;
}
private DynamicTrack<GPSFix> getStoredGPSFixTrack(DeviceIdentifier device) {
//test first outside of sync block for the likely case that the track already exists in {@link #tracksByDevice}
DynamicTrack<GPSFix> track = tracksByDevice.get(device);
if (track == null) {
synchronized(tracksByDevice) {
//now test again in sync, in case a track was added in the meantime
if (tracksByDevice.get(device) == null) {
try {
track = domainObjectFactory.loadGPSFixTrack(device);
} catch (Exception e) {
logger.log(Level.WARNING, "Could not load track", e);
}
tracksByDevice.put(device, track);
}
}
}
return track;
}
@Override
public void storeGPSFix(DeviceIdentifier device, GPSFix fix) {
getStoredGPSFixTrack(device).add(fix);
public GPSFixStore getGPSFixStore() {
return gpsFixStore;
}
}
@@ -24,6 +24,7 @@ import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.tracking.CourseDesignChangedListener;
import com.sap.sailing.domain.tracking.GPSFix;
import com.sap.sailing.domain.tracking.GPSFixMoving;
@@ -414,7 +415,7 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
}
@Override
public void waitUntilWindLoadingComplete() throws InterruptedException {
public void waitUntilLoadingFromStoresComplete() throws InterruptedException {
// TODO Auto-generated method stub
}
@@ -544,4 +545,10 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
return null;
}
@Override
public GPSFixStore getGPSFixStore() {
// TODO Auto-generated method stub
return null;
}
}
@@ -1,11 +1,13 @@
package com.sap.sailing.server.operationaltransformation;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.tracking.TrackedRegatta;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegatta;
import com.sap.sailing.domain.tracking.WindStore;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
import com.sap.sailing.server.RacingEventService;
@@ -32,16 +34,27 @@ public class CreateTrackedRace extends AbstractRaceOperation<TrackedRace> {
* {@link EmptyWindStore}.
*/
private transient final WindStore windStore;
/**
* If a {@link GPSFixStore} is provided to this command, it will be used for the construction of the tracked race.
* However, after de-serialization, the store will always be <code>null</code>, causing the use of an
* {@link EmptyGPSFixStore}.
*/
private transient final GPSFixStore gpsFixStore;
/**
* @param windStore
* if <code>null</code>, an {@link EmptyWindStore} will be used. Note that the {@link #windStore} field
* won't be serialized. A receiver of this operation will therefore always use an {@link EmptyWindStore}.
* @param gpsFixStore
* if <code>null</code>, an {@link EmptyGPSFixStore} will be used. Note that the {@link #gpsFixStore} field
* won't be serialized. A receiver of this operation will therefore always use an {@link EmptyWindStore}.
*/
public CreateTrackedRace(RegattaAndRaceIdentifier raceIdentifier, WindStore windStore,
public CreateTrackedRace(RegattaAndRaceIdentifier raceIdentifier, WindStore windStore, GPSFixStore gpsFixStore,
long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed) {
super(raceIdentifier);
this.windStore = windStore;
this.gpsFixStore = gpsFixStore;
this.delayToLiveInMillis = delayToLiveInMillis;
this.millisecondsOverWhichToAverageWind = millisecondsOverWhichToAverageWind;
this.millisecondsOverWhichToAverageSpeed = millisecondsOverWhichToAverageSpeed;
@@ -61,7 +74,8 @@ public class CreateTrackedRace extends AbstractRaceOperation<TrackedRace> {
@Override
public TrackedRace internalApplyTo(RacingEventService toState) {
return toState.createTrackedRace(getRaceIdentifier(), windStore == null ? EmptyWindStore.INSTANCE : windStore,
return toState.createTrackedRace(getRaceIdentifier(), windStore == null ? EmptyWindStore.INSTANCE : windStore,
gpsFixStore == null ? EmptyGPSFixStore.INSTANCE : gpsFixStore,
delayToLiveInMillis, millisecondsOverWhichToAverageWind, millisecondsOverWhichToAverageSpeed);
}