diff --git a/java/com.sap.sailing.domain.swisstimingadapter.test/src/com/sap/sailing/domain/swisstimingadapter/test/EndToEndListeningStoreAndFowardTest.java b/java/com.sap.sailing.domain.swisstimingadapter.test/src/com/sap/sailing/domain/swisstimingadapter/test/EndToEndListeningStoreAndFowardTest.java index c3e978a4541..90ce9894133 100755 --- a/java/com.sap.sailing.domain.swisstimingadapter.test/src/com/sap/sailing/domain/swisstimingadapter/test/EndToEndListeningStoreAndFowardTest.java +++ b/java/com.sap.sailing.domain.swisstimingadapter.test/src/com/sap/sailing/domain/swisstimingadapter/test/EndToEndListeningStoreAndFowardTest.java @@ -1,362 +1,365 @@ -package com.sap.sailing.domain.swisstimingadapter.test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.logging.Logger; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import com.mongodb.BasicDBObject; -import com.mongodb.DB; -import com.mongodb.DBCollection; -import com.sap.sailing.domain.base.Mark; -import com.sap.sailing.domain.base.Competitor; -import com.sap.sailing.domain.base.RaceDefinition; -import com.sap.sailing.domain.base.Regatta; -import com.sap.sailing.domain.base.Waypoint; -import com.sap.sailing.domain.common.impl.Util; -import com.sap.sailing.domain.swisstimingadapter.MessageType; -import com.sap.sailing.domain.swisstimingadapter.SailMasterConnector; -import com.sap.sailing.domain.swisstimingadapter.SailMasterTransceiver; -import com.sap.sailing.domain.swisstimingadapter.SwissTimingFactory; -import com.sap.sailing.domain.swisstimingadapter.impl.DomainFactoryImpl; -import com.sap.sailing.domain.swisstimingadapter.persistence.StoreAndForward; -import com.sap.sailing.domain.swisstimingadapter.persistence.SwissTimingAdapterPersistence; -import com.sap.sailing.domain.swisstimingadapter.persistence.impl.CollectionNames; -import com.sap.sailing.domain.swisstimingadapter.persistence.impl.FieldNames; -import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; -import com.sap.sailing.domain.tracking.GPSFix; -import com.sap.sailing.domain.tracking.GPSFixMoving; -import com.sap.sailing.domain.tracking.GPSFixTrack; -import com.sap.sailing.domain.tracking.RacesHandle; -import com.sap.sailing.domain.tracking.TrackedRace; -import com.sap.sailing.domain.tracking.impl.EmptyWindStore; -import com.sap.sailing.mongodb.MongoDBService; -import com.sap.sailing.server.RacingEventService; -import com.sap.sailing.server.impl.RacingEventServiceImpl; - -public class EndToEndListeningStoreAndFowardTest { - private static final Logger logger = Logger.getLogger(EndToEndListeningStoreAndFowardTest.class.getName()); - - private static final int RECEIVE_PORT = 6543; - private static final int CLIENT_PORT = 6544; - - private DB db; - private StoreAndForward storeAndForward; - private Socket sendingSocket; - private OutputStream sendingStream; - private SailMasterTransceiver transceiver; - private SailMasterConnector connector; - private SwissTimingAdapterPersistence swissTimingAdapterPersistence; - private SwissTimingFactory swissTimingFactory; - - private EmptyWindStore emptyWindStore; - private RacingEventService racingEventService; - - private List raceHandles; - - @Before - public void setUp() throws UnknownHostException, IOException, InterruptedException { - logger.info("EndToEndListeningStoreAndFowardTest.setUp"); - MongoDBService mongoDBService = MongoDBService.INSTANCE; - db = mongoDBService.getDB(); - db.getCollection(com.sap.sailing.domain.persistence.impl.CollectionNames.REGATTAS.name()).drop(); - swissTimingAdapterPersistence = SwissTimingAdapterPersistence.INSTANCE; - swissTimingAdapterPersistence.dropAllMessageData(); - swissTimingAdapterPersistence.dropAllRaceMasterData(); - storeAndForward = new StoreAndForward(RECEIVE_PORT, CLIENT_PORT, SwissTimingFactory.INSTANCE, - swissTimingAdapterPersistence, mongoDBService); - sendingSocket = new Socket("localhost", RECEIVE_PORT); - sendingStream = sendingSocket.getOutputStream(); - swissTimingFactory = SwissTimingFactory.INSTANCE; - emptyWindStore = EmptyWindStore.INSTANCE; - transceiver = swissTimingFactory.createSailMasterTransceiver(); - DBCollection lastMessageCountCollection = db.getCollection(CollectionNames.LAST_MESSAGE_COUNT.name()); - lastMessageCountCollection.update(new BasicDBObject(), - new BasicDBObject().append(FieldNames.LAST_MESSAGE_COUNT.name(), 0l), - /* upsert */true, /* multi */false); - // important: construct a new domain factory each time to make sure the competitor cache starts out empty - final com.sap.sailing.domain.base.impl.DomainFactoryImpl baseDomainFactory = new com.sap.sailing.domain.base.impl.DomainFactoryImpl(); - racingEventService = new RacingEventServiceImpl(mongoDBService, SwissTimingFactory.INSTANCE, - new DomainFactoryImpl(baseDomainFactory), - new com.sap.sailing.domain.tractracadapter.impl.DomainFactoryImpl(baseDomainFactory)); - raceHandles = new ArrayList(); - } - - @After - public void tearDown() throws InterruptedException, IOException { - logger.entering(getClass().getName(), "tearDown"); - for (RacesHandle raceHandle : raceHandles) { - racingEventService.stopTracking(raceHandle.getRegatta()); - } - logger.info("Calling StoreAndForward.stop() in tearDown"); - storeAndForward.stop(); - logger.exiting(getClass().getName(), "tearDown"); - } - - @Test - public void testEndToEndScenarioWithInitMessages() throws Exception { - String[] racesToTrack = new String[] { "4711", "4712" }; - String scriptName = "/InitMessagesScript.txt"; - setUpUsingScript(racesToTrack, scriptName); - - Set allTrackedRaces = new HashSet(); - Iterable allRegattas = racingEventService.getAllRegattas(); - for (Regatta regatta : allRegattas) { - DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(regatta); - Iterable trackedRaces = trackedRegatta.getTrackedRaces(); - for (TrackedRace trackedRace : trackedRaces) { - allTrackedRaces.add(trackedRace); - } - } - assertEquals(2, Util.size(allTrackedRaces)); - Set raceIDs = new HashSet(); - for (TrackedRace trackedRace : allTrackedRaces) { - RaceDefinition race = trackedRace.getRace(); - raceIDs.add(race.getName()); - } - Set expectedRaceIDs = new HashSet(); - for (String raceIDToTrack : new String[] { "4711", "4712" }) { - expectedRaceIDs.add(raceIDToTrack); - } - assertEquals(expectedRaceIDs, raceIDs); - } - - @Test - public void testLongRaceLog() throws Exception { - String[] racesToTrack = new String[] { "W4702" }; - String scriptName1 = "/SailMasterDataInterfaceRACandSTL.txt"; - String scriptName2 = "/SailMasterDataInterface-ExampleAsText.txt"; - setUpUsingScript(racesToTrack, scriptName1, scriptName2); - coreOfTestLongRaceLog(); - } - - @Test - public void testLongRaceLogNewVersion() throws Exception { - String[] racesToTrack = new String[] { "W4702" }; - String scriptName1 = "/W4702RACandSTLandCCG.txt"; - String scriptName2 = "/W4702AsText.txt"; - setUpUsingScript(racesToTrack, scriptName1, scriptName2); - coreOfTestLongRaceLog(); - } - - private void coreOfTestLongRaceLog() { - Set allTrackedRaces = new HashSet(); - Iterable allRegattas = racingEventService.getAllRegattas(); - for (Regatta regatta : allRegattas) { - DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(regatta); - Iterable trackedRaces = trackedRegatta.getTrackedRaces(); - for (TrackedRace trackedRace : trackedRaces) { - allTrackedRaces.add(trackedRace); - } - } - assertEquals(1, Util.size(allTrackedRaces)); - Set races = raceHandles.iterator().next().getRaceTracker().getRaces(); - assertEquals(1, races.size()); - RaceDefinition raceFromTracker = races.iterator().next(); - assertNotNull(raceFromTracker); - Set raceIDs = new HashSet(); - for (TrackedRace trackedRace : allTrackedRaces) { - RaceDefinition race = trackedRace.getRace(); - raceIDs.add(race.getName()); - assertEquals(46, Util.size(race.getCompetitors())); - assertEquals(7, Util.size(race.getCourse().getWaypoints())); - assertEquals(6, Util.size(race.getCourse().getLegs())); - for (Competitor competitor : race.getCompetitors()) { - if (!competitor.getName().equals("Competitor 35") && !competitor.getName().equals("Competitor 20") - && !competitor.getName().equals("GBR 831") && !competitor.getName().equals("NED 24")) { - final GPSFixTrack track = trackedRace.getTrack(competitor); - track.lockForRead(); - try { - assertTrue("Track of competitor " + competitor + " empty", !Util.isEmpty(track.getRawFixes())); - } finally { - track.unlockAfterRead(); - } - } - } - Set marks = new HashSet(); - for (Waypoint waypoint : race.getCourse().getWaypoints()) { - for (Mark mark : waypoint.getMarks()) { - marks.add(mark); - } - } - for (Mark mark : marks) { - final GPSFixTrack track = trackedRace.getOrCreateTrack(mark); - track.lockForRead(); - try { - assertTrue("Track of mark " + mark + " empty", - !Util.isEmpty(track.getRawFixes())); - } finally { - track.unlockAfterRead(); - } - } - } - Set expectedRaceIDs = new HashSet(); - for (String raceIDToTrack : new String[] { "W4702" }) { - expectedRaceIDs.add(raceIDToTrack); - } - assertEquals(expectedRaceIDs, raceIDs); - } - - @Test - public void testLongLogRaceNewConfig() throws Exception { - String[] racesToTrack = new String[] { "W4702" }; - String scriptName1 = "/SailMasterDataInterfaceRACandSTL.txt"; - String scriptName2 = "/SailMasterDataInterface-ExampleAsText.txt"; - String scriptNewCourseConfig = "/SailMasterDataInterfaceNewCourseConfig.txt"; - setUpUsingScript(racesToTrack, scriptName1, scriptName2, scriptNewCourseConfig); - Set allNewTrackedRaces = new HashSet(); - Iterable allNewEvents = racingEventService.getAllRegattas(); - for (Regatta event : allNewEvents) { - DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(event); - Iterable trackedRaces = trackedRegatta.getTrackedRaces(); - for (TrackedRace trackedRace : trackedRaces) { - allNewTrackedRaces.add(trackedRace); - } - } - assertEquals(1, Util.size(allNewTrackedRaces)); - Set races = raceHandles.iterator().next().getRaceTracker().getRaces(); - assertEquals(1, races.size()); - RaceDefinition raceFromTracker = races.iterator().next(); - assertNotNull(raceFromTracker); - Set raceIDs = new HashSet(); - for (TrackedRace trackedRace : allNewTrackedRaces) { - RaceDefinition race = trackedRace.getRace(); - raceIDs.add(race.getName()); - assertEquals(46, Util.size(race.getCompetitors())); - assertEquals(3, Util.size(race.getCourse().getWaypoints())); - assertEquals(2, Util.size(race.getCourse().getLegs())); - for (Competitor competitor : race.getCompetitors()) { - if (!competitor.getName().equals("NED 24") && !competitor.getName().equals("Competitor 35") - && !competitor.getName().equals("Competitor 20") && !competitor.getName().equals("GBR 831")) { - final GPSFixTrack track = trackedRace.getTrack(competitor); - track.lockForRead(); - try { - assertTrue("Track of competitor " + competitor + " empty", !Util.isEmpty(track.getRawFixes())); - } finally { - track.unlockAfterRead(); - } - } - } - Set marks = new HashSet(); - for (Waypoint waypoint : race.getCourse().getWaypoints()) { - for (Mark mark : waypoint.getMarks()) { - marks.add(mark); - } - } - for (Mark mark : marks) { - final GPSFixTrack track = trackedRace.getOrCreateTrack(mark); - track.lockForRead(); - try { - assertTrue("Track of mark " + mark + " empty", !Util.isEmpty(track.getRawFixes())); - } finally { - track.unlockAfterRead(); - } - } - } - Set expectedRaceIDs = new HashSet(); - for (String raceIDToTrack : new String[] { "W4702" }) { - expectedRaceIDs.add(raceIDToTrack); - } - assertEquals(expectedRaceIDs, raceIDs); - } - - @Test - public void testDuplicateCCGMessageAndWaypointUniqueness() throws Exception { - String[] racesToTrack = new String[] { "W4702" }; - setUpUsingScript(racesToTrack, "/DuplicateCCG.txt"); - - Set allTrackedRaces = new HashSet(); - Iterable allEvents = racingEventService.getAllRegattas(); - for (Regatta event : allEvents) { - DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(event); - Iterable trackedRaces = trackedRegatta.getTrackedRaces(); - for (TrackedRace trackedRace : trackedRaces) { - allTrackedRaces.add(trackedRace); - } - } - assertEquals(1, allTrackedRaces.size()); - TrackedRace trackedRace = allTrackedRaces.iterator().next(); - List waypoints = new ArrayList(); - for (Waypoint waypoint : trackedRace.getRace().getCourse().getWaypoints()) { - waypoints.add(waypoint); - } - assertEquals(7, Util.size(waypoints)); - } - - @Test - public void testRongRaceLogRACZero() throws Exception{ - String[] racesToTrack = new String[] { "W4702" }; - String scriptName2 = "/SailMasterDataInterfaceRACZero.txt"; - setUpUsingScript(racesToTrack, scriptName2); - Set allNewTrackedRaces = new HashSet(); - Iterable allNewEvents = racingEventService.getAllRegattas(); - for (Regatta event : allNewEvents) { - DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(event); - Iterable trackedRaces = trackedRegatta.getTrackedRaces(); - for (TrackedRace trackedRace : trackedRaces) { - allNewTrackedRaces.add(trackedRace); - } - } - assertEquals(0, Util.size(allNewTrackedRaces)); - } - - @Test - public void testEndToEndWithSwissTimingData() throws Exception { - String[] racesToTrack = new String[] { "W4702" }; - String scriptName1 = "/SailMasterDataInterfaceRACandSTL.txt"; - String scriptName2 = "/SailMasterDataInterface-ExampleAsText.txt"; - setUpUsingScript(racesToTrack, scriptName1, scriptName2); - - Set allTrackedRaces = new HashSet(); - Iterable allEvents = racingEventService.getAllRegattas(); - for (Regatta event : allEvents) { - DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(event); - Iterable trackedRaces = trackedRegatta.getTrackedRaces(); - for (TrackedRace trackedRace : trackedRaces) { - allTrackedRaces.add(trackedRace); - } - } - } - - private void setUpUsingScript(String[] racesToTrack, String... scriptNames) throws Exception { - for (String raceToTrack : racesToTrack) { - RacesHandle raceHandle = racingEventService.addSwissTimingRace(/* regattaToAddTo */ null /* use a default regatta */, - raceToTrack, "localhost", /* canSendRequests */ - CLIENT_PORT, false, emptyWindStore, -1); - raceHandles.add(raceHandle); - if (connector == null) { - connector = racingEventService.getSwissTimingFactory().getOrCreateSailMasterConnector("localhost", - CLIENT_PORT, swissTimingAdapterPersistence, /* canSendRequests */false); - } - } - ScriptedMessagesReader scriptedMessagesReader = new ScriptedMessagesReader(); - for (String scriptName : scriptNames) { - InputStream is = getClass().getResourceAsStream(scriptName); - scriptedMessagesReader.addMessagesFromTextFile(is); - } - for (String msg : scriptedMessagesReader.getMessages()) { - transceiver.sendMessage(msg, sendingStream); - } - transceiver.sendMessage(swissTimingFactory.createMessage(MessageType._STOPSERVER.name(), null), sendingStream); - synchronized (connector) { - while (!connector.isStopped()) { - connector.wait(); - } - } - } - -} +package com.sap.sailing.domain.swisstimingadapter.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.logging.Logger; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.mongodb.BasicDBObject; +import com.mongodb.DB; +import com.mongodb.DBCollection; +import com.sap.sailing.domain.base.Mark; +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.RaceDefinition; +import com.sap.sailing.domain.base.Regatta; +import com.sap.sailing.domain.base.Waypoint; +import com.sap.sailing.domain.common.impl.Util; +import com.sap.sailing.domain.racecommittee.impl.EmptyRaceCommitteeStore; +import com.sap.sailing.domain.swisstimingadapter.MessageType; +import com.sap.sailing.domain.swisstimingadapter.SailMasterConnector; +import com.sap.sailing.domain.swisstimingadapter.SailMasterTransceiver; +import com.sap.sailing.domain.swisstimingadapter.SwissTimingFactory; +import com.sap.sailing.domain.swisstimingadapter.impl.DomainFactoryImpl; +import com.sap.sailing.domain.swisstimingadapter.persistence.StoreAndForward; +import com.sap.sailing.domain.swisstimingadapter.persistence.SwissTimingAdapterPersistence; +import com.sap.sailing.domain.swisstimingadapter.persistence.impl.CollectionNames; +import com.sap.sailing.domain.swisstimingadapter.persistence.impl.FieldNames; +import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; +import com.sap.sailing.domain.tracking.GPSFix; +import com.sap.sailing.domain.tracking.GPSFixMoving; +import com.sap.sailing.domain.tracking.GPSFixTrack; +import com.sap.sailing.domain.tracking.RacesHandle; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.domain.tracking.impl.EmptyWindStore; +import com.sap.sailing.mongodb.MongoDBService; +import com.sap.sailing.server.RacingEventService; +import com.sap.sailing.server.impl.RacingEventServiceImpl; + +public class EndToEndListeningStoreAndFowardTest { + private static final Logger logger = Logger.getLogger(EndToEndListeningStoreAndFowardTest.class.getName()); + + private static final int RECEIVE_PORT = 6543; + private static final int CLIENT_PORT = 6544; + + private DB db; + private StoreAndForward storeAndForward; + private Socket sendingSocket; + private OutputStream sendingStream; + private SailMasterTransceiver transceiver; + private SailMasterConnector connector; + private SwissTimingAdapterPersistence swissTimingAdapterPersistence; + private SwissTimingFactory swissTimingFactory; + + private EmptyWindStore emptyWindStore; + private EmptyRaceCommitteeStore emptyRaceCommitteeStore; + private RacingEventService racingEventService; + + private List raceHandles; + + @Before + public void setUp() throws UnknownHostException, IOException, InterruptedException { + logger.info("EndToEndListeningStoreAndFowardTest.setUp"); + MongoDBService mongoDBService = MongoDBService.INSTANCE; + db = mongoDBService.getDB(); + db.getCollection(com.sap.sailing.domain.persistence.impl.CollectionNames.REGATTAS.name()).drop(); + swissTimingAdapterPersistence = SwissTimingAdapterPersistence.INSTANCE; + swissTimingAdapterPersistence.dropAllMessageData(); + swissTimingAdapterPersistence.dropAllRaceMasterData(); + storeAndForward = new StoreAndForward(RECEIVE_PORT, CLIENT_PORT, SwissTimingFactory.INSTANCE, + swissTimingAdapterPersistence, mongoDBService); + sendingSocket = new Socket("localhost", RECEIVE_PORT); + sendingStream = sendingSocket.getOutputStream(); + swissTimingFactory = SwissTimingFactory.INSTANCE; + emptyWindStore = EmptyWindStore.INSTANCE; + emptyRaceCommitteeStore = EmptyRaceCommitteeStore.INSTANCE; + transceiver = swissTimingFactory.createSailMasterTransceiver(); + DBCollection lastMessageCountCollection = db.getCollection(CollectionNames.LAST_MESSAGE_COUNT.name()); + lastMessageCountCollection.update(new BasicDBObject(), + new BasicDBObject().append(FieldNames.LAST_MESSAGE_COUNT.name(), 0l), + /* upsert */true, /* multi */false); + // important: construct a new domain factory each time to make sure the competitor cache starts out empty + final com.sap.sailing.domain.base.impl.DomainFactoryImpl baseDomainFactory = new com.sap.sailing.domain.base.impl.DomainFactoryImpl(); + racingEventService = new RacingEventServiceImpl(mongoDBService, SwissTimingFactory.INSTANCE, + new DomainFactoryImpl(baseDomainFactory), + new com.sap.sailing.domain.tractracadapter.impl.DomainFactoryImpl(baseDomainFactory)); + raceHandles = new ArrayList(); + } + + @After + public void tearDown() throws InterruptedException, IOException { + logger.entering(getClass().getName(), "tearDown"); + for (RacesHandle raceHandle : raceHandles) { + racingEventService.stopTracking(raceHandle.getRegatta()); + } + logger.info("Calling StoreAndForward.stop() in tearDown"); + storeAndForward.stop(); + logger.exiting(getClass().getName(), "tearDown"); + } + + @Test + public void testEndToEndScenarioWithInitMessages() throws Exception { + String[] racesToTrack = new String[] { "4711", "4712" }; + String scriptName = "/InitMessagesScript.txt"; + setUpUsingScript(racesToTrack, scriptName); + + Set allTrackedRaces = new HashSet(); + Iterable allRegattas = racingEventService.getAllRegattas(); + for (Regatta regatta : allRegattas) { + DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(regatta); + Iterable trackedRaces = trackedRegatta.getTrackedRaces(); + for (TrackedRace trackedRace : trackedRaces) { + allTrackedRaces.add(trackedRace); + } + } + assertEquals(2, Util.size(allTrackedRaces)); + Set raceIDs = new HashSet(); + for (TrackedRace trackedRace : allTrackedRaces) { + RaceDefinition race = trackedRace.getRace(); + raceIDs.add(race.getName()); + } + Set expectedRaceIDs = new HashSet(); + for (String raceIDToTrack : new String[] { "4711", "4712" }) { + expectedRaceIDs.add(raceIDToTrack); + } + assertEquals(expectedRaceIDs, raceIDs); + } + + @Test + public void testLongRaceLog() throws Exception { + String[] racesToTrack = new String[] { "W4702" }; + String scriptName1 = "/SailMasterDataInterfaceRACandSTL.txt"; + String scriptName2 = "/SailMasterDataInterface-ExampleAsText.txt"; + setUpUsingScript(racesToTrack, scriptName1, scriptName2); + coreOfTestLongRaceLog(); + } + + @Test + public void testLongRaceLogNewVersion() throws Exception { + String[] racesToTrack = new String[] { "W4702" }; + String scriptName1 = "/W4702RACandSTLandCCG.txt"; + String scriptName2 = "/W4702AsText.txt"; + setUpUsingScript(racesToTrack, scriptName1, scriptName2); + coreOfTestLongRaceLog(); + } + + private void coreOfTestLongRaceLog() { + Set allTrackedRaces = new HashSet(); + Iterable allRegattas = racingEventService.getAllRegattas(); + for (Regatta regatta : allRegattas) { + DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(regatta); + Iterable trackedRaces = trackedRegatta.getTrackedRaces(); + for (TrackedRace trackedRace : trackedRaces) { + allTrackedRaces.add(trackedRace); + } + } + assertEquals(1, Util.size(allTrackedRaces)); + Set races = raceHandles.iterator().next().getRaceTracker().getRaces(); + assertEquals(1, races.size()); + RaceDefinition raceFromTracker = races.iterator().next(); + assertNotNull(raceFromTracker); + Set raceIDs = new HashSet(); + for (TrackedRace trackedRace : allTrackedRaces) { + RaceDefinition race = trackedRace.getRace(); + raceIDs.add(race.getName()); + assertEquals(46, Util.size(race.getCompetitors())); + assertEquals(7, Util.size(race.getCourse().getWaypoints())); + assertEquals(6, Util.size(race.getCourse().getLegs())); + for (Competitor competitor : race.getCompetitors()) { + if (!competitor.getName().equals("Competitor 35") && !competitor.getName().equals("Competitor 20") + && !competitor.getName().equals("GBR 831") && !competitor.getName().equals("NED 24")) { + final GPSFixTrack track = trackedRace.getTrack(competitor); + track.lockForRead(); + try { + assertTrue("Track of competitor " + competitor + " empty", !Util.isEmpty(track.getRawFixes())); + } finally { + track.unlockAfterRead(); + } + } + } + Set marks = new HashSet(); + for (Waypoint waypoint : race.getCourse().getWaypoints()) { + for (Mark mark : waypoint.getMarks()) { + marks.add(mark); + } + } + for (Mark mark : marks) { + final GPSFixTrack track = trackedRace.getOrCreateTrack(mark); + track.lockForRead(); + try { + assertTrue("Track of mark " + mark + " empty", + !Util.isEmpty(track.getRawFixes())); + } finally { + track.unlockAfterRead(); + } + } + } + Set expectedRaceIDs = new HashSet(); + for (String raceIDToTrack : new String[] { "W4702" }) { + expectedRaceIDs.add(raceIDToTrack); + } + assertEquals(expectedRaceIDs, raceIDs); + } + + @Test + public void testLongLogRaceNewConfig() throws Exception { + String[] racesToTrack = new String[] { "W4702" }; + String scriptName1 = "/SailMasterDataInterfaceRACandSTL.txt"; + String scriptName2 = "/SailMasterDataInterface-ExampleAsText.txt"; + String scriptNewCourseConfig = "/SailMasterDataInterfaceNewCourseConfig.txt"; + setUpUsingScript(racesToTrack, scriptName1, scriptName2, scriptNewCourseConfig); + Set allNewTrackedRaces = new HashSet(); + Iterable allNewEvents = racingEventService.getAllRegattas(); + for (Regatta event : allNewEvents) { + DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(event); + Iterable trackedRaces = trackedRegatta.getTrackedRaces(); + for (TrackedRace trackedRace : trackedRaces) { + allNewTrackedRaces.add(trackedRace); + } + } + assertEquals(1, Util.size(allNewTrackedRaces)); + Set races = raceHandles.iterator().next().getRaceTracker().getRaces(); + assertEquals(1, races.size()); + RaceDefinition raceFromTracker = races.iterator().next(); + assertNotNull(raceFromTracker); + Set raceIDs = new HashSet(); + for (TrackedRace trackedRace : allNewTrackedRaces) { + RaceDefinition race = trackedRace.getRace(); + raceIDs.add(race.getName()); + assertEquals(46, Util.size(race.getCompetitors())); + assertEquals(3, Util.size(race.getCourse().getWaypoints())); + assertEquals(2, Util.size(race.getCourse().getLegs())); + for (Competitor competitor : race.getCompetitors()) { + if (!competitor.getName().equals("NED 24") && !competitor.getName().equals("Competitor 35") + && !competitor.getName().equals("Competitor 20") && !competitor.getName().equals("GBR 831")) { + final GPSFixTrack track = trackedRace.getTrack(competitor); + track.lockForRead(); + try { + assertTrue("Track of competitor " + competitor + " empty", !Util.isEmpty(track.getRawFixes())); + } finally { + track.unlockAfterRead(); + } + } + } + Set marks = new HashSet(); + for (Waypoint waypoint : race.getCourse().getWaypoints()) { + for (Mark mark : waypoint.getMarks()) { + marks.add(mark); + } + } + for (Mark mark : marks) { + final GPSFixTrack track = trackedRace.getOrCreateTrack(mark); + track.lockForRead(); + try { + assertTrue("Track of mark " + mark + " empty", !Util.isEmpty(track.getRawFixes())); + } finally { + track.unlockAfterRead(); + } + } + } + Set expectedRaceIDs = new HashSet(); + for (String raceIDToTrack : new String[] { "W4702" }) { + expectedRaceIDs.add(raceIDToTrack); + } + assertEquals(expectedRaceIDs, raceIDs); + } + + @Test + public void testDuplicateCCGMessageAndWaypointUniqueness() throws Exception { + String[] racesToTrack = new String[] { "W4702" }; + setUpUsingScript(racesToTrack, "/DuplicateCCG.txt"); + + Set allTrackedRaces = new HashSet(); + Iterable allEvents = racingEventService.getAllRegattas(); + for (Regatta event : allEvents) { + DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(event); + Iterable trackedRaces = trackedRegatta.getTrackedRaces(); + for (TrackedRace trackedRace : trackedRaces) { + allTrackedRaces.add(trackedRace); + } + } + assertEquals(1, allTrackedRaces.size()); + TrackedRace trackedRace = allTrackedRaces.iterator().next(); + List waypoints = new ArrayList(); + for (Waypoint waypoint : trackedRace.getRace().getCourse().getWaypoints()) { + waypoints.add(waypoint); + } + assertEquals(7, Util.size(waypoints)); + } + + @Test + public void testRongRaceLogRACZero() throws Exception{ + String[] racesToTrack = new String[] { "W4702" }; + String scriptName2 = "/SailMasterDataInterfaceRACZero.txt"; + setUpUsingScript(racesToTrack, scriptName2); + Set allNewTrackedRaces = new HashSet(); + Iterable allNewEvents = racingEventService.getAllRegattas(); + for (Regatta event : allNewEvents) { + DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(event); + Iterable trackedRaces = trackedRegatta.getTrackedRaces(); + for (TrackedRace trackedRace : trackedRaces) { + allNewTrackedRaces.add(trackedRace); + } + } + assertEquals(0, Util.size(allNewTrackedRaces)); + } + + @Test + public void testEndToEndWithSwissTimingData() throws Exception { + String[] racesToTrack = new String[] { "W4702" }; + String scriptName1 = "/SailMasterDataInterfaceRACandSTL.txt"; + String scriptName2 = "/SailMasterDataInterface-ExampleAsText.txt"; + setUpUsingScript(racesToTrack, scriptName1, scriptName2); + + Set allTrackedRaces = new HashSet(); + Iterable allEvents = racingEventService.getAllRegattas(); + for (Regatta event : allEvents) { + DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(event); + Iterable trackedRaces = trackedRegatta.getTrackedRaces(); + for (TrackedRace trackedRace : trackedRaces) { + allTrackedRaces.add(trackedRace); + } + } + } + + private void setUpUsingScript(String[] racesToTrack, String... scriptNames) throws Exception { + for (String raceToTrack : racesToTrack) { + RacesHandle raceHandle = racingEventService.addSwissTimingRace(/* regattaToAddTo */ null /* use a default regatta */, + raceToTrack, "localhost", /* canSendRequests */ + CLIENT_PORT, false, emptyWindStore, -1, emptyRaceCommitteeStore); + raceHandles.add(raceHandle); + if (connector == null) { + connector = racingEventService.getSwissTimingFactory().getOrCreateSailMasterConnector("localhost", + CLIENT_PORT, swissTimingAdapterPersistence, /* canSendRequests */false); + } + } + ScriptedMessagesReader scriptedMessagesReader = new ScriptedMessagesReader(); + for (String scriptName : scriptNames) { + InputStream is = getClass().getResourceAsStream(scriptName); + scriptedMessagesReader.addMessagesFromTextFile(is); + } + for (String msg : scriptedMessagesReader.getMessages()) { + transceiver.sendMessage(msg, sendingStream); + } + transceiver.sendMessage(swissTimingFactory.createMessage(MessageType._STOPSERVER.name(), null), sendingStream); + synchronized (connector) { + while (!connector.isStopped()) { + connector.wait(); + } + } + } + +} diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/DomainFactory.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/DomainFactory.java index 9333848c956..c5101c657b2 100755 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/DomainFactory.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/DomainFactory.java @@ -11,6 +11,7 @@ import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.racecommittee.RaceCommitteeStore; import com.sap.sailing.domain.swisstimingadapter.impl.DomainFactoryImpl; import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.MarkPassing; @@ -49,7 +50,7 @@ public interface DomainFactory { RaceTrackingConnectivityParameters createTrackingConnectivityParameters(String hostname, int port, String raceID, boolean canSendRequests, long delayToLiveInMillis, - SwissTimingFactory swissTimingFactory, DomainFactory domainFactory, WindStore windStore, + SwissTimingFactory swissTimingFactory, DomainFactory domainFactory, WindStore windStore, RaceCommitteeStore raceCommitteeStore, RaceSpecificMessageLoader messageLoader); BoatClass getOrCreateBoatClassFromRaceID(String raceID); diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/SwissTimingFactory.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/SwissTimingFactory.java index 7705e87a902..01d816eedf4 100755 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/SwissTimingFactory.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/SwissTimingFactory.java @@ -6,6 +6,7 @@ import java.text.ParseException; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.racecommittee.RaceCommitteeStore; import com.sap.sailing.domain.swisstimingadapter.impl.SwissTimingFactoryImpl; import com.sap.sailing.domain.tracking.RaceTracker; import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; @@ -51,11 +52,11 @@ public interface SwissTimingFactory { SwissTimingConfiguration createSwissTimingConfiguration(String name, String hostname, int port, boolean canSendRequests); - SwissTimingRaceTracker createRaceTracker(String raceID, String hostname, int port, boolean canSendRequests, long delayToLiveInMillis, WindStore windStore, RaceSpecificMessageLoader messageLoader, DomainFactory domainFactory, TrackedRegattaRegistry trackedRegattaRegistry) throws InterruptedException, UnknownHostException, IOException, ParseException; + SwissTimingRaceTracker createRaceTracker(String raceID, String hostname, int port, boolean canSendRequests, long delayToLiveInMillis, WindStore windStore, RaceSpecificMessageLoader messageLoader, DomainFactory domainFactory, TrackedRegattaRegistry trackedRegattaRegistry, RaceCommitteeStore raceCommitteeStore) throws InterruptedException, UnknownHostException, IOException, ParseException; RaceTracker createRaceTracker(Regatta regatta, String raceID, String hostname, int port, boolean canSendRequests, long delayToLiveInMillis, WindStore windStore, RaceSpecificMessageLoader messageLoader, DomainFactory domainFactory, - TrackedRegattaRegistry trackedRegattaRegistry) throws UnknownHostException, InterruptedException, + TrackedRegattaRegistry trackedRegattaRegistry, RaceCommitteeStore raceCommitteeStore) throws UnknownHostException, InterruptedException, IOException, ParseException; Race createRace(String raceId, String description, TimePoint startTime); diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/DomainFactoryImpl.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/DomainFactoryImpl.java index 8ec40bdf003..8a4c8ef2257 100755 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/DomainFactoryImpl.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/DomainFactoryImpl.java @@ -29,6 +29,7 @@ import com.sap.sailing.domain.base.impl.TeamImpl; import com.sap.sailing.domain.common.ScoringSchemeType; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.Util; +import com.sap.sailing.domain.racecommittee.RaceCommitteeStore; import com.sap.sailing.domain.swisstimingadapter.Course; import com.sap.sailing.domain.swisstimingadapter.DomainFactory; import com.sap.sailing.domain.swisstimingadapter.Fix; @@ -301,10 +302,10 @@ public class DomainFactoryImpl implements DomainFactory { @Override public RaceTrackingConnectivityParameters createTrackingConnectivityParameters(String hostname, int port, String raceID, boolean canSendRequests, long delayToLiveInMillis, - SwissTimingFactory swissTimingFactory, DomainFactory domainFactory, WindStore windStore, + SwissTimingFactory swissTimingFactory, DomainFactory domainFactory, WindStore windStore, RaceCommitteeStore raceCommitteeStore, RaceSpecificMessageLoader messageLoader) { return new SwissTimingTrackingConnectivityParameters(hostname, port, raceID, canSendRequests, delayToLiveInMillis, - swissTimingFactory, domainFactory, windStore, messageLoader); + swissTimingFactory, domainFactory, windStore, raceCommitteeStore, messageLoader); } } diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingFactoryImpl.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingFactoryImpl.java index 82cacd70241..95100c48b1d 100755 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingFactoryImpl.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingFactoryImpl.java @@ -9,6 +9,7 @@ import java.util.Map; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.Util.Triple; +import com.sap.sailing.domain.racecommittee.RaceCommitteeStore; import com.sap.sailing.domain.swisstimingadapter.DomainFactory; import com.sap.sailing.domain.swisstimingadapter.Race; import com.sap.sailing.domain.swisstimingadapter.RaceSpecificMessageLoader; @@ -80,19 +81,20 @@ public class SwissTimingFactoryImpl implements SwissTimingFactory { @Override public SwissTimingRaceTracker createRaceTracker(String raceID, String hostname, int port, boolean canSendRequests, long delayToLiveInMillis, WindStore windStore, RaceSpecificMessageLoader messageLoader, DomainFactory domainFactory, - TrackedRegattaRegistry trackedRegattaRegistry) throws InterruptedException, UnknownHostException, IOException, + TrackedRegattaRegistry trackedRegattaRegistry, RaceCommitteeStore raceCommitteeStore) throws InterruptedException, UnknownHostException, IOException, ParseException { return new SwissTimingRaceTrackerImpl(raceID, hostname, port, windStore, domainFactory, this, messageLoader, - trackedRegattaRegistry, canSendRequests, delayToLiveInMillis); + trackedRegattaRegistry, canSendRequests, delayToLiveInMillis, raceCommitteeStore); } @Override public RaceTracker createRaceTracker(Regatta regatta, String raceID, String hostname, int port, boolean canSendRequests, long delayToLiveInMillis, WindStore windStore, RaceSpecificMessageLoader messageLoader, - DomainFactory domainFactory, TrackedRegattaRegistry trackedRegattaRegistry) throws UnknownHostException, + DomainFactory domainFactory, TrackedRegattaRegistry trackedRegattaRegistry, + RaceCommitteeStore raceCommitteeStore) throws UnknownHostException, InterruptedException, IOException, ParseException { return new SwissTimingRaceTrackerImpl(regatta, raceID, hostname, port, windStore, domainFactory, this, - messageLoader, trackedRegattaRegistry, canSendRequests, delayToLiveInMillis); + messageLoader, trackedRegattaRegistry, canSendRequests, delayToLiveInMillis, raceCommitteeStore); } @Override diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingRaceTrackerImpl.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingRaceTrackerImpl.java index a8a2b51654a..d50bc28fa8b 100644 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingRaceTrackerImpl.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingRaceTrackerImpl.java @@ -22,6 +22,7 @@ import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.Util; import com.sap.sailing.domain.common.impl.Util.Triple; +import com.sap.sailing.domain.racecommittee.RaceCommitteeStore; import com.sap.sailing.domain.swisstimingadapter.Course; import com.sap.sailing.domain.swisstimingadapter.DomainFactory; import com.sap.sailing.domain.swisstimingadapter.Fix; @@ -59,6 +60,7 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl implemen private final Triple id; private final Regatta regatta; private final WindStore windStore; + private final RaceCommitteeStore raceCommitteeStore; private RaceDefinition race; private Course course; @@ -71,15 +73,17 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl implemen protected SwissTimingRaceTrackerImpl(String raceID, String hostname, int port, WindStore windStore, DomainFactory domainFactory, SwissTimingFactory factory, RaceSpecificMessageLoader messageLoader, - TrackedRegattaRegistry trackedRegattaRegistry, boolean canSendRequests, long delayToLiveInMillis) throws InterruptedException, + TrackedRegattaRegistry trackedRegattaRegistry, boolean canSendRequests, long delayToLiveInMillis, + RaceCommitteeStore raceCommitteeStore) throws InterruptedException, UnknownHostException, IOException, ParseException { this(domainFactory.getOrCreateRegatta(raceID, trackedRegattaRegistry), raceID, hostname, port, windStore, domainFactory, factory, - messageLoader, trackedRegattaRegistry, canSendRequests, delayToLiveInMillis); + messageLoader, trackedRegattaRegistry, canSendRequests, delayToLiveInMillis, raceCommitteeStore); } protected SwissTimingRaceTrackerImpl(Regatta regatta, String raceID, String hostname, int port, WindStore windStore, DomainFactory domainFactory, SwissTimingFactory factory, RaceSpecificMessageLoader messageLoader, - TrackedRegattaRegistry trackedRegattaRegistry, boolean canSendRequests, long delayToLiveInMillis) throws InterruptedException, + TrackedRegattaRegistry trackedRegattaRegistry, boolean canSendRequests, long delayToLiveInMillis, + RaceCommitteeStore raceCommitteeStore) throws InterruptedException, UnknownHostException, IOException, ParseException { super(); this.regatta = regatta; @@ -88,6 +92,7 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl implemen this.raceID = raceID; this.messageLoader = messageLoader; this.windStore = windStore; + this.raceCommitteeStore = raceCommitteeStore; this.id = createID(raceID, hostname, port); connector.addSailMasterListener(raceID, this); trackedRegatta = trackedRegattaRegistry.getOrCreateTrackedRegatta(regatta); @@ -278,7 +283,7 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl implemen // we already know our single RaceDefinition assert SwissTimingRaceTrackerImpl.this.race == race; } - }); + }, raceCommitteeStore); logger.info("Created SwissTiming RaceDefinition and TrackedRace for "+race.getName()); } diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingTrackingConnectivityParameters.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingTrackingConnectivityParameters.java index ad7e0ca7655..417d60280f2 100755 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingTrackingConnectivityParameters.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingTrackingConnectivityParameters.java @@ -1,6 +1,7 @@ package com.sap.sailing.domain.swisstimingadapter.impl; import com.sap.sailing.domain.base.Regatta; +import com.sap.sailing.domain.racecommittee.RaceCommitteeStore; import com.sap.sailing.domain.swisstimingadapter.DomainFactory; import com.sap.sailing.domain.swisstimingadapter.RaceSpecificMessageLoader; import com.sap.sailing.domain.swisstimingadapter.SwissTimingFactory; @@ -17,11 +18,12 @@ public class SwissTimingTrackingConnectivityParameters implements RaceTrackingCo private final SwissTimingFactory swissTimingFactory; private final DomainFactory domainFactory; private final WindStore windStore; + private final RaceCommitteeStore raceCommitteeStore; private final RaceSpecificMessageLoader messageLoader; private final long delayToLiveInMillis; public SwissTimingTrackingConnectivityParameters(String hostname, int port, String raceID, boolean canSendRequests, long delayToLiveInMillis, - SwissTimingFactory swissTimingFactory, DomainFactory domainFactory, WindStore windStore, + SwissTimingFactory swissTimingFactory, DomainFactory domainFactory, WindStore windStore, RaceCommitteeStore raceCommitteeStore, RaceSpecificMessageLoader messageLoader) { super(); this.hostname = hostname; @@ -32,20 +34,21 @@ public class SwissTimingTrackingConnectivityParameters implements RaceTrackingCo this.swissTimingFactory = swissTimingFactory; this.domainFactory = domainFactory; this.windStore = windStore; + this.raceCommitteeStore = raceCommitteeStore; this.messageLoader = messageLoader; } @Override public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry) throws Exception { return swissTimingFactory.createRaceTracker(raceID, hostname, port, canSendRequests, delayToLiveInMillis, windStore, messageLoader, - domainFactory, trackedRegattaRegistry); + domainFactory, trackedRegattaRegistry, raceCommitteeStore); } @Override public RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry) throws Exception { return swissTimingFactory.createRaceTracker(regatta, raceID, hostname, port, canSendRequests, delayToLiveInMillis, windStore, messageLoader, - domainFactory, trackedRegattaRegistry); + domainFactory, trackedRegattaRegistry, raceCommitteeStore); } @Override diff --git a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayToDomainAdapter.java b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayToDomainAdapter.java index bbe0b98bcc0..cdbb1d54673 100755 --- a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayToDomainAdapter.java +++ b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayToDomainAdapter.java @@ -34,6 +34,7 @@ import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.impl.Util; import com.sap.sailing.domain.common.impl.WindSourceWithAdditionalID; +import com.sap.sailing.domain.racecommittee.impl.EmptyRaceCommitteeStore; import com.sap.sailing.domain.swisstimingadapter.DomainFactory; import com.sap.sailing.domain.swisstimingreplayadapter.CompetitorStatus; import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayListener; @@ -305,7 +306,7 @@ public class SwissTimingReplayToDomainAdapter extends SwissTimingReplayAdapter { createTrackedRace(race, EmptyWindStore.INSTANCE, TrackedRace.DEFAULT_LIVE_DELAY_IN_MILLISECONDS, WindTrack.DEFAULT_MILLISECONDS_OVER_WHICH_TO_AVERAGE_WIND, /* time over which to average speed: */ race.getBoatClass().getApproximateManeuverDurationInMilliseconds(), - /* raceDefinitionSetToUpdate */ null); + /* raceDefinitionSetToUpdate */ null, EmptyRaceCommitteeStore.INSTANCE); TimePoint bestStartTimeKnownSoFar = bestStartTimePerRaceID.get(currentRaceID); if (bestStartTimeKnownSoFar != null) { trackedRace.setStartTimeReceived(bestStartTimeKnownSoFar); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseTest.java index d0af98bb745..a8d8fff04a2 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseTest.java @@ -25,6 +25,7 @@ import com.sap.sailing.domain.base.impl.MarkImpl; import com.sap.sailing.domain.base.impl.RaceDefinitionImpl; import com.sap.sailing.domain.base.impl.WaypointImpl; import com.sap.sailing.domain.common.impl.Util; +import com.sap.sailing.domain.racecommittee.impl.EmptyRaceCommitteeStore; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.TrackedLeg; import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl; @@ -243,7 +244,8 @@ public class CourseTest { hasso), EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 3000, /* millisecondsOverWhichToAverageWind */ 30000, - /* millisecondsOverWhichToAverageSpeed */ 8000); + /* millisecondsOverWhichToAverageSpeed */ 8000, + EmptyRaceCommitteeStore.INSTANCE); assertLegStructure(course, trackedRace); course.removeWaypoint(0); assertLegStructure(course, trackedRace); @@ -273,7 +275,8 @@ public class CourseTest { hasso), EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 3000, /* millisecondsOverWhichToAverageWind */ 30000, - /* millisecondsOverWhichToAverageSpeed */ 8000); + /* millisecondsOverWhichToAverageSpeed */ 8000, + EmptyRaceCommitteeStore.INSTANCE); assertLegStructure(course, trackedRace); final WaypointImpl wp1_5 = new WaypointImpl(new MarkImpl("Test Mark 1.5")); assertWaypointIndexes(course); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseUpdateTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseUpdateTest.java index 945828e6a4c..7171b8f673a 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseUpdateTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseUpdateTest.java @@ -31,6 +31,7 @@ import com.sap.sailing.domain.base.impl.CourseImpl; import com.sap.sailing.domain.base.impl.WaypointImpl; import com.sap.sailing.domain.common.impl.Util; import com.sap.sailing.domain.common.impl.Util.Triple; +import com.sap.sailing.domain.racecommittee.impl.EmptyRaceCommitteeStore; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; import com.sap.sailing.domain.tracking.TrackedLeg; @@ -76,7 +77,8 @@ public class CourseUpdateTest extends AbstractTracTracLiveTest { @Override public void addRaceDefinition(RaceDefinition race) {} }, /* delayToLiveInMillis */ 0l, - /* millisecondsOverWhichToAverageWind */ 30000, /* simulator */ null) { + /* millisecondsOverWhichToAverageWind */ 30000, /* simulator */ null, + EmptyRaceCommitteeStore.INSTANCE) { @Override protected void handleEvent(Triple event) { super.handleEvent(event); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java index aeb1f24f2b6..287647900a1 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java @@ -15,6 +15,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; +import com.sap.sailing.domain.racecommittee.impl.EmptyRaceCommitteeStore; import com.sap.sailing.domain.tracking.impl.EmptyWindStore; import com.sap.sailing.domain.tractracadapter.DomainFactory; import com.sap.sailing.domain.tractracadapter.TracTracConnectionConstants; @@ -50,25 +51,28 @@ public class MultipleClassesInRegattaTest { httpAndHost+"/events/event_20110505_SailingTea/clientparams.php?event=event_20110505_SailingTea&race=cce678c8-97e6-11e0-9aed-406186cbf87c"), new URI(liveURI), new URI(storedURI), /* startOfTracking */ null, /* endOfTracking */ null, /* delayToLiveInMillis */ 0l, /* simulateWithStartTimeNow */ false, - EmptyWindStore.INSTANCE, new DummyTrackedRegattaRegistry()); + EmptyWindStore.INSTANCE, new DummyTrackedRegattaRegistry(), EmptyRaceCommitteeStore.INSTANCE); kiwotest2 = domainFactory .createRaceTracker( new URL( httpAndHost+"/events/event_20110505_SailingTea/clientparams.php?event=event_20110505_SailingTea&race=11290bd6-97e7-11e0-9aed-406186cbf87c"), new URI(liveURI), new URI(storedURI), - /* startOfTracking */ null, /* endOfTracking */ null, /* delayToLiveInMillis */ 0l, /* simulateWithStartTimeNow */ false, EmptyWindStore.INSTANCE, new DummyTrackedRegattaRegistry()); + /* startOfTracking */ null, /* endOfTracking */ null, /* delayToLiveInMillis */ 0l, /* simulateWithStartTimeNow */ false, EmptyWindStore.INSTANCE, new DummyTrackedRegattaRegistry(), + EmptyRaceCommitteeStore.INSTANCE); kiwotest3 = domainFactory .createRaceTracker( new URL( httpAndHost+"/events/event_20110505_SailingTea/clientparams.php?event=event_20110505_SailingTea&race=39635b24-97e7-11e0-9aed-406186cbf87c"), new URI(liveURI), new URI(storedURI), - /* startOfTracking */ null, /* endOfTracking */ null, /* delayToLiveInMillis */ 0l, /* simulateWithStartTimeNow */ false, EmptyWindStore.INSTANCE, new DummyTrackedRegattaRegistry()); + /* startOfTracking */ null, /* endOfTracking */ null, /* delayToLiveInMillis */ 0l, /* simulateWithStartTimeNow */ false, EmptyWindStore.INSTANCE, new DummyTrackedRegattaRegistry(), + EmptyRaceCommitteeStore.INSTANCE); weym470may112014_2 = domainFactory .createRaceTracker( new URL( httpAndHost+"/events/event_20110505_SailingTea/clientparams.php?event=event_20110505_SailingTea&race=04498426-7dfd-11e0-8236-406186cbf87c"), new URI(liveURI), new URI(storedURI), - /* startOfTracking */ null, /* endOfTracking */ null, /* delayToLiveInMillis */ 0l, /* simulateWithStartTimeNow */ false, EmptyWindStore.INSTANCE, new DummyTrackedRegattaRegistry()); + /* startOfTracking */ null, /* endOfTracking */ null, /* delayToLiveInMillis */ 0l, /* simulateWithStartTimeNow */ false, EmptyWindStore.INSTANCE, new DummyTrackedRegattaRegistry(), + EmptyRaceCommitteeStore.INSTANCE); assertEquals("STG", kiwotest1.getRegatta().getBoatClass().getName()); assertEquals("505", kiwotest2.getRegatta().getBoatClass().getName()); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/OnlineTracTracBasedTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/OnlineTracTracBasedTest.java index e8dada94d20..8992d77e34a 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/OnlineTracTracBasedTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/OnlineTracTracBasedTest.java @@ -26,6 +26,7 @@ import com.sap.sailing.domain.base.impl.MillisecondsTimePoint; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.DegreePosition; +import com.sap.sailing.domain.racecommittee.impl.EmptyRaceCommitteeStore; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; import com.sap.sailing.domain.tracking.DynamicTrackedRace; @@ -89,7 +90,7 @@ public abstract class OnlineTracTracBasedTest extends AbstractTracTracLiveTest { throws InterruptedException { setStoredDataLoaded(false); ArrayList receivers = new ArrayList(); - for (Receiver r : domainFactory.getUpdateReceivers(trackedRegatta, getTracTracEvent(), EmptyWindStore.INSTANCE, + for (Receiver r : domainFactory.getUpdateReceivers(trackedRegatta, getTracTracEvent(), EmptyWindStore.INSTANCE, EmptyRaceCommitteeStore.INSTANCE, /* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* simulator */ null, new DynamicRaceDefinitionSet() { @Override diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReachingLegTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReachingLegTest.java index 4ccec0ed062..7184ca31c7a 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReachingLegTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReachingLegTest.java @@ -40,6 +40,7 @@ import com.sap.sailing.domain.common.WindSourceType; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.impl.WindSourceImpl; +import com.sap.sailing.domain.racecommittee.impl.EmptyRaceCommitteeStore; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.TrackedLeg; @@ -100,7 +101,8 @@ public class ReachingLegTest extends TrackBasedTest { DynamicTrackedRace trackedRace = new DynamicTrackedRaceImpl(trackedRegatta, race, EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 0, /* millisecondsOverWhichToAverageWind */ 30000, /* millisecondsOverWhichToAverageSpeed */ 30000, - /* delay for wind estimation cache invalidation */ 0); + /* delay for wind estimation cache invalidation */ 0, + EmptyRaceCommitteeStore.INSTANCE); // in this simplified artificial course, the top mark is exactly north of the right leeward gate, the offset // mark is slightly west of the top mark; wind from the north makes the leg from top to offset a reaching leg Position leftPosition = new DegreePosition(0, -0.00001); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveMarkPassingDataTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveMarkPassingDataTest.java index 31fa86479db..4794eb4b1fa 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveMarkPassingDataTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveMarkPassingDataTest.java @@ -15,6 +15,7 @@ import org.junit.Test; import com.maptrack.client.io.TypeController; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Waypoint; +import com.sap.sailing.domain.racecommittee.impl.EmptyRaceCommitteeStore; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl; @@ -94,7 +95,7 @@ public class ReceiveMarkPassingDataTest extends AbstractTracTracLiveTest { receivers.add(receiver); for (Receiver r : DomainFactory.INSTANCE.getUpdateReceivers( new DynamicTrackedRegattaImpl(DomainFactory.INSTANCE.getOrCreateDefaultRegatta(getTracTracEvent(), /* trackedRegattaRegistry */ null)), - getTracTracEvent(), EmptyWindStore.INSTANCE, /* startOfTracking */ null, /* endOfTracking */ null, /* delayToLiveInMillis */ 0l, + getTracTracEvent(), EmptyWindStore.INSTANCE, EmptyRaceCommitteeStore.INSTANCE, /* startOfTracking */ null, /* endOfTracking */ null, /* delayToLiveInMillis */ 0l, /* simulator */ null, new DynamicRaceDefinitionSet() { @Override public void addRaceDefinition(RaceDefinition race) {} diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveTrackingDataTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveTrackingDataTest.java index 5ef4d85e270..7848f66279e 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveTrackingDataTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReceiveTrackingDataTest.java @@ -14,6 +14,7 @@ import com.maptrack.client.io.TypeController; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.RaceDefinition; +import com.sap.sailing.domain.racecommittee.impl.EmptyRaceCommitteeStore; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; import com.sap.sailing.domain.tracking.DynamicTrackedRace; @@ -83,7 +84,7 @@ public class ReceiveTrackingDataTest extends AbstractTracTracLiveTest { @Override public void addRaceDefinition(RaceDefinition race) { } - }, /* trackedRegattaRegistry */ null)) { + }, /* trackedRegattaRegistry */ null, EmptyRaceCommitteeStore.INSTANCE)) { for (TypeController raceListener : receiver.getTypeControllersAndStart()) { listeners.add(raceListener); } @@ -94,7 +95,7 @@ public class ReceiveTrackingDataTest extends AbstractTracTracLiveTest { @Override public void addRaceDefinition(RaceDefinition race) { } - }, /* trackedRegattaRegistry */ null)); + }, /* trackedRegattaRegistry */ null, EmptyRaceCommitteeStore.INSTANCE)); } @Test diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackBasedTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackBasedTest.java index ffc6008d1b1..b46476f77f7 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackBasedTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackBasedTest.java @@ -33,6 +33,7 @@ import com.sap.sailing.domain.common.WindSourceType; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.impl.WindSourceImpl; +import com.sap.sailing.domain.racecommittee.impl.EmptyRaceCommitteeStore; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.TrackedRegatta; @@ -110,7 +111,8 @@ public abstract class TrackBasedTest { DynamicTrackedRace trackedRace = new DynamicTrackedRaceImpl(trackedRegatta, race, EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 0, /* millisecondsOverWhichToAverageWind */ 30000, /* millisecondsOverWhichToAverageSpeed */ 30000, - /* delay for wind estimation cache invalidation */ 0); + /* delay for wind estimation cache invalidation */ 0, + EmptyRaceCommitteeStore.INSTANCE); // in this simplified artificial course, the top mark is exactly north of the right leeward gate DegreePosition topPosition = new DegreePosition(54.48, 10.24); TimePoint afterTheRace = new MillisecondsTimePoint(timePointForFixes.asMillis() + 36000000); // 10h after the fix time diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/UnicodeCharactersInCompetitorNamesTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/UnicodeCharactersInCompetitorNamesTest.java index 0b8ecf5cc7c..e5a0c1196f9 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/UnicodeCharactersInCompetitorNamesTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/UnicodeCharactersInCompetitorNamesTest.java @@ -14,6 +14,7 @@ import org.junit.Before; import org.junit.Test; import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.racecommittee.impl.EmptyRaceCommitteeStore; import com.sap.sailing.domain.tracking.impl.EmptyWindStore; import com.sap.sailing.domain.tractracadapter.DomainFactory; import com.sap.sailing.domain.tractracadapter.TracTracConnectionConstants; @@ -54,7 +55,7 @@ public class UnicodeCharactersInCompetitorNamesTest { + TracTracConnectionConstants.PORT_STORED), /* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* simulateWithStartTimeNow */ false, EmptyWindStore.INSTANCE, - new DummyTrackedRegattaRegistry()); + new DummyTrackedRegattaRegistry(), EmptyRaceCommitteeStore.INSTANCE); Iterable competitors = fourtyninerYellow_2.getRacesHandle().getRaces().iterator().next() .getCompetitors(); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindTest.java index d4140796f94..f3e11edd3ec 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindTest.java @@ -36,6 +36,7 @@ import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.impl.Util; +import com.sap.sailing.domain.racecommittee.impl.EmptyRaceCommitteeStore; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.Wind; @@ -268,7 +269,8 @@ public class WindTest { boatClass, Collections.singleton(competitor)), EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 1000, /* millisecondsOverWhichToAverageWind */ 30000, - /* millisecondsOverWhichToAverageSpeed */ 30000); + /* millisecondsOverWhichToAverageSpeed */ 30000, + EmptyRaceCommitteeStore.INSTANCE); TimePoint start = MillisecondsTimePoint.now(); TimePoint topMarkRounding = start.plus(30000); TimePoint finish = topMarkRounding.plus(30000); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java index 7f78ed662f9..3943a04f238 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java @@ -445,7 +445,7 @@ public class MockedTrackedRace implements DynamicTrackedRace { @Override public DynamicTrackedRace createTrackedRace(RaceDefinition raceDefinition, WindStore windStore, long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed, - DynamicRaceDefinitionSet raceDefinitionSetToUpdate) { + DynamicRaceDefinitionSet raceDefinitionSetToUpdate, RaceCommitteeStore raceCommitteeStore) { // TODO Auto-generated method stub return null; } diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java index 6468e625379..5c82f02ce1e 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java @@ -22,6 +22,7 @@ import com.sap.sailing.domain.base.Team; import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.Util.Pair; +import com.sap.sailing.domain.racecommittee.RaceCommitteeStore; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; @@ -123,19 +124,23 @@ 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 raceCommitteeStore + * Provides the capability to obtain the {@link RaceCommitteeTrack} for the race. A trivial + * implementation is {@link EmptyRaceCommitteeStore} which simply provides new, empty tracks. This is always + * available but loses track of race committee events, e.g., during server restarts. */ TracTracRaceTracker createRaceTracker(URL paramURL, URI liveURI, URI storedURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, boolean simulateWithStartTimeNow, WindStore windStore, - TrackedRegattaRegistry trackedRegattaRegistry) throws MalformedURLException, FileNotFoundException, + TrackedRegattaRegistry trackedRegattaRegistry, RaceCommitteeStore raceCommitteeStore) throws MalformedURLException, FileNotFoundException, URISyntaxException; /** - * Same as {@link #createRaceTracker(URL, URI, URI, TimePoint, TimePoint, WindStore, TrackedRegattaRegistry)}, only that + * Same as {@link #createRaceTracker(URL, URI, URI, TimePoint, TimePoint, WindStore, TrackedRegattaRegistry, RaceCommitteeStore)}, only that * a predefined {@link Regatta} is used to hold the resulting races. */ RaceTracker createRaceTracker(Regatta regatta, URL paramURL, URI liveURI, URI storedURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, boolean simulateWithStartTimeNow, WindStore windStore, - TrackedRegattaRegistry trackedRegattaRegistry) throws MalformedURLException, FileNotFoundException, + TrackedRegattaRegistry trackedRegattaRegistry, RaceCommitteeStore raceCommitteeStore) throws MalformedURLException, FileNotFoundException, URISyntaxException; BoatClass getOrCreateBoatClass(CompetitorClass competitorClass); @@ -165,7 +170,8 @@ public interface DomainFactory { * {@link RaceCourseReceiver} created by this call */ Iterable getUpdateReceivers(DynamicTrackedRegatta trackedRegatta, Event tractracEvent, TimePoint startOfTracking, - TimePoint endOfTracking, long delayToLiveInMillis, Simulator simulator, WindStore windStore, DynamicRaceDefinitionSet raceDefinitionSetToUpdate, TrackedRegattaRegistry trackedRegattaRegistry); + TimePoint endOfTracking, long delayToLiveInMillis, Simulator simulator, WindStore windStore, DynamicRaceDefinitionSet raceDefinitionSetToUpdate, TrackedRegattaRegistry trackedRegattaRegistry, + RaceCommitteeStore raceCommitteeStore); /** * Creates a {@link RaceDefinition} from a TracTrac {@link Race} and a domain {@link Course} definition. The @@ -186,7 +192,7 @@ public interface DomainFactory { */ DynamicTrackedRace getOrCreateRaceDefinitionAndTrackedRace(TrackedRegatta trackedRegatta, Race race, Course course, WindStore windStore, long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, - DynamicRaceDefinitionSet raceDefinitionSetToUpdate); + DynamicRaceDefinitionSet raceDefinitionSetToUpdate, RaceCommitteeStore raceCommitteeStore); /** * The record may be for a single mark or a gate. If for a gate, the @@ -199,7 +205,7 @@ public interface DomainFactory { MarkPassing createMarkPassing(TimePoint timePoint, Waypoint passed, com.sap.sailing.domain.base.Competitor competitor); - Iterable getUpdateReceivers(DynamicTrackedRegatta trackedRegatta, Event tractracEvent, WindStore windStore, + Iterable getUpdateReceivers(DynamicTrackedRegatta trackedRegatta, Event tractracEvent, WindStore windStore, RaceCommitteeStore raceCommitteeStore, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, Simulator simulator, DynamicRaceDefinitionSet raceDefinitionSetToUpdate, TrackedRegattaRegistry trackedRegattaRegistry, ReceiverType... types); JSONService parseJSONURL(URL jsonURL) throws IOException, ParseException, org.json.simple.parser.ParseException, URISyntaxException; @@ -235,7 +241,8 @@ public interface DomainFactory { Pair, BoatClass> getCompetitorsAndDominantBoatClass(Race race); RaceTrackingConnectivityParameters createTrackingConnectivityParameters(URL paramURL, URI liveURI, URI storedURI, - TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, boolean simulateWithStartTimeNow, WindStore windStore); + TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, boolean simulateWithStartTimeNow, WindStore windStore, + RaceCommitteeStore raceCommitteeStore); /** * Removes all knowledge about tractracRace 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 diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java index 424b73d2f20..c01e4efe793 100644 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java @@ -45,6 +45,7 @@ import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.impl.Util; import com.sap.sailing.domain.common.impl.Util.Pair; +import com.sap.sailing.domain.racecommittee.RaceCommitteeStore; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; @@ -348,7 +349,7 @@ public class DomainFactoryImpl implements DomainFactory { @Override public Iterable getUpdateReceivers(DynamicTrackedRegatta trackedRegatta, - com.tractrac.clientmodule.Event tractracEvent, WindStore windStore, TimePoint startOfTracking, + com.tractrac.clientmodule.Event tractracEvent, WindStore windStore, RaceCommitteeStore raceCommitteeStore, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, Simulator simulator, DynamicRaceDefinitionSet raceDefinitionSetToUpdate, TrackedRegattaRegistry trackedRegattaRegistry, ReceiverType... types) { @@ -359,7 +360,7 @@ public class DomainFactoryImpl implements DomainFactory { result.add(new RaceCourseReceiver( this, trackedRegatta, tractracEvent, windStore, raceDefinitionSetToUpdate, delayToLiveInMillis, - WindTrack.DEFAULT_MILLISECONDS_OVER_WHICH_TO_AVERAGE_WIND, simulator)); + WindTrack.DEFAULT_MILLISECONDS_OVER_WHICH_TO_AVERAGE_WIND, simulator, raceCommitteeStore)); break; case MARKPOSITIONS: result.add(new MarkPositionReceiver( @@ -386,8 +387,8 @@ public class DomainFactoryImpl implements DomainFactory { public Iterable getUpdateReceivers(DynamicTrackedRegatta trackedRegatta, com.tractrac.clientmodule.Event tractracEvent, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, Simulator simulator, WindStore windStore, - DynamicRaceDefinitionSet raceDefinitionSetToUpdate, TrackedRegattaRegistry trackedRegattaRegistry) { - return getUpdateReceivers(trackedRegatta, tractracEvent, windStore, startOfTracking, endOfTracking, + DynamicRaceDefinitionSet raceDefinitionSetToUpdate, TrackedRegattaRegistry trackedRegattaRegistry, RaceCommitteeStore raceCommitteeStore) { + return getUpdateReceivers(trackedRegatta, tractracEvent, windStore, raceCommitteeStore, startOfTracking, endOfTracking, delayToLiveInMillis, simulator, raceDefinitionSetToUpdate, trackedRegattaRegistry, ReceiverType.RACECOURSE, ReceiverType.MARKPASSINGS, ReceiverType.MARKPOSITIONS, ReceiverType.RACESTARTFINISH, ReceiverType.RAWPOSITIONS); @@ -447,7 +448,7 @@ public class DomainFactoryImpl implements DomainFactory { @Override public DynamicTrackedRace getOrCreateRaceDefinitionAndTrackedRace(TrackedRegatta trackedRegatta, Race race, Course course, WindStore windStore, long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, - DynamicRaceDefinitionSet raceDefinitionSetToUpdate) { + DynamicRaceDefinitionSet raceDefinitionSetToUpdate, RaceCommitteeStore raceCommitteeStore) { synchronized (raceCache) { RaceDefinition raceDefinition = raceCache.get(race); if (raceDefinition == null) { @@ -459,7 +460,7 @@ public class DomainFactoryImpl implements DomainFactory { if (raceDefinition.getBoatClass() == trackedRegatta.getRegatta().getBoatClass()) { trackedRegatta.getRegatta().addRace(raceDefinition); DynamicTrackedRace trackedRace = createTrackedRace(trackedRegatta, raceDefinition, windStore, - delayToLiveInMillis, millisecondsOverWhichToAverageWind, raceDefinitionSetToUpdate); + delayToLiveInMillis, millisecondsOverWhichToAverageWind, raceDefinitionSetToUpdate, raceCommitteeStore); logger.info("Added race "+raceDefinition+" to regatta "+trackedRegatta.getRegatta()); synchronized (raceCache) { raceCache.put(race, raceDefinition); @@ -479,11 +480,12 @@ public class DomainFactoryImpl implements DomainFactory { } private DynamicTrackedRace createTrackedRace(TrackedRegatta trackedRegatta, RaceDefinition race, WindStore windStore, - long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, DynamicRaceDefinitionSet raceDefinitionSetToUpdate) { + long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, DynamicRaceDefinitionSet raceDefinitionSetToUpdate, + RaceCommitteeStore raceCommitteeStore) { return trackedRegatta.createTrackedRace(race, windStore, delayToLiveInMillis, millisecondsOverWhichToAverageWind, /* time over which to average speed: */ race.getBoatClass().getApproximateManeuverDurationInMilliseconds(), - raceDefinitionSetToUpdate); + raceDefinitionSetToUpdate, raceCommitteeStore); } @Override @@ -548,19 +550,20 @@ public class DomainFactoryImpl implements DomainFactory { @Override public TracTracRaceTracker createRaceTracker(URL paramURL, URI liveURI, URI storedURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, boolean simulateWithStartTimeNow, WindStore windStore, - TrackedRegattaRegistry trackedRegattaRegistry) throws MalformedURLException, FileNotFoundException, + TrackedRegattaRegistry trackedRegattaRegistry, RaceCommitteeStore raceCommitteeStore) throws MalformedURLException, FileNotFoundException, URISyntaxException { return new TracTracRaceTrackerImpl(this, paramURL, liveURI, storedURI, startOfTracking, endOfTracking, delayToLiveInMillis, - simulateWithStartTimeNow, windStore, trackedRegattaRegistry); + simulateWithStartTimeNow, windStore, trackedRegattaRegistry, raceCommitteeStore); } @Override public RaceTracker createRaceTracker(Regatta regatta, URL paramURL, URI liveURI, URI storedURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, - boolean simulateWithStartTimeNow, WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry) + boolean simulateWithStartTimeNow, WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry, + RaceCommitteeStore raceCommitteeStore) throws MalformedURLException, FileNotFoundException, URISyntaxException { return new TracTracRaceTrackerImpl(regatta, this, paramURL, liveURI, storedURI, startOfTracking, endOfTracking, delayToLiveInMillis, - simulateWithStartTimeNow, windStore, trackedRegattaRegistry); + simulateWithStartTimeNow, windStore, trackedRegattaRegistry, raceCommitteeStore); } @Override @@ -576,9 +579,9 @@ public class DomainFactoryImpl implements DomainFactory { @Override public RaceTrackingConnectivityParameters createTrackingConnectivityParameters(URL paramURL, URI liveURI, URI storedURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, - boolean simulateWithStartTimeNow, WindStore windStore) { + boolean simulateWithStartTimeNow, WindStore windStore, RaceCommitteeStore raceCommitteeStore) { return new RaceTrackingConnectivityParametersImpl(paramURL, liveURI, storedURI, startOfTracking, endOfTracking, - delayToLiveInMillis, simulateWithStartTimeNow, windStore, this); + delayToLiveInMillis, simulateWithStartTimeNow, windStore, this, raceCommitteeStore); } } diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java index 33f3bc2046e..59c864b41ac 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java @@ -11,6 +11,7 @@ import com.sap.sailing.domain.base.Course; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.common.impl.Util.Triple; +import com.sap.sailing.domain.racecommittee.RaceCommitteeStore; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; @@ -39,12 +40,13 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue(); Set typeControllers = new HashSet(); for (Receiver receiver : domainFactory.getUpdateReceivers(getTrackedRegatta(), tractracEvent, startOfTracking, - endOfTracking, delayToLiveInMillis, simulator, windStore, this, trackedRegattaRegistry)) { + endOfTracking, delayToLiveInMillis, simulator, windStore, this, trackedRegattaRegistry, raceCommitteeStore)) { receivers.add(receiver); for (TypeController typeController : receiver.getTypeControllersAndStart()) { typeControllers.add(typeController); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicTrackedRegatta.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicTrackedRegatta.java index 907197e36a6..ee619c3d9c3 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicTrackedRegatta.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicTrackedRegatta.java @@ -1,6 +1,7 @@ package com.sap.sailing.domain.tracking; import com.sap.sailing.domain.base.RaceDefinition; +import com.sap.sailing.domain.racecommittee.RaceCommitteeStore; public interface DynamicTrackedRegatta extends TrackedRegatta { @@ -19,6 +20,6 @@ public interface DynamicTrackedRegatta extends TrackedRegatta { */ DynamicTrackedRace createTrackedRace(RaceDefinition raceDefinition, WindStore windStore, long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed, - DynamicRaceDefinitionSet raceDefinitionSetToUpdate); + DynamicRaceDefinitionSet raceDefinitionSetToUpdate, RaceCommitteeStore raceCommitteeStore); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegatta.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegatta.java index eb0bb6549e9..52707f823d2 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegatta.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegatta.java @@ -8,6 +8,7 @@ import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.common.NoWindException; import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.racecommittee.RaceCommitteeStore; /** * Manages a set of {@link TrackedRace} objects that belong to the same {@link Regatta} (regatta, sailing regatta for a @@ -41,7 +42,7 @@ public interface TrackedRegatta extends Serializable { */ DynamicTrackedRace createTrackedRace(RaceDefinition raceDefinition, WindStore windStore, long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed, - DynamicRaceDefinitionSet raceDefinitionSetToUpdate); + DynamicRaceDefinitionSet raceDefinitionSetToUpdate, RaceCommitteeStore raceCommitteeStore); /** * Obtains the tracked race for race. Blocks until the tracked race has been created diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java index db8dc9b672e..762239c8aef 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java @@ -26,6 +26,7 @@ import com.sap.sailing.domain.common.WindSourceType; import com.sap.sailing.domain.common.impl.Util; import com.sap.sailing.domain.common.impl.WindSourceImpl; import com.sap.sailing.domain.racecommittee.RaceCommitteeEvent; +import com.sap.sailing.domain.racecommittee.RaceCommitteeStore; import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; @@ -56,9 +57,9 @@ public class DynamicTrackedRaceImpl extends TrackedRaceImpl implements public DynamicTrackedRaceImpl(TrackedRegatta trackedRegatta, RaceDefinition race, WindStore windStore, long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed, - long delayForCacheInvalidationOfWindEstimation) { + long delayForCacheInvalidationOfWindEstimation, RaceCommitteeStore raceCommitteeStore) { super(trackedRegatta, race, windStore, delayToLiveInMillis, millisecondsOverWhichToAverageWind, millisecondsOverWhichToAverageSpeed, - delayForCacheInvalidationOfWindEstimation); + delayForCacheInvalidationOfWindEstimation, raceCommitteeStore); this.raceIsKnownToStartUpwind = race.getBoatClass().typicallyStartsUpwind(); if (!raceIsKnownToStartUpwind) { Set windSourcesToExclude = new HashSet(); @@ -94,9 +95,9 @@ public class DynamicTrackedRaceImpl extends TrackedRaceImpl implements */ public DynamicTrackedRaceImpl(TrackedRegatta trackedRegatta, RaceDefinition race, WindStore windStore, long delayToLiveInMillis, - long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed) { + long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed, RaceCommitteeStore raceCommitteeStore) { this(trackedRegatta, race, windStore, delayToLiveInMillis, millisecondsOverWhichToAverageWind, millisecondsOverWhichToAverageSpeed, - millisecondsOverWhichToAverageWind/2); + millisecondsOverWhichToAverageWind/2, raceCommitteeStore); } @Override diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRegattaImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRegattaImpl.java index a4866c1964f..c32ac4d9ccf 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRegattaImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRegattaImpl.java @@ -2,6 +2,7 @@ package com.sap.sailing.domain.tracking.impl; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.RaceDefinition; +import com.sap.sailing.domain.racecommittee.RaceCommitteeStore; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; import com.sap.sailing.domain.tracking.DynamicTrackedRace; @@ -27,9 +28,9 @@ public class DynamicTrackedRegattaImpl extends TrackedRegattaImpl implements Dyn @Override public DynamicTrackedRace createTrackedRace(RaceDefinition raceDefinition, WindStore windStore, long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed, - DynamicRaceDefinitionSet raceDefinitionSetToUpdate) { + DynamicRaceDefinitionSet raceDefinitionSetToUpdate, RaceCommitteeStore raceCommitteeStore) { DynamicTrackedRace result = (DynamicTrackedRace) super.createTrackedRace(raceDefinition, windStore, delayToLiveInMillis, millisecondsOverWhichToAverageWind, - millisecondsOverWhichToAverageSpeed, raceDefinitionSetToUpdate); + millisecondsOverWhichToAverageSpeed, raceDefinitionSetToUpdate, raceCommitteeStore); addTrackedRace(result); return result; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java index 7951a03f4c6..d89557bf716 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java @@ -256,7 +256,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener { public TrackedRaceImpl(final TrackedRegatta trackedRegatta, RaceDefinition race, final WindStore windStore, long delayToLiveInMillis, final long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed, - long delayForWindEstimationCacheInvalidation/*, final RaceCommitteeStore raceCommitteeStore*/) { + long delayForWindEstimationCacheInvalidation, final RaceCommitteeStore raceCommitteeStore) { super(); locksForMarkPassings = new IdentityHashMap, NamedReentrantReadWriteLock>(); this.serializationLock = new NamedReentrantReadWriteLock("Serialization lock for tracked race "+race.getName(), /* fair */ true); @@ -264,7 +264,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener { this.updateCount = 0; this.race = race; this.windStore = windStore; - //this.raceCommitteeStore = raceCommitteeStore; + this.raceCommitteeStore = raceCommitteeStore; this.windSourcesToExclude = new HashSet(); this.directionFromStartToNextMarkCache = new HashMap>(); this.millisecondsOverWhichToAverageSpeed = millisecondsOverWhichToAverageSpeed; diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaImpl.java index 9a065631c1d..9504bc5a5ff 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaImpl.java @@ -18,6 +18,7 @@ import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.common.NoWindException; import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.racecommittee.RaceCommitteeStore; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.RaceListener; @@ -169,10 +170,10 @@ public class TrackedRegattaImpl implements TrackedRegatta { @Override public DynamicTrackedRace createTrackedRace(RaceDefinition raceDefinition, WindStore windStore, long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed, - DynamicRaceDefinitionSet raceDefinitionSetToUpdate) { + DynamicRaceDefinitionSet raceDefinitionSetToUpdate, RaceCommitteeStore raceCommitteeStore) { logger.log(Level.INFO, "Creating DynamicTrackedRaceImpl for RaceDefinition " + raceDefinition.getName()); DynamicTrackedRaceImpl result = new DynamicTrackedRaceImpl(this, raceDefinition, - windStore, delayToLiveInMillis, millisecondsOverWhichToAverageWind, millisecondsOverWhichToAverageSpeed); + windStore, delayToLiveInMillis, millisecondsOverWhichToAverageWind, millisecondsOverWhichToAverageSpeed, raceCommitteeStore); if (raceDefinitionSetToUpdate != null) { raceDefinitionSetToUpdate.addRaceDefinition(raceDefinition); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index cba4a6ead4c..e1f8556c82b 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -100,6 +100,7 @@ import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; import com.sap.sailing.domain.persistence.DomainObjectFactory; import com.sap.sailing.domain.persistence.MongoFactory; import com.sap.sailing.domain.persistence.MongoObjectFactory; +import com.sap.sailing.domain.persistence.MongoRaceCommitteeStoreFactory; import com.sap.sailing.domain.persistence.MongoWindStoreFactory; import com.sap.sailing.domain.racecommittee.RaceCommitteeEvent; import com.sap.sailing.domain.swisstimingadapter.SwissTimingArchiveConfiguration; @@ -1110,7 +1111,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S new URI(liveURI), new URI(storedURI), new MillisecondsTimePoint(rr.trackingStartTime), new MillisecondsTimePoint(rr.trackingEndTime), MongoWindStoreFactory.INSTANCE.getMongoWindStore(mongoObjectFactory, domainObjectFactory), - RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS, simulateWithStartTimeNow); + RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS, simulateWithStartTimeNow, + MongoRaceCommitteeStoreFactory.INSTANCE.getMongoRaceCommitteeStore(mongoObjectFactory, domainObjectFactory)); if (trackWind) { new Thread("Wind tracking starter for race "+rr.regattaName+"/"+rr.name) { public void run() { @@ -2336,7 +2338,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S final RacesHandle raceHandle = getService().addSwissTimingRace(regattaToAddTo, rr.ID, hostname, port, canSendRequests, MongoWindStoreFactory.INSTANCE.getMongoWindStore(mongoObjectFactory, domainObjectFactory), - RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS); + RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS, + MongoRaceCommitteeStoreFactory.INSTANCE.getMongoRaceCommitteeStore(mongoObjectFactory, domainObjectFactory)); if (trackWind) { new Thread("Wind tracking starter for race "+rr.ID+"/"+rr.description) { public void run() { diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingWindTracksTest.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingWindTracksTest.java index 594c146e870..b2a3c5b498d 100755 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingWindTracksTest.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingWindTracksTest.java @@ -27,6 +27,7 @@ import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.impl.WindSourceImpl; import com.sap.sailing.domain.persistence.impl.DomainObjectFactoryImpl; import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl; +import com.sap.sailing.domain.racecommittee.impl.EmptyRaceCommitteeStore; import com.sap.sailing.domain.test.AbstractTracTracLiveTest; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; @@ -73,7 +74,8 @@ public class TestStoringAndRetrievingWindTracksTest extends AbstractTracTracLive Regatta domainEvent = domainFactory.getOrCreateDefaultRegatta(getTracTracEvent(), /* trackedRegattaRegistry */ null); DynamicTrackedRegatta trackedRegatta = new RacingEventServiceImpl().getOrCreateTrackedRegatta(domainEvent); Iterable typeControllers = domainFactory.getUpdateReceivers(trackedRegatta, getTracTracEvent(), - EmptyWindStore.INSTANCE, /* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */ + EmptyWindStore.INSTANCE, EmptyRaceCommitteeStore.INSTANCE, + /* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */ 0l, /* simulator */ null, new DynamicRaceDefinitionSet() { @Override public void addRaceDefinition(RaceDefinition race) { @@ -86,7 +88,7 @@ public class TestStoringAndRetrievingWindTracksTest extends AbstractTracTracLive @Override public void addRaceDefinition(RaceDefinition race) { } - }); + }, EmptyRaceCommitteeStore.INSTANCE); WindSource windSource = new WindSourceImpl(WindSourceType.WEB); Mongo myFirstMongo = newMongo(); DB firstDatabase = myFirstMongo.getDB(dbConfiguration.getDatabaseName()); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/AdminApp.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/AdminApp.java index d34fe410d8e..7a3b4564d44 100755 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/AdminApp.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/AdminApp.java @@ -38,7 +38,10 @@ import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.impl.Util.Triple; import com.sap.sailing.domain.common.impl.WindSourceImpl; import com.sap.sailing.domain.persistence.MongoFactory; +import com.sap.sailing.domain.persistence.MongoRaceCommitteeStoreFactory; import com.sap.sailing.domain.persistence.MongoWindStoreFactory; +import com.sap.sailing.domain.racecommittee.RaceCommitteeStore; +import com.sap.sailing.domain.racecommittee.impl.EmptyRaceCommitteeStore; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.Wind; @@ -115,9 +118,11 @@ public class AdminApp extends SailingServerHttpServlet { private static final String PARAM_NAME_WINDSTORE = "windstore"; - private static final String WIND_STORE_EMPTY = "empty"; + private static final String STORE_EMPTY = "empty"; - private static final String WIND_STORE_MONGO = "mongo"; + private static final String STORE_MONGO = "mongo"; + + private static final String PARAM_NAME_RACECOMMITTEESTORE = "racecommitteestore"; public AdminApp() { } @@ -466,15 +471,15 @@ public class AdminApp extends SailingServerHttpServlet { URL jsonURL = new URL(req.getParameter(PARAM_NAME_EVENT_JSON_URL)); URI liveURI = new URI(req.getParameter(PARAM_NAME_LIVE_URI)); URI storedURI = new URI(req.getParameter(PARAM_NAME_STORED_URI)); - getService().addRegatta(jsonURL, liveURI, storedURI, getWindStore(req), /* timeoutInMilliseconds */ 60000); + getService().addRegatta(jsonURL, liveURI, storedURI, getWindStore(req), /* timeoutInMilliseconds */ 60000, getRaceCommitteeStore(req)); } private WindStore getWindStore(HttpServletRequest req) throws UnknownHostException, MongoException { String windStore = req.getParameter(PARAM_NAME_WINDSTORE); if (windStore != null) { - if (windStore.equals(WIND_STORE_EMPTY)) { + if (windStore.equals(STORE_EMPTY)) { return EmptyWindStore.INSTANCE; - } else if (windStore.equals(WIND_STORE_MONGO)) { + } else if (windStore.equals(STORE_MONGO)) { return MongoWindStoreFactory.INSTANCE.getMongoWindStore( MongoFactory.INSTANCE.getDefaultMongoObjectFactory(), MongoFactory.INSTANCE.getDefaultDomainObjectFactory()); @@ -490,7 +495,8 @@ public class AdminApp extends SailingServerHttpServlet { URL paramURL = new URL(req.getParameter(PARAM_NAME_PARAM_URL)); URI liveURI = new URI(req.getParameter(PARAM_NAME_LIVE_URI)); URI storedURI = new URI(req.getParameter(PARAM_NAME_STORED_URI)); - getService().addTracTracRace(paramURL, liveURI, storedURI, getWindStore(req), /* timeoutInMilliseconds */ 60000); + getService().addTracTracRace(paramURL, liveURI, storedURI, getWindStore(req), /* timeoutInMilliseconds */ 60000, + getRaceCommitteeStore(req)); } private void stopRace(HttpServletRequest req, HttpServletResponse resp) throws MalformedURLException, IOException, InterruptedException { @@ -506,5 +512,22 @@ public class AdminApp extends SailingServerHttpServlet { resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Regatta not found"); } } + + private RaceCommitteeStore getRaceCommitteeStore(HttpServletRequest req) throws UnknownHostException, MongoException { + String raceCommitteStore = req.getParameter(PARAM_NAME_RACECOMMITTEESTORE); + if (raceCommitteStore != null) { + if (raceCommitteStore.equals(STORE_EMPTY)) { + return EmptyRaceCommitteeStore.INSTANCE; + } else if (raceCommitteStore.equals(STORE_MONGO)) { + return MongoRaceCommitteeStoreFactory.INSTANCE.getMongoRaceCommitteeStore( + MongoFactory.INSTANCE.getDefaultMongoObjectFactory(), + MongoFactory.INSTANCE.getDefaultDomainObjectFactory()); + } else { + log("Couldn't find race committee store " + raceCommitteStore + ". Using EmptyRaceCommitteeStore instead."); + return EmptyRaceCommitteeStore.INSTANCE; + } + } + return EmptyRaceCommitteeStore.INSTANCE; + } } diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java index 38391147e35..c3e28c1bec5 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java @@ -1,174 +1,176 @@ -package com.sap.sailing.server.replication.test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; - -import java.net.URI; -import java.net.URL; -import java.util.Collections; -import java.util.GregorianCalendar; -import java.util.TimeZone; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import com.sap.sailing.domain.base.Fleet; -import com.sap.sailing.domain.base.RaceColumn; -import com.sap.sailing.domain.base.impl.MillisecondsTimePoint; -import com.sap.sailing.domain.common.LeaderboardNameConstants; -import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; -import com.sap.sailing.domain.common.RegattaNameAndRaceName; -import com.sap.sailing.domain.common.WindSourceType; -import com.sap.sailing.domain.common.impl.Util; -import com.sap.sailing.domain.common.impl.WindSourceImpl; -import com.sap.sailing.domain.leaderboard.Leaderboard; -import com.sap.sailing.domain.leaderboard.impl.LowPoint; -import com.sap.sailing.domain.test.AbstractTracTracLiveTest; -import com.sap.sailing.domain.tracking.DynamicTrackedRace; -import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; -import com.sap.sailing.domain.tracking.RacesHandle; -import com.sap.sailing.domain.tracking.TrackedRace; -import com.sap.sailing.domain.tracking.impl.EmptyWindStore; -import com.sap.sailing.server.OperationExecutionListener; -import com.sap.sailing.server.RacingEventServiceOperation; -import com.sap.sailing.server.operationaltransformation.AddColumnToLeaderboard; -import com.sap.sailing.server.operationaltransformation.ConnectTrackedRaceToLeaderboardColumn; -import com.sap.sailing.server.operationaltransformation.CreateFlexibleLeaderboard; -import com.sap.sailing.server.operationaltransformation.CreateTrackedRace; - -public class TrackRaceReplicationTest extends AbstractServerReplicationTest { - private TrackedRace masterTrackedRace; - private RegattaAndRaceIdentifier raceIdentifier; - private RacesHandle racesHandle; - private final boolean[] notifier = new boolean[1]; - private RaceTrackingConnectivityParameters trackingParams; - - @Before - @Override - public void setUp() throws Exception { - super.setUp(); - final String eventID = "event_20110505_SailingTea"; - final String raceID = "bd8c778e-7c65-11e0-8236-406186cbf87c"; - URL paramURL = AbstractTracTracLiveTest.getParamURL(eventID, raceID); - URI liveURI = AbstractTracTracLiveTest.getLiveURI(); - URI storedURI = AbstractTracTracLiveTest.getStoredURI(); - GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); - cal.set(2011, 05, 23, 13, 14, 31); - MillisecondsTimePoint startOfTracking = new MillisecondsTimePoint(cal.getTimeInMillis()); - cal.set(2011, 05, 23, 15, 14, 31); - MillisecondsTimePoint endOfTracking = new MillisecondsTimePoint(cal.getTimeInMillis()); - master.addOperationExecutionListener(new OperationExecutionListener() { - @Override - public void executed(RacingEventServiceOperation operation) { - if (operation instanceof CreateTrackedRace) { - synchronized (notifier) { - notifier[0] = true; - notifier.notifyAll(); - } - } - } - }); - trackingParams = com.sap.sailing.domain.tractracadapter.DomainFactory.INSTANCE.createTrackingConnectivityParameters(paramURL, - liveURI, storedURI, startOfTracking, endOfTracking, /* delayToLiveInMillis */ - 0l, /* simulateWithStartTimeNow */false, EmptyWindStore.INSTANCE); - } - - private void startTracking() throws Exception, InterruptedException { - startTrackingOnMaster(); - waitForTrackRaceReplicationTrigger(); - raceIdentifier = racesHandle.getRaceTracker().getRaceIdentifiers().iterator().next(); - masterTrackedRace = master.getTrackedRace(raceIdentifier); - } - - private void startTrackingOnMaster() throws Exception { - racesHandle = master.addRace(/* regattaToAddTo */ null, trackingParams, EmptyWindStore.INSTANCE, /* timeoutInMilliseconds */ 60000); - } - - private void waitForTrackRaceReplicationTrigger() throws InterruptedException { - while (!notifier[0]) { - synchronized (notifier) { - notifier.wait(); - } - } - } - - @Test - public void testStartTrackingRaceReplication() throws Exception { - startTracking(); - Thread.sleep(1000); - TrackedRace replicaTrackedRace = replica.getTrackedRace(raceIdentifier); - assertNotNull(replicaTrackedRace); - assertNotSame(masterTrackedRace, replicaTrackedRace); - assertNotSame(masterTrackedRace.getRace(), replicaTrackedRace.getRace()); - assertEquals(Util.size(masterTrackedRace.getRace().getCompetitors()), Util.size(replicaTrackedRace.getRace().getCompetitors())); - Leaderboard replicaDefaultLeaderboard = replica.getLeaderboardByName(LeaderboardNameConstants.DEFAULT_LEADERBOARD_NAME); - RaceColumn column = replicaDefaultLeaderboard.getRaceColumnByName(replicaTrackedRace.getRace().getName()); - assertNotNull(column); - assertSame(replicaTrackedRace, column.getTrackedRace(replicaDefaultLeaderboard.getFleet(null))); - } - - @Test - public void testReassignmentToLeaderboardReplication() throws Exception { - final String leaderboardName = "Test Leaderboard"; - Leaderboard masterLeaderboard = master.apply(new CreateFlexibleLeaderboard(leaderboardName, new int[0], new LowPoint())); - final String columnName = "R1"; - RaceColumn masterColumn = master.apply(new AddColumnToLeaderboard(columnName, leaderboardName, /* medalRace */ false)); - final Fleet defaultFleet = masterLeaderboard.getFleet(null); - master.apply(new ConnectTrackedRaceToLeaderboardColumn(leaderboardName, columnName, defaultFleet.getName(), - new RegattaNameAndRaceName("Academy Tracking 2011 (STG)", "weym470may122011"))); - startTracking(); - assertNotNull(masterColumn.getTrackedRace(defaultFleet)); // ensure the re-assignment worked on the master - Thread.sleep(1000); - TrackedRace replicaTrackedRace = replica.getTrackedRace(raceIdentifier); - assertNotNull(replicaTrackedRace); - assertNotSame(masterTrackedRace, replicaTrackedRace); - assertNotSame(masterTrackedRace.getRace(), replicaTrackedRace.getRace()); - assertEquals(Util.size(masterTrackedRace.getRace().getCompetitors()), Util.size(replicaTrackedRace.getRace().getCompetitors())); - Leaderboard replicaLeaderboard = replica.getLeaderboardByName(leaderboardName); - assertNotNull(replicaLeaderboard); - RaceColumn column = replicaLeaderboard.getRaceColumnByName(columnName); - assertNotNull(column); - assertSame(replicaTrackedRace, column.getTrackedRace(defaultFleet)); - } - - @Test - public void testRaceTimeReplication() throws InterruptedException, Exception { - startTracking(); - Thread.sleep(1000); - TrackedRace replicaTrackedRace = replica.getTrackedRace(raceIdentifier); - assertEquals(masterTrackedRace.getStartOfTracking(), replicaTrackedRace.getStartOfTracking()); - assertEquals(masterTrackedRace.getEndOfTracking(), replicaTrackedRace.getEndOfTracking()); - MillisecondsTimePoint now = MillisecondsTimePoint.now(); - assertFalse(now.equals(replicaTrackedRace.getStartOfRace())); - ((DynamicTrackedRace) masterTrackedRace).setStartTimeReceived(now); - Thread.sleep(3000); - assertEquals(now, replicaTrackedRace.getStartOfRace()); - } - - @Test - public void testWindSourcesToExcludeReplication() throws InterruptedException, Exception { - startTracking(); - Thread.sleep(1000); - TrackedRace replicaTrackedRace = replica.getTrackedRace(raceIdentifier); - assertTrue(Util.isEmpty(masterTrackedRace.getWindSourcesToExclude())); - assertTrue(Util.isEmpty(replicaTrackedRace.getWindSourcesToExclude())); - masterTrackedRace.setWindSourcesToExclude(Collections.singleton(new WindSourceImpl(WindSourceType.WEB))); - assertEquals(1, Util.size(masterTrackedRace.getWindSourcesToExclude())); - assertEquals(WindSourceType.WEB, masterTrackedRace.getWindSourcesToExclude().iterator().next().getType()); - Thread.sleep(1000); - assertEquals(1, Util.size(replicaTrackedRace.getWindSourcesToExclude())); - assertEquals(WindSourceType.WEB, replicaTrackedRace.getWindSourcesToExclude().iterator().next().getType()); - } - - @After - @Override - public void tearDown() throws Exception { - racesHandle.getRaceTracker().stop(); - super.tearDown(); - } -} +package com.sap.sailing.server.replication.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import java.net.URI; +import java.net.URL; +import java.util.Collections; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.sap.sailing.domain.base.Fleet; +import com.sap.sailing.domain.base.RaceColumn; +import com.sap.sailing.domain.base.impl.MillisecondsTimePoint; +import com.sap.sailing.domain.common.LeaderboardNameConstants; +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.domain.common.RegattaNameAndRaceName; +import com.sap.sailing.domain.common.WindSourceType; +import com.sap.sailing.domain.common.impl.Util; +import com.sap.sailing.domain.common.impl.WindSourceImpl; +import com.sap.sailing.domain.leaderboard.Leaderboard; +import com.sap.sailing.domain.leaderboard.impl.LowPoint; +import com.sap.sailing.domain.racecommittee.impl.EmptyRaceCommitteeStore; +import com.sap.sailing.domain.test.AbstractTracTracLiveTest; +import com.sap.sailing.domain.tracking.DynamicTrackedRace; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; +import com.sap.sailing.domain.tracking.RacesHandle; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.domain.tracking.impl.EmptyWindStore; +import com.sap.sailing.server.OperationExecutionListener; +import com.sap.sailing.server.RacingEventServiceOperation; +import com.sap.sailing.server.operationaltransformation.AddColumnToLeaderboard; +import com.sap.sailing.server.operationaltransformation.ConnectTrackedRaceToLeaderboardColumn; +import com.sap.sailing.server.operationaltransformation.CreateFlexibleLeaderboard; +import com.sap.sailing.server.operationaltransformation.CreateTrackedRace; + +public class TrackRaceReplicationTest extends AbstractServerReplicationTest { + private TrackedRace masterTrackedRace; + private RegattaAndRaceIdentifier raceIdentifier; + private RacesHandle racesHandle; + private final boolean[] notifier = new boolean[1]; + private RaceTrackingConnectivityParameters trackingParams; + + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + final String eventID = "event_20110505_SailingTea"; + final String raceID = "bd8c778e-7c65-11e0-8236-406186cbf87c"; + URL paramURL = AbstractTracTracLiveTest.getParamURL(eventID, raceID); + URI liveURI = AbstractTracTracLiveTest.getLiveURI(); + URI storedURI = AbstractTracTracLiveTest.getStoredURI(); + GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); + cal.set(2011, 05, 23, 13, 14, 31); + MillisecondsTimePoint startOfTracking = new MillisecondsTimePoint(cal.getTimeInMillis()); + cal.set(2011, 05, 23, 15, 14, 31); + MillisecondsTimePoint endOfTracking = new MillisecondsTimePoint(cal.getTimeInMillis()); + master.addOperationExecutionListener(new OperationExecutionListener() { + @Override + public void executed(RacingEventServiceOperation operation) { + if (operation instanceof CreateTrackedRace) { + synchronized (notifier) { + notifier[0] = true; + notifier.notifyAll(); + } + } + } + }); + trackingParams = com.sap.sailing.domain.tractracadapter.DomainFactory.INSTANCE.createTrackingConnectivityParameters(paramURL, + liveURI, storedURI, startOfTracking, endOfTracking, /* delayToLiveInMillis */ + 0l, /* simulateWithStartTimeNow */false, EmptyWindStore.INSTANCE, EmptyRaceCommitteeStore.INSTANCE); + } + + private void startTracking() throws Exception, InterruptedException { + startTrackingOnMaster(); + waitForTrackRaceReplicationTrigger(); + raceIdentifier = racesHandle.getRaceTracker().getRaceIdentifiers().iterator().next(); + masterTrackedRace = master.getTrackedRace(raceIdentifier); + } + + private void startTrackingOnMaster() throws Exception { + racesHandle = master.addRace(/* regattaToAddTo */ null, trackingParams, EmptyWindStore.INSTANCE, /* timeoutInMilliseconds */ 60000, + EmptyRaceCommitteeStore.INSTANCE); + } + + private void waitForTrackRaceReplicationTrigger() throws InterruptedException { + while (!notifier[0]) { + synchronized (notifier) { + notifier.wait(); + } + } + } + + @Test + public void testStartTrackingRaceReplication() throws Exception { + startTracking(); + Thread.sleep(1000); + TrackedRace replicaTrackedRace = replica.getTrackedRace(raceIdentifier); + assertNotNull(replicaTrackedRace); + assertNotSame(masterTrackedRace, replicaTrackedRace); + assertNotSame(masterTrackedRace.getRace(), replicaTrackedRace.getRace()); + assertEquals(Util.size(masterTrackedRace.getRace().getCompetitors()), Util.size(replicaTrackedRace.getRace().getCompetitors())); + Leaderboard replicaDefaultLeaderboard = replica.getLeaderboardByName(LeaderboardNameConstants.DEFAULT_LEADERBOARD_NAME); + RaceColumn column = replicaDefaultLeaderboard.getRaceColumnByName(replicaTrackedRace.getRace().getName()); + assertNotNull(column); + assertSame(replicaTrackedRace, column.getTrackedRace(replicaDefaultLeaderboard.getFleet(null))); + } + + @Test + public void testReassignmentToLeaderboardReplication() throws Exception { + final String leaderboardName = "Test Leaderboard"; + Leaderboard masterLeaderboard = master.apply(new CreateFlexibleLeaderboard(leaderboardName, new int[0], new LowPoint())); + final String columnName = "R1"; + RaceColumn masterColumn = master.apply(new AddColumnToLeaderboard(columnName, leaderboardName, /* medalRace */ false)); + final Fleet defaultFleet = masterLeaderboard.getFleet(null); + master.apply(new ConnectTrackedRaceToLeaderboardColumn(leaderboardName, columnName, defaultFleet.getName(), + new RegattaNameAndRaceName("Academy Tracking 2011 (STG)", "weym470may122011"))); + startTracking(); + assertNotNull(masterColumn.getTrackedRace(defaultFleet)); // ensure the re-assignment worked on the master + Thread.sleep(1000); + TrackedRace replicaTrackedRace = replica.getTrackedRace(raceIdentifier); + assertNotNull(replicaTrackedRace); + assertNotSame(masterTrackedRace, replicaTrackedRace); + assertNotSame(masterTrackedRace.getRace(), replicaTrackedRace.getRace()); + assertEquals(Util.size(masterTrackedRace.getRace().getCompetitors()), Util.size(replicaTrackedRace.getRace().getCompetitors())); + Leaderboard replicaLeaderboard = replica.getLeaderboardByName(leaderboardName); + assertNotNull(replicaLeaderboard); + RaceColumn column = replicaLeaderboard.getRaceColumnByName(columnName); + assertNotNull(column); + assertSame(replicaTrackedRace, column.getTrackedRace(defaultFleet)); + } + + @Test + public void testRaceTimeReplication() throws InterruptedException, Exception { + startTracking(); + Thread.sleep(1000); + TrackedRace replicaTrackedRace = replica.getTrackedRace(raceIdentifier); + assertEquals(masterTrackedRace.getStartOfTracking(), replicaTrackedRace.getStartOfTracking()); + assertEquals(masterTrackedRace.getEndOfTracking(), replicaTrackedRace.getEndOfTracking()); + MillisecondsTimePoint now = MillisecondsTimePoint.now(); + assertFalse(now.equals(replicaTrackedRace.getStartOfRace())); + ((DynamicTrackedRace) masterTrackedRace).setStartTimeReceived(now); + Thread.sleep(3000); + assertEquals(now, replicaTrackedRace.getStartOfRace()); + } + + @Test + public void testWindSourcesToExcludeReplication() throws InterruptedException, Exception { + startTracking(); + Thread.sleep(1000); + TrackedRace replicaTrackedRace = replica.getTrackedRace(raceIdentifier); + assertTrue(Util.isEmpty(masterTrackedRace.getWindSourcesToExclude())); + assertTrue(Util.isEmpty(replicaTrackedRace.getWindSourcesToExclude())); + masterTrackedRace.setWindSourcesToExclude(Collections.singleton(new WindSourceImpl(WindSourceType.WEB))); + assertEquals(1, Util.size(masterTrackedRace.getWindSourcesToExclude())); + assertEquals(WindSourceType.WEB, masterTrackedRace.getWindSourcesToExclude().iterator().next().getType()); + Thread.sleep(1000); + assertEquals(1, Util.size(replicaTrackedRace.getWindSourcesToExclude())); + assertEquals(WindSourceType.WEB, replicaTrackedRace.getWindSourcesToExclude().iterator().next().getType()); + } + + @After + @Override + public void tearDown() throws Exception { + racesHandle.getRaceTracker().stop(); + super.tearDown(); + } +} diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackedRaceContentsReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackedRaceContentsReplicationTest.java index ae7b09e4bd3..3ffeae0914b 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackedRaceContentsReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackedRaceContentsReplicationTest.java @@ -37,6 +37,7 @@ import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.impl.Util; import com.sap.sailing.domain.common.impl.WindSourceImpl; import com.sap.sailing.domain.persistence.MongoFactory; +import com.sap.sailing.domain.persistence.MongoRaceCommitteeStoreFactory; import com.sap.sailing.domain.persistence.MongoWindStore; import com.sap.sailing.domain.persistence.MongoWindStoreFactory; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; @@ -89,7 +90,9 @@ public class TrackedRaceContentsReplicationTest extends AbstractServerReplicatio trackedRace = (DynamicTrackedRace) master.apply(new CreateTrackedRace(raceIdentifier, MongoWindStoreFactory.INSTANCE.getMongoWindStore(MongoFactory.INSTANCE.getDefaultMongoObjectFactory(), MongoFactory.INSTANCE.getDefaultDomainObjectFactory()), /* delayToLiveInMillis */ 5000, - /* millisecondsOverWhichToAverageWind */ 10000, /* millisecondsOverWhichToAverageSpeed */10000)); + /* millisecondsOverWhichToAverageWind */ 10000, /* millisecondsOverWhichToAverageSpeed */10000, + MongoRaceCommitteeStoreFactory.INSTANCE.getMongoRaceCommitteeStore(MongoFactory.INSTANCE.getDefaultMongoObjectFactory(), + MongoFactory.INSTANCE.getDefaultDomainObjectFactory()))); trackedRace.waitUntilWindLoadingComplete(); } diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java index 0c433b404c1..b88e3586c6d 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java @@ -28,6 +28,7 @@ import com.sap.sailing.domain.base.impl.CourseImpl; import com.sap.sailing.domain.base.impl.RaceDefinitionImpl; import com.sap.sailing.domain.base.impl.RegattaImpl; import com.sap.sailing.domain.common.ScoringSchemeType; +import com.sap.sailing.domain.racecommittee.impl.EmptyRaceCommitteeStore; import com.sap.sailing.domain.tracking.RaceTracker; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRegatta; @@ -70,13 +71,13 @@ public class RaceTrackerStartStopTest { raceDef3 = new RaceDefinitionImpl(RACENAME3, new CourseImpl("Course3", new ArrayList()), boatClass, new ArrayList()); regatta.addRace(raceDef1); trackedRegatta1.createTrackedRace(raceDef1, /* windStore */ EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 0l, /* millisecondsOverWhichToAverageWind */ 0l, - /* millisecondsOverWhichToAverageSpeed */ 0l, /* raceDefinitionSetToUpdate */ null); + /* millisecondsOverWhichToAverageSpeed */ 0l, /* raceDefinitionSetToUpdate */ null, EmptyRaceCommitteeStore.INSTANCE); regatta.addRace(raceDef2); trackedRegatta1.createTrackedRace(raceDef2, /* windStore */ EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 0l, /* millisecondsOverWhichToAverageWind */ 0l, - /* millisecondsOverWhichToAverageSpeed */ 0l, /* raceDefinitionSetToUpdate */ null); + /* millisecondsOverWhichToAverageSpeed */ 0l, /* raceDefinitionSetToUpdate */ null, EmptyRaceCommitteeStore.INSTANCE); regatta.addRace(raceDef3); trackedRegatta1.createTrackedRace(raceDef3, /* windStore */ EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 0l, /* millisecondsOverWhichToAverageWind */ 0l, - /* millisecondsOverWhichToAverageSpeed */ 0l, /* raceDefinitionSetToUpdate */ null); + /* millisecondsOverWhichToAverageSpeed */ 0l, /* raceDefinitionSetToUpdate */ null, EmptyRaceCommitteeStore.INSTANCE); Set raceDefinitionSetRace1 = new HashSet(); raceDefinitionSetRace1.add(raceDef1); Set raceDefinitionSetRace2 = new HashSet(); diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerTest.java index f4885841fa1..f23c18d12bb 100755 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerTest.java @@ -20,6 +20,7 @@ import org.junit.Test; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.common.impl.Util; +import com.sap.sailing.domain.racecommittee.impl.EmptyRaceCommitteeStore; import com.sap.sailing.domain.tracking.RaceListener; import com.sap.sailing.domain.tracking.RacesHandle; import com.sap.sailing.domain.tracking.TrackedRace; @@ -60,7 +61,8 @@ public class RaceTrackerTest { public void setUp() throws Exception { service = new RacingEventServiceImpl(); logger.info("Calling service.addTracTracRace"); - raceHandle = service.addTracTracRace(paramUrl, liveUri, storedUri, EmptyWindStore.INSTANCE, /* timeoutInMilliseconds */ 60000); + raceHandle = service.addTracTracRace(paramUrl, liveUri, storedUri, EmptyWindStore.INSTANCE, /* timeoutInMilliseconds */ 60000, + EmptyRaceCommitteeStore.INSTANCE); logger.info("Calling raceHandle.getRaces()"); Set races = raceHandle.getRaces(); // wait for RaceDefinition to be completely wired in Regatta logger.info("Obtained races: "+races); @@ -116,7 +118,8 @@ public class RaceTrackerTest { RaceDefinition oldRaceDefinition = oldTrackedRace.getRace(); assertTrue(!Util.isEmpty(raceHandle.getRegatta().getAllRaces())); service.removeRegatta(raceHandle.getRegatta()); - RacesHandle myRaceHandle = service.addTracTracRace(paramUrl, liveUri, storedUri, EmptyWindStore.INSTANCE, /* timeoutInMilliseconds */ 60000); + RacesHandle myRaceHandle = service.addTracTracRace(paramUrl, liveUri, storedUri, EmptyWindStore.INSTANCE, /* timeoutInMilliseconds */ 60000, + EmptyRaceCommitteeStore.INSTANCE); TrackedRegatta newTrackedRegatta = myRaceHandle.getTrackedRegatta(); assertNotSame(oldTrackedRegatta, newTrackedRegatta); TrackedRace newTrackedRace = getTrackedRace(newTrackedRegatta); @@ -140,7 +143,8 @@ public class RaceTrackerTest { logger.entering(getClass().getName(), "testTrackingSameRaceWithoutStopping"); TrackedRegatta oldTrackedRegatta = raceHandle.getTrackedRegatta(); TrackedRace oldTrackedRace = getTrackedRace(oldTrackedRegatta); - RacesHandle myRaceHandle = service.addTracTracRace(paramUrl, liveUri, storedUri, EmptyWindStore.INSTANCE, /* timeoutInMilliseconds */ 60000); + RacesHandle myRaceHandle = service.addTracTracRace(paramUrl, liveUri, storedUri, EmptyWindStore.INSTANCE, /* timeoutInMilliseconds */ 60000, + EmptyRaceCommitteeStore.INSTANCE); TrackedRegatta newTrackedEvent = myRaceHandle.getTrackedRegatta(); TrackedRace newTrackedRace = getTrackedRace(newTrackedEvent); // expecting a new tracked race to be created when starting over with tracking diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java index 3168b05dc55..e016bfcff3b 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java @@ -1,481 +1,487 @@ -package com.sap.sailing.server; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.net.MalformedURLException; -import java.net.SocketException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.UnknownHostException; -import java.text.ParseException; -import java.util.ConcurrentModificationException; -import java.util.List; -import java.util.Map; - -import com.sap.sailing.domain.base.Event; -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; -import com.sap.sailing.domain.base.RegattaRegistry; -import com.sap.sailing.domain.base.Series; -import com.sap.sailing.domain.common.RaceFetcher; -import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; -import com.sap.sailing.domain.common.RegattaFetcher; -import com.sap.sailing.domain.common.RegattaIdentifier; -import com.sap.sailing.domain.common.RegattaName; -import com.sap.sailing.domain.common.ScoringSchemeType; -import com.sap.sailing.domain.common.TimePoint; -import com.sap.sailing.domain.common.impl.Util.Pair; -import com.sap.sailing.domain.common.impl.Util.Triple; -import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; -import com.sap.sailing.domain.leaderboard.Leaderboard; -import com.sap.sailing.domain.leaderboard.LeaderboardGroup; -import com.sap.sailing.domain.leaderboard.LeaderboardRegistry; -import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; -import com.sap.sailing.domain.leaderboard.ScoringScheme; -import com.sap.sailing.domain.swisstimingadapter.SwissTimingFactory; -import com.sap.sailing.domain.tracking.RaceListener; -import com.sap.sailing.domain.tracking.RaceTracker; -import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; -import com.sap.sailing.domain.tracking.RacesHandle; -import com.sap.sailing.domain.tracking.TrackedRace; -import com.sap.sailing.domain.tracking.TrackedRegatta; -import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; -import com.sap.sailing.domain.tracking.WindStore; -import com.sap.sailing.domain.tracking.impl.EmptyWindStore; -import com.sap.sailing.domain.tractracadapter.DomainFactory; -import com.sap.sailing.domain.tractracadapter.RaceRecord; -import com.sap.sailing.domain.tractracadapter.TracTracRaceTracker; -import com.sap.sailing.expeditionconnector.ExpeditionListener; - -/** - * An OSGi service that can be used to track boat races using a TracTrac connector that pushes - * live GPS boat location, waypoint, coarse and mark passing data.

- * - * If a race/regatta is already being tracked, another {@link #addTracTracRace(URL, URI, URI, WindStore, long)} or - * {@link #addRegatta(URL, URI, URI, WindStore, long)} call will have no effect, even if a different - * {@link WindStore} is requested.

- * - * When the tracking of a race/regatta is {@link #stopTracking(Regatta, RaceDefinition) stopped}, the next - * time it's started to be tracked, a new {@link TrackedRace} at least will be constructed. This also - * means that when a {@link TrackedRegatta} exists that still holds other {@link TrackedRace}s, the - * no longer tracked {@link TrackedRace} will be removed from the {@link TrackedRegatta}. - * corresponding information is removed also from the {@link DomainFactory}'s caches to ensure that - * clean, fresh data is received should another tracking request be issued later. - * - * @author Axel Uhl (d043530) - * - */ -public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetcher, RegattaRegistry, RaceFetcher, LeaderboardRegistry { - @Override - Regatta getRegatta(RegattaName regattaName); - - @Override - RaceDefinition getRace(RegattaAndRaceIdentifier raceIdentifier); - - TrackedRace getTrackedRace(Regatta regatta, RaceDefinition race); - - TrackedRace getTrackedRace(RegattaAndRaceIdentifier raceIdentifier); - - /** - * Obtains an unmodifiable map of the leaderboard configured in this service keyed by their names. - */ - Map getLeaderboards(); - - /** - * @return a leaderboard whose {@link Leaderboard#getName()} method returns the value of the name - * parameter, or null if no such leaderboard is known to this service - */ - Leaderboard getLeaderboardByName(String name); - - /** - * Defines the regatta and for each race listed in the JSON document that is not already being tracked by this service - * creates a {@link TracTracRaceTracker} that starts tracking the respective race. The {@link RaceDefinition}s obtained this - * way are all grouped into the single {@link Regatta} produced for the event listed in the JSON response. Note that - * the many race trackers will have their TracTrac Event each, all with the same name, meaning the same - * event but being distinct. - * - * @param jsonURL - * URL of a JSON response that contains an "event" object telling the event's name and ID, as well as a - * JSON array named "races" which tells ID and replay URL for the race. From those replay URLs the - * paramURL for the Java client can be derived. - * @param timeoutInMilliseconds - * if a race definition is not received for a race of this event within this time, the race tracker for - * that race is stopped; use -1 to wait forever - */ - Regatta addRegatta(URL jsonURL, URI liveURI, URI storedURI, WindStore windStore, long timeoutInMilliseconds) - throws MalformedURLException, FileNotFoundException, URISyntaxException, IOException, ParseException, - org.json.simple.parser.ParseException, Exception; - - /** - * If not already tracking the URL/URI/URI combination, adds a single race tracker and starts tracking the race, - * using the race's parameter URL which delivers the single configuration text file for that race. While the result - * of passing this URL to the TracTrac KeyValue.setup is a TracTrac Event, those events - * only manage a single race. In our domain model, we group those races into a single instance of our {@link Regatta} - * class. - *

- * - * If this is the first race of an event, the {@link Regatta} is created as well. If the {@link RaceDefinition} for - * the race already exists, it isn't created again. Also, if a {@link TracTracRaceTracker} for the given race already - * exists, it is not added again. - *

- * - * Note that when the race identified by paramURL, liveURI and storedURI is - * already being tracked, then regardless of the windStore selection the existing tracker will be used - * and its race handle will be returned. A log message will indicate a potential wind store mismatch (based on - * {@link WindStore#equals(Object)}). - * - * @param timeoutInMilliseconds - * if the race definition is not received for the race within this time, the race tracker for - * that race is stopped; use -1 to wait forever - */ - RacesHandle addTracTracRace(URL paramURL, URI liveURI, URI storedURI, WindStore windStore, long timeoutInMilliseconds) - throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception; - - /** - * Same as {@link #addTracTracRace(URL, URI, URI, WindStore, long)}, only that start and end of tracking are - * specified which may help reducing the amount of stored data (particularly mark positions) that needs to be - * loaded. - * - * @param regattaToAddTo - * if null, an existing regatta by the name of the TracTrac event with the boat class name - * appended in parentheses will be looked up; if not found, a default regatta with that name will be - * created, with a single default series and a single default fleet. If a valid {@link RegattaIdentifier} - * is specified, a regatta lookup is performed with that identifier; if the regatta is found, it is used - * to add the races to. Otherwise, a default regatta as described above will be created and used. - */ - RacesHandle addTracTracRace(RegattaIdentifier regattaToAddTo, URL paramURL, URI liveURI, URI storedURI, - TimePoint trackingStartTime, TimePoint trackingEndTime, WindStore windStore, - long timeoutForReceivingRaceDefinitionInMilliseconds, boolean simulateWithStartTimeNow) - throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception; - - /** - * Stops tracking all races of the regatta specified. This will also stop tracking wind for all races of this regatta. - * See {@link #stopTrackingWind(Regatta, RaceDefinition)}. If there were multiple calls to - * {@link #addTracTracRace(URL, URI, URI, WindStore, long)} with an equal combination of URLs/URIs, the {@link TracTracRaceTracker} - * already tracking the race was re-used. The trackers will be stopped by this call regardless of how many calls - * were made that ensured they were tracking. - */ - void stopTracking(Regatta regatta) throws MalformedURLException, IOException, InterruptedException; - - /** - * Removes race 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 regatta is removed as well and will no - * longer be returned by {@link #getAllRegattas()}. The wind tracking is stopped for race. - *

- * - * Any {@link RaceTracker} for which race is the last race tracked that is still reachable - * from {@link #getAllRegattas()} will be {@link RaceTracker#stop() stopped}. - * - * The race will be also removed from all leaderboards containing a column that has race'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; - - /** - * Stops all {@link RaceTracker}s currently tracking race. Note that if the same tracker also may have - * been tracking other races. Other races of the same event that are currently tracked will continue to be tracked. - * If wind tracking for the race is currently running, it will be stopped (see also - * {@link #stopTrackingWind(Regatta, RaceDefinition)}). The race (and the other races tracked by the - * same tracker) as well as the corresponding {@link TrackedRace}s will continue to exist, e.g., when asking - * {@link #getTrackedRace(Regatta, RaceDefinition)}. - */ - void stopTracking(Regatta regatta, RaceDefinition race) throws MalformedURLException, IOException, InterruptedException; - - /** - * @param port - * the UDP port on which to listen for incoming messages from Expedition clients - * @param correctByDeclination - * An optional service to convert the wind bearings (which the receiver may - * believe to be true bearings) from magnetic to true bearings. - * @throws SocketException - * thrown, e.g., in case there is already another listener on the port requested - */ - void startTrackingWind(Regatta regatta, RaceDefinition race, boolean correctByDeclination) throws SocketException; - - void stopTrackingWind(Regatta regatta, RaceDefinition race) throws SocketException, IOException; - - /** - * The {@link Triple#getC() third component} of the triples returned is a wind tracker-specific - * comment where a wind tracker may provide information such as its type name or, if applicable, - * connectivity information such as the network port on which it receives wind information. - */ - Iterable> getWindTrackedRaces(); - - /** - * For the JSON URL of an account / event, lists the paramURLs that can be used for {@link #addTracTracRace(URL, URI, URI, WindStore, long)} - * calls to individually start tracking races of this event, rather than tracking all races in the event which - * is hardly ever useful. The returned pair's first component is the event name. - */ - Pair> getTracTracRaceRecords(URL jsonURL) throws IOException, ParseException, - org.json.simple.parser.ParseException, URISyntaxException; - - List getSwissTimingRaceRecords(String hostname, int port, boolean canSendRequests) - throws InterruptedException, UnknownHostException, IOException, ParseException; - - boolean isRaceBeingTracked(RaceDefinition r); - - /** - * Creates a new leaderboard with the name specified. - * @param discardThresholds - * Tells the thresholds from which on a next higher number of worst races will be discarded per - * competitor. Example: [3, 6] means that starting from three races the single worst race - * will be discarded; starting from six races, the two worst races per competitor are discarded. - * - * @return the leaderboard created - */ - FlexibleLeaderboard addFlexibleLeaderboard(String name, int[] discardThresholds, ScoringScheme scoringScheme); - - RegattaLeaderboard addRegattaLeaderboard(RegattaIdentifier regattaIdentifier, int[] discardThresholds); - - void removeLeaderboard(String leaderboardName); - - /** - * Renames a leaderboard. If a leaderboard by the name oldName does not exist in {@link #getLeaderboards()}, - * or if a leaderboard with the name newName already exists, an {@link IllegalArgumentException} is thrown. - * If the method completes normally, the rename has been successful, and the leaderboard previously obtained by calling - * {@link #getLeaderboardByName(String) getLeaderboardByName(oldName)} can now be obtained by calling - * {@link #getLeaderboardByName(String) getLeaderboardByName(newName)}. - */ - void renameLeaderboard(String oldName, String newName); - - RaceColumn addColumnToLeaderboard(String columnName, String leaderboardName, boolean medalRace); - - void moveLeaderboardColumnUp(String leaderboardName, String columnName); - - void moveLeaderboardColumnDown(String leaderboardName, String columnName); - - void removeLeaderboardColumn(String leaderboardName, String columnName); - - void renameLeaderboardColumn(String leaderboardName, String oldColumnName, String newColumnName); - - /** - * @see RaceColumn#setFactor(Double) - */ - void updateLeaderboardColumnFactor(String leaderboardName, String columnName, Double factor); - - /** - * Updates the leaderboard data in the persistent store - */ - void updateStoredLeaderboard(Leaderboard leaderboard); - - void updateStoredRegatta(Regatta regatta); - - long getDelayToLiveInMillis(); - - void setDelayToLiveInMillis(long delayToLiveInMillis); - - /** - * @param regattaToAddTo - * if null, an existing regatta by the name of the TracTrac event with the boat class name - * appended in parentheses will be looked up; if not found, a default regatta with that name will be - * created, with a single default series and a single default fleet. If a valid {@link RegattaIdentifier} - * is specified, a regatta lookup is performed with that identifier; if the regatta is found, it is used - * to add the races to. Otherwise, a default regatta as described above will be created and used. - */ - RacesHandle addSwissTimingRace(RegattaIdentifier regattaToAddTo, String raceID, String hostname, int port, - boolean canSendRequests, WindStore windStore, long timeoutInMilliseconds) throws InterruptedException, UnknownHostException, - IOException, ParseException, Exception; - - SwissTimingFactory getSwissTimingFactory(); - - void storeSwissTimingDummyRace(String racMessage, String stlMesssage, String ccgMessage) throws IllegalArgumentException; - - void stopTrackingAndRemove(Regatta regatta) throws MalformedURLException, IOException, InterruptedException; - - void removeRegatta(Regatta regatta) throws MalformedURLException, IOException, InterruptedException; - - TrackedRace getExistingTrackedRace(RegattaAndRaceIdentifier raceIdentifier); - - /** - * Obtains an unmodifiable map of the leaderboard groups configured in this service keyed by their names. - */ - Map getLeaderboardGroups(); - - /** - * @param groupName The name of the requested leaderboard group - * @return The leaderboard group with the name groupName, or null if theres no such group - */ - LeaderboardGroup getLeaderboardGroupByName(String groupName); - - /** - * Creates a new group with the name groupName, the description desciption and the - * leaderboards with the names in leaderboardNames and saves it in the database. - * - * @param groupName - * The name of the new group - * @param description - * The description of the new group - * @param displayGroupsInReverseOrder TODO - * @param leaderboardNames - * The names of the leaderboards, which should be contained by the new group.
- * If there isn't a leaderboard with one of these names an {@link IllegalArgumentException} is thrown. - * @return The new leaderboard group - */ - LeaderboardGroup addLeaderboardGroup(String groupName, String description, boolean displayGroupsInReverseOrder, - List leaderboardNames, int[] overallLeaderboardDiscardThresholds, ScoringSchemeType overallLeaderboardScoringSchemeType); - - /** - * Removes the group with the name groupName from the service and the database. - * @param groupName The name of the group which shall be removed. - */ - void removeLeaderboardGroup(String groupName); - - /** - * Renames the group with the name oldName to the newName.
- * If there's no group with the name oldName or there's already a group with the name - * newName a {@link IllegalArgumentException} is thrown. - * - * @param oldName The old name of the group - * @param newName The new name of the group - */ - void renameLeaderboardGroup(String oldName, String newName); - - /** - * Updates the group data in the persistant store. - */ - void updateStoredLeaderboardGroup(LeaderboardGroup leaderboardGroup); - - void addExpeditionListener(ExpeditionListener listener, boolean validMessagesOnly) throws SocketException; - - void removeExpeditionListener(ExpeditionListener listener); - - /** - * @param regattaToAddTo - * if null or no regatta by that identifier is found, an existing regatta by the name of the - * TracTrac event with the boat class name appended in parentheses will be looked up; if not found, a - * default regatta with that name will be created, with a single default series and a single default - * fleet. If a valid {@link RegattaIdentifier} is specified, a regatta lookup is performed with that - * identifier; if the regatta is found, it is used to add the races to, and - * {@link #setRegattaForRace(Regatta, RaceDefinition)} is called to remember the association - * persistently. Otherwise, a default regatta as described above will be created and used. - * @param windStore - * must not be null, but can, e.g., be an {@link EmptyWindStore} - */ - RacesHandle addRace(RegattaIdentifier regattaToAddTo, RaceTrackingConnectivityParameters params, WindStore windStore, long timeoutInMilliseconds) - throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception; - - TrackedRace createTrackedRace(RegattaAndRaceIdentifier raceIdentifier, WindStore windStore, - long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed); - - Regatta getOrCreateRegatta(String regattaName, String boatClassName); - - Regatta createRegatta(String baseName, String boatClassName, Iterable series, boolean persistent, ScoringScheme scoringScheme); - - /** - * Adds raceDefinition to the {@link Regatta} such that it will appear in {@link Regatta#getAllRaces()} - * and {@link Regatta#getRaceByName(String)}. - * - * @param addToRegatta identifier of an regatta that must exist already - */ - void addRace(RegattaIdentifier addToRegatta, RaceDefinition raceDefinition); - - void updateLeaderboardGroup(String oldName, String newName, String description, List leaderboardNames, - int[] overallLeaderboardDiscardThresholds, ScoringSchemeType overallLeaderboardScoringSchemeType); - - /** - * Executes an operation whose effects need to be replicated to any replica of this service known and - * {@link OperationExecutionListener#executed(RacingEventServiceOperation) notifies} all registered - * operation execution listeners about the execution of the operation. - */ - T apply(RacingEventServiceOperation operation); - - void addOperationExecutionListener(OperationExecutionListener listener); - - void removeOperationExecutionListener(OperationExecutionListener listener); - - /** - * Produces a one-shot serializable copy of those elements required for replication into oos so that - * afterwards the {@link RacingEventServiceOperation}s can be {@link #apply(RacingEventServiceOperation) applied} to - * maintain consistency with the master copy of the service. The dual operation is {@link #initiallyFillFrom}. - */ - void serializeForInitialReplication(ObjectOutputStream oos) throws IOException; - - /** - * Dual, reading operation for {@link #serializeForInitialReplication(ObjectOutputStream)}. In other words, when - * this operation returns, this service instance is in a state "equivalent" to that of the service instance that - * produced the stream contents in its {@link #serializeForInitialReplication(ObjectOutputStream)}. "Equivalent" - * here means that a replica will have equal sets of regattas, tracked regattas, leaderboards and leaderboard groups but - * will not have any active trackers for wind or positions because it relies on these elements to be sent through - * the replication channel. - *

- * - * Tracked regattas read from the stream are observed (see {@link RaceListener}) by this object for automatic updates - * to the default leaderboard and for automatic linking to leaderboard columns. It is assumed that no explicit - * replication of these operations will happen based on the changes performed on the replication master.

- * - * Caution: All relevant contents of this service instance will be replaced by the stream contents. - */ - void initiallyFillFrom(ObjectInputStream ois) throws IOException, ClassNotFoundException; - - Event getEventByName(String name); - - /** - * @return a thread-safe copy of the events currently known by the service; it's safe for callers to iterate over - * the iterable returned, and no risk of a {@link ConcurrentModificationException} exists - */ - Iterable getAllEvents(); - - /** - * Creates a new event with the name eventName, the venuevenue and the - * regattas with the names in regattaNames and saves it in the database. - * - * @param eventName - * The name of the new event - * @param venue - * The name of the venue of the new event - * @param publicationUrl - * The publication URL of the new event - * @param isPublic - * Indicates whether the event is public accessible via the publication URL or not - * @param regattaNames - * The names of the regattas contained in the new event.
- * @return The new event - */ - Event addEvent(String eventName, String venueName, String publicationUrl, boolean isPublic, List regattaNames); - - /** - * Updates a sailing event with the name eventName, the venuevenue and the - * regattas with the names in regattaNames and updates it in the database. - * - * @param eventName - * The name of the event to update - * @param venueName - * The name of the venue of the event - * @param publicationUrl - * The publication URL of the event - * @param isPublic - * Indicates whether the event is public accessible via the publication URL or not - * @param regattaNames - * The names of the regattas contained in the event.
- * @return The new event - */ - void updateEvent(String eventName, String venueName, String publicationUrl, boolean isPublic, List regattaNames); - - /** - * Renames a sailing event. If a sailing event by the name oldName does not exist in {@link #getEvents()}, - * or if a event with the name newName already exists, an {@link IllegalArgumentException} is thrown. - * If the method completes normally, the rename has been successful, and the event previously obtained by calling - * {@link #getEventByName(String) getEventByName(oldName)} can now be obtained by calling - * {@link #getEventByName(String) getEventByName(newName)}. - */ - void renameEvent(String oldEventName, String newEventName); - - void removeEvent(String eventName); - - com.sap.sailing.domain.base.DomainFactory getBaseDomainFactory(); - - com.sap.sailing.domain.swisstimingadapter.DomainFactory getSwissTimingDomainFactory(); - -} +package com.sap.sailing.server; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.net.MalformedURLException; +import java.net.SocketException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.UnknownHostException; +import java.text.ParseException; +import java.util.ConcurrentModificationException; +import java.util.List; +import java.util.Map; + +import com.sap.sailing.domain.base.Event; +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; +import com.sap.sailing.domain.base.RegattaRegistry; +import com.sap.sailing.domain.base.Series; +import com.sap.sailing.domain.common.RaceFetcher; +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.domain.common.RegattaFetcher; +import com.sap.sailing.domain.common.RegattaIdentifier; +import com.sap.sailing.domain.common.RegattaName; +import com.sap.sailing.domain.common.ScoringSchemeType; +import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.common.impl.Util.Pair; +import com.sap.sailing.domain.common.impl.Util.Triple; +import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; +import com.sap.sailing.domain.leaderboard.Leaderboard; +import com.sap.sailing.domain.leaderboard.LeaderboardGroup; +import com.sap.sailing.domain.leaderboard.LeaderboardRegistry; +import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; +import com.sap.sailing.domain.leaderboard.ScoringScheme; +import com.sap.sailing.domain.racecommittee.RaceCommitteeStore; +import com.sap.sailing.domain.swisstimingadapter.SwissTimingFactory; +import com.sap.sailing.domain.tracking.RaceListener; +import com.sap.sailing.domain.tracking.RaceTracker; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; +import com.sap.sailing.domain.tracking.RacesHandle; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.domain.tracking.TrackedRegatta; +import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; +import com.sap.sailing.domain.tracking.WindStore; +import com.sap.sailing.domain.tracking.impl.EmptyWindStore; +import com.sap.sailing.domain.tractracadapter.DomainFactory; +import com.sap.sailing.domain.tractracadapter.RaceRecord; +import com.sap.sailing.domain.tractracadapter.TracTracRaceTracker; +import com.sap.sailing.expeditionconnector.ExpeditionListener; + +/** + * An OSGi service that can be used to track boat races using a TracTrac connector that pushes + * live GPS boat location, waypoint, coarse and mark passing data.

+ * + * If a race/regatta is already being tracked, another {@link #addTracTracRace(URL, URI, URI, WindStore, long)} or + * {@link #addRegatta(URL, URI, URI, WindStore, long)} call will have no effect, even if a different + * {@link WindStore} is requested.

+ * + * When the tracking of a race/regatta is {@link #stopTracking(Regatta, RaceDefinition) stopped}, the next + * time it's started to be tracked, a new {@link TrackedRace} at least will be constructed. This also + * means that when a {@link TrackedRegatta} exists that still holds other {@link TrackedRace}s, the + * no longer tracked {@link TrackedRace} will be removed from the {@link TrackedRegatta}. + * corresponding information is removed also from the {@link DomainFactory}'s caches to ensure that + * clean, fresh data is received should another tracking request be issued later. + * + * @author Axel Uhl (d043530) + * + */ +public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetcher, RegattaRegistry, RaceFetcher, LeaderboardRegistry { + @Override + Regatta getRegatta(RegattaName regattaName); + + @Override + RaceDefinition getRace(RegattaAndRaceIdentifier raceIdentifier); + + TrackedRace getTrackedRace(Regatta regatta, RaceDefinition race); + + TrackedRace getTrackedRace(RegattaAndRaceIdentifier raceIdentifier); + + /** + * Obtains an unmodifiable map of the leaderboard configured in this service keyed by their names. + */ + Map getLeaderboards(); + + /** + * @return a leaderboard whose {@link Leaderboard#getName()} method returns the value of the name + * parameter, or null if no such leaderboard is known to this service + */ + Leaderboard getLeaderboardByName(String name); + + /** + * Defines the regatta and for each race listed in the JSON document that is not already being tracked by this service + * creates a {@link TracTracRaceTracker} that starts tracking the respective race. The {@link RaceDefinition}s obtained this + * way are all grouped into the single {@link Regatta} produced for the event listed in the JSON response. Note that + * the many race trackers will have their TracTrac Event each, all with the same name, meaning the same + * event but being distinct. + * + * @param jsonURL + * URL of a JSON response that contains an "event" object telling the event's name and ID, as well as a + * JSON array named "races" which tells ID and replay URL for the race. From those replay URLs the + * paramURL for the Java client can be derived. + * @param timeoutInMilliseconds + * if a race definition is not received for a race of this event within this time, the race tracker for + * that race is stopped; use -1 to wait forever + */ + Regatta addRegatta(URL jsonURL, URI liveURI, URI storedURI, WindStore windStore, long timeoutInMilliseconds, + RaceCommitteeStore raceCommitteeStore) + throws MalformedURLException, FileNotFoundException, URISyntaxException, IOException, ParseException, + org.json.simple.parser.ParseException, Exception; + + /** + * If not already tracking the URL/URI/URI combination, adds a single race tracker and starts tracking the race, + * using the race's parameter URL which delivers the single configuration text file for that race. While the result + * of passing this URL to the TracTrac KeyValue.setup is a TracTrac Event, those events + * only manage a single race. In our domain model, we group those races into a single instance of our {@link Regatta} + * class. + *

+ * + * If this is the first race of an event, the {@link Regatta} is created as well. If the {@link RaceDefinition} for + * the race already exists, it isn't created again. Also, if a {@link TracTracRaceTracker} for the given race already + * exists, it is not added again. + *

+ * + * Note that when the race identified by paramURL, liveURI and storedURI is + * already being tracked, then regardless of the windStore selection the existing tracker will be used + * and its race handle will be returned. A log message will indicate a potential wind store mismatch (based on + * {@link WindStore#equals(Object)}). + * + * @param timeoutInMilliseconds + * if the race definition is not received for the race within this time, the race tracker for + * that race is stopped; use -1 to wait forever + */ + RacesHandle addTracTracRace(URL paramURL, URI liveURI, URI storedURI, WindStore windStore, long timeoutInMilliseconds, + RaceCommitteeStore raceCommitteeStore) + throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception; + + /** + * Same as {@link #addTracTracRace(URL, URI, URI, WindStore, long)}, only that start and end of tracking are + * specified which may help reducing the amount of stored data (particularly mark positions) that needs to be + * loaded. + * + * @param regattaToAddTo + * if null, an existing regatta by the name of the TracTrac event with the boat class name + * appended in parentheses will be looked up; if not found, a default regatta with that name will be + * created, with a single default series and a single default fleet. If a valid {@link RegattaIdentifier} + * is specified, a regatta lookup is performed with that identifier; if the regatta is found, it is used + * to add the races to. Otherwise, a default regatta as described above will be created and used. + */ + RacesHandle addTracTracRace(RegattaIdentifier regattaToAddTo, URL paramURL, URI liveURI, URI storedURI, + TimePoint trackingStartTime, TimePoint trackingEndTime, WindStore windStore, + long timeoutForReceivingRaceDefinitionInMilliseconds, boolean simulateWithStartTimeNow, RaceCommitteeStore raceCommitteeStore) + throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception; + + /** + * Stops tracking all races of the regatta specified. This will also stop tracking wind for all races of this regatta. + * See {@link #stopTrackingWind(Regatta, RaceDefinition)}. If there were multiple calls to + * {@link #addTracTracRace(URL, URI, URI, WindStore, long)} with an equal combination of URLs/URIs, the {@link TracTracRaceTracker} + * already tracking the race was re-used. The trackers will be stopped by this call regardless of how many calls + * were made that ensured they were tracking. + */ + void stopTracking(Regatta regatta) throws MalformedURLException, IOException, InterruptedException; + + /** + * Removes race 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 regatta is removed as well and will no + * longer be returned by {@link #getAllRegattas()}. The wind tracking is stopped for race. + *

+ * + * Any {@link RaceTracker} for which race is the last race tracked that is still reachable + * from {@link #getAllRegattas()} will be {@link RaceTracker#stop() stopped}. + * + * The race will be also removed from all leaderboards containing a column that has race'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; + + /** + * Stops all {@link RaceTracker}s currently tracking race. Note that if the same tracker also may have + * been tracking other races. Other races of the same event that are currently tracked will continue to be tracked. + * If wind tracking for the race is currently running, it will be stopped (see also + * {@link #stopTrackingWind(Regatta, RaceDefinition)}). The race (and the other races tracked by the + * same tracker) as well as the corresponding {@link TrackedRace}s will continue to exist, e.g., when asking + * {@link #getTrackedRace(Regatta, RaceDefinition)}. + */ + void stopTracking(Regatta regatta, RaceDefinition race) throws MalformedURLException, IOException, InterruptedException; + + /** + * @param port + * the UDP port on which to listen for incoming messages from Expedition clients + * @param correctByDeclination + * An optional service to convert the wind bearings (which the receiver may + * believe to be true bearings) from magnetic to true bearings. + * @throws SocketException + * thrown, e.g., in case there is already another listener on the port requested + */ + void startTrackingWind(Regatta regatta, RaceDefinition race, boolean correctByDeclination) throws SocketException; + + void stopTrackingWind(Regatta regatta, RaceDefinition race) throws SocketException, IOException; + + /** + * The {@link Triple#getC() third component} of the triples returned is a wind tracker-specific + * comment where a wind tracker may provide information such as its type name or, if applicable, + * connectivity information such as the network port on which it receives wind information. + */ + Iterable> getWindTrackedRaces(); + + /** + * For the JSON URL of an account / event, lists the paramURLs that can be used for {@link #addTracTracRace(URL, URI, URI, WindStore, long)} + * calls to individually start tracking races of this event, rather than tracking all races in the event which + * is hardly ever useful. The returned pair's first component is the event name. + */ + Pair> getTracTracRaceRecords(URL jsonURL) throws IOException, ParseException, + org.json.simple.parser.ParseException, URISyntaxException; + + List getSwissTimingRaceRecords(String hostname, int port, boolean canSendRequests) + throws InterruptedException, UnknownHostException, IOException, ParseException; + + boolean isRaceBeingTracked(RaceDefinition r); + + /** + * Creates a new leaderboard with the name specified. + * @param discardThresholds + * Tells the thresholds from which on a next higher number of worst races will be discarded per + * competitor. Example: [3, 6] means that starting from three races the single worst race + * will be discarded; starting from six races, the two worst races per competitor are discarded. + * + * @return the leaderboard created + */ + FlexibleLeaderboard addFlexibleLeaderboard(String name, int[] discardThresholds, ScoringScheme scoringScheme); + + RegattaLeaderboard addRegattaLeaderboard(RegattaIdentifier regattaIdentifier, int[] discardThresholds); + + void removeLeaderboard(String leaderboardName); + + /** + * Renames a leaderboard. If a leaderboard by the name oldName does not exist in {@link #getLeaderboards()}, + * or if a leaderboard with the name newName already exists, an {@link IllegalArgumentException} is thrown. + * If the method completes normally, the rename has been successful, and the leaderboard previously obtained by calling + * {@link #getLeaderboardByName(String) getLeaderboardByName(oldName)} can now be obtained by calling + * {@link #getLeaderboardByName(String) getLeaderboardByName(newName)}. + */ + void renameLeaderboard(String oldName, String newName); + + RaceColumn addColumnToLeaderboard(String columnName, String leaderboardName, boolean medalRace); + + void moveLeaderboardColumnUp(String leaderboardName, String columnName); + + void moveLeaderboardColumnDown(String leaderboardName, String columnName); + + void removeLeaderboardColumn(String leaderboardName, String columnName); + + void renameLeaderboardColumn(String leaderboardName, String oldColumnName, String newColumnName); + + /** + * @see RaceColumn#setFactor(Double) + */ + void updateLeaderboardColumnFactor(String leaderboardName, String columnName, Double factor); + + /** + * Updates the leaderboard data in the persistent store + */ + void updateStoredLeaderboard(Leaderboard leaderboard); + + void updateStoredRegatta(Regatta regatta); + + long getDelayToLiveInMillis(); + + void setDelayToLiveInMillis(long delayToLiveInMillis); + + /** + * @param regattaToAddTo + * if null, an existing regatta by the name of the TracTrac event with the boat class name + * appended in parentheses will be looked up; if not found, a default regatta with that name will be + * created, with a single default series and a single default fleet. If a valid {@link RegattaIdentifier} + * is specified, a regatta lookup is performed with that identifier; if the regatta is found, it is used + * to add the races to. Otherwise, a default regatta as described above will be created and used. + */ + RacesHandle addSwissTimingRace(RegattaIdentifier regattaToAddTo, String raceID, String hostname, int port, + boolean canSendRequests, WindStore windStore, long timeoutInMilliseconds, + RaceCommitteeStore raceCommitteeStore) throws InterruptedException, UnknownHostException, + IOException, ParseException, Exception; + + SwissTimingFactory getSwissTimingFactory(); + + void storeSwissTimingDummyRace(String racMessage, String stlMesssage, String ccgMessage) throws IllegalArgumentException; + + void stopTrackingAndRemove(Regatta regatta) throws MalformedURLException, IOException, InterruptedException; + + void removeRegatta(Regatta regatta) throws MalformedURLException, IOException, InterruptedException; + + TrackedRace getExistingTrackedRace(RegattaAndRaceIdentifier raceIdentifier); + + /** + * Obtains an unmodifiable map of the leaderboard groups configured in this service keyed by their names. + */ + Map getLeaderboardGroups(); + + /** + * @param groupName The name of the requested leaderboard group + * @return The leaderboard group with the name groupName, or null if theres no such group + */ + LeaderboardGroup getLeaderboardGroupByName(String groupName); + + /** + * Creates a new group with the name groupName, the description desciption and the + * leaderboards with the names in leaderboardNames and saves it in the database. + * + * @param groupName + * The name of the new group + * @param description + * The description of the new group + * @param displayGroupsInReverseOrder TODO + * @param leaderboardNames + * The names of the leaderboards, which should be contained by the new group.
+ * If there isn't a leaderboard with one of these names an {@link IllegalArgumentException} is thrown. + * @return The new leaderboard group + */ + LeaderboardGroup addLeaderboardGroup(String groupName, String description, boolean displayGroupsInReverseOrder, + List leaderboardNames, int[] overallLeaderboardDiscardThresholds, ScoringSchemeType overallLeaderboardScoringSchemeType); + + /** + * Removes the group with the name groupName from the service and the database. + * @param groupName The name of the group which shall be removed. + */ + void removeLeaderboardGroup(String groupName); + + /** + * Renames the group with the name oldName to the newName.
+ * If there's no group with the name oldName or there's already a group with the name + * newName a {@link IllegalArgumentException} is thrown. + * + * @param oldName The old name of the group + * @param newName The new name of the group + */ + void renameLeaderboardGroup(String oldName, String newName); + + /** + * Updates the group data in the persistant store. + */ + void updateStoredLeaderboardGroup(LeaderboardGroup leaderboardGroup); + + void addExpeditionListener(ExpeditionListener listener, boolean validMessagesOnly) throws SocketException; + + void removeExpeditionListener(ExpeditionListener listener); + + /** + * @param regattaToAddTo + * if null or no regatta by that identifier is found, an existing regatta by the name of the + * TracTrac event with the boat class name appended in parentheses will be looked up; if not found, a + * default regatta with that name will be created, with a single default series and a single default + * fleet. If a valid {@link RegattaIdentifier} is specified, a regatta lookup is performed with that + * identifier; if the regatta is found, it is used to add the races to, and + * {@link #setRegattaForRace(Regatta, RaceDefinition)} is called to remember the association + * persistently. Otherwise, a default regatta as described above will be created and used. + * @param windStore + * must not be null, but can, e.g., be an {@link EmptyWindStore} + */ + RacesHandle addRace(RegattaIdentifier regattaToAddTo, RaceTrackingConnectivityParameters params, WindStore windStore, long timeoutInMilliseconds, + RaceCommitteeStore raceCommitteeStore) + throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception; + + TrackedRace createTrackedRace(RegattaAndRaceIdentifier raceIdentifier, WindStore windStore, + long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed, + RaceCommitteeStore raceCommitteeStore); + + Regatta getOrCreateRegatta(String regattaName, String boatClassName); + + Regatta createRegatta(String baseName, String boatClassName, Iterable series, boolean persistent, ScoringScheme scoringScheme); + + /** + * Adds raceDefinition to the {@link Regatta} such that it will appear in {@link Regatta#getAllRaces()} + * and {@link Regatta#getRaceByName(String)}. + * + * @param addToRegatta identifier of an regatta that must exist already + */ + void addRace(RegattaIdentifier addToRegatta, RaceDefinition raceDefinition); + + void updateLeaderboardGroup(String oldName, String newName, String description, List leaderboardNames, + int[] overallLeaderboardDiscardThresholds, ScoringSchemeType overallLeaderboardScoringSchemeType); + + /** + * Executes an operation whose effects need to be replicated to any replica of this service known and + * {@link OperationExecutionListener#executed(RacingEventServiceOperation) notifies} all registered + * operation execution listeners about the execution of the operation. + */ + T apply(RacingEventServiceOperation operation); + + void addOperationExecutionListener(OperationExecutionListener listener); + + void removeOperationExecutionListener(OperationExecutionListener listener); + + /** + * Produces a one-shot serializable copy of those elements required for replication into oos so that + * afterwards the {@link RacingEventServiceOperation}s can be {@link #apply(RacingEventServiceOperation) applied} to + * maintain consistency with the master copy of the service. The dual operation is {@link #initiallyFillFrom}. + */ + void serializeForInitialReplication(ObjectOutputStream oos) throws IOException; + + /** + * Dual, reading operation for {@link #serializeForInitialReplication(ObjectOutputStream)}. In other words, when + * this operation returns, this service instance is in a state "equivalent" to that of the service instance that + * produced the stream contents in its {@link #serializeForInitialReplication(ObjectOutputStream)}. "Equivalent" + * here means that a replica will have equal sets of regattas, tracked regattas, leaderboards and leaderboard groups but + * will not have any active trackers for wind or positions because it relies on these elements to be sent through + * the replication channel. + *

+ * + * Tracked regattas read from the stream are observed (see {@link RaceListener}) by this object for automatic updates + * to the default leaderboard and for automatic linking to leaderboard columns. It is assumed that no explicit + * replication of these operations will happen based on the changes performed on the replication master.

+ * + * Caution: All relevant contents of this service instance will be replaced by the stream contents. + */ + void initiallyFillFrom(ObjectInputStream ois) throws IOException, ClassNotFoundException; + + Event getEventByName(String name); + + /** + * @return a thread-safe copy of the events currently known by the service; it's safe for callers to iterate over + * the iterable returned, and no risk of a {@link ConcurrentModificationException} exists + */ + Iterable getAllEvents(); + + /** + * Creates a new event with the name eventName, the venuevenue and the + * regattas with the names in regattaNames and saves it in the database. + * + * @param eventName + * The name of the new event + * @param venue + * The name of the venue of the new event + * @param publicationUrl + * The publication URL of the new event + * @param isPublic + * Indicates whether the event is public accessible via the publication URL or not + * @param regattaNames + * The names of the regattas contained in the new event.
+ * @return The new event + */ + Event addEvent(String eventName, String venueName, String publicationUrl, boolean isPublic, List regattaNames); + + /** + * Updates a sailing event with the name eventName, the venuevenue and the + * regattas with the names in regattaNames and updates it in the database. + * + * @param eventName + * The name of the event to update + * @param venueName + * The name of the venue of the event + * @param publicationUrl + * The publication URL of the event + * @param isPublic + * Indicates whether the event is public accessible via the publication URL or not + * @param regattaNames + * The names of the regattas contained in the event.
+ * @return The new event + */ + void updateEvent(String eventName, String venueName, String publicationUrl, boolean isPublic, List regattaNames); + + /** + * Renames a sailing event. If a sailing event by the name oldName does not exist in {@link #getEvents()}, + * or if a event with the name newName already exists, an {@link IllegalArgumentException} is thrown. + * If the method completes normally, the rename has been successful, and the event previously obtained by calling + * {@link #getEventByName(String) getEventByName(oldName)} can now be obtained by calling + * {@link #getEventByName(String) getEventByName(newName)}. + */ + void renameEvent(String oldEventName, String newEventName); + + void removeEvent(String eventName); + + com.sap.sailing.domain.base.DomainFactory getBaseDomainFactory(); + + com.sap.sailing.domain.swisstimingadapter.DomainFactory getSwissTimingDomainFactory(); + +} diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index cc099cb42ba..d7a40f854dd 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -27,11 +27,11 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; -import com.sap.sailing.domain.base.Mark; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.ControlPoint; import com.sap.sailing.domain.base.Event; import com.sap.sailing.domain.base.Fleet; +import com.sap.sailing.domain.base.Mark; import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.RaceColumnInSeries; import com.sap.sailing.domain.base.RaceDefinition; @@ -70,6 +70,7 @@ import com.sap.sailing.domain.persistence.DomainObjectFactory; import com.sap.sailing.domain.persistence.MongoFactory; import com.sap.sailing.domain.persistence.MongoObjectFactory; import com.sap.sailing.domain.racecommittee.RaceCommitteeEvent; +import com.sap.sailing.domain.racecommittee.RaceCommitteeStore; import com.sap.sailing.domain.swisstimingadapter.Race; import com.sap.sailing.domain.swisstimingadapter.SailMasterConnector; import com.sap.sailing.domain.swisstimingadapter.SailMasterMessage; @@ -107,8 +108,8 @@ import com.sap.sailing.server.operationaltransformation.AddRaceDefinition; import com.sap.sailing.server.operationaltransformation.AddSpecificRegatta; import com.sap.sailing.server.operationaltransformation.ConnectTrackedRaceToLeaderboardColumn; import com.sap.sailing.server.operationaltransformation.CreateTrackedRace; -import com.sap.sailing.server.operationaltransformation.RecordMarkGPSFix; import com.sap.sailing.server.operationaltransformation.RecordCompetitorGPSFix; +import com.sap.sailing.server.operationaltransformation.RecordMarkGPSFix; import com.sap.sailing.server.operationaltransformation.RecordRaceCommitteeEvent; import com.sap.sailing.server.operationaltransformation.RecordWindFix; import com.sap.sailing.server.operationaltransformation.RemoveWindFix; @@ -570,12 +571,13 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen } @Override - public Regatta addRegatta(URL jsonURL, URI liveURI, URI storedURI, WindStore windStore, long timeoutInMilliseconds) throws Exception { + public Regatta addRegatta(URL jsonURL, URI liveURI, URI storedURI, WindStore windStore, long timeoutInMilliseconds, + RaceCommitteeStore raceCommitteeStore) throws Exception { JSONService jsonService = getTracTracDomainFactory().parseJSONURL(jsonURL); Regatta regatta = null; for (RaceRecord rr : jsonService.getRaceRecords()) { URL paramURL = rr.getParamURL(); - regatta = addTracTracRace(paramURL, liveURI, storedURI, windStore, timeoutInMilliseconds).getRegatta(); + regatta = addTracTracRace(paramURL, liveURI, storedURI, windStore, timeoutInMilliseconds, raceCommitteeStore).getRegatta(); } return regatta; } @@ -629,21 +631,23 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen @Override public RacesHandle addSwissTimingRace(RegattaIdentifier regattaToAddTo, String raceID, String hostname, - int port, boolean canSendRequests, WindStore windStore, long timeoutInMilliseconds) throws Exception { + int port, boolean canSendRequests, WindStore windStore, long timeoutInMilliseconds, + RaceCommitteeStore raceCommitteeStore) throws Exception { return addRace( regattaToAddTo, swissTimingDomainFactory.createTrackingConnectivityParameters(hostname, port, raceID, canSendRequests, delayToLiveInMillis, - swissTimingFactory, swissTimingDomainFactory, windStore, swissTimingAdapterPersistence), windStore, timeoutInMilliseconds); + swissTimingFactory, swissTimingDomainFactory, windStore, raceCommitteeStore, swissTimingAdapterPersistence), windStore, timeoutInMilliseconds, + raceCommitteeStore); } @Override public RacesHandle addTracTracRace(URL paramURL, URI liveURI, URI storedURI, WindStore windStore, - long timeoutInMilliseconds) throws Exception { + long timeoutInMilliseconds, RaceCommitteeStore raceCommitteeStore) throws Exception { return addRace( /* regattaToAddTo */null, getTracTracDomainFactory().createTrackingConnectivityParameters(paramURL, liveURI, storedURI, /* startOfTracking */null, - /* endOfTracking */null, delayToLiveInMillis, /* simulateWithStartTimeNow */false, windStore), windStore, - timeoutInMilliseconds); + /* endOfTracking */null, delayToLiveInMillis, /* simulateWithStartTimeNow */false, windStore, raceCommitteeStore), windStore, + timeoutInMilliseconds, raceCommitteeStore); } @Override @@ -674,7 +678,7 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen @Override public RacesHandle addRace(RegattaIdentifier regattaToAddTo, RaceTrackingConnectivityParameters params, - WindStore windStore, long timeoutInMilliseconds) throws Exception { + WindStore windStore, long timeoutInMilliseconds, RaceCommitteeStore raceCommitteeStore) throws Exception { RaceTracker tracker = raceTrackersByID.get(params.getTrackerID()); if (tracker == null) { Regatta regatta = regattaToAddTo == null ? null : getRegatta(regattaToAddTo); @@ -781,20 +785,23 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen @Override public TrackedRace createTrackedRace(RegattaAndRaceIdentifier raceIdentifier, WindStore windStore, - long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed) { + long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed, + RaceCommitteeStore raceCommitteeStore) { DynamicTrackedRegatta trackedRegatta = getOrCreateTrackedRegatta(getRegatta(raceIdentifier)); RaceDefinition race = getRace(raceIdentifier); return trackedRegatta.createTrackedRace(race, windStore, delayToLiveInMillis, millisecondsOverWhichToAverageWind, millisecondsOverWhichToAverageSpeed, - /* raceDefinitionSetToUpdate */null); + /* raceDefinitionSetToUpdate */null, raceCommitteeStore); } @Override public RacesHandle addTracTracRace(RegattaIdentifier regattaToAddTo, URL paramURL, URI liveURI, URI storedURI, TimePoint startOfTracking, TimePoint endOfTracking, - WindStore windStore, long timeoutInMilliseconds, boolean simulateWithStartTimeNow) throws Exception { + WindStore windStore, long timeoutInMilliseconds, boolean simulateWithStartTimeNow, + RaceCommitteeStore raceCommitteeStore) throws Exception { return addRace(regattaToAddTo, getTracTracDomainFactory().createTrackingConnectivityParameters(paramURL, liveURI, storedURI, startOfTracking, - endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, windStore), windStore, timeoutInMilliseconds); + endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, windStore, raceCommitteeStore), windStore, timeoutInMilliseconds, + raceCommitteeStore); } private void ensureRegattaIsObservedForDefaultLeaderboardAndAutoLeaderboardLinking(DynamicTrackedRegatta trackedRegatta) { @@ -844,7 +851,8 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen // replicate the addition of the tracked race: CreateTrackedRace op = new CreateTrackedRace(trackedRace.getRaceIdentifier(), trackedRace.getWindStore(), trackedRace.getDelayToLiveInMillis(), - trackedRace.getMillisecondsOverWhichToAverageWind(), trackedRace.getMillisecondsOverWhichToAverageSpeed()); + trackedRace.getMillisecondsOverWhichToAverageWind(), trackedRace.getMillisecondsOverWhichToAverageSpeed(), + trackedRace.getRaceCommitteeStore()); replicate(op); linkRaceToConfiguredLeaderboardColumns(trackedRace); final FlexibleLeaderboard defaultLeaderboard = (FlexibleLeaderboard) leaderboardsByName.get(LeaderboardNameConstants.DEFAULT_LEADERBOARD_NAME); diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/CreateTrackedRace.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/CreateTrackedRace.java index b5247c09489..78c7401c6be 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/CreateTrackedRace.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/CreateTrackedRace.java @@ -4,6 +4,8 @@ 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.common.RaceIdentifier; +import com.sap.sailing.domain.racecommittee.RaceCommitteeStore; +import com.sap.sailing.domain.racecommittee.impl.EmptyRaceCommitteeStore; import com.sap.sailing.domain.tracking.TrackedRegatta; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.WindStore; @@ -32,16 +34,28 @@ public class CreateTrackedRace extends AbstractRaceOperation { * {@link EmptyWindStore}. */ private transient final WindStore windStore; + + /** + * If a {@link RaceCommitteeStore} is provided to this command, it will be used for the construction of the tracked race. + * However, after de-serialization, the race committee store will always be null, causing the use of an + * {@link EmptyRaceCommitteeStore}. + */ + private transient final RaceCommitteeStore raceCommitteeStore; /** * @param windStore * if null, 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 raceCommitteeStore + * if null, an {@link EmptyRaceCommitteeStore} will be used. Note that the {@link #raceCommitteeStore} field + * won't be serialized. A receiver of this operation will therefore always use an {@link EmptyRaceCommitteeStore}. */ public CreateTrackedRace(RegattaAndRaceIdentifier raceIdentifier, WindStore windStore, - long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed) { + long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed, + RaceCommitteeStore raceCommitteeStore) { super(raceIdentifier); this.windStore = windStore; + this.raceCommitteeStore = raceCommitteeStore; this.delayToLiveInMillis = delayToLiveInMillis; this.millisecondsOverWhichToAverageWind = millisecondsOverWhichToAverageWind; this.millisecondsOverWhichToAverageSpeed = millisecondsOverWhichToAverageSpeed; @@ -62,7 +76,8 @@ public class CreateTrackedRace extends AbstractRaceOperation { @Override public TrackedRace internalApplyTo(RacingEventService toState) { return toState.createTrackedRace(getRaceIdentifier(), windStore == null ? EmptyWindStore.INSTANCE : windStore, - delayToLiveInMillis, millisecondsOverWhichToAverageWind, millisecondsOverWhichToAverageSpeed); + delayToLiveInMillis, millisecondsOverWhichToAverageWind, millisecondsOverWhichToAverageSpeed, + raceCommitteeStore == null ? EmptyRaceCommitteeStore.INSTANCE : raceCommitteeStore); } }