Merge remote-tracking branch 'server/bug3302'

This commit is contained in:
Axel Uhl committed 2015-10-02 07:24:49 +02:00
commit 33e93f595e
20 files changed
+768 -181

No files matched your search

@@ -41,15 +41,15 @@ public class CompetitorWithID implements Competitor {
return id;
}
@Override
public String toString() {
return "CompetitorWithID [boatID=" + boatID + ", threeLetterIOCCode="
+ threeLetterIOCCode + ", name=" + name + ", id=" + id + "]";
}
@Override
public String toString() {
return "CompetitorWithID [boatID=" + boatID + ", threeLetterIOCCode=" + threeLetterIOCCode + ", name=" + name
+ ", id=" + id + "]";
}
@Override
public List<CrewMember> getCrew() {
return crew;
}
@Override
public List<CrewMember> getCrew() {
return crew;
}
}
@@ -249,12 +249,12 @@ public class DomainFactoryImpl implements DomainFactory {
private Iterable<Competitor> createCompetitorList(StartList startList, String raceId, BoatClass boatClass) {
List<Competitor> result = new ArrayList<Competitor>();
for (com.sap.sailing.domain.swisstimingadapter.Competitor swissTimingCompetitor : startList.getCompetitors()) {
Competitor domainCompetitor;
if(swissTimingCompetitor.getID() != null) {
Competitor domainCompetitor;
if (swissTimingCompetitor.getID() != null) {
domainCompetitor = createCompetitorWithID(swissTimingCompetitor, boatClass);
} else {
} else {
domainCompetitor = createCompetitorWithoutID(swissTimingCompetitor, raceId, boatClass);
}
}
result.add(domainCompetitor);
}
return result;
@@ -0,0 +1,282 @@
package com.sap.sailing.domain.test;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.junit.Before;
import org.junit.Test;
import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEventFactory;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogRegisterCompetitorEventImpl;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.impl.BoatClassImpl;
import com.sap.sailing.domain.base.impl.CourseAreaImpl;
import com.sap.sailing.domain.base.impl.FleetImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.SeriesImpl;
import com.sap.sailing.domain.common.BoatClassMasterdata;
import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.domain.leaderboard.impl.CompetitorProviderFromRaceColumnsAndRegattaLike;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.MillisecondsTimePoint;
/**
* Tests the behavior of the class {@link CompetitorProviderFromRaceColumnsAndRegattaLike}, paying particular
* attention to its caching and cache invalidation logic. When competitors are added to regatta logs or race logs
* or when a tracked race is attached to a column or a column is added or removed, its caches need to be
* invalidated and re-calculated accordingly.
*
* @author Axel Uhl (d043530)
*
*/
public class CompetitorProviderCacheInvalidationTest extends AbstractLeaderboardTest {
private CompetitorProviderFromRaceColumnsAndRegattaLike competitorProviderFlexibleLeaderboard;
private CompetitorProviderFromRaceColumnsAndRegattaLike competitorProviderRegattaLeaderboard;
private FlexibleLeaderboardImpl flexibleLeaderboard;
private RegattaLeaderboardImpl regattaLeaderboard;
private RegattaImpl regatta;
final int NUMBER_OF_COMP_LISTS = 4;
@SuppressWarnings("unchecked")
private List<Competitor>[] compLists = (List<Competitor>[]) new List<?>[NUMBER_OF_COMP_LISTS];
@Before
public void setUp() {
final CourseAreaImpl courseArea = new CourseAreaImpl(
"Test Course Area", UUID.randomUUID());
flexibleLeaderboard = new FlexibleLeaderboardImpl("Test Flexible Leaderboard",
new ThresholdBasedResultDiscardingRuleImpl(new int[0]), new LowPoint(), courseArea);
competitorProviderFlexibleLeaderboard = new CompetitorProviderFromRaceColumnsAndRegattaLike(flexibleLeaderboard);
regatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE,
EmptyRegattaLogStore.INSTANCE, "Test Regatta", new BoatClassImpl("49er", BoatClassMasterdata._49ER), /* startDate */
null, /* endDate */null, /* trackedRegattaRegistry */null, new LowPoint(), UUID.randomUUID(),
courseArea);
regattaLeaderboard = new RegattaLeaderboardImpl(regatta, new ThresholdBasedResultDiscardingRuleImpl(new int[0]));
regatta.addSeries(new SeriesImpl("Test Series", /* isMedal */false, Arrays.asList(new FleetImpl("Yellow"),
new FleetImpl("Blue")), Arrays.asList("R1", "R2", "R3"), /* trackedRegattaRegistry */null));
for (int l = 0; l < NUMBER_OF_COMP_LISTS; l++) {
compLists[l] = new ArrayList<Competitor>();
for (int i = 0; i < 10; i++) {
compLists[l].add(createCompetitor("" + l + "/" + i));
}
}
competitorProviderRegattaLeaderboard = new CompetitorProviderFromRaceColumnsAndRegattaLike(regattaLeaderboard);
}
@Test
public void testSimpleCompetitorListOnOneRaceInFlexibleLeaderboard() {
TrackedRace trackedRace = new MockedTrackedRaceWithStartTimeAndRanks(MillisecondsTimePoint.now(), compLists[0]);
flexibleLeaderboard.addRace(trackedRace, "R1", /* medalRace */ false);
Set<Competitor> expected = new HashSet<>(compLists[0]);
Set<Competitor> actual = new HashSet<>();
Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(), actual);
assertEquals(expected, actual);
Set<Competitor> actualForRace = new HashSet<>();
Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(
flexibleLeaderboard.getRaceColumnByName("R1"),
flexibleLeaderboard.getFleet(LeaderboardNameConstants.DEFAULT_FLEET_NAME)), actualForRace);
assertEquals(expected, actualForRace);
}
@Test
public void testSimpleCompetitorListOnOneRaceLogInFlexibleLeaderboard() {
flexibleLeaderboard.addRaceColumn("R1", /* medalRace */ false);
RaceLog raceLog = flexibleLeaderboard.getRacelog("R1", LeaderboardNameConstants.DEFAULT_FLEET_NAME);
for (Competitor c : compLists[0]) {
raceLog.add(RaceLogEventFactory.INSTANCE.createRegisterCompetitorEvent(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Me", 0), 1, c));
}
Set<Competitor> expected = new HashSet<>(compLists[0]);
Set<Competitor> actual = new HashSet<>();
Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(), actual);
assertEquals(expected, actual);
Set<Competitor> actualForRace = new HashSet<>();
Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(
flexibleLeaderboard.getRaceColumnByName("R1"),
flexibleLeaderboard.getFleet(LeaderboardNameConstants.DEFAULT_FLEET_NAME)), actualForRace);
assertEquals(expected, actualForRace);
}
@Test
public void testSimpleCompetitorListOnRegattaLogInFlexibleLeaderboard() {
flexibleLeaderboard.addRaceColumn("R1", /* medalRace */ false);
RegattaLog regattaLog = flexibleLeaderboard.getRegattaLog();
for (Competitor c : compLists[0]) {
regattaLog.add(new RegattaLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Me", 0), MillisecondsTimePoint.now(), UUID.randomUUID(), c));
}
Set<Competitor> expected = new HashSet<>(compLists[0]);
Set<Competitor> actual = new HashSet<>();
Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(), actual);
assertEquals(expected, actual);
Set<Competitor> actualForRace = new HashSet<>();
Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(
flexibleLeaderboard.getRaceColumnByName("R1"),
flexibleLeaderboard.getFleet(LeaderboardNameConstants.DEFAULT_FLEET_NAME)), actualForRace);
assertEquals(expected, actualForRace);
}
@Test
public void testSimpleCompetitorListOnOneRaceLogAndRegattaLogInFlexibleLeaderboard() {
flexibleLeaderboard.addRaceColumn("R1", /* medalRace */ false);
RegattaLog regattaLog = flexibleLeaderboard.getRegattaLog();
for (Competitor c : compLists[0]) {
regattaLog.add(new RegattaLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Me", 0), MillisecondsTimePoint.now(), UUID.randomUUID(), c));
}
RaceLog raceLog = flexibleLeaderboard.getRacelog("R1", LeaderboardNameConstants.DEFAULT_FLEET_NAME);
for (Competitor c : compLists[1]) {
raceLog.add(RaceLogEventFactory.INSTANCE.createRegisterCompetitorEvent(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Me", 0), 1, c));
}
Set<Competitor> expected = new HashSet<>(compLists[0]);
expected.addAll(compLists[1]);
Set<Competitor> actual = new HashSet<>();
Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(), actual);
assertEquals(expected, actual);
Set<Competitor> actualForRace = new HashSet<>();
Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(
flexibleLeaderboard.getRaceColumnByName("R1"),
flexibleLeaderboard.getFleet(LeaderboardNameConstants.DEFAULT_FLEET_NAME)), actualForRace);
assertEquals(expected, actualForRace);
}
@Test
public void testTwoCompetitorListsOnTwoRacesInFlexibleLeaderboard() {
TrackedRace trackedRace1 = new MockedTrackedRaceWithStartTimeAndRanks(MillisecondsTimePoint.now(), compLists[0]);
flexibleLeaderboard.addRace(trackedRace1, "R1", /* medalRace */ false);
TrackedRace trackedRace2 = new MockedTrackedRaceWithStartTimeAndRanks(MillisecondsTimePoint.now(), compLists[1]);
flexibleLeaderboard.addRace(trackedRace2, "R2", /* medalRace */ false);
Set<Competitor> expected = new HashSet<>(compLists[0]);
expected.addAll(compLists[1]);
Set<Competitor> actual = new HashSet<>();
Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(), actual);
assertEquals(expected, actual);
Set<Competitor> actualForRace1 = new HashSet<>();
Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(
flexibleLeaderboard.getRaceColumnByName("R1"),
flexibleLeaderboard.getFleet(LeaderboardNameConstants.DEFAULT_FLEET_NAME)), actualForRace1);
Set<Competitor> actualForRace2 = new HashSet<>();
Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(
flexibleLeaderboard.getRaceColumnByName("R2"),
flexibleLeaderboard.getFleet(LeaderboardNameConstants.DEFAULT_FLEET_NAME)), actualForRace2);
assertEquals(new HashSet<>(compLists[0]), actualForRace1);
assertEquals(new HashSet<>(compLists[1]), actualForRace2);
}
@Test
public void testSimpleCompetitorListOnOneRaceInRegattaLeaderboard() {
TrackedRace trackedRace = new MockedTrackedRaceWithStartTimeAndRanks(MillisecondsTimePoint.now(), compLists[0]);
regattaLeaderboard.getRaceColumnByName("R1").setTrackedRace(regattaLeaderboard.getRaceColumnByName("R1").getFleetByName("Yellow"), trackedRace);
Set<Competitor> expected = new HashSet<>(compLists[0]);
Set<Competitor> actual = new HashSet<>();
Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(), actual);
assertEquals(expected, actual);
Set<Competitor> actualForRaceYellow = new HashSet<>();
Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(
regattaLeaderboard.getRaceColumnByName("R1"), regattaLeaderboard.getFleet("Yellow")), actualForRaceYellow);
assertEquals(expected, actualForRaceYellow);
Set<Competitor> actualForRaceBlue = new HashSet<>();
Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(
regattaLeaderboard.getRaceColumnByName("R1"), regattaLeaderboard.getFleet("Blue")), actualForRaceBlue);
assertEquals(Collections.emptySet(), actualForRaceBlue);
}
@Test
public void testTwoCompetitorListsOnTwoRacesInRegattaLeaderboard() {
TrackedRace trackedRace1 = new MockedTrackedRaceWithStartTimeAndRanks(MillisecondsTimePoint.now(), compLists[0]);
regattaLeaderboard.getRaceColumnByName("R1").setTrackedRace(regattaLeaderboard.getRaceColumnByName("R1").getFleetByName("Yellow"), trackedRace1);
TrackedRace trackedRace2 = new MockedTrackedRaceWithStartTimeAndRanks(MillisecondsTimePoint.now(), compLists[1]);
regattaLeaderboard.getRaceColumnByName("R1").setTrackedRace(regattaLeaderboard.getRaceColumnByName("R1").getFleetByName("Blue"), trackedRace2);
Set<Competitor> expected = new HashSet<>(compLists[0]);
expected.addAll(compLists[1]);
Set<Competitor> actual = new HashSet<>();
Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(), actual);
assertEquals(expected, actual);
Set<Competitor> actualForRaceYellow = new HashSet<>();
Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(
regattaLeaderboard.getRaceColumnByName("R1"), regattaLeaderboard.getFleet("Yellow")), actualForRaceYellow);
assertEquals(new HashSet<>(compLists[0]), actualForRaceYellow);
Set<Competitor> actualForRaceBlue = new HashSet<>();
Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(
regattaLeaderboard.getRaceColumnByName("R1"), regattaLeaderboard.getFleet("Blue")), actualForRaceBlue);
assertEquals(new HashSet<>(compLists[1]), actualForRaceBlue);
}
@Test
public void testSimpleCompetitorListOnOneRaceLogInRegattaLeaderboard() {
RaceLog raceLog = regattaLeaderboard.getRacelog("R1", "Yellow");
for (Competitor c : compLists[0]) {
raceLog.add(RaceLogEventFactory.INSTANCE.createRegisterCompetitorEvent(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Me", 0), 1, c));
}
Set<Competitor> expected = new HashSet<>(compLists[0]);
Set<Competitor> actual = new HashSet<>();
Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(), actual);
assertEquals(expected, actual);
Set<Competitor> actualForRaceYellow = new HashSet<>();
Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(
regattaLeaderboard.getRaceColumnByName("R1"), regattaLeaderboard.getFleet("Yellow")), actualForRaceYellow);
assertEquals(new HashSet<>(compLists[0]), actualForRaceYellow);
}
@Test
public void testSimpleCompetitorListOnRegattaLogInRegattaLeaderboard() {
RegattaLog regattaLog = regattaLeaderboard.getRegatta().getRegattaLog();
for (Competitor c : compLists[0]) {
regattaLog.add(new RegattaLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Me", 0), MillisecondsTimePoint.now(), UUID.randomUUID(), c));
}
Set<Competitor> expected = new HashSet<>(compLists[0]);
Set<Competitor> actual = new HashSet<>();
Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(), actual);
assertEquals(expected, actual);
Set<Competitor> actualForRaceYellow = new HashSet<>();
Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(
regattaLeaderboard.getRaceColumnByName("R1"), regattaLeaderboard.getFleet("Yellow")), actualForRaceYellow);
assertEquals(new HashSet<>(compLists[0]), actualForRaceYellow);
Set<Competitor> actualForRaceBlue = new HashSet<>();
Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(
regattaLeaderboard.getRaceColumnByName("R1"), regattaLeaderboard.getFleet("Blue")), actualForRaceBlue);
assertEquals(new HashSet<>(compLists[0]), actualForRaceBlue);
}
@Test
public void testSimpleCompetitorListOnOneRaceLogAndRegattaLogInRegattaLeaderboard() {
RegattaLog regattaLog = regattaLeaderboard.getRegatta().getRegattaLog();
for (Competitor c : compLists[0]) {
regattaLog.add(new RegattaLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Me", 0), MillisecondsTimePoint.now(), UUID.randomUUID(), c));
}
RaceLog raceLog = regattaLeaderboard.getRacelog("R1", "Yellow");
for (Competitor c : compLists[1]) {
raceLog.add(RaceLogEventFactory.INSTANCE.createRegisterCompetitorEvent(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Me", 0), 1, c));
}
regattaLeaderboard.setSuppressed(compLists[0].get(compLists[0].size()-1), /* suppressed */ true);
Set<Competitor> expected = new HashSet<>(compLists[0]);
expected.addAll(compLists[1]);
Set<Competitor> expectedWithoutSuppressed = new HashSet<>(expected);
expectedWithoutSuppressed.remove(compLists[0].get(compLists[0].size()-1));
Set<Competitor> actual = new HashSet<>();
Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(), actual);
assertEquals(expected, actual);
Set<Competitor> actualWithoutSuppressed = new HashSet<>();
Util.addAll(regattaLeaderboard.getCompetitors(), actualWithoutSuppressed);
assertEquals(expectedWithoutSuppressed, actualWithoutSuppressed);
Set<Competitor> actualForRaceYellow = new HashSet<>();
Util.addAll(competitorProviderRegattaLeaderboard.getAllCompetitors(
regattaLeaderboard.getRaceColumnByName("R1"), regattaLeaderboard.getFleet("Yellow")), actualForRaceYellow);
assertEquals(expected, actualForRaceYellow);
}
}
@@ -49,6 +49,7 @@ import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.ranking.RankingMetric;
import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo;
import com.sap.sailing.domain.ranking.RankingMetricConstructor;
import com.sap.sailing.domain.regattalike.IsRegattaLike;
import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier;
import com.sap.sailing.domain.regattalike.RegattaLikeListener;
import com.sap.sailing.domain.tracking.CourseDesignChangedListener;
@@ -471,6 +472,21 @@ public class MockedTrackedRace implements DynamicTrackedRace {
public RaceColumn getRaceColumnByName(String raceColumnName) {
return null;
}
@Override
public IsRegattaLike getRegattaLike() {
return null;
}
@Override
public RaceLog getRacelog(String raceColumnName, String fleetName) {
return null;
}
@Override
public Iterable<? extends RaceColumn> getRaceColumns() {
return null;
}
};
}
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.base;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.MetaLeaderboard;
@@ -227,4 +228,18 @@ public interface RaceColumn extends Named {
boolean hasSplitFleets();
RaceExecutionOrderProvider getRaceExecutionOrderProvider();
/**
* Provides the combined set of competitors from all {@link #getTrackedRace(Fleet) tracked races attached to this
* column} or, in case a fleet does not have a tracked race attached, the competitors registered through the
* respective {@link RaceLog} {@link #getRaceLog(Fleet) attached to this column for that fleet}. Note that this does
* not include competitors registered through any {@link RegattaLog} on the structure owning (directly or
* transitively) this column.
*/
Iterable<Competitor> getAllCompetitors();
/**
* Same as {@link #getAllCompetitors()}, but restricted to the single race identified by the <code>fleet</code> parameter.
*/
Iterable<Competitor> getAllCompetitors(Fleet fleet);
}
@@ -4,6 +4,7 @@ import com.sap.sailing.domain.base.configuration.RegattaConfiguration;
import com.sap.sailing.domain.common.RankingMetrics;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.leaderboard.HasRaceColumnsAndRegattaLike;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.ranking.RankingMetricConstructor;
import com.sap.sailing.domain.ranking.RankingMetricsFactory;
@@ -22,7 +23,7 @@ import com.sap.sse.common.WithID;
* @author Axel Uhl (d043530)
*
*/
public interface Regatta extends Named, WithID, IsRegattaLike {
public interface Regatta extends Named, WithID, IsRegattaLike, HasRaceColumnsAndRegattaLike {
ScoringScheme getScoringScheme();
/**
@@ -3,11 +3,14 @@ package com.sap.sailing.domain.base.impl;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Logger;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.shared.analyzing.RegisteredCompetitorsAnalyzer;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
@@ -206,4 +209,41 @@ public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implem
public void setMasterDataExportOngoingThreadFlag(boolean flagValue) {
trackedRaces.setMasterDataExportOngoingThreadFlag(flagValue);
}
@Override
public Iterable<Competitor> getAllCompetitors() {
Set<Competitor> result = new HashSet<>();
for (Fleet fleet : getFleets()) {
TrackedRace trackedRace = getTrackedRace(fleet);
if (trackedRace != null) {
Util.addAll(trackedRace.getRace().getCompetitors(), result);
} else {
// if no tracked race is found, use competitors from race log; this assumes that if a tracked
// race exists, its competitors set takes precedence over what's in the race log. Usually,
// the tracked race will have the same competitors as those in the race log, or more because
// those from the regatta log are added to the tracked race as well.
Set<Competitor> viaRaceLog = new RegisteredCompetitorsAnalyzer<>(getRaceLog(fleet)).analyze();
result.addAll(viaRaceLog);
}
}
return result;
}
@Override
public Iterable<Competitor> getAllCompetitors(final Fleet fleet) {
final Iterable<Competitor> result;
TrackedRace trackedRace = getTrackedRace(fleet);
if (trackedRace != null) {
result = trackedRace.getRace().getCompetitors();
} else {
// if no tracked race is found, use competitors from race log; this assumes that if a tracked
// race exists, its competitors set takes precedence over what's in the race log. Usually,
// the tracked race will have the same competitors as those in the race log, or more because
// those from the regatta log are added to the tracked race as well.
Set<Competitor> viaRaceLog = new RegisteredCompetitorsAnalyzer<>(getRaceLog(fleet)).analyze();
result = viaRaceLog;
}
return result;
}
}
@@ -16,9 +16,9 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.shared.analyzing.RegisteredCompetitorsAnalyzer;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CourseArea;
@@ -40,6 +40,7 @@ import com.sap.sailing.domain.common.RegattaName;
import com.sap.sailing.domain.common.RegattaNameAndRaceName;
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.impl.CompetitorProviderFromRaceColumnsAndRegattaLike;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
@@ -111,6 +112,8 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
* Defaults to <code>true</code>. See {@link Regatta#useStartTimeInference()}.
*/
private boolean useStartTimeInference;
private transient CompetitorProviderFromRaceColumnsAndRegattaLike competitorsProvider;
/**
* Constructs a regatta with an empty {@link RaceLogStore}.
@@ -171,7 +174,6 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
* @param series
* all {@link Series} in this iterable will have their {@link Series#setRegatta(Regatta) regatta set} to
* this new regatta.
* @param rankingMetricConstructor TODO
*/
public <S extends Series> RegattaImpl(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
String name, BoatClass boatClass, TimePoint startDate, TimePoint endDate, Iterable<S> series, boolean persistent, ScoringScheme scoringScheme,
@@ -363,24 +365,15 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
@Override
public Iterable<Competitor> getAllCompetitors() {
Set<Competitor> result = new HashSet<Competitor>();
if (competitorsProvider == null) {
competitorsProvider = new CompetitorProviderFromRaceColumnsAndRegattaLike(this);
}
Util.addAll(competitorsProvider.getAllCompetitors(), result);
for (RaceDefinition race : getAllRaces()) {
for (Competitor c : race.getCompetitors()) {
result.add(c);
}
}
for (Series series : getSeries()) {
for (RaceColumn rc : series.getRaceColumns()) {
for (Fleet fleet : rc.getFleets()) {
TrackedRace trackedRace = rc.getTrackedRace(fleet);
if (trackedRace != null) {
Util.addAll(trackedRace.getRace().getCompetitors(), result);
}
}
}
}
//consider {@link RegattaLog}
Set<Competitor> viaLog = new RegisteredCompetitorsAnalyzer<>(regattaLikeHelper.getRegattaLog()).analyze();
result.addAll(viaLog);
return result;
}
@@ -700,4 +693,37 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
public RaceColumn getRaceColumnByName(String raceColumnName) {
return regattaLikeHelper.getRaceColumnByName(raceColumnName);
}
@Override
public IsRegattaLike getRegattaLike() {
return this;
}
@Override
public RaceLog getRacelog(String raceColumnName, String fleetName) {
final RaceLog result;
final RaceColumn raceColumn = getRaceColumnByName(raceColumnName);
if (raceColumn == null) {
result = null;
} else {
final Fleet fleet = raceColumn.getFleetByName(fleetName);
if (fleet == null) {
result = null;
} else {
result = raceColumn.getRaceLog(fleet);
}
}
return result;
}
@Override
public Iterable<? extends RaceColumn> getRaceColumns() {
final List<RaceColumnInSeries> result = new ArrayList<>();
for (final Series series : getSeries()) {
for (final RaceColumnInSeries rc : series.getRaceColumns()) {
result.add(rc);
}
}
return result;
}
}
@@ -0,0 +1,12 @@
package com.sap.sailing.domain.leaderboard;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceColumnListener;
public interface HasRaceColumns {
Iterable<? extends RaceColumn> getRaceColumns();
void addRaceColumnListener(RaceColumnListener listener);
void removeRaceColumnListener(RaceColumnListener listener);
}
@@ -0,0 +1,7 @@
package com.sap.sailing.domain.leaderboard;
import com.sap.sailing.domain.regattalike.HasRegattaLike;
public interface HasRaceColumnsAndRegattaLike extends HasRegattaLike, HasRaceColumns {
}
@@ -44,7 +44,7 @@ import com.sap.sse.common.Util;
* @author Axel Uhl (d043530)
*
*/
public interface Leaderboard extends LeaderboardBase {
public interface Leaderboard extends LeaderboardBase, HasRaceColumns {
/**
* If the leaderboard is a "matrix" with the cells being defined by a competitor / race "coordinate,"
* then this interface defines the structure of the "cells."
@@ -93,6 +93,22 @@ public interface Leaderboard extends LeaderboardBase {
* @return all competitors in this leaderboard, including the suppressed ones.
*/
Iterable<Competitor> getAllCompetitors();
/**
* Retrieves all competitors expected to race in the fleet and column specified.
* When a {@link TrackedRace} is {@link RaceColumn#getTrackedRace(Fleet) attached} to the race
* column for the <code>fleet</code> specified, its competitor set is returned. Otherwise,
* the competitors are collected from any other information, such as a regatta log and/or the
* race log for the combination of race column and fleet or, in case of a meta-leaderboard,
* from the leaderboard represented by the race column.
*/
Iterable<Competitor> getAllCompetitors(RaceColumn raceColumn, Fleet fleet);
/**
* Same as {@link #getAllCompetitors(RaceColumn, Fleet)} with competitors from {@link #getSuppressedCompetitors()}
* removed
*/
Iterable<Competitor> getCompetitors(RaceColumn raceColumn, Fleet fleet);
/**
* Convenience method which returns the difference between {@link #getAllCompetitors()} and {@link #getCompetitors()}.
@@ -8,7 +8,6 @@ import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CompetitorChangeListener;
import com.sap.sailing.domain.base.Fleet;
@@ -16,9 +15,9 @@ 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.WithNationality;
import com.sap.sailing.domain.base.impl.RaceColumnListenerWithDefaultAction;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.leaderboard.caching.LeaderboardCache;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.tracking.RaceChangeListener;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener;
@@ -262,9 +261,14 @@ public class LeaderboardCacheManager {
leaderboard.getScoreCorrection().addScoreCorrectionListener(scoreCorrectionListener);
scoreCorrectionListeners.put(leaderboard, scoreCorrectionListener);
competitorChangeListeners.put(leaderboard, competitorChangeListener);
final RaceColumnListener raceColumnListener = new RaceColumnListener() {
final RaceColumnListener raceColumnListener = new RaceColumnListenerWithDefaultAction() {
private static final long serialVersionUID = 8165124797028386317L;
@Override
public void defaultAction() {
removeFromCache(leaderboard);
}
@Override
public void trackedRaceLinked(RaceColumn raceColumn, Fleet fleet, TrackedRace trackedRace) {
removeFromCache(leaderboard);
@@ -278,70 +282,6 @@ public class LeaderboardCacheManager {
public boolean isTransient() {
return true;
}
@Override
public void trackedRaceUnlinked(RaceColumn raceColumn, Fleet fleet, TrackedRace trackedRace) {
removeFromCache(leaderboard); // removes all listeners from invalidationListenersPerLeaderboard and from their TrackedRaces
}
@Override
public void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace) {
removeFromCache(leaderboard);
}
@Override
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
removeFromCache(leaderboard);
}
@Override
public void hasSplitFleetContiguousScoringChanged(RaceColumn raceColumn, boolean hasSplitFleetContiguousScoring) {
removeFromCache(leaderboard);
}
@Override
public void isFirstColumnIsNonDiscardableCarryForwardChanged(RaceColumn raceColumn,
boolean firstColumnIsNonDiscardableCarryForward) {
removeFromCache(leaderboard);
}
@Override
public void raceColumnAddedToContainer(RaceColumn raceColumn) {
removeFromCache(leaderboard);
}
@Override
public void raceColumnRemovedFromContainer(RaceColumn raceColumn) {
removeFromCache(leaderboard);
}
@Override
public void raceColumnMoved(RaceColumn raceColumn, int newIndex) {
removeFromCache(leaderboard);
}
@Override
public void factorChanged(RaceColumn raceColumn, Double oldFactor, Double newFactor) {
removeFromCache(leaderboard);
}
@Override
public void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule,
ResultDiscardingRule newDiscardingRule) {
removeFromCache(leaderboard);
}
@Override
public void competitorDisplayNameChanged(Competitor competitor, String oldDisplayName,
String displayName) {
removeFromCache(leaderboard);
}
@Override
public void raceLogEventAdded(RaceColumn raceColumn, RaceLogIdentifier raceLogIdentifier,
RaceLogEvent event) {
removeFromCache(leaderboard);
}
};
leaderboard.addRaceColumnListener(raceColumnListener);
synchronized (raceColumnListeners) {
@@ -12,9 +12,9 @@ import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceColumnListener;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.leaderboard.HasRaceColumnsAndRegattaLike;
import com.sap.sailing.domain.leaderboard.ScoreCorrection;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.regattalike.HasRegattaLike;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.TimePoint;
@@ -28,14 +28,14 @@ import com.sap.sse.common.TimePoint;
* @author Axel Uhl (D043530)
*
*/
public abstract class AbstractLeaderboardImpl extends AbstractSimpleLeaderboardImpl implements HasRegattaLike{
public abstract class AbstractLeaderboardImpl extends AbstractSimpleLeaderboardImpl implements HasRaceColumnsAndRegattaLike {
private static final long serialVersionUID = -328091952760083438L;
/**
* Cache for the combined competitors of this leaderboard; taken from the {@link TrackedRace#getRace() races of the
* tracked races} associated with this leaderboard. Updated when the set of tracked races changes.
*/
private transient Iterable<Competitor> allCompetitorsCache;
private transient CompetitorProviderFromRaceColumnsAndRegattaLike competitorsProvider;
/**
* @param scoreComparator the comparator to use to compare basic scores, such as net points
@@ -77,20 +77,21 @@ public abstract class AbstractLeaderboardImpl extends AbstractSimpleLeaderboardI
*/
@Override
public Iterable<Competitor> getAllCompetitors() {
if (allCompetitorsCache == null) {
Set<Competitor> result = new HashSet<Competitor>();
synchronized (this) {
for (TrackedRace r : getTrackedRaces()) {
for (Competitor c : r.getRace().getCompetitors()) {
result.add(c);
}
}
}
allCompetitorsCache = result;
}
return allCompetitorsCache;
return getOrCreateCompetitorsProvider().getAllCompetitors();
}
@Override
public Iterable<Competitor> getAllCompetitors(RaceColumn raceColumn, Fleet fleet) {
return getOrCreateCompetitorsProvider().getAllCompetitors(raceColumn, fleet);
}
private CompetitorProviderFromRaceColumnsAndRegattaLike getOrCreateCompetitorsProvider() {
if (competitorsProvider == null) {
competitorsProvider = new CompetitorProviderFromRaceColumnsAndRegattaLike(this);
}
return competitorsProvider;
}
@Override
public Competitor getCompetitorByIdAsString(String idAsString) {
for (Competitor competitor : getAllCompetitors()) {
@@ -144,18 +145,6 @@ public abstract class AbstractLeaderboardImpl extends AbstractSimpleLeaderboardI
return correctedRank;
}
@Override
public void trackedRaceLinked(RaceColumn raceColumn, Fleet fleet, TrackedRace trackedRace) {
allCompetitorsCache = null;
super.trackedRaceLinked(raceColumn, fleet, trackedRace);
}
@Override
public void trackedRaceUnlinked(RaceColumn raceColumn, Fleet fleet, TrackedRace trackedRace) {
allCompetitorsCache = null;
super.trackedRaceUnlinked(raceColumn, fleet, trackedRace);
}
// Note: no need to redefine isMedalRaceChanged because that doesn't affect the competitorsCache
@Override
@@ -182,4 +171,5 @@ public abstract class AbstractLeaderboardImpl extends AbstractSimpleLeaderboardI
Fleet fleet = raceColumn.getFleetByName(fleetName);
return raceColumn.getRaceLog(fleet);
}
}
@@ -14,6 +14,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
@@ -1096,31 +1097,69 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
@Override
public Iterable<Competitor> getCompetitors() {
// TODO bug 1348: try to cache result; invalidation would have to listen for columns added and suppression changing;
// for meta-leaderbaords, transitive listening to those events would be necessary.
Set<Competitor> result = new HashSet<>();
for (Competitor competitor : getAllCompetitors()) {
if (!isSuppressed(competitor)) {
result.add(competitor);
}
final Iterable<Competitor> result;
// mostly the set of suppressed competitors is empty; in this case, avoid having to loop over the
// potentially large set of competitors
if (Util.isEmpty(getSuppressedCompetitors())) {
result = getAllCompetitors();
} else {
final Iterable<Competitor> allCompetitors = getAllCompetitors();
final Set<Competitor> suppressed = new HashSet<>();
Util.addAll(getSuppressedCompetitors(), suppressed);
result = getCompetitorIterableSkippingSuppressed(allCompetitors, suppressed);
}
return result;
}
@Override
public Iterable<Competitor> getCompetitors(RaceColumn raceColumn, Fleet fleet) {
return getCompetitorIterableSkippingSuppressed(getAllCompetitors(raceColumn, fleet), getSuppressedCompetitors());
}
/**
* Defines the difference between {@link #getCompetitors} and {@link #getAllCompetitors}. If a competitor is suppressed,
* it won't participate in the scoring process, particularly because it isn't considered by {@link #getCompetitorsFromBestToWorst(TimePoint)}
* nor {@link #getCompetitorsFromBestToWorst(RaceColumn, TimePoint)}.
* return an iterable with a smart iterator that filters out the suppressed elements on demand
*/
private boolean isSuppressed(Competitor competitor) {
LockUtil.lockForRead(suppressedCompetitorsLock);
try {
return suppressedCompetitors.contains(competitor);
} finally {
LockUtil.unlockAfterRead(suppressedCompetitorsLock);
}
protected Iterable<Competitor> getCompetitorIterableSkippingSuppressed(final Iterable<Competitor> allCompetitors,
final Iterable<Competitor> suppressed) {
final Iterable<Competitor> result;
result = new Iterable<Competitor>() {
@Override
public Iterator<Competitor> iterator() {
return new Iterator<Competitor>() {
private final Iterator<Competitor> allIter = allCompetitors.iterator();
private Competitor next = advance();
private Competitor advance() {
next = null;
while (allIter.hasNext() && next == null) {
next = allIter.next();
if (Util.contains(suppressed, next)) {
next = null;
}
}
return next;
}
@Override
public boolean hasNext() {
return next != null;
}
@Override
public Competitor next() {
if (next == null) {
throw new NoSuchElementException();
}
final Competitor result = next;
advance();
return result;
}
};
}
};
return result;
}
@Override
public Iterable<Competitor> getSuppressedCompetitors() {
LockUtil.lockForRead(suppressedCompetitorsLock);
@@ -0,0 +1,203 @@
package com.sap.sailing.domain.leaderboard.impl;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor;
import com.sap.sailing.domain.abstractlog.race.RaceLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.race.impl.BaseRaceLogEventVisitor;
import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogRegisterCompetitorEvent;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterCompetitorEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.regatta.impl.BaseRegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.shared.analyzing.RegisteredCompetitorsAnalyzer;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceColumnListener;
import com.sap.sailing.domain.base.impl.RaceColumnListenerWithDefaultAction;
import com.sap.sailing.domain.leaderboard.HasRaceColumnsAndRegattaLike;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
/**
* A caching provider of a competitor set, based on the tracked races and {@link RaceLog}s of the {@link RaceColumn}s of
* a {@link HasRaceColumnsAndRegattaLike} and the {@link RegattaLog} of the same object. After an answer has been provided,
* it is cached. The cache is invalidated when one of the following events occurs:
* <ul>
* <li>a competitor is registered with or unregistered from a race log of any of the race columns or the regatta log</li>
* <li>a race column is added or removed</li>
* <li>a tracked race is linked to or unlinked from any of the race columns</li>
* </ul>
*
* @author Axel Uhl (d043530)
*
*/
public class CompetitorProviderFromRaceColumnsAndRegattaLike {
private static final Logger logger = Logger.getLogger(CompetitorProviderFromRaceColumnsAndRegattaLike.class.getName());
private final HasRaceColumnsAndRegattaLike provider;
private final RegattaLogEventVisitor regattaLogCompetitorsCacheInvalidationListener;
private final RaceLogEventVisitor raceLogCompetitorsCacheInvalidationListener;
private final RaceColumnListener raceColumnListener;
private Iterable<Competitor> allCompetitorsCache;
private final ConcurrentHashMap<Pair<RaceColumn, Fleet>, Iterable<Competitor>> allCompetitorsCacheByRace;
public CompetitorProviderFromRaceColumnsAndRegattaLike(HasRaceColumnsAndRegattaLike provider) {
super();
this.provider = provider;
this.allCompetitorsCacheByRace = new ConcurrentHashMap<>();
// A note regarding listener serializability: RaceLogListener and RegattaLogListener objects
// don't need to be serializable as the log listeners are a transient structure. The race column
// listeners, however, need to explicitly declare that they are transient.
// This enclosing object is intended not to be serialized and shall be re-constructed by
// its users after de-serialization. Therefore, the transient-ness of the log listeners works
// as intended, and the race column listener explicitly returns true from its isTransient()
// method.
regattaLogCompetitorsCacheInvalidationListener = new BaseRegattaLogEventVisitor() {
@Override
public void visit(RegattaLogRegisterCompetitorEvent event) {
invalidateAllCompetitorsCaches();
}
@Override
public void visit(RegattaLogRevokeEvent event) {
try {
if (RegattaLogRegisterCompetitorEvent.class.isAssignableFrom(Class.forName(event.getRevokedEventType()))) {
invalidateAllCompetitorsCaches();
}
} catch (ClassNotFoundException e) {
logger.log(Level.WARNING, "Problem occurred trying to resolve revoked event class "+event.getRevokedEventType(), e);
}
}
};
raceLogCompetitorsCacheInvalidationListener = new BaseRaceLogEventVisitor() {
@Override
public void visit(RaceLogRegisterCompetitorEvent event) {
invalidateAllCompetitorsCaches();
}
@Override
public void visit(RaceLogRevokeEvent event) {
try {
if (RaceLogRegisterCompetitorEvent.class.isAssignableFrom(Class.forName(event.getRevokedEventType()))) {
invalidateAllCompetitorsCaches();
}
} catch (ClassNotFoundException e) {
logger.log(Level.WARNING, "Problem occurred trying to resolve revoked event class "+event.getRevokedEventType(), e);
}
}
};
raceColumnListener = new RaceColumnListenerWithDefaultAction() {
private static final long serialVersionUID = -8678230058730043052L;
@Override
public void defaultAction() {}
/**
* As the entire provider object is considered transient, its listeners are so, too.
*/
@Override
public boolean isTransient() {
return true;
}
@Override
public void trackedRaceLinked(RaceColumn raceColumn, Fleet fleet, TrackedRace trackedRace) {
invalidateAllCompetitorsCaches();
}
@Override
public void trackedRaceUnlinked(RaceColumn raceColumn, Fleet fleet, TrackedRace trackedRace) {
invalidateAllCompetitorsCaches();
}
@Override
public void raceColumnAddedToContainer(RaceColumn raceColumn) {
invalidateAllCompetitorsCaches();
}
@Override
public void raceColumnRemovedFromContainer(RaceColumn raceColumn) {
invalidateAllCompetitorsCaches();
}
};
}
public Iterable<Competitor> getAllCompetitors() {
if (allCompetitorsCache == null) {
final Set<Competitor> result = new HashSet<>();
for (RaceColumn rc : provider.getRaceColumns()) {
Util.addAll(rc.getAllCompetitors(), result);
for (final Fleet fleet : rc.getFleets()) {
rc.getRaceLog(fleet).addListener(raceLogCompetitorsCacheInvalidationListener);
}
}
provider.addRaceColumnListener(raceColumnListener);
// consider {@link RegattaLog}
Set<Competitor> viaLog = getCompetitorsFromRegattaLogAndRegisterAsRegattaLogListener();
result.addAll(viaLog);
allCompetitorsCache = result;
}
return allCompetitorsCache;
}
/**
* Obtains competitors from the regatta log and adds those from the tracked race for the
* raceColumn/fleet combination or the race log, if no tracked race is attached for this
* combination. Does <em>not</em> eliminate suppressed competitors.
*/
public Iterable<Competitor> getAllCompetitors(final RaceColumn raceColumn, final Fleet fleet) {
Pair<RaceColumn, Fleet> key = new Pair<>(raceColumn, fleet);
Iterable<Competitor> result = allCompetitorsCacheByRace.get(key);
if (result == null) {
final Set<Competitor> resultSet = new HashSet<>();
Util.addAll(raceColumn.getAllCompetitors(fleet), resultSet);
// note: adding listeners is idempotent; at most one occurrence of this listener exists in the race log's listeners set
raceColumn.getRaceLog(fleet).addListener(raceLogCompetitorsCacheInvalidationListener);
provider.addRaceColumnListener(raceColumnListener);
// consider {@link RegattaLog}
Set<Competitor> viaLog = getCompetitorsFromRegattaLogAndRegisterAsRegattaLogListener();
resultSet.addAll(viaLog);
result = resultSet;
allCompetitorsCacheByRace.put(key, result);
}
return result;
}
private Set<Competitor> getCompetitorsFromRegattaLogAndRegisterAsRegattaLogListener() {
Set<Competitor> viaLog = new RegisteredCompetitorsAnalyzer<>(provider.getRegattaLike().getRegattaLog()).analyze();
// note: adding listeners is idempotent; at most one occurrence of this listener exists in the regatta log's listeners set
provider.getRegattaLike().getRegattaLog().addListener(regattaLogCompetitorsCacheInvalidationListener);
return viaLog;
}
private void invalidateAllCompetitorsCaches() {
allCompetitorsCache = null;
// note: adding listeners was idempotent; at most one occurrence of this listener exists in the regatta log's listeners set
provider.getRegattaLike().getRegattaLog().removeListener(regattaLogCompetitorsCacheInvalidationListener);
for (final RaceColumn rc : provider.getRaceColumns()) {
for (final Fleet fleet : rc.getFleets()) {
// note: adding listeners was idempotent; at most one occurrence of this listener exists in the race log's listeners set
rc.getRaceLog(fleet).removeListener(raceLogCompetitorsCacheInvalidationListener);
}
}
// note: adding listeners was idempotent; at most one occurrence of this listener exists in the race column listeners set
provider.removeRaceColumnListener(raceColumnListener);
allCompetitorsCacheByRace.clear(); // this is a little coarse-grained; but given the typical access patterns it should be good enough.
// The change frequency of competitors lists on individual races is much lower than the access frequency to this structure.
}
}
@@ -8,14 +8,11 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.shared.analyzing.RegisteredCompetitorsAnalyzer;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.Fleet;
@@ -145,8 +142,10 @@ public class FlexibleLeaderboardImpl extends AbstractLeaderboardImpl implements
private FlexibleRaceColumn addRaceColumn(String name, boolean medalRace, boolean logAlreadyExistingColumn) {
FlexibleRaceColumn column = getRaceColumnByName(name);
if (column != null) {
final String msg = "Trying to create race column with duplicate name "+name+" in leaderboard "+getName();
logger.severe(msg);
if (logAlreadyExistingColumn) {
final String msg = "Trying to create race column with duplicate name " + name + " in leaderboard " + getName();
logger.severe(msg);
}
} else {
column = createRaceColumn(name, medalRace);
column.addRaceColumnListener(this);
@@ -319,18 +318,6 @@ public class FlexibleLeaderboardImpl extends AbstractLeaderboardImpl implements
return regattaLikeHelper.getTimeOnDistanceAllowancePerNauticalMile(competitor);
}
@Override
public Iterable<Competitor> getAllCompetitors() {
Set<Competitor> result = new HashSet<>();
for (Competitor c : super.getAllCompetitors()) {
result.add(c);
}
//consider {@link RegattaLog}
Set<Competitor> viaLog = new RegisteredCompetitorsAnalyzer<>(regattaLikeHelper.getRegattaLog()).analyze();
result.addAll(viaLog);
return result;
}
private class RaceExecutionOrderCache extends AbstractRaceExecutionOrderProvider {
private static final long serialVersionUID = 652833386555762661L;
@@ -5,6 +5,7 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -147,6 +148,17 @@ public abstract class AbstractMetaLeaderboard extends AbstractSimpleLeaderboardI
return result;
}
@Override
public Iterable<Competitor> getAllCompetitors(RaceColumn raceColumn, Fleet fleet) {
final Iterable<Competitor> result;
if (fleet == metaFleet && Util.contains(getRaceColumns(), raceColumn)) {
result = ((MetaLeaderboardColumn) raceColumn).getAllCompetitors();
} else {
result = Collections.emptySet();
}
return result;
}
@Override
public Fleet getFleet(String fleetName) {
return fleetName.equals(metaFleet.getName()) ? metaFleet : null;
@@ -203,4 +203,20 @@ public class MetaLeaderboardColumn extends SimpleAbstractRaceColumn implements R
public RaceExecutionOrderProvider getRaceExecutionOrderProvider() {
return null;
}
@Override
public Iterable<Competitor> getAllCompetitors() {
return leaderboard.getAllCompetitors();
}
@Override
public Iterable<Competitor> getAllCompetitors(Fleet fleet) {
final Iterable<Competitor> result;
if (fleet == metaFleet) {
result = leaderboard.getAllCompetitors();
} else {
result = Collections.emptySet();
}
return result;
}
}
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.regattalike;
import com.sap.sailing.domain.leaderboard.HasRaceColumnsAndRegattaLike;
import com.sap.sailing.domain.leaderboard.Leaderboard;
public interface LeaderboardThatHasRegattaLike extends Leaderboard, HasRegattaLike {
public interface LeaderboardThatHasRegattaLike extends Leaderboard, HasRaceColumnsAndRegattaLike {
}
@@ -13,7 +13,6 @@ import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.common.racelog.RaceLogServletConstants;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.gateway.AbstractJsonHttpServlet;
import com.sap.sailing.server.gateway.serialization.impl.BoatClassJsonSerializer;
@@ -28,61 +27,46 @@ public class CompetitorsJsonExportServlet extends AbstractJsonHttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String leaderboardName = request.getParameter(RaceLogServletConstants.PARAMS_LEADERBOARD_NAME);
if (leaderboardName == null) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST,
String.format("Missing parameter '%s'.", RaceLogServletConstants.PARAMS_LEADERBOARD_NAME));
return;
}
String raceColumnName = request.getParameter(RaceLogServletConstants.PARAMS_RACE_COLUMN_NAME);
if (raceColumnName == null) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST,
String.format("Missing parameter '%s'.", RaceLogServletConstants.PARAMS_RACE_COLUMN_NAME));
return;
}
String fleetName = request.getParameter(RaceLogServletConstants.PARAMS_RACE_FLEET_NAME);
if (fleetName == null) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST,
String.format("Missing parameter '%s'.", RaceLogServletConstants.PARAMS_RACE_FLEET_NAME));
return;
}
RacingEventService service = getService();
Leaderboard leaderboard = service.getLeaderboardByName(leaderboardName);
if (leaderboard == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "No such leaderboard found.");
return;
}
RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName);
if (raceColumn == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "No such race column found.");
return;
}
Fleet fleet = raceColumn.getFleetByName(fleetName);
if (fleet == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "No such fleet found.");
return;
}
TrackedRace trackedRace = raceColumn.getTrackedRace(fleet);
JSONArray result = new JSONArray();
if (trackedRace != null) {
CompetitorJsonSerializer serializer = new CompetitorJsonSerializer(new TeamJsonSerializer(
new PersonJsonSerializer(new NationalityJsonSerializer())), new BoatJsonSerializer(new BoatClassJsonSerializer()));
for (Competitor competitor : raceColumn.getRaceDefinition(fleet).getCompetitors()) {
result.add(serializer.serialize(competitor));
}
CompetitorJsonSerializer serializer = new CompetitorJsonSerializer(new TeamJsonSerializer(
new PersonJsonSerializer(new NationalityJsonSerializer())), new BoatJsonSerializer(new BoatClassJsonSerializer()));
for (Competitor competitor : leaderboard.getCompetitors(raceColumn, fleet)) {
result.add(serializer.serialize(competitor));
}
setJsonResponseHeader(response);
result.writeJSONString(response.getWriter());
}