diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInEventTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInEventTest.java new file mode 100755 index 00000000000..c3cd343db13 --- /dev/null +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInEventTest.java @@ -0,0 +1,65 @@ +package com.sap.sailing.domain.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; + +import java.io.FileNotFoundException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; + +import org.junit.Before; +import org.junit.Test; + +import com.sap.sailing.domain.tracking.impl.EmptyWindStore; +import com.sap.sailing.domain.tractracadapter.DomainFactory; +import com.sap.sailing.domain.tractracadapter.RaceTracker; +import com.sap.sailing.domain.tractracadapter.impl.DomainFactoryImpl; + +public class MultipleClassesInEventTest { + private DomainFactory domainFactory; + + @Before + public void setUp() { + domainFactory = new DomainFactoryImpl(); + } + + @Test + public void testLoadTwoRacesWithEqualEventNameButDifferentClasses() throws MalformedURLException, FileNotFoundException, URISyntaxException { + RaceTracker kiwotest1 = domainFactory + .createRaceTracker( + new URL( + "http://germanmaster.traclive.dk/events/event_20110505_SailingTea/clientparams.php?event=event_20110505_SailingTea&race=cce678c8-97e6-11e0-9aed-406186cbf87c"), + new URI("tcp://germanmaster.traclive.dk:4400"), new URI("tcp://germanmaster.traclive.dk:4401"), + EmptyWindStore.INSTANCE); + RaceTracker kiwotest2 = domainFactory + .createRaceTracker( + new URL( + "http://germanmaster.traclive.dk/events/event_20110505_SailingTea/clientparams.php?event=event_20110505_SailingTea&race=11290bd6-97e7-11e0-9aed-406186cbf87c"), + new URI("tcp://germanmaster.traclive.dk:4400"), new URI("tcp://germanmaster.traclive.dk:4401"), + EmptyWindStore.INSTANCE); + RaceTracker kiwotest3 = domainFactory + .createRaceTracker( + new URL( + "http://germanmaster.traclive.dk/events/event_20110505_SailingTea/clientparams.php?event=event_20110505_SailingTea&race=39635b24-97e7-11e0-9aed-406186cbf87c"), + new URI("tcp://germanmaster.traclive.dk:4400"), new URI("tcp://germanmaster.traclive.dk:4401"), + EmptyWindStore.INSTANCE); + RaceTracker weym470may112014_2 = domainFactory + .createRaceTracker( + new URL( + "http://germanmaster.traclive.dk/events/event_20110505_SailingTea/clientparams.php?event=event_20110505_SailingTea&race=04498426-7dfd-11e0-8236-406186cbf87c"), + new URI("tcp://germanmaster.traclive.dk:4400"), new URI("tcp://germanmaster.traclive.dk:4401"), + EmptyWindStore.INSTANCE); + + assertEquals("STG", kiwotest1.getEvent().getBoatClass().getName()); + assertEquals("505", kiwotest2.getEvent().getBoatClass().getName()); + assertEquals("49er", kiwotest3.getEvent().getBoatClass().getName()); + assertEquals("STG", weym470may112014_2.getEvent().getBoatClass().getName()); + assertSame(weym470may112014_2.getEvent(), kiwotest1.getEvent()); + assertNotSame(kiwotest1.getEvent(), kiwotest2.getEvent()); + assertNotSame(kiwotest1.getEvent(), kiwotest3.getEvent()); + assertNotSame(kiwotest2.getEvent(), kiwotest3.getEvent()); + } +} diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java index 8bb08afc795..f4e232b2603 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java @@ -24,8 +24,12 @@ import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.TrackedEvent; +import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.WindStore; +import com.sap.sailing.domain.tracking.WindTrack; +import com.sap.sailing.domain.tracking.impl.EmptyWindStore; import com.sap.sailing.domain.tractracadapter.impl.DomainFactoryImpl; +import com.sap.sailing.domain.tractracadapter.impl.RaceCourseReceiver; import com.tractrac.clientmodule.Competitor; import com.tractrac.clientmodule.CompetitorClass; import com.tractrac.clientmodule.ControlPoint; @@ -70,9 +74,34 @@ public interface DomainFactory { /** * Creates an {@link com.sap.sailing.domain.base.Event event} from a * TracTrac event description. It doesn't have {@link RaceDefinition}s yet. + * A new {@link com.sap.sailing.domain.base.Event} is created if no event by + * an equal name with a boat class with an equal name as the event's + * boat class exists yet. */ com.sap.sailing.domain.base.Event createEvent(Event event); + /** + * Creates a race tracked for the specified URL/URIs and starts receiving all available existing and future push + * data from there. Receiving continues until {@link RaceTracker#stop()} is called. + *

+ * + * A race tracker uses the paramURL for the TracTrac Java client to register for push data about one + * race. The {@link RaceDefinition} for that race, however, isn't created until the {@link Course} has been + * received. Therefore, the {@link RaceCourseReceiver} will create the {@link RaceDefinition} and will add it to the + * {@link com.sap.sailing.domain.base.Event}. + *

+ * + * The link to the {@link RaceDefinition} is created in the {@link DomainFactory} when the + * {@link RaceCourseReceiver} creates the {@link TrackedRace} object. Starting then, the {@link DomainFactory} will + * respond with the {@link RaceDefinition} when its {@link DomainFactory#getRace(Event)} is called with the TracTrac + * {@link Event} as argument that is used for its tracking. + *

+ * + * @param windStore + * 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. + */ RaceTracker createRaceTracker(URL paramURL, URI liveURI, URI storedURI, WindStore windStore) throws MalformedURLException, FileNotFoundException, URISyntaxException; diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java index cc9a3b5ad15..6d487d75d7d 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java @@ -61,6 +61,7 @@ import com.sap.sailing.domain.tractracadapter.JSONService; import com.sap.sailing.domain.tractracadapter.RaceTracker; import com.sap.sailing.domain.tractracadapter.Receiver; import com.sap.sailing.domain.tractracadapter.ReceiverType; +import com.sap.sailing.util.Util.Pair; import com.tractrac.clientmodule.CompetitorClass; import com.tractrac.clientmodule.ControlPoint; import com.tractrac.clientmodule.Race; @@ -99,10 +100,10 @@ public class DomainFactoryImpl implements DomainFactory { private final Map classCache = new HashMap(); /** - * Caches events by their name + * Caches events by their name and their boat class's name */ - private final Map eventCache = - new HashMap(); + private final Map, com.sap.sailing.domain.base.Event> eventCache = + new HashMap, com.sap.sailing.domain.base.Event>(); private final Map tractracEventToRaceDefinitionMap = new HashMap(); @@ -292,10 +293,26 @@ public class DomainFactoryImpl implements DomainFactory { @Override public Event createEvent(com.tractrac.clientmodule.Event event) { synchronized (eventCache) { - Event result = eventCache.get(event.getName()); + // FIXME Dialog with Lasse by Skype on 2011-06-17: + // [6:20:04 PM] Axel Uhl: Lasse, can Event.getCompetitorClassList() ever produce more than one result? + // [6:20:20 PM] Axel Uhl: Or is it similar to Event.getRaceList() which always delivers one Race? + // [6:22:19 PM] Lasse Staffensen: It can deliver several classes, if more classes are present in a race. + // [6:27:20 PM] Axel Uhl: Will that happen at Kiel Week? + // [6:27:58 PM] Lasse Staffensen: No + // [6:28:34 PM] Axel Uhl: Good :) + // This means that currently it is permissible to assume that we'll get at most one + // boat class per TracTrac event. Generally, however, we have to assume that + // one TracTrac event may map to multiple domain Event objects with one BoatClass each + Collection competitorClassList = event.getCompetitorClassList(); + BoatClass boatClass = null; + if (competitorClassList != null && !competitorClassList.isEmpty()) { + boatClass = getBoatClass(competitorClassList.iterator().next()); + } + Pair key = new Pair(event.getName(), boatClass==null?null:boatClass.getName()); + Event result = eventCache.get(key); if (result == null) { - result = new EventImpl(event.getName()); - eventCache.put(event.getName(), result); + result = new EventImpl(event.getName(), boatClass); + eventCache.put(key, result); } return result; } diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/MarkPositionReceiver.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/MarkPositionReceiver.java index 25d793aa2aa..c8c70cd8074 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/MarkPositionReceiver.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/MarkPositionReceiver.java @@ -47,6 +47,12 @@ public class MarkPositionReceiver extends AbstractReceiverWithQueue 1) { + logger.warning("Received event "+tractracEvent.getName()+" that has more than one race ("+tractracEvent.getRaceList().size()+")"); + } final Race race = tractracEvent.getRaceList().iterator().next(); new Thread("MarkPositionReceiver waiting for RaceDefinition for "+race.getName()) { public void run() { diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/EventImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/EventImpl.java index 89b9579f863..1c5f85978a4 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/EventImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/EventImpl.java @@ -1,7 +1,6 @@ package com.sap.sailing.domain.base.impl; import java.util.HashSet; -import java.util.Iterator; import java.util.Set; import com.sap.sailing.domain.base.BoatClass; @@ -11,10 +10,12 @@ import com.sap.sailing.domain.base.RaceDefinition; public class EventImpl extends NamedImpl implements Event { private final Set races; + private final BoatClass boatClass; - public EventImpl(String name) { + public EventImpl(String name, BoatClass boatClass) { super(name); races = new HashSet(); + this.boatClass = boatClass; } @Override @@ -32,12 +33,7 @@ public class EventImpl extends NamedImpl implements Event { @Override public BoatClass getBoatClass() { - Iterator raceIter = getAllRaces().iterator(); - if (raceIter.hasNext()) { - return raceIter.next().getBoatClass(); - } else { - return null; - } + return boatClass; } @Override diff --git a/java/com.tractrac.clientmodule/.classpath b/java/com.tractrac.clientmodule/.classpath index ff31d9ec76d..8b1610c2c82 100755 --- a/java/com.tractrac.clientmodule/.classpath +++ b/java/com.tractrac.clientmodule/.classpath @@ -3,7 +3,7 @@ - +