using TrackedRegattaRegistry.removeRace to ensure consistent duplicate race removal

Change-Id: I245828fbd6d49f36e0f1f7d240471e5ea709a481
This commit is contained in:
Axel Uhl committed 2017-12-20 00:45:18 +01:00
1 parent de25afd906
commit de70631ede
11 files changed
+127 -129

No files matched your search

@@ -1620,7 +1620,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
DBObject key = new BasicDBObject();
key.putAll(paramsPersistenceService.getKey(params));
DBObject dbObject = new BasicDBObject();
dbObject.putAll(paramsPersistenceService.mapFrom(params));
dbObject.putAll(paramsPersistenceService.mapFrom(params)); // TODO bug 4402: should we use the Tracker ID as key here to avoid duplicates?
collection.update(key, dbObject, /* upsert */ true, /* multi */ false, WriteConcern.SAFE);
} catch (NoCorrespondingServiceRegisteredException e) {
logger.log(Level.WARNING, "Couldn't find a persistence service for connectivity parameters of type "+typeIdentifier+
@@ -47,6 +47,11 @@ public class DummyTrackedRegattaRegistry implements TrackedRegattaRegistry {
return null;
}
@Override
public void removeRace(Regatta regatta, RaceDefinition race)
throws MalformedURLException, IOException, InterruptedException {
}
@Override
public boolean isRaceBeingTracked(Regatta regattaContext, RaceDefinition r) {
return false;
@@ -145,6 +145,10 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
public void stopTracking(Regatta regatta, RaceDefinition race) throws MalformedURLException, IOException,
InterruptedException {
}
@Override
public void removeRace(Regatta regatta, RaceDefinition race)
throws MalformedURLException, IOException, InterruptedException {
}
};
LeaderboardDTO leaderboardDTO = leaderboard.getLeaderboardDTO(now, emptySet, /* addOverallDetails */ true, trackedRegattaRegistry, DomainFactory.INSTANCE, /* fillTotalPointsUncorrected */ false);
assertNotNull(leaderboardDTO);
@@ -132,21 +132,19 @@ public interface DomainFactory {
* Provides the capability to obtain the {@link WindTrack}s for the different wind sources. A trivial
* implementation is {@link EmptyWindStore} which simply provides new, empty tracks. This is always
* available but loses track of the wind, e.g., during server restarts.
* @param timeoutInMilliseconds TODO
*/
TracTracRaceTracker createRaceTracker(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry,
RaceLogResolver raceLogResolver, RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds)
throws MalformedURLException, FileNotFoundException, URISyntaxException, CreateModelException, SubscriberInitializationException;
throws MalformedURLException, FileNotFoundException, URISyntaxException, CreateModelException, SubscriberInitializationException, IOException, InterruptedException;
/**
* Same as {@link #createRaceTracker(URL, URI, URI, URI, TimePoint, TimePoint, WindStore, TrackedRegattaRegistry)},
* only that a predefined {@link Regatta} is used to hold the resulting races.
* @param timeoutInMilliseconds TODO
*/
RaceTracker createRaceTracker(Regatta regatta, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry,
RaceLogResolver raceLogResolver, RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds)
throws MalformedURLException, FileNotFoundException, URISyntaxException, CreateModelException,
SubscriberInitializationException;
SubscriberInitializationException, IOException, InterruptedException;
BoatClass getOrCreateBoatClass(String competitorClassName);
@@ -161,7 +159,6 @@ public interface DomainFactory {
* must have been created before through
* {@link #getOrCreateTrackedRegatta(com.sap.sailing.domain.base.Regatta)} because otherwise the link to
* the {@link IEvent} can't be established
* @param timeoutInMilliseconds TODO
* @param tokenToRetrieveAssociatedRace
* used to update the set of{@link RaceDefinition}s received by the {@link RaceCourseReceiver} created by
* this call
@@ -173,47 +170,39 @@ public interface DomainFactory {
IEventSubscriber eventSubscriber, IRaceSubscriber raceSubscriber, boolean useInternalMarkPassingAlgorithm, long timeoutInMilliseconds);
/**
* Creates a {@link RaceDefinition} from a TracTrac {@link IRace} and a
* domain {@link Course} definition. The resulting {@link RaceDefinition} is
* added to the {@link com.sap.sailing.domain.base.Regatta} to which
* <code>trackedRegatta</code> belongs (see
* {@link TrackedRegatta#getRegatta()}). It is added to the internal race
* cache. The corresponding {@link TrackedRace} object is also created, and
* the notification of threads waiting on the race cache such as a blocking
* {@link #getAndWaitForRaceDefinition(UUID)} happens only <em>after</em>
* the tracked race has been created and the {@link RaceDefinition} was
* {@link com.sap.sailing.domain.base.Regatta#addRace(RaceDefinition) added}
* to the domain event. This ensures that waiters for the
* {@link RaceDefinition} are guaranteed to obtain a valid,
* non-<code>null</code> tracked race already immediately after the
* notification was sent, and that the {@link RaceDefinition} is already
* {@link com.sap.sailing.domain.base.Regatta#getAllRaces() known} by its
* containing {@link com.sap.sailing.domain.base.Regatta}.
* Creates a {@link RaceDefinition} from a TracTrac {@link IRace} and a domain {@link Course} definition. The
* resulting {@link RaceDefinition} is added to the {@link com.sap.sailing.domain.base.Regatta} to which
* <code>trackedRegatta</code> belongs (see {@link TrackedRegatta#getRegatta()}). It is added to the internal race
* cache. The corresponding {@link TrackedRace} object is also created, and the notification of threads waiting on
* the race cache such as a blocking {@link #getAndWaitForRaceDefinition(UUID)} happens only <em>after</em> the
* tracked race has been created and the {@link RaceDefinition} was
* {@link com.sap.sailing.domain.base.Regatta#addRace(RaceDefinition) added} to the domain event. This ensures that
* waiters for the {@link RaceDefinition} are guaranteed to obtain a valid, non-<code>null</code> tracked race
* already immediately after the notification was sent, and that the {@link RaceDefinition} is already
* {@link com.sap.sailing.domain.base.Regatta#getAllRaces() known} by its containing
* {@link com.sap.sailing.domain.base.Regatta}.
*
* @param raceDefinitionSetToUpdate
* if not <code>null</code>, after creating the
* {@link TrackedRace}, the {@link RaceDefinition} is
* {@link DynamicRaceDefinitionSet#addRaceDefinition(RaceDefinition, DynamicTrackedRace)
* added} to that object.
* if not <code>null</code>, after creating the {@link TrackedRace}, the {@link RaceDefinition} is
* {@link DynamicRaceDefinitionSet#addRaceDefinition(RaceDefinition, DynamicTrackedRace) added} to that
* object.
* @param runBeforeExposingRace
* if not {@code null} then this consumer will be passed the
* {@link DynamicTrackedRace} if it was actually created by this
* call. This happens while still in the
* {@code synchronized(raceCache)} block, therefore before calls
* waiting for the race (e.g.,
* {@link #getAndWaitForRaceDefinition(UUID)}) return the race.
* @param tractracRace TODO
*/
DynamicTrackedRace getOrCreateRaceDefinitionAndTrackedRace(DynamicTrackedRegatta trackedRegatta, UUID raceId,
String raceName, Iterable<com.sap.sailing.domain.base.Competitor> competitors, BoatClass boatClass,
Map<Competitor, Boat> competitorBoats, Course course, Iterable<Sideline> sidelines, WindStore windStore,
long delayToLiveInMillis, long millisecondsOverWhichToAverageWind,
DynamicRaceDefinitionSet raceDefinitionSetToUpdate, URI courseDesignUpdateURI, UUID tracTracEventUuid,
String tracTracUsername, String tracTracPassword, boolean ignoreTracTracMarkPassings, RaceLogResolver raceLogResolver, Consumer<DynamicTrackedRace> runBeforeExposingRace, IRace tractracRace);
* if not {@code null} then this consumer will be passed the {@link DynamicTrackedRace} if it was
* actually created by this call. This happens while still in the {@code synchronized(raceCache)} block,
* therefore before calls waiting for the race (e.g., {@link #getAndWaitForRaceDefinition(UUID)}) return
* the race.
*/
DynamicTrackedRace getOrCreateRaceDefinitionAndTrackedRace(DynamicTrackedRegatta trackedRegatta, UUID raceId,
String raceName, Iterable<com.sap.sailing.domain.base.Competitor> competitors, BoatClass boatClass,
Map<Competitor, Boat> competitorBoats, Course course, Iterable<Sideline> sidelines, WindStore windStore,
long delayToLiveInMillis, long millisecondsOverWhichToAverageWind,
DynamicRaceDefinitionSet raceDefinitionSetToUpdate, URI courseDesignUpdateURI, UUID tracTracEventUuid,
String tracTracUsername, String tracTracPassword, boolean ignoreTracTracMarkPassings,
RaceLogResolver raceLogResolver, Consumer<DynamicTrackedRace> runBeforeExposingRace, IRace tractracRace);
/**
* The record may be for a single mark or a gate. If for a gate, the
* {@link ControlPointPositionData#getIndex() index} is used to determine
* which of its marks is affected.
* The record may be for a single mark or a gate. If for a gate, the {@link ControlPointPositionData#getIndex()
* index} is used to determine which of its marks is affected.
*/
Mark getMark(TracTracControlPoint controlPoint, int zeroBasedMarkIndex);
@@ -223,8 +212,6 @@ public interface DomainFactory {
/**
* If the vm argument tractrac.usemarkpassings=false, the RecieverType MARKPASSINGS will not return anything
* @param raceLogResolver TODO
* @param timeoutInMilliseconds TODO
*/
Iterable<Receiver> getUpdateReceivers(DynamicTrackedRegatta trackedRegatta, IRace tractracRace, WindStore windStore,
long delayToLiveInMillis, Simulator simulator, DynamicRaceDefinitionSet raceDefinitionSetToUpdate, TrackedRegattaRegistry trackedRegattaRegistry,
@@ -274,23 +261,17 @@ public interface DomainFactory {
* {@code File}) then if this parameter is {@code true} the race will be loaded from the replay file
* instead of the {@code storedURI}/{@code liveURI} specified. This is particularly useful for restoring
* races if since the last connection the race was migrated to a replay file format.
* @param timeoutInMillis TODO
*/
RaceTrackingConnectivityParameters createTrackingConnectivityParameters(URL paramURL, URI liveURI, URI storedURI,
URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility, boolean trackWind, boolean correctWindDirectionByMagneticDeclination,
boolean preferReplayIfAvailable, int timeoutInMillis) throws Exception;
/**
* Removes all knowledge about <code>tractracRace</code> which includes removing it from the race cache, from the
* {@link com.sap.sailing.domain.base.Regatta} and, if a {@link TrackedRace} for the corresponding
* {@link RaceDefinition} exists, from the {@link TrackedRegatta}. If removing the race from the event, the event is
* removed from the event cache such that {@link #getOrCreateDefaultRegatta(IEvent, TrackedRegattaRegistry)} will have to create a new one. Similarly,
* if the {@link TrackedRace} that was removed from the {@link TrackedRegatta} was the last one, the
* {@link TrackedRegatta} is removed such that {@link #getOrCreateTrackedRegatta(com.sap.sailing.domain.base.Regatta)}
* will have to create a new one.
* Removes all knowledge about <code>tractracRace</code> from the race cache.
*/
void removeRace(IEvent tractracEvent, IRace tractracRace, TrackedRegattaRegistry trackedRegattaRegistry);
RaceDefinition removeRace(IEvent tractracEvent, IRace tractracRace, Regatta regattaToLoadRaceInto, TrackedRegattaRegistry trackedRegattaRegistry);
/**
* Computes an ID to use for a {@link RaceDefinition} based on the TracTrac race.
@@ -1,9 +1,7 @@
package com.sap.sailing.domain.tractracadapter.impl;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
@@ -457,7 +455,7 @@ public class DomainFactoryImpl implements DomainFactory {
}
@Override
public void removeRace(IEvent tractracEvent, IRace tractracRace, TrackedRegattaRegistry trackedRegattaRegistry) {
public RaceDefinition removeRace(IEvent tractracEvent, IRace tractracRace, Regatta regattaToLoadRaceInto, TrackedRegattaRegistry trackedRegattaRegistry) {
RaceDefinition raceDefinition;
synchronized (raceCache) {
raceDefinition = getExistingRaceDefinitionForRace(tractracRace.getId());
@@ -467,45 +465,33 @@ public class DomainFactoryImpl implements DomainFactory {
}
}
if (raceDefinition != null) {
// FIXME this only removes the race from its default regatta, not any explicit regatta
Pair<String, BoatClass> defaultRegattaNameAndBoatClass = getDefaultRegattaNameAndBoatClass(tractracRace);
Pair<String, String> key = new Pair<String, String>(defaultRegattaNameAndBoatClass.getA(),
defaultRegattaNameAndBoatClass.getB() == null ? null :
defaultRegattaNameAndBoatClass.getB().getName());
synchronized (regattaCache) {
Regatta regatta = regattaCache.get(key);
final Regatta regatta;
final Pair<String, String> key;
if (regattaToLoadRaceInto != null) {
regatta = regattaToLoadRaceInto;
key = new Pair<>(regatta.getName(), regatta.getBoatClass().getName());
} else {
Pair<String, BoatClass> defaultRegattaNameAndBoatClass = getDefaultRegattaNameAndBoatClass(tractracRace);
key = new Pair<String, String>(defaultRegattaNameAndBoatClass.getA(),
defaultRegattaNameAndBoatClass.getB() == null ? null :
defaultRegattaNameAndBoatClass.getB().getName());
regatta = regattaCache.get(key);
}
if (regatta != null) {
// The following fixes bug 202: when tracking of multiple races of the same event has been started, this may not
// remove any race; however, the event may already have been created by another tracker whose race hasn't
// arrived yet and therefore the races list is still empty; therefore, only remove the event if its
// race list became empty by the removal performed here.
int oldSize = Util.size(regatta.getAllRaces());
regatta.removeRace(raceDefinition);
if (oldSize > 0 && Util.size(regatta.getAllRaces()) == 0) {
if (Util.contains(regatta.getAllRaces(), raceDefinition) && Util.size(regatta.getAllRaces()) == 1) {
logger.info("Removing regatta "+regatta.getName()+" ("+regatta.hashCode()+") from TracTrac DomainFactoryImpl");
regattaCache.remove(key);
weakDefaultRegattaCache.remove(tractracRace);
}
TrackedRegatta trackedRegatta = trackedRegattaRegistry.getTrackedRegatta(regatta);
if (trackedRegatta != null) {
// see above; only remove tracked regatta if it *became* empty because of the tracked race removal here
final int oldSizeOfTrackedRaces;
final int newSizeOfTrackedRaces;
trackedRegatta.lockTrackedRacesForWrite();
try {
oldSizeOfTrackedRaces = Util.size(trackedRegatta.getTrackedRaces());
trackedRegatta.removeTrackedRace(raceDefinition);
newSizeOfTrackedRaces = Util.size(trackedRegatta.getTrackedRaces());
} finally {
trackedRegatta.unlockTrackedRacesAfterWrite();
}
if (oldSizeOfTrackedRaces > 0 && newSizeOfTrackedRaces == 0) {
trackedRegattaRegistry.removeTrackedRegatta(regatta);
}
}
}
}
}
return raceDefinition;
}
@Override
@@ -716,17 +702,19 @@ public class DomainFactoryImpl implements DomainFactory {
}
@Override
public TracTracRaceTracker createRaceTracker(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry,
RaceLogResolver raceLogResolver, RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds) throws MalformedURLException,
FileNotFoundException, URISyntaxException, CreateModelException, SubscriberInitializationException {
public TracTracRaceTracker createRaceTracker(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver,
RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds)
throws URISyntaxException, CreateModelException, SubscriberInitializationException, IOException,
InterruptedException {
return new TracTracRaceTrackerImpl(this, raceLogStore, regattaLogStore, windStore, trackedRegattaRegistry, raceLogResolver, connectivityParams, timeoutInMilliseconds);
}
@Override
public RaceTracker createRaceTracker(Regatta regatta, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds)
throws MalformedURLException, FileNotFoundException, URISyntaxException, CreateModelException,
SubscriberInitializationException {
throws URISyntaxException, CreateModelException,
SubscriberInitializationException, IOException, InterruptedException {
return new TracTracRaceTrackerImpl(regatta, this, raceLogStore, regattaLogStore, windStore, trackedRegattaRegistry,
raceLogResolver, connectivityParams, timeoutInMilliseconds);
}
@@ -1,7 +1,6 @@
package com.sap.sailing.domain.tractracadapter.impl;
import java.io.FileNotFoundException;
import java.net.MalformedURLException;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
@@ -116,9 +115,10 @@ public class RaceTrackingConnectivityParametersImpl extends AbstractRaceTracking
@Override
public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore,
RaceLogResolver raceLogResolver, long timeoutInMilliseconds) throws MalformedURLException, FileNotFoundException, URISyntaxException,
CreateModelException, SubscriberInitializationException {
RaceTracker tracker = domainFactory.createRaceTracker(raceLogStore, regattaLogStore, windStore, trackedRegattaRegistry, raceLogResolver, this, timeoutInMilliseconds);
RaceLogResolver raceLogResolver, long timeoutInMilliseconds) throws URISyntaxException,
CreateModelException, SubscriberInitializationException, IOException, InterruptedException {
RaceTracker tracker = domainFactory.createRaceTracker(raceLogStore, regattaLogStore, windStore,
trackedRegattaRegistry, raceLogResolver, this, timeoutInMilliseconds);
return tracker;
}
@@ -1,7 +1,6 @@
package com.sap.sailing.domain.tractracadapter.impl;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -263,7 +262,7 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl
TracTracRaceTrackerImpl(DomainFactory domainFactory, RaceLogStore raceLogStore,
RegattaLogStore regattaLogStore, WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry,
RaceLogResolver raceLogResolver, RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds)
throws URISyntaxException, MalformedURLException, FileNotFoundException, SubscriberInitializationException {
throws URISyntaxException, SubscriberInitializationException, IOException, InterruptedException {
this(/* regatta */ null, domainFactory, raceLogStore, regattaLogStore, windStore, trackedRegattaRegistry,
raceLogResolver, connectivityParams, timeoutInMilliseconds);
}
@@ -285,7 +284,7 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl
TracTracRaceTrackerImpl(final Regatta regatta, DomainFactory domainFactory, RaceLogStore raceLogStore,
RegattaLogStore regattaLogStore, WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry,
RaceLogResolver raceLogResolver, RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds)
throws URISyntaxException, MalformedURLException, FileNotFoundException, SubscriberInitializationException {
throws URISyntaxException, SubscriberInitializationException, IOException, InterruptedException {
super(connectivityParams);
final URL paramURL = connectivityParams.getParamURL();
final URI liveURI = connectivityParams.getLiveURI();
@@ -388,7 +387,10 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl
// removeRace may detach the domain regatta from the domain factory if that
// removed the last race; therefore, it's important to getOrCreate the
// domain regatta *after* calling removeRace
domainFactory.removeRace(tractracRace.getEvent(), tractracRace, trackedRegattaRegistry);
final RaceDefinition raceDefinition = domainFactory.removeRace(tractracRace.getEvent(), tractracRace, effectiveRegatta, trackedRegattaRegistry);
if (raceDefinition != null) {
trackedRegattaRegistry.removeRace(effectiveRegatta, raceDefinition);
}
// if regatta is still null, no previous assignment of any of the races in this TracTrac event to a Regatta was
// found;
// in this case, create a default regatta based on the TracTrac event data
@@ -24,7 +24,12 @@ public interface RaceTrackingConnectivityParameters {
* Starts a {@link RaceTracker} using the connectivity parameters provided by this object. As no specific
* {@link Regatta} is provided, this will first look up a regatta for the race from
* {@link TrackedRegattaRegistry#getRememberedRegattaForRace(java.io.Serializable)} and if not found will look up or
* create a default regatta based on race data such as an event name and the boat class.
* create a default regatta based on race data such as an event name and the boat class.<p>
*
* If a race equal to the one being created by this call already exists in the default regatta, its tracking
* will be stopped (if any), the {@link RaceDefinition} will be removed from the default regatta, and the
* {@link TrackedRace} will be removed from the {@link TrackedRegatta}. All this happens by means of
* calling {@link TrackedRegattaRegistry#removeRace(Regatta, com.sap.sailing.domain.base.RaceDefinition)}.
*
* @param timeoutInMilliseconds
* gives the tracker a possibility to abort tracking the race after so many milliseconds of
@@ -37,11 +42,15 @@ public interface RaceTrackingConnectivityParameters {
RaceLogResolver raceLogResolver, long timeoutInMilliseconds) throws Exception;
/**
* Starts a {@link RaceTracker}, associating the resulting races with the {@link Regatta} passed as argument
* instead of using the tracker's domain factory to obtain a default {@link Regatta} object for the tracking
* parameters. This is particularly useful if a predefined regatta with {@link Series} and {@link Fleet}s
* is to be used.
* @param timeoutInMilliseconds TODO
* Starts a {@link RaceTracker}, associating the resulting races with the {@link Regatta} passed as argument instead
* of using the tracker's domain factory to obtain a default {@link Regatta} object for the tracking parameters.
* This is particularly useful if a predefined regatta with {@link Series} and {@link Fleet}s is to be used.
* <p>
*
* If a race equal to the one being created by this call already exists in the {@code regatta}, its tracking will be
* stopped (if any), the {@link RaceDefinition} will be removed from the default regatta, and the
* {@link TrackedRace} will be removed from the {@link TrackedRegatta}. All this happens by means of calling
* {@link TrackedRegattaRegistry#removeRace(Regatta, com.sap.sailing.domain.base.RaceDefinition)}.
*/
RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore,
RaceLogResolver raceLogResolver, long timeoutInMilliseconds) throws Exception;
@@ -4,6 +4,8 @@ import java.io.IOException;
import java.io.Serializable;
import java.net.MalformedURLException;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
@@ -28,6 +30,27 @@ public interface TrackedRegattaRegistry {
*/
DynamicTrackedRegatta getTrackedRegatta(Regatta regatta);
/**
* Removes <code>race</code> and any corresponding {@link #getTrackedRace(Regatta, RaceDefinition) tracked race}
* from this service. If it was the last {@link RaceDefinition} in its {@link Regatta} and the regatta
* {@link Regatta#isPersistent() is not stored persistently}, the <code>regatta</code> is removed as well and will no
* longer be returned by {@link #getAllRegattas()}. The wind tracking is stopped for <code>race</code>.
* <p>
*
* Any {@link RaceTracker} for which <code>race</race> is the last race tracked that is still reachable
* from {@link #getAllRegattas()} will be {@link RaceTracker#stop(boolean) stopped}.
*
* The <code>race</code> will be also removed from all leaderboards containing a column that has <code>race</code>'s
* {@link #getTrackedRace(Regatta, RaceDefinition) corresponding} {@link TrackedRace} as its
* {@link RaceColumn#getTrackedRace(Fleet)}.
*
* @param regatta
* the regatta from which to remove the race
* @param race
* the race to remove
*/
void removeRace(Regatta regatta, RaceDefinition race) throws MalformedURLException, IOException,InterruptedException;
void removeTrackedRegatta(Regatta regatta);
/**
@@ -29,7 +29,6 @@ import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.LeaderboardSearchResult;
import com.sap.sailing.domain.base.LeaderboardSearchResultBase;
import com.sap.sailing.domain.base.Mark;
@@ -174,27 +173,6 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch
*/
void stopTracking(Regatta regatta, boolean willBeRemoved) throws MalformedURLException, IOException, InterruptedException;
/**
* Removes <code>race</code> and any corresponding {@link #getTrackedRace(Regatta, RaceDefinition) tracked race}
* from this service. If it was the last {@link RaceDefinition} in its {@link Regatta} and the regatta
* {@link Regatta#isPersistent() is not stored persistently}, the <code>regatta</code> is removed as well and will no
* longer be returned by {@link #getAllRegattas()}. The wind tracking is stopped for <code>race</code>.
* <p>
*
* Any {@link RaceTracker} for which <code>race</race> is the last race tracked that is still reachable
* from {@link #getAllRegattas()} will be {@link RaceTracker#stop(boolean) stopped}.
*
* The <code>race</code> will be also removed from all leaderboards containing a column that has <code>race</code>'s
* {@link #getTrackedRace(Regatta, RaceDefinition) corresponding} {@link TrackedRace} as its
* {@link RaceColumn#getTrackedRace(Fleet)}.
*
* @param regatta
* the regatta from which to remove the race
* @param race
* the race to remove
*/
void removeRace(Regatta regatta, RaceDefinition race) throws MalformedURLException, IOException,InterruptedException;
/**
* @param port
* the UDP port on which to listen for incoming messages from Expedition clients
@@ -2415,19 +2415,27 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
TrackedRace trackedRace = getExistingTrackedRace(regatta, race);
if (trackedRace != null) {
TrackedRegatta trackedRegatta = getTrackedRegatta(regatta);
final boolean isTrackedRacesEmpty;
final boolean isTrackedRacesBecameEmpty;
if (trackedRegatta != null) {
trackedRegatta.lockTrackedRacesForWrite();
// The following fixes bug 202: when tracking of multiple races of the same event has been started, this may not
// remove any race; however, the event may already have been created by another tracker whose race hasn't
// arrived yet and therefore the races list is still empty; therefore, only remove the event if its
// race list became empty by the removal performed here.
final int oldSizeOfTrackedRaces;
final int newSizeOfTrackedRaces;
oldSizeOfTrackedRaces = Util.size(trackedRegatta.getTrackedRaces());
try {
trackedRegatta.removeTrackedRace(trackedRace);
isTrackedRacesEmpty = Util.isEmpty(trackedRegatta.getTrackedRaces());
newSizeOfTrackedRaces = Util.size(trackedRegatta.getTrackedRaces());
isTrackedRacesBecameEmpty = (oldSizeOfTrackedRaces > 0 && newSizeOfTrackedRaces == 0);
} finally {
trackedRegatta.unlockTrackedRacesAfterWrite();
}
} else {
isTrackedRacesEmpty = false;
isTrackedRacesBecameEmpty = false;
}
if (isTrackedRacesEmpty) {
if (isTrackedRacesBecameEmpty) {
removeTrackedRegatta(regatta);
}
// remove tracked race from RaceColumns of regatta