Merge branch 'master' into bug1351

This commit is contained in:
Axel Uhl committed 2013-06-21 22:12:33 +02:00
commit 4fc4d4adee
7 files changed
+231 -51

No files matched your search

@@ -685,4 +685,6 @@ public interface StringMessages extends Messages {
String stopAllReplicas();
String warningServerIsReplica();
String dropReplicaConnection();
String timePanelName();
String competitorFilterExplanation();
}
@@ -677,4 +677,6 @@ raceAbandonedNoMoreRacingToday=Race abandoned - No more racing today
raceAbandonedFurtherSignalsAshore=Race abandoned - Further signals ashore
stopAllReplicas=Drop connection to all replicas (Dangerous!)
warningServerIsReplica=ATTENTION: This server is configured to be a replica. Do NOT change anything or this will lead to strange effects!
dropReplicaConnection=Drop connection
dropReplicaConnection=Drop connection
timePanelName=Time Control
competitorFilterExplanation=The filter will show all competitors matching the criterias:
@@ -14,7 +14,7 @@ dampeningInterval=Mittelungsintervall
map=Karte
race=Wettfahrt
races=Wettfahrten
tracked=verfolgt
tracked=getracked
time=Zeit
timeTooltip=Zeit, die der Teilnehmer auf diesem Schenkel gesegelt ist.
playSpeed=Abspielgeschwindigkeit
@@ -75,7 +75,7 @@ rankGain=Platz +/-
rankGainTooltip=Die Differenz zwischen der Platzierung im aktuellen und im vorangegangenen Schenkel
sapSailingAnalytics=Sailing Analytics
leaderboard=Ergebnisse
leaderboards=Ranglistenlisten
leaderboards=Ranglisten
leaderboardSettings=Einstellungen Rangliste
settings=Einstellungen
selectAtLeastOneLegDetail=Mindestens ein Detail auswählen
@@ -279,12 +279,12 @@ stepSizeMustBeGreaterThanNull=Die Schrittweite muss größer als 0 sein
stepSizeInSeconds=Schrittweite in Sekunden
refreshInterval=Aktualisierungs-Interval (s)
refreshIntervalMustBeGreaterThanXSeconds=Aktualisierungs-Interval muss größer als {0} Sekunden sein
competitorCharts=Competitor charts
competitorCharts=Teilnehmer-Diagramme
selectAtLeastOneCompetitor=Um die Teilnehmer-Diagramme anzeigen zu lassen, wähle mindestens einen Teilnehmer aus
directionChange=Richtungsänderung
speedChange=Geschwindigkeitsänderung
degreesShort=deg
untracked=Nicht verfolgt
untracked=Nicht getracked
delayForLiveMode=Verzögerung im Live-Modus:
notAvailable=Nicht verfügbar
details=Details
@@ -663,4 +663,7 @@ raceAbandonedNoMoreRacingToday=Rennabbruch - heute keine Rennen mehr
raceAbandonedFurtherSignalsAshore=Rennabbruch - weitere Signale an Land
stoppAllReplicas=Verbindung zu allen Replicas abbrechen
warningServerIsReplica=ACHTUNG: Dieser Server ist als Replica konfiguriert. Vermeiden Sie alle Operationen!
dropReplicaConnection=Verbindung abbrechen
dropReplicaConnection=Verbindung abbrechen
timePanelName=Zeitregler
competitorFilterExplanation=Der Filter zeigt alle Teilnehmer, welche folgende Bedingungen erfüllen:
The filter will show all competitors matching the criterias:
@@ -489,7 +489,7 @@ public class TimePanel<T extends TimePanelSettings> extends SimplePanel implemen
@Override
public String getLocalizedShortName() {
return "Time control";
return stringMessages.timePanelName();
}
@Override
@@ -251,7 +251,7 @@ public abstract class AbstractCompetitorsFilterSetDialog extends DataEntryDialog
competitorsFiltersGridHeadline.setVisible(showGridHeadline);
competitorsFiltersGridFooter.setVisible(showGridFooter);
if(showGridHeadline) {
competitorsFiltersGridHeadline.setText("The filter will show all competitors matching the criterias:");
competitorsFiltersGridHeadline.setText(stringMessages.competitorFilterExplanation());
}
if(showGridFooter) {
competitorsFiltersGridFooter.setText("");
@@ -0,0 +1,141 @@
package com.sap.sailing.server.gateway.impl;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.stub;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.hamcrest.Matcher;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentMatcher;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.impl.KnotSpeedWithBearingImpl;
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.WindSource;
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.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.Wind;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.impl.CombinedWindTrackImpl;
import com.sap.sailing.domain.tracking.impl.WindImpl;
import com.sap.sailing.domain.tracking.impl.WindTrackImpl;
/**
* After refactoring the {@link WindJsonGetServlet} to first copy the relevant fixes to hold the read lock only
* for a short time, this test uses the old implementation to compute the expected result and compares the
* output received from the servlet.
*
* @author Axel Uhl (d043530)
*
*/
public class WindJsonGetServletRefactoringTest {
private WindJsonGetServlet servlet;
@Before
public void setUp() {
servlet = new WindJsonGetServlet();
}
private JSONObject getExpectedResult(String windSourceToRead, TrackedRace trackedRace, TimePoint from, TimePoint to) {
JSONObject jsonWindTracks = new JSONObject();
List<WindSource> windSources = servlet.getAvailableWindSources(trackedRace);
for (WindSource windSource : windSources) {
if("ALL".equals(windSourceToRead) || windSource.getType().name().equalsIgnoreCase(windSourceToRead)) {
JSONArray jsonWindArray = new JSONArray();
WindTrack windTrack = trackedRace.getOrCreateWindTrack(windSource);
windTrack.lockForRead();
try {
Iterator<Wind> windIter = windTrack.getFixesIterator(from, /* inclusive */true);
while (windIter.hasNext()) {
Wind wind = windIter.next();
if (wind.getTimePoint().compareTo(to) > 0) {
break;
}
JSONObject jsonWind = new JSONObject();
jsonWind.put("truebearingdeg", wind.getBearing().getDegrees());
jsonWind.put("knotspeed", wind.getKnots());
jsonWind.put("meterspersecondspeed", wind.getMetersPerSecond());
if (wind.getTimePoint() != null) {
jsonWind.put("timepoint", wind.getTimePoint().asMillis());
jsonWind.put("dampenedtruebearingdeg",
windTrack.getAveragedWind(wind.getPosition(), wind.getTimePoint())
.getBearing().getDegrees());
jsonWind.put("dampenedknotspeed",
windTrack.getAveragedWind(wind.getPosition(), wind.getTimePoint()).getKnots());
jsonWind.put("dampenedmeterspersecondspeed",
windTrack.getAveragedWind(wind.getPosition(), wind.getTimePoint())
.getMetersPerSecond());
}
if (wind.getPosition() != null) {
jsonWind.put("latdeg", wind.getPosition().getLatDeg());
jsonWind.put("lngdeg", wind.getPosition().getLngDeg());
}
jsonWindArray.add(jsonWind);
}
} finally {
windTrack.unlockAfterRead();
}
jsonWindTracks.put(windSource.toString(), jsonWindArray);
}
}
return jsonWindTracks;
}
@Test
public void compareOldAndNewImplementation() {
TrackedRace trackedRace = mock(TrackedRace.class);
final WindSourceImpl webWindSource = new WindSourceImpl(WindSourceType.WEB);
when(trackedRace.getWindSources()).thenReturn(Arrays.asList(new WindSource[] { webWindSource }));
RaceDefinition race = mock(RaceDefinition.class);
when(race.getName()).thenReturn("Race Name");
when(trackedRace.getRace()).thenReturn(race);
Set<WindSource> noWindSourcesToExclude = Collections.emptySet();
when(trackedRace.getWindSourcesToExclude()).thenReturn(noWindSourcesToExclude);
WindTrackImpl webWindTrack = new WindTrackImpl(/* millisecondsOverWhichToAverage */ 30000, /* useSpeed */ true, /* nameForReadWriteLock */ "compareOldAndNewImplementation");
when(trackedRace.getOrCreateWindTrack(webWindSource)).thenReturn(webWindTrack);
WindSourceImpl combinedWindSource = new WindSourceImpl(WindSourceType.COMBINED);
CombinedWindTrackImpl combinedWindTrack = new CombinedWindTrackImpl(trackedRace, /* baseConfidence */ 0.5);
when(trackedRace.getOrCreateWindTrack(combinedWindSource)).thenReturn(combinedWindTrack);
final TimePoint now = MillisecondsTimePoint.now();
final TimePoint earlier = now.minus(10000);
final TimePoint later = now.plus(10000);
final Matcher<TimePoint> isTooEarlyOrTooLate = new ArgumentMatcher<TimePoint>() {
@Override
public boolean matches(Object item) {
return item instanceof TimePoint &&
((TimePoint) item).before(earlier);
}
};
final Matcher<TimePoint> isNeitherTooEarlyNorTooLate = new ArgumentMatcher<TimePoint>() {
@Override
public boolean matches(Object argument) {
return !isTooEarlyOrTooLate.matches(argument);
}
};
stub(trackedRace.getWind(isA(Position.class), argThat(isTooEarlyOrTooLate))).toReturn(null);
Wind someCombinedWindFix = new WindImpl(new DegreePosition(50, 4), now,
new KnotSpeedWithBearingImpl(15, new DegreeBearingImpl(234)));
stub(trackedRace.getWind(isA(Position.class), argThat(isNeitherTooEarlyNorTooLate))).toReturn(someCombinedWindFix);
webWindTrack.add(new WindImpl(new DegreePosition(49, 3), now,
new KnotSpeedWithBearingImpl(12, new DegreeBearingImpl(123))));
assertEquals(getExpectedResult(WindJsonGetServlet.ALL, trackedRace, earlier, later),
servlet.getResult(WindJsonGetServlet.ALL, trackedRace, earlier, later));
}
}
@@ -2,8 +2,10 @@ package com.sap.sailing.server.gateway.impl;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@@ -26,6 +28,8 @@ import com.sap.sailing.server.gateway.AbstractJsonHttpServlet;
import com.sap.sailing.util.InvalidDateException;
public class WindJsonGetServlet extends AbstractJsonHttpServlet {
static final String ALL = "ALL";
private static final long serialVersionUID = -1408004464252437535L;
private static final String PARAM_NAME_WINDSOURCE = "windsource";
@@ -45,7 +49,7 @@ public class WindJsonGetServlet extends AbstractJsonHttpServlet {
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Race not found");
} else {
String windSourceParam = req.getParameter(PARAM_NAME_WINDSOURCE);
String windSourceToRead = windSourceParam != null ? windSourceParam : "ALL";
String windSourceToRead = windSourceParam != null ? windSourceParam : ALL;
TrackedRace trackedRace = getService().getOrCreateTrackedRegatta(regatta).getTrackedRace(race);
@@ -66,54 +70,82 @@ public class WindJsonGetServlet extends AbstractJsonHttpServlet {
return;
}
JSONObject jsonWindTracks = new JSONObject();
List<WindSource> windSources = getAvailableWindSources(trackedRace);
for (WindSource windSource : windSources) {
if("ALL".equals(windSourceToRead) || windSource.getType().name().equalsIgnoreCase(windSourceToRead)) {
JSONArray jsonWindArray = new JSONArray();
WindTrack windTrack = trackedRace.getOrCreateWindTrack(windSource);
windTrack.lockForRead();
try {
Iterator<Wind> windIter = windTrack.getFixesIterator(from, /* inclusive */true);
while (windIter.hasNext()) {
Wind wind = windIter.next();
if (wind.getTimePoint().compareTo(to) > 0) {
break;
}
JSONObject jsonWind = new JSONObject();
jsonWind.put("truebearingdeg", wind.getBearing().getDegrees());
jsonWind.put("knotspeed", wind.getKnots());
jsonWind.put("meterspersecondspeed", wind.getMetersPerSecond());
if (wind.getTimePoint() != null) {
jsonWind.put("timepoint", wind.getTimePoint().asMillis());
jsonWind.put("dampenedtruebearingdeg",
windTrack.getAveragedWind(wind.getPosition(), wind.getTimePoint())
.getBearing().getDegrees());
jsonWind.put("dampenedknotspeed",
windTrack.getAveragedWind(wind.getPosition(), wind.getTimePoint()).getKnots());
jsonWind.put("dampenedmeterspersecondspeed",
windTrack.getAveragedWind(wind.getPosition(), wind.getTimePoint())
.getMetersPerSecond());
}
if (wind.getPosition() != null) {
jsonWind.put("latdeg", wind.getPosition().getLatDeg());
jsonWind.put("lngdeg", wind.getPosition().getLngDeg());
}
jsonWindArray.add(jsonWind);
}
} finally {
windTrack.unlockAfterRead();
}
jsonWindTracks.put(windSource.toString(), jsonWindArray);
}
}
JSONObject jsonWindTracks = getResult(windSourceToRead, trackedRace, from, to);
setJsonResponseHeader(resp);
jsonWindTracks.writeJSONString(resp.getWriter());
}
}
}
private List<WindSource> getAvailableWindSources(TrackedRace trackedRace) {
JSONObject getResult(String windSourceToRead, TrackedRace trackedRace, TimePoint from, TimePoint to) {
Map<WindSource, List<Wind>> fixes = getRelevantFixes(windSourceToRead, trackedRace, from, to);
JSONObject jsonWindTracks = getResultAsJsonObject(trackedRace, fixes);
return jsonWindTracks;
}
private Map<WindSource, List<Wind>> getRelevantFixes(String windSourceToRead, TrackedRace trackedRace, TimePoint from,
TimePoint to) {
// quickly extract relevant fixes to hold locks as shortly as possible; process later
List<WindSource> windSources = getAvailableWindSources(trackedRace);
Map<WindSource, List<Wind>> fixes = new HashMap<>();
for (WindSource windSource : windSources) {
if (ALL.equals(windSourceToRead) || windSource.getType().name().equalsIgnoreCase(windSourceToRead)) {
ArrayList<Wind> fixesForWindSource = new ArrayList<>();
fixes.put(windSource, fixesForWindSource);
WindTrack windTrack = trackedRace.getOrCreateWindTrack(windSource);
windTrack.lockForRead();
try {
Iterator<Wind> windIter = windTrack.getFixesIterator(from, /* inclusive */true);
while (windIter.hasNext()) {
Wind wind = windIter.next();
if (wind.getTimePoint().compareTo(to) > 0) {
break;
} else {
fixesForWindSource.add(wind);
}
}
} finally {
windTrack.unlockAfterRead();
}
}
}
return fixes;
}
private JSONObject getResultAsJsonObject(TrackedRace trackedRace, Map<WindSource, List<Wind>> fixes) {
JSONObject jsonWindTracks = new JSONObject();
for (Map.Entry<WindSource, List<Wind>> e : fixes.entrySet()) {
WindSource windSource = e.getKey();
JSONArray jsonWindArray = new JSONArray();
WindTrack windTrack = trackedRace.getOrCreateWindTrack(windSource);
for (Wind wind : e.getValue()) {
JSONObject jsonWind = new JSONObject();
jsonWind.put("truebearingdeg", wind.getBearing().getDegrees());
jsonWind.put("knotspeed", wind.getKnots());
jsonWind.put("meterspersecondspeed", wind.getMetersPerSecond());
if (wind.getTimePoint() != null) {
jsonWind.put("timepoint", wind.getTimePoint().asMillis());
jsonWind.put("dampenedtruebearingdeg",
windTrack.getAveragedWind(wind.getPosition(), wind.getTimePoint()).getBearing()
.getDegrees());
jsonWind.put("dampenedknotspeed",
windTrack.getAveragedWind(wind.getPosition(), wind.getTimePoint()).getKnots());
jsonWind.put("dampenedmeterspersecondspeed",
windTrack.getAveragedWind(wind.getPosition(), wind.getTimePoint())
.getMetersPerSecond());
}
if (wind.getPosition() != null) {
jsonWind.put("latdeg", wind.getPosition().getLatDeg());
jsonWind.put("lngdeg", wind.getPosition().getLngDeg());
}
jsonWindArray.add(jsonWind);
}
jsonWindTracks.put(windSource.toString(), jsonWindArray);
}
return jsonWindTracks;
}
List<WindSource> getAvailableWindSources(TrackedRace trackedRace) {
List<WindSource> windSources = new ArrayList<WindSource>();
for (WindSource windSource : trackedRace.getWindSources()) {
windSources.add(windSource);