Merge remote-tracking branch 'origin/master' into riccardo

Conflicts:
	java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/CompetitorDTOImpl.java
	java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/PreviousCompetitorDTOImpl.java
	java/com.sap.sailing.gwt.ui/SailingGWT.launch
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractTrackedRacesListComposite.java
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaListComposite.java
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java
	java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/BoatClassJsonDeserializer.java
	java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java
	java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/util/RaceDataAccessEval.java
	java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/util/RacingEventPersistence.java
	java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/util/TracTracReaderFromURLs.java
	java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/util/TracTracWriter.java
	java/target/stop
This commit is contained in:
D049941 committed 2013-11-22 16:15:07 +01:00
commit 69b7096b3d
372 files changed
+72853 -877361

No files matched your search

+2
View File
@@ -8,6 +8,8 @@ manifest.txt
java/com.sap.sailing.gwt.ui/gwt-unitCache/*
java/com.sap.sailing.gwt.ui/war/WEB-INF/deploy/*
sailing_debug0*
dist
build
/java/.settings/org.eclipse.jdt.core.prefs
/mobile/com.sap.sailing.racecommittee.app/.settings/org.eclipse.jdt.core.prefs
/mobile/com.sap.sailing.racecommittee.app/bin/*
+124 -10
View File
@@ -12,8 +12,8 @@ find_project_home ()
PARENT_DIR=`cd $1/..;pwd`
OUTPUT=$(find_project_home $PARENT_DIR)
if [ "$OUTPUT" = "" ] && [ -d "$PARENT_DIR/code" ] && [ -d "$PARENT_DIR/code/.git" ]; then
OUTPUT="$PARENT_DIR/code"
if [ "$OUTPUT" = "" ] && [ -d "$PARENT_DIR/$CODE_DIRECTORY" ] && [ -d "$PARENT_DIR/$CODE_DIRECTORY/.git" ]; then
OUTPUT="$PARENT_DIR/$CODE_DIRECTORY"
fi
echo $OUTPUT
return 0
@@ -56,6 +56,7 @@ active_branch=`basename $active_branch`
HEAD_SHA=$(git show-ref --head -s | head -1)
HEAD_DATE=$(date "+%Y%m%d%H%M")
VERSION_INFO="$HEAD_SHA-$active_branch-$HEAD_DATE"
SIMPLE_VERSION_INFO="$active_branch-$HEAD_DATE"
MAVEN_SETTINGS=$PROJECT_HOME/configuration/maven-settings.xml
MAVEN_SETTINGS_PROXY=$PROJECT_HOME/configuration/maven-settings-proxy.xml
@@ -75,7 +76,7 @@ suppress_confirmation=0
extra=''
if [ $# -eq 0 ]; then
echo "buildAndUpdateProduct [-b -u -g -t -o -c -m <config> -n <package> -l <port>] [build|install|all|hot-deploy|remote-deploy]"
echo "buildAndUpdateProduct [-b -u -g -t -o -c -m <config> -n <package> -l <port>] [build|install|all|hot-deploy|remote-deploy|release]"
echo ""
echo "-g Disable GWT compile, no gwt files will be generated, old ones will be preserved."
echo "-b Build GWT permutation only for one browser and English language."
@@ -86,15 +87,22 @@ if [ $# -eq 0 ]; then
echo "-m <path to file> Specify alternate maven configuration (possibly has side effect on proxy setting)"
echo "-n <package name> Name of the bundle you want to hot deploy. Needs fully qualified name like"
echo " com.sap.sailing.monitoring. Only works if there is a fully built server available."
echo " This parameter can also hold the name of the release if you are using the release command."
echo "-l <telnet port> Telnet port the OSGi server is running. Optional but enables fully automatic hot-deploy."
echo "-s <target server> Name of server you want to use as target for install, hot-deploy or remote-reploy. This overrides default behaviour."
echo "-w <ssh target> Target for remote-deploy. Must comply with the following format: user@server."
echo "-w <ssh target> Target for remote-deploy and release. Must comply with the following format: user@server."
echo "-u Run without confirmation messages. Use with extreme care."
echo ""
echo "build: builds the server code using Maven to $PROJECT_HOME (log to $START_DIR/build.log)"
echo ""
echo "install: installs product and configuration to $SERVERS_HOME/$active_branch. Overwrites any configuration by using config from branch."
echo ""
echo "all: invokes build and then install"
echo ""
echo "release: Releases a server package to the location specified by -w parameter. The release is named using the branch name and the date."
echo "You can overwrite the generated release name by specifying a name with the parameter -n. Do not use spaces or other special characters!"
echo "Example: $0 -w trac@sapsailing.com -n release-ess-brazil-2013 release"
echo ""
echo "hot-deploy: performs hot deployment of named bundle into OSGi server"
echo "Example: $0 -n com.sap.sailing.www -l 14888 hot-deploy"
echo ""
@@ -119,7 +127,7 @@ do
case $option in
g) gwtcompile=0;;
t) testing=0;;
b) onegwtpermutationonly=1;;
b) onegwtpermutationonly=1;;
o) offline=1;;
c) clean="";;
p) proxy=1;;
@@ -141,10 +149,111 @@ echo INSTALL goes to $ACDIR
shift $((OPTIND-1))
if [[ $@ == "" ]]; then
echo "You need to specify an action [build|install|all|hot-deploy|remote-deploy]"
echo "You need to specify an action [build|install|all|hot-deploy|remote-deploy|release]"
exit 2
fi
if [[ "$@" == "release" ]]; then
if [ ! -d $p2PluginRepository/plugins ]; then
echo "Could not find source directory $p2PluginRepository!"
exit
fi
RELEASE_NOTES=""
echo ""
echo "Please provide me with some notes about this release. You can add more than"
echo "one line. Please include major changes or new features. After your notes I will"
echo "also include the commits of the last 4 weeks. You can save and quit by hitting ctrl+d."
while read -e -p "> " line; do
RELEASE_NOTES="$RELEASE_NOTES\n$line"
done
if [[ $RELEASE_NOTES == "" ]]; then
echo -e "\nCome on - I can not release without at least some notes about this release!"
exit
fi
echo -e "\nThank you! One last thing..."
echo "How many weeks of commits do you want to include (0=No commits)?"
read -p "> " -e COMMIT_WEEK_COUNT
mkdir -p $PROJECT_HOME/dist
mkdir -p $PROJECT_HOME/build
RELEASE_NOTES="Release $VERSION_INFO\n$RELEASE_NOTES"
echo -e $RELEASE_NOTES > $PROJECT_HOME/build/release-notes.txt
echo "" >> $PROJECT_HOME/build/release-notes.txt
echo "Commits for the last $COMMIT_WEEK_COUNT weeks:" >> $PROJECT_HOME/build/release-notes.txt
echo "" >> $PROJECT_HOME/build/release-notes.txt
cd $PROJECT_HOME
git log --decorate --pretty=format:"%h - %an, %ar : %s" --date=relative --abbrev-commit --since=$COMMIT_WEEK_COUNT.weeks >> $PROJECT_HOME/build/release-notes.txt
cd $PROJECT_HOME/build
ACDIR=$PROJECT_HOME/build
mkdir -p configuration/jetty/etc
mkdir plugins
cp -v $PROJECT_HOME/java/target/start $ACDIR/
cp -v $PROJECT_HOME/java/target/stop $ACDIR/
cp -v $PROJECT_HOME/java/target/status $ACDIR/
cp -v $PROJECT_HOME/java/target/shouldIBuildOrShouldIGo.sh $ACDIR/
cp -v $PROJECT_HOME/java/target/env.sh $ACDIR/
cp -v $p2PluginRepository/configuration/config.ini configuration/
cp -v $PROJECT_HOME/java/target/configuration/jetty/etc/jetty.xml configuration/jetty/etc
cp -v $PROJECT_HOME/java/target/configuration/jetty/etc/jetty-selector.xml configuration/jetty/etc
cp -v $PROJECT_HOME/java/target/configuration/jetty/etc/jetty-deployer.xml configuration/jetty/etc
cp -v $PROJECT_HOME/java/target/configuration/jetty/etc/realm.properties configuration/jetty/etc
cp -v $PROJECT_HOME/java/target/configuration/monitoring.properties configuration/
cp -v $PROJECT_HOME/configuration/mongodb.cfg $ACDIR/
cp -v $PROJECT_HOME/java/target/udpmirror $ACDIR/
cp -v $PROJECT_HOME/java/target/http2udpmirror $ACDIR
cp -v $PROJECT_HOME/java/target/configuration/logging.properties $ACDIR/configuration
cp -r -v $p2PluginRepository/configuration/org.eclipse.equinox.simpleconfigurator configuration/
cp -vr $p2PluginRepository/plugins $ACDIR/
cp -rv $PROJECT_HOME/configuration/native-libraries $ACDIR/
cp -v $PROJECT_HOME/configuration/buildAndUpdateProduct.sh $ACDIR/
# make sure to save the information from env.sh
. $ACDIR/env.sh
echo "$VERSION_INFO System:" > $ACDIR/configuration/jetty/version.txt
sed -i "/mongo.host/d" "$ACDIR/configuration/config.ini"
sed -i "/mongo.port/d" "$ACDIR/configuration/config.ini"
sed -i "/expedition.udp.port/d" "$ACDIR/configuration/config.ini"
sed -i "/replication.exchangeName/d" "$ACDIR/configuration/config.ini"
sed -i "/replication.exchangeHost/d" "$ACDIR/configuration/config.ini"
sed -i "s/^.*jetty.port.*$/<Set name=\"port\"><Property name=\"jetty.port\" default=\"$SERVER_PORT\"\/><\/Set>/g" "$ACDIR/configuration/jetty/etc/jetty-selector.xml"
if [[ $OSGI_BUNDLE_NAME != "" ]]; then
SIMPLE_VERSION_INFO=$OSGI_BUNDLE_NAME
fi
mkdir $PROJECT_HOME/dist/$SIMPLE_VERSION_INFO
`which tar` cvzf $PROJECT_HOME/dist/$SIMPLE_VERSION_INFO/$SIMPLE_VERSION_INFO.tar.gz *
cp $ACDIR/env.sh $PROJECT_HOME/dist/$SIMPLE_VERSION_INFO
cp $PROJECT_HOME/build/release-notes.txt $PROJECT_HOME/dist/$SIMPLE_VERSION_INFO
cd $PROJECT_HOME
rm -rf build/*
SSH_CMD="ssh $REMOTE_SERVER_LOGIN"
SCP_CMD="scp -r"
echo "Packaged release $PROJECT_HOME/dist/$SIMPLE_VERSION_INFO.tar.gz! I've put an env.sh that matches the current branch to $PROJECT_HOME/dist/$SIMPLE_VERSION_INFO/env.sh!"
echo "Checking the remote connection..."
REMOTE_HOME=`ssh $REMOTE_SERVER_LOGIN 'echo $HOME/releases'`
echo "Now uploading release to $REMOTE_SERVER_LOGIN:$REMOTE_HOME. Can take quite a while!"
`which scp` -r $PROJECT_HOME/dist/$SIMPLE_VERSION_INFO $REMOTE_SERVER_LOGIN:$REMOTE_HOME/
echo "Uploaded release to $REMOTE_HOME! Make sure to also put an updated env.sh if needed to the right place ($REMOTE_HOME/environment in most cases)"
fi
if [[ "$@" == "hot-deploy" ]]; then
# check parameters
if [[ $OSGI_BUNDLE_NAME == "" ]]; then
@@ -291,7 +400,7 @@ if [[ "$@" == "build" ]] || [[ "$@" == "all" ]]; then
for i in com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/*.gwt.xml; do
echo "INFO: Patching $i files such that only one GWT permutation needs to be compiled"
cp $i $i.bak
cat $i | sed -e 's/^[ ]*<extend-property *name="locale" *values="de" *\/>/<!-- <extend-property name="locale" values="de"\/> --> <set-property name="user.agent" value="safari" \/>/' >$i.sed
cat $i | sed -e 's/^[ ]*<extend-property *name="locale" *values="de" *\/>/<!-- <extend-property name="locale" values="de"\/> --> <set-property name="user.agent" value="gecko1_8" \/>/' >$i.sed
mv $i.sed $i
done
else
@@ -299,7 +408,7 @@ if [[ "$@" == "build" ]] || [[ "$@" == "all" ]]; then
for i in com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/*.gwt.xml; do
echo "INFO: Patching $i files such that all GWT permutations are compiled"
cp $i $i.bak
cat $i | sed -e 's/<!-- <extend-property *name="locale" *values="de" *\/> --> <set-property name="user.agent" value="safari" \/>/<extend-property name="locale" values="de"\/>/' >$i.sed
cat $i | sed -e 's/<!-- <extend-property *name="locale" *values="de" *\/> --> <set-property name="user.agent" value="gecko1_8" \/>/<extend-property name="locale" values="de"\/>/' >$i.sed
mv $i.sed $i
done
fi
@@ -329,8 +438,12 @@ if [[ "$@" == "build" ]] || [[ "$@" == "all" ]]; then
extra="$extra -P no-debug.without-proxy"
fi
echo "Using following command: mvn $extra -fae -s $MAVEN_SETTINGS $clean install"
mvn $extra -fae -s $MAVEN_SETTINGS $clean install 2>&1 | tee $START_DIR/build.log
# make sure to honour the service configuration
# needed to make sure that tests use the right servers
APP_PARAMETERS="-Dmongo.host=$MONGODB_HOST -Dmongo.port=$MONGODB_PORT -Dexpedition.udp.port=$EXPEDITION_PORT -Dreplication.exchangeHost=$REPLICATION_HOST -Dreplication.exchangeName=$REPLICATION_CHANNEL"
echo "Using following command: mvn $extra -DargLine=\"$APP_PARAMETERS\" -fae -s $MAVEN_SETTINGS $clean install"
mvn $extra -DargLine="$APP_PARAMETERS" -fae -s $MAVEN_SETTINGS $clean install 2>&1 | tee $START_DIR/build.log
if [ $gwtcompile -eq 1 ]; then
# Now move back the backup .gwt.xml files before they were (maybe) patched
@@ -393,6 +506,7 @@ if [[ "$@" == "install" ]] || [[ "$@" == "all" ]]; then
cp -v $PROJECT_HOME/java/target/start $ACDIR/
cp -v $PROJECT_HOME/java/target/stop $ACDIR/
cp -v $PROJECT_HOME/java/target/status $ACDIR/
cp -v $PROJECT_HOME/java/target/shouldIBuildOrShouldIGo.sh $ACDIR/
if [ ! -f "$ACDIR/env.sh" ]; then
cp -v $PROJECT_HOME/java/target/env.sh $ACDIR/
Binary file not shown.
-874580
View File
File diff suppressed because it is too large. Load diff
+6
View File
@@ -25,6 +25,8 @@ Like businesses, sailors need the latest information to make strategic decisions
* [[Development Environment|wiki/development-environment]]
* [[Working with GWT Locally|wiki/local-gwt]]
* [[Mobile Development|wiki/mobile-development]]
* Information for Webdesigners
* [[Layout repository|wiki/webdesign]]
* General Information
* [[Architecture and Infrastructure|wiki/architecture-and-infrastructure]]
* [[Sailing Domain Algorithms|wiki/sailing-domain-algorithms]]
@@ -46,3 +48,7 @@ Like businesses, sailors need the latest information to make strategic decisions
* [Main Sailing Website](http://www.sapsailing.com)
* [Visitor Statistics](http://analysis.sapsailing.com/)
* [Traffic and CPU for app01](http://mrtg.sapsailing.com/)
### TracTrac Workshop in Kopenhagen
* [[TracTrac Workshop in October 2013 in Kopenhagen|wiki/TracTracWorkshop2013]]
@@ -7,7 +7,7 @@
<listEntry value="1"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.sap.sailing.declination.impl.DeclinationStore"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="2012 2012 1"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="2014 2014 1"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.declination"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dhttp.proxyHost=proxy.wdf.sap.corp -Dhttp.proxyPort=8080"/>
</launchConfiguration>
File diff suppressed because it is too large. Load diff
@@ -166,62 +166,10 @@ public class DeclinationStore {
Calendar cal = new GregorianCalendar(year, month, /* dayOfMonth */ 1);
TimePoint timePoint = new MillisecondsTimePoint(cal.getTimeInMillis());
for (double lat = 0; lat < 90; lat += grid) {
System.out.println("Date: " + year + "/" + (month + 1) + ", Latitude: " + lat);
for (double lng = 0; lng < 180; lng += grid) {
Position point = new DegreePosition(lat, lng);
Declination existingDeclinationRecord;
synchronized (storedDeclinations) {
existingDeclinationRecord = storedDeclinations.get(point);
}
if (existingDeclinationRecord == null
|| DeclinationServiceImpl.timeAndSpaceDistance(existingDeclinationRecord
.getPosition().getDistance(point), timePoint, existingDeclinationRecord
.getTimePoint()) > 0.1) {
// less than ~6 nautical miles and/or ~.6 months off
fetchAndAppendDeclination(timePoint, point, importer, out);
}
}
for (double lng = -grid; lng > -180; lng -= grid) {
Position point = new DegreePosition(lat, lng);
Declination existingDeclinationRecord;
synchronized (storedDeclinations) {
existingDeclinationRecord = storedDeclinations.get(point);
}
if (existingDeclinationRecord == null
|| DeclinationServiceImpl.timeAndSpaceDistance(existingDeclinationRecord
.getPosition().getDistance(point), timePoint, existingDeclinationRecord
.getTimePoint()) > 0.1) {
// less than ~6 nautical miles and/or ~.6 months off
fetchAndAppendDeclination(timePoint, point, importer, out);
}
}
fetchAndAppendDeclinationForLatitude(grid, importer, year, storedDeclinations, out, month, timePoint, lat);
}
for (double lat = -grid; lat > -90; lat -= grid) {
System.out.println("Date: " + year + "/" + (month + 1) + ", Latitude: " + lat);
for (double lng = 0; lng < 180; lng += grid) {
Position point = new DegreePosition(lat, lng);
Declination existingDeclinationRecord;
synchronized (storedDeclinations) {
existingDeclinationRecord = storedDeclinations.get(point);
}
if (DeclinationServiceImpl.timeAndSpaceDistance(existingDeclinationRecord.getPosition().getDistance(point),
timePoint, existingDeclinationRecord.getTimePoint()) > 0.1) {
// less than ~6 nautical miles and/or ~.6 months off
fetchAndAppendDeclination(timePoint, point, importer, out);
}
}
for (double lng = -grid; lng > -180; lng -= grid) {
Position point = new DegreePosition(lat, lng);
Declination existingDeclinationRecord;
synchronized (storedDeclinations) {
existingDeclinationRecord = storedDeclinations.get(point);
}
if (DeclinationServiceImpl.timeAndSpaceDistance(existingDeclinationRecord.getPosition().getDistance(point),
timePoint, existingDeclinationRecord.getTimePoint()) > 0.1) {
// less than ~6 nautical miles and/or ~.6 months off
fetchAndAppendDeclination(timePoint, point, importer, out);
}
}
fetchAndAppendDeclinationForLatitude(grid, importer, year, storedDeclinations, out, month, timePoint, lat);
}
out.close();
}
@@ -229,6 +177,33 @@ public class DeclinationStore {
}
}
private void fetchAndAppendDeclinationForLatitude(double grid, NOAAImporter importer, int year,
QuadTree<Declination> storedDeclinations, Writer out, int month, TimePoint timePoint, double lat)
throws IOException {
System.out.println("Date: " + year + "/" + (month + 1) + ", Latitude: " + lat);
for (double lng = 0; lng < 180; lng += grid) {
fetchAndAppendDeclination(importer, storedDeclinations, out, timePoint, lat, lng);
}
for (double lng = -grid; lng > -180; lng -= grid) {
fetchAndAppendDeclination(importer, storedDeclinations, out, timePoint, lat, lng);
}
}
private void fetchAndAppendDeclination(NOAAImporter importer, QuadTree<Declination> storedDeclinations, Writer out,
TimePoint timePoint, double lat, double lng) throws IOException {
Position point = new DegreePosition(lat, lng);
final Declination existingDeclinationRecord;
synchronized (storedDeclinations) {
existingDeclinationRecord = storedDeclinations.get(point);
}
if (existingDeclinationRecord == null
|| DeclinationServiceImpl.timeAndSpaceDistance(existingDeclinationRecord.getPosition().getDistance(point),
timePoint, existingDeclinationRecord.getTimePoint()) > 0.1) {
// less than ~6 nautical miles and/or ~.6 months off
fetchAndAppendDeclination(timePoint, point, importer, out);
}
}
/**
* Launches the importer, writing to resources/declination-year.txt (where "year" represents the year for which the
* values are stored in the file) the declinations downloaded online for the years <code>args[0]</code> to
@@ -44,16 +44,15 @@ public class AbstractPosition implements Position {
@Override
public double getCentralAngleRad(Position p) {
// Sinnott:
double dLat = p.getLatRad()-getLatRad();
double dLon = p.getLngRad()-getLngRad();
double a = Math.sin(dLat/2.) * Math.sin(dLat/2.) +
Math.cos(getLatRad()) * Math.cos(p.getLatRad()) *
Math.sin(dLon/2.) * Math.sin(dLon/2.);
return 2. * Math.atan2(Math.sqrt(a), Math.sqrt(1.-a));
double dLat = p.getLatRad() - getLatRad();
double dLon = p.getLngRad() - getLngRad();
double a = Math.sin(dLat / 2.) * Math.sin(dLat / 2.) + Math.cos(getLatRad()) * Math.cos(p.getLatRad())
* Math.sin(dLon / 2.) * Math.sin(dLon / 2.);
return 2. * Math.atan2(Math.sqrt(a), Math.sqrt(1. - a));
// Spherical Law of Cosines; simpler formula, but doesn't work well for very small distances
// return Math.acos(Math.sin(getLatRad()) * Math.sin(p.getLatRad())
// + Math.cos(getLatRad()) * Math.cos(p.getLatRad())
// * Math.cos(p.getLngRad() - getLngRad()));
// return Math.acos(Math.sin(getLatRad()) * Math.sin(p.getLatRad())
// + Math.cos(getLatRad()) * Math.cos(p.getLatRad())
// * Math.cos(p.getLngRad() - getLngRad()));
}
@Override
@@ -79,44 +78,46 @@ public class AbstractPosition implements Position {
@Override
public Position translateRhumb(Bearing bearing, Distance distance) {
/*
* This algorithm is limited to distances such that dlon < pi/2, i.e those that extend around less than one quarter of the circumference
* of the earth in longitude. A completely general, but more complicated algorithm is necessary if greater distances are allowed.
* This algorithm is limited to distances such that dlon < pi/2, i.e those that extend around less than one
* quarter of the circumference of the earth in longitude. A completely general, but more complicated algorithm
* is necessary if greater distances are allowed.
*/
double distanceRad = distance.getKilometers() / 6371.0; // r = 6371 means earth's radius in km
double distanceRad = distance.getKilometers() / 6371.0; // r = 6371 means earth's radius in km
double lat1 = getLatRad();
double lon1 = getLngRad();
double bearingRad = bearing.getRadians();
double lat2 = Math.asin(Math.sin(lat1) * Math.cos(distanceRad) +
Math.cos(lat1) * Math.sin(distanceRad) * Math.cos(bearingRad));
double lon2 = lon1 + Math.atan2(Math.sin(bearingRad)*Math.sin(distanceRad)*Math.cos(lat1),
Math.cos(distanceRad)-Math.sin(lat1)*Math.sin(lat2));
lon2 = (lon2+3*Math.PI) % (2*Math.PI) - Math.PI; // normalize to -180..+180
double lat2 = Math.asin(Math.sin(lat1) * Math.cos(distanceRad) + Math.cos(lat1) * Math.sin(distanceRad)
* Math.cos(bearingRad));
double lon2 = lon1
+ Math.atan2(Math.sin(bearingRad) * Math.sin(distanceRad) * Math.cos(lat1), Math.cos(distanceRad)
- Math.sin(lat1) * Math.sin(lat2));
lon2 = (lon2 + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalize to -180..+180
return new DegreePosition(lat2 / Math.PI * 180., lon2 / Math.PI * 180.);
return new DegreePosition(lat2 / Math.PI * 180., lon2 / Math.PI * 180.);
}
@Override
public Position translateGreatCircle(Bearing bearing, Distance distance) {
double lat = Math.asin(Math.sin(getLatRad())
* Math.cos(distance.getCentralAngleRad())
+ Math.cos(getLatRad())
* Math.sin(distance.getCentralAngleRad())
* Math.cos(bearing.getRadians()));
double lat = Math.asin(Math.sin(getLatRad()) * Math.cos(distance.getCentralAngleRad()) + Math.cos(getLatRad())
* Math.sin(distance.getCentralAngleRad()) * Math.cos(bearing.getRadians()));
double lng = getLngRad()
+ Math.atan2(
Math.sin(bearing.getRadians())
* Math.sin(distance.getCentralAngleRad())
Math.sin(bearing.getRadians()) * Math.sin(distance.getCentralAngleRad())
* Math.cos(getLatRad()),
Math.cos(distance.getCentralAngleRad())
- Math.sin(getLatRad()) * Math.sin(lat));
Math.cos(distance.getCentralAngleRad()) - Math.sin(getLatRad()) * Math.sin(lat));
return new RadianPosition(lat, lng);
}
@Override
public Distance crossTrackError(Position pos2, Bearing bearing) {
return new CentralAngleDistance(Math.abs(Math.asin(Math.sin(pos2.getCentralAngleRad(this))
* Math.sin(pos2.getBearingGreatCircle(this).getRadians() - bearing.getRadians()))));
public Distance absoluteCrossTrackError(Position p, Bearing bearing) {
return new CentralAngleDistance(Math.abs(crossTrackError(p, bearing).getCentralAngleRad()));
}
@Override
public Distance crossTrackError(Position p, Bearing bearing) {
return new CentralAngleDistance(Math.asin(Math.sin(p.getCentralAngleRad(this))
* Math.sin(p.getBearingGreatCircle(this).getRadians() - bearing.getRadians())));
}
@Override
@@ -125,19 +126,41 @@ public class AbstractPosition implements Position {
}
@Override
public Distance alongTrackDistance(Position pos2, Bearing bearing) {
double direction = Math.signum(Math.cos(pos2.getBearingGreatCircle(this).getRadians() - bearing.getRadians()));
public Distance alongTrackDistance(Position from, Bearing bearing) {
double direction = Math.signum(Math.cos(from.getBearingGreatCircle(this).getRadians() - bearing.getRadians()));
// Test if denominator gets ridiculously small; if so, the cross-track error is about 90� central angle.
// This means that the cross-track error is maximized, and that there is no way to determine how far along
// the great circle described by pos2 and bearing we should travel. This is an exception which will
// surface as a division-by-zero exception or a NaN result
return new CentralAngleDistance(direction * Math.acos(Math.cos(pos2.getCentralAngleRad(this))
/ Math.cos(crossTrackError(pos2, bearing).getCentralAngleRad())));
return new CentralAngleDistance(direction
* Math.acos(Math.cos(from.getCentralAngleRad(this))
/ Math.cos(crossTrackError(from, bearing).getCentralAngleRad())));
}
@Override
public Distance getDistanceToLine(Position left, Position right) {
final Distance distance;
double toLeft = Math.abs(left.getBearingGreatCircle(this).getDifferenceTo(left.getBearingGreatCircle(right))
.getDegrees());
double toRight = Math.abs(right.getBearingGreatCircle(this).getDifferenceTo(right.getBearingGreatCircle(left))
.getDegrees());
if (toLeft > 90) {
distance = this.getDistance(left);
} else {
if (toRight > 90) {
distance = this.getDistance(right);
} else {
distance = this.absoluteCrossTrackError(left, left.getBearingGreatCircle(right));
}
}
// Right now this always returns a positiv value. It might be possible to change it so that the sign indicates
// which side of the Line the Position is on.
return distance;
}
@Override
public String toString() {
return "("+getLatDeg()+","+getLngDeg()+")";
return "(" + getLatDeg() + "," + getLngDeg() + ")";
}
}
@@ -32,6 +32,7 @@ public enum DetailType implements Serializable {
MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS(1, DESCENDING),
TOTAL_DISTANCE_TRAVELED(0, ASCENDING), TOTAL_AVERAGE_SPEED_OVER_GROUND(2, DESCENDING),
TOTAL_TIME_SAILED_IN_SECONDS(1, ASCENDING), RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING),
RACE_TOTAL_POINTS(2, ASCENDING), REGATTA_TOTAL_POINTS(2, ASCENDING),
RACE_TIME_SINCE_LAST_POSITION_FIX_IN_SECONDS(0 , ASCENDING),
RACE_TRACKING_QUALITY(0 , ASCENDING), RACE_DISTANCE_TO_START_FIVE_SECONDS_BEFORE_RACE_START(1, ASCENDING),
RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START(2, DESCENDING),
@@ -0,0 +1,35 @@
package com.sap.sailing.domain.common;
public enum LeaderboardType {
RegattaLeaderboard(false, true), RegattaMetaLeaderboard(true, true),
FlexibleLeaderboard(false, false), FlexibleMetaLeaderboard(true, false);
boolean isRegattaLeaderboard;
boolean isMetaLeaderboard;
LeaderboardType(boolean isMetaLeaderboard, boolean isRegattaLeaderboard) {
this.isMetaLeaderboard = isMetaLeaderboard;
this.isRegattaLeaderboard = isRegattaLeaderboard;
}
public boolean isMetaLeaderboard() {
return isMetaLeaderboard;
}
public boolean isRegattaLeaderboard() {
return isRegattaLeaderboard;
}
public boolean isFlexibleLeaderboard() {
return !isRegattaLeaderboard;
}
public String toString() {
String result = "";
if(isMetaLeaderboard) {
result += "Meta, ";
}
result += isRegattaLeaderboard ? "Regatta" : "Flexible";
return result;
}
}
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.common;
import java.io.Serializable;
public interface Position extends Serializable {
double getLatRad();
@@ -47,6 +48,12 @@ public interface Position extends Serializable {
/**
* @return an unsigned distance
*/
Distance absoluteCrossTrackError(Position p, Bearing bearing);
/**
* @return a signed distance; positive distances mean that this position is to the right (starboard) of the
* line one gets when traveling from <code>p</code> with <code>bearing</code>
*/
Distance crossTrackError(Position p, Bearing bearing);
/**
@@ -56,5 +63,11 @@ public interface Position extends Serializable {
* if the angle between this position and the great circle is 90 degrees then there is
* no solution, and a <code>NaN</code> or exception will result.
*/
Distance alongTrackDistance(Position from, Bearing bearing);
/**
* Computes the distance from this position to the line between <code>left<code> and <code>right<code>.
*/
Distance getDistanceToLine(Position left, Position right);
}
@@ -1,6 +1,6 @@
package com.sap.sailing.domain.common;
public enum TrackedRaceStatusEnum { PREPARED(0), LOADING(1), TRACKING(2), FINISHED(3);
public enum TrackedRaceStatusEnum { PREPARED(0), LOADING(1), TRACKING(2), FINISHED(3), ERROR(4);
private final int order;
@@ -9,6 +9,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import com.sap.sailing.domain.common.LeaderboardType;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.impl.Util.Pair;
@@ -28,8 +29,7 @@ public abstract class AbstractLeaderboardDTO implements Serializable {
public String defaultCourseAreaIdAsString;
public String defaultCourseAreaName;
public ScoringSchemeType scoringScheme;
public boolean isMetaLeaderboard;
public boolean isRegattaLeaderboard;
public LeaderboardType type;
private Long delayToLiveInMillisForLatestRace;
@@ -328,7 +328,7 @@ public abstract class AbstractLeaderboardDTO implements Serializable {
return false;
if (hasCarriedPoints != other.hasCarriedPoints)
return false;
if (isMetaLeaderboard != other.isMetaLeaderboard)
if (type != other.type)
return false;
if (scoringScheme != other.scoringScheme)
return false;
@@ -2,6 +2,15 @@ package com.sap.sailing.domain.common.dto;
import java.io.Serializable;
/**
* Equality and hash code are based on the {@link #getIdAsString() ID}, the {@link #getSailID() sail number}, the
* {@link #getBoatClass() boat class} (whose equality and hash code, in turn, depends on its name) and the
* {@link #getThreeLetterIocCountryCode() IOC country code}. Note that the three latter properties are subject
* to change for a competitor while the ID remains unchanged.
*
* @author Axel Uhl (d043530)
*
*/
public class CompetitorDTOImpl extends NamedDTO implements CompetitorDTO, Serializable {
private static final long serialVersionUID = -4997852354821083154L;
private String twoLetterIsoCountryCode;
@@ -27,8 +36,11 @@ public class CompetitorDTOImpl extends NamedDTO implements CompetitorDTO, Serial
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * getIdAsString().hashCode();
int result = super.hashCode();
result = prime * result + ((boatClass == null) ? 0 : boatClass.hashCode());
result = prime * result + ((idAsString == null) ? 0 : idAsString.hashCode());
result = prime * result + ((sailID == null) ? 0 : sailID.hashCode());
result = prime * result + ((threeLetterIocCountryCode == null) ? 0 : threeLetterIocCountryCode.hashCode());
return result;
}
@@ -36,15 +48,30 @@ public class CompetitorDTOImpl extends NamedDTO implements CompetitorDTO, Serial
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
CompetitorDTOImpl other = (CompetitorDTOImpl) obj;
if (getIdAsString() == null) {
if (other.getIdAsString() != null)
if (boatClass == null) {
if (other.boatClass != null)
return false;
} else if (!getIdAsString().equals(other.getIdAsString()))
} else if (!boatClass.equals(other.boatClass))
return false;
if (idAsString == null) {
if (other.idAsString != null)
return false;
} else if (!idAsString.equals(other.idAsString))
return false;
if (sailID == null) {
if (other.sailID != null)
return false;
} else if (!sailID.equals(other.sailID))
return false;
if (threeLetterIocCountryCode == null) {
if (other.threeLetterIocCountryCode != null)
return false;
} else if (!threeLetterIocCountryCode.equals(other.threeLetterIocCountryCode))
return false;
return true;
}
@@ -508,7 +508,7 @@ public class IncrementalLeaderboardDTO extends LeaderboardDTO implements Increme
competitorIndexesInPreviousCompetitorsList = new int[competitors.size()];
// for this stripping run, remembers the mapping of real CompetitorDTO objects to the compact form that only holds an int as reference to the
// previous version's competitors list; those will be used to replace the real CompetitorDTO objects where possible and will be replaced the other
// way in apply(...).
// way in applyThisToPreviousVersionByUpdatingThis(...).
Map<CompetitorDTO, CompetitorDTO> compactCompetitorMap = new HashMap<CompetitorDTO, CompetitorDTO>();
int i=0;
for (CompetitorDTO competitor : competitors) {
@@ -1,5 +1,11 @@
package com.sap.sailing.domain.common.dto;
/**
* Equality and hash code are based on the index pointing into a previous leaderboard's competitors list.
*
* @author Axel Uhl (d043530)
*
*/
public class PreviousCompetitorDTOImpl implements CompetitorDTO {
private static final long serialVersionUID = 8820028699103040805L;
private int indexInPreviousCompetitorList;
@@ -15,6 +21,28 @@ public class PreviousCompetitorDTOImpl implements CompetitorDTO {
return indexInPreviousCompetitorList;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + indexInPreviousCompetitorList;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PreviousCompetitorDTOImpl other = (PreviousCompetitorDTOImpl) obj;
if (indexInPreviousCompetitorList != other.indexInPreviousCompetitorList)
return false;
return true;
}
@Override
public String getTwoLetterIsoCountryCode() {
throw new RuntimeException("Internal error. Objects of type "+PreviousCompetitorDTOImpl.class.getName()+
@@ -0,0 +1,54 @@
package com.sap.sailing.domain.common.impl;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
public class CountingOutputStream extends FilterOutputStream {
private static final Logger logger = Logger.getLogger(CountingOutputStream.class.getName());
private long count;
private final long logEveryHowManyBytes;
private long lastMultiple;
private final Level level;
private final String name;
public CountingOutputStream(OutputStream out, long logEveryHowManyBytes, Level logLevel, String name) {
super(out);
this.level = logLevel;
this.name = name;
this.logEveryHowManyBytes = logEveryHowManyBytes;
}
@Override
public void write(int b) throws IOException {
super.write(b);
count(1);
}
private void count(int bytesWritten) {
count += bytesWritten;
if (count >= (lastMultiple+1)*logEveryHowManyBytes) {
lastMultiple++;
log();
}
}
private void log() {
logger.log(level, "wrote "+count+" bytes to "+name);
}
@Override
public void write(byte[] b) throws IOException {
super.write(b);
count(b.length);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
super.write(b, off, len);
count(len);
}
}
@@ -246,4 +246,22 @@ public class Util {
return result;
}
public static <T> int compareToWithNull(Comparable<T> o1, T o2) {
final int result;
if (o1 == null) {
if (o2 == null) {
result = 0;
} else {
result = -1;
}
} else {
if (o2 == null) {
result = 1;
} else {
result = o1.compareTo(o2);
}
}
return result;
}
}
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
</classpath>
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>com.sap.sailing.domain.igtimiadapter.persistence</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>
@@ -0,0 +1,7 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.7
@@ -0,0 +1,3 @@
eclipse.preferences.version=1
pluginProject.extensions=false
resolve.requirebundle=false
@@ -0,0 +1,9 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Persistence
Bundle-SymbolicName: com.sap.sailing.domain.igtimiadapter.persistence
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Require-Bundle: com.sun.jersey;bundle-version="1.17.0",
org.objectweb.asm;bundle-version="3.3.1"
@@ -0,0 +1,4 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>root</artifactId>
<groupId>com.sap.sailing</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>com.sap.sailing.domain.igtimiadapter.persistence</artifactId>
<packaging>eclipse-plugin</packaging>
</project>
@@ -0,0 +1,5 @@
package com.sap.sailing.domain.igtimiadapter.persistence;
public interface DomainObjectFactory {
}
@@ -0,0 +1,5 @@
package com.sap.sailing.domain.igtimiadapter.persistence;
public interface MongoObjectFactory {
}
@@ -0,0 +1,7 @@
package com.sap.sailing.domain.igtimiadapter.persistence.impl;
import com.sap.sailing.domain.igtimiadapter.persistence.DomainObjectFactory;
public class DomainObjectFactoryimpl implements DomainObjectFactory {
}
@@ -0,0 +1,7 @@
package com.sap.sailing.domain.igtimiadapter.persistence.impl;
import com.sap.sailing.domain.igtimiadapter.persistence.MongoObjectFactory;
public class MongoObjectFactoryImpl implements MongoObjectFactory {
}
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
</classpath>
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>com.sap.sailing.domain.igtimiadapter</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>
@@ -0,0 +1,7 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.7
@@ -0,0 +1,3 @@
eclipse.preferences.version=1
pluginProject.extensions=false
resolve.requirebundle=false
@@ -0,0 +1,14 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Igtimiadapter
Bundle-SymbolicName: com.sap.sailing.domain.igtimiadapter
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Require-Bundle: org.json.simple;bundle-version="1.1.0",
com.sun.jersey;bundle-version="1.17.0",
org.objectweb.asm;bundle-version="3.3.1"
Import-Package: javax.ws.rs;version="1.1.1",
javax.ws.rs.core;version="1.1.1",
javax.ws.rs.ext;version="1.1.1"
Web-ContextPath: /igtimi
+21
View File
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
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>
<servlet-name>Igtimi Oauth Callback REST API</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.sap.sailing.domain.igtimiadapter.oauth.RestApiApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Igtimi Oauth Callback REST API</servlet-name>
<url-pattern>/oauth/*</url-pattern>
</servlet-mapping>
</web-app>
@@ -0,0 +1,4 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>root</artifactId>
<groupId>com.sap.sailing</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>com.sap.sailing.domain.igtimiadapter</artifactId>
<packaging>eclipse-plugin</packaging>
</project>
@@ -0,0 +1,21 @@
package com.sap.sailing.domain.igtimiadapter;
import javax.security.auth.callback.Callback;
/**
* Represents what the Igtimi API calls a "client." In particular, a client has an ID and a secret. The Igtimi
* connector of which this interface is a part acts as a client. A client needs to be authorized by a user to
* be allowed to access the user's data via the Igtimi web services API. Authorization works by directing the
* user to a URL using his/her user agent (usually a browser) which is constructed by the
* {@link IgtimiConnectorFactory} and encodes a callback URL that leads to the {@link Callback} servlet.
* The servlet records the access token that can be used for Igtimi web service API calls that require
* access to the authenticated user's data.
*
* @author Axel Uhl (d043530)
*
*/
public interface Client {
String getId();
String getSecret();
}
@@ -0,0 +1,7 @@
package com.sap.sailing.domain.igtimiadapter;
import com.sap.sailing.domain.igtimiadapter.impl.IgtimiConnectorFactoryImpl;
public interface IgtimiConnectorFactory {
static IgtimiConnectorFactory INSTANCE = new IgtimiConnectorFactoryImpl();
}
@@ -0,0 +1,7 @@
package com.sap.sailing.domain.igtimiadapter.impl;
import com.sap.sailing.domain.igtimiadapter.IgtimiConnectorFactory;
public class IgtimiConnectorFactoryImpl implements IgtimiConnectorFactory {
}
@@ -0,0 +1,17 @@
package com.sap.sailing.domain.igtimiadapter.oauth;
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;
@Path("/v1/authorizationcallback")
public class Callback {
@GET
@Produces("application/json;charset=UTF-8")
@Path("/")
public Response getEvent(@QueryParam("access_token") String accessToken) {
return Response.ok().build();
}
}
@@ -0,0 +1,17 @@
package com.sap.sailing.domain.igtimiadapter.oauth;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.core.Application;
public class RestApiApplication extends Application {
public RestApiApplication() {
}
@Override
public Set<Class<?>> getClasses() {
return new HashSet<Class<?>>(Arrays.asList(new Class<?>[] { Callback.class }));
}
}
@@ -10,9 +10,12 @@ Require-Bundle: com.sap.sailing.mongodb,
com.sap.sailing.domain,
com.mongodb.driver;bundle-version="2.6.2",
com.sap.sailing.domain.common,
com.sap.sailing.domain.shared.android
com.sap.sailing.domain.shared.android,
com.sap.sailing.server.gateway.serialization;bundle-version="1.0.0"
Export-Package: com.sap.sailing.domain.persistence,
com.sap.sailing.domain.persistence.impl;x-friends:="com.sap.sailing.mongodb.test,com.sap.sailing.domain.swisstimingadapter.test,com.sap.sailing.domain.persistence.test",
com.sap.sailing.domain.persistence.media
Bundle-ActivationPolicy: lazy
Import-Package: org.osgi.framework;version="1.6.0"
Import-Package: org.json.simple,
org.json.simple.parser,
org.osgi.framework;version="1.6.0"
@@ -1,8 +1,11 @@
package com.sap.sailing.domain.persistence;
import java.util.Collection;
import java.util.Map;
import com.mongodb.DBObject;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
@@ -86,4 +89,12 @@ public interface DomainObjectFactory {
Map<String, Regatta> loadRaceIDToRegattaAssociations(RegattaRegistry regattaRegistry);
RaceLog loadRaceLog(RaceLogIdentifier identifier);
/**
* Loads all competitors, and resolves them via the domain factory.
* @return
*/
Collection<Competitor> loadAllCompetitors();
DomainFactory getBaseDomainFactory();
}
@@ -2,7 +2,9 @@ package com.sap.sailing.domain.persistence;
import java.io.Serializable;
import com.mongodb.DB;
import com.mongodb.DBObject;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
@@ -86,4 +88,18 @@ public interface MongoObjectFactory {
void removeRegattaForRaceID(String raceIDAsString, Regatta regatta);
/**
* Stores a competitor, including the team and boat. This should not be done for competitors for which
* the master data is supplied by other systems, such as TracTrac, but rather for smartphone tracking,
* where this data is otherwise not recoverable.
* @param competitor the competitor to store/update in the database
*/
void storeCompetitor(Competitor competitor);
void removeAllCompetitors();
void removeCompetitor(Competitor competitor);
DB getDatabase();
}
@@ -1,13 +1,14 @@
package com.sap.sailing.domain.persistence;
import com.sap.sailing.domain.persistence.impl.MongoFactoryImpl;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.persistence.impl.PersistenceFactoryImpl;
import com.sap.sailing.mongodb.MongoDBService;
public interface MongoFactory {
MongoFactory INSTANCE = new MongoFactoryImpl();
public interface PersistenceFactory {
PersistenceFactory INSTANCE = new PersistenceFactoryImpl();
DomainObjectFactory getDefaultDomainObjectFactory();
DomainObjectFactory getDomainObjectFactory(MongoDBService mongoDBService);
DomainObjectFactory getDomainObjectFactory(MongoDBService mongoDBService, DomainFactory baseDomainFactory);
MongoObjectFactory getDefaultMongoObjectFactory();
MongoObjectFactory getMongoObjectFactory(MongoDBService mongoDBService);
}
@@ -51,5 +51,10 @@ public enum CollectionNames {
/**
* Stores the race log events for a tracked race.
*/
RACE_LOGS;
RACE_LOGS,
/**
* Stores competitors for smartphone tracking.
*/
COMPETITORS,
}
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.persistence.impl;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -12,6 +13,8 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import org.bson.types.ObjectId;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
@@ -20,6 +23,7 @@ import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.util.JSON;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.ControlPoint;
@@ -110,20 +114,31 @@ import com.sap.sailing.domain.tracking.Wind;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.impl.WindImpl;
import com.sap.sailing.domain.tracking.impl.WindTrackImpl;
import com.sap.sailing.server.gateway.deserialization.impl.CompetitorJsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.Helpers;
public class DomainObjectFactoryImpl implements DomainObjectFactory {
private static final Logger logger = Logger.getLogger(DomainObjectFactoryImpl.class.getName());
private final CompetitorJsonDeserializer competitorDeserializer;
private final DB database;
private RaceLogEventRestoreFactory raceLogEventFactory;
private final DomainFactory baseDomainFactory;
public DomainObjectFactoryImpl(DB db) {
public DomainObjectFactoryImpl(DB db, DomainFactory baseDomainFactory) {
super();
this.baseDomainFactory = baseDomainFactory;
this.competitorDeserializer = CompetitorJsonDeserializer.create(baseDomainFactory);
this.database = db;
this.raceLogEventFactory = RaceLogEventRestoreFactory.INSTANCE;
}
@Override
public DomainFactory getBaseDomainFactory() {
return baseDomainFactory;
}
public Wind loadWind(DBObject object) {
return new WindImpl(loadPosition(object), loadTimePoint(object), loadSpeedWithBearing(object));
}
@@ -366,7 +381,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
CourseArea courseArea = null;
if (courseAreaId != null) {
UUID courseAreaUuid = UUID.fromString(courseAreaId.toString());
courseArea = DomainFactory.INSTANCE.getExistingCourseAreaById(courseAreaUuid);
courseArea = baseDomainFactory.getExistingCourseAreaById(courseAreaUuid);
}
result = new FlexibleLeaderboardImpl(raceLogStore, (String) dbLeaderboard.get(FieldNames.LEADERBOARD_NAME
@@ -418,7 +433,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
private ScoringScheme loadScoringScheme(DBObject dbLeaderboard) {
ScoringSchemeType scoringSchemeType = getScoringSchemeType(dbLeaderboard);
final ScoringScheme scoringScheme = DomainFactory.INSTANCE.createScoringScheme(scoringSchemeType);
final ScoringScheme scoringScheme = baseDomainFactory.createScoringScheme(scoringSchemeType);
return scoringScheme;
}
@@ -825,7 +840,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
private CourseArea loadCourseArea(DBObject courseAreaDBObject) {
String name = (String) courseAreaDBObject.get(FieldNames.COURSE_AREA_NAME.name());
Serializable id = (Serializable) courseAreaDBObject.get(FieldNames.COURSE_AREA_ID.name());
return DomainFactory.INSTANCE.getOrCreateCourseArea(id, name);
return baseDomainFactory.getOrCreateCourseArea(id, name);
}
@Override
@@ -860,7 +875,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
BoatClass boatClass = null;
if (boatClassName != null) {
boolean typicallyStartsUpwind = (Boolean) dbRegatta.get(FieldNames.BOAT_CLASS_TYPICALLY_STARTS_UPWIND.name());
boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass(boatClassName, typicallyStartsUpwind);
boatClass = baseDomainFactory.getOrCreateBoatClass(boatClassName, typicallyStartsUpwind);
}
BasicDBList dbSeries = (BasicDBList) dbRegatta.get(FieldNames.REGATTA_SERIES.name());
Iterable<Series> series = loadSeries(dbSeries, trackedRegattaRegistry);
@@ -872,7 +887,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
CourseArea courseArea = null;
if (courseAreaId != null) {
UUID courseAreaUuid = UUID.fromString(courseAreaId.toString());
courseArea = DomainFactory.INSTANCE.getExistingCourseAreaById(courseAreaUuid);
courseArea = baseDomainFactory.getExistingCourseAreaById(courseAreaUuid);
}
result = new RegattaImpl(raceLogStore, baseName, boatClass, series, /* persistent */ true, loadScoringScheme(dbRegatta), id, courseArea);
@@ -1166,7 +1181,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
List<Competitor> competitors = new ArrayList<Competitor>();
for (Object object : dbCompetitorList) {
Serializable competitorId = (Serializable) object;
Competitor competitor = DomainFactory.INSTANCE.getExistingCompetitorById(competitorId);
Competitor competitor = baseDomainFactory.getExistingCompetitorById(competitorId);
competitors.add(competitor);
}
return competitors;
@@ -1240,7 +1255,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
Mark leftMark = loadMark((DBObject) dbObject.get(FieldNames.GATE_LEFT.name()));
Mark rightMark = loadMark((DBObject) dbObject.get(FieldNames.GATE_RIGHT.name()));
Gate gate = DomainFactory.INSTANCE.createGate(gateId, leftMark, rightMark, gateName);
Gate gate = baseDomainFactory.createGate(gateId, leftMark, rightMark, gateName);
return gate;
}
@@ -1252,7 +1267,24 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
String markShape = (String) dbObject.get(FieldNames.MARK_SHAPE.name());
MarkType markType = MarkType.valueOf((String) dbObject.get(FieldNames.MARK_TYPE.name()));
Mark mark = DomainFactory.INSTANCE.getOrCreateMark(markId, markName, markType, markColor, markShape, markPattern);
Mark mark = baseDomainFactory.getOrCreateMark(markId, markName, markType, markColor, markShape, markPattern);
return mark;
}
@Override
public Collection<Competitor> loadAllCompetitors() {
ArrayList<Competitor> result = new ArrayList<Competitor>();
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name());
try {
for (DBObject o : collection.find()) {
JSONObject json = Helpers.toJSONObjectSafe(new JSONParser().parse(JSON.serialize(o)));
Competitor c = competitorDeserializer.deserialize(json);
result.add(c);
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Error connecting to MongoDB, unable to load competitors.");
logger.log(Level.SEVERE, "loadCompetitors", e);
}
return result;
}
}
@@ -6,12 +6,14 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import org.bson.types.ObjectId;
import org.json.simple.JSONObject;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.util.JSON;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.ControlPoint;
import com.sap.sailing.domain.base.CourseArea;
@@ -66,16 +68,23 @@ import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegatta;
import com.sap.sailing.domain.tracking.Wind;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.server.gateway.serialization.impl.CompetitorJsonSerializer;
public class MongoObjectFactoryImpl implements MongoObjectFactory {
private static Logger logger = Logger.getLogger(MongoObjectFactoryImpl.class.getName());
private final DB database;
private final CompetitorJsonSerializer competitorSerializer = CompetitorJsonSerializer.create();
public MongoObjectFactoryImpl(DB database) {
super();
this.database = database;
}
@Override
public DB getDatabase() {
return database;
}
public DBObject storeWind(Wind wind) {
DBObject result = new BasicDBObject();
storePositioned(wind, result);
@@ -858,4 +867,27 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
return passing;
}
@Override
public void storeCompetitor(Competitor competitor) {
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name());
JSONObject json = competitorSerializer.serialize(competitor);
BasicDBObject query = new BasicDBObject(CompetitorJsonSerializer.FIELD_ID, competitor.getId());
DBObject entry = (DBObject) JSON.parse(json.toString());
collection.update(query, entry, /* upsrt */true, /* multi */false);
}
@Override
public void removeAllCompetitors() {
logger.info("Removing all persistent competitor info");
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name());
collection.drop();
}
@Override
public void removeCompetitor(Competitor competitor) {
logger.info("Removing persistent competitor info for competitor "+competitor.getName()+" with ID "+competitor.getId());
DBCollection collection = database.getCollection(CollectionNames.COMPETITORS.name());
BasicDBObject query = new BasicDBObject(CompetitorJsonSerializer.FIELD_ID, competitor.getId().toString());
collection.remove(query);
}
}
@@ -1,28 +1,34 @@
package com.sap.sailing.domain.persistence.impl;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.persistence.DomainObjectFactory;
import com.sap.sailing.domain.persistence.MongoFactory;
import com.sap.sailing.domain.persistence.MongoObjectFactory;
import com.sap.sailing.domain.persistence.PersistenceFactory;
import com.sap.sailing.mongodb.MongoDBService;
public class MongoFactoryImpl implements MongoFactory {
public class PersistenceFactoryImpl implements PersistenceFactory {
private final DomainObjectFactory defaultDomainObjectFactory;
private final MongoObjectFactory defaultMongoObjectFactory;
public MongoFactoryImpl() {
public PersistenceFactoryImpl() {
super();
this.defaultDomainObjectFactory = new DomainObjectFactoryImpl(MongoDBService.INSTANCE.getDB());
this.defaultDomainObjectFactory = new DomainObjectFactoryImpl(MongoDBService.INSTANCE.getDB(), DomainFactory.INSTANCE);
this.defaultMongoObjectFactory = new MongoObjectFactoryImpl(MongoDBService.INSTANCE.getDB());
}
/**
* A default domain object factory for test purposes only. In a server environment, ensure NOT to use this and use
* {@link #getDomainObjectFactory(MongoDBService, DomainFactory)} instead, using the base domain factory that
* is provided by <code>RacingEventService.getBaseDomainFactory</code>.
*/
@Override
public DomainObjectFactory getDefaultDomainObjectFactory() {
return defaultDomainObjectFactory;
}
@Override
public DomainObjectFactory getDomainObjectFactory(MongoDBService mongoDBService) {
return new DomainObjectFactoryImpl(mongoDBService.getDB());
public DomainObjectFactory getDomainObjectFactory(MongoDBService mongoDBService, DomainFactory baseDomainFactory) {
return new DomainObjectFactoryImpl(mongoDBService.getDB(), baseDomainFactory);
}
@Override
@@ -7,5 +7,4 @@ public interface Competitor extends Named, WithID, IsManagedBySharedDomainFactor
Team getTeam();
Boat getBoat();
}
@@ -43,7 +43,5 @@ public interface CourseBase extends Named {
void removeWaypoint(int zeroBasedPosition);
Iterable<Leg> getLegsAdjacentTo(Mark mark);
Leg getFirstLeg();
}
@@ -3,6 +3,9 @@ package com.sap.sailing.domain.base;
import java.io.Serializable;
import java.util.UUID;
import com.sap.sailing.domain.base.impl.DynamicBoat;
import com.sap.sailing.domain.base.impl.DynamicCompetitor;
import com.sap.sailing.domain.base.impl.DynamicTeam;
import com.sap.sailing.domain.common.MarkType;
import com.sap.sailing.domain.common.NauticalSide;
@@ -72,11 +75,30 @@ public interface SharedDomainFactory {
*/
BoatClass getOrCreateBoatClass(String name);
/**
* If a valid competitor is returned and the caller has information available that could be used to update the competitor,
* the caller must check the result of {@link #isCompetitorToUpdateDuringGetOrCreate(Competitor)}, and if <code>true</code>,
* must call {@link #getOrCreateCompetitor(Serializable, String, DynamicTeam, DynamicBoat)} to cause an update of the
* competitor's values.
*/
Competitor getExistingCompetitorById(Serializable competitorId);
Competitor createCompetitor(Serializable id, String name, Team team, Boat boat);
/**
* Checks if the <code>competitor</code> shall be updated from the default provided by, e.g., a tracking infrastructure.
* Callers of {@link #getExistingCompetitorById(Serializable)} or {@link #getExistingCompetitorByIdAsString(String)}
* must call this method in case they retrieve a valid competitor by ID and have data available that can be used to update
* the competitor.
*/
boolean isCompetitorToUpdateDuringGetOrCreate(Competitor result);
Competitor getOrCreateCompetitor(Serializable competitorId, String name, Team team, Boat boat);
Competitor getOrCreateCompetitor(Serializable competitorId, String name, DynamicTeam team, DynamicBoat boat);
/**
* Same as {@link #getOrCreateCompetitor(Serializable, String, DynamicTeam, DynamicBoat)} but returns the writable
* specialization of the {@link Competitor} interface. Use with care because the setters offered by the <code>Dynamic...</code>
* subinterfaces don't automatically replicate or store the changes.
*/
DynamicCompetitor getOrCreateDynamicCompetitor(UUID competitorId, String name, DynamicTeam team, DynamicBoat boat);
CourseArea getOrCreateCourseArea(Serializable courseAreaId, String name);
@@ -1,13 +1,12 @@
package com.sap.sailing.domain.base.impl;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.common.impl.NamedImpl;
public class BoatImpl extends NamedImpl implements Boat {
public class BoatImpl extends NamedImpl implements DynamicBoat {
private static final long serialVersionUID = 3489730487528955788L;
private final BoatClass boatClass;
private final String sailID;
private String sailID;
public BoatImpl(String name, BoatClass boatClass, String sailID) {
super(name);
@@ -24,4 +23,9 @@ public class BoatImpl extends NamedImpl implements Boat {
public String getSailID() {
return sailID;
}
@Override
public void setSailId(String newSailId) {
this.sailID = newSailId;
}
}
@@ -2,37 +2,50 @@ package com.sap.sailing.domain.base.impl;
import java.io.Serializable;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.SharedDomainFactory;
import com.sap.sailing.domain.base.Team;
import com.sap.sailing.domain.common.impl.NamedImpl;
public class CompetitorImpl extends NamedImpl implements Competitor {
public class CompetitorImpl implements DynamicCompetitor {
private static final long serialVersionUID = 294603681016643157L;
private final Team team;
private final Boat boat;
private final DynamicTeam team;
private final DynamicBoat boat;
private final Serializable id;
private String name;
public CompetitorImpl(Serializable id, String name, Team team, Boat boat) {
super(name);
public CompetitorImpl(Serializable id, String name, DynamicTeam team, DynamicBoat boat) {
this.id = id;
this.name = name;
this.team = team;
this.boat = boat;
}
@Override
public String getName() {
return name;
}
@Override
public String toString() {
return getName();
}
@Override
public void setName(String newName) {
this.name = newName;
}
@Override
public Serializable getId() {
return id;
}
@Override
public Team getTeam() {
public DynamicTeam getTeam() {
return team;
}
@Override
public Boat getBoat() {
public DynamicBoat getBoat() {
return boat;
}
@@ -6,7 +6,6 @@ import java.util.List;
import com.sap.sailing.domain.base.ControlPoint;
import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.base.Leg;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.impl.NamedImpl;
@@ -71,12 +70,6 @@ public class CourseDataImpl extends NamedImpl implements CourseBase {
this.waypoints.remove(zeroBasedPosition);
}
@Override
public Iterable<Leg> getLegsAdjacentTo(Mark mark) {
// TODO Auto-generated method stub
return null;
}
@Override
public Leg getFirstLeg() {
// TODO Auto-generated method stub
@@ -0,0 +1,7 @@
package com.sap.sailing.domain.base.impl;
import com.sap.sailing.domain.base.Boat;
public interface DynamicBoat extends Boat {
void setSailId(String newSailId);
}
@@ -0,0 +1,9 @@
package com.sap.sailing.domain.base.impl;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.Renamable;
public interface DynamicCompetitor extends Competitor, Renamable {
DynamicBoat getBoat();
DynamicTeam getTeam();
}
@@ -0,0 +1,8 @@
package com.sap.sailing.domain.base.impl;
import com.sap.sailing.domain.base.Nationality;
import com.sap.sailing.domain.base.Person;
public interface DynamicPerson extends Person {
void setNationality(Nationality newNationality);
}
@@ -0,0 +1,8 @@
package com.sap.sailing.domain.base.impl;
import com.sap.sailing.domain.base.Nationality;
import com.sap.sailing.domain.base.Team;
public interface DynamicTeam extends Team {
void setNationality(Nationality newNationality);
}
@@ -48,4 +48,33 @@ public class NationalityImpl implements Nationality {
return domainFactory.getOrCreateNationality(getThreeLetterIOCAcronym());
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((threeLetterIOCAcronym == null) ? 0 : threeLetterIOCAcronym.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
NationalityImpl other = (NationalityImpl) obj;
if (threeLetterIOCAcronym == null) {
if (other.threeLetterIOCAcronym != null)
return false;
} else if (!threeLetterIOCAcronym.equals(other.threeLetterIOCAcronym))
return false;
return true;
}
@Override
public String toString() {
return getThreeLetterIOCAcronym();
}
}
@@ -4,12 +4,11 @@ import java.io.InputStream;
import java.util.Date;
import com.sap.sailing.domain.base.Nationality;
import com.sap.sailing.domain.base.Person;
import com.sap.sailing.domain.common.impl.NamedImpl;
public class PersonImpl extends NamedImpl implements Person {
public class PersonImpl extends NamedImpl implements DynamicPerson {
private static final long serialVersionUID = -2104903799224233508L;
private final Nationality nationality;
private Nationality nationality;
private final Date dateOfBirth;
private final String description;
@@ -30,6 +29,11 @@ public class PersonImpl extends NamedImpl implements Person {
return nationality;
}
@Override
public void setNationality(Nationality newNationality) {
this.nationality = newNationality;
}
@Override
public Date getDateOfBirth() {
return dateOfBirth;
@@ -2,15 +2,14 @@ package com.sap.sailing.domain.base.impl;
import com.sap.sailing.domain.base.Nationality;
import com.sap.sailing.domain.base.Person;
import com.sap.sailing.domain.base.Team;
import com.sap.sailing.domain.common.impl.NamedImpl;
public class TeamImpl extends NamedImpl implements Team {
public class TeamImpl extends NamedImpl implements DynamicTeam {
private static final long serialVersionUID = 4646922280429210183L;
private final Iterable<? extends Person> sailors;
private final Iterable<? extends DynamicPerson> sailors;
private final Person coach;
public TeamImpl(String name, Iterable<? extends Person> sailors, Person coach) {
public TeamImpl(String name, Iterable<? extends DynamicPerson> sailors, Person coach) {
super(name);
this.sailors = sailors;
this.coach = coach;
@@ -39,4 +38,11 @@ public class TeamImpl extends NamedImpl implements Team {
return null;
}
@Override
public void setNationality(Nationality newNationality) {
for (Person sailor : getSailors()) {
((DynamicPerson) sailor).setNationality(newNationality);
}
}
}
@@ -43,8 +43,6 @@ public interface SwissTimingAdapterPersistence extends RaceSpecificMessageLoader
void storeSwissTimingArchiveConfiguration(SwissTimingArchiveConfiguration createSwissTimingArchiveConfiguration);
void storeSailMasterMessage(SailMasterMessage message);
void storeRace(Race race);
void dropAllRaceMasterData();
@@ -30,13 +30,15 @@ import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.TrackedRaceStatusEnum;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.persistence.PersistenceFactory;
import com.sap.sailing.domain.persistence.media.MediaDB;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.swisstimingadapter.MessageType;
import com.sap.sailing.domain.swisstimingadapter.SailMasterConnector;
import com.sap.sailing.domain.swisstimingadapter.SailMasterTransceiver;
import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapter;
import com.sap.sailing.domain.swisstimingadapter.SwissTimingFactory;
import com.sap.sailing.domain.swisstimingadapter.impl.DomainFactoryImpl;
import com.sap.sailing.domain.swisstimingadapter.impl.SwissTimingAdapterFactoryImpl;
import com.sap.sailing.domain.swisstimingadapter.persistence.StoreAndForward;
import com.sap.sailing.domain.swisstimingadapter.persistence.SwissTimingAdapterPersistence;
import com.sap.sailing.domain.swisstimingadapter.persistence.impl.CollectionNames;
@@ -78,6 +80,8 @@ public class EndToEndListeningStoreAndFowardTest {
private EmptyWindStore emptyWindStore;
private EmptyRaceLogStore emptyRaceLogStore;
private RacingEventService racingEventService;
private SwissTimingAdapter swissTimingAdapter;
private List<RacesHandle> raceHandles;
@@ -116,9 +120,9 @@ public class EndToEndListeningStoreAndFowardTest {
/* upsert */true, /* multi */false);
// important: construct a new domain factory each time to make sure the competitor cache starts out empty
final com.sap.sailing.domain.base.impl.DomainFactoryImpl baseDomainFactory = new com.sap.sailing.domain.base.impl.DomainFactoryImpl();
racingEventService = new RacingEventServiceImpl(mongoDBService, SwissTimingFactory.INSTANCE,
new DomainFactoryImpl(baseDomainFactory),
new com.sap.sailing.domain.tractracadapter.impl.DomainFactoryImpl(baseDomainFactory), MediaDB.TEST_STUB);
swissTimingAdapter = new SwissTimingAdapterFactoryImpl().getOrCreateSwissTimingAdapter(baseDomainFactory, swissTimingAdapterPersistence);
racingEventService = new RacingEventServiceImpl(PersistenceFactory.INSTANCE.getDomainObjectFactory(
mongoDBService, baseDomainFactory), PersistenceFactory.INSTANCE.getMongoObjectFactory(mongoDBService), MediaDB.TEST_STUB);
raceHandles = new ArrayList<RacesHandle>();
}
@@ -365,12 +369,12 @@ public class EndToEndListeningStoreAndFowardTest {
private void setUpUsingScript(String[] racesToTrack, String... scriptNames) throws Exception {
for (String raceToTrack : racesToTrack) {
RacesHandle raceHandle = racingEventService.addSwissTimingRace(/* regattaToAddTo */ null /* use a default regatta */,
raceToTrack, "localhost", /* canSendRequests */
CLIENT_PORT, false, emptyWindStore, emptyRaceLogStore, -1);
RacesHandle raceHandle = swissTimingAdapter.addSwissTimingRace(racingEventService,
/* regattaToAddTo */ null /* use a default regatta */, raceToTrack, /* canSendRequests */
"localhost", CLIENT_PORT, false, emptyWindStore, emptyRaceLogStore, -1);
raceHandles.add(raceHandle);
if (connector == null) {
connector = racingEventService.getSwissTimingFactory().getOrCreateSailMasterConnector("localhost",
connector = swissTimingAdapter.getSwissTimingFactory().getOrCreateSailMasterConnector("localhost",
CLIENT_PORT, swissTimingAdapterPersistence, /* canSendRequests */false);
}
}
@@ -198,26 +198,25 @@ public class OverlappingStoredAndReceivedMessagesTest implements RaceSpecificMes
@Override
public Iterable<Race> getRaces() {
// TODO Auto-generated method stub
return null;
}
@Override
public Race getRace(String raceID) {
// TODO Auto-generated method stub
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;
}
@Override
public void storeSailMasterMessage(SailMasterMessage message) {
}
}
@@ -12,3 +12,5 @@ Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.domain.common
Export-Package: com.sap.sailing.domain.swisstimingadapter,
com.sap.sailing.domain.swisstimingadapter.impl;x-friends:="com.sap.sailing.server.test"
Import-Package: org.osgi.framework;version="1.7.0"
Bundle-Activator: com.sap.sailing.domain.swisstimingadapter.impl.Activator
@@ -21,6 +21,11 @@ import com.sap.sailing.domain.tracking.WindStore;
import difflib.PatchFailedException;
public interface DomainFactory {
/**
* A default domain factory for test purposes only. In a server environment, ensure NOT to use this. Use what can be
* reached from <code>RacingEventService.getBaseDomainFactory()</code> instead which should be the single instance
* used by all other services linked to the <code>RacingEventService</code>.
*/
final static DomainFactory INSTANCE = new DomainFactoryImpl(com.sap.sailing.domain.base.DomainFactory.INSTANCE);
com.sap.sailing.domain.base.DomainFactory getBaseDomainFactory();
@@ -18,6 +18,8 @@ public interface RaceSpecificMessageLoader {
*/
List<SailMasterMessage> loadRaceMessages(String raceID);
void storeSailMasterMessage(SailMasterMessage message);
Iterable<Race> getRaces();
Race getRace(String raceID);
@@ -0,0 +1,37 @@
package com.sap.sailing.domain.swisstimingadapter;
import java.io.IOException;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.util.List;
import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.tracking.RacesHandle;
import com.sap.sailing.domain.tracking.TrackerManager;
import com.sap.sailing.domain.tracking.WindStore;
public interface SwissTimingAdapter {
List<com.sap.sailing.domain.swisstimingadapter.RaceRecord> getSwissTimingRaceRecords(String hostname, int port,
boolean canSendRequests) throws InterruptedException, UnknownHostException, IOException, ParseException;
/**
* @param trackerManager TODO
* @param regattaToAddTo
* if <code>null</code>, an existing regatta by the name of the TracTrac event with the boat class name
* appended in parentheses will be looked up; if not found, a default regatta with that name will be
* created, with a single default series and a single default fleet. If a valid {@link RegattaIdentifier}
* is specified, a regatta lookup is performed with that identifier; if the regatta is found, it is used
* to add the races to. Otherwise, a default regatta as described above will be created and used.
*/
RacesHandle addSwissTimingRace(TrackerManager trackerManager, RegattaIdentifier regattaToAddTo, String raceID, String hostname,
int port, boolean canSendRequests, WindStore windStore, RaceLogStore logStore, long timeoutInMilliseconds)
throws InterruptedException, UnknownHostException, IOException, ParseException, Exception;
SwissTimingFactory getSwissTimingFactory();
void storeSwissTimingDummyRace(String racMessage, String stlMesssage, String ccgMessage) throws IllegalArgumentException;
com.sap.sailing.domain.swisstimingadapter.DomainFactory getSwissTimingDomainFactory();
}
@@ -0,0 +1,7 @@
package com.sap.sailing.domain.swisstimingadapter;
import com.sap.sailing.domain.base.DomainFactory;
public interface SwissTimingAdapterFactory {
SwissTimingAdapter getOrCreateSwissTimingAdapter(DomainFactory baseDomainFactory, RaceSpecificMessageLoader persistence);
}
@@ -0,0 +1,29 @@
package com.sap.sailing.domain.swisstimingadapter.impl;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapterFactory;
public class Activator implements BundleActivator {
private static final Logger logger = Logger.getLogger(Activator.class.getName());
private final SwissTimingAdapterFactory swissTimingAdapterFactory;
public Activator() {
// there is exactly one instance of the racingEventService in the whole server
swissTimingAdapterFactory = new SwissTimingAdapterFactoryImpl();
}
public void start(BundleContext context) throws Exception {
// register the racing service in the OSGi registry
context.registerService(SwissTimingAdapterFactory.class.getName(), swissTimingAdapterFactory, null);
logger.log(Level.INFO, "Started "+context.getBundle().getSymbolicName());
}
public void stop(BundleContext context) throws Exception {
}
}
@@ -9,18 +9,18 @@ import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.ControlPoint;
import com.sap.sailing.domain.base.Nationality;
import com.sap.sailing.domain.base.Person;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Team;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.BoatImpl;
import com.sap.sailing.domain.base.impl.CourseImpl;
import com.sap.sailing.domain.base.impl.DynamicBoat;
import com.sap.sailing.domain.base.impl.DynamicPerson;
import com.sap.sailing.domain.base.impl.DynamicTeam;
import com.sap.sailing.domain.base.impl.PersonImpl;
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl;
@@ -41,9 +41,9 @@ import com.sap.sailing.domain.swisstimingadapter.Mark;
import com.sap.sailing.domain.swisstimingadapter.MessageType;
import com.sap.sailing.domain.swisstimingadapter.Race;
import com.sap.sailing.domain.swisstimingadapter.RaceSpecificMessageLoader;
import com.sap.sailing.domain.swisstimingadapter.StartList;
import com.sap.sailing.domain.swisstimingadapter.RaceType;
import com.sap.sailing.domain.swisstimingadapter.RaceType.OlympicRaceCode;
import com.sap.sailing.domain.swisstimingadapter.StartList;
import com.sap.sailing.domain.swisstimingadapter.SwissTimingFactory;
import com.sap.sailing.domain.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.MarkPassing;
@@ -127,14 +127,14 @@ public class DomainFactoryImpl implements DomainFactory {
@Override
public Competitor getOrCreateCompetitor(com.sap.sailing.domain.swisstimingadapter.Competitor competitor, RaceType raceType) {
Competitor result = getCompetitorByBoatIDAndRaceType(competitor.getBoatID(), raceType);
if (result == null) {
Boat boat = new BoatImpl(competitor.getName(), raceType.getBoatClass(), competitor.getBoatID());
List<Person> teamMembers = new ArrayList<Person>();
if (result == null || baseDomainFactory.isCompetitorToUpdateDuringGetOrCreate(result)) {
DynamicBoat boat = new BoatImpl(competitor.getName(), raceType.getBoatClass(), competitor.getBoatID());
List<DynamicPerson> teamMembers = new ArrayList<DynamicPerson>();
for (String teamMemberName : competitor.getName().split("[-+&]")) {
teamMembers.add(new PersonImpl(teamMemberName.trim(), getOrCreateNationality(competitor.getThreeLetterIOCCode()),
/* dateOfBirth */ null, teamMemberName.trim()));
}
Team team = new TeamImpl(competitor.getName(), teamMembers, /* coach */ null);
DynamicTeam team = new TeamImpl(competitor.getName(), teamMembers, /* coach */ null);
result = baseDomainFactory.getOrCreateCompetitor(getCompetitorID(competitor.getBoatID(), raceType),
competitor.getName(), team, boat);
}
@@ -0,0 +1,28 @@
package com.sap.sailing.domain.swisstimingadapter.impl;
import java.util.HashMap;
import java.util.Map;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.swisstimingadapter.RaceSpecificMessageLoader;
import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapter;
import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapterFactory;
public class SwissTimingAdapterFactoryImpl implements SwissTimingAdapterFactory {
private final Map<DomainFactory, SwissTimingAdapter> adaptersForBaseFactories;
public SwissTimingAdapterFactoryImpl() {
adaptersForBaseFactories = new HashMap<>();
}
@Override
public synchronized SwissTimingAdapter getOrCreateSwissTimingAdapter(DomainFactory baseDomainFactory, RaceSpecificMessageLoader persistence) {
SwissTimingAdapter result = adaptersForBaseFactories.get(baseDomainFactory);
if (result == null) {
result = new SwissTimingAdapterImpl(baseDomainFactory, persistence);
adaptersForBaseFactories.put(baseDomainFactory, result);
}
return result;
}
}
@@ -0,0 +1,90 @@
package com.sap.sailing.domain.swisstimingadapter.impl;
import java.io.IOException;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.swisstimingadapter.Race;
import com.sap.sailing.domain.swisstimingadapter.RaceSpecificMessageLoader;
import com.sap.sailing.domain.swisstimingadapter.SailMasterConnector;
import com.sap.sailing.domain.swisstimingadapter.SailMasterMessage;
import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapter;
import com.sap.sailing.domain.swisstimingadapter.SwissTimingFactory;
import com.sap.sailing.domain.tracking.RacesHandle;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackerManager;
import com.sap.sailing.domain.tracking.WindStore;
public class SwissTimingAdapterImpl implements SwissTimingAdapter {
private final SwissTimingFactory swissTimingFactory;
private final com.sap.sailing.domain.swisstimingadapter.DomainFactory swissTimingDomainFactory;
private final RaceSpecificMessageLoader raceSpecificMessageLoader;
public SwissTimingAdapterImpl(DomainFactory baseDomainFactory, RaceSpecificMessageLoader raceSpecificMessageLoader) {
swissTimingFactory = SwissTimingFactory.INSTANCE;
swissTimingDomainFactory = new DomainFactoryImpl(baseDomainFactory);
this.raceSpecificMessageLoader = raceSpecificMessageLoader;
}
@Override
public com.sap.sailing.domain.swisstimingadapter.DomainFactory getSwissTimingDomainFactory() {
return swissTimingDomainFactory;
}
@Override
public SwissTimingFactory getSwissTimingFactory() {
return swissTimingFactory;
}
@Override
public List<com.sap.sailing.domain.swisstimingadapter.RaceRecord> getSwissTimingRaceRecords(String hostname,
int port, boolean canSendRequests) throws InterruptedException, UnknownHostException, IOException,
ParseException {
List<com.sap.sailing.domain.swisstimingadapter.RaceRecord> result = new ArrayList<com.sap.sailing.domain.swisstimingadapter.RaceRecord>();
SailMasterConnector swissTimingConnector = swissTimingFactory.getOrCreateSailMasterConnector(hostname, port,
raceSpecificMessageLoader, canSendRequests);
for (Race race : swissTimingConnector.getRaces()) {
String raceID = race.getRaceID();
TimePoint startTime = swissTimingConnector.getStartTime(raceID);
boolean hasCourse = swissTimingConnector.hasCourse(raceID);
boolean hasStartlist = swissTimingConnector.hasStartlist(raceID);
result.add(new com.sap.sailing.domain.swisstimingadapter.RaceRecord(raceID,
race.getDescription(), startTime == null ? null : startTime.asDate(),
hasCourse, hasStartlist));
}
return result;
}
@Override
public RacesHandle addSwissTimingRace(TrackerManager trackerManager, RegattaIdentifier regattaToAddTo, String raceID, String hostname,
int port, boolean canSendRequests, WindStore windStore, RaceLogStore logStore, long timeoutInMilliseconds)
throws Exception {
return trackerManager.addRace(regattaToAddTo, swissTimingDomainFactory.createTrackingConnectivityParameters(hostname, port,
raceID, canSendRequests, TrackedRace.DEFAULT_LIVE_DELAY_IN_MILLISECONDS, swissTimingFactory,
swissTimingDomainFactory, logStore, windStore, raceSpecificMessageLoader), windStore,
timeoutInMilliseconds);
}
@Override
public void storeSwissTimingDummyRace(String racMessage, String stlMessage, String ccgMessage) {
SailMasterMessage racSMMessage = swissTimingFactory.createMessage(racMessage, null);
SailMasterMessage stlSMMessage = swissTimingFactory.createMessage(stlMessage, null);
SailMasterMessage ccgSMMessage = swissTimingFactory.createMessage(ccgMessage, null);
if (raceSpecificMessageLoader.getRace(stlSMMessage.getRaceID()) != null) {
throw new IllegalArgumentException("Race with raceID \"" + stlSMMessage.getRaceID() + "\" already exists.");
} else {
raceSpecificMessageLoader.storeSailMasterMessage(racSMMessage);
raceSpecificMessageLoader.storeSailMasterMessage(stlSMMessage);
raceSpecificMessageLoader.storeSailMasterMessage(ccgSMMessage);
}
}
}
@@ -17,6 +17,7 @@ import java.util.Map;
import org.junit.Ignore;
import org.junit.Test;
import com.sap.sailing.domain.swisstimingadapter.DomainFactory;
import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayRace;
import com.sap.sailing.domain.swisstimingreplayadapter.impl.SwissTimingRaceConfig;
import com.sap.sailing.domain.swisstimingreplayadapter.impl.SwissTimingReplayServiceImpl;
@@ -29,13 +30,13 @@ public class SwissTimingRaceConfigurationTest {
@Test
public void testLoadConfigurations() throws IOException, ParseException, org.json.simple.parser.ParseException {
InputStream inputStream = getClass().getResourceAsStream(JSON_URL);
List<SwissTimingReplayRace> races = new SwissTimingReplayServiceImpl().parseJSONObject(inputStream , JSON_URL);
List<SwissTimingReplayRace> races = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE).parseJSONObject(inputStream , JSON_URL);
Map<String, SwissTimingRaceConfig> configsById = new HashMap<String, SwissTimingRaceConfig>();
for (SwissTimingReplayRace race : races) {
URL configFileURL = new URL(MessageFormat.format(SwissTimingReplayServiceImpl.RACE_CONFIG_URL_TEMPLATE, race.getRaceId()));
URLConnection connection = configFileURL.openConnection();
InputStream configDataStream = connection.getInputStream();
SwissTimingRaceConfig raceConfig = new SwissTimingReplayServiceImpl().loadRaceConfig(configDataStream);
SwissTimingRaceConfig raceConfig = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE).loadRaceConfig(configDataStream);
configsById.put(race.getRaceId(), raceConfig);
}
}
@@ -45,7 +46,7 @@ public class SwissTimingRaceConfigurationTest {
URL configFileURL = new URL(MessageFormat.format(SwissTimingReplayServiceImpl.RACE_CONFIG_URL_TEMPLATE, "446483"));
URLConnection connection = configFileURL.openConnection();
InputStream configDataStream = connection.getInputStream();
SwissTimingRaceConfig config_446483 = new SwissTimingReplayServiceImpl().loadRaceConfig(configDataStream);
SwissTimingRaceConfig config_446483 = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE).loadRaceConfig(configDataStream);
assertNotNull(config_446483);
assertEquals("GB", config_446483.country_code);
assertNull(config_446483.event_name);
@@ -62,7 +63,7 @@ public class SwissTimingRaceConfigurationTest {
URL configFileURL = new URL(MessageFormat.format(SwissTimingReplayServiceImpl.RACE_CONFIG_URL_TEMPLATE, "6260"));
URLConnection connection = configFileURL.openConnection();
InputStream configDataStream = connection.getInputStream();
SwissTimingRaceConfig config_446483 = new SwissTimingReplayServiceImpl().loadRaceConfig(configDataStream);
SwissTimingRaceConfig config_446483 = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE).loadRaceConfig(configDataStream);
assertNotNull(config_446483);
assertEquals("GB", config_446483.country_code);
assertEquals("London 2012 Olympic Games", config_446483.event_name);
@@ -32,7 +32,7 @@ public class SwissTimingReplayAdapterServiceTest {
public void testLoadRaceJson() throws Exception {
String swissTimingUrlText = "/2012_OSG.json";
InputStream inputStream = getClass().getResourceAsStream(swissTimingUrlText);
final SwissTimingReplayServiceImpl swissTimingReplayService = new SwissTimingReplayServiceImpl();
final SwissTimingReplayServiceImpl swissTimingReplayService = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE);
List<SwissTimingReplayRace> races = swissTimingReplayService.parseJSONObject(inputStream , swissTimingUrlText);
assertEquals(201, races.size());
assertEquals("446483", races.get(0).getRaceId());
@@ -12,3 +12,5 @@ Require-Bundle: com.sap.sailing.domain.common,
com.sap.sailing.domain.swisstimingadapter,
com.googlecode.java-diff-utils;bundle-version="1.3.0"
Export-Package: com.sap.sailing.domain.swisstimingreplayadapter
Import-Package: org.osgi.framework;version="1.7.0"
Bundle-Activator: com.sap.sailing.domain.swisstimingreplayadapter.impl.Activator
@@ -7,7 +7,6 @@ import java.text.ParseException;
import java.util.List;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.swisstimingadapter.DomainFactory;
import com.sap.sailing.domain.swisstimingreplayadapter.impl.SwissTimingRaceConfig;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
@@ -41,6 +40,6 @@ public interface SwissTimingReplayService {
* the regatta to which the race shall be added; if <code>null</code>, a default regatta will be
* created/used
*/
void loadRaceData(String link, DomainFactory domainFactory, Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry);
void loadRaceData(String link, Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry);
}
@@ -1,9 +1,10 @@
package com.sap.sailing.domain.swisstimingreplayadapter;
import com.sap.sailing.domain.swisstimingadapter.DomainFactory;
import com.sap.sailing.domain.swisstimingreplayadapter.impl.SwissTimingReplayServiceFactoryImpl;
public interface SwissTimingReplayServiceFactory {
static SwissTimingReplayServiceFactory INSTANCE = new SwissTimingReplayServiceFactoryImpl();
SwissTimingReplayService createSwissTimingReplayService();
SwissTimingReplayService createSwissTimingReplayService(DomainFactory domainFactory);
}
@@ -0,0 +1,29 @@
package com.sap.sailing.domain.swisstimingreplayadapter.impl;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayServiceFactory;
public class Activator implements BundleActivator {
private static final Logger logger = Logger.getLogger(Activator.class.getName());
private final SwissTimingReplayServiceFactory swissTimingReplayServiceFactory;
public Activator() {
// there is exactly one instance of the racingEventService in the whole server
swissTimingReplayServiceFactory = new SwissTimingReplayServiceFactoryImpl();
}
public void start(BundleContext context) throws Exception {
// register the racing service in the OSGi registry
context.registerService(SwissTimingReplayServiceFactory.class.getName(), swissTimingReplayServiceFactory, null);
logger.log(Level.INFO, "Started "+context.getBundle().getSymbolicName());
}
public void stop(BundleContext context) throws Exception {
}
}
@@ -1,13 +1,26 @@
package com.sap.sailing.domain.swisstimingreplayadapter.impl;
import java.util.HashMap;
import java.util.Map;
import com.sap.sailing.domain.swisstimingadapter.DomainFactory;
import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayService;
import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayServiceFactory;
public class SwissTimingReplayServiceFactoryImpl implements SwissTimingReplayServiceFactory {
private final Map<DomainFactory, SwissTimingReplayService> servicesForDomainFactories;
@Override
public SwissTimingReplayService createSwissTimingReplayService() {
return new SwissTimingReplayServiceImpl();
public SwissTimingReplayServiceFactoryImpl() {
servicesForDomainFactories = new HashMap<>();
}
@Override
public SwissTimingReplayService createSwissTimingReplayService(DomainFactory domainFactory) {
SwissTimingReplayService result = servicesForDomainFactories.get(domainFactory);
if (result == null) {
result = new SwissTimingReplayServiceImpl(domainFactory);
servicesForDomainFactories.put(domainFactory, result);
}
return result;
}
}
@@ -33,6 +33,12 @@ public class SwissTimingReplayServiceImpl implements SwissTimingReplayService {
private static final String SWISSTIMING_DATEFORMAT_PATTERN = "dd.MM.yyyy HH:mm";
private static final TimeZone DEFAULT_TIMEZONE = TimeZone.getTimeZone("GMT");
private final DomainFactory domainFactory;
public SwissTimingReplayServiceImpl(DomainFactory domainFactory) {
this.domainFactory = domainFactory;
}
@Override
public List<SwissTimingReplayRace> listReplayRaces(String swissTimingUrlText) {
URL raceListUrl;
@@ -143,7 +149,7 @@ public class SwissTimingReplayServiceImpl implements SwissTimingReplayService {
}
@Override
public void loadRaceData(String link, DomainFactory domainFactory, Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry) {
public void loadRaceData(String link, Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry) {
SwissTimingReplayListener listener = new SwissTimingReplayToDomainAdapter(regatta, domainFactory, trackedRegattaRegistry);
loadRaceData(link, listener);
}
@@ -16,5 +16,5 @@
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.domain.test"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dhttp.proxyHost=proxy -Dhttp.proxyPort=8080 -XX:+UseParallelGC -Dtractrac.tunnel=true -Dtractrac.tunnel.host=10.18.22.156 -ea -Djava.util.logging.config.file=${project_loc:com.sap.sailing.server}/../target/configuration/logging_debug.properties"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dhttp.proxyHost=proxy -Dhttp.proxyPort=8080 -XX:+UseParallelGC -Dtractrac.tunnel=true -Dtractrac.tunnel.host=localhost -ea -Djava.util.logging.config.file=${project_loc:com.sap.sailing.server}/../target/configuration/logging_debug.properties"/>
</launchConfiguration>
@@ -16,6 +16,6 @@ public abstract class AbstractLeaderboardTest {
/* 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",
new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null));
new BoatClassImpl("505", /* typicallyStartsUpwind */ true), /* sailID */ null));
}
}
@@ -0,0 +1,68 @@
package com.sap.sailing.domain.test;
import static org.junit.Assert.assertEquals;
import java.util.Collections;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.common.NauticalSide;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.GPSFix;
import com.sap.sailing.domain.tracking.LineLengthAndAdvantage;
import com.sap.sailing.domain.tracking.impl.GPSFixImpl;
public class LineAnalysisTest extends TrackBasedTest {
private MillisecondsTimePoint now;
private DynamicTrackedRace trackedRace;
@Before
public void setUp() {
final List<Competitor> emptyCompetitorList = Collections.emptyList();
now = MillisecondsTimePoint.now();
trackedRace = createTestTrackedRace("Test Regatta", "Test Race", "505", emptyCompetitorList, now);
}
@Test
public void testLinesAnalysisWithNoAdvantage() {
final LineLengthAndAdvantage startLine = trackedRace.getStartLine(now);
assertEquals(0, startLine.getAdvantage().getMeters(), 0.001);
final LineLengthAndAdvantage finishLine = trackedRace.getFinishLine(now);
assertEquals(0, finishLine.getAdvantage().getMeters(), 0.001);
}
@Test
public void testStartLineAnalysisWithAdvantageOnStarboard() {
Mark right = getMark("Right lee gate buoy");
final DynamicGPSFixTrack<Mark, GPSFix> rightMarkTrack = trackedRace.getOrCreateTrack(right);
Position rightMarkAtNow = rightMarkTrack.getFirstRawFix().getPosition();
TimePoint aBitAfterNow = now.plus(1000);
final MeterDistance advantage = new MeterDistance(10);
rightMarkTrack.addGPSFix(new GPSFixImpl(rightMarkAtNow.translateGreatCircle(new DegreeBearingImpl(0), advantage), aBitAfterNow));
final LineLengthAndAdvantage startLine = trackedRace.getStartLine(aBitAfterNow);
assertEquals(advantage.getMeters(), startLine.getAdvantage().getMeters(), 0.1);
assertEquals(NauticalSide.STARBOARD, startLine.getAdvantageousSideWhileApproachingLine());
final LineLengthAndAdvantage finishLine = trackedRace.getFinishLine(aBitAfterNow);
assertEquals(advantage.getMeters(), finishLine.getAdvantage().getMeters(), 0.1);
assertEquals(NauticalSide.PORT, finishLine.getAdvantageousSideWhileApproachingLine());
}
private Mark getMark(String name) {
for (Mark mark : trackedRace.getMarks()) {
if (mark.getName().equals(name)) {
return mark;
}
}
return null;
}
}
@@ -14,18 +14,25 @@ import java.util.Set;
import org.junit.Test;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Nationality;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.BoatImpl;
import com.sap.sailing.domain.base.impl.CompetitorImpl;
import com.sap.sailing.domain.base.impl.CourseAreaImpl;
import com.sap.sailing.domain.base.impl.DomainFactoryImpl;
import com.sap.sailing.domain.base.impl.PersonImpl;
import com.sap.sailing.domain.base.impl.TeamImpl;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.HighPoint;
import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard;
public class OfflineSerializationTest extends AbstractSerializationTest {
/**
@@ -87,6 +94,22 @@ public class OfflineSerializationTest extends AbstractSerializationTest {
clone.getDiscardIndexResultsStartingWithHowManyRaces()));
}
// see bug 1605
@Test
public void testSerializingOverallLeaderboardWithFactorOnColumn() throws ClassNotFoundException, IOException {
Leaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 5 }), new HighPoint(), new CourseAreaImpl("Alpha", "Alpha"));
LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl("LeaderboardGroup", "Test Leaderboard Group", /* displayGroupsInReverseOrder */ false, Arrays.asList(new Leaderboard[] { leaderboard }));
final LeaderboardGroupMetaLeaderboard overallLeaderboard = new LeaderboardGroupMetaLeaderboard(leaderboardGroup, new HighPoint(), new ThresholdBasedResultDiscardingRuleImpl(new int[0]));
leaderboardGroup.setOverallLeaderboard(overallLeaderboard);
final double FACTOR = 2.0;
overallLeaderboard.getRaceColumnByName("Test Leaderboard").setFactor(FACTOR);
DomainFactory receiverDomainFactory = new DomainFactoryImpl();
LeaderboardGroup clone = cloneBySerialization(leaderboardGroup, receiverDomainFactory);
assertEquals(FACTOR, overallLeaderboard.getRaceColumnByName("Test Leaderboard").getFactor(), 0.00000001);
assertEquals(FACTOR, clone.getOverallLeaderboard().getRaceColumnByName("Test Leaderboard").getFactor(), 0.00000001);
}
@Test
public void testIdentityStabilityOfMarkSerialization() throws ClassNotFoundException, IOException {
DomainFactory senderDomainFactory = new DomainFactoryImpl();
@@ -150,14 +150,38 @@ public class PositionTest {
assertEquals(0.5*p1.getDistance(p2).getMeters(), alongTrackDistanceFromP1ToPNorth.getMeters(), 0.001);
}
@Test
public void negativeAlongTrackDistanceTest() {
Position p1 = new DegreePosition(1, 0);
Position p2 = new DegreePosition(1, -0.0001);
final Bearing bearingFromP1ToP2 = p1.getBearingGreatCircle(p2);
assertEquals(-p1.getDistance(p2).getMeters(), p1.alongTrackDistance(p2, bearingFromP1ToP2).getMeters(), 0.001);
}
@Test
public void testZeroCrossTrackError() {
Position p1 = new DegreePosition(20, 15);
Position p2 = new DegreePosition(15, 15);
Distance result = p1.crossTrackError(p2, new DegreeBearingImpl(0));
Distance result = p1.absoluteCrossTrackError(p2, new DegreeBearingImpl(0));
assertEquals(0, result.getMeters(), 0.0000001);
}
@Test
public void testSignedCrossTrackErrorToRight() {
Position p1 = new DegreePosition(20, 16);
Position p2 = new DegreePosition(15, 15);
Distance result = p1.crossTrackError(p2, new DegreeBearingImpl(0));
assertTrue(result.getMeters() > 0);
}
@Test
public void testSignedCrossTrackErrorToLeft() {
Position p1 = new DegreePosition(20, 14);
Position p2 = new DegreePosition(15, 15);
Distance result = p1.crossTrackError(p2, new DegreeBearingImpl(0));
assertTrue(result.getMeters() < 0);
}
@Test
public void addBearings() {
Bearing b1 = new DegreeBearingImpl(123);
@@ -196,5 +220,26 @@ public class PositionTest {
assertEquals(180., b3.getDifferenceTo(b6).getDegrees(), 0.000000001);
assertEquals(-180., b6.getDifferenceTo(b3).getDegrees(), 0.000000001);
}
@Test
public void distanceToLineTest(){
double delta = 0.000000001;
Position left = new DegreePosition(-1, -1);
Position right = new DegreePosition(1, 1);
Position p1 = new DegreePosition(0,0);
Position p2 = new DegreePosition(1, -1);
Position p3 = new DegreePosition(-2, 0);
Position p4 = new DegreePosition(5, 0);
Position p5 = new DegreePosition(2, 2);
Position p6 = new DegreePosition(0, -2);
assertEquals(p2.getDistanceToLine(left, right).getMeters(), p2.absoluteCrossTrackError(left, left.getBearingGreatCircle(right)).getMeters(), delta);
assertEquals(p3.getDistanceToLine(left, right).getMeters(), p3.getDistance(left).getMeters(), delta);
assertEquals(p4.getDistanceToLine(left, right).getMeters(), p4.getDistance(right).getMeters(), delta);
assertEquals(p5.getDistanceToLine(left, right).getMeters(), p5.getDistance(right).getMeters(), delta);
assertEquals(p1.getDistanceToLine(p3, p4).getMeters(), 0, delta);
assertEquals(p4.getDistanceToLine(p1, p3).getMeters(), p4.getDistance(p1).getMeters(), delta);
assertEquals(p6.getDistanceToLine(p3, p4).getMeters(), p6.getDistance(p1).getMeters(), delta);
assertEquals(p6.getDistanceToLine(p1, p4).getMeters(), p6.getDistance(p1).getMeters(), delta);
assertEquals(right.getDistanceToLine(p6, p1).getMeters(), right.getDistance(p1).getMeters(), delta);
}
}
@@ -17,11 +17,11 @@ import java.util.Map;
import java.util.Set;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Person;
import com.sap.sailing.domain.base.Team;
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.DynamicPerson;
import com.sap.sailing.domain.base.impl.DynamicTeam;
import com.sap.sailing.domain.base.impl.PersonImpl;
import com.sap.sailing.domain.base.impl.TeamImpl;
import com.sap.sailing.domain.common.Position;
@@ -114,8 +114,8 @@ public abstract class StoredTrackBasedTest extends TrackBasedTest {
Map<Competitor, DynamicGPSFixTrack<Competitor, GPSFixMoving>> tracks = new HashMap<Competitor, DynamicGPSFixTrack<Competitor,GPSFixMoving>>();
final String KIELER_WOCHE = "Kieler Woche";
for (String competitorName : getCompetitorNamesOfStoredTracks(KIELER_WOCHE)) {
Person p = new PersonImpl(competitorName, /* nationality */ null, /* dateOfBirth */ null, /* description */ null);
Team t = new TeamImpl(competitorName, Collections.singleton(p), /* coach */ null);
DynamicPerson p = new PersonImpl(competitorName, /* nationality */ null, /* dateOfBirth */ null, /* description */ null);
DynamicTeam t = new TeamImpl(competitorName, Collections.singleton(p), /* coach */ null);
Competitor c = new CompetitorImpl(competitorName, competitorName, t, new BoatImpl(competitorName,
new BoatClassImpl("505", /* typicallyStartsUpwind */ true), null));
DynamicGPSFixTrack<Competitor, GPSFixMoving> track = readTrack(c, KIELER_WOCHE);
@@ -305,6 +305,69 @@ public class TrackTest {
assertEquals(0, average.getBearing().getDegrees(), 0.00001);
}
@Test
public void testFixValidEvenIfFixProvidedSpeedIsOutrageous() {
DynamicGPSFixTrack<Object, GPSFixMoving> track = new DynamicGPSFixMovingTrackImpl<Object>(null, /* millisecondsOverWhichToAverage */ 5000l);
TimePoint t1 = new MillisecondsTimePoint(1000);
TimePoint t2 = new MillisecondsTimePoint(2000);
TimePoint t3 = new MillisecondsTimePoint(3000);
GPSFixMoving f1 = new GPSFixMovingImpl(new DegreePosition(1./3600.*1./60., 0), t1, new KnotSpeedWithBearingImpl(1, new DegreeBearingImpl(0)));
GPSFixMoving f2 = new GPSFixMovingImpl(new DegreePosition(2./3600.*1./60., 0), t2, new KnotSpeedWithBearingImpl(150, new DegreeBearingImpl(0))); // outrageous speed; to be ignored by getEstimatedSpeed
GPSFixMoving f3 = new GPSFixMovingImpl(new DegreePosition(3./3600.*1./60., 0), t3, new KnotSpeedWithBearingImpl(1, new DegreeBearingImpl(0)));
track.addGPSFix(f1);
track.addGPSFix(f2);
track.addGPSFix(f3);
assertEquals(f2, track.getFirstFixAtOrAfter(f2.getTimePoint())); // expect the fix to still be valid, but only its provided speed shall be ignored
}
@Test
public void testFixInvalidEvenIfPositionInferredSpeedIsOutrageous() {
DynamicGPSFixTrack<Object, GPSFixMoving> track = new DynamicGPSFixMovingTrackImpl<Object>(null, /* millisecondsOverWhichToAverage */ 5000l);
TimePoint t1 = new MillisecondsTimePoint(1000);
TimePoint t2 = new MillisecondsTimePoint(2000);
TimePoint t3 = new MillisecondsTimePoint(3000);
GPSFixMoving f1 = new GPSFixMovingImpl(new DegreePosition(1./3600.*1./60., 0), t1, new KnotSpeedWithBearingImpl(1, new DegreeBearingImpl(0)));
GPSFixMoving f2 = new GPSFixMovingImpl(new DegreePosition(150./3600.*1./60., 0), t2, new KnotSpeedWithBearingImpl(150, new DegreeBearingImpl(0))); // outrageous speed; to be ignored by getEstimatedSpeed
GPSFixMoving f3 = new GPSFixMovingImpl(new DegreePosition(3./3600.*1./60., 0), t3, new KnotSpeedWithBearingImpl(1, new DegreeBearingImpl(0)));
track.addGPSFix(f1);
track.addGPSFix(f2);
track.addGPSFix(f3);
assertEquals(f3, track.getFirstFixAtOrAfter(f2.getTimePoint())); // expect the f2 fix to be invalid
}
@Test
public void testFixInvalidBecauseProvidedSpeedIsTooDifferent() {
DynamicGPSFixTrack<Object, GPSFixMoving> track = new DynamicGPSFixMovingTrackImpl<Object>(null, /* millisecondsOverWhichToAverage */ 5000l);
TimePoint t1 = new MillisecondsTimePoint(1000);
TimePoint t2 = new MillisecondsTimePoint(2000);
TimePoint t3 = new MillisecondsTimePoint(3000);
GPSFixMoving f1 = new GPSFixMovingImpl(new DegreePosition(1./3600.*1./60., 0), t1, new KnotSpeedWithBearingImpl(1, new DegreeBearingImpl(0)));
GPSFixMoving f2 = new GPSFixMovingImpl(new DegreePosition(2./3600.*1./60., 0), t2, new KnotSpeedWithBearingImpl(10, new DegreeBearingImpl(0))); // outrageous speed; to be ignored by getEstimatedSpeed
GPSFixMoving f3 = new GPSFixMovingImpl(new DegreePosition(3./3600.*1./60., 0), t3, new KnotSpeedWithBearingImpl(1, new DegreeBearingImpl(0)));
track.addGPSFix(f1);
track.addGPSFix(f2);
track.addGPSFix(f3);
// expect the f2 fix to be invalid because provided speed is too different
assertEquals(0, f3.getPosition().getDistance(track.getFirstFixAtOrAfter(f2.getTimePoint()).getPosition()).getMeters(), 0.001);
}
@Test
public void testIgnoringFixProvidedSpeedIfItIsOutrageous() {
DynamicGPSFixTrack<Object, GPSFixMoving> track = new DynamicGPSFixMovingTrackImpl<Object>(null, /* millisecondsOverWhichToAverage */ 5000l);
TimePoint t1 = new MillisecondsTimePoint(1000);
TimePoint t2 = new MillisecondsTimePoint(2000);
TimePoint t3 = new MillisecondsTimePoint(3000);
GPSFixMoving f1 = new GPSFixMovingImpl(new DegreePosition(1./3600.*1./60., 0), t1, new KnotSpeedWithBearingImpl(1, new DegreeBearingImpl(0)));
GPSFixMoving f2 = new GPSFixMovingImpl(new DegreePosition(2./3600.*1./60., 0), t2, new KnotSpeedWithBearingImpl(150, new DegreeBearingImpl(0))); // outrageous speed; to be ignored by getEstimatedSpeed
GPSFixMoving f3 = new GPSFixMovingImpl(new DegreePosition(3./3600.*1./60., 0), t3, new KnotSpeedWithBearingImpl(1, new DegreeBearingImpl(0)));
track.addGPSFix(f1);
track.addGPSFix(f2);
track.addGPSFix(f3);
assertEquals(f2, track.getFirstFixAtOrAfter(f2.getTimePoint())); // expect the fix to still be valid, but only its provided speed shall be ignored
SpeedWithBearing average = track.getEstimatedSpeed(t2);
assertEquals(1, average.getKnots(), 0.001);
}
@Test
public void testBearingAveragingAcrossZeroDegreesWithGPSFixMoving() {
DynamicGPSFixMovingTrackImpl<Object> track = new DynamicGPSFixMovingTrackImpl<Object>(null, /* millisecondsOverWhichToAverage */ 5000);
@@ -352,7 +415,7 @@ public class TrackTest {
}
private TimePoint addMillisToTimepoint(TimePoint p, long millis) {
return new MillisecondsTimePoint(p.asMillis() + millis);
return p.plus(millis);
}
@Test
@@ -38,6 +38,7 @@ import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.GPSFix;
import com.sap.sailing.domain.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.LineLengthAndAdvantage;
import com.sap.sailing.domain.tracking.Maneuver;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.RaceChangeListener;
@@ -423,6 +424,12 @@ public class MockedTrackedRace implements DynamicTrackedRace {
// TODO Auto-generated method stub
return false;
}
@Override
public RegattaAndRaceIdentifier getRaceIdentifier(RaceDefinition race) {
// TODO Auto-generated method stub
return null;
}
};
}
@@ -858,4 +865,17 @@ public class MockedTrackedRace implements DynamicTrackedRace {
// TODO Auto-generated method stub
return null;
}
@Override
public LineLengthAndAdvantage getStartLine(TimePoint at) {
// TODO Auto-generated method stub
return null;
}
@Override
public LineLengthAndAdvantage getFinishLine(TimePoint at) {
// TODO Auto-generated method stub
return null;
}
}
@@ -29,6 +29,7 @@ import com.sap.sailing.domain.tracking.CourseDesignChangedListener;
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.LineLengthAndAdvantage;
import com.sap.sailing.domain.tracking.Maneuver;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.RaceChangeListener;
@@ -563,4 +564,16 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
return null;
}
@Override
public LineLengthAndAdvantage getStartLine(TimePoint at) {
// TODO Auto-generated method stub
return null;
}
@Override
public LineLengthAndAdvantage getFinishLine(TimePoint at) {
// TODO Auto-generated method stub
return null;
}
}
@@ -1,8 +1,6 @@
package com.sap.sailing.domain.tractracadapter.persistence;
import com.sap.sailing.domain.tractracadapter.TracTracConfiguration;
import com.sap.sailing.domain.tractracadapter.persistence.impl.DomainObjectFactoryImpl;
import com.sap.sailing.mongodb.MongoDBService;
/**
* Offers methods to load domain objects from a Mongo DB
@@ -11,8 +9,5 @@ import com.sap.sailing.mongodb.MongoDBService;
*
*/
public interface DomainObjectFactory {
DomainObjectFactory INSTANCE = new DomainObjectFactoryImpl(MongoDBService.INSTANCE.getDB());
Iterable<TracTracConfiguration> getTracTracConfigurations();
}
@@ -0,0 +1,11 @@
package com.sap.sailing.domain.tractracadapter.persistence;
import com.mongodb.DB;
import com.sap.sailing.domain.tractracadapter.DomainFactory;
import com.sap.sailing.domain.tractracadapter.persistence.impl.PersistenceFactoryImpl;
public interface PersistenceFactory {
PersistenceFactory INSTANCE = new PersistenceFactoryImpl();
DomainObjectFactory createDomainObjectFactory(DB db, DomainFactory tracTracDomainFactory);
}
@@ -18,9 +18,12 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
private final DB database;
public DomainObjectFactoryImpl(DB db) {
private final DomainFactory tracTracDomainFactory;
public DomainObjectFactoryImpl(DB db, DomainFactory tracTracDomainFactory) {
super();
this.database = db;
this.tracTracDomainFactory = tracTracDomainFactory;
}
@Override
@@ -50,7 +53,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
String tracTracUsername = tracTracUsernameObject == null ? "" : (String) tracTracUsernameObject;
String tracTracPassword = tracTracPasswordObject == null ? "" : (String) tracTracPasswordObject;
return DomainFactory.INSTANCE.createTracTracConfiguration((String) object.get(FieldNames.TT_CONFIG_NAME.name()),
return tracTracDomainFactory.createTracTracConfiguration((String) object.get(FieldNames.TT_CONFIG_NAME.name()),
(String) object.get(FieldNames.TT_CONFIG_JSON_URL.name()),
(String) object.get(FieldNames.TT_CONFIG_LIVE_DATA_URI.name()),
(String) object.get(FieldNames.TT_CONFIG_STORED_DATA_URI.name()),
@@ -0,0 +1,13 @@
package com.sap.sailing.domain.tractracadapter.persistence.impl;
import com.mongodb.DB;
import com.sap.sailing.domain.tractracadapter.DomainFactory;
import com.sap.sailing.domain.tractracadapter.persistence.DomainObjectFactory;
import com.sap.sailing.domain.tractracadapter.persistence.PersistenceFactory;
public class PersistenceFactoryImpl implements PersistenceFactory {
@Override
public DomainObjectFactory createDomainObjectFactory(DB db, DomainFactory tracTracDomainFactory) {
return new DomainObjectFactoryImpl(db, tracTracDomainFactory);
}
}
@@ -14,4 +14,6 @@ Export-Package: com.sap.sailing.domain.tractracadapter,
com.sap.sailing.domain.tractracadapter.impl;x-friends:="com.sap.sailing.domain.test,com.sap.sailing.domain.swisstimingadapter.test,com.sap.sailing.server.test"
Bundle-ActivationPolicy: lazy
Import-Package: org.json.simple,
org.json.simple.parser
org.json.simple.parser,
org.osgi.framework;version="1.7.0"
Bundle-Activator: com.sap.sailing.domain.tractracadapter.impl.Activator
@@ -20,7 +20,6 @@ import com.sap.sailing.domain.base.Person;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Sideline;
import com.sap.sailing.domain.base.Team;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.NauticalSide;
import com.sap.sailing.domain.common.TimePoint;
@@ -52,6 +51,11 @@ import com.tractrac.clientmodule.data.Position;
import difflib.PatchFailedException;
public interface DomainFactory {
/**
* A default domain factory for test purposes only. In a server environment, ensure NOT to use this. Use what can be
* reached from <code>RacingEventService.getBaseDomainFactory()</code> instead which should be the single instance
* used by all other services linked to the <code>RacingEventService</code>.
*/
static DomainFactory INSTANCE = new DomainFactoryImpl(com.sap.sailing.domain.base.DomainFactory.INSTANCE);
com.sap.sailing.domain.base.DomainFactory getBaseDomainFactory();
@@ -79,12 +83,6 @@ public interface DomainFactory {
Person getOrCreatePerson(String name, Nationality nationality, UUID id);
/**
* If a team called <code>name</code> already is known by this domain factory, it is returned. Otherwise, the team name
* is split along "+" signs with one {@link Person} object created for each part.
*/
Team getOrCreateTeam(String name, Nationality nationality, UUID competitorId);
/**
* Fetch a race definition previously created by a call to {@link #getOrCreateRaceDefinitionAndTrackedRace}. If no such
* race definition was created so far, the call blocks until such a definition is provided by a call to
@@ -0,0 +1,115 @@
package com.sap.sailing.domain.tractracadapter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.ParseException;
import java.util.List;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.tracking.RacesHandle;
import com.sap.sailing.domain.tracking.TrackerManager;
import com.sap.sailing.domain.tracking.WindStore;
public interface TracTracAdapter {
DomainFactory getTracTracDomainFactory();
/**
* If not already tracking the URL/URI/URI combination, adds a single race tracker and starts tracking the race,
* using the race's parameter URL which delivers the single configuration text file for that race. While the result
* of passing this URL to the TracTrac <code>KeyValue.setup</code> is a TracTrac <code>Event</code>, those events
* only manage a single race. In our domain model, we group those races into a single instance of our
* {@link Regatta} class.
* <p>
*
* If this is the first race of an event, the {@link Regatta} is created as well. If the {@link RaceDefinition} for
* the race already exists, it isn't created again. Also, if a {@link TracTracRaceTracker} for the given race
* already exists, it is not added again.
* <p>
*
* Note that when the race identified by <code>paramURL</code>, <code>liveURI</code> and <code>storedURI</code> is
* already being tracked, then regardless of the <code>windStore</code> selection the existing tracker will be used
* and its race handle will be returned. A log message will indicate a potential wind store mismatch (based on
* {@link WindStore#equals(Object)}).
*
* @param trackerManager
* the race will be added to that manager
* @param timeoutInMilliseconds
* if the race definition is not received for the race within this time, the race tracker for that race
* is stopped; use -1 to wait forever
*/
RacesHandle addTracTracRace(TrackerManager trackerManager, URL paramURL, URI liveURI, URI storedURI,
URI courseDesignUpdateURI, RaceLogStore raceLogStore, WindStore windStore, long timeoutInMilliseconds,
String tracTracUsername, String tracTracPassword) throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception;
/**
* Same as {@link #addTracTracRace(URL, URI, URI, WindStore, long)}, only that start and end of tracking are
* specified which may help reducing the amount of stored data (particularly mark positions) that needs to be
* loaded.
*
* @param trackerManager
* the race will be added to that manager
* @param regattaToAddTo
* if <code>null</code>, an existing regatta by the name of the TracTrac event with the boat class name
* appended in parentheses will be looked up; if not found, a default regatta with that name will be
* created, with a single default series and a single default fleet. If a valid {@link RegattaIdentifier}
* is specified, a regatta lookup is performed with that identifier; if the regatta is found, it is used
* to add the races to. Otherwise, a default regatta as described above will be created and used.
*/
RacesHandle addTracTracRace(TrackerManager trackerManager, RegattaIdentifier regattaToAddTo, URL paramURL, URI liveURI,
URI storedURI, URI courseDesignUpdateURI, TimePoint trackingStartTime,
TimePoint trackingEndTime, RaceLogStore raceLogStore, WindStore windStore,
long timeoutForReceivingRaceDefinitionInMilliseconds, boolean simulateWithStartTimeNow, String tracTracUsername, String tracTracPassword)
throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception;
/**
* Defines the regatta and for each race listed in the JSON document that is not already being tracked by this service
* creates a {@link TracTracRaceTracker} that starts tracking the respective race. The {@link RaceDefinition}s obtained this
* way are all grouped into the single {@link Regatta} produced for the event listed in the JSON response. Note that
* the many race trackers will have their TracTrac <code>Event</code> each, all with the same name, meaning the same
* event but being distinct.
* @param trackerManager TODO
* @param jsonURL
* URL of a JSON response that contains an "event" object telling the event's name and ID, as well as a
* JSON array named "races" which tells ID and replay URL for the race. From those replay URLs the
* paramURL for the Java client can be derived.
* @param timeoutInMilliseconds
* if a race definition is not received for a race of this event within this time, the race tracker for
* that race is stopped; use -1 to wait forever
* @param raceLogStore TODO
*/
Regatta addRegatta(TrackerManager trackerManager, URL jsonURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI, WindStore windStore,
long timeoutInMilliseconds, String tracTracUsername, String tracTracPassword, RaceLogStore raceLogStore)
throws MalformedURLException, FileNotFoundException, URISyntaxException, IOException, ParseException,
org.json.simple.parser.ParseException, Exception;
/**
* For the JSON URL of an account / event, lists the paramURLs that can be used for
* {@link #addTracTracRace(URL, URI, URI, WindStore, long)} calls to individually start tracking races of this
* event, rather than tracking <em>all</em> races in the event which is hardly ever useful. The returned pair's
* first component is the event name.
*
* @param loadClientParams
* shall the properties from the clientparams.php file such as liveURI and storedURI already be loaded?
* Generally, this is not necessary as the
* {@link #addTracTracRace(TrackerManager, RegattaIdentifier, URL, URI, URI, URI, TimePoint, TimePoint, RaceLogStore, WindStore, long, boolean, String, String)}
* and {@link #addTracTracRace(TrackerManager, URL, URI, URI, URI, RaceLogStore, WindStore, long, String, String)} will
* fetch the JSON and clientparams.php documents to work with up-to-date data.
*/
Pair<String, List<RaceRecord>> getTracTracRaceRecords(URL jsonURL, boolean loadClientParams) throws IOException,
ParseException, org.json.simple.parser.ParseException, URISyntaxException;
RaceRecord getSingleTracTracRaceRecord(URL jsonURL, String raceId, boolean loadClientParams) throws Exception;
TracTracConfiguration createTracTracConfiguration(String name, String jsonURL, String liveDataURI,
String storedDataURI, String courseDesignUpdateURI, String tracTracUsername, String tracTracPassword);
}
Loaded 100 of 372 files, more files were not shown because too many files have changed in this diff. Show more