From c2cbc75754a4a1f47c44065125214fee5c039d3f Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 2 Apr 2015 11:15:35 +0200 Subject: [PATCH 1/3] some comments and formatting --- .../domain/persistence/impl/DomainObjectFactoryImpl.java | 2 ++ .../tracking/impl/TrackBasedEstimationWindTrackImpl.java | 6 ++---- .../com/sap/sailing/gwt/ui/server/SailingServiceImpl.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java index bd3f36827d8..9c5bc3eb2b4 100755 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java @@ -857,6 +857,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { DBCollection windTracks = database.getCollection(CollectionNames.WIND_TRACKS.name()); ensureIndicesOnWindTracks(windTracks); BasicDBObject queryById = new BasicDBObject(); + // the default query is by the RACE_ID key: queryById.put(FieldNames.RACE_ID.name(), race.getId()); if (constrainToWindSource != null) { queryById.put(FieldNames.WIND_SOURCE_NAME.name(), constrainToWindSource.name()); @@ -864,6 +865,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { for (DBObject dbWind : windTracks.find(queryById)) { loadWindFix(result, dbWind, millisecondsOverWhichToAverageWind); } + // Additionally check for legacy wind fixes stored with the old EVENT_NAME key; if any are found, migrate them BasicDBObject queryByName = new BasicDBObject(); queryByName.put(FieldNames.EVENT_NAME.name(), regattaName); queryByName.put(FieldNames.RACE_NAME.name(), race.getName()); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackBasedEstimationWindTrackImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackBasedEstimationWindTrackImpl.java index 3a3db8e0522..5adf80ef1c6 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackBasedEstimationWindTrackImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackBasedEstimationWindTrackImpl.java @@ -617,10 +617,8 @@ public class TrackBasedEstimationWindTrackImpl extends VirtualWindTrackImpl { private void invalidateForNewWind(Wind wind, WindSource windSource) { WindTrack windTrack = getTrackedRace().getOrCreateWindTrack(windSource); // check what the next fixes before and after the one affected are; if they are further than the - // averagingInterval - // away, extend the invalidation interval accordingly because the entire span up to the next fix may be - // influenced - // by adding/removing a fix in a sparsely occupied track. See + // averagingInterval away, extend the invalidation interval accordingly because the entire span up + // to the next fix may be influenced by adding/removing a fix in a sparsely occupied track. See // WindTrackImpl.getAveragedWindUnsynchronized(Position p, TimePoint at) long averagingInterval = getTrackedRace().getMillisecondsOverWhichToAverageWind(); final TimePoint timePoint = wind.getTimePoint(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 43531ede3e2..ab78ba29b86 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -1605,7 +1605,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } Wind wind = new WindImpl(p, at, speedWithBearing); Iterable webWindSources = trackedRace.getWindSources(WindSourceType.WEB); - if(Util.size(webWindSources) == 0) { + if (Util.size(webWindSources) == 0) { // create a new WEB wind source if not available trackedRace.recordWind(wind, new WindSourceImpl(WindSourceType.WEB)); } else { From 2e8f5dd9dde252e6eaa63422b033e587cde9dc32 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 2 Apr 2015 14:45:01 +0200 Subject: [PATCH 2/3] fixing locking issue in TrackedRaceImpl.getCurrentLeg(...) which could have lead to NPEs during course updates --- .../domain/tracking/impl/TrackedRaceImpl.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java index 60d244a0821..50d348a935a 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java @@ -1249,13 +1249,19 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl DummyMarkPassingWithTimePointOnly markPassingTimePoint = new DummyMarkPassingWithTimePointOnly(timePoint); TrackedLegOfCompetitor result = null; if (!competitorMarkPassings.isEmpty()) { - MarkPassing lastMarkPassingAtOfBeforeTimePoint = competitorMarkPassings.floor(markPassingTimePoint); - if (lastMarkPassingAtOfBeforeTimePoint != null) { - Waypoint waypointPassedLastAtOrBeforeTimePoint = lastMarkPassingAtOfBeforeTimePoint.getWaypoint(); - // don't return a leg if competitor has already finished last leg and therefore the race - if (waypointPassedLastAtOrBeforeTimePoint != getRace().getCourse().getLastWaypoint()) { - result = getTrackedLegStartingAt(waypointPassedLastAtOrBeforeTimePoint).getTrackedLeg(competitor); + final Course course = getRace().getCourse(); + course.lockForRead(); + try { + MarkPassing lastMarkPassingAtOfBeforeTimePoint = competitorMarkPassings.floor(markPassingTimePoint); + if (lastMarkPassingAtOfBeforeTimePoint != null) { + Waypoint waypointPassedLastAtOrBeforeTimePoint = lastMarkPassingAtOfBeforeTimePoint.getWaypoint(); + // don't return a leg if competitor has already finished last leg and therefore the race + if (waypointPassedLastAtOrBeforeTimePoint != course.getLastWaypoint()) { + result = getTrackedLegStartingAt(waypointPassedLastAtOrBeforeTimePoint).getTrackedLeg(competitor); + } } + } finally { + course.unlockAfterRead(); } } return result; From 6c97c93172e28ddb444bbea0855a8ff31f9b6101 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 2 Apr 2015 15:05:25 +0200 Subject: [PATCH 3/3] obtain read lock on course before iterating over the tracked legs in RegattasResource --- .../gateway/jaxrs/api/RegattasResource.java | 212 +++++++++--------- 1 file changed, 111 insertions(+), 101 deletions(-) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java index e1d661415ba..589ad7c701a 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java @@ -724,109 +724,119 @@ public class RegattasResource extends AbstractSailingServerResource { .getStartOfRace().asMillis()); JSONArray jsonLegs = new JSONArray(); - for (TrackedLeg leg : trackedRace.getTrackedLegs()) { - JSONObject jsonLeg = new JSONObject(); - jsonLeg.put("from", leg.getLeg().getFrom().getName()); - jsonLeg.put("fromWaypointId", leg.getLeg().getFrom().getId()); - jsonLeg.put("to", leg.getLeg().getTo().getName()); - jsonLeg.put("toWaypointId", leg.getLeg().getTo().getId()); - try { - jsonLeg.put("upOrDownwindLeg", leg.isUpOrDownwindLeg(timePoint)); - } catch (NoWindException e) { - // no wind, then it's simply no upwind or downwind leg - jsonLeg.put("upOrDownwindLeg", "false"); - } - JSONArray jsonCompetitors = new JSONArray(); - Map ranks = leg.getRanks(timePoint); - for (Competitor competitor : trackedRace.getRace().getCompetitors()) { - JSONObject jsonCompetitorInLeg = new JSONObject(); - TrackedLegOfCompetitor trackedLegOfCompetitor = leg.getTrackedLeg(competitor); - if (trackedLegOfCompetitor != null) { - jsonCompetitorInLeg.put("id", competitor.getId() != null ? competitor.getId().toString() - : null); - jsonCompetitorInLeg.put("name", competitor.getName()); - jsonCompetitorInLeg.put("sailNumber", competitor.getBoat().getSailID()); - jsonCompetitorInLeg.put("color", competitor.getColor() != null ? competitor.getColor() - .getAsHtml() : null); - - Speed averageSpeedOverGround = trackedLegOfCompetitor.getAverageSpeedOverGround(timePoint); - if (averageSpeedOverGround != null) { - jsonCompetitorInLeg.put("averageSOG-kts", UnitSerializationUtil.knotsDecimalFormatter - .format(averageSpeedOverGround.getKnots())); - } - try { - Integer numberOfTacks = trackedLegOfCompetitor.getNumberOfTacks(timePoint, /* waitForLatest */ - false); - Integer numberOfJibes = trackedLegOfCompetitor.getNumberOfJibes(timePoint, /* waitForLatest */ - false); - Integer numberOfPenaltyCircles = trackedLegOfCompetitor.getNumberOfPenaltyCircles( - timePoint, /* waitForLatest */false); - jsonCompetitorInLeg.put("tacks", numberOfTacks); - jsonCompetitorInLeg.put("jibes", numberOfJibes); - jsonCompetitorInLeg.put("penaltyCircles", numberOfPenaltyCircles); - } catch (NoWindException e) { - logger.log(Level.FINE, - "No wind information while trying to determing maneuvers for competitor " - + competitor.getName(), e); - } - - TimePoint startTime = trackedLegOfCompetitor.getStartTime(); - TimePoint finishTime = trackedLegOfCompetitor.getFinishTime(); - TimePoint startOfRace = trackedRace.getStartOfRace(); - // between the start of the race and the start of the first leg we have no 'timeSinceGun' - // for the competitor - if (startOfRace != null && startTime != null) { - long timeSinceGun = -1; - if (finishTime != null) { - timeSinceGun = finishTime.asMillis() - startOfRace.asMillis(); - } else { - timeSinceGun = timePoint.asMillis() - startOfRace.asMillis(); - } - if (timeSinceGun > 0) { - jsonCompetitorInLeg.put("timeSinceGun-ms", timeSinceGun); - } - Distance distanceSinceGun = trackedRace.getTrack(competitor).getDistanceTraveled( - startOfRace, finishTime != null ? finishTime : timePoint); - if (distanceSinceGun != null) { - jsonCompetitorInLeg.put("distanceSinceGun-m", distanceSinceGun.getMeters()); - } - } - - Distance distanceTraveled = trackedLegOfCompetitor.getDistanceTraveled(timePoint); - if (distanceTraveled != null) { - jsonCompetitorInLeg.put("distanceTraveled-m", - UnitSerializationUtil.distanceDecimalFormatter.format(distanceTraveled - .getMeters())); - } - Distance distanceTraveledIncludingGateStart = trackedLegOfCompetitor.getDistanceTraveledConsideringGateStart(timePoint); - if (distanceTraveledIncludingGateStart != null) { - jsonCompetitorInLeg.put("distanceTraveledIncludingGateStart-m", - UnitSerializationUtil.distanceDecimalFormatter - .format(distanceTraveledIncludingGateStart.getMeters())); - } - try { - Integer rank = ranks.get(competitor); - jsonCompetitorInLeg.put("rank", rank); - } catch (RuntimeException re) { - if (re.getCause() != null && re.getCause() instanceof NoWindException) { - // well, we don't know the wind direction, so we can't compute a ranking - } else { - throw re; - } - } - try { - jsonCompetitorInLeg.put("gapToLeader-s", trackedLegOfCompetitor - .getGapToLeaderInSeconds(timePoint, WindPositionMode.LEG_MIDDLE)); - } catch (NoWindException e1) { - // well, we don't know the wind direction... then no gap to leader will be shown... - } - jsonCompetitorInLeg.put("started", trackedLegOfCompetitor.hasStartedLeg(timePoint)); - jsonCompetitorInLeg.put("finished", trackedLegOfCompetitor.hasFinishedLeg(timePoint)); - jsonCompetitors.add(jsonCompetitorInLeg); + Course course = trackedRace.getRace().getCourse(); + course.lockForRead(); + try { + for (TrackedLeg leg : trackedRace.getTrackedLegs()) { + JSONObject jsonLeg = new JSONObject(); + jsonLeg.put("from", leg.getLeg().getFrom().getName()); + jsonLeg.put("fromWaypointId", leg.getLeg().getFrom().getId()); + jsonLeg.put("to", leg.getLeg().getTo().getName()); + jsonLeg.put("toWaypointId", leg.getLeg().getTo().getId()); + try { + jsonLeg.put("upOrDownwindLeg", leg.isUpOrDownwindLeg(timePoint)); + } catch (NoWindException e) { + // no wind, then it's simply no upwind or downwind leg + jsonLeg.put("upOrDownwindLeg", "false"); } + JSONArray jsonCompetitors = new JSONArray(); + Map ranks = leg.getRanks(timePoint); + for (Competitor competitor : trackedRace.getRace().getCompetitors()) { + JSONObject jsonCompetitorInLeg = new JSONObject(); + TrackedLegOfCompetitor trackedLegOfCompetitor = leg.getTrackedLeg(competitor); + if (trackedLegOfCompetitor != null) { + jsonCompetitorInLeg.put("id", competitor.getId() != null ? competitor.getId() + .toString() : null); + jsonCompetitorInLeg.put("name", competitor.getName()); + jsonCompetitorInLeg.put("sailNumber", competitor.getBoat().getSailID()); + jsonCompetitorInLeg.put("color", competitor.getColor() != null ? competitor.getColor() + .getAsHtml() : null); + + Speed averageSpeedOverGround = trackedLegOfCompetitor + .getAverageSpeedOverGround(timePoint); + if (averageSpeedOverGround != null) { + jsonCompetitorInLeg.put("averageSOG-kts", + UnitSerializationUtil.knotsDecimalFormatter.format(averageSpeedOverGround + .getKnots())); + } + try { + Integer numberOfTacks = trackedLegOfCompetitor.getNumberOfTacks(timePoint, /* waitForLatest */ + false); + Integer numberOfJibes = trackedLegOfCompetitor.getNumberOfJibes(timePoint, /* waitForLatest */ + false); + Integer numberOfPenaltyCircles = trackedLegOfCompetitor.getNumberOfPenaltyCircles( + timePoint, /* waitForLatest */false); + jsonCompetitorInLeg.put("tacks", numberOfTacks); + jsonCompetitorInLeg.put("jibes", numberOfJibes); + jsonCompetitorInLeg.put("penaltyCircles", numberOfPenaltyCircles); + } catch (NoWindException e) { + logger.log(Level.FINE, + "No wind information while trying to determing maneuvers for competitor " + + competitor.getName(), e); + } + + TimePoint startTime = trackedLegOfCompetitor.getStartTime(); + TimePoint finishTime = trackedLegOfCompetitor.getFinishTime(); + TimePoint startOfRace = trackedRace.getStartOfRace(); + // between the start of the race and the start of the first leg we have no + // 'timeSinceGun' + // for the competitor + if (startOfRace != null && startTime != null) { + long timeSinceGun = -1; + if (finishTime != null) { + timeSinceGun = finishTime.asMillis() - startOfRace.asMillis(); + } else { + timeSinceGun = timePoint.asMillis() - startOfRace.asMillis(); + } + if (timeSinceGun > 0) { + jsonCompetitorInLeg.put("timeSinceGun-ms", timeSinceGun); + } + Distance distanceSinceGun = trackedRace.getTrack(competitor).getDistanceTraveled( + startOfRace, finishTime != null ? finishTime : timePoint); + if (distanceSinceGun != null) { + jsonCompetitorInLeg.put("distanceSinceGun-m", distanceSinceGun.getMeters()); + } + } + + Distance distanceTraveled = trackedLegOfCompetitor.getDistanceTraveled(timePoint); + if (distanceTraveled != null) { + jsonCompetitorInLeg.put("distanceTraveled-m", + UnitSerializationUtil.distanceDecimalFormatter.format(distanceTraveled + .getMeters())); + } + Distance distanceTraveledIncludingGateStart = trackedLegOfCompetitor + .getDistanceTraveledConsideringGateStart(timePoint); + if (distanceTraveledIncludingGateStart != null) { + jsonCompetitorInLeg.put("distanceTraveledIncludingGateStart-m", + UnitSerializationUtil.distanceDecimalFormatter + .format(distanceTraveledIncludingGateStart.getMeters())); + } + try { + Integer rank = ranks.get(competitor); + jsonCompetitorInLeg.put("rank", rank); + } catch (RuntimeException re) { + if (re.getCause() != null && re.getCause() instanceof NoWindException) { + // well, we don't know the wind direction, so we can't compute a ranking + } else { + throw re; + } + } + try { + jsonCompetitorInLeg.put("gapToLeader-s", trackedLegOfCompetitor + .getGapToLeaderInSeconds(timePoint, WindPositionMode.LEG_MIDDLE)); + } catch (NoWindException e1) { + // well, we don't know the wind direction... then no gap to leader will be shown... + } + jsonCompetitorInLeg.put("started", trackedLegOfCompetitor.hasStartedLeg(timePoint)); + jsonCompetitorInLeg.put("finished", trackedLegOfCompetitor.hasFinishedLeg(timePoint)); + jsonCompetitors.add(jsonCompetitorInLeg); + } + } + jsonLeg.put("competitors", jsonCompetitors); + jsonLegs.add(jsonLeg); } - jsonLeg.put("competitors", jsonCompetitors); - jsonLegs.add(jsonLeg); + } finally { + course.unlockAfterRead(); } jsonRaceResults.put("legs", jsonLegs);