Extended the TrackedRace Constructor by the RaceCommitteeStore, so that

a RC store has to be given at TrackedRace creation, necessary for
replication. All following TrackedRace constructor calls and calling
methods are adapted accordingly.
This commit is contained in:
Armin Zamani committed 2012-12-28 15:50:36 +01:00
1 parent 7a7d0553c4
commit 6fd712e9de
40 files changed
+1260 -1133

No files matched your search

@@ -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<RacesHandle> 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<RacesHandle>();
}
@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<TrackedRace> allTrackedRaces = new HashSet<TrackedRace>();
Iterable<Regatta> allRegattas = racingEventService.getAllRegattas();
for (Regatta regatta : allRegattas) {
DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(regatta);
Iterable<TrackedRace> trackedRaces = trackedRegatta.getTrackedRaces();
for (TrackedRace trackedRace : trackedRaces) {
allTrackedRaces.add(trackedRace);
}
}
assertEquals(2, Util.size(allTrackedRaces));
Set<String> raceIDs = new HashSet<String>();
for (TrackedRace trackedRace : allTrackedRaces) {
RaceDefinition race = trackedRace.getRace();
raceIDs.add(race.getName());
}
Set<String> expectedRaceIDs = new HashSet<String>();
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<TrackedRace> allTrackedRaces = new HashSet<TrackedRace>();
Iterable<Regatta> allRegattas = racingEventService.getAllRegattas();
for (Regatta regatta : allRegattas) {
DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(regatta);
Iterable<TrackedRace> trackedRaces = trackedRegatta.getTrackedRaces();
for (TrackedRace trackedRace : trackedRaces) {
allTrackedRaces.add(trackedRace);
}
}
assertEquals(1, Util.size(allTrackedRaces));
Set<RaceDefinition> races = raceHandles.iterator().next().getRaceTracker().getRaces();
assertEquals(1, races.size());
RaceDefinition raceFromTracker = races.iterator().next();
assertNotNull(raceFromTracker);
Set<String> raceIDs = new HashSet<String>();
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<Competitor, GPSFixMoving> track = trackedRace.getTrack(competitor);
track.lockForRead();
try {
assertTrue("Track of competitor " + competitor + " empty", !Util.isEmpty(track.getRawFixes()));
} finally {
track.unlockAfterRead();
}
}
}
Set<Mark> marks = new HashSet<Mark>();
for (Waypoint waypoint : race.getCourse().getWaypoints()) {
for (Mark mark : waypoint.getMarks()) {
marks.add(mark);
}
}
for (Mark mark : marks) {
final GPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(mark);
track.lockForRead();
try {
assertTrue("Track of mark " + mark + " empty",
!Util.isEmpty(track.getRawFixes()));
} finally {
track.unlockAfterRead();
}
}
}
Set<String> expectedRaceIDs = new HashSet<String>();
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<TrackedRace> allNewTrackedRaces = new HashSet<TrackedRace>();
Iterable<Regatta> allNewEvents = racingEventService.getAllRegattas();
for (Regatta event : allNewEvents) {
DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(event);
Iterable<TrackedRace> trackedRaces = trackedRegatta.getTrackedRaces();
for (TrackedRace trackedRace : trackedRaces) {
allNewTrackedRaces.add(trackedRace);
}
}
assertEquals(1, Util.size(allNewTrackedRaces));
Set<RaceDefinition> races = raceHandles.iterator().next().getRaceTracker().getRaces();
assertEquals(1, races.size());
RaceDefinition raceFromTracker = races.iterator().next();
assertNotNull(raceFromTracker);
Set<String> raceIDs = new HashSet<String>();
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<Competitor, GPSFixMoving> track = trackedRace.getTrack(competitor);
track.lockForRead();
try {
assertTrue("Track of competitor " + competitor + " empty", !Util.isEmpty(track.getRawFixes()));
} finally {
track.unlockAfterRead();
}
}
}
Set<Mark> marks = new HashSet<Mark>();
for (Waypoint waypoint : race.getCourse().getWaypoints()) {
for (Mark mark : waypoint.getMarks()) {
marks.add(mark);
}
}
for (Mark mark : marks) {
final GPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(mark);
track.lockForRead();
try {
assertTrue("Track of mark " + mark + " empty", !Util.isEmpty(track.getRawFixes()));
} finally {
track.unlockAfterRead();
}
}
}
Set<String> expectedRaceIDs = new HashSet<String>();
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<TrackedRace> allTrackedRaces = new HashSet<TrackedRace>();
Iterable<Regatta> allEvents = racingEventService.getAllRegattas();
for (Regatta event : allEvents) {
DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(event);
Iterable<TrackedRace> trackedRaces = trackedRegatta.getTrackedRaces();
for (TrackedRace trackedRace : trackedRaces) {
allTrackedRaces.add(trackedRace);
}
}
assertEquals(1, allTrackedRaces.size());
TrackedRace trackedRace = allTrackedRaces.iterator().next();
List<Waypoint> waypoints = new ArrayList<Waypoint>();
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<TrackedRace> allNewTrackedRaces = new HashSet<TrackedRace>();
Iterable<Regatta> allNewEvents = racingEventService.getAllRegattas();
for (Regatta event : allNewEvents) {
DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(event);
Iterable<TrackedRace> 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<TrackedRace> allTrackedRaces = new HashSet<TrackedRace>();
Iterable<Regatta> allEvents = racingEventService.getAllRegattas();
for (Regatta event : allEvents) {
DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(event);
Iterable<TrackedRace> 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<RacesHandle> 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<RacesHandle>();
}
@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<TrackedRace> allTrackedRaces = new HashSet<TrackedRace>();
Iterable<Regatta> allRegattas = racingEventService.getAllRegattas();
for (Regatta regatta : allRegattas) {
DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(regatta);
Iterable<TrackedRace> trackedRaces = trackedRegatta.getTrackedRaces();
for (TrackedRace trackedRace : trackedRaces) {
allTrackedRaces.add(trackedRace);
}
}
assertEquals(2, Util.size(allTrackedRaces));
Set<String> raceIDs = new HashSet<String>();
for (TrackedRace trackedRace : allTrackedRaces) {
RaceDefinition race = trackedRace.getRace();
raceIDs.add(race.getName());
}
Set<String> expectedRaceIDs = new HashSet<String>();
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<TrackedRace> allTrackedRaces = new HashSet<TrackedRace>();
Iterable<Regatta> allRegattas = racingEventService.getAllRegattas();
for (Regatta regatta : allRegattas) {
DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(regatta);
Iterable<TrackedRace> trackedRaces = trackedRegatta.getTrackedRaces();
for (TrackedRace trackedRace : trackedRaces) {
allTrackedRaces.add(trackedRace);
}
}
assertEquals(1, Util.size(allTrackedRaces));
Set<RaceDefinition> races = raceHandles.iterator().next().getRaceTracker().getRaces();
assertEquals(1, races.size());
RaceDefinition raceFromTracker = races.iterator().next();
assertNotNull(raceFromTracker);
Set<String> raceIDs = new HashSet<String>();
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<Competitor, GPSFixMoving> track = trackedRace.getTrack(competitor);
track.lockForRead();
try {
assertTrue("Track of competitor " + competitor + " empty", !Util.isEmpty(track.getRawFixes()));
} finally {
track.unlockAfterRead();
}
}
}
Set<Mark> marks = new HashSet<Mark>();
for (Waypoint waypoint : race.getCourse().getWaypoints()) {
for (Mark mark : waypoint.getMarks()) {
marks.add(mark);
}
}
for (Mark mark : marks) {
final GPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(mark);
track.lockForRead();
try {
assertTrue("Track of mark " + mark + " empty",
!Util.isEmpty(track.getRawFixes()));
} finally {
track.unlockAfterRead();
}
}
}
Set<String> expectedRaceIDs = new HashSet<String>();
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<TrackedRace> allNewTrackedRaces = new HashSet<TrackedRace>();
Iterable<Regatta> allNewEvents = racingEventService.getAllRegattas();
for (Regatta event : allNewEvents) {
DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(event);
Iterable<TrackedRace> trackedRaces = trackedRegatta.getTrackedRaces();
for (TrackedRace trackedRace : trackedRaces) {
allNewTrackedRaces.add(trackedRace);
}
}
assertEquals(1, Util.size(allNewTrackedRaces));
Set<RaceDefinition> races = raceHandles.iterator().next().getRaceTracker().getRaces();
assertEquals(1, races.size());
RaceDefinition raceFromTracker = races.iterator().next();
assertNotNull(raceFromTracker);
Set<String> raceIDs = new HashSet<String>();
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<Competitor, GPSFixMoving> track = trackedRace.getTrack(competitor);
track.lockForRead();
try {
assertTrue("Track of competitor " + competitor + " empty", !Util.isEmpty(track.getRawFixes()));
} finally {
track.unlockAfterRead();
}
}
}
Set<Mark> marks = new HashSet<Mark>();
for (Waypoint waypoint : race.getCourse().getWaypoints()) {
for (Mark mark : waypoint.getMarks()) {
marks.add(mark);
}
}
for (Mark mark : marks) {
final GPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(mark);
track.lockForRead();
try {
assertTrue("Track of mark " + mark + " empty", !Util.isEmpty(track.getRawFixes()));
} finally {
track.unlockAfterRead();
}
}
}
Set<String> expectedRaceIDs = new HashSet<String>();
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<TrackedRace> allTrackedRaces = new HashSet<TrackedRace>();
Iterable<Regatta> allEvents = racingEventService.getAllRegattas();
for (Regatta event : allEvents) {
DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(event);
Iterable<TrackedRace> trackedRaces = trackedRegatta.getTrackedRaces();
for (TrackedRace trackedRace : trackedRaces) {
allTrackedRaces.add(trackedRace);
}
}
assertEquals(1, allTrackedRaces.size());
TrackedRace trackedRace = allTrackedRaces.iterator().next();
List<Waypoint> waypoints = new ArrayList<Waypoint>();
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<TrackedRace> allNewTrackedRaces = new HashSet<TrackedRace>();
Iterable<Regatta> allNewEvents = racingEventService.getAllRegattas();
for (Regatta event : allNewEvents) {
DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(event);
Iterable<TrackedRace> 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<TrackedRace> allTrackedRaces = new HashSet<TrackedRace>();
Iterable<Regatta> allEvents = racingEventService.getAllRegattas();
for (Regatta event : allEvents) {
DynamicTrackedRegatta trackedRegatta = racingEventService.getTrackedRegatta(event);
Iterable<TrackedRace> 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();
}
}
}
}
@@ -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);
@@ -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);
@@ -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);
}
}
@@ -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
@@ -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<String, String, Integer> 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());
}
@@ -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
@@ -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);
@@ -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);
@@ -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<Route, RouteData, Race> event) {
super.handleEvent(event);
@@ -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());
@@ -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<Receiver> receivers = new ArrayList<Receiver>();
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
@@ -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);
@@ -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) {}
@@ -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
@@ -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
@@ -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<Competitor> competitors = fourtyninerYellow_2.getRacesHandle().getRaces().iterator().next()
.getCompetitors();
@@ -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);
@@ -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;
}
@@ -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<Receiver> 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<Receiver> getUpdateReceivers(DynamicTrackedRegatta trackedRegatta, Event tractracEvent, WindStore windStore,
Iterable<Receiver> 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<List<com.sap.sailing.domain.base.Competitor>, 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 <code>tractracRace</code> which includes removing it from the race cache, from the
* {@link com.sap.sailing.domain.base.Regatta} and, if a {@link TrackedRace} for the corresponding
@@ -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<Receiver> 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<Receiver> 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);
}
}
@@ -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<Route, RouteDa
private final long millisecondsOverWhichToAverageWind;
private final long delayToLiveInMillis;
private final WindStore windStore;
private final RaceCommitteeStore raceCommitteeStore;
private final DynamicRaceDefinitionSet raceDefinitionSetToUpdate;
public RaceCourseReceiver(DomainFactory domainFactory, DynamicTrackedRegatta trackedRegatta,
com.tractrac.clientmodule.Event tractracEvent, WindStore windStore,
DynamicRaceDefinitionSet raceDefinitionSetToUpdate, long delayToLiveInMillis,
long millisecondsOverWhichToAverageWind, Simulator simulator) {
long millisecondsOverWhichToAverageWind, Simulator simulator, RaceCommitteeStore raceCommitteeStore) {
super(domainFactory, tractracEvent, trackedRegatta, simulator);
this.millisecondsOverWhichToAverageWind = millisecondsOverWhichToAverageWind;
this.delayToLiveInMillis = delayToLiveInMillis;
@@ -54,6 +56,7 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue<Route, RouteDa
this.windStore = simulator.simulatingWindStore(windStore);
}
this.raceDefinitionSetToUpdate = raceDefinitionSetToUpdate;
this.raceCommitteeStore = raceCommitteeStore;
}
/**
@@ -101,7 +104,7 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue<Route, RouteDa
// create race definition and add to event
DynamicTrackedRace trackedRace = getDomainFactory().getOrCreateRaceDefinitionAndTrackedRace(
getTrackedRegatta(), event.getC(), course, windStore, delayToLiveInMillis,
millisecondsOverWhichToAverageWind, raceDefinitionSetToUpdate);
millisecondsOverWhichToAverageWind, raceDefinitionSetToUpdate, raceCommitteeStore);
if (getSimulator() != null) {
getSimulator().setTrackedRace(trackedRace);
}
@@ -112,7 +115,7 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue<Route, RouteDa
getTrackedRegatta().createTrackedRace(race,
windStore, delayToLiveInMillis, millisecondsOverWhichToAverageWind,
/* time over which to average speed: */ race.getBoatClass().getApproximateManeuverDurationInMilliseconds(),
raceDefinitionSetToUpdate);
raceDefinitionSetToUpdate, raceCommitteeStore);
}
}
@@ -9,6 +9,7 @@ import java.net.URL;
import com.sap.sailing.domain.base.Regatta;
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.tracking.RaceTracker;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
@@ -22,13 +23,14 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne
private final TimePoint startOfTracking;
private final TimePoint endOfTracking;
private final WindStore windStore;
private final RaceCommitteeStore raceCommitteeStore;
private final DomainFactory domainFactory;
private final long delayToLiveInMillis;
private final boolean simulateWithStartTimeNow;
public RaceTrackingConnectivityParametersImpl(URL paramURL, URI liveURI, URI storedURI, TimePoint startOfTracking,
TimePoint endOfTracking, long delayToLiveInMillis, boolean simulateWithStartTimeNow, WindStore windStore,
DomainFactory domainFactory) {
DomainFactory domainFactory, RaceCommitteeStore raceCommitteeStore) {
super();
this.paramURL = paramURL;
this.liveURI = liveURI;
@@ -37,6 +39,7 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne
this.endOfTracking = endOfTracking;
this.delayToLiveInMillis = delayToLiveInMillis;
this.windStore = windStore;
this.raceCommitteeStore = raceCommitteeStore;
this.domainFactory = domainFactory;
this.simulateWithStartTimeNow = simulateWithStartTimeNow;
}
@@ -45,7 +48,7 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne
public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry) throws MalformedURLException,
FileNotFoundException, URISyntaxException {
RaceTracker tracker = domainFactory.createRaceTracker(paramURL, liveURI, storedURI, startOfTracking,
endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, windStore, trackedRegattaRegistry);
endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, windStore, trackedRegattaRegistry, raceCommitteeStore);
return tracker;
}
@@ -53,7 +56,7 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne
public RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry)
throws Exception {
RaceTracker tracker = domainFactory.createRaceTracker(regatta, paramURL, liveURI, storedURI, startOfTracking,
endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, windStore, trackedRegattaRegistry);
endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, windStore, trackedRegattaRegistry, raceCommitteeStore);
return tracker;
}
@@ -33,6 +33,7 @@ import com.sap.sailing.domain.base.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.domain.racecommittee.RaceCommitteeStore;
import com.sap.sailing.domain.tracking.AbstractRaceTrackerImpl;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet;
@@ -118,18 +119,19 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
*/
protected TracTracRaceTrackerImpl(DomainFactory domainFactory, 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 URISyntaxException, MalformedURLException, FileNotFoundException {
this(KeyValue.setup(paramURL), domainFactory, paramURL, liveURI, storedURI, startOfTracking, endOfTracking,
delayToLiveInMillis, simulateWithStartTimeNow, windStore, trackedRegattaRegistry);
delayToLiveInMillis, simulateWithStartTimeNow, windStore, trackedRegattaRegistry, raceCommitteeStore);
}
private TracTracRaceTrackerImpl(Event tractracEvent, DomainFactory domainFactory, URL paramURL, URI liveURI, URI storedURI,
TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, boolean simulateWithStartTimeNow,
WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry) throws URISyntaxException, MalformedURLException,
FileNotFoundException {
WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry, RaceCommitteeStore raceCommitteeStore)
throws URISyntaxException, MalformedURLException, FileNotFoundException {
this(tractracEvent, null, domainFactory, paramURL, liveURI, storedURI,
startOfTracking, endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, windStore, trackedRegattaRegistry);
startOfTracking, endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, windStore, trackedRegattaRegistry, raceCommitteeStore);
}
/**
@@ -140,10 +142,10 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
*/
protected TracTracRaceTrackerImpl(Regatta regatta, DomainFactory domainFactory, URL paramURL, URI liveURI, URI storedURI,
TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, boolean simulateWithStartTimeNow,
WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry) throws URISyntaxException, MalformedURLException,
FileNotFoundException {
WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry, RaceCommitteeStore raceCommitteeStore)
throws URISyntaxException, MalformedURLException, FileNotFoundException {
this(KeyValue.setup(paramURL), regatta, domainFactory, paramURL, liveURI, storedURI, startOfTracking,
endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, windStore, trackedRegattaRegistry);
endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, windStore, trackedRegattaRegistry, raceCommitteeStore);
}
/**
@@ -159,7 +161,8 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
*/
private TracTracRaceTrackerImpl(Event tractracEvent, final Regatta regatta, DomainFactory domainFactory,
URL paramURL, URI liveURI, URI storedURI, TimePoint startOfTracking, TimePoint endOfTracking,
long delayToLiveInMillis, boolean simulateWithStartTimeNow, WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry)
long delayToLiveInMillis, boolean simulateWithStartTimeNow, WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry,
RaceCommitteeStore raceCommitteeStore)
throws URISyntaxException, MalformedURLException, FileNotFoundException {
super();
this.tractracEvent = tractracEvent;
@@ -204,7 +207,7 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
receivers = new HashSet<Receiver>();
Set<TypeController> typeControllers = new HashSet<TypeController>();
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);
@@ -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);
}
@@ -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 <code>race</code>. Blocks until the tracked race has been created
@@ -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<WindSource> windSourcesToExclude = new HashSet<WindSource>();
@@ -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
@@ -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;
}
@@ -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<Iterable<MarkPassing>, 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<WindSource>();
this.directionFromStartToNextMarkCache = new HashMap<TimePoint, Future<Wind>>();
this.millisecondsOverWhichToAverageSpeed = millisecondsOverWhichToAverageSpeed;
@@ -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);
}
@@ -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() {
@@ -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<Receiver> 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());
@@ -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;
}
}
@@ -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 <T> void executed(RacingEventServiceOperation<T> 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 <T> void executed(RacingEventServiceOperation<T> 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();
}
}
@@ -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();
}
@@ -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<Waypoint>()), boatClass, new ArrayList<Competitor>());
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<RaceDefinition> raceDefinitionSetRace1 = new HashSet<RaceDefinition>();
raceDefinitionSetRace1.add(raceDef1);
Set<RaceDefinition> raceDefinitionSetRace2 = new HashSet<RaceDefinition>();
@@ -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<RaceDefinition> 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
@@ -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.<p>
*
* 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.<p>
*
* 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<String, Leaderboard> getLeaderboards();
/**
* @return a leaderboard whose {@link Leaderboard#getName()} method returns the value of the <code>name</code>
* parameter, or <code>null</code> 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 <code>Event</code> 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 <code>KeyValue.setup</code> is a TracTrac <code>Event</code>, those events
* only manage a single race. In our domain model, we group those races into a single instance of our {@link Regatta}
* class.
* <p>
*
* 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.
* <p>
*
* Note that when the race identified by <code>paramURL</code>, <code>liveURI</code> and <code>storedURI</code> is
* already being tracked, then regardless of the <code>windStore</code> 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 <code>null</code>, 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 <code>race</code> and any corresponding {@link #getTrackedRace(Regatta, RaceDefinition) tracked race}
* from this service. If it was the last {@link RaceDefinition} in its {@link Regatta} and the regatta
* {@link Regatta#isPersistent() is not stored persistently}, the <code>regatta</code> is removed as well and will no
* longer be returned by {@link #getAllRegattas()}. The wind tracking is stopped for <code>race</code>.
* <p>
*
* Any {@link RaceTracker} for which <code>race</race> is the last race tracked that is still reachable
* from {@link #getAllRegattas()} will be {@link RaceTracker#stop() stopped}.
*
* The <code>race</code> will be also removed from all leaderboards containing a column that has <code>race</code>'s
* {@link #getTrackedRace(Regatta, RaceDefinition) corresponding} {@link TrackedRace} as its
* {@link RaceColumn#getTrackedRace(Fleet)}.
*
* @param regatta
* the regatta from which to remove the race
* @param race
* the race to remove
*/
void removeRace(Regatta regatta, RaceDefinition race) throws MalformedURLException, IOException,InterruptedException;
/**
* Stops all {@link RaceTracker}s currently tracking <code>race</code>. 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 <code>race</code> (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<Triple<Regatta, RaceDefinition, String>> 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 <em>all</em> races in the event which
* is hardly ever useful. The returned pair's first component is the event name.
*/
Pair<String, List<RaceRecord>> getTracTracRaceRecords(URL jsonURL) throws IOException, ParseException,
org.json.simple.parser.ParseException, URISyntaxException;
List<com.sap.sailing.domain.swisstimingadapter.RaceRecord> getSwissTimingRaceRecords(String hostname, int port, boolean canSendRequests)
throws InterruptedException, UnknownHostException, IOException, ParseException;
boolean isRaceBeingTracked(RaceDefinition r);
/**
* Creates a new leaderboard with the <code>name</code> specified.
* @param discardThresholds
* Tells the thresholds from which on a next higher number of worst races will be discarded per
* competitor. Example: <code>[3, 6]</code> 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 <code>oldName</code> does not exist in {@link #getLeaderboards()},
* or if a leaderboard with the name <code>newName</code> 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 <code>null</code>, 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<String, LeaderboardGroup> getLeaderboardGroups();
/**
* @param groupName The name of the requested leaderboard group
* @return The leaderboard group with the name <code>groupName</code>, or <code>null</code> if theres no such group
*/
LeaderboardGroup getLeaderboardGroupByName(String groupName);
/**
* Creates a new group with the name <code>groupName</code>, the description <code>desciption</code> and the
* leaderboards with the names in <code>leaderboardNames</code> 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.<br />
* 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<String> leaderboardNames, int[] overallLeaderboardDiscardThresholds, ScoringSchemeType overallLeaderboardScoringSchemeType);
/**
* Removes the group with the name <code>groupName</code> 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 <code>oldName</code> to the <code>newName</code>.<br />
* If there's no group with the name <code>oldName</code> or there's already a group with the name
* <code>newName</code> 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 <code>null</code> 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 <code>null</code>, 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<? extends Series> series, boolean persistent, ScoringScheme scoringScheme);
/**
* Adds <code>raceDefinition</code> 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<String> 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> T apply(RacingEventServiceOperation<T> operation);
void addOperationExecutionListener(OperationExecutionListener listener);
void removeOperationExecutionListener(OperationExecutionListener listener);
/**
* Produces a one-shot serializable copy of those elements required for replication into <code>oos</code> 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.
* <p>
*
* 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.<p>
*
* <b>Caution:</b> 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<Event> getAllEvents();
/**
* Creates a new event with the name <code>eventName</code>, the venue<code>venue</code> and the
* regattas with the names in <code>regattaNames</code> 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.<br />
* @return The new event
*/
Event addEvent(String eventName, String venueName, String publicationUrl, boolean isPublic, List<String> regattaNames);
/**
* Updates a sailing event with the name <code>eventName</code>, the venue<code>venue</code> and the
* regattas with the names in <code>regattaNames</code> 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.<br />
* @return The new event
*/
void updateEvent(String eventName, String venueName, String publicationUrl, boolean isPublic, List<String> regattaNames);
/**
* Renames a sailing event. If a sailing event by the name <code>oldName</code> does not exist in {@link #getEvents()},
* or if a event with the name <code>newName</code> 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.<p>
*
* 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.<p>
*
* 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<String, Leaderboard> getLeaderboards();
/**
* @return a leaderboard whose {@link Leaderboard#getName()} method returns the value of the <code>name</code>
* parameter, or <code>null</code> 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 <code>Event</code> 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 <code>KeyValue.setup</code> is a TracTrac <code>Event</code>, those events
* only manage a single race. In our domain model, we group those races into a single instance of our {@link Regatta}
* class.
* <p>
*
* 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.
* <p>
*
* Note that when the race identified by <code>paramURL</code>, <code>liveURI</code> and <code>storedURI</code> is
* already being tracked, then regardless of the <code>windStore</code> 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 <code>null</code>, 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 <code>race</code> and any corresponding {@link #getTrackedRace(Regatta, RaceDefinition) tracked race}
* from this service. If it was the last {@link RaceDefinition} in its {@link Regatta} and the regatta
* {@link Regatta#isPersistent() is not stored persistently}, the <code>regatta</code> is removed as well and will no
* longer be returned by {@link #getAllRegattas()}. The wind tracking is stopped for <code>race</code>.
* <p>
*
* Any {@link RaceTracker} for which <code>race</race> is the last race tracked that is still reachable
* from {@link #getAllRegattas()} will be {@link RaceTracker#stop() stopped}.
*
* The <code>race</code> will be also removed from all leaderboards containing a column that has <code>race</code>'s
* {@link #getTrackedRace(Regatta, RaceDefinition) corresponding} {@link TrackedRace} as its
* {@link RaceColumn#getTrackedRace(Fleet)}.
*
* @param regatta
* the regatta from which to remove the race
* @param race
* the race to remove
*/
void removeRace(Regatta regatta, RaceDefinition race) throws MalformedURLException, IOException,InterruptedException;
/**
* Stops all {@link RaceTracker}s currently tracking <code>race</code>. 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 <code>race</code> (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<Triple<Regatta, RaceDefinition, String>> 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 <em>all</em> races in the event which
* is hardly ever useful. The returned pair's first component is the event name.
*/
Pair<String, List<RaceRecord>> getTracTracRaceRecords(URL jsonURL) throws IOException, ParseException,
org.json.simple.parser.ParseException, URISyntaxException;
List<com.sap.sailing.domain.swisstimingadapter.RaceRecord> getSwissTimingRaceRecords(String hostname, int port, boolean canSendRequests)
throws InterruptedException, UnknownHostException, IOException, ParseException;
boolean isRaceBeingTracked(RaceDefinition r);
/**
* Creates a new leaderboard with the <code>name</code> specified.
* @param discardThresholds
* Tells the thresholds from which on a next higher number of worst races will be discarded per
* competitor. Example: <code>[3, 6]</code> 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 <code>oldName</code> does not exist in {@link #getLeaderboards()},
* or if a leaderboard with the name <code>newName</code> 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 <code>null</code>, 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<String, LeaderboardGroup> getLeaderboardGroups();
/**
* @param groupName The name of the requested leaderboard group
* @return The leaderboard group with the name <code>groupName</code>, or <code>null</code> if theres no such group
*/
LeaderboardGroup getLeaderboardGroupByName(String groupName);
/**
* Creates a new group with the name <code>groupName</code>, the description <code>desciption</code> and the
* leaderboards with the names in <code>leaderboardNames</code> 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.<br />
* 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<String> leaderboardNames, int[] overallLeaderboardDiscardThresholds, ScoringSchemeType overallLeaderboardScoringSchemeType);
/**
* Removes the group with the name <code>groupName</code> 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 <code>oldName</code> to the <code>newName</code>.<br />
* If there's no group with the name <code>oldName</code> or there's already a group with the name
* <code>newName</code> 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 <code>null</code> 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 <code>null</code>, 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<? extends Series> series, boolean persistent, ScoringScheme scoringScheme);
/**
* Adds <code>raceDefinition</code> 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<String> 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> T apply(RacingEventServiceOperation<T> operation);
void addOperationExecutionListener(OperationExecutionListener listener);
void removeOperationExecutionListener(OperationExecutionListener listener);
/**
* Produces a one-shot serializable copy of those elements required for replication into <code>oos</code> 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.
* <p>
*
* 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.<p>
*
* <b>Caution:</b> 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<Event> getAllEvents();
/**
* Creates a new event with the name <code>eventName</code>, the venue<code>venue</code> and the
* regattas with the names in <code>regattaNames</code> 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.<br />
* @return The new event
*/
Event addEvent(String eventName, String venueName, String publicationUrl, boolean isPublic, List<String> regattaNames);
/**
* Updates a sailing event with the name <code>eventName</code>, the venue<code>venue</code> and the
* regattas with the names in <code>regattaNames</code> 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.<br />
* @return The new event
*/
void updateEvent(String eventName, String venueName, String publicationUrl, boolean isPublic, List<String> regattaNames);
/**
* Renames a sailing event. If a sailing event by the name <code>oldName</code> does not exist in {@link #getEvents()},
* or if a event with the name <code>newName</code> 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();
}
@@ -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);
@@ -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<TrackedRace> {
* {@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 <code>null</code>, causing the use of an
* {@link EmptyRaceCommitteeStore}.
*/
private transient final RaceCommitteeStore raceCommitteeStore;
/**
* @param windStore
* if <code>null</code>, an {@link EmptyWindStore} will be used. Note that the {@link #windStore} field
* won't be serialized. A receiver of this operation will therefore always use an {@link EmptyWindStore}.
* @param raceCommitteeStore
* if <code>null</code>, 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<TrackedRace> {
@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);
}
}