Merge branch 'master' into racecommittee_app

This commit is contained in:
Lukas Niemeier committed 2013-09-16 01:47:47 +02:00
commit 492947d2f1
141 files changed
+3045 -1033

No files matched your search

-2
View File
@@ -2,8 +2,6 @@
<classpath>
<classpathentry exported="true" kind="lib" path="lib/gwt-dev.jar"/>
<classpathentry exported="true" kind="lib" path="lib/gwt-user.jar"/>
<classpathentry exported="true" kind="lib" path="lib/validation-api-1.0.0.GA-sources.jar"/>
<classpathentry exported="true" kind="lib" path="lib/validation-api-1.0.0.GA.jar"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="output" path="bin"/>
@@ -7,10 +7,7 @@ Bundle-Vendor: GOOGLE
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Bundle-ClassPath: lib/gwt-dev.jar,
lib/gwt-user.jar,
lib/validation-api-1.0.0.GA-sources.jar,
lib/validation-api-1.0.0.GA.jar,
.
Eclipse-BuddyPolicy: registered
Export-Package: com.gargoylesoftware.htmlunit,
com.gargoylesoftware.htmlunit.attachment,
com.gargoylesoftware.htmlunit.gae,
@@ -601,4 +598,6 @@ Export-Package: com.gargoylesoftware.htmlunit,
org.mortbay.util,
org.mortbay.util.ajax,
org.mortbay.xml
Require-Bundle: javax.servlet;bundle-version="2.5.0";visibility:=reexport
Require-Bundle: javax.validation;bundle-version="1.0.0"
Import-Package: javax.servlet;version="2.5.0",
javax.servlet.http;version="2.5.0"
+1 -3
View File
@@ -1,6 +1,4 @@
bin.includes = META-INF/,\
.,\
lib/gwt-dev.jar,\
lib/gwt-user.jar,\
lib/validation-api-1.0.0.GA-sources.jar,\
lib/validation-api-1.0.0.GA.jar
lib/gwt-user.jar
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>com.google.gwt.user.osgi</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
@@ -6,8 +6,7 @@ Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Fragment-Host: com.sap.sailing.declination
Require-Bundle: org.junit4;bundle-version="4.8.2"
Import-Package: com.sap.sailing.domain.common,
com.sap.sailing.domain.common.impl,
com.sap.sailing.domain.common.quadtree,
junit.framework;version="4.8.1",
org.junit;version="4.8.1"
com.sap.sailing.domain.common.quadtree
@@ -1,6 +1,5 @@
package com.sap.sailing.domain.common.impl;
import java.text.SimpleDateFormat;
import java.util.Comparator;
import java.util.Date;
@@ -9,8 +8,6 @@ import com.sap.sailing.domain.common.TimePoint;
public abstract class AbstractTimePoint implements TimePoint {
private static final long serialVersionUID = 8825508619301420378L;
public static SimpleDateFormat TIMEPOINT_FORMATTER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
public static Comparator<TimePoint> TIMEPOINT_COMPARATOR = new SerializableComparator<TimePoint>() {
private static final long serialVersionUID = 7644726881387366025L;
@@ -68,7 +65,7 @@ public abstract class AbstractTimePoint implements TimePoint {
@Override
public String toString() {
return TIMEPOINT_FORMATTER.format(asDate());
return asDate().toString();
}
/**
@@ -185,6 +185,26 @@ public class SwissTimingAdapterPersistenceImpl implements SwissTimingAdapterPers
return null;
}
@Override
public boolean hasRaceStartlist(String raceID) {
DBCollection races = database.getCollection(CollectionNames.RACES_MESSAGES.name());
BasicDBObject query = new BasicDBObject();
query.append(FieldNames.RACE_ID.name(), raceID);
query.append(FieldNames.MESSAGE_COMMAND.name(), MessageType.STL.name());
DBObject object = races.findOne(query);
return object != null;
}
@Override
public boolean hasRaceCourse(String raceID) {
DBCollection races = database.getCollection(CollectionNames.RACES_MESSAGES.name());
BasicDBObject query = new BasicDBObject();
query.append(FieldNames.RACE_ID.name(), raceID);
query.append(FieldNames.MESSAGE_COMMAND.name(), MessageType.CCG.name());
DBObject object = races.findOne(query);
return object != null;
}
@Override
public Iterable<Race> getRaces() {
DBCollection races = database.getCollection(CollectionNames.RACES_MASTERDATA.name());
@@ -16,9 +16,8 @@ Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.domain.persistence,
com.sap.sailing.domain.tractracadapter,
com.sap.sailing.domain.swisstimingadapter.persistence;bundle-version="1.0.0",
org.junit4;bundle-version="4.8.2",
org.hamcrest.core;bundle-version="1.1.0"
Bundle-ClassPath: resources/,
.
Import-Package: com.sap.sailing.server,
junit.framework;version="4.8.1",
org.junit;version="4.8.1"
Import-Package: com.sap.sailing.server
@@ -47,14 +47,6 @@ public class DomainFactoryTest {
assertThat(raceType.getRaceCode(), is(OlympicRaceCode._470_WOMEN));
}
@Test
public void testGet470WomenBoatClass2() throws Exception {
String raceID = "SAW005_xxx";
RaceType raceType = swissTimingDomainFactory.getRaceTypeFromRaceID(raceID );
assertThat(raceType, is(notNullValue()));
assertThat(raceType.getRaceCode(), is(OlympicRaceCode._470_WOMEN));
}
@Test
public void testDontGetWrongBoatClass() throws Exception {
String raceID = "ABC";
@@ -208,4 +208,16 @@ public class OverlappingStoredAndReceivedMessagesTest implements RaceSpecificMes
return null;
}
@Override
public boolean hasRaceStartlist(String raceID) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean hasRaceCourse(String raceID) {
// TODO Auto-generated method stub
return false;
}
}
@@ -45,7 +45,7 @@ public class SimpleDomainFactoryTest {
@Test
public void testExtractBoatClassFromRaceIDWithEventID() {
String raceID = "QINSWC12013;SAW005901";
String raceID = "QINSWC12013_SAW005901";
RaceType raceType = DomainFactory.INSTANCE.getRaceTypeFromRaceID(raceID);
assertEquals("470", raceType.getBoatClass().getName());
}
@@ -7,11 +7,15 @@ public class RaceRecord {
private String raceID;
private String description;
private Date startTime;
private boolean hasCourse;
private boolean hasStartlist;
public RaceRecord(String raceID, String description, Date startTime) {
public RaceRecord(String raceID, String description, Date startTime, boolean hasCourse, boolean hasStartlist) {
this.raceID = raceID;
this.description = description;
this.startTime = startTime;
this.hasCourse = hasCourse;
this.hasStartlist = hasStartlist;
}
public String getRaceID() {
@@ -26,4 +30,11 @@ public class RaceRecord {
return startTime;
}
public boolean hasCourse() {
return hasCourse;
}
public boolean hasStartlist() {
return hasStartlist;
}
}
@@ -21,4 +21,8 @@ public interface RaceSpecificMessageLoader {
Iterable<Race> getRaces();
Race getRace(String raceID);
boolean hasRaceStartlist(String raceID);
boolean hasRaceCourse(String raceID);
}
@@ -26,12 +26,12 @@ public interface RaceType {
_470_MEN("SAM005", "470", Gender.male),
STAR("SAM007", "Star", Gender.male),
_49ER("SAM009", "49er", Gender.male),
_49ERFX("SAW009", "49erFX", Gender.female),
ELLIOTT6M("SAW007", "Elliott 6M", Gender.female),
RSX_WOMEN("SAW102", "RS:X", Gender.female),
RSX_MEN("SAM102", "RS:X", Gender.male),
LASER_RADIAL("SAW103", "Laser Radial", Gender.female),
_49ERFX_TODO("xxxxx_TODO_xxxxx", "49er FX", Gender.female),
NACRA17_TOTO("xxxxx_TODO_xxxxx", "Nacra 17", Gender.mixed);
NACRA17("SAX017", "Nacra 17", Gender.mixed),
LASER_RADIAL("SAW103", "Laser Radial", Gender.female);
enum Gender {
male, female, mixed, unspecified;
@@ -19,8 +19,12 @@ public interface SailMasterConnector {
Course getCourse(String raceID) throws UnknownHostException, IOException, InterruptedException;
boolean hasCourse(String raceID);
StartList getStartList(String raceID) throws UnknownHostException, IOException, InterruptedException;
boolean hasStartlist(String raceID);
TimePoint getStartTime(String raceID) throws UnknownHostException, IOException, ParseException, InterruptedException;
Distance getDistanceToMark(String raceID, int markIndex, String boatID) throws UnknownHostException, IOException, InterruptedException;
@@ -206,7 +206,7 @@ public class DomainFactoryImpl implements DomainFactory {
public RaceType getRaceTypeFromRaceID(String raceID) {
final RaceType result;
if (raceID != null && raceID.length() >= 6) {
final String[] optionalEventIDAndMandatoryRaceID = raceID.split(";");
final String[] optionalEventIDAndMandatoryRaceID = raceID.split("_");
final String swissTimingRaceCode = optionalEventIDAndMandatoryRaceID[optionalEventIDAndMandatoryRaceID.length-1].substring(0, 6).toUpperCase();
RaceType raceType = raceTypeByID.get(swissTimingRaceCode);
if (raceType == null) {
@@ -606,6 +606,24 @@ public class SailMasterConnectorImpl extends SailMasterTransceiverImpl implement
return result;
}
@Override
public boolean hasCourse(String raceID) {
boolean result = false;
if (messageLoader != null) {
result = messageLoader.hasRaceCourse(raceID);
}
return result;
}
@Override
public boolean hasStartlist(String raceID) {
boolean result = false;
if (messageLoader != null) {
result = messageLoader.hasRaceStartlist(raceID);
}
return result;
}
private List<Race> parseAvailableRacesMessage(SailMasterMessage availableRacesMessage) {
assertMessageType(MessageType.RAC, availableRacesMessage);
int count = Integer.valueOf(availableRacesMessage.getSections()[1]);
@@ -7,6 +7,6 @@ Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Fragment-Host: com.sap.sailing.domain.swisstimingreplayadapter
Import-Package: org.json.simple,
org.json.simple.parser,
org.junit;version="4.8.2"
Require-Bundle: com.sap.sailing.domain.test
org.json.simple.parser
Require-Bundle: com.sap.sailing.domain.test,
org.junit4;bundle-version="4.8.2"
@@ -14,10 +14,8 @@ Require-Bundle: com.sap.sailing.domain,
org.mockito.mockito-core;bundle-version="1.9.5",
com.sap.sailing.domain.test,
org.hamcrest;bundle-version="1.1.0",
org.objenesis;bundle-version="1.3.0"
org.objenesis;bundle-version="1.3.0",
org.junit4;bundle-version="4.8.2",
org.json.simple;bundle-version="1.1.0"
Export-Package: com.sap.sailing.domain.test,
com.sap.sailing.domain.test.mock
Import-Package: junit.framework;version="4.8.1",
org.json.simple,
org.json.simple.parser,
org.junit;version="4.8.1"
@@ -179,25 +179,26 @@ public interface DomainFactory {
/**
* Creates a {@link RaceDefinition} from a TracTrac {@link Race} and a domain {@link Course} definition. The
* resulting {@link RaceDefinition} is added to the {@link com.sap.sailing.domain.base.Regatta} to which
* <code>trackedRegatta</code> belongs (see {@link TrackedRegatta#getRegatta()}). It is added to the internal race cache.
* The corresponding {@link TrackedRace} object is also created, and the notification of threads waiting on the race
* cache such as a blocking {@link #getAndWaitForRaceDefinition(UUID)} happens only <em>after</em> the tracked race
* has been created and the {@link RaceDefinition} was
* <code>trackedRegatta</code> belongs (see {@link TrackedRegatta#getRegatta()}). It is added to the internal race
* cache. The corresponding {@link TrackedRace} object is also created, and the notification of threads waiting on
* the race cache such as a blocking {@link #getAndWaitForRaceDefinition(UUID)} happens only <em>after</em> the
* tracked race has been created and the {@link RaceDefinition} was
* {@link com.sap.sailing.domain.base.Regatta#addRace(RaceDefinition) added} to the domain event. This ensures that
* waiters for the {@link RaceDefinition} are guaranteed to obtain a valid, non- <code>null</code> tracked race
* waiters for the {@link RaceDefinition} are guaranteed to obtain a valid, non-<code>null</code> tracked race
* already immediately after the notification was sent, and that the {@link RaceDefinition} is already
* {@link com.sap.sailing.domain.base.Regatta#getAllRaces() known} by its containing
* {@link com.sap.sailing.domain.base.Regatta}.
* @param raceName TODO
* @param competitors TODO
* @param boatClass TODO
*
* @param raceDefinitionSetToUpdate
* if not <code>null</code>, after creating the {@link TrackedRace}, the {@link RaceDefinition} is
* {@link DynamicRaceDefinitionSet#addRaceDefinition(RaceDefinition, DynamicTrackedRace) added} to that object.
* {@link DynamicRaceDefinitionSet#addRaceDefinition(RaceDefinition, DynamicTrackedRace) added} to that
* object.
*/
DynamicTrackedRace getOrCreateRaceDefinitionAndTrackedRace(TrackedRegatta trackedRegatta, UUID raceId,
String raceName, Iterable<com.sap.sailing.domain.base.Competitor> competitors, BoatClass boatClass, Course course, Iterable<Sideline> sidelines,
WindStore windStore, long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, DynamicRaceDefinitionSet raceDefinitionSetToUpdate, URI courseDesignUpdateURI, UUID tracTracEventUuid, String tracTracUsername, String tracTracPassword);
DynamicTrackedRace getOrCreateRaceDefinitionAndTrackedRace(DynamicTrackedRegatta trackedRegatta, UUID raceId,
String raceName, Iterable<com.sap.sailing.domain.base.Competitor> competitors, BoatClass boatClass,
Course course, Iterable<Sideline> sidelines, WindStore windStore, long delayToLiveInMillis,
long millisecondsOverWhichToAverageWind, DynamicRaceDefinitionSet raceDefinitionSetToUpdate,
URI courseDesignUpdateURI, UUID tracTracEventUuid, String tracTracUsername, String tracTracPassword);
/**
* The record may be for a single mark or a gate. If for a gate, the
@@ -469,7 +469,7 @@ public class DomainFactoryImpl implements DomainFactory {
}
@Override
public DynamicTrackedRace getOrCreateRaceDefinitionAndTrackedRace(TrackedRegatta trackedRegatta, UUID raceId,
public DynamicTrackedRace getOrCreateRaceDefinitionAndTrackedRace(DynamicTrackedRegatta trackedRegatta, UUID raceId,
String raceName, Iterable<Competitor> competitors, BoatClass boatClass, Course course,
Iterable<Sideline> sidelines, WindStore windStore, long delayToLiveInMillis,
long millisecondsOverWhichToAverageWind, DynamicRaceDefinitionSet raceDefinitionSetToUpdate,
@@ -479,36 +479,38 @@ public class DomainFactoryImpl implements DomainFactory {
if (raceDefinition == null) {
logger.info("Creating RaceDefinitionImpl for race "+raceName);
raceDefinition = new RaceDefinitionImpl(raceName, course, boatClass, competitors, raceId);
} else {
logger.info("Already found RaceDefinitionImpl for race "+raceName);
}
DynamicTrackedRace trackedRace = trackedRegatta.getExistingTrackedRace(raceDefinition);
if (trackedRace == null) {
// add to existing regatta only if boat class matches
if (raceDefinition.getBoatClass() == trackedRegatta.getRegatta().getBoatClass()) {
trackedRegatta.getRegatta().addRace(raceDefinition);
DynamicTrackedRace trackedRace = createTrackedRace(trackedRegatta, raceDefinition, sidelines, windStore,
trackedRace = createTrackedRace(trackedRegatta, raceDefinition, sidelines, windStore,
delayToLiveInMillis, millisecondsOverWhichToAverageWind, raceDefinitionSetToUpdate);
logger.info("Added race "+raceDefinition+" to regatta "+trackedRegatta.getRegatta());
TracTracCourseDesignUpdateHandler courseDesignHandler = new TracTracCourseDesignUpdateHandler(tracTracUpdateURI,
tracTracUsername, tracTracPassword,
tracTracEventUuid, raceDefinition.getId());
logger.info("Added race " + raceDefinition + " to regatta " + trackedRegatta.getRegatta());
TracTracCourseDesignUpdateHandler courseDesignHandler = new TracTracCourseDesignUpdateHandler(
tracTracUpdateURI, tracTracUsername, tracTracPassword, tracTracEventUuid,
raceDefinition.getId());
trackedRace.addCourseDesignChangedListener(courseDesignHandler);
TracTracStartTimeUpdateHandler startTimeHandler = new TracTracStartTimeUpdateHandler(tracTracUpdateURI,
tracTracUsername, tracTracPassword, tracTracEventUuid, raceDefinition.getId());
TracTracStartTimeUpdateHandler startTimeHandler = new TracTracStartTimeUpdateHandler(
tracTracUpdateURI, tracTracUsername, tracTracPassword, tracTracEventUuid,
raceDefinition.getId());
trackedRace.addStartTimeChangedListener(startTimeHandler);
synchronized (raceCache) {
raceCache.put(raceId, raceDefinition);
raceCache.notifyAll();
}
return trackedRace;
raceCache.put(raceId, raceDefinition);
raceCache.notifyAll();
} else {
logger.warning("Not adding race "+raceDefinition+" to regatta "+trackedRegatta.getRegatta()+
" because boat class "+raceDefinition.getBoatClass()+" doesn't match regatta's boat class "+
trackedRegatta.getRegatta().getBoatClass());
return null;
logger.warning("Not adding race " + raceDefinition + " to regatta " + trackedRegatta.getRegatta()
+ " because boat class " + raceDefinition.getBoatClass()
+ " doesn't match regatta's boat class " + trackedRegatta.getRegatta().getBoatClass());
}
} else {
throw new RuntimeException("Race "+raceName+" already exists");
logger.info("Found existing tracked race for race "+raceName+" with ID "+raceId);
}
return trackedRace;
}
}
@@ -29,17 +29,21 @@ public class Simulator {
private static final Logger logger = Logger.getLogger(Simulator.class.getName());
private DynamicTrackedRace trackedRace;
private WindStore windStore;
private final WindStore windStore;
private long advanceInMillis = -1;
private Timer timer = new Timer("Timer for TracTrac Simulator");
private boolean stopped;
public Simulator(WindStore windStore) {
super();
assert windStore != null;
this.windStore = windStore;
}
/**
* Creates a wind store which replays the wind store events against a tracked race, correcting the wind fixes with
* the simulation delay. The wind store returned is an {@link EmptyWindStore}.
* The wind store returned is an {@link EmptyWindStore}.
*/
public WindStore simulatingWindStore(WindStore windStore) {
this.windStore = windStore;
return EmptyWindStore.INSTANCE;
}
@@ -63,7 +67,6 @@ public class Simulator {
*/
private void startWindPlayer() {
assert this.trackedRace != null;
assert this.windStore != null;
for (final Map.Entry<? extends WindSource, ? extends WindTrack> windSourceAndTrack : windStore.loadWindTracks(
trackedRace.getTrackedRegatta(), trackedRace,
/* millisecondsOverWhichToAverageWind doesn't matter because we only use raw fixes */ 10000).entrySet()) {
@@ -192,7 +192,7 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
this.domainFactory = domainFactory;
final Simulator simulator;
if (simulateWithStartTimeNow) {
simulator = new Simulator();
simulator = new Simulator(windStore);
} else {
simulator = null;
}
@@ -225,8 +225,6 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
// in this case, create a default regatta based on the TracTrac event data
this.regatta = effectiveRegatta == null ? domainFactory.getOrCreateDefaultRegatta(raceLogStore, tractracEvent, trackedRegattaRegistry) : effectiveRegatta;
trackedRegatta = trackedRegattaRegistry.getOrCreateTrackedRegatta(this.regatta);
// Read event data from configuration file
controlPointPositionPoller = scheduleClientParamsPHPPoller(paramURL, simulator, tracTracUpdateURI, delayToLiveInMillis, tracTracUsername, tracTracPassword);
receivers = new HashSet<Receiver>();
Set<TypeController> typeControllers = new HashSet<TypeController>();
for (Receiver receiver : domainFactory.getUpdateReceivers(getTrackedRegatta(), tractracEvent, startOfTracking,
@@ -237,6 +235,8 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
}
}
addListenersForStoredDataAndStartController(typeControllers);
// Read event data from configuration file
controlPointPositionPoller = scheduleClientParamsPHPPoller(paramURL, simulator, tracTracUpdateURI, delayToLiveInMillis, tracTracUsername, tracTracPassword);
}
@Override
@@ -550,6 +550,25 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
logger.info("stopped TracTrac tracking for "+getRaces());
lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.FINISHED, 1.0);
updateStatusOfTrackedRaces();
for (final RaceDefinition race : getRaces()) {
// Ask RacingEventService to cleanly stop and unregister this tracker
// if the race has all data loaded. Doing this asynchronously because
// stopping can take longer and if you're loading many races in parallel
// this can slow down loading extremely because this call-back seems to be
// called by TTCM synchronously.
Thread raceStopper = new Thread(new Runnable() {
@Override
public void run() {
try {
trackedRegattaRegistry.stopTracking(regatta, race);
} catch (Exception e) {
logger.log(Level.SEVERE, "Error trying to stop tracker for race " + race.getName()
+ " in regatta " + getRegatta().getName(), e);
}
}
});
raceStopper.start();
}
}
private void updateStatusOfTrackedRaces() {
@@ -562,7 +581,8 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
}
private void updateStatusOfTrackedRace(DynamicTrackedRace trackedRace) {
if (lastStatus != null) {
// can't update a race status once it has been set to FINISHED
if (lastStatus != null && trackedRace.getStatus() != null && trackedRace.getStatus().getStatus() != TrackedRaceStatusEnum.FINISHED) {
trackedRace.setStatus(lastStatus);
}
}
@@ -580,31 +600,6 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
if (isLiveTracking) {
lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.TRACKING, 1);
updateStatusOfTrackedRaces();
} else {
// ask RacingEventService to cleanly stop and unregister this tracker
// if the race has all data loaded. Doing this asynchronously because
// stopping can take longer and if you're loading many races in parallel
// this can slow down loading extremely
for (final RaceDefinition race : getRaces()) {
DynamicTrackedRace trackedRace = getTrackedRegatta().getExistingTrackedRace(race);
final Regatta regatta = getRegatta();
if (trackedRace.getStatus().getLoadingProgress() == 1.0) {
Thread raceStopper = new Thread(new Runnable() {
@Override
public void run() {
try {
trackedRegattaRegistry.stopTracking(regatta, race);
} catch (Exception e) {
logger.log(Level.SEVERE, "Error trying to stop tracker for race "+race.getName()+
" in regatta "+getRegatta().getName(), e);
}
}
});
raceStopper.start();
} else {
logger.log(Level.SEVERE, "Not stopping race "+race.getName()+" because it has not all data loaded! Progress: " + trackedRace.getStatus().getLoadingProgress());
}
}
}
}
@@ -31,7 +31,7 @@ import com.sap.sailing.domain.tracking.Wind;
public class DynamicTrackedRaceLogListener implements RaceLogEventVisitor {
public static final Logger log = Logger.getLogger(DynamicTrackedRaceLogListener.class.getName());
private static final Logger logger = Logger.getLogger(DynamicTrackedRaceLogListener.class.getName());
private DynamicTrackedRace trackedRace;
@@ -112,7 +112,7 @@ public class DynamicTrackedRaceLogListener implements RaceLogEventVisitor {
// ... onCourseDesignChangedByRaceCommittee() might be called more than once.
trackedRace.onCourseDesignChangedByRaceCommittee(courseDesign);
} else {
log.info("Could not find any course design update on race log of " + trackedRace.getRace().getName() + "! Not sending out any events.");
logger.info("Could not find any course design update on race log of " + trackedRace.getRace().getName() + "! Not sending out any events.");
}
}
@@ -125,7 +125,7 @@ public class DynamicTrackedRaceLogListener implements RaceLogEventVisitor {
/* invoke listeners with received start time, this will also trigger tractrac update */
trackedRace.onStartTimeChangedByRaceCommittee(startTime);
} else {
log.info("Could not find any valid start time on race log of " + trackedRace.getRace().getName() + "! Not sending out any events.");
logger.info("Could not find any valid start time on race log of " + trackedRace.getRace().getName() + "! Not sending out any events.");
}
}
@@ -8,7 +8,5 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Fragment-Host: com.sap.sailing.expeditionconnector
Require-Bundle: com.sap.sailing.server,
com.sap.sailing.domain.test,
com.sap.sailing.udpconnector
Import-Package: junit.framework;version="4.8.1",
org.junit;version="4.8.1",
org.osgi.framework;version="1.6.0"
com.sap.sailing.udpconnector,
org.junit4;bundle-version="4.8.2"
@@ -34,10 +34,10 @@
<plugin id="com.sap.sailing.barbados.resultimport" autoStart="true" startLevel="3" />
<plugin id="com.sap.sailing.ess40.resultimport" autoStart="true" startLevel="3" />
<plugin id="com.sap.sailing.freg.resultimport" autoStart="true" startLevel="3" />
<plugin id="com.sap.sailing.gwt.ui" autoStart="true" startLevel="4" />
<plugin id="com.sap.sailing.gwt.ui" autoStart="true" startLevel="5" />
<plugin id="com.sap.sailing.kiworesultimport" autoStart="true" startLevel="3" />
<plugin id="com.sap.sailing.manage2sail.resultimport" autoStart="true" startLevel="3" />
<plugin id="com.sap.sailing.monitoring" autoStart="true" startLevel="5" />
<plugin id="com.sap.sailing.monitoring" autoStart="true" startLevel="6" />
<plugin id="com.sap.sailing.resultimport" autoStart="true" startLevel="3" />
<plugin id="com.sap.sailing.sailwave.resultimport" autoStart="true" startLevel="3" />
<plugin id="com.sap.sailing.server" autoStart="true" startLevel="4" />
@@ -27,17 +27,15 @@
<import plugin="org.eclipse.osgi"/>
<import plugin="org.eclipse.osgi.services"/>
<import plugin="slf4j.api"/>
<import plugin="javax.el"/>
<import plugin="javax.servlet.jsp"/>
<import plugin="javax.servlet.jsp.jstl"/>
<import plugin="org.apache.jasper.glassfish"/>
<import plugin="org.apache.taglibs.standard.glassfish"/>
<import plugin="org.eclipse.jetty.osgi.boot"/>
<import plugin="org.eclipse.jetty.websocket.api"/>
<import plugin="org.eclipse.jetty.websocket.client"/>
<import plugin="org.eclipse.jetty.websocket.common"/>
<import plugin="org.eclipse.jetty.websocket.server"/>
<import plugin="org.eclipse.jetty.websocket.servlet"/>
<import plugin="org.apache.felix.gogo.command"/>
<import plugin="org.apache.felix.gogo.runtime"/>
<import plugin="org.apache.felix.gogo.shell"/>
</requires>
<plugin
@@ -47,14 +45,6 @@
version="9.0.4.v20130625"
unpack="false"/>
<plugin
id="org.eclipse.jetty.osgi.boot.jsp"
download-size="0"
install-size="0"
version="9.0.4.v20130625"
fragment="true"
unpack="false"/>
<plugin
id="jul.to.slf4j"
download-size="0"
@@ -98,4 +88,11 @@
version="0.0.0"
unpack="false"/>
<plugin
id="org.eclipse.equinox.console"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
</feature>
+23 -33
View File
@@ -22,20 +22,38 @@ Computes leaderboard information for sailing races and offers RESTful APIs to al
<requires>
<import plugin="com.googlecode.java-diff-utils" version="1.3.0" match="greaterOrEqual"/>
<import plugin="org.eclipse.osgi"/>
<import plugin="org.eclipse.osgi" version="3.9.0" match="greaterOrEqual"/>
<import plugin="javax.servlet" version="2.6.0" match="greaterOrEqual"/>
<import plugin="com.mongodb.driver" version="2.6.2" match="greaterOrEqual"/>
<import plugin="com.google.gwt.osgi" version="2.4.0" match="greaterOrEqual"/>
<import plugin="com.sap.sailing.domain.common"/>
<import plugin="com.google.gwt.osgi" version="2.5.1" match="greaterOrEqual"/>
<import plugin="org.json.simple" version="1.1.0" match="greaterOrEqual"/>
<import plugin="com.sap.sailing.operationaltransformation"/>
<import plugin="org.moxieapps.gwt.highcharts" version="1.1.4" match="greaterOrEqual"/>
<import plugin="com.sap.sailing.geocoding"/>
<import plugin="org.apache.geronimo.specs.geronimo-jms_1.1_spec"/>
<import plugin="com.rabbitmq.client"/>
<import plugin="org.apache.geronimo.specs.geronimo-j2ee-management_1.1_spec"/>
</requires>
<plugin
id="com.sap.sailing.geocoding"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.sap.sailing.operationaltransformation"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.sap.sailing.domain.common"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.sap.sailing.domain"
download-size="0"
@@ -85,13 +103,6 @@ Computes leaderboard information for sailing races and offers RESTful APIs to al
version="0.0.0"
unpack="false"/>
<plugin
id="org.json.simple"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.sap.sailing.declination"
download-size="0"
@@ -99,13 +110,6 @@ Computes leaderboard information for sailing races and offers RESTful APIs to al
version="0.0.0"
unpack="false"/>
<plugin
id="com.mongodb.driver"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.sap.sailing.mongodb"
download-size="0"
@@ -176,20 +180,6 @@ Computes leaderboard information for sailing races and offers RESTful APIs to al
version="0.0.0"
unpack="false"/>
<plugin
id="com.google.gwt.osgi"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.googlecode.java-diff-utils"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
<plugin
id="com.sap.sailing.www"
download-size="0"
@@ -6,6 +6,5 @@ Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Fragment-Host: com.sap.sailing.geocoding
Import-Package: junit.framework;version="4.8.2",
org.json.simple.parser,
org.junit;version="4.8.2"
Require-Bundle: org.junit4;bundle-version="4.8.2"
Import-Package: org.json.simple.parser
+1 -1
View File
@@ -3,8 +3,8 @@
<classpathentry kind="src" path="src/main/resources"/>
<classpathentry kind="src" path="src/main/java"/>
<classpathentry exported="true" kind="lib" path="WEB-INF/classes/"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="lib" path="WEB-INF/lib/gwt-servlet.jar" sourcepath="/com.google.gwt.osgi/lib/gwt-user.jar"/>
<classpathentry kind="lib" path="WEB-INF/lib/gwt-maps.jar"/>
<classpathentry kind="output" path="bin"/>
@@ -16,7 +16,6 @@ Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.domain.persistence,
com.sap.sailing.domain.swisstimingadapter.persistence,
com.sap.sailing.domain.tractracadapter.persistence,
com.google.gwt.osgi;bundle-version="2.4.0",
com.sap.sailing.domain.common,
com.sap.sailing.resultimport,
org.moxieapps.gwt.highcharts;bundle-version="1.5.0",
@@ -27,12 +26,14 @@ Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.domain.swisstimingreplayadapter,
com.sap.sailing.simulator;bundle-version="1.0.0",
com.sap.sailing.server.gateway.serialization;bundle-version="1.0.0",
de.csenk.gwt.ws
de.csenk.gwt.ws,
com.google.gwt.osgi;bundle-version="2.5.1"
Bundle-Activator: com.sap.sailing.gwt.ui.server.Activator
Bundle-ActivationPolicy: lazy
Import-Package: javax.jms;version="1.1.0",
javax.servlet;version="2.6.0",
javax.servlet.http;version="2.6.0",
javax.servlet.descriptor;version="2.6.0",
org.json.simple,
org.json.simple.parser,
org.osgi.framework,
@@ -816,32 +816,36 @@ public class LeaderboardConfigPanel extends FormPanel implements SelectedLeaderb
private void selectTrackedRaceInRaceList() {
final String selectedLeaderboardName = getSelectedLeaderboardName();
final RaceColumnDTOAndFleetDTOWithNameBasedEquality selectedRaceColumnAndFleetNameInLeaderboard = getSelectedRaceColumnWithFleet();
final String selectedRaceColumnName = selectedRaceColumnAndFleetNameInLeaderboard.getA().getRaceColumnName();
final String selectedFleetName = selectedRaceColumnAndFleetNameInLeaderboard.getB().getName();
sailingService.getRegattaAndRaceNameOfTrackedRaceConnectedToLeaderboardColumn(selectedLeaderboardName,
selectedRaceColumnName, new AsyncCallback<Map<String, RegattaAndRaceIdentifier>>() {
@Override
public void onFailure(Throwable t) {
errorReporter.reportError("Error trying to determine tracked race linked to race column "
+ selectedRaceColumnName + " in leaderboard " + selectedLeaderboardName + ": "
+ t.getMessage());
}
if (selectedLeaderboardName != null) {
final RaceColumnDTOAndFleetDTOWithNameBasedEquality selectedRaceColumnAndFleetNameInLeaderboard = getSelectedRaceColumnWithFleet();
final String selectedRaceColumnName = selectedRaceColumnAndFleetNameInLeaderboard.getA()
.getRaceColumnName();
final String selectedFleetName = selectedRaceColumnAndFleetNameInLeaderboard.getB().getName();
sailingService.getRegattaAndRaceNameOfTrackedRaceConnectedToLeaderboardColumn(selectedLeaderboardName,
selectedRaceColumnName, new AsyncCallback<Map<String, RegattaAndRaceIdentifier>>() {
@Override
public void onFailure(Throwable t) {
errorReporter.reportError("Error trying to determine tracked race linked to race column "
+ selectedRaceColumnName + " in leaderboard " + selectedLeaderboardName + ": "
+ t.getMessage());
}
@Override
public void onSuccess(Map<String, RegattaAndRaceIdentifier> regattaAndRaceNamesPerFleet) {
if (regattaAndRaceNamesPerFleet != null && !regattaAndRaceNamesPerFleet.isEmpty()) {
RegattaAndRaceIdentifier raceIdentifier = regattaAndRaceNamesPerFleet.get(selectedFleetName);
if (raceIdentifier != null) {
selectRaceInList(raceIdentifier.getRegattaName(), raceIdentifier.getRaceName());
} else {
trackedRacesListComposite.clearSelection();
}
} else {
trackedRacesListComposite.clearSelection();
}
}
});
@Override
public void onSuccess(Map<String, RegattaAndRaceIdentifier> regattaAndRaceNamesPerFleet) {
if (regattaAndRaceNamesPerFleet != null && !regattaAndRaceNamesPerFleet.isEmpty()) {
RegattaAndRaceIdentifier raceIdentifier = regattaAndRaceNamesPerFleet
.get(selectedFleetName);
if (raceIdentifier != null) {
selectRaceInList(raceIdentifier.getRegattaName(), raceIdentifier.getRaceName());
} else {
trackedRacesListComposite.clearSelection();
}
} else {
trackedRacesListComposite.clearSelection();
}
}
});
}
}
private void selectRaceInList(String regattaName, String raceName) {
@@ -46,14 +46,9 @@ import com.sap.sailing.gwt.ui.shared.SwissTimingConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.SwissTimingRaceRecordDTO;
/**
* Allows the user to start and stop tracking of events, regattas and races using the TracTrac connector. In particular,
* Allows the user to start and stop tracking of races using the SwissTiming connector. In particular,
* previously configured connections can be retrieved from a drop-down list which then pre-populates all connection
* parameters. The user can also choose to enter connection information manually. Using a "hierarchical" entry system
* comparable to that of, e.g., the Eclipse CVS connection setup wizard, components entered will be used to
* automatically assemble the full URL which can still be overwritten manually. There is a propagation order across the
* fields. Hostname propagates to JSON URL, Live URI and Stored URI. Port Live Data propagates to Port Stored Data,
* incremented by one. The ports propagate to Live URI and Stored URI, respectively. The event name propagates to the
* JSON URL.
* parameters. The user can also choose to enter connection information manually.
*
* @author Axel Uhl (D043530)
*
@@ -166,7 +161,16 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane
}
};
TextColumn<SwissTimingRaceRecordDTO> raceStartTrackingColumn = new TextColumn<SwissTimingRaceRecordDTO>() {
TextColumn<SwissTimingRaceRecordDTO> raceStateColumn = new TextColumn<SwissTimingRaceRecordDTO>() {
@Override
public String getValue(SwissTimingRaceRecordDTO object) {
String result = object.hasCourse ? "C" : "";
result += object.hasStartlist ? "S" : "";
return result;
}
};
TextColumn<SwissTimingRaceRecordDTO> raceStartTimeColumn = new TextColumn<SwissTimingRaceRecordDTO>() {
@Override
public String getValue(SwissTimingRaceRecordDTO object) {
return object.raceStartTime==null?"":dateFormatter.render(object.raceStartTime) + " " + timeFormatter.render(object.raceStartTime);
@@ -228,14 +232,15 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane
trackPanel.setStyleName("paddedPanel");
raceNameColumn.setSortable(true);
raceStartTrackingColumn.setSortable(true);
raceStartTimeColumn.setSortable(true);
AdminConsoleTableResources tableRes = GWT.create(AdminConsoleTableResources.class);
raceTable = new CellTable<SwissTimingRaceRecordDTO>(/* pageSize */ 10000, tableRes);
raceTable.addColumn(raceNameColumn, stringConstants.name());
raceTable.addColumn(raceDescriptionColumn, stringConstants.description());
raceTable.addColumn(boatClassColumn, stringConstants.boatClass());
raceTable.addColumn(raceStartTrackingColumn, stringConstants.raceStartTimeColumn());
raceTable.addColumn(raceStateColumn, stringConstants.status());
raceTable.addColumn(raceStartTimeColumn, stringConstants.startTime());
raceTable.setWidth("300px");
raceTable.setSelectionModel(new MultiSelectionModel<SwissTimingRaceRecordDTO>() {});
@@ -244,7 +249,7 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane
raceList = new ListDataProvider<SwissTimingRaceRecordDTO>();
raceList.addDataDisplay(raceTable);
Handler columnSortHandler = getRaceTableColumnSortHandler(raceList.getList(), raceNameColumn, raceStartTrackingColumn);
Handler columnSortHandler = getRaceTableColumnSortHandler(raceList.getList(), raceNameColumn, raceStartTimeColumn);
raceTable.addColumnSortHandler(columnSortHandler);
Label lblTrackSettings = new Label(stringConstants.trackSettings());
@@ -65,6 +65,7 @@ public class MediaSelector implements PlayStateListener, TimeListener,
private StringMessages stringMessages;
private final ErrorReporter errorReporter;
private final UserDTO user;
private boolean autoSelectMedia;
private MediaPlayer activeAudioPlayer;
private Date currentRaceTime;
@@ -73,7 +74,7 @@ public class MediaSelector implements PlayStateListener, TimeListener,
public MediaSelector(RegattaAndRaceIdentifier selectedRaceIdentifier, RaceTimesInfoProvider raceTimesInfoProvider,
Timer raceTimer, MediaServiceAsync mediaService, StringMessages stringMessages,
ErrorReporter errorReporter, UserDTO user) {
ErrorReporter errorReporter, UserDTO user, boolean autoSelectMedia) {
this.raceIdentifier = selectedRaceIdentifier;
this.raceTimesInfoProvider = raceTimesInfoProvider;
this.raceTimer = raceTimer;
@@ -81,6 +82,7 @@ public class MediaSelector implements PlayStateListener, TimeListener,
this.stringMessages = stringMessages;
this.errorReporter = errorReporter;
this.user = user;
this.autoSelectMedia = autoSelectMedia;
Window.addCloseHandler(this);
Window.addWindowClosingHandler(this);
@@ -120,7 +122,6 @@ public class MediaSelector implements PlayStateListener, TimeListener,
}
});
setWidgetsVisible(false);
}
@@ -286,6 +287,11 @@ public class MediaSelector implements PlayStateListener, TimeListener,
setStatus(mediaTrack);
}
setWidgetsVisible((this.mediaTracks.size() > 0) || (this.user != null));
toggleMediaButton.setValue(autoSelectMedia);
if (autoSelectMedia) {
playDefault();
}
}
private void setWidgetsVisible(boolean isVisible) {
@@ -82,8 +82,9 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint {
boolean showCompetitorsChart = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_COMPETITORSCHART, false /* default*/);
String activeCompetitorsFilterSetName = GwtHttpRequestUtils.getStringParameter(RaceBoardViewConfiguration.PARAM_VIEW_COMPETITOR_FILTER, null /* default*/);
final boolean canReplayWhileLiveIsPossible = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_CAN_REPLAY_DURING_LIVE_RACES, false);
final boolean autoSelectMedia = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_AUTOSELECT_MEDIA, false);
raceboardViewConfig = new RaceBoardViewConfiguration(viewMode, activeCompetitorsFilterSetName, showLeaderboard,
showWindChart, showCompetitorsChart, canReplayWhileLiveIsPossible);
showWindChart, showCompetitorsChart, canReplayWhileLiveIsPossible, autoSelectMedia);
final ParallelExecutionCallback<List<String>> getLeaderboardNamesCallback = new ParallelExecutionCallback<List<String>>();
final ParallelExecutionCallback<List<RegattaDTO>> getRegattasCallback = new ParallelExecutionCallback<List<RegattaDTO>>();
@@ -221,7 +222,7 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint {
}
FlowPanel logoAndTitlePanel = createLogoAndTitlePanel(raceBoardPanel);
FlowPanel timePanel = createTimePanel(raceBoardPanel);
p.addNorth(logoAndTitlePanel, 68);
p.addNorth(logoAndTitlePanel, 68);
p.addNorth(toolbarPanel, 40);
p.addSouth(timePanel, 90);
p.add(raceBoardPanel);
@@ -160,7 +160,6 @@ public class RaceBoardPanel extends SimplePanel implements RegattaDisplayer, Rac
toolbarPanel = new FlowPanel();
toolbarPanel.setWidth("100%");
mainPanel.add(toolbarPanel);
componentControlsPanel = new FlowPanel();
componentControlsPanel.addStyleName("raceBoardNavigation");
toolbarPanel.add(componentControlsPanel);
@@ -181,7 +180,7 @@ public class RaceBoardPanel extends SimplePanel implements RegattaDisplayer, Rac
}
CompetitorsFilterSets loadedCompetitorsFilterSets = loadCompetitorsFilterSets();
if(loadedCompetitorsFilterSets != null) {
if (loadedCompetitorsFilterSets != null) {
competitorsFilterSets = loadedCompetitorsFilterSets;
insertSelectedCompetitorsFilter(competitorsFilterSets);
} else {
@@ -190,9 +189,9 @@ public class RaceBoardPanel extends SimplePanel implements RegattaDisplayer, Rac
}
// in case the URL configuration contains the name of a competitors filter set we try to activate it
if(raceboardViewConfiguration.getActiveCompetitorsFilterSetName() != null) {
for(FilterSet<CompetitorDTO, FilterWithUI<CompetitorDTO>> filterSet: competitorsFilterSets.getFilterSets()) {
if(filterSet.getName().equals(raceboardViewConfiguration.getActiveCompetitorsFilterSetName())) {
if (raceboardViewConfiguration.getActiveCompetitorsFilterSetName() != null) {
for (FilterSet<CompetitorDTO, FilterWithUI<CompetitorDTO>> filterSet: competitorsFilterSets.getFilterSets()) {
if (filterSet.getName().equals(raceboardViewConfiguration.getActiveCompetitorsFilterSetName())) {
competitorsFilterSets.setActiveFilterSet(filterSet);
break;
}
@@ -277,7 +276,7 @@ public class RaceBoardPanel extends SimplePanel implements RegattaDisplayer, Rac
}
private void addMediaSelectorToNavigationMenu() {
MediaSelector mediaSelector = new MediaSelector(selectedRaceIdentifier, raceTimesInfoProvider, timer, mediaService, stringMessages, errorReporter, this.user);
MediaSelector mediaSelector = new MediaSelector(selectedRaceIdentifier, raceTimesInfoProvider, timer, mediaService, stringMessages, errorReporter, this.user, getConfiguration().isAutoSelectMedia());
// raceTimesInfoProvider.addRaceTimesInfoProviderListener(mediaSelector);
timer.addPlayStateListener(mediaSelector);
timer.addTimeListener(mediaSelector);
@@ -461,7 +460,7 @@ public class RaceBoardPanel extends SimplePanel implements RegattaDisplayer, Rac
private <SettingsType> void addComponentToNavigationMenu(final ComponentViewer componentViewer,
final Component<SettingsType> component, boolean isToogleCheckboxEnabled) {
final CheckBox checkBox= new CheckBox(component.getLocalizedShortName());
final CheckBox checkBox = new CheckBox(component.getLocalizedShortName());
checkBox.getElement().getStyle().setFloat(Style.Float.LEFT);
checkBox.setEnabled(isToogleCheckboxEnabled);
@@ -489,7 +488,7 @@ public class RaceBoardPanel extends SimplePanel implements RegattaDisplayer, Rac
componentControlsPanel.add(checkBox);
if(component.hasSettings()) {
if (component.hasSettings()) {
Button settingsButton = new Button("");
settingsButton.addClickHandler(new ClickHandler() {
@Override
@@ -12,14 +12,16 @@ public class RaceBoardViewConfiguration {
private ViewModes viewMode;
private String activeCompetitorsFilterSetName;
private boolean canReplayDuringLiveRaces;
private final boolean autoSelectMedia;
public static final String PARAM_VIEW_MODE = "viewMode";
public static final String PARAM_VIEW_MODE = "viewMode";
public static final String PARAM_VIEW_SHOW_LEADERBOARD = "viewShowLeaderboard";
public static final String PARAM_VIEW_SHOW_NAVIGATION_PANEL = "viewShowNavigationPanel";
public static final String PARAM_VIEW_SHOW_WINDCHART = "viewShowWindChart";
public static final String PARAM_VIEW_SHOW_COMPETITORSCHART = "viewShowCompetitorsChart";
public static final String PARAM_VIEW_COMPETITOR_FILTER = "viewCompetitorFilter";
public static final String PARAM_CAN_REPLAY_DURING_LIVE_RACES = "canReplayDuringLiveRaces";
public static final String PARAM_AUTOSELECT_MEDIA = "autoSelectMedia";
public static enum ViewModes { ONESCREEN };
@@ -28,16 +30,18 @@ public class RaceBoardViewConfiguration {
showLeaderboard = true;
showWindChart = false;
showCompetitorsChart = false;
}
autoSelectMedia = false;
}
public RaceBoardViewConfiguration(ViewModes viewMode, String activeCompetitorsFilterSetName,
boolean showLeaderboard, boolean showWindChart, boolean showCompetitorsChart, boolean canReplayDuringLiveRaces) {
boolean showLeaderboard, boolean showWindChart, boolean showCompetitorsChart, boolean canReplayDuringLiveRaces, boolean autoSelectMedia) {
this.viewMode = viewMode;
this.activeCompetitorsFilterSetName = activeCompetitorsFilterSetName;
this.showLeaderboard = showLeaderboard;
this.showWindChart = showWindChart;
this.showCompetitorsChart = showCompetitorsChart;
this.canReplayDuringLiveRaces = canReplayDuringLiveRaces;
this.autoSelectMedia = autoSelectMedia;
}
public boolean isShowLeaderboard() {
@@ -63,4 +67,9 @@ public class RaceBoardViewConfiguration {
public boolean isCanReplayDuringLiveRaces() {
return canReplayDuringLiveRaces;
}
public boolean isAutoSelectMedia() {
return autoSelectMedia;
}
}
@@ -1256,13 +1256,18 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
GPSFixMoving fix = fixIter.next();
while (fix != null && (fix.getTimePoint().compareTo(toTimePointExcluding) < 0 ||
(fix.getTimePoint().equals(toTimePointExcluding) && toTimePointExcluding.equals(fromTimePoint)))) {
Tack tack = trackedRace.getTack(competitor, fix.getTimePoint());
Tack tack;
try {
tack = trackedRace.getTack(competitor, fix.getTimePoint());
} catch (NoWindException nwe) {
tack = null;
}
TrackedLegOfCompetitor trackedLegOfCompetitor = trackedRace.getTrackedLeg(competitor,
fix.getTimePoint());
LegType legType = trackedLegOfCompetitor == null ? null : trackedRace.getTrackedLeg(
trackedLegOfCompetitor.getLeg()).getLegType(fix.getTimePoint());
Wind wind = trackedRace.getWind(fix.getPosition(),toTimePointExcluding);
WindDTO windDTO = createWindDTOFromAlreadyAveraged(wind, toTimePointExcluding);
WindDTO windDTO = wind == null ? null : createWindDTOFromAlreadyAveraged(wind, toTimePointExcluding);
GPSFixDTO fixDTO = createGPSFixDTO(fix, fix.getSpeed(), windDTO, tack, legType, /* extrapolate */
false);
fixesForCompetitor.add(fixDTO);
@@ -1272,13 +1277,18 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
// check if fix was at date and if extrapolation is requested
if (!fix.getTimePoint().equals(toTimePointExcluding) && extrapolate) {
Position position = track.getEstimatedPosition(toTimePointExcluding, extrapolate);
Tack tack2 = trackedRace.getTack(competitor, toTimePointExcluding);
Tack tack2;
try {
tack2 = trackedRace.getTack(competitor, toTimePointExcluding);
} catch (NoWindException nwe) {
tack2 = null;
}
LegType legType2 = trackedLegOfCompetitor == null ? null : trackedRace
.getTrackedLeg(trackedLegOfCompetitor.getLeg()).getLegType(
fix.getTimePoint());
SpeedWithBearing speedWithBearing = track.getEstimatedSpeed(toTimePointExcluding);
Wind wind2 = trackedRace.getWind(position, toTimePointExcluding);
WindDTO windDTO2 = createWindDTOFromAlreadyAveraged(wind2, toTimePointExcluding);
WindDTO windDTO2 = wind2 == null ? null : createWindDTOFromAlreadyAveraged(wind2, toTimePointExcluding);
GPSFixDTO extrapolated = new GPSFixDTO(
toPerCompetitorIdAsString.get(competitorDTO.getIdAsString()),
position==null?null:new PositionDTO(position.getLatDeg(), position.getLngDeg()),
@@ -2028,6 +2038,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
BoatClass boatClass = com.sap.sailing.domain.swisstimingadapter.DomainFactory.INSTANCE.getRaceTypeFromRaceID(rr.getRaceID()).getBoatClass();
swissTimingRaceRecordDTO.boatClass = boatClass != null ? boatClass.getName() : null;
swissTimingRaceRecordDTO.discipline = rr.getRaceID().length() >= 3 ? rr.getRaceID().substring(2, 3) : null;
swissTimingRaceRecordDTO.hasCourse = rr.hasCourse();
swissTimingRaceRecordDTO.hasStartlist = rr.hasStartlist();
result.add(swissTimingRaceRecordDTO);
}
return result;
@@ -13,6 +13,9 @@ public class SwissTimingRaceRecordDTO implements IsSerializable {
public String boatClass;
public String discipline;
public boolean hasCourse;
public boolean hasStartlist;
public SwissTimingRaceRecordDTO() {}
public SwissTimingRaceRecordDTO(String id, String description, Date raceStartTime) {
@@ -43,8 +43,9 @@ public class TVEntryPoint extends AbstractEntryPoint {
final boolean showCompetitorsChart = GwtHttpRequestUtils.getBooleanParameter(
RaceBoardViewConfiguration.PARAM_VIEW_SHOW_COMPETITORSCHART, false /* default */);
String activeCompetitorsFilterSetName = GwtHttpRequestUtils.getStringParameter(RaceBoardViewConfiguration.PARAM_VIEW_COMPETITOR_FILTER, null /* default*/);
final boolean autoSelectMedia = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_AUTOSELECT_MEDIA, false);
raceboardViewConfig = new RaceBoardViewConfiguration(RaceBoardViewConfiguration.ViewModes.ONESCREEN,
activeCompetitorsFilterSetName, showLeaderboard, showWindChart, showCompetitorsChart, /* canReplayWhileLiveIsPossible */ false);
activeCompetitorsFilterSetName, showLeaderboard, showWindChart, showCompetitorsChart, /* canReplayWhileLiveIsPossible */ false, autoSelectMedia);
sailingService.getLeaderboardNames(new AsyncCallback<List<String>>() {
@Override
@@ -169,7 +169,7 @@ public class TVViewController implements RaceTimesInfoProviderListener {
ScrollPanel leaderboardContentPanel = new ScrollPanel();
leaderboardContentPanel.add(leaderboardPanel);
dockPanel.add(leaderboardContentPanel);
if(logoAndTitlePanel != null) {
if (logoAndTitlePanel != null) {
logoAndTitlePanel.setSubTitle(leaderboardName);
}
currentLiveRace = null;
@@ -180,7 +180,7 @@ public class TVViewController implements RaceTimesInfoProviderListener {
}
private void showRaceBoard() {
if(activeTvView != TVViews.Raceboard) {
if (activeTvView != TVViews.Raceboard) {
clearContentPanels();
RaceBoardPanel raceBoardPanel = createRaceBoardPanel(leaderboardName, currentLiveRace);
raceBoardPanel.setSize("100%", "100%");
@@ -17,9 +17,8 @@ Require-Bundle: com.sap.sailing.domain,
org.json.simple;bundle-version="1.1.0",
com.sap.sailing.domain.swisstimingadapter,
org.objenesis;bundle-version="1.3.0",
org.junit4;bundle-version="4.8.2",
org.hamcrest;bundle-version="1.1.0",
org.mockito.mockito-core;bundle-version="1.9.5"
Import-Package: com.sap.sailing.domain.common.impl,
junit.framework;version="4.8.2",
org.junit;version="4.8.2"
Import-Package: com.sap.sailing.domain.common.impl
@@ -5,5 +5,5 @@ Bundle-SymbolicName: com.sap.sailing.operationaltransformation.test
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Require-Bundle: org.junit4;bundle-version="4.8.2"
Fragment-Host: com.sap.sailing.operationaltransformation
Import-Package: org.junit;version="4.8.2"
@@ -24,6 +24,12 @@ import com.sap.sailing.selenium.test.PageArea;
public class TracTracEventManagementPanel extends PageArea {
@FindBy(how = BySeleniumId.class, using = "JSONURL")
private WebElement jsonURLField;
@FindBy(how = BySeleniumId.class, using = "LiveURI")
private WebElement liveURIField;
@FindBy(how = BySeleniumId.class, using = "StoredURI")
private WebElement storedURIField;
@FindBy(how = BySeleniumId.class, using = "ListRaces")
private WebElement listRacesButton;
@@ -55,9 +61,11 @@ public class TracTracEventManagementPanel extends PageArea {
* The URL for which the races are to list.
*/
public void listRaces(String url) {
this.jsonURLField.clear();
this.jsonURLField.sendKeys(url);
this.listRacesButton.click();
liveURIField.clear();
storedURIField.clear();
jsonURLField.clear();
jsonURLField.sendKeys(url);
listRacesButton.click();
waitForAjaxRequests();
}
@@ -5,8 +5,7 @@ Bundle-SymbolicName: com.sap.sailing.server.gateway.ess40
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Import-Package: org.apache.jasper.servlet,
org.osgi.framework;version="1.3.0",
Import-Package: org.osgi.framework;version="1.3.0",
org.osgi.util.tracker;version="1.3.1",
com.sap.sailing.server
Bundle-ActivationPolicy: lazy
@@ -17,9 +16,6 @@ Require-Bundle: com.sap.sailing.domain,
com.mongodb.driver;bundle-version="2.6.2",
javax.servlet;bundle-version="2.6.0",
com.sap.sailing.domain.common,
javax.servlet.jsp.jstl;bundle-version="1.2.0",
javax.servlet.jsp;bundle-version="2.2.0",
javax.el;bundle-version="2.2.0",
com.sap.sailing.server.gateway
Bundle-ClassPath: .
Web-ContextPath: /gateway-ess40
@@ -1,120 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%@ page import="java.util.*" %>
<%@ page import="java.text.*" %>
<%@ page import="com.sap.sailing.domain.leaderboard.Leaderboard" %>
<%@ page import="com.sap.sailing.domain.base.*" %>
<%@ page import="com.sap.sailing.domain.base.impl.*" %>
<%@ page import="com.sap.sailing.domain.common.*" %>
<%@ page import="com.sap.sailing.domain.tracking.*" %>
<%@ page pageEncoding="UTF-8" %>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<title>SAP Sailing Analytics Leaderboard</title>
<link rel="stylesheet" href="<%= request.getContextPath() %>/css/leaderboard.css" type="text/css"></link>
<link rel="stylesheet" href="<%= request.getContextPath() %>/css/tablesorter.css" type="text/css"></link>
<script type="text/javascript" src="<%= request.getContextPath() %>/js/jquery-latest.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/js/jquery.tablesorter.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$("#leaderboardTable").tablesorter();
});
</script>
</head>
<body>
<%
TimePoint timePoint = MillisecondsTimePoint.now();
Leaderboard leaderboard = (Leaderboard) request.getAttribute("leaderboard");
// competitors are ordered according to total rank
List<Competitor> competitorsFromBestToWorst = leaderboard.getCompetitorsFromBestToWorst(timePoint);
Map<RaceColumn, List<Competitor>> rankedCompetitorsPerColumn = new HashMap<RaceColumn, List<Competitor>>();
Iterable<RaceColumn> raceColumns = leaderboard.getRaceColumns();
NumberFormat scoreFormat = NumberFormat.getNumberInstance();
int digitPresicion = 1;
scoreFormat.setMinimumFractionDigits(digitPresicion);
scoreFormat.setMaximumFractionDigits(digitPresicion);
String imgFlagPath = "/gwt/images/flags/";
%>
<h3>Leaderboard: <%= leaderboard.getName() %></h3>
<table id="leaderboardTable" class="tablesorter">
<thead>
<tr>
<th>Rank</th>
<th>Team</th>
<% for (RaceColumn raceColumn : raceColumns) { %>
<th><%= raceColumn.getName() %></th>
<% } %>
<th>Points</th>
</tr>
</thead>
<tbody>
<%
for (Competitor competitor : competitorsFromBestToWorst) {
int totalRank = competitorsFromBestToWorst.indexOf(competitor) + 1;
Nationality nationality = competitor.getTeam().getNationality();
Double totalPoints = leaderboard.getTotalPoints(competitor, timePoint);
if(leaderboard.hasCarriedPoints(competitor)) {
Double carriedPoints = leaderboard.getCarriedPoints(competitor);
if(carriedPoints != null) {
totalPoints = totalPoints != null ? totalPoints += carriedPoints : carriedPoints;
}
}
%>
<tr>
<td><%= totalRank %></td>
<td style="white-space:nowrap;"><img src="<%= imgFlagPath + nationality.getCountryCode().getTwoLetterISOCode().toLowerCase() + ".png"%>" />&nbsp;&nbsp;<%= competitor.getName() %></td>
<% for (RaceColumn raceColumn : raceColumns) {
List<Competitor> rankedCompetitorsForColumn = rankedCompetitorsPerColumn.get(raceColumn);
if (rankedCompetitorsForColumn == null) {
rankedCompetitorsForColumn = leaderboard.getCompetitorsFromBestToWorst(raceColumn, timePoint);
rankedCompetitorsPerColumn.put(raceColumn, rankedCompetitorsForColumn);
}
Double netRacePoints = leaderboard.getNetPoints(competitor, raceColumn, timePoint);
Double totalRacePoints = leaderboard.getTotalPoints(competitor, raceColumn, timePoint);
MaxPointsReason maxPointsReason = leaderboard.getMaxPointsReason(competitor, raceColumn, timePoint);
int rank = rankedCompetitorsForColumn.indexOf(competitor)+1;
TrackedRace trackedRace = raceColumn.getTrackedRace(competitor);
if (trackedRace != null) {
int raceRank = trackedRace.getRank(competitor, timePoint);
}
boolean isDiscarded = leaderboard.isDiscarded(competitor, raceColumn, timePoint);
boolean isCorrected = leaderboard.getScoreCorrection().isScoreCorrected(competitor, raceColumn);
String racePointsStyleClasses = "";
String racePoints = "&nbsp;";
if (maxPointsReason == null || maxPointsReason == MaxPointsReason.NONE) {
if (!isDiscarded) {
racePointsStyleClasses = "leaderboard-points-TotalPoints-NotDiscarded";
if(totalRacePoints != null) {
racePoints = scoreFormat.format(totalRacePoints);
}
} else {
racePointsStyleClasses = "leaderboard-points-TotalPoints-Discarded";
if(netRacePoints != null) {
racePoints = scoreFormat.format(netRacePoints);
}
}
} else {
if (!isDiscarded) {
racePointsStyleClasses = "leaderboard-points-MaxPoints-NotDiscarded";
} else {
racePointsStyleClasses = "leaderboard-points-MaxPoints-Discarded";
}
racePoints = maxPointsReason.name();
}
%>
<td class="<%= racePointsStyleClasses %>"><%= racePoints %></td>
<% } %>
<td><%= totalPoints %></td>
</tr>
<% } %>
</tbody>
</table>
</body>
</html>
@@ -1,135 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%@ page import="java.util.*" %>
<%@ page import="java.text.*" %>
<%@ page import="com.sap.sailing.domain.leaderboard.*" %>
<%@ page import="com.sap.sailing.domain.leaderboard.meta.*" %>
<%@ page import="com.sap.sailing.domain.base.*" %>
<%@ page import="com.sap.sailing.domain.base.impl.*" %>
<%@ page import="com.sap.sailing.domain.common.*" %>
<%@ page import="com.sap.sailing.domain.tracking.*" %>
<%@ page pageEncoding="UTF-8" %>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<title>SAP Sailing Analytics Leaderboard</title>
<link rel="stylesheet" href="<%= request.getContextPath() %>/css/leaderboard.css" type="text/css"></link>
<link rel="stylesheet" href="<%= request.getContextPath() %>/css/tablesorter.css" type="text/css"></link>
<script type="text/javascript" src="<%= request.getContextPath() %>/js/jquery-latest.js"></script>
<script type="text/javascript" src="<%= request.getContextPath() %>/js/jquery.tablesorter.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$("#leaderboardTable").tablesorter();
});
</script>
</head>
<body>
<%
TimePoint timePoint = MillisecondsTimePoint.now();
LeaderboardGroupMetaLeaderboard leaderboard = (LeaderboardGroupMetaLeaderboard) request.getAttribute("leaderboard");
Boolean showRaceDetails = (Boolean) request.getAttribute("showRaceDetails");
// competitors are ordered according to total rank
List<Competitor> competitorsFromBestToWorst = leaderboard.getCompetitorsFromBestToWorst(timePoint);
Map<RaceColumn, List<Competitor>> rankedCompetitorsPerColumn = new HashMap<RaceColumn, List<Competitor>>();
Iterable<RaceColumn> raceColumns = leaderboard.getRaceColumns();
List<Competitor> suppressedCompetitors = new ArrayList<Competitor>();
for(Competitor c: leaderboard.getSuppressedCompetitors()) {
suppressedCompetitors.add(c);
}
NumberFormat scoreFormat = NumberFormat.getNumberInstance();
int digitPresicion = 1;
scoreFormat.setMinimumFractionDigits(digitPresicion);
scoreFormat.setMaximumFractionDigits(digitPresicion);
String imgFlagPath = "/gwt/images/flags/";
%>
<h3>Overall Leaderboard: <%= leaderboard.getName() %></h3>
<table id="leaderboardTable" class="tablesorter">
<thead>
<tr>
<th>Rank</th>
<th>Team</th>
<% if(showRaceDetails) {
for (RaceColumn raceColumn : raceColumns) { %>
<th><%
String raceColumnName = raceColumn.getName();
String prefixToCut = "Extreme 40 ";
if(raceColumnName.startsWith(prefixToCut)) {
raceColumnName = raceColumnName.substring(prefixToCut.length(), raceColumnName.length());
}
%><%= raceColumnName %></th>
<% } } %>
<th>Points</th>
</tr>
</thead>
<tbody>
<%
for (Competitor competitor : competitorsFromBestToWorst) {
int totalRank = competitorsFromBestToWorst.indexOf(competitor) + 1;
Nationality nationality = competitor.getTeam().getNationality();
Double totalPoints = leaderboard.getTotalPoints(competitor, timePoint);
if(leaderboard.hasCarriedPoints(competitor)) {
Double carriedPoints = leaderboard.getCarriedPoints(competitor);
if(carriedPoints != null) {
totalPoints = totalPoints != null ? totalPoints += carriedPoints : carriedPoints;
}
}
if(!suppressedCompetitors.contains(competitor)) {
%>
<tr>
<td><%= totalRank %></td>
<td style="white-space:nowrap;"><img src="<%= imgFlagPath + nationality.getCountryCode().getTwoLetterISOCode().toLowerCase() + ".png"%>" />&nbsp;&nbsp;<%= competitor.getName() %></td>
<% if(showRaceDetails) {
for (RaceColumn raceColumn : raceColumns) {
List<Competitor> rankedCompetitorsForColumn = rankedCompetitorsPerColumn.get(raceColumn);
if (rankedCompetitorsForColumn == null) {
rankedCompetitorsForColumn = leaderboard.getCompetitorsFromBestToWorst(raceColumn, timePoint);
rankedCompetitorsPerColumn.put(raceColumn, rankedCompetitorsForColumn);
}
Double netRacePoints = leaderboard.getNetPoints(competitor, raceColumn, timePoint);
Double totalRacePoints = leaderboard.getTotalPoints(competitor, raceColumn, timePoint);
MaxPointsReason maxPointsReason = leaderboard.getMaxPointsReason(competitor, raceColumn, timePoint);
int rank = rankedCompetitorsForColumn.indexOf(competitor)+1;
TrackedRace trackedRace = raceColumn.getTrackedRace(competitor);
if (trackedRace != null) {
int raceRank = trackedRace.getRank(competitor, timePoint);
}
boolean isDiscarded = leaderboard.isDiscarded(competitor, raceColumn, timePoint);
boolean isCorrected = leaderboard.getScoreCorrection().isScoreCorrected(competitor, raceColumn);
String racePointsStyleClasses = "";
String racePoints = "&nbsp;";
if (maxPointsReason == null || maxPointsReason == MaxPointsReason.NONE) {
if (!isDiscarded) {
racePointsStyleClasses = "leaderboard-points-TotalPoints-NotDiscarded";
if(totalRacePoints != null) {
racePoints = scoreFormat.format(totalRacePoints);
}
} else {
racePointsStyleClasses = "leaderboard-points-TotalPoints-Discarded";
if(netRacePoints != null) {
racePoints = scoreFormat.format(netRacePoints);
}
}
} else {
if (!isDiscarded) {
racePointsStyleClasses = "leaderboard-points-MaxPoints-NotDiscarded";
} else {
racePointsStyleClasses = "leaderboard-points-MaxPoints-Discarded";
}
racePoints = maxPointsReason.name();
}
%>
<td class="<%= racePointsStyleClasses %>"><%= racePoints %></td>
<% } } %>
<td><%= totalPoints %></td>
</tr>
<% } } %>
</tbody>
</table>
</body>
</html>
@@ -4,39 +4,6 @@
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<servlet id="jsp">
<servlet-name>jspServlet</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>logVerbosityLevel</param-name>
<param-value>DEBUG</param-value>
</init-param>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>keepgenerated</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jspServlet</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>ESS40JspForwardServlet</servlet-name>
<servlet-class>com.sap.sailing.server.gateway.ess40.ESS40JspForwardServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ESS40JspForwardServlet</servlet-name>
<url-pattern>/html/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>ESS40ResultsAsCSVServlet</servlet-name>
@@ -1,58 +0,0 @@
package com.sap.sailing.server.gateway.ess40;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.gateway.SailingServerHttpServlet;
public class ESS40JspForwardServlet extends SailingServerHttpServlet {
private static final long serialVersionUID = -1017961881555515288L;
private static final String PARAM_NAME_LEADERBOARDNAME = "leaderboardName";
private static final String PARAM_NAME_SHOWDETAILS = "showDetails";
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
RacingEventService racingEventService = getService();
String pathInfo = request.getPathInfo();
String jspPath = "/WEB-INF/jsp";
String leaderboardNameParam = request.getParameter(PARAM_NAME_LEADERBOARDNAME);
String showDetailsParam = request.getParameter(PARAM_NAME_SHOWDETAILS);
Boolean showRaceDetails = showDetailsParam != null && showDetailsParam.equalsIgnoreCase("true");
if (leaderboardNameParam == null) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "You need to specify a leaderboard name using the "+
PARAM_NAME_LEADERBOARDNAME+" parameter");
return;
}
Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardNameParam);
if(pathInfo.equals("/leaderboard") && racingEventService != null && leaderboard != null) {
String jspFile = null;
if(leaderboard instanceof LeaderboardGroupMetaLeaderboard) {
jspFile = "overallLeaderboard.jsp";
} else {
jspFile = "leaderboard.jsp";
}
request.setAttribute("showRaceDetails", showRaceDetails);
request.setAttribute("leaderboard", leaderboard);
request.setAttribute("racingEventService", racingEventService);
ServletContext sc = getServletContext();
RequestDispatcher requestDispatcher = sc.getRequestDispatcher(jspPath + "/" + jspFile);
requestDispatcher.forward(request, response);
} else {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Error during leaderboard export.");
}
}
}
@@ -5,8 +5,8 @@ Bundle-SymbolicName: com.sap.sailing.server.gateway.serialization.test
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Require-Bundle: com.sap.sailing.server.gateway.serialization;bundle-version="1.0.0",
com.sap.sailing.domain;bundle-version="1.0.0",
Fragment-Host: com.sap.sailing.server.gateway.serialization
Require-Bundle: com.sap.sailing.domain;bundle-version="1.0.0",
com.sap.sailing.domain.common;bundle-version="1.0.0",
org.junit4;bundle-version="4.8.2",
com.sap.sailing.domain.shared.android,
@@ -14,5 +14,3 @@ Require-Bundle: com.sap.sailing.server.gateway.serialization;bundle-version="1.0
org.mockito.mockito-core;bundle-version="1.9.5",
org.hamcrest;bundle-version="1.1.0",
org.objenesis;bundle-version="1.3.0"
Import-Package: org.json.simple
Bundle-ClassPath: .
@@ -13,7 +13,7 @@ import com.sap.sailing.server.gateway.serialization.JsonSerializer;
public class SeriesJsonSerializer implements JsonSerializer<Series> {
public static final String FIELD_NAME = "name";
public static final String FIELD_FLEETS = "fleets";
public static final String FIELD_RACES = "races";
public static final String FIELD_RACES = "races"; // TODO: 'races' is wrong here... it's actually 'raceColumns'
public static final String FIELD_TRACKED_RACES = "trackedRaces";
public static final String FIELD_STARTS_WITH_ZERO_SCORE = "startsWithZeroScore";
public static final String FIELD_IS_MEDAL_SERIES = "isMedalSeries";
@@ -9,6 +9,6 @@ Fragment-Host: com.sap.sailing.server.gateway
Require-Bundle: org.mockito.mockito-core;bundle-version="1.9.5",
com.sap.sailing.domain.test,
org.hamcrest;bundle-version="1.1.0",
org.objenesis;bundle-version="1.3.0"
Import-Package: com.sap.sailing.server.impl,
org.junit;version="4.8.2"
org.objenesis;bundle-version="1.3.0",
org.junit4;bundle-version="4.8.2"
Import-Package: com.sap.sailing.server.impl
@@ -8,6 +8,7 @@ import static org.mockito.Mockito.when;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
@@ -26,7 +27,6 @@ import com.sap.sailing.domain.base.impl.NationalityImpl;
import com.sap.sailing.domain.base.impl.PersonImpl;
import com.sap.sailing.domain.base.impl.TeamImpl;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.AbstractTimePoint;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.mongodb.MongoDBConfiguration;
import com.sap.sailing.mongodb.MongoDBService;
@@ -37,6 +37,8 @@ import com.sap.sailing.server.impl.RacingEventServiceImpl;
public abstract class AbstractJsonExportTest {
protected RacingEventService racingEventService;
protected MongoDBService service;
protected static SimpleDateFormat TIMEPOINT_FORMATTER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
public void setUp() {
service = MongoDBService.INSTANCE;
@@ -75,7 +77,7 @@ public abstract class AbstractJsonExportTest {
protected TimePoint parseTimepointFromJsonString(String timePointAsJsonString) throws ParseException {
TimePoint result = null;
if(timePointAsJsonString != null && !timePointAsJsonString.isEmpty()) {
Date date = AbstractTimePoint.TIMEPOINT_FORMATTER.parse(timePointAsJsonString);
Date date = TIMEPOINT_FORMATTER.parse(timePointAsJsonString);
result = new MillisecondsTimePoint(date);
}
return result;
@@ -0,0 +1,83 @@
package com.sap.sailing.server.gateway.test.jaxrs;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import javax.ws.rs.core.Response;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.impl.BoatClassImpl;
import com.sap.sailing.domain.base.impl.BoatImpl;
import com.sap.sailing.domain.base.impl.CompetitorImpl;
import com.sap.sailing.domain.base.impl.NationalityImpl;
import com.sap.sailing.domain.base.impl.PersonImpl;
import com.sap.sailing.domain.base.impl.TeamImpl;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.mongodb.MongoDBConfiguration;
import com.sap.sailing.mongodb.MongoDBService;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource;
import com.sap.sailing.server.impl.RacingEventServiceImpl;
public abstract class AbstractJaxRsApiTest {
protected RacingEventService racingEventService;
protected MongoDBService service;
protected static SimpleDateFormat TIMEPOINT_FORMATTER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
public void setUp() {
service = MongoDBService.INSTANCE;
service.setConfiguration(MongoDBConfiguration.getDefaultTestConfiguration());
service.getDB().dropDatabase();
racingEventService = new RacingEventServiceImpl();
}
protected <T extends AbstractSailingServerResource> T spyResource(T resource) {
T spyResource = spy(resource);
doReturn(racingEventService).when(spyResource).getService();
return spyResource;
}
protected String decodeResponseFromByteArray(Response response) throws UnsupportedEncodingException {
byte[] entity = (byte[]) response.getEntity();
return new String(entity, "UTF-8");
}
protected TimePoint parseTimepointFromJsonString(String timePointAsJsonString) throws ParseException {
TimePoint result = null;
if(timePointAsJsonString != null && !timePointAsJsonString.isEmpty()) {
Date date = TIMEPOINT_FORMATTER.parse(timePointAsJsonString);
result = new MillisecondsTimePoint(date);
}
return result;
}
protected List<Competitor> createCompetitors(int numberOfCompetitorsToCreate) {
List<Competitor> result = new ArrayList<Competitor>();
BoatClass boatClass = new BoatClassImpl("505", /* typicallyStartsUpwind */ true);
for (int i = 1; i <= numberOfCompetitorsToCreate; i++) {
String competitorName = "C" + i;
Competitor competitor = new CompetitorImpl(123, competitorName, new TeamImpl("STG", Collections.singleton(
new PersonImpl(competitorName, new NationalityImpl("GER"),
/* dateOfBirth */ null, "This is famous "+competitorName)),
new PersonImpl("Rigo van Maas", new NationalityImpl("NED"),
/* dateOfBirth */null, "This is Rigo, the coach")), new BoatImpl(competitorName + "'s boat",
boatClass, null));
result.add(competitor);
}
return result;
}
}
@@ -0,0 +1,68 @@
package com.sap.sailing.server.gateway.test.jaxrs;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import javax.ws.rs.core.Response;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.junit.Before;
import org.junit.Test;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.impl.FleetImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.SeriesImpl;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.server.gateway.jaxrs.api.RegattasResource;
public class RegattasResourceTest extends AbstractJaxRsApiTest {
private String boatClassName = "49er";
private String regattaName = "TestRegatta";
@Before
public void setUp() {
super.setUp();
List<Series> series = new ArrayList<Series>();
List<Fleet> fleets = new ArrayList<Fleet>();
List<String> raceColumnNames = new ArrayList<String>();
fleets.add(new FleetImpl("Fleet1"));
fleets.add(new FleetImpl("Fleet2"));
Series testSeries = new SeriesImpl("TestSeries", /* isMedal */false, fleets,
raceColumnNames, /* trackedRegattaRegistry */null);
series.add(testSeries);
racingEventService.createRegatta(regattaName, boatClassName, UUID.randomUUID(), series, /*persistent*/ true,
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null);
testSeries.addRaceColumn("R1", /* trackedRegattaRegistry */ null);
testSeries.addRaceColumn("R2", /* trackedRegattaRegistry */ null);
}
@Test
public void testGetRegattas() throws Exception {
RegattasResource resource = new RegattasResource();
RegattasResource spyResource = spyResource(resource);
Response regattasResponse = spyResource.getRegattas();
String jsonString = decodeResponseFromByteArray(regattasResponse);
Object obj= JSONValue.parse(jsonString);
JSONArray array= (JSONArray) obj;
assertTrue(array.size() == 1);
JSONObject firstElement = (JSONObject) array.get(0);
String jsonName = (String) firstElement.get("name");
String jsonBoatClass = (String) firstElement.get("boatclass");
assertTrue(RegattaImpl.getDefaultName(regattaName, boatClassName).equals(jsonName));
assertTrue(boatClassName.equals(jsonBoatClass));
}
}
@@ -7,7 +7,9 @@ Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Import-Package: com.sap.sailing.server,
com.sap.sailing.server.operationaltransformation,
org.apache.jasper.servlet,
javax.ws.rs;version="1.1.1",
javax.ws.rs.core;version="1.1.1",
javax.ws.rs.ext;version="1.1.1",
org.json.simple,
org.json.simple.parser,
org.osgi.framework;version="1.3.0",
@@ -25,17 +27,16 @@ Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.udpconnector,
org.jdom;bundle-version="1.0.0",
com.sap.sailing.server.gateway.serialization;bundle-version="1.0.0",
javax.servlet.jsp.jstl;bundle-version="1.2.0",
javax.servlet.jsp;bundle-version="2.2.0",
javax.el;bundle-version="2.1.0",
rome;bundle-version="1.0.0",
com.sap.sailing.domain.shared.android;bundle-version="1.0.0",
com.sap.sailing.server.trackfiles;bundle-version="1.0.0",
org.eclipse.jetty.websocket.api;bundle-version="9.0.4",
org.eclipse.jetty.websocket.server;bundle-version="9.0.4",
org.eclipse.jetty.websocket.servlet;bundle-version="9.0.4",
org.eclipse.jetty.websocket.client;bundle-version="9.0.4",
org.eclipse.jetty.websocket.common;bundle-version="9.0.4"
org.eclipse.jetty.websocket.common;bundle-version="9.0.4",
javax.validation;bundle-version="1.0.0",
com.sun.jersey;bundle-version="1.17.0",
org.objectweb.asm;bundle-version="3.3.1"
Bundle-ClassPath: .
Web-ContextPath: /sailingserver
Export-Package: com.sap.sailing.server.gateway
@@ -177,15 +177,6 @@
<url-pattern>/expeditionWindStatus</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>LiveRaceRankingRssGetServlet</servlet-name>
<servlet-class>com.sap.sailing.server.gateway.impl.LiveRaceRankingRssGetServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LiveRaceRankingRssGetServlet</servlet-name>
<url-pattern>/rss/liveRaceRanking</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>MasterDataByLeaderboardGroupJsonGetServlet</servlet-name>
<servlet-class>com.sap.sailing.server.gateway.impl.MasterDataByLeaderboardGroupJsonGetServlet</servlet-class>
@@ -203,26 +194,6 @@
<servlet-name>TrackFilesExportPostServlet</servlet-name>
<url-pattern>/trackfiles</url-pattern>
</servlet-mapping>
<servlet id="jsp">
<servlet-name>jspServet</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>logVerbosityLevel</param-name>
<param-value>DEBUG</param-value>
</init-param>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>keepgenerated</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet>
<servlet-name>websockettest</servlet-name>
@@ -234,10 +205,32 @@
<url-pattern>/websockettest</url-pattern>
</servlet-mapping>
<!--
<servlet-mapping>
<servlet-name>jspServet</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>
-->
<servlet>
<servlet-name>Jersey REST API</servlet-name>
<servlet-class>com.sap.sailing.server.gateway.jaxrs.RestServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.sap.sailing.server.gateway.jaxrs.api.RestApiApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST API</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Jersey REST SPI</servlet-name>
<servlet-class>com.sap.sailing.server.gateway.jaxrs.RestServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.sap.sailing.server.gateway.jaxrs.spi.RestSpiApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST SPI</servlet-name>
<url-pattern>/spi/*</url-pattern>
</servlet-mapping>
</web-app>
Whitespace-only changes.
+1 -1
View File
@@ -26,7 +26,7 @@ body {
font-size: 14px;
font-family: OpenSansRegular, Arial, Verdana, sans-serif;
font-weight: normal;
margin: 0px;
margin: 10px;
padding: 0px;
line-height: 18px;
}
@@ -2,6 +2,7 @@ package com.sap.sailing.server.gateway.impl;
import java.io.IOException;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
@@ -59,6 +60,7 @@ public class LeaderboardJsonGetServlet extends AbstractJsonHttpServlet implement
// for backward compatibility the default result state is live
private final ResultStates DEFAULT_RESULT_STATE = ResultStates.Live;
private static SimpleDateFormat TIMEPOINT_FORMATTER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
private static final int MAX_TOTAL_NUMBER_OF_CACHE_ENTRIES = 1000;
@@ -220,7 +222,7 @@ public class LeaderboardJsonGetServlet extends AbstractJsonHttpServlet implement
setJsonResponseHeader(resp);
synchronized (jsonLeaderboardAsString) {
int indexOfFirstOpeningBrace = jsonLeaderboardAsString.indexOf("{");
final String requestTimePointAsJson = "\"requestTimepoint\": \""+requestTimePoint.toString()+"\", ";
final String requestTimePointAsJson = "\"requestTimepoint\": \"" + formatTimePoint(requestTimePoint) + "\", ";
if (indexOfFirstOpeningBrace >= 0) {
jsonLeaderboardAsString.insert(indexOfFirstOpeningBrace+1, requestTimePointAsJson);
}
@@ -319,6 +321,10 @@ public class LeaderboardJsonGetServlet extends AbstractJsonHttpServlet implement
return jsonLeaderboard;
}
private String formatTimePoint(TimePoint timepoint) {
return timepoint == null ? null : TIMEPOINT_FORMATTER.format(timepoint.asDate());
}
private JSONObject createEmptyLeaderboardJson(Leaderboard leaderboard,
ResultStates resultState, TimePoint requestTimePoint, Integer maxCompetitorsCount) throws NoWindException {
JSONObject jsonLeaderboard = new JSONObject();
@@ -365,9 +371,9 @@ public class LeaderboardJsonGetServlet extends AbstractJsonHttpServlet implement
jsonLeaderboard.put("name", leaderboard.getName());
// for backward compatibility
jsonLeaderboard.put("timepoint", resultTimePoint != null ? resultTimePoint.toString() : null);
jsonLeaderboard.put("timepoint", formatTimePoint(resultTimePoint));
jsonLeaderboard.put("resultTimepoint", resultTimePoint != null ? resultTimePoint.toString() : null);
jsonLeaderboard.put("resultTimepoint", formatTimePoint(resultTimePoint));
jsonLeaderboard.put("resultState", resultState.name());
jsonLeaderboard.put("maxCompetitorsCount", maxCompetitorsCount);
@@ -375,7 +381,7 @@ public class LeaderboardJsonGetServlet extends AbstractJsonHttpServlet implement
if (scoreCorrection != null) {
jsonLeaderboard.put("scoringComment", scoreCorrection.getComment());
TimePoint lastUpdateTimepoint = scoreCorrection.getTimePointOfLastCorrectionsValidity();
jsonLeaderboard.put("lastScoringUpdate", lastUpdateTimepoint != null ? lastUpdateTimepoint.asDate().toString(): null);
jsonLeaderboard.put("lastScoringUpdate", formatTimePoint(lastUpdateTimepoint));
} else {
jsonLeaderboard.put("scoringComment", null);
jsonLeaderboard.put("lastScoringUpdate", null);
@@ -1,158 +0,0 @@
package com.sap.sailing.server.gateway.impl;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.server.gateway.SailingServerHttpServlet;
import com.sun.syndication.feed.synd.SyndContent;
import com.sun.syndication.feed.synd.SyndContentImpl;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndEntryImpl;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.feed.synd.SyndFeedImpl;
import com.sun.syndication.io.SyndFeedOutput;
public class LiveRaceRankingRssGetServlet extends SailingServerHttpServlet {
private static final long serialVersionUID = -508373964426868319L;
private static final String PARAM_NAME_LEADERBOARDNAME = "leaderboardName";
private static final String PARAM_MAX_COMPETITORS = "max";
private String charEncoding = "UTF-8";
private int maxCompetitorsToShow = 1000;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// to allow access to the rss feed directly from a client side javascript
resp.setHeader("Access-Control-Allow-Origin", "*");
resp.setContentType("application/rss+xml");
resp.setCharacterEncoding(charEncoding);
TimePoint timePoint = MillisecondsTimePoint.now();
String leaderboardName = req.getParameter(PARAM_NAME_LEADERBOARDNAME);
String maxCompetitorsParam = req.getParameter(PARAM_MAX_COMPETITORS);
if(maxCompetitorsParam != null) {
try {
maxCompetitorsToShow = Integer.parseInt(maxCompetitorsParam);
} catch (NumberFormatException e) {
}
}
if (leaderboardName == null) {
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Need to specify a leaderboard name using the "+
PARAM_NAME_LEADERBOARDNAME+" parameter");
} else {
Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName);
if (leaderboard == null) {
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Leaderboard "+leaderboardName+" not found");
} else {
try {
RaceColumn raceColumnToShow = null;
TrackedRace liveTrackedRace = null;
// find a live race in the leaderboard
Iterable<RaceColumn> races = leaderboard.getRaceColumns();
for (RaceColumn raceInLeaderboard : races) {
for (Fleet fleet : raceInLeaderboard.getFleets()) {
TrackedRace trackedRaceOfFleet = raceInLeaderboard.getTrackedRace(fleet);
if(trackedRaceOfFleet != null && isLive(trackedRaceOfFleet)) {
raceColumnToShow = raceInLeaderboard;
liveTrackedRace = trackedRaceOfFleet;
break;
}
}
}
SyndFeed feed = new SyndFeedImpl();
String title = leaderboard.getName();
feed.setTitle(title);
feed.setFeedType("rss_2.0");
feed.setLink("http://www.sapsaling.com");
feed.setDescription("SAP Sailing Analytics");
feed.setPublishedDate(new Date());
feed.setEncoding(charEncoding);
List<SyndEntry> entries = new ArrayList<SyndEntry>();
SyndContent description = new SyndContentImpl();
SyndEntry entry = new SyndEntryImpl();
entry.setLink("http://www.sapsailing.com");
description.setType("text/plain");
if(raceColumnToShow != null && liveTrackedRace != null) {
String entryTitle = "Live ranking of race '" + raceColumnToShow.getName() + "'";
entry.setTitle(entryTitle);
List<Competitor> competitorsFromBestToWorst = leaderboard.getCompetitorsFromBestToWorst(raceColumnToShow, timePoint);
int currentCompetitorCounter = 0;
String feedText = "";
for (Competitor competitor : competitorsFromBestToWorst) {
if(currentCompetitorCounter == maxCompetitorsToShow) {
break;
}
currentCompetitorCounter++;
String sailID = competitor.getBoat().getSailID();
feedText += currentCompetitorCounter + ". " + sailID + "\n";
// String competitorName = competitor.getName();
// final String displayName = leaderboard.getDisplayName(competitor);
// String id = competitor.getId().toString();
// Nationality nationality = competitor.getTeam().getNationality();
// String nat = nationality != null ? nationality.getThreeLetterIOCAcronym(): null;
// String countryCode = nationality != null ? nationality.getCountryCode().getTwoLetterISOCode(): null;
// String totalRank = "" +competitorsFromBestToWorst.indexOf(competitor) + 1;
// String totalPoints = "" + leaderboard.getTotalPoints(competitor, timePoint);
// String raceColumnName = raceColumnToShow.getName();
// final Fleet fleetOfCompetitor = raceColumnToShow.getFleetOfCompetitor(competitor);
// String fleet = fleetOfCompetitor==null?"":fleetOfCompetitor.getName();
// String netpoints = String.valueOf(leaderboard.getNetPoints(competitor, raceColumnToShow, timePoint));
// String totalPointsRaceColumn = String.valueOf(leaderboard.getTotalPoints(competitor, raceColumnToShow, timePoint));
// MaxPointsReason maxPointsReason = leaderboard.getMaxPointsReason(competitor, raceColumnToShow, timePoint);
// String maxPoints = maxPointsReason != null ? maxPointsReason.toString(): null;
// String isDiscarded = String.valueOf(leaderboard.isDiscarded(competitor, raceColumnToShow, timePoint));
// String isCorrected = String.valueOf(leaderboard.getScoreCorrection().isScoreCorrected(competitor, raceColumnToShow));
}
description.setValue(feedText);
} else {
String entryTitle = "No live race right now";
entry.setTitle(entryTitle);
description.setValue("");
}
entry.setDescription(description);
entries.add(entry);
feed.setEntries(entries);
PrintWriter writer = resp.getWriter();
SyndFeedOutput feedOutput = new SyndFeedOutput();
feedOutput.output(feed, writer);
} catch (Exception e) {
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
}
}
}
}
/**
* @return <code>true</code> if the startOfTracking is after the current date and there's no end of the race
*/
private boolean isLive(TrackedRace trackedRace) {
return trackedRace.getEndOfRace() == null && (trackedRace.getStartOfTracking() != null ? new Date().after(trackedRace.getStartOfTracking().asDate()) : false);
}
}
@@ -0,0 +1,66 @@
package com.sap.sailing.server.gateway.jaxrs;
import javax.servlet.ServletContext;
import javax.ws.rs.core.Context;
import org.osgi.util.tracker.ServiceTracker;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.util.DateParser;
import com.sap.sailing.util.InvalidDateException;
public abstract class AbstractSailingServerResource {
@Context ServletContext servletContext;
public RacingEventService getService() {
@SuppressWarnings("unchecked")
ServiceTracker<RacingEventService, RacingEventService> tracker = (ServiceTracker<RacingEventService, RacingEventService>) servletContext.getAttribute(RestServletContainer.RACING_EVENT_SERVICE_TRACKER_NAME);
return tracker.getService();
}
protected Regatta findRegattaByName(String regattaName) {
return getService().getRegattaByName(regattaName);
}
protected RaceDefinition findRaceByName(Regatta regatta, String raceName) {
RaceDefinition result = null;
if(regatta != null) {
for (RaceDefinition race : regatta.getAllRaces()) {
if (race.getName().equals(raceName)) {
result = race;
break;
}
}
}
return result;
}
protected TimePoint parseTimePoint(String isoTime, Long timeAsMillis, TimePoint defaultTime) throws InvalidDateException {
TimePoint timePoint;
if (isoTime != null && isoTime.length() > 0) {
timePoint = new MillisecondsTimePoint(DateParser.parse(isoTime).getTime());
} else {
timePoint = timeAsMillis != null ? new MillisecondsTimePoint(timeAsMillis) : defaultTime;
}
return timePoint;
}
protected TrackedRace findTrackedRace(String regattaName, String raceName) {
Regatta regatta = findRegattaByName(regattaName);
RaceDefinition race = findRaceByName(regatta, raceName);
TrackedRace trackedRace = null;
if (regatta != null && race != null) {
DynamicTrackedRegatta trackedRegatta = getService().getTrackedRegatta(regatta);
if(trackedRegatta != null) {
trackedRace = trackedRegatta.getTrackedRace(race);
}
}
return trackedRace;
}
}
@@ -0,0 +1,57 @@
package com.sap.sailing.server.gateway.jaxrs;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.ws.rs.core.Application;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
import com.sap.sailing.server.RacingEventService;
import com.sun.jersey.spi.container.servlet.ServletContainer;
public class RestServletContainer extends ServletContainer {
private static final long serialVersionUID = -3132655822281021061L;
private static final String OSGI_RFC66_WEBBUNDLE_BUNDLECONTEXT_NAME = "osgi-bundlecontext";
public static final String RACING_EVENT_SERVICE_TRACKER_NAME = "racingEventServiceTracker";
private ServiceTracker<RacingEventService, RacingEventService> racingEventServiceTracker;
public RestServletContainer() {
super();
}
public RestServletContainer(Application app) {
super(app);
}
public RestServletContainer(Class<? extends Application> appClass) {
super(appClass);
}
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
BundleContext context = (BundleContext) config.getServletContext().getAttribute(OSGI_RFC66_WEBBUNDLE_BUNDLECONTEXT_NAME);
racingEventServiceTracker = new ServiceTracker<RacingEventService, RacingEventService>(context, RacingEventService.class.getName(), null);
racingEventServiceTracker.open();
config.getServletContext().setAttribute(RACING_EVENT_SERVICE_TRACKER_NAME, racingEventServiceTracker);
}
@Override
public void destroy() {
super.destroy();
if(racingEventServiceTracker != null) {
racingEventServiceTracker.close();
}
}
public RacingEventService getService() {
return racingEventServiceTracker.getService();
}
}
@@ -0,0 +1,22 @@
package com.sap.sailing.server.gateway.jaxrs;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
public class UnitSerializationUtil {
public static final DecimalFormat distanceDecimalFormatter = new DecimalFormat("#.##");
public static final DecimalFormat knotsDecimalFormatter = new DecimalFormat("#.##");
public static final DecimalFormat bearingDecimalFormatter = new DecimalFormat("#.#");
public static final DecimalFormat speedDecimalFormatter = new DecimalFormat("#.#");
public static final DecimalFormat latLngDecimalFormatter = new DecimalFormat("#.######");
static {
DecimalFormatSymbols symbol = DecimalFormatSymbols.getInstance();
symbol.setDecimalSeparator('.');
distanceDecimalFormatter.setDecimalFormatSymbols(symbol);
knotsDecimalFormatter.setDecimalFormatSymbols(symbol);
bearingDecimalFormatter.setDecimalFormatSymbols(symbol);
speedDecimalFormatter.setDecimalFormatSymbols(symbol);
latLngDecimalFormatter.setDecimalFormatSymbols(symbol);
}
}
@@ -0,0 +1,80 @@
package com.sap.sailing.server.gateway.jaxrs.api;
import java.util.ArrayList;
import java.util.Iterator;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.tracking.Wind;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.server.gateway.jaxrs.UnitSerializationUtil;
public class DefaultWindTrackJsonSerializer implements WindTrackJsonSerializer {
public static final String FIELD_ID = "id";
public static final String FIELD_NAME = "name";
public static final String FIELD_PUBLICATION_URL = "publicationUrl";
public static final String FIELD_VENUE = "venue";
private TimePoint fromTime;
private TimePoint toTime;
private WindSource windSource;
public JSONObject serialize(WindTrack windTrack) {
JSONObject result = new JSONObject();
JSONArray jsonWindFixes = new JSONArray();
// quickly extract relevant fixes to hold locks as shortly as possible; process later
ArrayList<Wind> fixes = new ArrayList<>();
windTrack.lockForRead();
try {
Iterator<Wind> windIter = windTrack.getFixesIterator(fromTime, /* inclusive */true);
while (windIter.hasNext()) {
Wind wind = windIter.next();
if (wind.getTimePoint().compareTo(toTime) > 0) {
break;
} else {
fixes.add(wind);
}
}
} finally {
windTrack.unlockAfterRead();
}
for (Wind wind : fixes) {
JSONObject jsonWind = new JSONObject();
jsonWind.put("trueBearing-deg", UnitSerializationUtil.bearingDecimalFormatter.format(wind.getBearing().getDegrees()));
jsonWind.put("speed-kts", UnitSerializationUtil.speedDecimalFormatter.format(wind.getKnots()));
jsonWind.put("speed-m/s", UnitSerializationUtil.speedDecimalFormatter.format(wind.getMetersPerSecond()));
if (wind.getTimePoint() != null) {
jsonWind.put("timepoint-ms", wind.getTimePoint().asMillis());
final Wind averagedWind = windTrack.getAveragedWind(wind.getPosition(), wind.getTimePoint());
jsonWind.put("dampenedTrueBearing-deg", UnitSerializationUtil.bearingDecimalFormatter.format(averagedWind.getBearing().getDegrees()));
jsonWind.put("dampenedSpeed-kts", UnitSerializationUtil.speedDecimalFormatter.format(averagedWind.getKnots()));
jsonWind.put("dampenedSpeed-m/s", UnitSerializationUtil.speedDecimalFormatter.format(averagedWind.getMetersPerSecond()));
}
if (wind.getPosition() != null) {
jsonWind.put("lat-deg", UnitSerializationUtil.latLngDecimalFormatter.format(wind.getPosition().getLatDeg()));
jsonWind.put("lng-deg", UnitSerializationUtil.latLngDecimalFormatter.format(wind.getPosition().getLngDeg()));
}
jsonWindFixes.add(jsonWind);
}
result.put(windSource.name(), jsonWindFixes);
return result;
}
public void setFromTime(TimePoint fromTime) {
this.fromTime = fromTime;
}
public void setToTime(TimePoint toTime) {
this.toTime = toTime;
}
public void setWindSource(WindSource windSource) {
this.windSource = windSource;
}
}
@@ -0,0 +1,47 @@
package com.sap.sailing.server.gateway.jaxrs.api;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
import org.json.simple.JSONArray;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource;
import com.sap.sailing.server.gateway.serialization.JsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.CourseAreaJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.EventJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.VenueJsonSerializer;
@Path("/v1/events")
public class EventsResource extends AbstractSailingServerResource {
@GET
@Produces("application/json;charset=UTF-8")
public Response getEvents() {
JsonSerializer<EventBase> eventSerializer = new EventJsonSerializer(new VenueJsonSerializer(new CourseAreaJsonSerializer()));
JSONArray result = new JSONArray();
for (EventBase event : getService().getAllEvents()) {
result.add(eventSerializer.serialize(event));
}
byte[] json = result.toJSONString().getBytes();
return Response.ok(json).build();
}
@GET
@Produces("application/json;charset=UTF-8")
@Path("{eventId}")
public Response getEvent(@PathParam("eventId") String eventId) {
return Response.noContent().build();
}
@GET
@Produces("application/json;charset=UTF-8")
@Path("{eventId}/racestates")
public Response getRaceStates(@PathParam("eventId") String eventId) {
return Response.noContent().build();
}
}
@@ -0,0 +1,138 @@
package com.sap.sailing.server.gateway.jaxrs.api;
import java.util.Map;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.leaderboard.MetaLeaderboard;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource;
@Path("/v1/leaderboardgroups")
public class LeaderboardGroupsResource extends AbstractSailingServerResource {
@GET
@Produces("application/json;charset=UTF-8")
public Response getLeaderboardGroups() {
JSONArray jsonLeaderboardGroups = new JSONArray();
Map<String, LeaderboardGroup> leaderboardGroups = getService().getLeaderboardGroups();
for (String leaderboardGroupName : leaderboardGroups.keySet()) {
jsonLeaderboardGroups.add(leaderboardGroupName);
}
byte[] json = jsonLeaderboardGroups.toJSONString().getBytes();
return Response.ok(json).build();
}
@GET
@Produces("application/json;charset=UTF-8")
@Path("{name}")
public Response getLeaderboardGroup(@PathParam("name") String leaderboardGroupName) {
Response response;
LeaderboardGroup leaderboardGroup = getService().getLeaderboardGroupByName(leaderboardGroupName);
if (leaderboardGroup == null) {
response = Response.status(Status.NOT_FOUND).entity("Could not find a leaderboard group with name '"+leaderboardGroupName+"'.").type(MediaType.TEXT_PLAIN).build();
} else {
TimePoint timePoint = MillisecondsTimePoint.now();
JSONObject jsonLeaderboardGroup = new JSONObject();
jsonLeaderboardGroup.put("name", leaderboardGroup.getName());
jsonLeaderboardGroup.put("description", leaderboardGroup.getDescription());
jsonLeaderboardGroup.put("timepoint", timePoint.toString());
JSONArray jsonLeaderboardEntries = new JSONArray();
jsonLeaderboardGroup.put("leaderboards", jsonLeaderboardEntries);
for (Leaderboard leaderboard: leaderboardGroup.getLeaderboards()) {
boolean isMetaLeaderboard = leaderboard instanceof MetaLeaderboard ? true : false;
boolean isRegattaLeaderboard = leaderboard instanceof RegattaLeaderboard ? true : false;
JSONObject jsonLeaderboard = new JSONObject();
jsonLeaderboard.put("name", leaderboard.getName());
jsonLeaderboard.put("displayName", leaderboard.getDisplayName());
jsonLeaderboard.put("isMetaLeaderboard", isMetaLeaderboard);
jsonLeaderboard.put("isRegattaLeaderboard", isRegattaLeaderboard);
jsonLeaderboardEntries.add(jsonLeaderboard);
SettableScoreCorrection scoreCorrection = leaderboard.getScoreCorrection();
if (scoreCorrection != null) {
jsonLeaderboard.put("scoringComment", scoreCorrection.getComment());
TimePoint lastUpdateTimepoint = scoreCorrection.getTimePointOfLastCorrectionsValidity();
jsonLeaderboard.put("lastScoringUpdate", lastUpdateTimepoint != null ? lastUpdateTimepoint.asDate().toString(): null);
} else {
jsonLeaderboard.put("scoringComment", null);
jsonLeaderboard.put("lastScoringUpdate", null);
}
if (isRegattaLeaderboard) {
RegattaLeaderboard regattaLeaderboard = (RegattaLeaderboard) leaderboard;
Regatta regatta = regattaLeaderboard.getRegatta();
jsonLeaderboard.put("scoringScheme", leaderboard.getScoringScheme().getType());
jsonLeaderboard.put("regattaName", regatta.getName());
} else {
jsonLeaderboard.put("scoringScheme", leaderboard.getScoringScheme().getType());
jsonLeaderboard.put("regattaName", null);
JSONArray jsonSeriesEntries = new JSONArray();
jsonLeaderboard.put("series", jsonSeriesEntries);
// write a 'default' series to be conform with our common regatta structure
JSONObject jsonSeries = new JSONObject();
jsonSeriesEntries.add(jsonSeries);
jsonSeries.put("name", LeaderboardNameConstants.DEFAULT_SERIES_NAME);
jsonSeries.put("isMedalSeries", null);
JSONArray jsonFleetsEntries = new JSONArray();
jsonSeries.put("fleets", jsonFleetsEntries);
Fleet fleet = leaderboard.getFleet(LeaderboardNameConstants.DEFAULT_FLEET_NAME);
if (fleet != null) {
JSONObject jsonFleet = new JSONObject();
jsonFleet.put("name", fleet.getName());
jsonFleet.put("color", fleet.getColor() != null ? fleet.getColor().getAsHtml() : null);
jsonFleet.put("ordering", fleet.getOrdering());
jsonFleetsEntries.add(jsonFleet);
JSONArray jsonRacesEntries = new JSONArray();
jsonFleet.put("races", jsonRacesEntries);
for (RaceColumn raceColumn: leaderboard.getRaceColumns()) {
JSONObject jsonRaceColumn = new JSONObject();
jsonRaceColumn.put("name", raceColumn.getName());
jsonRaceColumn.put("isMedalRace" , raceColumn.isMedalRace());
TrackedRace trackedRace = raceColumn.getTrackedRace(fleet);
if (trackedRace != null) {
jsonRaceColumn.put("isTracked", true);
jsonRaceColumn.put("regattaName", trackedRace.getTrackedRegatta().getRegatta().getName());
jsonRaceColumn.put("trackedRaceName", trackedRace.getRace().getName());
jsonRaceColumn.put("hasGpsData", trackedRace.hasGPSData());
jsonRaceColumn.put("hasWindData", trackedRace.hasWindData());
} else {
jsonRaceColumn.put("isTracked", false);
jsonRaceColumn.put("trackedRaceName", null);
jsonRaceColumn.put("hasGpsData", false);
jsonRaceColumn.put("hasWindData", false);
}
jsonRacesEntries.add(jsonRaceColumn);
}
}
}
}
byte[] json = jsonLeaderboardGroup.toJSONString().getBytes();
response = Response.ok(json).build();
}
return response;
}
}
@@ -0,0 +1,299 @@
package com.sap.sailing.server.gateway.jaxrs.api;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.Nationality;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.dto.CompetitorDTO;
import com.sap.sailing.domain.common.dto.FleetDTO;
import com.sap.sailing.domain.common.dto.LeaderboardDTO;
import com.sap.sailing.domain.common.dto.LeaderboardEntryDTO;
import com.sap.sailing.domain.common.dto.LeaderboardRowDTO;
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource;
@Path("/v1/leaderboards")
public class LeaderboardsResource extends AbstractSailingServerResource {
public enum ResultStates { Live, Preliminary, Final };
@GET
@Produces("application/json;charset=UTF-8")
public Response getLeaderboards() {
JSONArray jsonLeaderboards = new JSONArray();
Map<String, Leaderboard> leaderboards = getService().getLeaderboards();
for (String leaderboardName : leaderboards.keySet()) {
if(!leaderboardName.equals(LeaderboardNameConstants.DEFAULT_LEADERBOARD_NAME))
jsonLeaderboards.add(leaderboardName);
}
byte[] json = jsonLeaderboards.toJSONString().getBytes();
return Response.ok(json).build();
}
@GET
@Produces("application/json;charset=UTF-8")
@Path("{name}")
public Response getLeaderboard(@PathParam("name") String leaderboardName,
@DefaultValue("Live") @QueryParam("resultState") ResultStates resultState,
@DefaultValue("true") @QueryParam("useCache") boolean useCache,
@DefaultValue("1000") @QueryParam("maxCompetitorsCount") int maxCompetitorsCount) {
Response response;
TimePoint requestTimePoint = MillisecondsTimePoint.now();
Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName);
if (leaderboard == null) {
response = Response.status(Status.NOT_FOUND).entity("Could not find a leaderboard with name '" + leaderboardName + "'.").type(MediaType.TEXT_PLAIN).build();
} else {
try {
TimePoint resultTimePoint = calculateTimePointForResultState(leaderboard, resultState);
JSONObject jsonLeaderboard;
if (resultTimePoint != null) {
Triple<TimePoint, ResultStates, Integer> resultStateAndTimePoint = new Triple<>(resultTimePoint, resultState, maxCompetitorsCount);
jsonLeaderboard = getLeaderboardJson(leaderboard, resultStateAndTimePoint);
} else {
jsonLeaderboard = createEmptyLeaderboardJson(leaderboard, resultState, requestTimePoint, maxCompetitorsCount);
}
StringWriter sw = new StringWriter();
jsonLeaderboard.writeJSONString(sw);
byte[] json = sw.getBuffer().toString().getBytes();
response = Response.ok(json).build();
} catch (NoWindException | InterruptedException | ExecutionException | IOException e) {
response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).type(MediaType.TEXT_PLAIN).build();
}
}
return response;
}
private JSONObject getLeaderboardJson(Leaderboard leaderboard, Triple<TimePoint, ResultStates, Integer> timePointAndResultStateAndMaxCompetitorsCount)
throws NoWindException, InterruptedException, ExecutionException {
LeaderboardDTO leaderboardDTO = leaderboard.getLeaderboardDTO(timePointAndResultStateAndMaxCompetitorsCount.getA(),
Collections.<String> emptyList(), getService(), getService().getBaseDomainFactory());
TimePoint resultTimePoint = timePointAndResultStateAndMaxCompetitorsCount.getA();
ResultStates resultState = timePointAndResultStateAndMaxCompetitorsCount.getB();
Integer maxCompetitorsCount = timePointAndResultStateAndMaxCompetitorsCount.getC();
JSONObject jsonLeaderboard = new JSONObject();
writeCommonLeaderboardData(jsonLeaderboard, leaderboardDTO, resultState, resultTimePoint, maxCompetitorsCount);
JSONArray jsonCompetitorEntries = new JSONArray();
jsonLeaderboard.put("competitors", jsonCompetitorEntries);
int counter = 1;
for (CompetitorDTO competitor : leaderboardDTO.competitors) {
LeaderboardRowDTO leaderboardRowDTO = leaderboardDTO.rows.get(competitor);
if(maxCompetitorsCount != null && counter > maxCompetitorsCount) {
break;
}
JSONObject jsonCompetitor = new JSONObject();
jsonCompetitor.put("name", competitor.getName());
final String displayName = leaderboardDTO.getDisplayName(competitor);
jsonCompetitor.put("displayName", displayName==null?competitor.getName():displayName);
jsonCompetitor.put("id", competitor.getIdAsString());
jsonCompetitor.put("sailID", competitor.getSailID());
jsonCompetitor.put("nationality", competitor.getThreeLetterIocCountryCode());
jsonCompetitor.put("countryCode", competitor.getTwoLetterIsoCountryCode());
jsonCompetitor.put("rank", counter);
jsonCompetitor.put("carriedPoints", leaderboardRowDTO.carriedPoints);
jsonCompetitor.put("totalPoints", leaderboardRowDTO.totalPoints);
jsonCompetitorEntries.add(jsonCompetitor);
JSONObject jsonRaceColumns = new JSONObject();
jsonCompetitor.put("raceScores", jsonRaceColumns);
for (RaceColumnDTO raceColumn : leaderboardDTO.getRaceList()) {
List<CompetitorDTO> rankedCompetitorsForColumn = leaderboardDTO.getCompetitorOrderingPerRaceColumnName().get(raceColumn.getName());
JSONObject jsonEntry = new JSONObject();
jsonRaceColumns.put(raceColumn.getName(), jsonEntry);
LeaderboardEntryDTO leaderboardEntry = leaderboardRowDTO.fieldsByRaceColumnName.get(raceColumn.getName());
final FleetDTO fleetOfCompetitor = leaderboardEntry.fleet;
jsonEntry.put("fleet", fleetOfCompetitor==null?"":fleetOfCompetitor.getName());
jsonEntry.put("netPoints", leaderboardEntry.netPointsCorrected);
jsonEntry.put("uncorrectedNetPoints", leaderboardEntry.netPoints);
jsonEntry.put("totalPoints", leaderboardEntry.totalPoints);
MaxPointsReason maxPointsReason = leaderboardEntry.reasonForMaxPoints;
jsonEntry.put("maxPointsReason", maxPointsReason != null ? maxPointsReason.toString(): null);
jsonEntry.put("rank", rankedCompetitorsForColumn.indexOf(competitor)+1);
// final TrackedRace trackedRace = raceColumn.getTrackedRace(competitor);
// if (trackedRace != null) {
// jsonEntry.put("raceRank", trackedRace.getRank(competitor, resultTimePoint));
// }
jsonEntry.put("isDiscarded", leaderboardEntry.discarded);
jsonEntry.put("isCorrected", leaderboardEntry.hasScoreCorrection());
}
counter++;
}
return jsonLeaderboard;
}
private JSONObject createEmptyLeaderboardJson(Leaderboard leaderboard,
ResultStates resultState, TimePoint requestTimePoint, Integer maxCompetitorsCount) throws NoWindException {
JSONObject jsonLeaderboard = new JSONObject();
writeCommonLeaderboardData(jsonLeaderboard, leaderboard, resultState, null, maxCompetitorsCount);
JSONArray jsonCompetitorEntries = new JSONArray();
jsonLeaderboard.put("competitors", jsonCompetitorEntries);
for (Competitor competitor : leaderboard.getCompetitors()) {
JSONObject jsonCompetitor = new JSONObject();
jsonCompetitor.put("name", competitor.getName());
final String displayName = leaderboard.getDisplayName(competitor);
jsonCompetitor.put("displayName", displayName==null?competitor.getName():displayName);
jsonCompetitor.put("id", competitor.getId().toString());
jsonCompetitor.put("sailID", competitor.getBoat().getSailID());
Nationality nationality = competitor.getTeam().getNationality();
jsonCompetitor.put("nationality", nationality != null ? nationality.getThreeLetterIOCAcronym(): null);
jsonCompetitor.put("countryCode", nationality != null ? (nationality.getCountryCode() != null ? nationality.getCountryCode().getTwoLetterISOCode(): null) : null);
jsonCompetitor.put("rank", 0);
jsonCompetitor.put("carriedPoints", null);
jsonCompetitor.put("totalPoints", null);
jsonCompetitorEntries.add(jsonCompetitor);
JSONObject jsonRaceColumns = new JSONObject();
jsonCompetitor.put("raceScores", jsonRaceColumns);
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
JSONObject jsonEntry = new JSONObject();
jsonRaceColumns.put(raceColumn.getName(), jsonEntry);
final Fleet fleetOfCompetitor = raceColumn.getFleetOfCompetitor(competitor);
jsonEntry.put("fleet", fleetOfCompetitor==null?"":fleetOfCompetitor.getName());
jsonEntry.put("netPoints", null);
jsonEntry.put("totalPoints", null);
jsonEntry.put("maxPointsReason", "");
jsonEntry.put("rank", 0);
jsonEntry.put("isDiscarded", false);
jsonEntry.put("isCorrected", false);
}
}
return jsonLeaderboard;
}
private void writeCommonLeaderboardData(JSONObject jsonLeaderboard, LeaderboardDTO leaderboard, ResultStates resultState,
TimePoint resultTimePoint, Integer maxCompetitorsCount) {
jsonLeaderboard.put("name", leaderboard.name);
jsonLeaderboard.put("resultTimepoint", resultTimePoint != null ? resultTimePoint.asMillis() : null);
jsonLeaderboard.put("resultState", resultState.name());
jsonLeaderboard.put("maxCompetitorsCount", maxCompetitorsCount);
jsonLeaderboard.put("scoringComment", leaderboard.getComment());
Date lastUpdateTimepoint = leaderboard.getTimePointOfLastCorrectionsValidity();
jsonLeaderboard.put("lastScoringUpdate", lastUpdateTimepoint != null ? lastUpdateTimepoint.getTime(): null);
JSONArray jsonColumnNames = new JSONArray();
jsonLeaderboard.put("columnNames", jsonColumnNames);
for (RaceColumnDTO raceColumn : leaderboard.getRaceList()) {
jsonColumnNames.add(raceColumn.getName());
}
}
private void writeCommonLeaderboardData(JSONObject jsonLeaderboard, Leaderboard leaderboard, ResultStates resultState,
TimePoint resultTimePoint, Integer maxCompetitorsCount) {
jsonLeaderboard.put("name", leaderboard.getName());
jsonLeaderboard.put("resultTimepoint", resultTimePoint != null ? resultTimePoint.asMillis() : null);
jsonLeaderboard.put("resultState", resultState.name());
jsonLeaderboard.put("maxCompetitorsCount", maxCompetitorsCount);
SettableScoreCorrection scoreCorrection = leaderboard.getScoreCorrection();
if (scoreCorrection != null) {
jsonLeaderboard.put("scoringComment", scoreCorrection.getComment());
TimePoint lastUpdateTimepoint = scoreCorrection.getTimePointOfLastCorrectionsValidity();
jsonLeaderboard.put("lastScoringUpdate", lastUpdateTimepoint != null ? lastUpdateTimepoint.asMillis(): null);
} else {
jsonLeaderboard.put("scoringComment", null);
jsonLeaderboard.put("lastScoringUpdate", null);
}
JSONArray jsonColumnNames = new JSONArray();
jsonLeaderboard.put("columnNames", jsonColumnNames);
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
jsonColumnNames.add(raceColumn.getName());
}
}
private TimePoint calculateTimePointForResultState(Leaderboard leaderboard, ResultStates resultState) {
TimePoint result = null;
switch (resultState) {
case Live:
result = leaderboard.getTimePointOfLatestModification();
if (result == null) {
result = MillisecondsTimePoint.now();
}
break;
case Preliminary:
case Final:
if (leaderboard.getScoreCorrection() != null && leaderboard.getScoreCorrection().getTimePointOfLastCorrectionsValidity() != null) {
result = leaderboard.getScoreCorrection().getTimePointOfLastCorrectionsValidity();
// As we don't have implemented bug 1246 (Define a clear result state for races and leaderboards) so far
// we need to make sure that the timpoint for the final state is not determined in the middle of a running race,
// because this would deliver not only final results but also some "mixed-in" live results.
// Therefore, if there is a race that hasn't finished yet and whose first start mark passing is before
// the current result, move result to before the start mark passing.
for (TrackedRace trackedRace : leaderboard.getTrackedRaces()) {
TimePoint endOfRace = trackedRace.getEndOfRace();
if (endOfRace == null) {
Waypoint firstWaypoint = trackedRace.getRace().getCourse().getFirstWaypoint();
if (firstWaypoint != null) {
Iterable<MarkPassing> markPassingsForFirstWaypoint = trackedRace.getMarkPassingsInOrder(firstWaypoint);
if (markPassingsForFirstWaypoint != null) {
trackedRace.lockForRead(markPassingsForFirstWaypoint);
try {
Iterator<MarkPassing> i = markPassingsForFirstWaypoint.iterator();
if (i.hasNext()) {
TimePoint earliestMarkPassingTimePoint = i.next().getTimePoint();
if (result == null || earliestMarkPassingTimePoint.before(result)) {
result = earliestMarkPassingTimePoint.minus(1);
}
}
} finally {
trackedRace.unlockAfterRead(markPassingsForFirstWaypoint);
}
}
}
}
}
}
break;
}
return result;
}
}
@@ -0,0 +1,571 @@
package com.sap.sailing.server.gateway.jaxrs.api;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.Mark;
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.Distance;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.RegattaName;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.common.impl.Util.Pair;
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.TrackedLeg;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource;
import com.sap.sailing.server.gateway.jaxrs.UnitSerializationUtil;
import com.sap.sailing.server.gateway.serialization.JsonSerializer;
import com.sap.sailing.server.gateway.serialization.coursedata.impl.ControlPointJsonSerializer;
import com.sap.sailing.server.gateway.serialization.coursedata.impl.CourseBaseJsonSerializer;
import com.sap.sailing.server.gateway.serialization.coursedata.impl.GateJsonSerializer;
import com.sap.sailing.server.gateway.serialization.coursedata.impl.MarkJsonSerializer;
import com.sap.sailing.server.gateway.serialization.coursedata.impl.WaypointJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.ColorJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.CompetitorJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.FleetJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.NationalityJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.PersonJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.RegattaJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.SeriesJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.TeamJsonSerializer;
import com.sap.sailing.util.InvalidDateException;
@Path("/v1/regattas")
public class RegattasResource extends AbstractSailingServerResource {
private Response getBadRegattaErrorResponse(String regattaName) {
return Response.status(Status.NOT_FOUND).entity("Could not find a regatta with name '" + regattaName + "'.").type(MediaType.TEXT_PLAIN).build();
}
private Response getBadRaceErrorResponse(String regattaName, String raceName) {
return Response.status(Status.NOT_FOUND).entity("Could not find a race with name '" + raceName + "' in regatta '" + regattaName + "'.").type(MediaType.TEXT_PLAIN).build();
}
@GET
@Produces("application/json;charset=UTF-8")
public Response getRegattas() {
RegattaJsonSerializer regattaJsonSerializer = new RegattaJsonSerializer();
JSONArray regattasJson = new JSONArray();
for (Regatta regatta : getService().getAllRegattas()) {
regattasJson.add(regattaJsonSerializer.serialize(regatta));
}
byte[] json = regattasJson.toJSONString().getBytes();
return Response.ok(json).build();
}
@GET
@Produces("application/json;charset=UTF-8")
@Path("{regattaname}")
public Response getRegatta(@PathParam("regattaname") String regattaName) {
Response response;
Regatta regatta = getService().getRegatta(new RegattaName(regattaName));
if (regatta == null) {
response = getBadRegattaErrorResponse(regattaName);
} else {
SeriesJsonSerializer seriesJsonSerializer = new SeriesJsonSerializer(new FleetJsonSerializer(new ColorJsonSerializer()));
JsonSerializer<Regatta> regattaSerializer = new RegattaJsonSerializer(seriesJsonSerializer, null);
JSONObject serializedRegatta = regattaSerializer.serialize(regatta);
byte[] json = serializedRegatta.toJSONString().getBytes();
response = Response.ok(json).build();
}
return response;
}
/**
* Gets all entries for a regatta.
* @param regattaName the name of the regatta
* @return
*/
@GET
@Produces("application/json;charset=UTF-8")
@Path("{regattaname}/entries")
public Response getEntries(@PathParam("regattaname") String regattaName) {
Response response;
Regatta regatta = getService().getRegatta(new RegattaName(regattaName));
if (regatta == null) {
response = getBadRegattaErrorResponse(regattaName);
} else {
NationalityJsonSerializer nationalityJsonSerializer = new NationalityJsonSerializer();
CompetitorJsonSerializer competitorJsonSerializer = new CompetitorJsonSerializer(
new TeamJsonSerializer(new PersonJsonSerializer(nationalityJsonSerializer)));
JsonSerializer<Regatta> regattaSerializer = new RegattaJsonSerializer(null, competitorJsonSerializer);
JSONObject serializedRegatta = regattaSerializer.serialize(regatta);
byte[] json = serializedRegatta.toJSONString().getBytes();
response = Response.ok(json).build();
}
return response;
}
/**
* Gets all GPS positions of the competitors for a given race.
* @param regattaName the name of the regatta
* @return
*/
@GET
@Produces("application/json;charset=UTF-8")
@Path("{regattaname}/races/{racename}/competitors/positions")
public Response getCompetitorPositions(@PathParam("regattaname") String regattaName, @PathParam("racename") String raceName,
@QueryParam("fromtime") String fromtime, @QueryParam("fromtimeasmillis") Long fromtimeasmillis,
@QueryParam("totime") String totime, @QueryParam("totimeasmillis") Long totimeasmillis) {
Response response;
Regatta regatta = findRegattaByName(regattaName);
if (regatta == null) {
response = getBadRegattaErrorResponse(regattaName);
} else {
RaceDefinition race = findRaceByName(regatta, raceName);
if (race == null) {
response = getBadRaceErrorResponse(regattaName, raceName);
} else {
TrackedRace trackedRace = findTrackedRace(regattaName, raceName);
TimePoint from;
TimePoint to;
try {
from = parseTimePoint(fromtime, fromtimeasmillis,
trackedRace.getStartOfRace() == null ? new MillisecondsTimePoint(0) :
/* 24h before race start */ new MillisecondsTimePoint(trackedRace.getStartOfRace().asMillis()-24*3600*1000));
} catch (InvalidDateException e1) {
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the 'from' time.").type(MediaType.TEXT_PLAIN).build();
}
try {
to = parseTimePoint(totime, totimeasmillis, MillisecondsTimePoint.now());
} catch (InvalidDateException e1) {
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the 'to' time.").type(MediaType.TEXT_PLAIN).build();
}
JSONObject jsonRace = new JSONObject();
jsonRace.put("name", trackedRace.getRace().getName());
jsonRace.put("regatta", regatta.getName());
JSONArray jsonCompetitors = new JSONArray();
for (Competitor competitor : trackedRace.getRace().getCompetitors()) {
JSONObject jsonCompetitor = new JSONObject();
jsonCompetitor.put("id", competitor.getId() != null ? competitor.getId().toString() : null);
jsonCompetitor.put("name", competitor.getName());
jsonCompetitor.put("sailNumber", competitor.getBoat().getSailID());
GPSFixTrack<Competitor, GPSFixMoving> track = trackedRace.getTrack(competitor);
JSONArray jsonFixes = new JSONArray();
track.lockForRead();
try {
Iterator<GPSFixMoving> fixIter;
if (from == null) {
fixIter = track.getFixes().iterator();
} else {
fixIter = track.getFixesIterator(from, /* inclusive */true);
}
while (fixIter.hasNext()) {
GPSFixMoving fix = fixIter.next();
if (to != null && fix.getTimePoint() != null && to.compareTo(fix.getTimePoint()) < 0) {
break;
}
JSONObject jsonFix = new JSONObject();
jsonFix.put("timepoint-ms", fix.getTimePoint().asMillis());
jsonFix.put("lat-deg", UnitSerializationUtil.latLngDecimalFormatter.format(fix.getPosition().getLatDeg()));
jsonFix.put("lng-deg", UnitSerializationUtil.latLngDecimalFormatter.format(fix.getPosition().getLngDeg()));
jsonFix.put("truebearing-deg", fix.getSpeed().getBearing().getDegrees());
jsonFix.put("speed-kts", UnitSerializationUtil.knotsDecimalFormatter.format(fix.getSpeed().getKnots()));
String tackName;
try {
final Tack tack = trackedRace.getTack(competitor, fix.getTimePoint());
if (tack != null) {
tackName = tack.name();
jsonFix.put("tack", tackName);
}
} catch (NoWindException e) {
// don't output tack
}
jsonFixes.add(jsonFix);
}
} finally {
track.unlockAfterRead();
}
jsonCompetitor.put("track", jsonFixes);
jsonCompetitors.add(jsonCompetitor);
}
jsonRace.put("competitors", jsonCompetitors);
byte[] json = jsonRace.toJSONString().getBytes();
response = Response.ok(json).build();
}
}
return response;
}
/**
* Gets all GPS positions of the course marks for a given race.
* @param regattaName the name of the regatta
* @return
*/
@GET
@Produces("application/json;charset=UTF-8")
@Path("{regattaname}/races/{racename}/marks/positions")
public Response getMarkPositions(@PathParam("regattaname") String regattaName, @PathParam("racename") String raceName,
@QueryParam("fromtime") String fromtime, @QueryParam("fromtimeasmillis") Long fromtimeasmillis,
@QueryParam("totime") String totime, @QueryParam("totimeasmillis") Long totimeasmillis) {
Response response;
Regatta regatta = findRegattaByName(regattaName);
if (regatta == null) {
response = getBadRegattaErrorResponse(regattaName);
} else {
RaceDefinition race = findRaceByName(regatta, raceName);
if (race == null) {
response = getBadRaceErrorResponse(regattaName, raceName);
} else {
TrackedRace trackedRace = findTrackedRace(regattaName, raceName);
TimePoint from;
TimePoint to;
try {
from = parseTimePoint(fromtime, fromtimeasmillis,
trackedRace.getStartOfRace() == null ? new MillisecondsTimePoint(0) :
/* 24h before race start */ new MillisecondsTimePoint(trackedRace.getStartOfRace().asMillis()-24*3600*1000));
} catch (InvalidDateException e1) {
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the 'from' time.").type(MediaType.TEXT_PLAIN).build();
}
try {
to = parseTimePoint(totime, totimeasmillis, MillisecondsTimePoint.now());
} catch (InvalidDateException e1) {
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the 'to' time.").type(MediaType.TEXT_PLAIN).build();
}
JSONObject jsonRace = new JSONObject();
jsonRace.put("name", trackedRace.getRace().getName());
jsonRace.put("regatta", regatta.getName());
JSONArray jsonMarks = new JSONArray();
Set<Mark> marks = new HashSet<Mark>();
Course course = trackedRace.getRace().getCourse();
for (Waypoint waypoint : course.getWaypoints()) {
for (Mark mark : waypoint.getMarks()) {
marks.add(mark);
}
}
for (Mark mark : marks) {
JSONObject jsonMark = new JSONObject();
jsonMark.put("name", mark.getName());
jsonMark.put("id", mark.getId() != null ? mark.getId().toString() : null);
GPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(mark);
JSONArray jsonFixes = new JSONArray();
track.lockForRead();
try {
Iterator<GPSFix> fixIter;
if (from == null) {
fixIter = track.getFixes().iterator();
} else {
fixIter = track.getFixesIterator(from, /* inclusive */true);
}
while (fixIter.hasNext()) {
GPSFix fix = fixIter.next();
if (to != null && fix.getTimePoint() != null && to.compareTo(fix.getTimePoint()) < 0) {
break;
}
JSONObject jsonFix = new JSONObject();
jsonFix.put("timepoint-ms", fix.getTimePoint().asMillis());
jsonFix.put("lat-deg", UnitSerializationUtil.latLngDecimalFormatter.format(fix.getPosition().getLatDeg()));
jsonFix.put("lng-deg", UnitSerializationUtil.latLngDecimalFormatter.format(fix.getPosition().getLngDeg()));
jsonFixes.add(jsonFix);
}
} finally {
track.unlockAfterRead();
}
jsonMark.put("track", jsonFixes);
jsonMarks.add(jsonMark);
}
jsonRace.put("marks", jsonMarks);
byte[] json = jsonRace.toJSONString().getBytes();
response = Response.ok(json).build();
}
}
return response;
}
/**
* Gets the course of the race.
* @param regattaName the name of the regatta
* @return
*/
@GET
@Produces("application/json;charset=UTF-8")
@Path("{regattaname}/races/{racename}/course")
public Response getCourse(@PathParam("regattaname") String regattaName, @PathParam("racename") String raceName) {
Response response;
Regatta regatta = findRegattaByName(regattaName);
if (regatta == null) {
response = getBadRegattaErrorResponse(regattaName);
} else {
RaceDefinition race = findRaceByName(regatta, raceName);
if (race == null) {
response = getBadRaceErrorResponse(regattaName, raceName);
} else {
Course course = race.getCourse();
CourseBaseJsonSerializer serializer = new CourseBaseJsonSerializer(
new WaypointJsonSerializer(
new ControlPointJsonSerializer(
new MarkJsonSerializer(),
new GateJsonSerializer(new MarkJsonSerializer()))));
JSONObject jsonCourse = serializer.serialize(course);
byte[] json = jsonCourse.toJSONString().getBytes();
response = Response.ok(json).build();
}
}
return response;
}
/**
* Gets the relevant times of the race.
* @param regattaName the name of the regatta
* @return
*/
@GET
@Produces("application/json;charset=UTF-8")
@Path("{regattaname}/races/{racename}/times")
public Response getTimes(@PathParam("regattaname") String regattaName, @PathParam("racename") String raceName) {
Response response = null;
Regatta regatta = findRegattaByName(regattaName);
if (regatta == null) {
response = getBadRegattaErrorResponse(regattaName);
} else {
RaceDefinition race = findRaceByName(regatta, raceName);
if (race == null) {
response = getBadRaceErrorResponse(regattaName, raceName);
} else {
TrackedRace trackedRace = findTrackedRace(regattaName, raceName);
JSONObject jsonRaceTimes = new JSONObject();
jsonRaceTimes.put("name", trackedRace.getRace().getName());
jsonRaceTimes.put("regatta", regatta.getName());
jsonRaceTimes.put("startOfRace-ms", trackedRace.getStartOfRace() == null ? null : trackedRace.getStartOfRace().asMillis());
jsonRaceTimes.put("startOfTracking-ms", trackedRace.getStartOfTracking() == null ? null : trackedRace.getStartOfTracking().asMillis());
jsonRaceTimes.put("newestTrackingEvent-ms", trackedRace.getTimePointOfNewestEvent() == null ? null : trackedRace.getTimePointOfNewestEvent().asMillis());
jsonRaceTimes.put("endOfTracking-ms", trackedRace.getEndOfTracking() == null ? null : trackedRace.getEndOfTracking().asMillis());
jsonRaceTimes.put("endOfRace-ms", trackedRace.getEndOfRace() == null ? null : trackedRace.getEndOfRace().asMillis());
jsonRaceTimes.put("delayToLive-ms", trackedRace.getDelayToLiveInMillis());
JSONArray jsonMarkPassingTimes = new JSONArray();
List<TimePoint> firstPassingTimepoints = new ArrayList<>();
Iterable<Pair<Waypoint, Pair<TimePoint, TimePoint>>> markPassingsTimes = trackedRace.getMarkPassingsTimes();
synchronized (markPassingsTimes) {
int numberOfWaypoints = Util.size(markPassingsTimes);
int wayPointNumber = 1;
for (Pair<Waypoint, Pair<TimePoint, TimePoint>> markPassingTimes : markPassingsTimes) {
JSONObject jsonMarkPassing = new JSONObject();
String name = "M" + (wayPointNumber - 1);
if (wayPointNumber == numberOfWaypoints) {
name = "F";
}
jsonMarkPassing.put("name", name);
Pair<TimePoint, TimePoint> timesPair = markPassingTimes.getB();
TimePoint firstPassingTime = timesPair.getA();
TimePoint lastPassingTime = timesPair.getB();
jsonMarkPassing.put("firstPassing-ms", firstPassingTime == null ? null : firstPassingTime.asMillis());
jsonMarkPassing.put("lastPassing-ms", lastPassingTime == null ? null : lastPassingTime.asMillis());
firstPassingTimepoints.add(firstPassingTime);
jsonMarkPassingTimes.add(jsonMarkPassing);
wayPointNumber++;
}
}
jsonRaceTimes.put("markPassings", jsonMarkPassingTimes);
JSONArray jsonLegInfos = new JSONArray();
trackedRace.getRace().getCourse().lockForRead();
try {
Iterable<TrackedLeg> trackedLegs = trackedRace.getTrackedLegs();
int legNumber = 1;
for (TrackedLeg trackedLeg : trackedLegs) {
JSONObject jsonLegInfo = new JSONObject();
jsonLegInfo.put("name", "L" + legNumber);
try {
TimePoint firstPassingTime = firstPassingTimepoints.get(legNumber - 1);
if (firstPassingTime != null) {
jsonLegInfo.put("type", trackedLeg.getLegType(firstPassingTime));
jsonLegInfo.put("bearing-deg", UnitSerializationUtil.bearingDecimalFormatter.format(trackedLeg.getLegBearing(firstPassingTime).getDegrees()));
}
} catch (NoWindException e) {
// do nothing
}
jsonLegInfos.add(jsonLegInfo);
legNumber++;
}
} finally {
trackedRace.getRace().getCourse().unlockAfterRead();
}
jsonRaceTimes.put("legs", jsonLegInfos);
Date now = new Date();
jsonRaceTimes.put("currentServerTime-ms", now.getTime());
byte[] json = jsonRaceTimes.toJSONString().getBytes();
response = Response.ok(json).build();
}
}
return response;
}
@GET
@Produces("application/json;charset=UTF-8")
@Path("{regattaname}/races/{racename}/wind")
public Response getWind(@PathParam("regattaname") String regattaName, @PathParam("racename") String raceName,
@DefaultValue("COMBINED") @QueryParam("windsource") String windSource,
@QueryParam("fromtime") String fromtime, @QueryParam("fromtimeasmillis") Long fromtimeasmillis,
@QueryParam("totime") String totime, @QueryParam("totimeasmillis") Long totimeasmillis) {
Response response;
Regatta regatta = findRegattaByName(regattaName);
if (regatta == null) {
response = Response.status(Status.NOT_FOUND).entity("Could not find a regatta with name '" + regattaName + "'.").type(MediaType.TEXT_PLAIN).build();
} else {
RaceDefinition race = findRaceByName(regatta, raceName);
if (race == null) {
response = Response.status(Status.NOT_FOUND).entity("Could not find a race with name '" + raceName + "'.").type(MediaType.TEXT_PLAIN).build();
} else {
TrackedRace trackedRace = findTrackedRace(regattaName, raceName);
TimePoint from;
TimePoint to;
try {
from = parseTimePoint(fromtime, fromtimeasmillis,
trackedRace.getStartOfRace() == null ? new MillisecondsTimePoint(0) :
/* 24h before race start */ new MillisecondsTimePoint(trackedRace.getStartOfRace().asMillis()-24*3600*1000));
} catch (InvalidDateException e1) {
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the 'from' time.").type(MediaType.TEXT_PLAIN).build();
}
try {
to = parseTimePoint(totime, totimeasmillis, MillisecondsTimePoint.now());
} catch (InvalidDateException e1) {
return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the 'to' time.").type(MediaType.TEXT_PLAIN).build();
}
TrackedRaceJsonSerializer serializer = new TrackedRaceJsonSerializer(new DefaultWindTrackJsonSerializer());
serializer.setWindSource(windSource);
serializer.setFromTime(from);
serializer.setToTime(to);
JSONObject jsonWindTracks = serializer.serialize(trackedRace);
byte[] json = jsonWindTracks.toJSONString().getBytes();
return Response.ok(json).build();
}
}
return response;
}
@GET
@Produces("application/json;charset=UTF-8")
@Path("{regattaname}/races/{racename}/competitors/legs")
public Response getCompetitorRanks(@PathParam("regattaname") String regattaName, @PathParam("racename") String raceName) {
Response response;
Regatta regatta = findRegattaByName(regattaName);
if (regatta == null) {
response = Response.status(Status.NOT_FOUND).entity("Could not find a regatta with name '" + regattaName + "'.").type(MediaType.TEXT_PLAIN).build();
} else {
RaceDefinition race = findRaceByName(regatta, raceName);
if (race == null) {
response = Response.status(Status.NOT_FOUND).entity("Could not find a race with name '" + raceName + "'.").type(MediaType.TEXT_PLAIN).build();
} else {
TrackedRace trackedRace = findTrackedRace(regattaName, raceName);
TimePoint timePoint = trackedRace.getTimePointOfNewestEvent() == null ? MillisecondsTimePoint.now()
: trackedRace.getTimePointOfNewestEvent();
JSONObject jsonRaceResults = new JSONObject();
jsonRaceResults.put("name", trackedRace.getRace().getName());
jsonRaceResults.put("regatta", regatta.getName());
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<Competitor, Integer> 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());
Distance distanceTraveled = trackedLegOfCompetitor.getDistanceTraveled(timePoint);
if (distanceTraveled != null) {
jsonCompetitorInLeg.put("distanceTraveled-m", UnitSerializationUtil.distanceDecimalFormatter.format(distanceTraveled.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));
} 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);
}
jsonRaceResults.put("legs", jsonLegs);
byte[] json = jsonRaceResults.toJSONString().getBytes();
return Response.ok(json).build();
}
}
return response;
}
}
@@ -0,0 +1,20 @@
package com.sap.sailing.server.gateway.jaxrs.api;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.core.Application;
public class RestApiApplication extends Application {
public Set<Class<?>> getClasses() {
HashSet<Class<?>> classes = new HashSet<>();
classes.add(LeaderboardGroupsResource.class);
classes.add(EventsResource.class);
classes.add(RegattasResource.class);
classes.add(LeaderboardsResource.class);
// classes.add(WindResource.class);
return classes;
}
}
@@ -0,0 +1,85 @@
package com.sap.sailing.server.gateway.jaxrs.api;
import java.util.ArrayList;
import java.util.List;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
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.WindSourceImpl;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.server.gateway.serialization.JsonSerializer;
public class TrackedRaceJsonSerializer implements JsonSerializer<TrackedRace> {
public static final String FIELD_ID = "id";
public static final String FIELD_NAME = "name";
public static final String FIELD_REGATTA = "regatta";
public static final String FIELD_WINDSOURCES = "windSources";
public static final String ALL_WINDSOURCES = "ALL";
private String windSourceToSerialize;
private TimePoint fromTime;
private TimePoint toTime;
private final WindTrackJsonSerializer windTrackSerializer;
public TrackedRaceJsonSerializer(WindTrackJsonSerializer windTrackSerializer) {
this.windTrackSerializer = windTrackSerializer;
windSourceToSerialize = WindSourceType.COMBINED.name();
}
public JSONObject serialize(TrackedRace trackedRace) {
JSONObject jsonRace = new JSONObject();
jsonRace.put(FIELD_NAME, trackedRace.getRace().getName());
jsonRace.put(FIELD_REGATTA, trackedRace.getRaceIdentifier().getRegattaName());
if(windTrackSerializer != null) {
JSONArray windTracks = new JSONArray();
List<WindSource> windSources = getAvailableWindSources(trackedRace);
for (WindSource windSource : windSources) {
if (ALL_WINDSOURCES.equals(windSourceToSerialize) || windSource.getType().name().equalsIgnoreCase(windSourceToSerialize)) {
windTrackSerializer.setFromTime(fromTime);
windTrackSerializer.setToTime(toTime);
windTrackSerializer.setWindSource(windSource);
WindTrack windTrack = trackedRace.getOrCreateWindTrack(windSource);
JSONObject jsonWindTrack = windTrackSerializer.serialize(windTrack);
windTracks.add(jsonWindTrack);
}
}
jsonRace.put(FIELD_WINDSOURCES, windTracks);
}
return jsonRace;
}
public void setWindSource(String windSourceToSerialize) {
this.windSourceToSerialize = windSourceToSerialize;
}
public void setFromTime(TimePoint fromTime) {
this.fromTime = fromTime;
}
public void setToTime(TimePoint toTime) {
this.toTime = toTime;
}
private List<WindSource> getAvailableWindSources(TrackedRace trackedRace) {
List<WindSource> windSources = new ArrayList<WindSource>();
for (WindSource windSource : trackedRace.getWindSources()) {
windSources.add(windSource);
}
for (WindSource windSourceToExclude : trackedRace.getWindSourcesToExclude()) {
windSources.remove(windSourceToExclude);
}
windSources.add(new WindSourceImpl(WindSourceType.COMBINED));
return windSources;
}
}
@@ -0,0 +1,12 @@
package com.sap.sailing.server.gateway.jaxrs.api;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.server.gateway.serialization.JsonSerializer;
public interface WindTrackJsonSerializer extends JsonSerializer<WindTrack> {
void setFromTime(TimePoint fromTime);
void setToTime(TimePoint toTime);
void setWindSource(WindSource windSource);
}
@@ -0,0 +1,45 @@
package com.sap.sailing.server.gateway.jaxrs.spi;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import org.json.simple.JSONObject;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource;
import com.sap.sailing.server.gateway.serialization.masterdata.impl.TopLevelMasterDataSerializer;
@Path("/v1/leaderboardgroups/structure")
public class LeaderboardGroupsStructureResource extends AbstractSailingServerResource {
@GET
@Produces("application/json;charset=UTF-8")
public Response getStructureOfLeaderboardGroups(@QueryParam("names") List<String> leaderboardGroupNames) {
Map<String, LeaderboardGroup> leaderboardGroups = getService().getLeaderboardGroups();
Set<String> requestedLeaderboardGroupNames = new HashSet<String>();
if (leaderboardGroupNames != null && !leaderboardGroupNames.isEmpty()) {
requestedLeaderboardGroupNames.addAll(leaderboardGroupNames);
} else {
// No range supplied. Export all for now
requestedLeaderboardGroupNames.addAll(leaderboardGroups.keySet());
}
TopLevelMasterDataSerializer masterSerializer = new TopLevelMasterDataSerializer(leaderboardGroups,
getService().getAllEvents(), getService().getPersistentRegattasForRaceIDs(), getService().getAllMediaTracks());
JSONObject masterData = masterSerializer.serialize(requestedLeaderboardGroupNames);
byte[] json = masterData.toJSONString().getBytes();
return Response.ok(json).build();
}
}
@@ -0,0 +1,16 @@
package com.sap.sailing.server.gateway.jaxrs.spi;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.core.Application;
public class RestSpiApplication extends Application {
public Set<Class<?>> getClasses() {
HashSet<Class<?>> classes = new HashSet<>();
classes.add(LeaderboardGroupsStructureResource.class);
return classes;
}
}
@@ -2,10 +2,10 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<link href='../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../sap.ico' type='image/x-icon'>
<title>Gateway services</title>
<link href='../../../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
<title>Deprecated Web Services API</title>
</head>
<body>
<h1>SAP Sailing Analytics - Web Services</h1>
@@ -0,0 +1,110 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<link href='../../../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
<title>Deprecated Web Services API</title>
</head>
<body>
<h1>SAP Sailing Analytics Deprecated Web Services API - Overview</h1>
<p>
The best entry point to navigating the contents of a server is usually the <b>leaderboardgroups</b> service,
listing the leaderboard groups offered by the server. For example,
<a href="http://www.sapsailing.com/sailingserver/leaderboardgroups">http://www.sapsailing.com/sailingserver/leaderboardgroups</a>
lists the names of the leaderboard groups that the server instance knows. The names returned in the list can then be used
as parameter for the <b>leaderboardgroup</b> service, e.g., as follows:
<a href="http://www.sapsailing.com/sailingserver/leaderboardgroup?leaderboardGroupName=Star%20IDM%202013">http://www.sapsailing.com/sailingserver/leaderboardgroup?leaderboardGroupName=Star%20IDM%202013</a>.
Note, of course, that the name needs to be URL-encoded with special characters and white spaces correctly
translated into URL format.
</p>
<div style="height: 1em;"></div>
<p>
The leaderboard group service list the leaderboards in the group. There are essentially two types of leaderboard:
flexible leaderboards and regatta leaderboards. For flexible leaderboards, the service tells the list of races
in the leaderboard, and for each race in particular the regatta name and the tracked race name. These two attributes
can, in turn, be used as parameters for further services to identify the race for which information shall be retrieved and
to fetch information about the regatta with its structure such as series and fleets.
Again, note that those names need to be URL-encoded accordingly when used as parameter to a service call.
</p>
<div style="height: 1em;"></div>
<p>
When a service takes a time point as a parameter, there are usually two ways of specifying it: either as an
ISO time stamp, e.g., 2013-05-08T10:12:59Z or as Unix time stamp (milliseconds since 1970-01-01T00:00:00Z). The parameters
in the latter format are usually called <b>...asmillis</b>.
</p>
<div style="height: 1em;"></div>
<table>
<thead>
<tr>
<td>Web Service</td>
<td>Description</td>
<td>Supported formats</td>
</trh>
</thead>
<tbody>
<tr>
<td><a href="eventsExportDoc.html">/events</a></td>
<td>Gets all events</td>
<td><a href="eventsExportSample.json">Json</a></td>
</tr>
<tr>
<td><a href="regattasExportDoc.html">/regattas</a></td>
<td>Gets all regattas</td>
<td><a href="regattasExportSample.json">Json</a></td>
</tr>
<tr>
<td><a href="regattaExportDoc.html">/regatta</a></td>
<td>Gets a regatta</td>
<td><a href="regattaExportSample.json">Json</a></td>
</tr>
<tr>
<td><a href="leaderboardGroupsExportDoc.html">/leaderboardgroups</a></td>
<td>Gets all leaderboard groups</td>
<td><a href="leaderboardGroupsExportSample.json">Json</a></td>
</tr>
<tr>
<td><a href="leaderboardGroupExportDoc.html">/leaderboardgroup</a></td>
<td>Gets a group of leaderboards</td>
<td><a href="leaderboardGroupExportSample.json">Json</a></td>
</tr>
<tr>
<td><a href="leaderboardsExportDoc.html">/leaderboards</a></td>
<td>Gets all leaderboards</td>
<td><a href="leaderboardsExportSample.json">Json</a></td>
</tr>
<tr>
<td><a href="leaderboardExportDoc.html">/leaderboard</a></td>
<td>Gets a leaderboard</td>
<td><a href="leaderboardExportSample.json">Json</a></td>
</tr>
<tr>
<td><a href="windExportDoc.html">/wind</a></td>
<td>Gets wind data for a race</td>
<td><a href="windExportSample.json">Json</a></td>
</tr>
<!--
<tr>
<td><a href="showraceDoc.html">showrace</a></td>
<td>Gets general time-dependent data for a race and the competitors</td>
<td><a href="showraceSample.json">Json</a></td>
</tr>
<tr>
<td><a href="showwaypointsDoc.html">showwaypoints</a></td>
<td>Gets course and waypoint data for a race</td>
<td><a href="showwaypointsSample.json">Json</a></td>
</tr>
<tr>
<td><a href="showboatpositionsDoc.html">showboatpositions</a></td>
<td>Gets boat GPS data for a race</td>
<td><a href="showboatpositionsSample.json">Json</a></td>
</tr>
-->
</tbody>
</table>
<div style="height: 1em;"></div>
<a href="../../index.html">Back to Web Services Home</a>
</body>
</html>
@@ -2,10 +2,10 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<link href='../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../sap.ico' type='image/x-icon'>
<title>Gateway services</title>
<link href='../../../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
<title>Deprecated Web Services API</title>
</head>
<body>
<h1>SAP Sailing Analytics - Web Services</h1>
@@ -2,10 +2,10 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<link href='../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../sap.ico' type='image/x-icon'>
<title>Gateway services</title>
<link href='../../../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
<title>Deprecated Web Services API</title>
</head>
<body>
<h1>SAP Sailing Analytics - Web Services</h1>
@@ -2,10 +2,10 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<link href='../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../sap.ico' type='image/x-icon'>
<title>Gateway services</title>
<link href='../../../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
<title>Deprecated Web Services API</title>
</head>
<body>
<h1>SAP Sailing Analytics - Web Services</h1>
@@ -2,10 +2,10 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<link href='../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../sap.ico' type='image/x-icon'>
<title>Gateway services</title>
<link href='../../../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
<title>Deprecated Web Services API</title>
</head>
<body>
<h1>SAP Sailing Analytics - Web Services</h1>
@@ -2,10 +2,10 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<link href='../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../sap.ico' type='image/x-icon'>
<title>Gateway services</title>
<link href='../../../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
<title>Deprecated Web Services API</title>
</head>
<body>
<h1>SAP Sailing Analytics - Web Services</h1>
@@ -2,10 +2,10 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<link href='../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../sap.ico' type='image/x-icon'>
<title>Gateway services</title>
<link href='../../../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
<title>Deprecated Web Services API</title>
</head>
<body>
<h1>SAP Sailing Analytics - Web Services</h1>
@@ -2,10 +2,10 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<link href='../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../sap.ico' type='image/x-icon'>
<title>Gateway services</title>
<link href='../../../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
<title>Deprecated Web Services API</title>
</head>
<body>
<h1>SAP Sailing Analytics - Web Services</h1>
@@ -2,10 +2,10 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<link href='../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../sap.ico' type='image/x-icon'>
<title>Gateway services</title>
<link href='../../../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
<title>Deprecated Web Services API</title>
</head>
<body>
<h1>SAP Sailing Analytics - Web Services</h1>
@@ -2,10 +2,10 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<link href='../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../sap.ico' type='image/x-icon'>
<title>Gateway services</title>
<link href='../../../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
<title>Deprecated Web Services API</title>
</head>
<body>
<h1>SAP Sailing Analytics - Web Services</h1>
@@ -2,10 +2,10 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<link href='../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../sap.ico' type='image/x-icon'>
<title>Gateway services</title>
<link href='../../../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
<title>Deprecated Web Services API</title>
</head>
<body>
<h1>SAP Sailing Analytics - Web Services</h1>
@@ -0,0 +1,46 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<link href='../../../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
<title>SAP Sailing Analytics Webservices API Version 1.0 Beta</title>
</head>
<body>
<h1>SAP Sailing Analytics Webservices API Version 1.0 Beta</h1>
<h2>URL: /api/v1/events</h2>
<b>Description:</b>
<p>The service gets all existing events with their venues and course areas</p>
<br/>
<table>
<tr>
<td>Webservice Type:</td>
<td>REST</td>
</tr>
<tr>
<td>Output format:</td>
<td>Json</td>
</tr>
<tr>
<td>Mandatory parameters:</td>
<td>None</td>
</tr>
<tr>
<td>Optional parameters:</td>
<td>None</td>
</tr>
<tr>
<td>Request method:</td>
<td>GET</td>
</tr>
<tr>
<td>Example:</td>
<td><a href="http://www.sapsailing.com/sailingserver/api/v1/events">http://www.sapsailing.com/sailingserver/api/v1/events</a></td>
</tr>
</table>
<div style="height: 1em;"></div>
<a href="index.html">Back to Web Service Overview</a>
</body>
</html>
Loaded 100 of 141 files, more files were not shown because too many files have changed in this diff. Show more