diff --git a/.gitignore b/.gitignore
index 8bed8bde408..35a16d43c1d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,36 +1,37 @@
-/target
-www-test
-war
-build.log
-reporting.log
-.metadata
-*.class
-*.swp
-*~
-manifest.txt
-java/com.sap.sailing.gwt.ui/gwt-unitCache/*
-java/com.sap.sailing.gwt.ui/war/WEB-INF/deploy/*
-java/com.sap.sse.security.ui/WEB-INF/deploy/*
-sailing_debug0*
-dist
-build
-.DS_Store
-java/com.sap.sailing.simulator/bin/com/sap/sailing/simulator/Simulator.gwt.xml
-/mobile/com.sap.sailing.racecommittee.app/res/values/keys.xml
-java/com.sap.sailing.gwt.ui/SailingGWT Remote Profiling (Run-Mode only).launch
-java/com.sap.sailing.gwt.ui/SailingGWT.launch
-
-## Android Studio and Intellij and Android in general
-gen/
-.idea/
-*.ipr
-*.iws
-*.iml
-out/
-.gradle
-local.properties
-git.xml
-mobile/github-advrecyclerview_lib/bin
-captures
-/.recommenders/
-/java/.recommenders/
\ No newline at end of file
+/target
+www-test
+war
+build.log
+reporting.log
+.metadata
+*.class
+*.swp
+*~
+manifest.txt
+java/com.sap.sailing.gwt.ui/gwt-unitCache/*
+java/com.sap.sailing.gwt.ui/war/WEB-INF/deploy/*
+java/com.sap.sse.security.ui/WEB-INF/deploy/*
+sailing_debug0*
+dist
+build
+.DS_Store
+java/com.sap.sailing.simulator/bin/com/sap/sailing/simulator/Simulator.gwt.xml
+/mobile/com.sap.sailing.racecommittee.app/res/values/keys.xml
+java/com.sap.sailing.gwt.ui/SailingGWT Remote Profiling (Run-Mode only).launch
+java/com.sap.sailing.gwt.ui/SailingGWT.launch
+
+## Android Studio and Intellij and Android in general
+gen/
+.idea/
+*.ipr
+*.iws
+*.iml
+out/
+.gradle
+local.properties
+git.xml
+mobile/github-advrecyclerview_lib/bin
+captures
+/.recommenders/
+/java/.recommenders/
+java/com.sap.sailing.selenium.test/com.sap.sailing.selenium.test (No Proxy) individual.launch
diff --git a/configuration/buildAndUpdateProduct.sh b/configuration/buildAndUpdateProduct.sh
index 7555e3d4c12..80289012902 100755
--- a/configuration/buildAndUpdateProduct.sh
+++ b/configuration/buildAndUpdateProduct.sh
@@ -27,6 +27,15 @@ find_project_home ()
echo $(correct_file_path "$1")
}
+clean_gwt_artifacts ()
+{
+ cd $PROJECT_HOME/java
+ rm -rf com.sap.$PROJECT_TYPE.gwt.ui/com.sap.$PROJECT_TYPE.* com.sap.$PROJECT_TYPE.gwt.ui/.generated
+ rm -rf com.sap.sailing.dashboards.gwt/com.sap.sailing.dashboards.gwt.* com.sap.sailing.dashboards.gwt/.generated
+ rm -rf com.sap.sse.security.ui/com.sap.sse.security.ui.* com.sap.sse.security.ui/.generated
+ rm -rf com.sap.sse.gwt/com.sap.sse.gwt.* com.sap.sse.gwt/.generated
+}
+
# this holds for default installation
USER_HOME=~
START_DIR="`pwd`"
@@ -207,10 +216,7 @@ if [[ "$@" == "clean" ]]; then
if [[ $? != 0 ]]; then
exit 100
fi
- cd $PROJECT_HOME/java
- rm -rf com.sap.$PROJECT_TYPE.gwt.ui/com.sap.$PROJECT_TYPE.*
- rm -rf com.sap.sailing.dashboards.gwt/com.sap.sailing.dashboards.gwt.*
- rm -rf com.sap.sse.security.ui/com.sap.sse.security.ui.*
+ clean_gwt_artifacts
cd $PROJECT_HOME
echo "Using following command: mvn $extra -DargLine=\"$APP_PARAMETERS\" -fae -s $MAVEN_SETTINGS $clean"
echo "Maven version used: `mvn --version`"
@@ -559,27 +565,26 @@ if [[ "$@" == "build" ]] || [[ "$@" == "all" ]]; then
cd $PROJECT_HOME/java
if [ $gwtcompile -eq 1 ] && [[ "$clean" == "clean" ]]; then
echo "INFO: Compiling GWT (rm -rf com.sap.$PROJECT_TYPE.gwt.ui/com.sap.$PROJECT_TYPE.*)"
- rm -rf com.sap.$PROJECT_TYPE.gwt.ui/com.sap.$PROJECT_TYPE.*
- GWT_XML_FILES=`find . -name '*.gwt.xml'`
- if [ $onegwtpermutationonly -eq 1 ]; then
- echo "INFO: Patching .gwt.xml files such that only one GWT permutation needs to be compiled"
- for i in $GWT_XML_FILES; 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/AllPermutations/SinglePermutation/' >$i.sed
- mv $i.sed $i
- done
- else
- echo "INFO: Patching .gwt.xml files such that all GWT permutations are compiled"
- for i in $GWT_XML_FILES; do
- echo "INFO: Patching $i files such that all GWT permutations are compiled"
- cp $i $i.bak
- cat $i | sed -e 's/SinglePermutation/AllPermutations/' >$i.sed
- mv $i.sed $i
-
- done
- fi
-
+ clean_gwt_artifacts
+ GWT_XML_FILES=`find . -name '*.gwt.xml'`
+ if [ $onegwtpermutationonly -eq 1 ]; then
+ echo "INFO: Patching .gwt.xml files such that only one GWT permutation needs to be compiled"
+ for i in $GWT_XML_FILES; 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/AllPermutations/SinglePermutation/' >$i.sed
+ mv $i.sed $i
+ done
+ else
+ echo "INFO: Patching .gwt.xml files such that all GWT permutations are compiled"
+ for i in $GWT_XML_FILES; do
+ echo "INFO: Patching $i files such that all GWT permutations are compiled"
+ cp $i $i.bak
+ cat $i | sed -e 's/SinglePermutation/AllPermutations/' >$i.sed
+ mv $i.sed $i
+
+ done
+ fi
else
echo "INFO: GWT Compilation disabled"
extra="$extra -Pdebug.no-gwt-compile"
diff --git a/configuration/launchhudsonslave b/configuration/launchhudsonslave
index 7a70f7e4697..71dcf06e6e8 100755
--- a/configuration/launchhudsonslave
+++ b/configuration/launchhudsonslave
@@ -1,8 +1,8 @@
#!/bin/bash
AWS=/root/.local/bin/aws
REGION=eu-west-1
-#HUDSON_SLAVE_AMI_ID=ami-0812bc35c7464732e
-HUDSON_SLAVE_AMI_ID=ami-04956fe5a5293da6f
+#HUDSON_SLAVE_AMI_ID=ami-04956fe5a5293da6f
+HUDSON_SLAVE_AMI_ID=ami-0251a199f8d4a4729
echo Launching instance...
instanceid=`$AWS ec2 run-instances --image-id $HUDSON_SLAVE_AMI_ID --count 1 --instance-type c5d.4xlarge --key-name Axel --security-groups "Sailing Analytics App" --region $REGION --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=Hudson Ubuntu Slave}]' --instance-initiated-shutdown-behavior terminate | tee /tmp/slavelaunch.out | jq .Instances[0].InstanceId | sed -e 's/"//g'`
if [ "$instanceid" = "" ]; then
@@ -17,7 +17,7 @@ else
echo Instance $instanceid seems running now
private_ip=`$AWS ec2 describe-instances --region $REGION --instance-ids $instanceid | jq .Reservations[0].Instances[0].PrivateIpAddress | sed -e 's/"//g'`
echo Probing for SSH on private IP $private_ip
- while su - hudson -c "ssh -o ConnectTimeout=10,StrictHostKeyChecking=no hudson@$private_ip ls >/dev/null"; do
+ while su - hudson -c "ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no hudson@$private_ip ls >/dev/null"; do
echo SSH daemon not reachable yet. Trying again...
done
echo SSH daemon reached. State should be ready to connect to now.
diff --git a/configuration/launchhudsonslave-java11 b/configuration/launchhudsonslave-java11
index 6b02d18147f..1e06b77b531 100755
--- a/configuration/launchhudsonslave-java11
+++ b/configuration/launchhudsonslave-java11
@@ -16,7 +16,7 @@ else
echo Instance $instanceid seems running now
private_ip=`$AWS ec2 describe-instances --region $REGION --instance-ids $instanceid | jq .Reservations[0].Instances[0].PrivateIpAddress | sed -e 's/"//g'`
echo Probing for SSH on private IP $private_ip
- while su - hudson -c "ssh -o ConnectTimeout=10,StrictHostKeyChecking=no hudson@$private_ip ls >/dev/null"; do
+ while su - hudson -c "ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no hudson@$private_ip ls >/dev/null"; do
echo SSH daemon not reachable yet. Trying again...
done
echo SSH daemon reached. State should be ready to connect to now.
diff --git a/configuration/mail_forward.js b/configuration/mail_forward.js
deleted file mode 100644
index 6bc037db681..00000000000
--- a/configuration/mail_forward.js
+++ /dev/null
@@ -1,38 +0,0 @@
-// Taken from https://github.com/sirceljm/AWS-SES-simple-mail-forward/blob/master/mail_forward.js
-// Install as an AWS Lambda according to https://github.com/sirceljm/AWS-SES-simple-mail-forward
-// and configure the to_address environment variable as a JSON array of strings representing
-// the recipients' e-mail addresses. Subscribe the lambda to your SNS topic that receives the
-// notification messages about your inbound e-mail. Use mail_forward_policy.json for the IAM role's
-// security policy.
-var AWS = require("aws-sdk");
-var ses = new AWS.SES();
-
-exports.handler = (event, context, callback) => {
- let data = JSON.parse(event.Records[0].Sns.Message);
- let source = data.content;
-
- source = source.replace(/^DKIM-Signature/im, "X-Original-DKIM-Signature");
- // source = source.replace(/^From/im, "X-Original-From");
- // source = source.replace(/^Source/im, "X-Original-Source");
- // source = source.replace(/^Sender/im, "X-Original-Sender");
- // source = source.replace(/^Return-Path/im, "X-Original-Return-Path");
- source = source.replace(/^Domainkey-Signature/im, "X-Original-Domainkey-Signature");
- // source = `From: AWS SES Mail Forwarding <${data.mail.destination}>\n` + source;
-
- if (!process.env.to_address) {
- console.log("You need to supply to_address enviromnent variable");
- } else if (data.mail.destination == process.env.to_address) {
- console.log("MAIL SEND ABORTED! - Forwarded mail address and to_address are the same - THIS CAN CAUSE AN INFINITE LOOP!");
- } else {
- ses.sendRawEmail({
- RawMessage: {
- Data: source,
- },
- Destinations: JSON.parse(process.env.to_address),
- }, function (err, data) {
- if (err) {
- console.error(err, err.stack);
- }
- });
- }
-};
diff --git a/configuration/mail_forward.py b/configuration/mail_forward.py
new file mode 100644
index 00000000000..d986da6e81a
--- /dev/null
+++ b/configuration/mail_forward.py
@@ -0,0 +1,117 @@
+# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# This file is licensed under the Apache License, Version 2.0 (the "License").
+# You may not use this file except in compliance with the License. A copy of the
+# License is located at
+#
+# http://aws.amazon.com/apache2.0/
+#
+# This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
+# OF ANY KIND, either express or implied. See the License for the specific
+# language governing permissions and limitations under the License.
+
+import os
+import boto3
+import email
+import re
+import json
+from botocore.exceptions import ClientError
+from email.parser import Parser
+from email.policy import default
+
+region = os.environ['Region']
+
+def get_message_from_s3(bucket_name, object_key):
+ # Create a new S3 client.
+ client_s3 = boto3.client("s3")
+ # Get the email object from the S3 bucket.
+ object_s3 = client_s3.get_object(Bucket=bucket_name, Key=object_key)
+ # Read the content of the message.
+ file = object_s3['Body'].read()
+ return file
+
+def create_message(file):
+ senderMap = json.loads(os.environ["MailSenderJSON"])
+ recipientMap = json.loads(os.environ["MailRecipientsJSON"])
+ mailobject = email.message_from_string(file.decode('utf-8'))
+ # Define sender, either from 'To'-Header or from 'Cc'-Header
+ sender = None
+ # Check 'To'-Header
+ toHeader = Parser(policy=default).parsestr('To: ' + mailobject['To'])
+ for x in toHeader['to'].addresses:
+ if x.addr_spec in recipientMap:
+ recipientList = recipientMap.get(x.addr_spec)
+ sender = senderMap.get(x.addr_spec, x.addr_spec)
+ # Check 'Cc'-Header
+ if sender is None:
+ if mailobject['Cc']:
+ ccHeader = Parser(policy=default).parsestr('Cc: ' + mailobject['Cc'])
+ for x in ccHeader['cc'].addresses:
+ if x.addr_spec in recipientMap:
+ recipientList = recipientMap.get(x.addr_spec)
+ sender = senderMap.get(x.addr_spec, x.addr_spec)
+ # Bcc mail, there is no header so find address from 'Received'-Header
+ else:
+ receivedString = re.search(r'\<(.*?)\>', ''.join(mailobject.get_all('Received'))).group(1)
+ if receivedString in recipientMap:
+ recipientList = recipientMap.get(receivedString)
+ sender = senderMap.get(receivedString, receivedString)
+ # Change original headers to X-
+ mailobject['X-From'] = mailobject['From']
+ if not mailobject['Reply-To']:
+ mailobject['Reply-To'] = mailobject['From']
+ mailobject['X-To'] = mailobject['To']
+ mailobject['X-Return-Path'] = mailobject['Return-Path']
+ # Replace original headers and set to SES Value
+ del mailobject['DKIM-Signature']
+ mailobject.replace_header('From', sender)
+ mailobject.replace_header('Return-Path', sender)
+ mailobject.replace_header('To', ','.join(recipientList))
+ message = {
+ "Source": sender,
+ "Destinations": recipientList,
+ "Data" : mailobject.as_string()
+ }
+ return message
+
+def send_email(message):
+ # Create a new SES client.
+ client_ses = boto3.client('ses', region)
+ # Send the email.
+ try:
+ #Provide the contents of the email.
+ response = client_ses.send_raw_email(
+ Source=message['Source'],
+ Destinations=
+ message['Destinations']
+ ,
+ RawMessage={
+ 'Data':message['Data']
+ }
+ )
+ # Display an error if something goes wrong.
+ except ClientError as e:
+ output = e.response['Error']['Message']
+ else:
+ # Modify output message
+ if re.search(r'\<(.*?)\>', message['Source']):
+ fromString = ','.join(re.findall(r'\<(.*?)\>', message['Source']))
+ else:
+ fromString = message['Source']
+ output = "Email for: " + fromString + " forwarded to: " + ', '.join(message['Destinations']) + "! Message ID: " + response['MessageId']
+ return output
+
+def lambda_handler(event, context):
+ # Get the unique ID of the message. This corresponds to the name of the file in S3.
+ sns = event['Records'][0]['Sns']
+ message = json.loads(sns['Message'])
+ bucket_name = message['receipt']['action']['bucketName']
+ object_key = message['receipt']['action']['objectKey']
+ print(f"Received message with object key {object_key} in bucket {bucket_name}")
+ # Retrieve the file from the S3 bucket.
+ file = get_message_from_s3(bucket_name, object_key)
+ # Create the message.
+ message = create_message(file)
+ # Send the email and print the result.
+ result = send_email(message)
+ print(result)
diff --git a/configuration/mail_forward_policy.json b/configuration/mail_forward_policy.json
index e586059e9e8..18bfd2e3fa4 100644
--- a/configuration/mail_forward_policy.json
+++ b/configuration/mail_forward_policy.json
@@ -1,22 +1,27 @@
{
- "Version": "2012-10-17",
- "Statement": [
- {
- "Effect": "Allow",
- "Action": [
- "logs:CreateLogGroup",
- "logs:CreateLogStream",
- "logs:PutLogEvents"
- ],
- "Resource": "arn:aws:logs:*:*:*"
- },
- {
- "Effect": "Allow",
- "Action": [
- "ses:SendEmail",
- "ses:SendRawEmail"
- ],
- "Resource": "*"
- }
- ]
-}
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Sid": "VisualEditor0",
+ "Effect": "Allow",
+ "Action": [
+ "logs:CreateLogStream",
+ "logs:CreateLogGroup",
+ "logs:PutLogEvents"
+ ],
+ "Resource": "*"
+ },
+ {
+ "Sid": "VisualEditor1",
+ "Effect": "Allow",
+ "Action": [
+ "s3:GetObject",
+ "ses:SendRawEmail"
+ ],
+ "Resource": [
+ "arn:aws:s3:::emails-for-sapsailing-com/*",
+ "arn:aws:ses:eu-west-1:017363970217:identity/*"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docker/Dockerfile.tpl b/docker/Dockerfile.tpl
index 302a49a5548..deb26fe8027 100755
--- a/docker/Dockerfile.tpl
+++ b/docker/Dockerfile.tpl
@@ -1,4 +1,4 @@
-FROM donaldduck70/sapjvm8:8.1.059
+FROM donaldduck70/sapjvm8:8.1.062
LABEL maintainer=axel.uhl@sap.com
# Download and extract the release
WORKDIR /home/sailing/servers/server
diff --git a/docker/Dockerfile_sapjvm b/docker/Dockerfile_sapjvm
index 53b15227628..3b8a44845ad 100644
--- a/docker/Dockerfile_sapjvm
+++ b/docker/Dockerfile_sapjvm
@@ -6,7 +6,7 @@ ENV JAVA_HOME=/opt/sapjvm_8
WORKDIR /opt
RUN apt-get update \
&& apt-get install -y wget unzip \
- && curl --cookie eula_3_1_agreed=tools.hana.ondemand.com/developer-license-3_1.txt "https://tools.hana.ondemand.com/additional/sapjvm-8.1.059-linux-x64.zip" --output sapjvm8-linux-x64.zip \
+ && curl --cookie eula_3_1_agreed=tools.hana.ondemand.com/developer-license-3_1.txt "https://tools.hana.ondemand.com/additional/sapjvm-8.1.062-linux-x64.zip" --output sapjvm8-linux-x64.zip \
&& unzip sapjvm8-linux-x64.zip \
&& rm sapjvm8-linux-x64.zip \
&& echo "export JAVA_HOME=/opt/sapjvm_8; export PATH=\${PATH}:/opt/sapjvm_8/bin" > /etc/profile.d/javahome.sh
diff --git a/java/com.sap.sailing.dashboards.gwt/META-INF/MANIFEST.MF b/java/com.sap.sailing.dashboards.gwt/META-INF/MANIFEST.MF
index 83a4af85605..de6b585902a 100644
--- a/java/com.sap.sailing.dashboards.gwt/META-INF/MANIFEST.MF
+++ b/java/com.sap.sailing.dashboards.gwt/META-INF/MANIFEST.MF
@@ -12,6 +12,7 @@ Require-Bundle: com.sap.sailing.domain,
com.google.gwt.user;bundle-version="2.8.2";resolution:=optional,
com.google.gwt.servlet;bundle-version="2.8.2",
com.sap.sse.common,
+ com.sap.sse.debranding,
com.sap.sailing.domain.common,
com.sap.sailing.domain.shared.android,
com.sap.sailing.gwt.ui,
diff --git a/java/com.sap.sailing.dashboards.gwt/RibDashboard.html b/java/com.sap.sailing.dashboards.gwt/RibDashboard.html
index 3bea985d183..6c55c754bf9 100644
--- a/java/com.sap.sailing.dashboards.gwt/RibDashboard.html
+++ b/java/com.sap.sailing.dashboards.gwt/RibDashboard.html
@@ -7,44 +7,38 @@
-
-
-
-
-
-
-
-
-Dashboard
-
-
-
-
-
-
-
-
+
+
+
+
+
+ Dashboard
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
diff --git a/java/com.sap.sailing.dashboards.gwt/WEB-INF/web.xml b/java/com.sap.sailing.dashboards.gwt/WEB-INF/web.xml
index 32fcb29c829..48a75890c33 100644
--- a/java/com.sap.sailing.dashboards.gwt/WEB-INF/web.xml
+++ b/java/com.sap.sailing.dashboards.gwt/WEB-INF/web.xml
@@ -1,74 +1,83 @@
+ 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">
-
- default
- org.eclipse.jetty.servlet.DefaultServlet
-
-
- dirAllowed
- false
-
-
- useFileMappedBuffer
- false
-
-
+
+ default
+ org.eclipse.jetty.servlet.DefaultServlet
+
+
+ dirAllowed
+ false
+
+
+ useFileMappedBuffer
+ false
+
+
-
- Sailing Service
- SailingService
- com.sap.sailing.gwt.ui.server.SailingServiceImpl
-
-
- SailingService
- /service/sailing
-
+
+ Sailing Service
+ SailingService
+ com.sap.sailing.gwt.ui.server.SailingServiceImpl
+
+
+ SailingService
+ /service/sailing
+
-
- LocaleInjectionFilter
- com.sap.sse.security.ui.server.LocaleInjectionFilter
-
-
- LocaleInjectionFilter
- *.html
-
+
+ ClientConfigurationServlet
+ ClientConfigurationServlet
+ com.sap.sse.debranding.ClientConfigurationServlet
+
+
+ ClientConfigurationServlet
+ *.html
+
-
- GWTCacheControlServletFilter
- com.sap.sse.gwt.server.GWTCacheControlServletFilter
-
-
- GWTCacheControlServletFilter
- /*
-
-
-
- Dashboard Dispatch Service
- dashboardDispatch
- com.sap.sailing.dashboards.gwt.server.DashboardDispatchServlet
-
-
- dashboardDispatch
- /service/dispatch
-
-
-
-
- remoteLoggingServlet
- com.google.gwt.logging.server.RemoteLoggingServiceImpl
-
-
- remoteLoggingServlet
- /com.sap.sailing.dashboards.gwt.RibDashboard/remote_logging
-
+
+ LocaleInjectionFilter
+ com.sap.sse.security.ui.server.LocaleInjectionFilter
+
+
+ LocaleInjectionFilter
+ *.html
+
-
- manifest
- text/cache-manifest
-
+
+ GWTCacheControlServletFilter
+ com.sap.sse.gwt.server.GWTCacheControlServletFilter
+
+
+ GWTCacheControlServletFilter
+ /*
+
+
+ Dashboard Dispatch Service
+ dashboardDispatch
+ com.sap.sailing.dashboards.gwt.server.DashboardDispatchServlet
+
+
+ dashboardDispatch
+ /service/dispatch
+
+
+
+
+ remoteLoggingServlet
+ com.google.gwt.logging.server.RemoteLoggingServiceImpl
+
+
+ remoteLoggingServlet
+ /com.sap.sailing.dashboards.gwt.RibDashboard/remote_logging
+
+
+
+ manifest
+ text/cache-manifest
+
diff --git a/java/com.sap.sailing.dashboards.gwt/images/dashboardicon-whitelabeled.png b/java/com.sap.sailing.dashboards.gwt/images/dashboardicon-whitelabeled.png
new file mode 100644
index 00000000000..d109e11fc07
Binary files /dev/null and b/java/com.sap.sailing.dashboards.gwt/images/dashboardicon-whitelabeled.png differ
diff --git a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/header/DashboardHeader.java b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/header/DashboardHeader.java
index 3f641a2b5b3..55f0b9290da 100644
--- a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/header/DashboardHeader.java
+++ b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/header/DashboardHeader.java
@@ -12,6 +12,7 @@ import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.StringMessages;
+import com.sap.sse.gwt.shared.ClientConfiguration;
public class DashboardHeader extends Composite implements HasWidgets {
@@ -37,7 +38,9 @@ public class DashboardHeader extends Composite implements HasWidgets {
public DashboardHeader() {
dashboardHeaderResources.gss().ensureInjected();
initWidget(uiBinder.createAndBindUi(this));
- sapLogo.setResource(DashboardHeaderResources.INSTANCE.sapLogo());
+ if (ClientConfiguration.getInstance().isBrandingActive()) {
+ sapLogo.setResource(DashboardHeaderResources.INSTANCE.sapLogo());
+ }
event.setInnerHTML(StringMessages.INSTANCE.dashboardHeader());
}
diff --git a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/linker/DashboardManifestLinker.java b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/linker/DashboardManifestLinker.java
index 9ce145b54d3..6e2de373572 100644
--- a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/linker/DashboardManifestLinker.java
+++ b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/linker/DashboardManifestLinker.java
@@ -16,6 +16,7 @@ public class DashboardManifestLinker extends ManifestLinker {
return new String[] { "/dashboards/RibDashboard.html",
"/dashboards/RibDashboard.css",
"/dashboards/images/dashboardicon.png",
+ "/dashboards/images/dashboardicon-whitelabeled.png",
"/dashboards/js/jquery-3.3.1.min.js",
"/sailing-fontface-1.0.cache.css"};
}
diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java
index ba674d1f8e4..33938635f67 100644
--- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java
+++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java
@@ -1292,6 +1292,8 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
.get(FieldNames.REGATTA_USE_START_TIME_INFERENCE.name());
final Boolean controlTrackingFromStartAndFinishTimes = (Boolean) dbRegatta
.get(FieldNames.REGATTA_CONTROL_TRACKING_FROM_START_AND_FINISH_TIMES.name());
+ final Boolean autoRestartTrackingUponCompetitorSetChange = (Boolean) dbRegatta
+ .get(FieldNames.REGATTA_AUTO_RESTART_TRACKING_UPON_COMPETITOR_SET_CHANGE.name());
Boolean canBoatsOfCompetitorsChangePerRace = (Boolean) dbRegatta
.get(FieldNames.REGATTA_CAN_BOATS_OF_COMPETITORS_CHANGE_PER_RACE.name());
// for backward compatibility
@@ -1300,7 +1302,6 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
canBoatsOfCompetitorsChangePerRace = false;
createMigratableRegatta = true;
}
-
CompetitorRegistrationType competitorRegistrationType = CompetitorRegistrationType
.valueOfOrDefault((String) dbRegatta.get(FieldNames.REGATTA_COMPETITOR_REGISTRATION_TYPE.name()));
final RankingMetricConstructor rankingMetricConstructor = loadRankingMetricConstructor(dbRegatta);
@@ -1313,7 +1314,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
: buoyZoneRadiusInHullLengths,
useStartTimeInference == null ? true : useStartTimeInference,
controlTrackingFromStartAndFinishTimes == null ? false : controlTrackingFromStartAndFinishTimes,
- rankingMetricConstructor, new MongoObjectFactoryImpl(database), registrationLinkSecret);
+ autoRestartTrackingUponCompetitorSetChange, rankingMetricConstructor, new MongoObjectFactoryImpl(database), registrationLinkSecret);
} else {
result = new RegattaImpl(getRaceLogStore(), getRegattaLogStore(), name, boatClass,
canBoatsOfCompetitorsChangePerRace, competitorRegistrationType, startDate, endDate, series, /* persistent */true,
@@ -1322,7 +1323,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
: buoyZoneRadiusInHullLengths,
useStartTimeInference == null ? true : useStartTimeInference,
controlTrackingFromStartAndFinishTimes == null ? false : controlTrackingFromStartAndFinishTimes,
- rankingMetricConstructor, registrationLinkSecret);
+ autoRestartTrackingUponCompetitorSetChange == null ? false : autoRestartTrackingUponCompetitorSetChange, rankingMetricConstructor, registrationLinkSecret);
}
result.setRegattaConfiguration(configuration);
}
diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java
index 352a3c8cc9d..5467cb8de54 100644
--- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java
+++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java
@@ -38,6 +38,7 @@ public enum FieldNames {
REGATTA_BUOY_ZONE_RADIUS_IN_HULL_LENGTHS,
REGATTA_USE_START_TIME_INFERENCE,
REGATTA_CONTROL_TRACKING_FROM_START_AND_FINISH_TIMES,
+ REGATTA_AUTO_RESTART_TRACKING_UPON_COMPETITOR_SET_CHANGE,
REGATTA_CAN_BOATS_OF_COMPETITORS_CHANGE_PER_RACE,
REGATTA_COMPETITOR_REGISTRATION_TYPE,
REGATTA_REGISTRATION_LINK_SECRET,
diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MigratableRegattaImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MigratableRegattaImpl.java
index 9ed54fa3fc7..9b06d52e891 100644
--- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MigratableRegattaImpl.java
+++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MigratableRegattaImpl.java
@@ -32,12 +32,12 @@ public class MigratableRegattaImpl extends RegattaImpl implements MigratableRega
TimePoint startDate, TimePoint endDate,
Iterable series, boolean persistent, ScoringScheme scoringScheme, Serializable id, CourseArea courseArea,
Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference,
- boolean controlTrackingFromStartAndFinishTimes, RankingMetricConstructor rankingMetricConstructor,
- MongoObjectFactory mongoObjectFactory, String registrationLinkSecret) {
+ boolean controlTrackingFromStartAndFinishTimes, boolean autoRestartTrackingUponCompetitorSetChange,
+ RankingMetricConstructor rankingMetricConstructor, MongoObjectFactory mongoObjectFactory, String registrationLinkSecret) {
super(raceLogStore, regattaLogStore, name, boatClass, canBoatsOfCompetitorsChangePerRace, competitorRegistrationType, startDate, endDate, series,
persistent, scoringScheme, id, courseArea, buoyZoneRadiusInHullLengths, useStartTimeInference,
- controlTrackingFromStartAndFinishTimes, rankingMetricConstructor,
- registrationLinkSecret);
+ controlTrackingFromStartAndFinishTimes, autoRestartTrackingUponCompetitorSetChange,
+ rankingMetricConstructor, registrationLinkSecret);
this.mongoObjectFactory = mongoObjectFactory;
mongoObjectFactory.storeRegatta(this); // make sure the canBoatsOfCompetitorsChangePerRace flag makes it into the DB
}
diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java
index a1e67c42e72..1aabc2b38b0 100644
--- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java
+++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java
@@ -729,6 +729,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
dbRegatta.put(FieldNames.REGATTA_BUOY_ZONE_RADIUS_IN_HULL_LENGTHS.name(), regatta.getBuoyZoneRadiusInHullLengths());
dbRegatta.put(FieldNames.REGATTA_USE_START_TIME_INFERENCE.name(), regatta.useStartTimeInference());
dbRegatta.put(FieldNames.REGATTA_CONTROL_TRACKING_FROM_START_AND_FINISH_TIMES.name(), regatta.isControlTrackingFromStartAndFinishTimes());
+ dbRegatta.put(FieldNames.REGATTA_AUTO_RESTART_TRACKING_UPON_COMPETITOR_SET_CHANGE.name(), regatta.isAutoRestartTrackingUponCompetitorSetChange());
dbRegatta.put(FieldNames.REGATTA_CAN_BOATS_OF_COMPETITORS_CHANGE_PER_RACE.name(), regatta.canBoatsOfCompetitorsChangePerRace());
dbRegatta.put(FieldNames.REGATTA_COMPETITOR_REGISTRATION_TYPE.name(), regatta.getCompetitorRegistrationType().name());
dbRegatta.put(FieldNames.REGATTA_REGISTRATION_LINK_SECRET.name(), regatta.getRegistrationLinkSecret());
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java
index 89822fc7c60..7cf78cf058c 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java
@@ -103,7 +103,7 @@ public class CreateAndTrackWithRaceLogTest extends RaceLogTrackingTestHelper {
/* registrationLinkSecret */ null, /* startDate */null, /* endDate */null, UUID.randomUUID(),
Collections. singletonList(series), /* persistent */ true, new HighPoint(), UUID.randomUUID(),
/* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */true,
- /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new);
+ /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, OneDesignRankingMetric::new);
series.addRaceColumn(columnName, /* trackedRegattaRegistry */null);
leaderboard = service.addRegattaLeaderboard(regatta.getRegattaIdentifier(), "RegattaLeaderboard", new int[] {});
adapter = RaceLogTrackingAdapterFactory.INSTANCE.getAdapter(DomainFactory.INSTANCE);
diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogRaceTracker.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogRaceTracker.java
index 596e42a0509..f0280bd3dc5 100644
--- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogRaceTracker.java
+++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogRaceTracker.java
@@ -20,6 +20,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogCourseDesignChangedEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEndOfTrackingEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor;
import com.sap.sailing.domain.abstractlog.race.RaceLogRaceStatusEvent;
+import com.sap.sailing.domain.abstractlog.race.RaceLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartOfTrackingEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.LastPublishedCourseDesignFinder;
@@ -28,12 +29,18 @@ import com.sap.sailing.domain.abstractlog.race.impl.BaseRaceLogEventVisitor;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogEndOfTrackingEventImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogStartOfTrackingEventImpl;
import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogDenoteForTrackingEvent;
+import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogRegisterCompetitorEvent;
import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogStartTrackingEvent;
import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.RaceInformationFinder;
import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.RaceLogTrackingStateAnalyzer;
+import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.RegisteredCompetitorsAndBoatsAnalyzer;
+import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogRegisterCompetitorEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDefineMarkEvent;
+import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRegisterCompetitorEvent;
+import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRevokeEvent;
+import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogRegisterCompetitorEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.impl.BaseRegattaLogEventVisitor;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.BoatClass;
@@ -146,6 +153,26 @@ public class RaceLogRaceTracker extends AbstractRaceTrackerBaseImpl {
trackedRace.updateStartAndEndOfTracking(/* waitForGPSFixesToLoad */ false);
}
}
+
+ @Override
+ public void visit(RaceLogRevokeEvent event) {
+ if (event.getRevokedEventType().equals(RaceLogRegisterCompetitorEventImpl.class.getName())) {
+ try {
+ checkForChangeOfCompetitorSet();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ @Override
+ public void visit(RaceLogRegisterCompetitorEvent event) {
+ try {
+ checkForChangeOfCompetitorSet();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
};
visitors.put(log, visitor);
((RaceLog) log).addListener(visitor);
@@ -155,6 +182,26 @@ public class RaceLogRaceTracker extends AbstractRaceTrackerBaseImpl {
public void visit(RegattaLogDefineMarkEvent event) {
RaceLogRaceTracker.this.onDefineMarkEvent(event);
}
+
+ @Override
+ public void visit(RegattaLogRevokeEvent event) {
+ if (event.getRevokedEventType().equals(RegattaLogRegisterCompetitorEventImpl.class.getName())) {
+ try {
+ checkForChangeOfCompetitorSet();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ @Override
+ public void visit(RegattaLogRegisterCompetitorEvent event) {
+ try {
+ checkForChangeOfCompetitorSet();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
};
visitors.put(log, visitor);
((RegattaLog) log).addListener(visitor);
@@ -168,6 +215,13 @@ public class RaceLogRaceTracker extends AbstractRaceTrackerBaseImpl {
}
}
+ private void checkForChangeOfCompetitorSet() throws Exception {
+ final Map competitorsAndTheirBoats = new RegisteredCompetitorsAndBoatsAnalyzer(params.getRaceLog(), params.getRegattaLog()).analyze();
+ if (!Util.setEquals(competitorsAndTheirBoats.keySet(), getRace().getCompetitors())) {
+ trackedRegattaRegistry.updateRaceCompetitors(getRegatta(), getRace());
+ }
+ }
+
@Override
protected void onStop(boolean preemptive, boolean willBeRemoved) {
RaceLog raceLog = params.getRaceLog();
@@ -273,18 +327,18 @@ public class RaceLogRaceTracker extends AbstractRaceTrackerBaseImpl {
}
private void startTracking(RaceLogStartTrackingEvent event) {
- RaceLog raceLog = params.getRaceLog();
- RaceColumn raceColumn = params.getRaceColumn();
- Fleet fleet = params.getFleet();
- RaceLogDenoteForTrackingEvent denoteEvent = new RaceInformationFinder(raceLog).analyze();
+ final RaceLog raceLog = params.getRaceLog();
+ final RaceColumn raceColumn = params.getRaceColumn();
+ final Fleet fleet = params.getFleet();
+ final RaceLogDenoteForTrackingEvent denoteEvent = new RaceInformationFinder(raceLog).analyze();
final Pair trackingTimes = new TrackingTimesFinder(raceLog).analyze();
if (!trackedRegatta.getRegatta().isControlTrackingFromStartAndFinishTimes() &&
(trackingTimes == null || trackingTimes.getA() == null || trackingTimes.getA().getTimePoint() == null)) {
// the start of tracking interval is unset or set to null; enter "now" as start of tracking into the race log
raceLog.add(new RaceLogStartOfTrackingEventImpl(MillisecondsTimePoint.now(), raceLogEventAuthor, raceLog.getCurrentPassId()));
}
- BoatClass boatClass = denoteEvent.getBoatClass();
- String raceName = denoteEvent.getRaceName();
+ final BoatClass boatClass = denoteEvent.getBoatClass();
+ final String raceName = denoteEvent.getRaceName();
CourseBase courseBase = new LastPublishedCourseDesignFinder(raceLog, /* onlyCoursesWithValidWaypointList */ true).analyze();
if (courseBase == null) {
courseBase = new CourseDataImpl("Default course for " + raceName);
@@ -303,8 +357,8 @@ public class RaceLogRaceTracker extends AbstractRaceTrackerBaseImpl {
}
throw new RaceNotCreatedException(String.format("Race for racelog (%s) has already been created", raceLog));
}
- Map competitorsAndTheirBoats = raceColumn.getAllCompetitorsAndTheirBoats(params.getFleet());
- Serializable raceId = denoteEvent.getRaceId();
+ final Map competitorsAndTheirBoats = raceColumn.getAllCompetitorsAndTheirBoats(params.getFleet());
+ final Serializable raceId = denoteEvent.getRaceId();
try {
final RaceDefinition raceDef = raceTrackingHandler.createRaceDefinition(trackedRegatta.getRegatta(), raceName, course,
boatClass, competitorsAndTheirBoats, raceId);
diff --git a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/FinishedTimeFinderTest.java b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/FinishedTimeFinderTest.java
index 09f56c2cb77..b9f3abc2125 100644
--- a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/FinishedTimeFinderTest.java
+++ b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/FinishedTimeFinderTest.java
@@ -2,8 +2,10 @@ package com.sap.sailing.domain.racelog.analyzing.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
import static org.mockito.Mockito.when;
+import java.io.Serializable;
import java.util.Arrays;
import org.junit.Test;
@@ -12,6 +14,7 @@ import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogRaceStatusEvent;
+import com.sap.sailing.domain.abstractlog.race.RaceLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.FinishedTimeFinder;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sse.common.TimePoint;
@@ -43,10 +46,8 @@ public class FinishedTimeFinderTest extends PassAwareRaceLogAnalyzerTest, WithID {
*/
boolean isEmpty();
+ /**
+ * Callers that want to iterate over the collection returned need to use {@link #lockForRead()} and
+ * {@link #unlockAfterRead()} to avoid {@link ConcurrentModificationException}s.
+ */
Iterable getRawFixesDescending();
+ /**
+ * Callers that want to iterate over the collection returned need to use {@link #lockForRead()} and
+ * {@link #unlockAfterRead()} to avoid {@link ConcurrentModificationException}s.
+ */
Iterable getFixesDescending();
void addAllListeners(Iterable listeners);
@@ -103,14 +112,17 @@ extends Track, WithID {
EventT getEventById(Serializable id);
/**
- * Get a {@link NavigableSet} of unrevoked events regardless of the {@code pass}. Events are sorted by
- * their {@link TimePoint} and the oldest is returned first.
- * @return
+ * Get a {@link NavigableSet} of unrevoked events regardless of the {@code pass}. Events are sorted by their
+ * {@link TimePoint} and the oldest is returned first. Callers that want to iterate over the collection returned
+ * need to use {@link #lockForRead()} and {@link #unlockAfterRead()} to avoid
+ * {@link ConcurrentModificationException}s.
*/
NavigableSet getUnrevokedEvents();
/**
- * Get a {@link NavigableSet} of unrevoked events regardless of the {@code pass}.
+ * Get a {@link NavigableSet} of unrevoked events regardless of the {@code pass}. Callers that want to iterate over
+ * the collection returned need to use {@link #lockForRead()} and {@link #unlockAfterRead()} to avoid
+ * {@link ConcurrentModificationException}s.
*/
NavigableSet getUnrevokedEventsDescending();
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/BaseLogAnalyzer.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/BaseLogAnalyzer.java
index d6e33462b4a..d2b90eb52e2 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/BaseLogAnalyzer.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/BaseLogAnalyzer.java
@@ -30,6 +30,9 @@ public abstract class BaseLogAnalyzer
}
}
+ /**
+ * Always called under the {@link #getLog() log's} read lock
+ */
protected abstract ResultT performAnalysis();
protected Iterable getAllEvents() {
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/impl/AbstractLogImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/impl/AbstractLogImpl.java
index 5c16f3c9d1a..d9fc807d5c0 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/impl/AbstractLogImpl.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/impl/AbstractLogImpl.java
@@ -33,13 +33,13 @@ import com.sap.sse.util.impl.ArrayListNavigableSet;
/**
* {@link Track} implementation for {@link AbstractLogEvent}s.
- *
+ *
*
* {@link TrackImpl#getDummyFix(com.sap.sailing.domain.common.TimePoint)} is not overridden, see
* {@link RaceLogEventComparator} for sorting when interface methods like
* {@link Track#getFirstFixAfter(com.sap.sailing.domain.common.TimePoint)} are used.
*
- *
+ *
*/
public abstract class AbstractLogImpl, VisitorT>
extends TrackImpl implements AbstractLog {
@@ -52,7 +52,7 @@ extends TrackImpl implements AbstractLog {
* Clients can use the {@link #add(RaceLogEvent, UUID)} method
*/
private transient Map> eventsDeliveredToClient = new HashMap>();
-
+
private Map eventsById = new HashMap();
private final Serializable id;
@@ -67,7 +67,7 @@ extends TrackImpl implements AbstractLog {
/**
* Initializes a new {@link RaceLogImpl}.
- *
+ *
* @param nameForReadWriteLock
* name of lock.
*/
@@ -82,7 +82,7 @@ extends TrackImpl implements AbstractLog {
public Serializable getId() {
return this.id;
}
-
+
protected void onSuccessfulAdd(EventT event, boolean notifyListeners) {
revokeIfNecessary(event);
eventsById.put(event.getId(), event);
@@ -90,7 +90,7 @@ extends TrackImpl implements AbstractLog {
notifyListenersAboutReceive(event);
}
}
-
+
protected boolean add(EventT event, boolean notifyListeners) {
boolean isAdded = false;
lockForWrite();
@@ -107,7 +107,7 @@ extends TrackImpl implements AbstractLog {
}
return isAdded;
}
-
+
@Override
public boolean add(EventT event) {
return add(event, true);
@@ -117,7 +117,7 @@ extends TrackImpl implements AbstractLog {
public boolean load(EventT event) {
return add(event, false);
}
-
+
private void revokeIfNecessary(EventT newEvent) {
if (newEvent instanceof RevokeEvent) {
RevokeEvent> revokeEvent = (RevokeEvent>) newEvent;
@@ -134,7 +134,7 @@ extends TrackImpl implements AbstractLog {
}
}
}
-
+
private void checkIfSuccessfullyRevokes(RevokeEvent> revokeEvent) throws NotRevokableException {
lockForRead();
EventT revokedEvent;
@@ -166,7 +166,7 @@ extends TrackImpl implements AbstractLog {
add(event);
return getEventsToDeliver(clientId, event);
}
-
+
@Override
public Iterable getEventsToDeliver(UUID clientId) {
return getEventsToDeliver(clientId, null);
@@ -311,25 +311,15 @@ extends TrackImpl implements AbstractLog {
assertReadLock();
return eventsById.get(id);
}
-
+
@Override
public NavigableSet getUnrevokedEvents() {
- return new PartialNavigableSetView(super.getInternalFixes()) {
- @Override
- protected boolean isValid(EventT e) {
- return ! (e instanceof RevokeEvent) && ! revokedEventIds.contains(e.getId());
- }
- };
+ return new FilteredPartialNavigableSetView<>(super.getInternalFixes(), new RevokedValidator<>(revokedEventIds));
}
-
+
@Override
public NavigableSet getUnrevokedEventsDescending() {
- return new PartialNavigableSetView(super.getInternalFixes().descendingSet()) {
- @Override
- protected boolean isValid(EventT e) {
- return ! (e instanceof RevokeEvent) && ! revokedEventIds.contains(e.getId());
- }
- };
+ return new FilteredPartialNavigableSetView(super.getInternalFixes().descendingSet(), new RevokedValidator<>(revokedEventIds));
}
@Override
@@ -375,14 +365,14 @@ extends TrackImpl implements AbstractLog {
unlockAfterWrite();
}
}
-
+
@Override
public void revokeEvent(AbstractLogEventAuthor author, EventT toRevoke) throws NotRevokableException {
revokeEvent(author, toRevoke, null);
}
-
+
protected abstract EventT createRevokeEvent(AbstractLogEventAuthor author, EventT toRevoke, String reason);
-
+
@Override
public void revokeEvent(AbstractLogEventAuthor author, EventT toRevoke, String reason) throws NotRevokableException {
if (toRevoke == null) {
@@ -392,4 +382,43 @@ extends TrackImpl implements AbstractLog {
checkIfSuccessfullyRevokes((RevokeEvent>) revokeEvent);
add(revokeEvent);
}
+
+ @FunctionalInterface
+ public interface NavigableSetViewValidator {
+ boolean isValid(T item);
+ }
+
+ /**
+ * Considers an event valid if it is not a {@link RevokeEvent} and is not contained in the set of events passed to
+ * the constructor that have been revoked.
+ */
+ public static class RevokedValidator, VisitorT> implements NavigableSetViewValidator {
+ final Set revokedEventIds;
+
+ public RevokedValidator(Set revokedEventIds) {
+ this.revokedEventIds = revokedEventIds;
+ }
+
+ @Override
+ public boolean isValid(EventT item) {
+ return !(item instanceof RevokeEvent) && !revokedEventIds.contains(item.getId());
+ }
+ }
+
+ public static class FilteredPartialNavigableSetView extends PartialNavigableSetView {
+ private final NavigableSetViewValidator validator;
+
+ public FilteredPartialNavigableSetView(NavigableSet set, final NavigableSetViewValidator validator) {
+ super(set);
+ if (validator == null) {
+ throw new NullPointerException();
+ }
+ this.validator = validator;
+ }
+
+ @Override
+ protected boolean isValid(T t) {
+ return validator.isValid(t);
+ }
+ }
}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLog.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLog.java
index 84199147b1f..bd136ad5575 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLog.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLog.java
@@ -1,18 +1,33 @@
package com.sap.sailing.domain.abstractlog.race;
import com.sap.sailing.domain.abstractlog.AbstractLog;
+import com.sap.sailing.domain.abstractlog.impl.AbstractLogImpl;
import com.sap.sse.common.WithID;
/**
-* "Fix" validity is decided based on the {@link #getCurrentPassId() current pass}. The validity is not cached.
-*/
+ * "Fix" validity is decided based on the {@link #getCurrentPassId() current pass}. The validity is not cached.
+ */
public interface RaceLog extends AbstractLog, WithID {
public static final int DefaultPassId = 0;
-
+
+ class PassValidator implements AbstractLogImpl.NavigableSetViewValidator {
+ final int pass;
+
+ public PassValidator(int pass) {
+ this.pass = pass;
+ }
+
+ @Override
+ public boolean isValid(RaceLogEvent item) {
+ return item.getPassId() == pass;
+ }
+ }
+
/**
* Gets the current pass id.
- *
+ *
* @return the pass id.
*/
int getCurrentPassId();
+
}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogRaceStatusEvent.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogRaceStatusEvent.java
index 4e455a2ca7f..d7cd0a7f16c 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogRaceStatusEvent.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogRaceStatusEvent.java
@@ -1,12 +1,13 @@
package com.sap.sailing.domain.abstractlog.race;
+import com.sap.sailing.domain.abstractlog.Revokable;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
/**
* Events to indicate the new race status which is valid for all events
* arriving after this event (on the same pass).
*/
-public interface RaceLogRaceStatusEvent extends RaceLogEvent {
+public interface RaceLogRaceStatusEvent extends RaceLogEvent, Revokable {
RaceLogRaceStatus getNextStatus();
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/FinishedTimeFinder.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/FinishedTimeFinder.java
index e4a07981d29..07034e1211a 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/FinishedTimeFinder.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/FinishedTimeFinder.java
@@ -7,16 +7,32 @@ import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sse.common.TimePoint;
public class FinishedTimeFinder extends RaceLogAnalyzer {
-
public FinishedTimeFinder(RaceLog raceLog) {
super(raceLog);
}
+ public RaceLogRaceStatusEvent findFinishedEvent() {
+ log.lockForRead();
+ try {
+ for (RaceLogEvent event : getPassUnrevokedEventsDescending()) {
+ if (event instanceof RaceLogRaceStatusEvent) {
+ final RaceLogRaceStatusEvent statusEvent = (RaceLogRaceStatusEvent) event;
+ if (statusEvent.getNextStatus().equals(RaceLogRaceStatus.FINISHED)) {
+ return statusEvent;
+ }
+ }
+ }
+ return null;
+ } finally {
+ log.unlockAfterRead();
+ }
+ }
+
@Override
protected TimePoint performAnalysis() {
- for (RaceLogEvent event : getPassEventsDescending()) {
+ for (RaceLogEvent event : getPassUnrevokedEventsDescending()) {
if (event instanceof RaceLogRaceStatusEvent) {
- RaceLogRaceStatusEvent statusEvent = (RaceLogRaceStatusEvent) event;
+ final RaceLogRaceStatusEvent statusEvent = (RaceLogRaceStatusEvent) event;
if (statusEvent.getNextStatus().equals(RaceLogRaceStatus.FINISHED)) {
return statusEvent.getLogicalTimePoint();
}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/FinishingTimeFinder.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/FinishingTimeFinder.java
index 143f5f6f2a8..79f32482732 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/FinishingTimeFinder.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/FinishingTimeFinder.java
@@ -12,18 +12,33 @@ public class FinishingTimeFinder extends RaceLogAnalyzer {
super(raceLog);
}
+ public RaceLogRaceStatusEvent findFinishingEvent() {
+ log.lockForRead();
+ try {
+ for (RaceLogEvent event : getPassUnrevokedEventsDescending()) {
+ if (event instanceof RaceLogRaceStatusEvent) {
+ final RaceLogRaceStatusEvent statusEvent = (RaceLogRaceStatusEvent) event;
+ if (statusEvent.getNextStatus().equals(RaceLogRaceStatus.FINISHING)) {
+ return statusEvent;
+ }
+ }
+ }
+ return null;
+ } finally {
+ log.unlockAfterRead();
+ }
+ }
+
@Override
protected TimePoint performAnalysis() {
- for (RaceLogEvent event : getPassEventsDescending()) {
+ for (RaceLogEvent event : getPassUnrevokedEventsDescending()) {
if (event instanceof RaceLogRaceStatusEvent) {
- RaceLogRaceStatusEvent statusEvent = (RaceLogRaceStatusEvent) event;
+ final RaceLogRaceStatusEvent statusEvent = (RaceLogRaceStatusEvent) event;
if (statusEvent.getNextStatus().equals(RaceLogRaceStatus.FINISHING)) {
return statusEvent.getLogicalTimePoint();
}
}
}
-
return null;
}
-
}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/RaceLogAnalyzer.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/RaceLogAnalyzer.java
index aeaf179fb2d..3a737723c63 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/RaceLogAnalyzer.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/RaceLogAnalyzer.java
@@ -1,22 +1,54 @@
package com.sap.sailing.domain.abstractlog.race.analyzing.impl;
+import java.util.ConcurrentModificationException;
+
import com.sap.sailing.domain.abstractlog.BaseLogAnalyzer;
+import com.sap.sailing.domain.abstractlog.impl.AbstractLogImpl;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor;
+import com.sap.sailing.domain.tracking.Track;
public abstract class RaceLogAnalyzer extends BaseLogAnalyzer
- {
+ {
public RaceLogAnalyzer(RaceLog raceLog) {
super(raceLog);
}
+ /**
+ * Callers that want to iterate over the collection returned need to use {@link Track#lockForRead()} and
+ * {@link Track#unlockAfterRead()} on the underlying {@link #getLog() log} to avoid
+ * {@link ConcurrentModificationException}s.
+ */
protected Iterable getPassEvents() {
return log.getFixes();
}
-
+
+ /**
+ * Callers that want to iterate over the collection returned need to use {@link Track#lockForRead()} and
+ * {@link Track#unlockAfterRead()} on the underlying {@link #getLog() log} to avoid
+ * {@link ConcurrentModificationException}s.
+ */
protected Iterable getPassEventsDescending() {
return log.getFixesDescending();
}
+
+ /**
+ * Callers that want to iterate over the collection returned need to use {@link Track#lockForRead()} and
+ * {@link Track#unlockAfterRead()} on the underlying {@link #getLog() log} to avoid
+ * {@link ConcurrentModificationException}s.
+ */
+ protected Iterable getPassUnrevokedEvents() {
+ return new AbstractLogImpl.FilteredPartialNavigableSetView<>(log.getUnrevokedEvents(), new RaceLog.PassValidator(log.getCurrentPassId()));
+ }
+
+ /**
+ * Callers that want to iterate over the collection returned need to use {@link Track#lockForRead()} and
+ * {@link Track#unlockAfterRead()} on the underlying {@link #getLog() log} to avoid
+ * {@link ConcurrentModificationException}s.
+ */
+ protected Iterable getPassUnrevokedEventsDescending() {
+ return new AbstractLogImpl.FilteredPartialNavigableSetView<>(log.getUnrevokedEventsDescending(), new RaceLog.PassValidator(log.getCurrentPassId()));
+ }
}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/RaceStatusAnalyzer.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/RaceStatusAnalyzer.java
index ae4b65c7cf1..f025c5f35f6 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/RaceStatusAnalyzer.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/RaceStatusAnalyzer.java
@@ -59,7 +59,7 @@ public class RaceStatusAnalyzer extends RaceLogAnalyzer performAnalysis() {
ArrayListNavigableSet statusEvents = new ArrayListNavigableSet<>(
RaceLogRaceStatusEventComparator.INSTANCE);
- for (RaceLogEvent event : getPassEvents()) {
+ for (RaceLogEvent event : getPassUnrevokedEvents()) {
if (event instanceof RaceLogRaceStatusEvent) {
statusEvents.add((RaceLogRaceStatusEvent) event);
}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogImpl.java
index 290bbea15e9..140f89cd242 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogImpl.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogImpl.java
@@ -9,22 +9,21 @@ import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor;
import com.sap.sailing.domain.tracking.Track;
-import com.sap.sailing.domain.tracking.impl.PartialNavigableSetView;
import com.sap.sailing.domain.tracking.impl.TrackImpl;
/**
* {@link Track} implementation for {@link RaceLogEvent}s.
- *
+ *
*
* "Fix" validity is decided based on the {@link #getCurrentPassId() current pass}. The validity is not cached.
*
- *
+ *
*
* {@link TrackImpl#getDummyFix(com.sap.sailing.domain.common.TimePoint)} is not overridden, see
* {@link RaceLogEventComparator} for sorting when interface methods like
* {@link Track#getFirstFixAfter(com.sap.sailing.domain.common.TimePoint)} are used.
*
- *
+ *
*/
public class RaceLogImpl extends AbstractLogImpl implements RaceLog {
private static final long serialVersionUID = 98032278604708475L;
@@ -39,19 +38,19 @@ public class RaceLogImpl extends AbstractLogImpl getInternalFixes() {
- return new PartialNavigableSetView(super.getInternalFixes()) {
- @Override
- protected boolean isValid(RaceLogEvent e) {
- return e.getPassId() == getCurrentPassId();
- }
- };
+ return new FilteredPartialNavigableSetView<>(super.getInternalFixes(), new PassValidator(getCurrentPassId()));
}
}
diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/RegattaLogDeviceMappingFinder.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/RegattaLogDeviceMappingFinder.java
index 4a7b9da2d40..22364e72637 100644
--- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/RegattaLogDeviceMappingFinder.java
+++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/regatta/tracking/analyzing/impl/RegattaLogDeviceMappingFinder.java
@@ -77,16 +77,22 @@ public class RegattaLogDeviceMappingFinder
protected void findUnrevokedMappingAndClosingEvents(Map>> events,
Map closingEvents) {
- for (RegattaLogEvent e : getLog().getUnrevokedEvents()) {
- if (e instanceof RegattaLogDeviceMappingEvent && isValidMapping(((RegattaLogDeviceMappingEvent>) e))) {
- @SuppressWarnings("unchecked")
- RegattaLogDeviceMappingEvent mappingEvent = (RegattaLogDeviceMappingEvent) e;
- getMappingEventsForItem(events, mappingEvent.getMappedTo()).add(mappingEvent);
- } else if (e instanceof RegattaLogCloseOpenEndedDeviceMappingEvent) {
- //a higher priority closing events for the same mapping event overwrites the lower priority one
- RegattaLogCloseOpenEndedDeviceMappingEvent closingEvent = (RegattaLogCloseOpenEndedDeviceMappingEvent) e;
- closingEvents.put(closingEvent.getDeviceMappingEventId(), closingEvent);
+ final RegattaLog regattaLog = getLog();
+ regattaLog.lockForRead();
+ try {
+ for (RegattaLogEvent e : regattaLog.getUnrevokedEvents()) {
+ if (e instanceof RegattaLogDeviceMappingEvent && isValidMapping(((RegattaLogDeviceMappingEvent>) e))) {
+ @SuppressWarnings("unchecked")
+ RegattaLogDeviceMappingEvent mappingEvent = (RegattaLogDeviceMappingEvent) e;
+ getMappingEventsForItem(events, mappingEvent.getMappedTo()).add(mappingEvent);
+ } else if (e instanceof RegattaLogCloseOpenEndedDeviceMappingEvent) {
+ //a higher priority closing events for the same mapping event overwrites the lower priority one
+ RegattaLogCloseOpenEndedDeviceMappingEvent closingEvent = (RegattaLogCloseOpenEndedDeviceMappingEvent) e;
+ closingEvents.put(closingEvent.getDeviceMappingEventId(), closingEvent);
+ }
}
+ } finally {
+ regattaLog.unlockAfterRead();
}
}
diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/DomainFactory.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/DomainFactory.java
index 85dbc373119..a49609626b5 100755
--- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/DomainFactory.java
+++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/DomainFactory.java
@@ -92,4 +92,6 @@ public interface DomainFactory {
*/
void addUpdateHandlers(String updateURL, String username, String password, Serializable eventId,
RaceDefinition raceDefinition, DynamicTrackedRace trackedRace) throws URISyntaxException;
+
+ Map createCompetitorsAndBoats(StartList startList, String raceId, BoatClass boatClass, RaceTrackingHandler raceTrackHandler);
}
diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/DomainFactoryImpl.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/DomainFactoryImpl.java
index 20229dd6b72..8f84c702e45 100755
--- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/DomainFactoryImpl.java
+++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/DomainFactoryImpl.java
@@ -291,7 +291,8 @@ public class DomainFactoryImpl implements DomainFactory {
return result;
}
- private Map createCompetitorsAndBoats(StartList startList, String raceId, BoatClass boatClass,
+ @Override
+ public Map createCompetitorsAndBoats(StartList startList, String raceId, BoatClass boatClass,
RaceTrackingHandler raceTrackHandler) {
Map result = new LinkedHashMap<>();
for (com.sap.sailing.domain.swisstimingadapter.Competitor swissTimingCompetitor : startList.getCompetitors()) {
diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingRaceTrackerImpl.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingRaceTrackerImpl.java
index 94289ae02a9..6d61ce4a071 100644
--- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingRaceTrackerImpl.java
+++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingRaceTrackerImpl.java
@@ -18,6 +18,7 @@ import java.util.UUID;
import java.util.logging.Level;
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.CompetitorAndBoatStore;
@@ -65,6 +66,7 @@ import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.domain.tracking.TrackingDataLoader;
import com.sap.sailing.domain.tracking.WindStore;
import com.sap.sailing.domain.tracking.WindTrack;
+import com.sap.sailing.domain.tracking.RaceTrackingHandler.DefaultRaceTrackingHandler;
import com.sap.sailing.domain.tracking.impl.TrackedRaceStatusImpl;
import com.sap.sailing.domain.tracking.impl.TrackingConnectorInfoImpl;
import com.sap.sailing.domain.tracking.impl.UpdateHandler;
@@ -443,6 +445,15 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl
this.startList = startList;
if (oldStartList == null && course != null) {
createRaceDefinition(course);
+ } else if (trackedRace != null) {
+ final Map competitorsAndTheirBoats = domainFactory.createCompetitorsAndBoats(startList, raceID, boatClass, new DefaultRaceTrackingHandler());
+ if (!Util.setEquals(competitorsAndTheirBoats.keySet(), getRace().getCompetitors())) {
+ try {
+ trackedRegattaRegistry.updateRaceCompetitors(getRegatta(), getRace());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
}
}
}
@@ -470,7 +481,7 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl
assert startList != null;
assert course != null;
// now we can create the RaceDefinition and most other things
- Race swissTimingRace = new RaceImpl(raceID, raceName, raceDescription, boatClass);
+ final Race swissTimingRace = new RaceImpl(raceID, raceName, raceDescription, boatClass);
try {
synchronized (this) {
race = domainFactory.createRaceDefinition(regatta, swissTimingRace, startList, course,
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/ranking/TestFarthestAheadWithSkippedMarkPassing.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/ranking/TestFarthestAheadWithSkippedMarkPassing.java
index 781b78d1b11..8c69d95c3cd 100755
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/ranking/TestFarthestAheadWithSkippedMarkPassing.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/ranking/TestFarthestAheadWithSkippedMarkPassing.java
@@ -102,8 +102,8 @@ public class TestFarthestAheadWithSkippedMarkPassing {
/* canBoatsOfCompetitorsChangePerRace */ true, CompetitorRegistrationType.CLOSED,
/*startDate*/ null, /*endDate*/ null, /* trackedRegattaRegistry */ null,
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), "123", /* courseArea */ null,
- /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new,
- /* registrationLinkSecret */ UUID.randomUUID().toString());
+ /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false,
+ OneDesignRankingMetric::new, /* registrationLinkSecret */ UUID.randomUUID().toString());
TrackedRegatta trackedRegatta = new DynamicTrackedRegattaImpl(regatta);
List waypoints = new ArrayList();
// create a two-lap upwind/downwind course:
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/ranking/TestSimpleTimeOnTimeRankingWithOneUpwindLeg.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/ranking/TestSimpleTimeOnTimeRankingWithOneUpwindLeg.java
index 96fb14346e8..989e2e72836 100755
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/ranking/TestSimpleTimeOnTimeRankingWithOneUpwindLeg.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/ranking/TestSimpleTimeOnTimeRankingWithOneUpwindLeg.java
@@ -83,8 +83,8 @@ public class TestSimpleTimeOnTimeRankingWithOneUpwindLeg {
RegattaImpl.getDefaultName("Test Regatta", boatClass.getName()), boatClass,
/* canBoatsOfCompetitorsChangePerRace */ true, CompetitorRegistrationType.CLOSED, /*startDate*/ null, /*endDate*/ null, /* trackedRegattaRegistry */ null,
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), "123", /* courseArea */ null,
- /* controlTrackingFromStartAndFinishTimes */ false, TimeOnTimeAndDistanceRankingMetric::new,
- /* registrationLinkSecret */ UUID.randomUUID().toString());
+ /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false,
+ TimeOnTimeAndDistanceRankingMetric::new, /* registrationLinkSecret */ UUID.randomUUID().toString());
TrackedRegatta trackedRegatta = new DynamicTrackedRegattaImpl(regatta);
List waypoints = new ArrayList();
// create a two-lap upwind/downwind course:
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DummyTrackedRegattaRegistry.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DummyTrackedRegattaRegistry.java
index cdc1c355622..bfdfa2e6fc0 100644
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DummyTrackedRegattaRegistry.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DummyTrackedRegattaRegistry.java
@@ -9,6 +9,7 @@ import java.util.Map;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
+import com.sap.sailing.domain.tracking.RaceHandle;
import com.sap.sailing.domain.tracking.RaceTracker;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
@@ -72,4 +73,9 @@ public class DummyTrackedRegattaRegistry implements TrackedRegattaRegistry {
public Regatta getOrCreateDefaultRegatta(String name, String boatClassName, Serializable id) {
return null;
}
+
+ @Override
+ public RaceHandle updateRaceCompetitors(Regatta regatta, RaceDefinition race) throws Exception {
+ return null;
+ }
}
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardOfflineTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardOfflineTest.java
index e43f4d80eb9..329e61de846 100644
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardOfflineTest.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LeaderboardOfflineTest.java
@@ -40,6 +40,7 @@ import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRul
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithFixedRank;
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithFixedRankAndManyCompetitors;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
+import com.sap.sailing.domain.tracking.RaceHandle;
import com.sap.sailing.domain.tracking.RaceTracker;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
@@ -159,6 +160,10 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
public Regatta getOrCreateDefaultRegatta(String name, String boatClassName, Serializable id) {
return null;
}
+ @Override
+ public RaceHandle updateRaceCompetitors(Regatta regatta, RaceDefinition race) throws Exception {
+ return null;
+ }
};
LeaderboardDTO leaderboardDTO = leaderboard.getLeaderboardDTO(now, emptySet, /* addOverallDetails */ true, trackedRegattaRegistry, DomainFactory.INSTANCE, /* fillTotalPointsUncorrected */ false);
assertNotNull(leaderboardDTO);
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TestDeadlockInRegattaListener.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TestDeadlockInRegattaListener.java
index f5bf2e0c4c7..902b6a9c411 100644
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TestDeadlockInRegattaListener.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TestDeadlockInRegattaListener.java
@@ -55,7 +55,7 @@ public class TestDeadlockInRegattaListener {
/* startDate */ null, /* endDate */ null,
/* trackedRegattaRegistry */ null, new LowPoint(), UUID.randomUUID(),
/* courseArea */ null, /* controlTrackingFromStartAndFinishTimes */ true,
- OneDesignRankingMetric::new, /* registrationLinkSecret */ UUID.randomUUID().toString());
+ /* autoRestartTrackingUponCompetitorSetChange */ false, OneDesignRankingMetric::new, /* registrationLinkSecret */ UUID.randomUUID().toString());
DynamicTrackedRegatta trackedRegatta = new DynamicTrackedRegattaImpl(regatta) {
private static final long serialVersionUID = -3599667964201700780L;
diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java
index ff855818226..b0757e95608 100755
--- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java
+++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java
@@ -618,6 +618,16 @@ public class MockedTrackedRace implements DynamicTrackedRace {
@Override
public void setCompetitorRegistrationType(CompetitorRegistrationType competitorRegistrationType) {
}
+
+ @Override
+ public boolean isAutoRestartTrackingUponCompetitorSetChange() {
+ return false;
+ }
+
+ @Override
+ public void setAutoRestartTrackingUponCompetitorSetChange(
+ boolean autoRestartTrackingUponCompetitorSetChange) {
+ }
};
}
diff --git a/java/com.sap.sailing.domain.tractracadapter.persistence/src/com/sap/sailing/domain/tractracadapter/persistence/impl/TracTracConnectivityParamsHandler.java b/java/com.sap.sailing.domain.tractracadapter.persistence/src/com/sap/sailing/domain/tractracadapter/persistence/impl/TracTracConnectivityParamsHandler.java
index 1e28e51757e..7306248e547 100644
--- a/java/com.sap.sailing.domain.tractracadapter.persistence/src/com/sap/sailing/domain/tractracadapter/persistence/impl/TracTracConnectivityParamsHandler.java
+++ b/java/com.sap.sailing.domain.tractracadapter.persistence/src/com/sap/sailing/domain/tractracadapter/persistence/impl/TracTracConnectivityParamsHandler.java
@@ -1,16 +1,13 @@
package com.sap.sailing.domain.tractracadapter.persistence.impl;
import java.io.IOException;
-import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URI;
+import java.net.URISyntaxException;
import java.net.URL;
-import java.net.URLConnection;
import java.util.HashMap;
import java.util.Map;
-import org.json.simple.JSONObject;
-import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import com.sap.sailing.domain.racelog.RaceLogStore;
@@ -28,7 +25,8 @@ import com.sap.sse.common.impl.MillisecondsDurationImpl;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.security.SecurityService;
import com.sap.sse.security.SessionUtils;
-import com.sap.sse.util.HttpUrlConnectionHelper;
+import com.tractrac.model.lib.api.event.CreateModelException;
+import com.tractrac.util.lib.api.exceptions.TimeOutException;
/**
* Handles mapping TracTrac connectivity parameters from and to a map with {@link String} keys. The
@@ -144,14 +142,8 @@ public class TracTracConnectivityParamsHandler extends AbstractRaceTrackingConne
}
private void updatePersistentTracTracConfiguration(RaceTrackingConnectivityParametersImpl params)
- throws MalformedURLException, IOException, ParseException {
- final String EVENT_MANAGER_HOSTNAME_PREFIX = "em.";
- final URL paramsJsonUrl = new URL(params.getParamURL().getProtocol(),
- params.getParamURL().getHost().startsWith(EVENT_MANAGER_HOSTNAME_PREFIX) ? params.getParamURL().getHost() : EVENT_MANAGER_HOSTNAME_PREFIX+params.getParamURL().getHost(),
- "/events/"+params.getTractracRace().getEvent().getId().toString()+"/races/"+params.getTractracRace().getId().toString()+".json");
- final URLConnection conn = HttpUrlConnectionHelper.redirectConnection(paramsJsonUrl);
- final JSONObject paramsJson = (JSONObject) new JSONParser().parse(new InputStreamReader(conn.getInputStream()));
- final String jsonURL = (String) paramsJson.get("eventJSON");
+ throws MalformedURLException, IOException, ParseException, CreateModelException, URISyntaxException, TimeOutException {
+ final String jsonURL = params.getTractracRace().getParameterSet().getParameter("eventJSON");
final String creatorName = SessionUtils.getPrincipal().toString();
final TracTracConfigurationImpl tracTracConfiguration = new TracTracConfigurationImpl(creatorName, params.getTractracRace().getEvent().getName(), jsonURL,
/* live URI */ null, /* stored URI */ null, // we mainly want to enable the user to list the event's races again in case they are removed; live/stored stuff comes from the tracking params
diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java
index 1ad3b41a51c..67db80c8ba2 100755
--- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java
+++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/DomainFactory.java
@@ -65,6 +65,7 @@ import com.tractrac.model.lib.api.route.IControlPoint;
import com.tractrac.subscription.lib.api.IEventSubscriber;
import com.tractrac.subscription.lib.api.IRaceSubscriber;
import com.tractrac.subscription.lib.api.SubscriberInitializationException;
+import com.tractrac.util.lib.api.exceptions.TimeOutException;
import difflib.PatchFailedException;
@@ -152,8 +153,8 @@ public interface DomainFactory {
WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogAndTrackedRaceResolver raceLogResolver,
LeaderboardGroupResolver leaderboardGroupResolver,
RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds,
- RaceTrackingHandler raceTrackingHandler)
- throws URISyntaxException, SubscriberInitializationException, IOException, InterruptedException;
+ RaceTrackingHandler raceTrackingHandler) throws URISyntaxException, SubscriberInitializationException,
+ IOException, InterruptedException, CreateModelException, TimeOutException;
/**
* Same as {@link #createRaceTracker(URL, URI, URI, URI, TimePoint, TimePoint, WindStore, TrackedRegattaRegistry)},
@@ -165,7 +166,7 @@ public interface DomainFactory {
RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds,
RaceTrackingHandler raceTrackingHandler)
throws MalformedURLException, FileNotFoundException, URISyntaxException, CreateModelException,
- SubscriberInitializationException, IOException, InterruptedException;
+ SubscriberInitializationException, IOException, InterruptedException, TimeOutException;
BoatClass getOrCreateBoatClass(String competitorClassName);
diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java
index 597b7fc3a80..63c90d7fbcc 100755
--- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java
+++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java
@@ -116,6 +116,7 @@ import com.tractrac.model.lib.api.route.IControlPoint;
import com.tractrac.subscription.lib.api.IEventSubscriber;
import com.tractrac.subscription.lib.api.IRaceSubscriber;
import com.tractrac.subscription.lib.api.SubscriberInitializationException;
+import com.tractrac.util.lib.api.exceptions.TimeOutException;
import difflib.PatchFailedException;
@@ -497,8 +498,8 @@ public class DomainFactoryImpl implements DomainFactory {
trackedRegattaRegistry,
// use the low-point system as the default scoring scheme
getBaseDomainFactory().createScoringScheme(ScoringSchemeType.LOW_POINT), race.getId(), null,
- /* controlTrackingFromStartAndFinishTimes */ false, rankingMetricConstructor,
- /* registrationLinkSecret */ UUID.randomUUID().toString());
+ /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false,
+ rankingMetricConstructor, /* registrationLinkSecret */ UUID.randomUUID().toString());
regattaCache.put(key, result);
weakDefaultRegattaCache.put(race, result);
logger.info("Created regatta "+result.getName()+" ("+result.hashCode()+") because none found for key "+key);
@@ -645,7 +646,8 @@ public class DomainFactoryImpl implements DomainFactory {
try {
raceDefinition = raceTrackingHandler.createRaceDefinition(trackedRegatta.getRegatta(), raceName, course, boatClass, competitorsAndBoats, raceId);
} catch (RuntimeException exception) {
- final String reasonForNotAddingRaceToRegatta = "Error while creating race " + raceDefinition + " for regatta " + trackedRegatta.getRegatta();
+ final String reasonForNotAddingRaceToRegatta = "Error while creating race " + raceDefinition
+ + " for regatta " + trackedRegatta.getRegatta() + ": " + exception.getMessage();
errorWhileTryingToTrackRace(trackedRegatta, raceDefinitionSetToUpdate, raceDefinition, reasonForNotAddingRaceToRegatta);
}
} else {
@@ -1029,7 +1031,7 @@ public class DomainFactoryImpl implements DomainFactory {
LeaderboardGroupResolver leaderboardGroupResolver,
RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds,
RaceTrackingHandler raceTrackingHandler)
- throws URISyntaxException, SubscriberInitializationException, IOException, InterruptedException {
+ throws URISyntaxException, SubscriberInitializationException, IOException, InterruptedException, CreateModelException, TimeOutException {
return new TracTracRaceTrackerImpl(this, raceLogStore, regattaLogStore, windStore, trackedRegattaRegistry,
raceLogResolver, leaderboardGroupResolver, connectivityParams, timeoutInMilliseconds, raceTrackingHandler);
}
@@ -1040,7 +1042,7 @@ public class DomainFactoryImpl implements DomainFactory {
LeaderboardGroupResolver leaderboardGroupResolver,
RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds,
RaceTrackingHandler raceTrackingHandler) throws URISyntaxException, CreateModelException,
- SubscriberInitializationException, IOException, InterruptedException {
+ SubscriberInitializationException, IOException, InterruptedException, TimeOutException {
return new TracTracRaceTrackerImpl(regatta, this, raceLogStore, regattaLogStore, windStore, trackedRegattaRegistry,
raceLogResolver, leaderboardGroupResolver, connectivityParams, timeoutInMilliseconds, raceTrackingHandler);
}
diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceTrackingConnectivityParametersImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceTrackingConnectivityParametersImpl.java
index 6e1257a58df..8307fb03296 100755
--- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceTrackingConnectivityParametersImpl.java
+++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceTrackingConnectivityParametersImpl.java
@@ -25,6 +25,7 @@ import com.tractrac.model.lib.api.ModelLocator;
import com.tractrac.model.lib.api.event.CreateModelException;
import com.tractrac.model.lib.api.event.IRace;
import com.tractrac.subscription.lib.api.SubscriberInitializationException;
+import com.tractrac.util.lib.api.exceptions.TimeOutException;
public class RaceTrackingConnectivityParametersImpl extends AbstractRaceTrackingConnectivityParameters {
@@ -49,7 +50,6 @@ public class RaceTrackingConnectivityParametersImpl extends AbstractRaceTracking
private final String raceVisibility;
private final boolean useInternalMarkPassingAlgorithm;
private final boolean preferReplayIfAvailable;
- private final transient IRace tractracRace;
private final int timeoutInMillis;
private final boolean useOfficialEventsToUpdateRaceLog;
@@ -78,11 +78,7 @@ public class RaceTrackingConnectivityParametersImpl extends AbstractRaceTracking
this.useOfficialEventsToUpdateRaceLog = useOfficialEventsToUpdateRaceLog;
this.paramURL = paramURL;
this.timeoutInMillis = timeoutInMillis;
- if (timeoutInMillis == -1) {
- this.tractracRace = ModelLocator.getEventFactory().createRace(new URI(paramURL.toString()));
- } else {
- this.tractracRace = ModelLocator.getEventFactory().createRace(new URI(paramURL.toString()), timeoutInMillis);
- }
+ final IRace tractracRace = getTractracRace();
if (preferReplayIfAvailable && isReplayRace(tractracRace) &&
(!Util.equalsWithNull(liveURI, tractracRace.getLiveURI()) || !Util.equalsWithNull(storedURI, tractracRace.getStoredURI()))) {
logger.info("Replay format available and preferred for race " + tractracRace.getName()
@@ -114,8 +110,14 @@ public class RaceTrackingConnectivityParametersImpl extends AbstractRaceTracking
return tractracRace.getStoredURI() != null && tractracRace.getStoredURI().toString().toLowerCase().endsWith(".mtb");
}
- public IRace getTractracRace() {
- return tractracRace;
+ public IRace getTractracRace() throws CreateModelException, URISyntaxException, TimeOutException {
+ final IRace result;
+ if (getTimeoutInMillis() == -1) {
+ result = ModelLocator.getEventFactory().createRace(new URI(paramURL.toString()));
+ } else {
+ result = ModelLocator.getEventFactory().createRace(new URI(paramURL.toString()), getTimeoutInMillis());
+ }
+ return result;
}
@Override
@@ -127,7 +129,7 @@ public class RaceTrackingConnectivityParametersImpl extends AbstractRaceTracking
public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore,
RaceLogAndTrackedRaceResolver raceLogResolver, LeaderboardGroupResolver leaderboardGroupResolver,
long timeoutInMilliseconds, RaceTrackingHandler raceTrackingHandler) throws URISyntaxException,
- CreateModelException, SubscriberInitializationException, IOException, InterruptedException {
+ CreateModelException, SubscriberInitializationException, IOException, InterruptedException, TimeOutException {
RaceTracker tracker = domainFactory.createRaceTracker(raceLogStore, regattaLogStore, windStore,
trackedRegattaRegistry, raceLogResolver, leaderboardGroupResolver, this, timeoutInMilliseconds,
raceTrackingHandler);
diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java
index 3f9d278809c..eb87642d3b6 100755
--- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java
+++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java
@@ -56,6 +56,7 @@ import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.util.impl.ThreadFactoryWithPriority;
+import com.tractrac.model.lib.api.event.CreateModelException;
import com.tractrac.model.lib.api.event.DataSource;
import com.tractrac.model.lib.api.event.ICompetitor;
import com.tractrac.model.lib.api.event.IEvent;
@@ -71,6 +72,7 @@ import com.tractrac.subscription.lib.api.event.IConnectionStatusListener;
import com.tractrac.subscription.lib.api.event.ILiveDataEvent;
import com.tractrac.subscription.lib.api.event.IStoredDataEvent;
import com.tractrac.subscription.lib.api.race.IRacesListener;
+import com.tractrac.util.lib.api.exceptions.TimeOutException;
public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl
implements IConnectionStatusListener, TracTracRaceTracker, DynamicRaceDefinitionSet, TrackingDataLoader {
@@ -270,11 +272,12 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl
* you expect things to have loaded; see also
* {@link RaceTracker#TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS}.
*/
- TracTracRaceTrackerImpl(DomainFactory domainFactory, RaceLogStore raceLogStore,
- RegattaLogStore regattaLogStore, WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry,
- RaceLogAndTrackedRaceResolver raceLogResolver, LeaderboardGroupResolver leaderboardGroupResolver, RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds,
- RaceTrackingHandler raceTrackingHandler)
- throws URISyntaxException, SubscriberInitializationException, IOException, InterruptedException {
+ TracTracRaceTrackerImpl(DomainFactory domainFactory, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
+ WindStore windStore, TrackedRegattaRegistry trackedRegattaRegistry,
+ RaceLogAndTrackedRaceResolver raceLogResolver, LeaderboardGroupResolver leaderboardGroupResolver,
+ RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds,
+ RaceTrackingHandler raceTrackingHandler) throws URISyntaxException, SubscriberInitializationException,
+ IOException, InterruptedException, CreateModelException, TimeOutException {
this(/* regatta */ null, domainFactory, raceLogStore, regattaLogStore, windStore, trackedRegattaRegistry,
raceLogResolver, leaderboardGroupResolver, connectivityParams, timeoutInMilliseconds,
raceTrackingHandler);
@@ -299,7 +302,7 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl
RaceLogAndTrackedRaceResolver raceLogResolver, LeaderboardGroupResolver leaderboardGroupResolver,
RaceTrackingConnectivityParametersImpl connectivityParams, long timeoutInMilliseconds,
RaceTrackingHandler raceTrackingHandler)
- throws URISyntaxException, SubscriberInitializationException, IOException, InterruptedException {
+ throws URISyntaxException, SubscriberInitializationException, IOException, InterruptedException, CreateModelException, TimeOutException {
super(connectivityParams);
final URL paramURL = connectivityParams.getParamURL();
final URI liveURI = connectivityParams.getLiveURI();
@@ -358,10 +361,20 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl
@Override
public void deleteCompetitor(UUID competitorId) {
+ try {
+ trackedRegattaRegistry.updateRaceCompetitors(getRegatta(), getRace());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}
@Override
public void addCompetitor(ICompetitor competitor) {
+ try {
+ trackedRegattaRegistry.updateRaceCompetitors(getRegatta(), getRace());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}
};
eventSubscriber.subscribeCompetitors(competitorsListener);
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Regatta.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Regatta.java
index 23bd93ebfd1..60997a38154 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Regatta.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Regatta.java
@@ -238,11 +238,26 @@ public interface Regatta
* show_bug.cgi?id=3588.
*/
boolean isControlTrackingFromStartAndFinishTimes();
-
+
/**
* @see #isControlTrackingFromStartAndFinishTimes()
*/
void setControlTrackingFromStartAndFinishTimes(boolean controlTrackingFromStartAndFinishTimes);
+
+ /**
+ * Tells whether in the scope of this regatta tracking of a race shall automatically be re-started if the competitor
+ * registrations change. Removing the {@link TrackedRace tracked races} and {@link RaceDefinition}s affected is then
+ * required because the {@link RaceDefinition#getCompetitors() set of competitors of a race} is modeled to be
+ * immutable. As the set of competitors that are part of a tracked race is provided by the tracking connector, the
+ * responsibility for adhering to this flag, which rather expresses a request than describes a fact, lies with the
+ * respective connectors.
+ */
+ boolean isAutoRestartTrackingUponCompetitorSetChange();
+
+ /**
+ * @see #isAutoRestartTrackingUponCompetitorSetChange()
+ */
+ void setAutoRestartTrackingUponCompetitorSetChange(boolean autoRestartTrackingUponCompetitorSetChange);
@Override
default QualifiedObjectIdentifier getIdentifier() {
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractRaceColumn.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractRaceColumn.java
index 363c67afbf0..2e49291ab4e 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractRaceColumn.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractRaceColumn.java
@@ -389,19 +389,28 @@ public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implem
}
Set competitorSet = new HashSet();
Util.addAll(competitors, competitorSet);
- RaceLog raceLog = getRaceLog(fleet);
- for (RaceLogEvent event : raceLog.getUnrevokedEventsDescending()) {
- if (event instanceof RegisterCompetitorEvent) {
- RegisterCompetitorEvent> registerEvent = (RegisterCompetitorEvent>) event;
- if (competitorSet.contains(registerEvent.getCompetitor())) {
- try {
- raceLog.revokeEvent(raceLogEventAuthorForRaceColumn, event,
- "unregistering competitor because no longer selected for registration");
- } catch (NotRevokableException e) {
- logger.log(Level.WARNING, "could not unregister competitor by adding RevokeEvent", e);
+ final RaceLog raceLog = getRaceLog(fleet);
+ final Set competitorRegistrationEventsToRevoke = new HashSet<>();
+ raceLog.lockForRead();
+ try {
+ for (RaceLogEvent event : raceLog.getUnrevokedEventsDescending()) {
+ if (event instanceof RegisterCompetitorEvent) {
+ RegisterCompetitorEvent> registerEvent = (RegisterCompetitorEvent>) event;
+ if (competitorSet.contains(registerEvent.getCompetitor())) {
+ competitorRegistrationEventsToRevoke.add(event);
}
}
}
+ } finally {
+ raceLog.unlockAfterRead();
+ }
+ for (final RaceLogEvent eventToRevoke : competitorRegistrationEventsToRevoke) {
+ try {
+ raceLog.revokeEvent(raceLogEventAuthorForRaceColumn, eventToRevoke,
+ "unregistering competitor because no longer selected for registration");
+ } catch (NotRevokableException e) {
+ logger.log(Level.WARNING, "could not unregister competitor by adding RevokeEvent", e);
+ }
}
}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java
index 06cdbbd8d80..4cb2b71e4f4 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java
@@ -152,6 +152,8 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
* show_bug.cgi?id=3588.
*/
private boolean controlTrackingFromStartAndFinishTimes;
+
+ private boolean autoRestartTrackingUponCompetitorSetChange;
private boolean canBoatsOfCompetitorsChangePerRace;
@@ -169,7 +171,8 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
/**
* Constructs a regatta with an empty {@link RaceLogStore} and with
- * {@link Regatta#isControlTrackingFromStartAndFinishTimes()} set to {@code false}.
+ * {@link Regatta#isControlTrackingFromStartAndFinishTimes()} and
+ * {@link #isAutoRestartTrackingUponCompetitorSetChange()} set to {@code false}.
*/
public RegattaImpl(String name, BoatClass boatClass, boolean canBoatsOfCompetitorsChangePerRace, CompetitorRegistrationType competitorRegistrationType,
TimePoint startDate, TimePoint endDate,
@@ -178,13 +181,14 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
this(EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, name, boatClass, canBoatsOfCompetitorsChangePerRace, competitorRegistrationType,
startDate, endDate, series,
persistent, scoringScheme, id, courseArea, 0.0, /* useStartTimeInference */true, /* controlTrackingFromStartAndFinishTimes */ false,
- rankingMetricConstructor, registrationLinkSecret);
+ /* autoRestartTrackingUponCompetitorSetChange */ false, rankingMetricConstructor, registrationLinkSecret);
}
/**
* Constructs a regatta with a single default series with empty race column list, and a single default fleet which
* is not {@link #isPersistent() marked for persistence}. The regatta is created with
- * {@link Regatta#isControlTrackingFromStartAndFinishTimes()} set to {@code false}.
+ * {@link Regatta#isControlTrackingFromStartAndFinishTimes()} and
+ * {@link #isAutoRestartTrackingUponCompetitorSetChange()} set to {@code false}.
*
* @param trackedRegattaRegistry
* used to find the {@link TrackedRegatta} for this column's series' {@link Series#getRegatta() regatta}
@@ -199,8 +203,8 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
ScoringScheme scoringScheme, Serializable id, CourseArea courseArea, String registrationLinkSecret) {
this(raceLogStore, regattaLogStore, name, boatClass, canBoatsOfCompetitorsChangePerRace, competitorRegistrationType,
startDate, endDate, trackedRegattaRegistry, scoringScheme,
- id, courseArea, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new,
- registrationLinkSecret);
+ id, courseArea, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false,
+ OneDesignRankingMetric::new, registrationLinkSecret);
}
/**
@@ -217,15 +221,15 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
boolean canBoatsOfCompetitorsChangePerRace, CompetitorRegistrationType competitorRegistrationType,
TimePoint startDate, TimePoint endDate, TrackedRegattaRegistry trackedRegattaRegistry,
ScoringScheme scoringScheme, Serializable id, CourseArea courseArea,
- boolean controlTrackingFromStartAndFinishTimes, RankingMetricConstructor rankingMetricConstructor,
- String registrationLinkSecret) {
+ boolean controlTrackingFromStartAndFinishTimes, boolean autoRestartTrackingUponCompetitorSetChange,
+ RankingMetricConstructor rankingMetricConstructor, String registrationLinkSecret) {
this(raceLogStore, regattaLogStore, name, boatClass, canBoatsOfCompetitorsChangePerRace, competitorRegistrationType,
startDate, endDate, Collections.singletonList(new SeriesImpl(LeaderboardNameConstants.DEFAULT_SERIES_NAME,
/* isMedal */false, /* isFleetsCanRunInParallel */ true, Collections
.singletonList(new FleetImpl(LeaderboardNameConstants.DEFAULT_FLEET_NAME)),
/* race column names */new ArrayList(), trackedRegattaRegistry)), /* persistent */false,
scoringScheme, id, courseArea, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */true, controlTrackingFromStartAndFinishTimes,
- rankingMetricConstructor, registrationLinkSecret);
+ autoRestartTrackingUponCompetitorSetChange, rankingMetricConstructor, registrationLinkSecret);
}
/**
@@ -237,13 +241,14 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
BoatClass boatClass, boolean canBoatsOfCompetitorsChangePerRace, CompetitorRegistrationType competitorRegistrationType,
TimePoint startDate, TimePoint endDate, Iterable series, boolean persistent,
ScoringScheme scoringScheme, Serializable id, CourseArea courseArea, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference,
- boolean controlTrackingFromStartAndFinishTimes, RankingMetricConstructor rankingMetricConstructor,
- String registrationLinkSecret) {
+ boolean controlTrackingFromStartAndFinishTimes, boolean autoRestartTrackingUponCompetitorSetChange,
+ RankingMetricConstructor rankingMetricConstructor, String registrationLinkSecret) {
super(name);
this.registrationLinkSecret = registrationLinkSecret;
this.rankingMetricConstructor = rankingMetricConstructor;
this.useStartTimeInference = useStartTimeInference;
this.controlTrackingFromStartAndFinishTimes = controlTrackingFromStartAndFinishTimes;
+ this.autoRestartTrackingUponCompetitorSetChange = autoRestartTrackingUponCompetitorSetChange;
this.id = id;
this.raceLogStore = raceLogStore;
races = new ConcurrentHashMap<>();
@@ -671,10 +676,20 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
return controlTrackingFromStartAndFinishTimes;
}
+ @Override
+ public boolean isAutoRestartTrackingUponCompetitorSetChange() {
+ return autoRestartTrackingUponCompetitorSetChange;
+ }
+
@Override
public void setControlTrackingFromStartAndFinishTimes(boolean controlTrackingFromStartAndFinishTimes) {
this.controlTrackingFromStartAndFinishTimes = controlTrackingFromStartAndFinishTimes;
}
+
+ @Override
+ public void setAutoRestartTrackingUponCompetitorSetChange(boolean autoRestartTrackingUponCompetitorSetChange) {
+ this.autoRestartTrackingUponCompetitorSetChange = autoRestartTrackingUponCompetitorSetChange;
+ }
@Override
public void setUseStartTimeInference(boolean useStartTimeInference) {
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegattaRegistry.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegattaRegistry.java
index 0a557343779..d509d4aaf41 100644
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegattaRegistry.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegattaRegistry.java
@@ -91,4 +91,26 @@ public interface TrackedRegattaRegistry {
Regatta getOrCreateDefaultRegatta(String name, String boatClassName, Serializable id);
+ /**
+ * Does what is necessary to react to an external change in the {@code race}'s set of competitors. This can, e.g.,
+ * entail removing and re-tracking that race which, however, has to be explicitly
+ * {@link Regatta#isAutoRestartTrackingUponCompetitorSetChange() permitted by the regatta} as it will lead to a
+ * temporary unavailability of the race and any information related to it such as the leaderboard scores.
+ *
+ * By and large, we assume that a race's {@link RaceDefinition#getCompetitors() set of competitors} remains
+ * unchanged once the race has been created. There are, however, scenarios in which this causes some trouble. For
+ * example, if tracking for a regatta shall be started early, so as to enable race officials to see the course
+ * geometry and wind data for the first race of the day, then especially in self-service scenarios there may be late
+ * competitors registrations that happen after tracking has been started. These competitors, depending on how the
+ * connector in use works, may make it into the regatta's leaderboard, but not into the {@link RaceDefinition}. This
+ * would be unfortunate as those competitors then would remain invisible until the race is reloaded.
+ *
+ *
+ * See also bug 5219 for a discussion.
+ *
+ * @return {@code null} if it was not possible to re-load the race, either because the regatta doesn't allow for it,
+ * or because the connectivity parameters were not found; a {@link RaceHandle} for the re-started race
+ * otherwise.
+ */
+ RaceHandle updateRaceCompetitors(Regatta regatta, RaceDefinition race) throws Exception;
}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackerManager.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackerManager.java
index d4e55b47316..e8c3942a619 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackerManager.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackerManager.java
@@ -6,6 +6,7 @@ import java.net.URISyntaxException;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
+import com.sap.sailing.domain.base.RegattaListener;
import com.sap.sailing.domain.common.RegattaIdentifier;
public interface TrackerManager {
@@ -14,7 +15,13 @@ public interface TrackerManager {
* Creates a {@link RaceTracker} based on the {@code params}. If the {@code params} request
* {@link RaceTrackingConnectivityParameters#isTrackWind() wind tracking}, a callback is
* {@link RaceTracker#add(com.sap.sailing.domain.tracking.RaceTracker.RaceCreationListener) registered} with the
- * {@link RaceTracker} that, when the race has been created by the tracker, will start wind tracking.
+ * {@link RaceTracker} that, when the race has been created by the tracker, will start wind tracking.
+ *
+ * The effects of calling this operation will be replicated by means of a listener that is notified when the connector
+ * has successfully loaded and installed the race. {@code RacingEventServiceImpl.raceAdded(...)} which is notified
+ * when the race definition has been added to the regatta (implementing {@link RegattaListener#raceAdded(Regatta, RaceDefinition)}),
+ * and {@code RacingEventServiceImpl.RaceAdditionListener.raceAdded(...)} which is called when the {@link TrackedRace}
+ * is added to the {@link TrackedRegatta}.
*
* @param regattaToAddTo
* if null or no regatta by that identifier is found, the regatta into which the race has
diff --git a/java/com.sap.sailing.feature.p2build/scripts/analyzeLaunch.sh b/java/com.sap.sailing.feature.p2build/scripts/analyzeLaunch.sh
deleted file mode 100755
index 6e54a0d1415..00000000000
--- a/java/com.sap.sailing.feature.p2build/scripts/analyzeLaunch.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-target_bundles=$(cat "$1" | grep 'key="target_bundles"' | sed -e 's/^.* value="\([^"]*\)"\/>/\1/' | sed -e 's/,/\n/g' | sed -e 's/^\(.*\)@\(.*\):\(.*\)$/\1 \2 \3/')
-workspace_bundles=$(cat "$1" | grep 'key="workspace_bundles"' | sed -e 's/^.* value="\([^"]*\)"\/>/\1/' | sed -e 's/,/\n/g' | sed -e 's/^\(.*\)@\(.*\):\(.*\)$/\1 \2 \3/')
-echo " *** Target Bundles ***"
-echo "$target_bundles" | sort
-echo " *** Workspace Bundles ***"
-echo "$workspace_bundles" | sort
-
diff --git a/java/com.sap.sailing.feature.p2build/scripts/analyzeProduct.sh b/java/com.sap.sailing.feature.p2build/scripts/analyzeProduct.sh
index 0c22346e9a1..0d570e4ec2e 100755
--- a/java/com.sap.sailing.feature.p2build/scripts/analyzeProduct.sh
+++ b/java/com.sap.sailing.feature.p2build/scripts/analyzeProduct.sh
@@ -1,12 +1,21 @@
#!/bin/bash
# Call like this: analyzeProduct.sh [ ...]
-# Will output two lines in format
-#
-#
+# Updates Eclipse .launch files with the bundles needed for starting an SAP Sailing server based on the current feature.xml.
+# The .launch files must be in the workspace format that was introduced with Eclipse 2019-12. The script prints an error when no matching
+# replacement section could be found.
+# Will output two sections in format
+#
+#
+#
+#
+# "
else
# contribute to workspace_bundles
- workspace_bundles="${workspace_bundles},${bundle_spec_for_launch}"
+ workspace_bundles="${workspace_bundles}${NL}"
fi
done
done
echo ' *** Target Bundles ***'
-echo ""
+echo "${target_bundles}"
echo ' *** Workspace Bundles ***'
-echo ""
+echo "${workspace_bundles}"
# Now patch the .launch file if provided
while [ "$2" != "" ]; do
- echo "Patching $2 ..."
- if echo "$2" | grep -q winddbTest; then
- patched_workspace_bundles="${workspace_bundles},com.sap.sailing.server.gateway.test.support@default:default"
+ if grep -q key=\"target_bundles\" "$2"; then
+ echo "Not patching $2 ... please use script for eclipse 2019-09 or older"
else
- patched_workspace_bundles="${workspace_bundles}"
+ echo "Patching $2 ..."
+ if echo "$2" | grep -q winddbTest; then
+ patched_workspace_bundles="${workspace_bundles}${NL}${NL}"
+ else
+ patched_workspace_bundles="${workspace_bundles}"
+ fi
+ selected_target_bundles="${target_bundles}${NL}<\/setAttribute>"
+ patched_workspace_bundles="${patched_workspace_bundles}${NL}<\/setAttribute>"
+ # Used Perl CLI options:
+ #0 -> specifies the record separator, as you alread found out, it defaults to \0 so it does not terminate before the end of the text file. Ensures that the regular expression is applied over the whole file.
+ #p -> apply looping wrapper, see perl man page
+ #e -> denotes the (regular) expression that is goint ot be applied.
+ #i -> inplace editing of files
+ perl -i -p0e "s/.*?<\/setAttribute>/${selected_target_bundles}/s" "$2"
+ perl -i -p0e "s/.*?<\/setAttribute>/${patched_workspace_bundles}/s" "$2"
+ # remove eventuelly created bak files (this happens at least on windows platform)
+ rm -f "$2.bak"
fi
- sed -i 's/
+
+
diff --git a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/PingMarkViaRegattaLogTest.java b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/PingMarkViaRegattaLogTest.java
index ceecd2aa025..8cf7b6a5a99 100644
--- a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/PingMarkViaRegattaLogTest.java
+++ b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/PingMarkViaRegattaLogTest.java
@@ -45,7 +45,7 @@ public class PingMarkViaRegattaLogTest {
/* canBoatsOfCompetitorsChangePerRace */ true, CompetitorRegistrationType.CLOSED, /* registrationLinkSecret */ null, /*startDate*/ null, /*endDate*/ null,
UUID.randomUUID(), Collections.singletonList(series),
false, new HighPoint(), UUID.randomUUID(), /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true,
- /* controlTrackingFromStartAndFinishTimes */ false, RankingMetricsFactory.getRankingMetricConstructor(RankingMetrics.ONE_DESIGN));
+ /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, RankingMetricsFactory.getRankingMetricConstructor(RankingMetrics.ONE_DESIGN));
RegattaLeaderboard leaderboard = service.addRegattaLeaderboard(regatta.getRegattaIdentifier(), "RegattaLeaderboard", new int[] {});
MarkDTO mark = new MarkDTO("mark", "mark", "m");
diff --git a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/SlicingTest.java b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/SlicingTest.java
index f452ff75ebb..3414f6e3909 100644
--- a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/SlicingTest.java
+++ b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/SlicingTest.java
@@ -99,7 +99,7 @@ public class SlicingTest {
seriesCreationParameters);
final RegattaDTO regatta = sailingService.createRegatta(regattaName, boatClassName, false,
CompetitorRegistrationType.CLOSED, null, null, null, regattaCreationParameters, false,
- ScoringSchemeType.HIGH_POINT, null, 3.0, false, false, RankingMetrics.ONE_DESIGN);
+ ScoringSchemeType.HIGH_POINT, null, 3.0, false, false, /* autoRestartTrackingUponCompetitorSetChange */ false, RankingMetrics.ONE_DESIGN);
final List> columnNames = new ArrayList<>();
columnNames.add(new Pair<>(columnNameOriginalRace, 0));
sailingService.addRaceColumnsToSeries(regatta.getRegattaIdentifier(), seriesName, columnNames);
diff --git a/java/com.sap.sailing.gwt.ui/AdminConsole.html b/java/com.sap.sailing.gwt.ui/AdminConsole.html
index 9a0d013b0f9..db0e90a4034 100644
--- a/java/com.sap.sailing.gwt.ui/AdminConsole.html
+++ b/java/com.sap.sailing.gwt.ui/AdminConsole.html
@@ -12,13 +12,13 @@
-
+
- SAP Sailing Analytics Administration Console
+ ${SAP}Sailing Analytics Administration Console
@@ -27,12 +27,16 @@
-
+
-
-
-
+
+
+
@@ -40,72 +44,72 @@
-
-
+ @-webkit-keyframes pulsate {
+ 0% {
+ -webkit-transform:scale(.1);
+ transform:scale(.1);
+ opacity: 0.0;
+ }
+ 50% {
+ opacity:1;
+ }
+ 100% {
+ -webkit-transform:scale(1.2);
+ transform:scale(1.2);
+ opacity:0;
+ }
+ }
+
+ @keyframes pulsate {
+ 0% {
+ -webkit-transform:scale(.1);
+ transform:scale(.1);
+ opacity: 0.0;
+ }
+ 50% {
+ opacity:1;
+ }
+ 100% {
+ -webkit-transform:scale(1.2);
+ transform:scale(1.2);
+ opacity:0;
+ }
+ }
+
@@ -126,10 +130,10 @@
-