Merge branch 'master' of ssh://195.227.10.246/home/trac/git

This commit is contained in:
Simon Pamies committed 2011-06-13 20:41:59 +02:00
commit 193aa9d04a
7 files changed
+127 -38

No files matched your search

@@ -25,4 +25,9 @@ public interface RaceHandle {
DynamicTrackedEvent getTrackedEvent();
/**
* The tracker managing the tracking of the race to which this is a handle.
*/
RaceTracker getRaceTracker();
}
@@ -2,12 +2,22 @@ package com.sap.sailing.domain.tractracadapter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.tracking.DynamicTrackedEvent;
import com.sap.sailing.domain.tracking.TrackedEvent;
import com.sap.sailing.domain.tracking.WindStore;
import com.sap.sailing.util.Util.Triple;
public interface RaceTracker {
/**
* Stops tracking the race and removes the {@link TrackedRace} object one gets from calling
* {@link #getTrackedEvent()}.{@link TrackedEvent#getTrackedRace(RaceDefinition) getTrackedRace(}{@link #getRace() getRace())}
* from the {@link #getTrackedEvent() tracked event}.
*/
void stop() throws MalformedURLException, IOException, InterruptedException;
com.sap.sailing.domain.base.Event getEvent();
@@ -22,5 +32,9 @@ public interface RaceTracker {
RaceHandle getRaceHandle();
DynamicTrackedEvent getTrackedEvent();
WindStore getWindStore();
Triple<URL, URI, URI> getURLs();
}
@@ -4,17 +4,20 @@ import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.tracking.DynamicTrackedEvent;
import com.sap.sailing.domain.tractracadapter.DomainFactory;
import com.sap.sailing.domain.tractracadapter.RaceHandle;
import com.sap.sailing.domain.tractracadapter.RaceTracker;
import com.tractrac.clientmodule.Event;
public class RaceHandleImpl implements RaceHandle {
private final Event tractracEvent;
private final DomainFactory domainFactory;
private final DynamicTrackedEvent trackedEvent;
private final RaceTracker raceTracker;
public RaceHandleImpl(DomainFactory domainFactory, Event tractracEvent, DynamicTrackedEvent trackedEvent) {
public RaceHandleImpl(DomainFactory domainFactory, Event tractracEvent, DynamicTrackedEvent trackedEvent, RaceTracker raceTracker) {
this.domainFactory = domainFactory;
this.tractracEvent = tractracEvent;
this.trackedEvent = trackedEvent;
this.raceTracker = raceTracker;
}
@Override
@@ -32,5 +35,10 @@ public class RaceHandleImpl implements RaceHandle {
public DynamicTrackedEvent getTrackedEvent() {
return trackedEvent;
}
@Override
public RaceTracker getRaceTracker() {
return raceTracker;
}
}
@@ -22,6 +22,7 @@ import com.sap.sailing.domain.tractracadapter.DomainFactory;
import com.sap.sailing.domain.tractracadapter.RaceHandle;
import com.sap.sailing.domain.tractracadapter.RaceTracker;
import com.sap.sailing.domain.tractracadapter.Receiver;
import com.sap.sailing.util.Util.Triple;
import com.tractrac.clientmodule.Event;
import com.tractrac.clientmodule.data.DataController;
import com.tractrac.clientmodule.data.DataController.Listener;
@@ -35,6 +36,8 @@ public class RaceTrackerImpl implements Listener, RaceTracker {
private final Set<Receiver> receivers;
private final DomainFactory domainFactory;
private final DynamicTrackedEvent trackedEvent;
private final WindStore windStore;
private final Triple<URL, URI, URI> urls;
/**
* Creates a race tracked for the specified URL/URIs and starts receiving all available existing and future push
@@ -60,6 +63,8 @@ public class RaceTrackerImpl implements Listener, RaceTracker {
*/
protected RaceTrackerImpl(DomainFactory domainFactory, URL paramURL, URI liveURI, URI storedURI, WindStore windStore)
throws URISyntaxException, MalformedURLException, FileNotFoundException {
urls = new Triple<URL, URI, URI>(paramURL, liveURI, storedURI);
this.windStore = windStore;
this.domainFactory = domainFactory;
// Read event data from configuration file
tractracEvent = KeyValue.setup(paramURL);
@@ -87,6 +92,16 @@ public class RaceTrackerImpl implements Listener, RaceTracker {
addListenersForStoredDataAndStartController(typeControllers);
}
@Override
public Triple<URL, URI, URI> getURLs() {
return urls;
}
@Override
public WindStore getWindStore() {
return windStore;
}
@Override
public DynamicTrackedEvent getTrackedEvent() {
return trackedEvent;
@@ -94,7 +109,7 @@ public class RaceTrackerImpl implements Listener, RaceTracker {
@Override
public RaceHandle getRaceHandle() {
return new RaceHandleImpl(domainFactory, tractracEvent, getTrackedEvent());
return new RaceHandleImpl(domainFactory, tractracEvent, getTrackedEvent(), this);
}
@Override
@@ -2,6 +2,7 @@ package com.sap.sailing.server.test;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -83,7 +84,7 @@ public class RaceTrackerTest {
@Test
public void testStopTracking() throws MalformedURLException, IOException, InterruptedException, URISyntaxException {
TrackedEvent oldTrackedEvent = raceHandle.getTrackedEvent();
TrackedRace oldTrackedRace = oldTrackedEvent.getTrackedRaces().iterator().next();
TrackedRace oldTrackedRace = getTrackedRace(oldTrackedEvent);
service.stopTracking(raceHandle.getEvent());
RaceHandle myRaceHandle = service.addRace(paramUrl, liveUri, storedUri, EmptyWindStore.INSTANCE);
TrackedEvent newTrackedEvent = myRaceHandle.getTrackedEvent();
@@ -91,4 +92,20 @@ public class RaceTrackerTest {
// expecting a new tracked race to be created when starting over with tracking
assertNotSame(oldTrackedRace, newTrackedRace);
}
/**
* This test asserts that tracking the same race twice doesn't create another tracker and in particular no
* new tracked event / tracked race.
*/
@Test
public void testTrackingSameRaceWithoutStopping() throws MalformedURLException, IOException, InterruptedException, URISyntaxException {
TrackedEvent oldTrackedEvent = raceHandle.getTrackedEvent();
TrackedRace oldTrackedRace = getTrackedRace(oldTrackedEvent);
RaceHandle myRaceHandle = service.addRace(paramUrl, liveUri, storedUri, EmptyWindStore.INSTANCE);
TrackedEvent newTrackedEvent = myRaceHandle.getTrackedEvent();
TrackedRace newTrackedRace = getTrackedRace(newTrackedEvent);
// expecting a new tracked race to be created when starting over with tracking
assertSame(oldTrackedRace, newTrackedRace);
assertSame(raceHandle.getRaceTracker(), myRaceHandle.getRaceTracker());
}
}
@@ -46,10 +46,11 @@ public interface RacingEventService {
DomainFactory getDomainFactory();
/**
* Defines the event and for each race listed in the JSON document creates a {@link RaceTracker} that starts
* tracking the respective race. The {@link RaceDefinition}s obtained this way are all grouped into the single
* {@link Event} 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.
* Defines the event and for each race listed in the JSON document that is not already being tracked by this service
* creates a {@link RaceTracker} that starts tracking the respective race. The {@link RaceDefinition}s obtained this
* way are all grouped into the single {@link Event} 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
@@ -60,22 +61,31 @@ public interface RacingEventService {
URISyntaxException, IOException, ParseException, org.json.simple.parser.ParseException;
/**
* 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 Event} class.
* 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 Event}
* class.
* <p>
*
* If this is the first race of an event, the {@link Event} is created as well. If the {@link RaceDefinition} for
* the race already exists, it isn't created again. Also, if a {@link RaceTracker} for the given race already
* exists, it is not added again.
* 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)}).
*/
RaceHandle addRace(URL paramURL, URI liveURI, URI storedURI, WindStore windStore) throws MalformedURLException, FileNotFoundException,
URISyntaxException;
/**
* Stops tracking all races of the event specified. This will also stop tracking wind for all races of this event.
* See {@link #stopTrackingWind(Event, RaceDefinition)}.
* See {@link #stopTrackingWind(Event, RaceDefinition)}. If there were multiple calls to
* {@link #addRace(URL, URI, URI, WindStore)} with an equal combination of URLs/URIs, the {@link RaceTracker}
* 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(Event event) throws MalformedURLException, IOException, InterruptedException;
@@ -16,6 +16,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import com.sap.sailing.declination.DeclinationService;
import com.sap.sailing.domain.base.Event;
@@ -34,11 +35,13 @@ import com.sap.sailing.util.Util.Pair;
import com.sap.sailing.util.Util.Triple;
public class RacingEventServiceImpl implements RacingEventService {
private static final Logger logger = Logger.getLogger(RacingEventServiceImpl.class.getName());
private final DomainFactory domainFactory;
private final Map<String, Event> eventsByName;
private final Map<Event, Set<RaceTracker>> raceTrackers;
private final Map<Event, Set<RaceTracker>> raceTrackersByEvent;
/**
* Remembers the wind tracker and the port on which the UDP receiver with which the wind tracker is
@@ -46,13 +49,19 @@ public class RacingEventServiceImpl implements RacingEventService {
*/
private final Map<RaceDefinition, Pair<WindTracker, Integer>> windTrackers;
/**
* Remembers the trackers by paramURL/liveURI/storedURI to avoid duplication
*/
private final Map<Triple<URL, URI, URI>, RaceTracker> raceTrackersByURLs;
private final Map<Integer, UDPExpeditionReceiver> windReceivers;
public RacingEventServiceImpl() {
domainFactory = DomainFactory.INSTANCE;
eventsByName = new HashMap<String, Event>();
raceTrackers = new HashMap<Event, Set<RaceTracker>>();
raceTrackersByEvent = new HashMap<Event, Set<RaceTracker>>();
windTrackers = new HashMap<RaceDefinition, Pair<WindTracker, Integer>>();
raceTrackersByURLs = new HashMap<Triple<URL, URI, URI>, RaceTracker>();
windReceivers = new HashMap<Integer, UDPExpeditionReceiver>();
}
@@ -85,33 +94,44 @@ public class RacingEventServiceImpl implements RacingEventService {
@Override
public RaceHandle addRace(URL paramURL, URI liveURI, URI storedURI, WindStore windStore) throws MalformedURLException, FileNotFoundException,
URISyntaxException {
// FIXME check if a tracker for this parameter combination already exists and if so, return its race handle
RaceTracker tracker = getDomainFactory().createRaceTracker(paramURL, liveURI, storedURI, windStore);
Set<RaceTracker> trackers = raceTrackers.get(tracker.getEvent());
if (trackers == null) {
trackers = new HashSet<RaceTracker>();
raceTrackers.put(tracker.getEvent(), trackers);
}
trackers.add(tracker);
String eventName = tracker.getEvent().getName();
Event eventWithName = eventsByName.get(eventName);
if (eventWithName != null) {
if (eventWithName != tracker.getEvent()) {
throw new RuntimeException("Internal error. Two Event objects with equal name "+eventName);
Triple<URL, URI, URI> key = new Triple<URL, URI, URI>(paramURL, liveURI, storedURI);
RaceTracker tracker = raceTrackersByURLs.get(key);
if (tracker == null) {
tracker = getDomainFactory().createRaceTracker(paramURL, liveURI, storedURI, windStore);
raceTrackersByURLs.put(key, tracker);
Set<RaceTracker> trackers = raceTrackersByEvent.get(tracker.getEvent());
if (trackers == null) {
trackers = new HashSet<RaceTracker>();
raceTrackersByEvent.put(tracker.getEvent(), trackers);
}
trackers.add(tracker);
String eventName = tracker.getEvent().getName();
Event eventWithName = eventsByName.get(eventName);
if (eventWithName != null) {
if (eventWithName != tracker.getEvent()) {
throw new RuntimeException("Internal error. Two Event objects with equal name "+eventName);
}
} else {
eventsByName.put(eventName, tracker.getEvent());
}
} else {
eventsByName.put(eventName, tracker.getEvent());
WindStore existingTrackersWindStore = tracker.getWindStore();
if (!existingTrackersWindStore.equals(windStore)) {
logger.warning("Wind store mismatch. Requested wind store: "+windStore+
". Wind store in use by existing tracker: "+existingTrackersWindStore);
}
}
return tracker.getRaceHandle();
}
@Override
public void stopTracking(Event event) throws MalformedURLException, IOException, InterruptedException {
if (raceTrackers.containsKey(event)) {
for (RaceTracker raceTracker : raceTrackers.get(event)) {
raceTracker.stop();
if (raceTrackersByEvent.containsKey(event)) {
for (RaceTracker raceTracker : raceTrackersByEvent.get(event)) {
raceTracker.stop(); // this also removes the TrackedRace from trackedEvent
raceTrackersByURLs.remove(raceTracker.getURLs());
}
raceTrackers.remove(event);
raceTrackersByEvent.remove(event);
}
if (event != null && event.getName() != null) {
eventsByName.remove(event.getName());
@@ -123,19 +143,19 @@ public class RacingEventServiceImpl implements RacingEventService {
@Override
public void stopTracking(Event event, RaceDefinition race) throws MalformedURLException, IOException, InterruptedException {
if (raceTrackers.containsKey(event)) {
Iterator<RaceTracker> trackerIter = raceTrackers.get(event).iterator();
if (raceTrackersByEvent.containsKey(event)) {
Iterator<RaceTracker> trackerIter = raceTrackersByEvent.get(event).iterator();
while (trackerIter.hasNext()) {
RaceTracker raceTracker = trackerIter.next();
if (raceTracker.getRace() == race) {
raceTracker.stop();
raceTracker.stop(); // this also removes the TrackedRace from trackedEvent
trackerIter.remove();
raceTrackersByURLs.remove(raceTracker.getURLs());
}
}
}
// FIXME remove TrackedRace from TrackedEvent
// if the last tracked race was removed, remove the entire event
if (raceTrackers.get(event).isEmpty()) {
if (raceTrackersByEvent.get(event).isEmpty()) {
stopTracking(event);
}
}