diff --git a/XLF/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties.xlf b/XLF/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties.xlf index 03128dc78cc..298fe071d5b 100644 --- a/XLF/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties.xlf +++ b/XLF/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties.xlf @@ -1,12 +1,12 @@ - +
- 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 - +
@@ -1050,7 +1050,7 @@ Registered at {0} - Replicating from master {0}:{2}, messaging port {3}:{1}, exchange {4} + Replicating from master {0}:{2}, messaging port {3}:{1}, exchange {4}, replicating {5} Show wind speed data @@ -1965,10 +1965,10 @@ SOG when Starting - Distance to start line at race start time + Distance to start line at race start time; distance to nearest line end in case the competitor projects beyond the line''s ends - Distance to start line five seconds before the start + Distance to start line five seconds before the start; distance to nearest line end in case the competitor projects beyond the line''s ends Speed over ground at race start time @@ -4567,7 +4567,7 @@ https://play.google.com/store/apps/details?id=com.sap.sailing.android.buoy.positioning.app - https://play.google.com/intl/en/badges/images/generic/en-play-badge.png + https://play.google.com/intl/en/badges/images/badge_new.png http://appstore.com/sapsailinsight @@ -4611,6 +4611,9 @@ There are no events yet. + + Please select a competitor import provider... + Please select a result import provider... @@ -4978,7 +4981,7 @@ Jobs - © 2011-2016 SAP Sailing Analytics + © 2011-2017 SAP Sailing Analytics SAP Sailing in @@ -5163,6 +5166,42 @@ Incorrect value for the buoy zone radius in hull lengths + + Import Competitors + + + Select competitors to be imported on the left. Selecting a single competitor shows any existing competitors that may represent the same entity on the right. To use an existing one, select it. When done mapping, select all competitors you want to import in the table on the left and press OK. Note that the tables can be sorted by clicking or tapping on the column headers. + + + Existing Competitors + + + Competitors to Import + + + Has matches + + + Filter competitors to import by name, sail number, race or fleet + + + Error loading competitor import providers: {0} + + + Error loading competitor import descriptors: {0} + + + Could not save competitor registrations: {0} + + + Use, e.g., the race+fleet name + + + Version: {0} + + + Replicating objects +
\ No newline at end of file diff --git a/XLF/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties.xlf b/XLF/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties.xlf index c84676f6049..44aeb1fa75b 100644 --- a/XLF/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties.xlf +++ b/XLF/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties.xlf @@ -1,12 +1,12 @@ - +
- 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 - +
@@ -1396,8 +1396,8 @@ 登録時刻 {0} - Replicating from master {0}:{2}, messaging port {3}:{1}, exchange {4} - マスタ {0}:{2}、メッセージングポート {3}:{1}、エクスチェンジ {4} から複製 + Replicating from master {0}:{2}, messaging port {3}:{1}, exchange {4}, replicating {5} + マスタ {0}:{2}、メッセージングポート {3}:{1}、エクスチェンジ {4} から複製、{5} を複製 Show wind speed data @@ -2616,12 +2616,12 @@ スタート時の SOG - Distance to start line at race start time - レーススタート時刻のスタートラインまでの距離 + Distance to start line at race start time; distance to nearest line end in case the competitor projects beyond the line''s ends + レーススタート時刻のスタートラインまでの距離 (競技者がスタートラインの各エンドより外側に位置している場合は近い方のエンドまでの距離) - Distance to start line five seconds before the start - スタート 5 秒前のスタートラインまでの距離 + Distance to start line five seconds before the start; distance to nearest line end in case the competitor projects beyond the line''s ends + スタート 5 秒前のスタートラインまでの距離 (競技者がスタートラインの各エンドより外側に位置している場合は近い方のエンドまでの距離) Speed over ground at race start time @@ -3185,7 +3185,7 @@ Protest starts at - 抗議開始時刻 + 抗議開始時刻 Protest ends at @@ -3849,7 +3849,7 @@ Sailing World Cup Start - セーリングワールドカップ開始 + セーリングワールドカップ開始 Activate configuration @@ -4629,7 +4629,7 @@ Loading wind data... - 風データをロード中... + 風データをロード中... No Videos @@ -5877,11 +5877,11 @@ The following competitors cannot be registered to this race : {0} - 次の競技者はこのレースに登録することができません: {0} + 次の競技者はこのレースに登録することができません: {0} {0} has already been assigned to fleet {2} in race {3} and therefore cannot be assigned to fleet {1} within the same race - {0} はすでにレース {3} のフリート {2} に割り当てられており、そのため同一レース内でフリート {1} に割り当てることはできません + {0} はすでにレース {3} のフリート {2} に割り当てられており、そのため同一レース内でフリート {1} に割り当てることはできません Add Mark to Regatta @@ -6084,8 +6084,8 @@ https://play.google.com/store/apps/details?id=com.sap.sailing.android.buoy.positioning.app - https://play.google.com/intl/en/badges/images/generic/en-play-badge.png - https://play.google.com/intl/en/badges/images/generic/en-play-badge.png + https://play.google.com/intl/en/badges/images/badge_new.png + https://play.google.com/intl/en/badges/images/badge_new.png http://appstore.com/sapsailinsight @@ -6143,6 +6143,10 @@ There are no events yet. イベントがまだありません。 + + Please select a competitor import provider... + 競技者インポートプロバイダを選択してください... + Please select a result import provider... 結果インポートプロバイダを選択してください... @@ -6632,8 +6636,8 @@ 採用 - © 2011-2016 SAP Sailing Analytics - © 2011-2016 SAP Sailing Analytics + © 2011-2017 SAP Sailing Analytics + © 2011-2017 SAP Sailing Analytics SAP Sailing in @@ -6879,6 +6883,54 @@ Incorrect value for the buoy zone radius in hull lengths 艇身単位のゾーン半径の値が不適切 + + Import Competitors + 競技者のインポート + + + Select competitors to be imported on the left. Selecting a single competitor shows any existing competitors that may represent the same entity on the right. To use an existing one, select it. When done mapping, select all competitors you want to import in the table on the left and press OK. Note that the tables can be sorted by clicking or tapping on the column headers. + インポートする競技者を左側で選択します。単一の競技者を選択しても、同一の対象を表す既存の競技者が複数あればそれらが右側に表示されます。既存の競技者を使用するには、その競技者を選択します。マッピングが終了したら、テーブルにインポートするすべての競技者を左側で選択して、OK を押します。テーブルは、列ヘッダをクリックするかタップしてソートが可能であることに注意してください。 + + + Existing Competitors + 既存の競技者 + + + Competitors to Import + インポートする競技者 + + + Has matches + 一致あり + + + Filter competitors to import by name, sail number, race or fleet + インポートする競技者を名前、セールナンバー、レース、またはフリートでフィルタリング + + + Error loading competitor import providers: {0} + 競技者インポートプロバイダのロードでエラーが発生: {0} + + + Error loading competitor import descriptors: {0} + 競技者インポート記述子のロードでエラーが発生: {0} + + + Could not save competitor registrations: {0} + 競技者登録を保存できませんでした: {0} + + + Use, e.g., the race+fleet name + たとえば、レース+フリート名を使用 + + + Version: {0} + バージョン: {0} + + + Replicating objects + オブジェクトの複製 +
\ No newline at end of file diff --git a/XLF/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties.xlf b/XLF/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties.xlf index dbfe0575ee0..ab0941cca5d 100644 --- a/XLF/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties.xlf +++ b/XLF/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties.xlf @@ -1,12 +1,12 @@ - +
- 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 - +
@@ -1396,8 +1396,8 @@ Зарегистрировано в {0} - Replicating from master {0}:{2}, messaging port {3}:{1}, exchange {4} - Репликация с главного экземпляра {0}:{2}, порт обмена сообщениями {3}:{1}, Exchange {4} + Replicating from master {0}:{2}, messaging port {3}:{1}, exchange {4}, replicating {5} + Репликация с главного экземпляра {0}:{2}, порт обмена сообщениями {3}:{1}, Exchange {4}, репликация {5} Show wind speed data @@ -2616,12 +2616,12 @@ SOG при старте - Distance to start line at race start time - Дистанция до линии старта во время старта гонки + Distance to start line at race start time; distance to nearest line end in case the competitor projects beyond the line''s ends + Дистанция до линии старта во время старта гонки; в случае проецирования участника за концы линии — дистанция до ближайшего конца линии - Distance to start line five seconds before the start - Дистанция до линии старта за пять секунд до старта + Distance to start line five seconds before the start; distance to nearest line end in case the competitor projects beyond the line''s ends + Дистанция до линии старта за пять секунд до старта гонки; в случае проецирования участника за концы линии — дистанция до ближайшего конца линии Speed over ground at race start time @@ -3185,7 +3185,7 @@ Protest starts at - Протест начинается в + Протест начинается в Protest ends at @@ -3849,7 +3849,7 @@ Sailing World Cup Start - Старт Кубка мира по парусному спорту + Старт Кубка мира по парусному спорту Activate configuration @@ -4629,7 +4629,7 @@ Loading wind data... - Загрузка данных о ветре... + Загрузка данных о ветре... No Videos @@ -5877,11 +5877,11 @@ The following competitors cannot be registered to this race : {0} - Регистрация следующих участников на эту гонку невозможна: {0} + Регистрация следующих участников на эту гонку невозможна: {0} {0} has already been assigned to fleet {2} in race {3} and therefore cannot be assigned to fleet {1} within the same race - {0} уже присвоен флоту {2} в гонке {3} и поэтому не может быть присвоен флоту {1} в той же самой гонке + {0} уже присвоен флоту {2} в гонке {3} и поэтому не может быть присвоен флоту {1} в той же самой гонке Add Mark to Regatta @@ -6084,8 +6084,8 @@ https://play.google.com/store/apps/details?id=com.sap.sailing.android.buoy.positioning.app - https://play.google.com/intl/en/badges/images/generic/en-play-badge.png - https://play.google.com/intl/en/badges/images/generic/en-play-badge.png + https://play.google.com/intl/en/badges/images/badge_new.png + https://play.google.com/intl/en/badges/images/badge_new.png http://appstore.com/sapsailinsight @@ -6143,6 +6143,10 @@ There are no events yet. Событий еще нет. + + Please select a competitor import provider... + Выберите поставщика импорта участников... + Please select a result import provider... Выберите поставщика импорта результатов... @@ -6632,8 +6636,8 @@ Работа - © 2011-2016 SAP Sailing Analytics - © SAP Sailing Analytics, 2011–2016. + © 2011-2017 SAP Sailing Analytics + © SAP Sailing Analytics, 2011-2017. SAP Sailing in @@ -6879,6 +6883,54 @@ Incorrect value for the buoy zone radius in hull lengths Неверное значение радиуса зоны буя в длинах корпуса + + Import Competitors + Импорт участников + + + Select competitors to be imported on the left. Selecting a single competitor shows any existing competitors that may represent the same entity on the right. To use an existing one, select it. When done mapping, select all competitors you want to import in the table on the left and press OK. Note that the tables can be sorted by clicking or tapping on the column headers. + Слева выберите участников для импорта. При выборе одного участника справа отображаются все существующие участники, которые могут представлять одну и ту же организацию. Чтобы использовать существующего участника, выберите его. По завершении сопоставления выберите всех участников, которых требуется импортировать, в таблице слева и нажмите кнопку "ОК". Таблица можно сортировать щелчком или касанием заголовков столбцов. + + + Existing Competitors + Существующие участники + + + Competitors to Import + Участники для импорта + + + Has matches + Есть соответствия + + + Filter competitors to import by name, sail number, race or fleet + Отфильтруйте участников для импорта по названию, номеру на парусе, гонке или флоту + + + Error loading competitor import providers: {0} + Ошибка при загрузке поставщиков импорта участников: {0} + + + Error loading competitor import descriptors: {0} + Ошибка при загрузке дескрипторов импорта участников: {0} + + + Could not save competitor registrations: {0} + Не удалось сохранить регистрации участников: {0} + + + Use, e.g., the race+fleet name + Используйте, например, гонку + название флота + + + Version: {0} + Версия: {0} + + + Replicating objects + Репликация объектов +
\ No newline at end of file diff --git a/XLF/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh_CN.properties.xlf b/XLF/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh_CN.properties.xlf index 8a94a8fa43f..53ba5bf4660 100644 --- a/XLF/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh_CN.properties.xlf +++ b/XLF/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh_CN.properties.xlf @@ -1,12 +1,12 @@ - +
- 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 - +
@@ -1396,8 +1396,8 @@ 注册日期{0} - Replicating from master {0}:{2}, messaging port {3}:{1}, exchange {4} - 正在从主服务器{0}:{2}、消息传送端口{3}:{1}、交换机{4}复制 + Replicating from master {0}:{2}, messaging port {3}:{1}, exchange {4}, replicating {5} + 正在从主服务器{0}:{2}、消息传送端口{3}:{1}、交换机{4}复制,正在复制{5} Show wind speed data @@ -2616,12 +2616,12 @@ 起航时的实际速度 - Distance to start line at race start time - 比赛轮次开始时离开起航线的距离 + Distance to start line at race start time; distance to nearest line end in case the competitor projects beyond the line''s ends + 比赛轮次开始时离开起航线的距离;参赛队突破航线终点时与最近航线终点的距离 - Distance to start line five seconds before the start - 起航前五秒时离开起航线的距离 + Distance to start line five seconds before the start; distance to nearest line end in case the competitor projects beyond the line''s ends + 起航前五秒时离开起航线的距离;参赛队突破航线终点时与最近航线终点的距离 Speed over ground at race start time @@ -6084,8 +6084,8 @@ https://play.google.com/store/apps/details?id=com.sap.sailing.android.buoy.positioning.app - https://play.google.com/intl/en/badges/images/generic/en-play-badge.png - https://play.google.com/intl/en/badges/images/generic/en-play-badge.png + https://play.google.com/intl/en/badges/images/badge_new.png + https://play.google.com/intl/en/badges/images/badge_new.png http://appstore.com/sapsailinsight @@ -6143,6 +6143,10 @@ There are no events yet. 暂无活动。 + + Please select a competitor import provider... + 请选择参赛队导入提供方... + Please select a result import provider... 请选择结果导入提供方... @@ -6632,8 +6636,8 @@ 招贤纳士 - © 2011-2016 SAP Sailing Analytics - 版权所有 © 2011-2016 SAP Sailing Analytics + © 2011-2017 SAP Sailing Analytics + 版权所有 © 2011-2017 SAP Sailing Analytics SAP Sailing in @@ -6879,6 +6883,54 @@ Incorrect value for the buoy zone radius in hull lengths 船体长度表示的浮标区半径的值不正确 + + Import Competitors + 导入参赛队 + + + Select competitors to be imported on the left. Selecting a single competitor shows any existing competitors that may represent the same entity on the right. To use an existing one, select it. When done mapping, select all competitors you want to import in the table on the left and press OK. Note that the tables can be sorted by clicking or tapping on the column headers. + 在左侧选择待导入的参赛队。选择单个参赛队会在右侧显示代表相同实体的任何现有参赛队。要使用现有参赛队,请选择。完成映射时,在左侧的表中选择待导入的所有参赛队,并按“确定”。请注意,点击或点按列标题即可对表进行排序。 + + + Existing Competitors + 现有参赛队 + + + Competitors to Import + 待导入的参赛队 + + + Has matches + 有比赛 + + + Filter competitors to import by name, sail number, race or fleet + 按名称、帆号、比赛轮次或船队筛选待导入的参赛队 + + + Error loading competitor import providers: {0} + 加载参赛队导入提供方时出错:{0} + + + Error loading competitor import descriptors: {0} + 加载参赛队导入描述符时出错:{0} + + + Could not save competitor registrations: {0} + 无法保存参赛队注册:{0} + + + Use, e.g., the race+fleet name + 例如,使用比赛轮次+船队名称 + + + Version: {0} + 版本:{0} + + + Replicating objects + 正在复制对象 +
\ No newline at end of file diff --git a/java/com.google.gwt.user/META-INF/MANIFEST.MF b/java/com.google.gwt.user/META-INF/MANIFEST.MF old mode 100644 new mode 100755 index e5e6be69714..f95c14ecdd7 --- a/java/com.google.gwt.user/META-INF/MANIFEST.MF +++ b/java/com.google.gwt.user/META-INF/MANIFEST.MF @@ -187,5 +187,5 @@ Export-Package: com.google.gwt.activity.shared, javaemul.internal.annotations, jsinterop.annotations Require-Bundle: javax.servlet;bundle-version="3.1.0", - org.junit4;bundle-version="4.8.2";resolution:=optional, + org.junit;bundle-version="4.8.2";resolution:=optional, com.google.gwt.dev;bundle-version="2.8.0" diff --git a/java/com.sap.sailing.autoload/.classpath b/java/com.sap.sailing.autoload/.classpath deleted file mode 100644 index 4f83b2397ec..00000000000 --- a/java/com.sap.sailing.autoload/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/java/com.sap.sailing.autoload/.gitignore b/java/com.sap.sailing.autoload/.gitignore deleted file mode 100644 index 092357e47d3..00000000000 --- a/java/com.sap.sailing.autoload/.gitignore +++ /dev/null @@ -1 +0,0 @@ -bin diff --git a/java/com.sap.sailing.autoload/.project b/java/com.sap.sailing.autoload/.project deleted file mode 100755 index c38dee8ba08..00000000000 --- a/java/com.sap.sailing.autoload/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - com.sap.sailing.autoload - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/java/com.sap.sailing.autoload/.settings/org.eclipse.jdt.core.prefs b/java/com.sap.sailing.autoload/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index a698e59674f..00000000000 --- a/java/com.sap.sailing.autoload/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,12 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.8 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.8 diff --git a/java/com.sap.sailing.autoload/.settings/org.eclipse.pde.core.prefs b/java/com.sap.sailing.autoload/.settings/org.eclipse.pde.core.prefs deleted file mode 100755 index 42ee077170e..00000000000 --- a/java/com.sap.sailing.autoload/.settings/org.eclipse.pde.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -#Mon Aug 29 13:20:38 CEST 2011 -eclipse.preferences.version=1 -pluginProject.extensions=false -resolve.requirebundle=false diff --git a/java/com.sap.sailing.autoload/META-INF/MANIFEST.MF b/java/com.sap.sailing.autoload/META-INF/MANIFEST.MF deleted file mode 100644 index cb8d57d083b..00000000000 --- a/java/com.sap.sailing.autoload/META-INF/MANIFEST.MF +++ /dev/null @@ -1,23 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: AutoLoad Races -Bundle-SymbolicName: com.sap.sailing.autoload -Bundle-Version: 1.0.0.qualifier -Bundle-Vendor: SAP -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Bundle-ActivationPolicy: lazy -Export-Package: com.sap.sailing.autoload -Import-Package: org.json.simple, - org.json.simple.parser, - org.osgi.framework;version="1.7.0", - org.osgi.util.tracker;version="1.5.1" -Require-Bundle: com.sap.sailing.gwt.ui, - com.sap.sse.common, - com.sap.sailing.domain.shared.android, - com.sap.sailing.domain.tractracadapter, - com.sap.sailing.server, - com.sap.sailing.domain, - com.sap.sailing.domain.common, - com.sap.sailing.domain.persistence, - com.sap.sse.replication -Bundle-Activator: com.sap.sailing.autoload.Activator diff --git a/java/com.sap.sailing.autoload/build.properties b/java/com.sap.sailing.autoload/build.properties deleted file mode 100755 index 41eb6ade2b4..00000000000 --- a/java/com.sap.sailing.autoload/build.properties +++ /dev/null @@ -1,4 +0,0 @@ -source.. = src/ -output.. = bin/ -bin.includes = META-INF/,\ - . diff --git a/java/com.sap.sailing.autoload/pom.xml b/java/com.sap.sailing.autoload/pom.xml deleted file mode 100644 index b68132bd8ba..00000000000 --- a/java/com.sap.sailing.autoload/pom.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - 4.0.0 - - root - com.sap.sailing - 1.0.0-SNAPSHOT - - com.sap.sailing.autoload - eclipse-plugin - diff --git a/java/com.sap.sailing.autoload/src/com/sap/sailing/autoload/Activator.java b/java/com.sap.sailing.autoload/src/com/sap/sailing/autoload/Activator.java deleted file mode 100644 index 0c1a601cab3..00000000000 --- a/java/com.sap.sailing.autoload/src/com/sap/sailing/autoload/Activator.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.sap.sailing.autoload; - -import java.util.logging.Logger; - -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; -import org.osgi.util.tracker.ServiceTracker; - -import com.sap.sailing.server.RacingEventService; - - -public class Activator implements BundleActivator { - Logger logger = Logger.getLogger(Activator.class.getName()); - - @Override - public void start(BundleContext context) throws Exception { - RacingEventService racingEventService = createAndOpenRacingEventServiceTracker(context).getService(); - new TracTrac(context, racingEventService); - } - - @Override - public void stop(BundleContext context) throws Exception { - } - - protected ServiceTracker createAndOpenRacingEventServiceTracker( - BundleContext context) { - ServiceTracker result = new ServiceTracker( - context, RacingEventService.class.getName(), null); - result.open(); - return result; - } -} diff --git a/java/com.sap.sailing.autoload/src/com/sap/sailing/autoload/TracTrac.java b/java/com.sap.sailing.autoload/src/com/sap/sailing/autoload/TracTrac.java deleted file mode 100644 index e461b040993..00000000000 --- a/java/com.sap.sailing.autoload/src/com/sap/sailing/autoload/TracTrac.java +++ /dev/null @@ -1,141 +0,0 @@ -package com.sap.sailing.autoload; - -import java.io.IOException; -import java.io.Serializable; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Logger; - -import org.osgi.framework.BundleContext; -import org.osgi.util.tracker.ServiceTracker; - -import com.sap.sailing.domain.base.Regatta; -import com.sap.sailing.domain.common.RegattaIdentifier; -import com.sap.sailing.domain.persistence.MongoRaceLogStoreFactory; -import com.sap.sailing.domain.persistence.MongoRegattaLogStoreFactory; -import com.sap.sailing.domain.racelog.RaceLogStore; -import com.sap.sailing.domain.regattalog.RegattaLogStore; -import com.sap.sailing.domain.tracking.RaceTracker; -import com.sap.sailing.domain.tracking.TrackerManager; -import com.sap.sailing.domain.tractracadapter.RaceRecord; -import com.sap.sailing.domain.tractracadapter.TracTracAdapter; -import com.sap.sailing.domain.tractracadapter.TracTracAdapterFactory; -import com.sap.sailing.domain.tractracadapter.TracTracConnectionConstants; -import com.sap.sailing.gwt.ui.shared.TracTracRaceRecordDTO; -import com.sap.sailing.server.RacingEventService; -import com.sap.sse.common.Util.Pair; -import com.sap.sse.common.impl.MillisecondsTimePoint; - -/** - * Auto load TracTrac races from a parameter called tractrac.autoload.json_urls. This - * parameter contains a list of all event json urls from which you want to load races from. Only - * races in state REPLAY will be loaded. - * - * @author Simon Marcel Pamies - */ -public class TracTrac { - Logger logger = Logger.getLogger(TracTrac.class.getName()); - - private final TracTracAdapter tracTracAdapter; - private final RacingEventService racingEventService; - - public TracTrac(BundleContext context, RacingEventService racingEventService) { - this.racingEventService = racingEventService; - this.tracTracAdapter = createAndOpenTracTracAdapterTracker(context).getService().getOrCreateTracTracAdapter(this.racingEventService.getBaseDomainFactory()); - try { - autoLoadTrackTrackRaces(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - private TracTracAdapter getTracTracAdapter() { - return this.tracTracAdapter; - } - - private RacingEventService getService() { - return this.racingEventService; - } - - private void autoLoadTrackTrackRaces() throws Exception { - String jsonUrls = System.getProperty("tractrac.autoload.json_urls"); - if (jsonUrls != null) { - String[] splitJsonUrls = jsonUrls.split(","); - for (String jsonUrl : splitJsonUrls) { - logger.info("Loading races from " + jsonUrl); - Pair> racesThatCanBeTracked = listTracTracRacesInEvent(jsonUrl, /*listHiddenRaces*/false); - - for (TracTracRaceRecordDTO raceRecord : racesThatCanBeTracked.getB()) { - RaceRecord record = getTracTracAdapter().getSingleTracTracRaceRecord(new URL(raceRecord.jsonURL), raceRecord.id, /*loadClientParams*/true); - if (record.getRaceStatus().equals(TracTracConnectionConstants.REPLAY_STATUS) || record.getRaceVisibility().equals(TracTracConnectionConstants.REPLAY_VISIBILITY)) { - RegattaIdentifier regattaForRaceRecord = null; - if (raceRecord.hasRememberedRegatta()) { - Regatta regatta = getService().getRememberedRegattaForRace(raceRecord.id); - if (regatta != null) { - regattaForRaceRecord = regatta.getRegattaIdentifier(); - } - } - RaceLogStore raceLogStore = MongoRaceLogStoreFactory.INSTANCE.getMongoRaceLogStore(getService() - .getMongoObjectFactory(), getService().getDomainObjectFactory()); - RegattaLogStore regattaLogStore = MongoRegattaLogStoreFactory.INSTANCE.getMongoRegattaLogStore( - getService().getMongoObjectFactory(), getService().getDomainObjectFactory()); - getTracTracAdapter().addTracTracRace( - (TrackerManager) getService(), - regattaForRaceRecord, - record.getParamURL(), /* effectiveLiveURI */ - null, - record.getStoredURI(), - new URI(""), - new MillisecondsTimePoint(record.getTrackingStartTime().asMillis()), - new MillisecondsTimePoint(record.getTrackingEndTime().asMillis()), - raceLogStore, regattaLogStore, - RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS, /* offsetToStartTimeOfSimulatedRace */ - null, /*useTracTracMarkPassings*/ false, - /* tracTracUsername */"", /* tracTracPassword */"", record.getRaceStatus(), - record.getRaceVisibility()); - } else { - logger.info("Ignoring race " + record.getName() + " because it is in status " - + record.getRaceStatus() + " and visibility " + record.getRaceVisibility()); - } - } - } - } - } - - public com.sap.sse.common.Util.Pair> listTracTracRacesInEvent(String eventJsonURL, boolean listHiddenRaces) throws MalformedURLException, IOException, ParseException, org.json.simple.parser.ParseException, URISyntaxException { - com.sap.sse.common.Util.Pair> raceRecords; - raceRecords = getTracTracAdapter().getTracTracRaceRecords(new URL(eventJsonURL), /*loadClientParam*/ false); - List result = new ArrayList(); - for (RaceRecord raceRecord : raceRecords.getB()) { - if (listHiddenRaces == false && raceRecord.getRaceVisibility().equals(TracTracConnectionConstants.HIDDEN_VISIBILITY)) { - continue; - } - - result.add(new TracTracRaceRecordDTO(raceRecord.getID(), raceRecord.getEventName(), raceRecord.getName(), - raceRecord.getTrackingStartTime().asDate(), - raceRecord - .getTrackingEndTime().asDate(), raceRecord.getRaceStartTime().asDate(), - raceRecord.getBoatClassNames(), raceRecord.getRaceStatus(), raceRecord.getRaceVisibility(), raceRecord.getJsonURL().toString(), - hasRememberedRegatta(raceRecord.getID()))); - } - return new com.sap.sse.common.Util.Pair>(raceRecords.getA(), result); - } - - private boolean hasRememberedRegatta(Serializable raceID) { - return racingEventService.getRememberedRegattaForRace(raceID) != null; - } - - private ServiceTracker createAndOpenTracTracAdapterTracker(BundleContext context) { - ServiceTracker result = new ServiceTracker( - context, TracTracAdapterFactory.class.getName(), null); - result.open(); - result.getService().getOrCreateTracTracAdapter(racingEventService.getBaseDomainFactory()); - return result; - } - -} diff --git a/java/com.sap.sailing.barbados.resultimport.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.barbados.resultimport.test/META-INF/MANIFEST.MF index 9829a6d3b4c..4d151e7392a 100644 --- a/java/com.sap.sailing.barbados.resultimport.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.barbados.resultimport.test/META-INF/MANIFEST.MF @@ -7,7 +7,7 @@ Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: org.osgi.framework;version="1.3.0" Bundle-ActivationPolicy: lazy -Require-Bundle: org.junit4;bundle-version="4.8.2", +Require-Bundle: org.junit;bundle-version="4.8.2", com.sap.sailing.barbados.resultimport, com.sap.sailing.domain.common, com.sap.sailing.domain, diff --git a/java/com.sap.sailing.barbados.resultimport/META-INF/MANIFEST.MF b/java/com.sap.sailing.barbados.resultimport/META-INF/MANIFEST.MF index 40b356a0688..eccd0e62539 100644 --- a/java/com.sap.sailing.barbados.resultimport/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.barbados.resultimport/META-INF/MANIFEST.MF @@ -13,9 +13,7 @@ Bundle-ActivationPolicy: lazy Require-Bundle: com.sap.sailing.server, com.sap.sailing.domain.common, com.sap.sailing.domain, - jcl.over.slf4j;bundle-version="1.6.4", com.sap.sailing.resultimport, - slf4j.api;bundle-version="1.6.4", com.sap.sse.common, org.dom4j;bundle-version="1.6.1", org.apache.xmlbeans;bundle-version="2.3.0", 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 a86323c6d2f..234cca5e792 100644 --- a/java/com.sap.sailing.dashboards.gwt/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.dashboards.gwt/META-INF/MANIFEST.MF @@ -15,7 +15,7 @@ Require-Bundle: com.sap.sailing.domain, com.sap.sailing.domain.common, com.sap.sailing.domain.shared.android, com.sap.sailing.gwt.ui, - org.moxieapps.gwt.highcharts;bundle-version="1.6.0", + org.moxieapps.gwt.highcharts;bundle-version="1.6.0";resolution:=optional, com.sap.sse.gwt, com.sap.sailing.domain.igtimiadapter, com.sap.sailing.expeditionconnector, @@ -23,7 +23,7 @@ Require-Bundle: com.sap.sailing.domain, com.sap.sailing.declination, org.json.simple;bundle-version="1.1.0", com.sap.sailing.udpconnector, - com.googlecode.mgwt;bundle-version="2.0.0", + com.googlecode.mgwt;bundle-version="2.0.0";resolution:=optional, com.sap.sse.gwt.adminconsole, org.apache.commons.math;bundle-version="2.1.0" Bundle-ActivationPolicy: lazy diff --git a/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages.properties b/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages.properties index c3317d84fd8..7b21a7c9a2a 100644 --- a/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages.properties +++ b/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages.properties @@ -20,7 +20,8 @@ CompetitorSearchTag=Competitor Search Tag LeaderboardSailingDomainRetrieverChain=Leaderboards RaceSailingDomainRetrieverChain=Races LegSailingDomainRetrieverChain=Legs -GPSFixSailingDomainRetrieverChain=GPS-Fixes +GPSFixSailingDomainRetrieverChain=GPS Fixes +WindFixSailingDomainRetrieverChain=Wind Fixes MarkPassingSailingDomainRetrieverChain=Mark Passings RaceResultSailingDomainRetrieverChain=Race Results RaceOfCompetitorSailingDomainRetrieverChain=Races of Competitors @@ -30,7 +31,8 @@ Leaderboard=Leaderboard Leg=Leg MarkPassing=Mark Passing LegOfCompetitor=Leg of competitor -GpsFix=GPS-Fix +GpsFix=GPS Fix +WindFix=Wind Fix Maneuver=Maneuver WindStrengthInBeaufort=Wind Strength in Beaufort Bft0=Calm @@ -72,6 +74,8 @@ Tack=Tack SpeedBefore=Speed Before Maneuver SpeedAfter=Speed After Maneuver DirectionChange=Direction Change in Degrees +AbsoluteDirectionChange=Absolute Direction Change in Degrees (Positive) +ToSide=To Side (Port / Starboard) ManeuverLoss=Maneuver Loss Waypoint=Waypoint PassingSide=Passing Side @@ -82,4 +86,11 @@ WindwardDistanceToAdvantageousEndOfLine=Windward Distance to Advantageous End of RankAfterHalfOfTheFirstLeg=Rank after the half of the first leg AdvantageousEndOfLine=Advantageous End of Line TimeSpentInSeconds=Time Spent in Seconds -MedalRace=Medal Race \ No newline at end of file +MedalRace=Medal Race +WindSourceType=Wind Source Type +WindSourceName=Wind Source Name +windFrom=True Wind Direction (TWD) +windSpeedKnots=True Wind Speed (kts) +speedInKnots=Speed (kts) +bearing=Course over Ground (°T) +WindTrack=Wind Track \ No newline at end of file diff --git a/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_de.properties b/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_de.properties index 4c6d486e4ca..f8f5e12b8b7 100644 --- a/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_de.properties +++ b/java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_de.properties @@ -19,6 +19,7 @@ LeaderboardSailingDomainRetrieverChain=Leaderboards RaceSailingDomainRetrieverChain=Rennen LegSailingDomainRetrieverChain=Schenkel GPSFixSailingDomainRetrieverChain=GPS-Fixes +WindFixSailingDomainRetrieverChain=Windmessungen MarkPassingSailingDomainRetrieverChain=Tonnenrundungen RaceResultSailingDomainRetrieverChain=Rennergebnisse RaceOfCompetitorSailingDomainRetrieverChain=Rennen eines Teilnehmers @@ -29,6 +30,7 @@ Leg=Schenkel MarkPassing=Tonnenrundung LegOfCompetitor=Schenkel eines Teilnehmers GpsFix=GPS-Fix +WindFix=Windmessung Maneuver=Manöver Speed=Geschwindigkeit SpeedTenSecondsBefore=Geschwindigkeit 10s vorher @@ -72,6 +74,8 @@ Tack=Bug SpeedBefore=Geschwindigkeit bevor dem Manöver SpeedAfter=Geschwindigkeit nach dem Manöver DirectionChange=Richtungsänderung in Grad +AbsoluteDirectionChange=Absolute Richtungsänderung in Grad (positiv) +ToSide=In Richtung (Backbord / Steuerbord) ManeuverLoss=Manöververlust Waypoint=Wegpunkt PassingSide=Passierende Seite @@ -82,4 +86,11 @@ WindwardDistanceToAdvantageousEndOfLine=Windw RankAfterHalfOfTheFirstLeg=Platzierung nach der Hälfte des ersten Schenkels AdvantageousEndOfLine=Bevorteiltes Ende der Startlinie TimeSpentInSeconds=Benötigte Zeit in Sekunden -MedalRace=Medaillenrennen \ No newline at end of file +MedalRace=Medaillenrennen +WindSourceType=Art der Windmessung +WindSourceName=Name der Windmessreihe +windFrom=Wind aus Richtung (TWD) +windSpeedKnots=Wahre Windgeschwindigkeit (kn) +speedInKnots=Fahrt (kn) +bearing=Kurs über Grund (°) +WindTrack=Windmessreihe \ No newline at end of file diff --git a/java/com.sap.sailing.datamining/resources/stringmessages/XLF/Sailing_StringMessages_ja.properties.xlf b/java/com.sap.sailing.datamining/resources/stringmessages/XLF/Sailing_StringMessages_ja.properties.xlf index a388689ee7e..9f8850c79de 100644 --- a/java/com.sap.sailing.datamining/resources/stringmessages/XLF/Sailing_StringMessages_ja.properties.xlf +++ b/java/com.sap.sailing.datamining/resources/stringmessages/XLF/Sailing_StringMessages_ja.properties.xlf @@ -1,6 +1,6 @@ - +
H4sIAAAAAAAAAIVWS28jKRC+8yu4jJQcRopn9qkVKznJ2vHKeci2Ntoj7i53o7TBApys59dvVdEP7Ew8F7rq4wOKetHLHUCpljQKHldgl1A4W4Zr2DgPaU6OroJcMyBuTYjaFrDy+hUaXN0BskPEg96CokHMoVoddqDwK0kQS22a2a2ij3zYb9fgxb+gvaKB2Aljfjv9oKNxVjcmHlQmi3HhnT1sVfsVC6h0jFq1X3Hj9j7A2INWSZQki2un402jQ1AkSRbFpAGIikex0AUoGnCH7Q6iic6rQeQNeK1Ygd4qGjLmEu9R1CtdZUtkAiWieEVdgl877UvygbHVrdtqYxcQvYFX8Dc1aiqjBbboHJnmAznv/I5VENOn5cT8d46GjM9Iwf3utX95Qucg89wCosmWl0xdQNg38UcGy0RLSx43mQd/dFPpNnKgk6EW9kg5b2Xi5P6ferff5Z6WjOSUfJYcTF7MHXN0eyLkF+EkRluLIXemu4DO7ZzcWz6Y92xsuYwebBXrmb0Gvceai4pg2eHSWNnNiOtNvFI3utmSNFJzU9VRauNJ/dKqaw/wDQj5qqZgYwMZ9JO6d3hFHXPwZzXxEOoM+UXh8c5WGfSrusPt5RvaRupvaqobnvi941YtMLpCxPlk40j9Y1yDZsjQY1/U3d57U6AbsI4brPJXWBbUfjpNsiovrrANhfiHHMk3jH28PKbP7BP4Avc+XYcu+3RKpWQ6t4CzlBeO18E1+wgLbV9Up0jSUsCoRb6LFvdNCtUm9j1zHJdRI6NvmdFJRiS2IhZSF36usQ2TSjmWdjomnLbptO3QqxPariBDx3Fi0F+Ur4p02o8RSZBIvfZx0yViUAmh/O2xnrXSxUvOYL2f/dtgiLJZ1vvZJ8D+HQ83xhfNEa2dkO0MWz3F2g2Pfu5CQC7bzZDEpprA4TlydNnUWE35oadToRNDXnyiY+dcU5eDfa40++3EWBPq3DqGZcKz62Cs80uQ+t7ya4hvALaPwNiW7QHfu5K8GLVhkZ//bE+8FLOAz2vxgu/tLMhW7K0Ymg737syg7BVKbX2ItH85JfOZXfdPQefXu1PSE07RVjSkVDz6T2jzru9njI43EXtcIrA8zN8aDwW96dimbQWq12UCqIBuocKeM5hEXhpMIk0868POGaziThDdy4WBVq3MUWfru+Qg+cPCYktP6yqZP5TVqjY+Ht6v4Lh+PVlAfeEN029I2XH5qm3UFbh9+MuWj5u5saA6nsyTN6dK5HLuGuqXVN10yp1uNlibNXCe0WuVCp0tiDXIGgm0jOQN136Dz9n3bfjwuJXZAvrDxlnnKEWQZIzC1aLiHkrd8M8US9xOxf+VN8i0cAoAAA== diff --git a/java/com.sap.sailing.datamining/resources/stringmessages/XLF/Sailing_StringMessages_ru.properties.xlf b/java/com.sap.sailing.datamining/resources/stringmessages/XLF/Sailing_StringMessages_ru.properties.xlf index b324dfe30fe..48b2d7e49dd 100644 --- a/java/com.sap.sailing.datamining/resources/stringmessages/XLF/Sailing_StringMessages_ru.properties.xlf +++ b/java/com.sap.sailing.datamining/resources/stringmessages/XLF/Sailing_StringMessages_ru.properties.xlf @@ -1,6 +1,6 @@ - +
H4sIAAAAAAAAAIVWS28jKRC+8yu4jJQcRopn9qkVKznJ2vHKeci2Ntoj7i53o7TBApys59dvVdEP7Ew8F7rq4wOKetHLHUCpljQKHldgl1A4W4Zr2DgPaU6OroJcMyBuTYjaFrDy+hUaXN0BskPEg96CokHMoVoddqDwK0kQS22a2a2ij3zYb9fgxb+gvaKB2Aljfjv9oKNxVjcmHlQmi3HhnT1sVfsVC6h0jFq1X3Hj9j7A2INWSZQki2un402jQ1AkSRbFpAGIikex0AUoGnCH7Q6iic6rQeQNeK1Ygd4qGjLmEu9R1CtdZUtkAiWieEVdgl877UvygbHVrdtqYxcQvYFX8Dc1aiqjBbboHJnmAznv/I5VENOn5cT8d46GjM9Iwf3utX95Qucg89wCosmWl0xdQNg38UcGy0RLSx43mQd/dFPpNnKgk6EW9kg5b2Xi5P6ferff5Z6WjOSUfJYcTF7MHXN0eyLkF+EkRluLIXemu4DO7ZzcWz6Y92xsuYwebBXrmb0Gvceai4pg2eHSWNnNiOtNvFI3utmSNFJzU9VRauNJ/dKqaw/wDQj5qqZgYwMZ9JO6d3hFHXPwZzXxEOoM+UXh8c5WGfSrusPt5RvaRupvaqobnvi941YtMLpCxPlk40j9Y1yDZsjQY1/U3d57U6AbsI4brPJXWBbUfjpNsiovrrANhfiHHMk3jH28PKbP7BP4Avc+XYcu+3RKpWQ6t4CzlBeO18E1+wgLbV9Up0jSUsCoRb6LFvdNCtUm9j1zHJdRI6NvmdFJRiS2IhZSF36usQ2TSjmWdjomnLbptO3QqxPariBDx3Fi0F+Ur4p02o8RSZBIvfZx0yViUAmh/O2xnrXSxUvOYL2f/dtgiLJZ1vvZJ8D+HQ83xhfNEa2dkO0MWz3F2g2Pfu5CQC7bzZDEpprA4TlydNnUWE35oadToRNDXnyiY+dcU5eDfa40++3EWBPq3DqGZcKz62Cs80uQ+t7ya4hvALaPwNiW7QHfu5K8GLVhkZ//bE+8FLOAz2vxgu/tLMhW7K0Ymg737syg7BVKbX2ItH85JfOZXfdPQefXu1PSE07RVjSkVDz6T2jzru9njI43EXtcIrA8zN8aDwW96dimbQWq12UCqIBuocKeM5hEXhpMIk0868POGaziThDdy4WBVq3MUWfru+Qg+cPCYktP6yqZP5TVqjY+Ht6v4Lh+PVlAfeEN029I2XH5qm3UFbh9+MuWj5u5saA6nsyTN6dK5HLuGuqXVN10yp1uNlibNXCe0WuVCp0tiDXIGgm0jOQN136Dz9n3bfjwuJXZAvrDxlnnKEWQZIzC1aLiHkrd8M8US9xOxf+VN8i0cAoAAA== diff --git a/java/com.sap.sailing.datamining/resources/stringmessages/XLF/Sailing_StringMessages_zh_CN.properties.xlf b/java/com.sap.sailing.datamining/resources/stringmessages/XLF/Sailing_StringMessages_zh_CN.properties.xlf index b7349273236..003edbf8858 100644 --- a/java/com.sap.sailing.datamining/resources/stringmessages/XLF/Sailing_StringMessages_zh_CN.properties.xlf +++ b/java/com.sap.sailing.datamining/resources/stringmessages/XLF/Sailing_StringMessages_zh_CN.properties.xlf @@ -1,6 +1,6 @@ - +
H4sIAAAAAAAAAIVWS28jKRC+8yu4jJQcRopn9qkVKznJ2vHKeci2Ntoj7i53o7TBApys59dvVdEP7Ew8F7rq4wOKetHLHUCpljQKHldgl1A4W4Zr2DgPaU6OroJcMyBuTYjaFrDy+hUaXN0BskPEg96CokHMoVoddqDwK0kQS22a2a2ij3zYb9fgxb+gvaKB2Aljfjv9oKNxVjcmHlQmi3HhnT1sVfsVC6h0jFq1X3Hj9j7A2INWSZQki2un402jQ1AkSRbFpAGIikex0AUoGnCH7Q6iic6rQeQNeK1Ygd4qGjLmEu9R1CtdZUtkAiWieEVdgl877UvygbHVrdtqYxcQvYFX8Dc1aiqjBbboHJnmAznv/I5VENOn5cT8d46GjM9Iwf3utX95Qucg89wCosmWl0xdQNg38UcGy0RLSx43mQd/dFPpNnKgk6EW9kg5b2Xi5P6ferff5Z6WjOSUfJYcTF7MHXN0eyLkF+EkRluLIXemu4DO7ZzcWz6Y92xsuYwebBXrmb0Gvceai4pg2eHSWNnNiOtNvFI3utmSNFJzU9VRauNJ/dKqaw/wDQj5qqZgYwMZ9JO6d3hFHXPwZzXxEOoM+UXh8c5WGfSrusPt5RvaRupvaqobnvi941YtMLpCxPlk40j9Y1yDZsjQY1/U3d57U6AbsI4brPJXWBbUfjpNsiovrrANhfiHHMk3jH28PKbP7BP4Avc+XYcu+3RKpWQ6t4CzlBeO18E1+wgLbV9Up0jSUsCoRb6LFvdNCtUm9j1zHJdRI6NvmdFJRiS2IhZSF36usQ2TSjmWdjomnLbptO3QqxPariBDx3Fi0F+Ur4p02o8RSZBIvfZx0yViUAmh/O2xnrXSxUvOYL2f/dtgiLJZ1vvZJ8D+HQ83xhfNEa2dkO0MWz3F2g2Pfu5CQC7bzZDEpprA4TlydNnUWE35oadToRNDXnyiY+dcU5eDfa40++3EWBPq3DqGZcKz62Cs80uQ+t7ya4hvALaPwNiW7QHfu5K8GLVhkZ//bE+8FLOAz2vxgu/tLMhW7K0Ymg737syg7BVKbX2ItH85JfOZXfdPQefXu1PSE07RVjSkVDz6T2jzru9njI43EXtcIrA8zN8aDwW96dimbQWq12UCqIBuocKeM5hEXhpMIk0868POGaziThDdy4WBVq3MUWfru+Qg+cPCYktP6yqZP5TVqjY+Ht6v4Lh+PVlAfeEN029I2XH5qm3UFbh9+MuWj5u5saA6nsyTN6dK5HLuGuqXVN10yp1uNlibNXCe0WuVCp0tiDXIGgm0jOQN136Dz9n3bfjwuJXZAvrDxlnnKEWQZIzC1aLiHkrd8M8US9xOxf+VN8i0cAoAAA== diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/Activator.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/Activator.java index 183cba04870..994ab4c6972 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/Activator.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/Activator.java @@ -17,6 +17,9 @@ import com.sap.sailing.datamining.data.HasRaceResultOfCompetitorContext; import com.sap.sailing.datamining.data.HasTrackedLegContext; import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext; import com.sap.sailing.datamining.data.HasTrackedRaceContext; +import com.sap.sailing.datamining.data.HasWindFixContext; +import com.sap.sailing.datamining.data.HasWindTrackContext; +import com.sap.sailing.datamining.impl.components.aggregators.ParallelBearingAverageDegreesAggregationProcessor; import com.sap.sailing.datamining.impl.components.aggregators.ParallelBoolSumAggregationProcessor; import com.sap.sailing.datamining.impl.components.aggregators.ParallelDistanceAverageAggregationProcessor; import com.sap.sailing.datamining.impl.components.aggregators.ParallelDistanceMaxAggregationProcessor; @@ -81,6 +84,8 @@ public class Activator extends AbstractDataMiningActivatorWithPredefinedQueries internalClasses.add(HasTrackedLegContext.class); internalClasses.add(HasTrackedLegOfCompetitorContext.class); internalClasses.add(HasGPSFixContext.class); + internalClasses.add(HasWindTrackContext.class); + internalClasses.add(HasWindFixContext.class); internalClasses.add(HasManeuverContext.class); internalClasses.add(HasMarkPassingContext.class); internalClasses.add(HasRaceOfCompetitorContext.class); @@ -112,6 +117,7 @@ public class Activator extends AbstractDataMiningActivatorWithPredefinedQueries aggregators.add(ParallelDistanceMaxAggregationProcessor.getDefinition()); aggregators.add(ParallelDistanceMinAggregationProcessor.getDefinition()); aggregators.add(ParallelDistanceMedianAggregationProcessor.getDefinition()); + aggregators.add(ParallelBearingAverageDegreesAggregationProcessor.getDefinition()); return aggregators; } @@ -133,7 +139,6 @@ public class Activator extends AbstractDataMiningActivatorWithPredefinedQueries if (INSTANCE == null) { INSTANCE = new Activator(); // probably non-OSGi case, as in test execution } - return INSTANCE; } diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/SailingDataRetrievalChainDefinitions.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/SailingDataRetrievalChainDefinitions.java index d5dedde95c0..c38c72668be 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/SailingDataRetrievalChainDefinitions.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/SailingDataRetrievalChainDefinitions.java @@ -12,6 +12,8 @@ import com.sap.sailing.datamining.data.HasRaceResultOfCompetitorContext; import com.sap.sailing.datamining.data.HasTrackedLegContext; import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext; import com.sap.sailing.datamining.data.HasTrackedRaceContext; +import com.sap.sailing.datamining.data.HasWindFixContext; +import com.sap.sailing.datamining.data.HasWindTrackContext; import com.sap.sailing.datamining.impl.components.CompetitorOfRaceInLeaderboardRetrievalProcessor; import com.sap.sailing.datamining.impl.components.GPSFixRetrievalProcessor; import com.sap.sailing.datamining.impl.components.LeaderboardGroupRetrievalProcessor; @@ -22,6 +24,8 @@ import com.sap.sailing.datamining.impl.components.RaceOfCompetitorRetrievalProce import com.sap.sailing.datamining.impl.components.TrackedLegOfCompetitorRetrievalProcessor; import com.sap.sailing.datamining.impl.components.TrackedLegRetrievalProcessor; import com.sap.sailing.datamining.impl.components.TrackedRaceRetrievalProcessor; +import com.sap.sailing.datamining.impl.components.WindFixRetrievalProcessor; +import com.sap.sailing.datamining.impl.components.WindTrackRetrievalProcessor; import com.sap.sailing.datamining.impl.data.LeaderboardGroupWithContext; import com.sap.sailing.server.RacingEventService; import com.sap.sse.datamining.components.DataRetrieverChainDefinition; @@ -69,6 +73,13 @@ public class SailingDataRetrievalChainDefinitions { HasGPSFixContext.class, "GpsFix"); dataRetrieverChainDefinitions.add(gpsFixRetrieverChainDefinition); + final DataRetrieverChainDefinition windFixRetrieverChainDefinition = new SimpleDataRetrieverChainDefinition<>( + trackedRaceRetrieverChainDefinition, HasWindFixContext.class, "WindFixSailingDomainRetrieverChain"); + windFixRetrieverChainDefinition.addAfter(TrackedRaceRetrievalProcessor.class, WindTrackRetrievalProcessor.class, HasWindTrackContext.class, "WindTrack"); + windFixRetrieverChainDefinition.endWith(WindTrackRetrievalProcessor.class, WindFixRetrievalProcessor.class, + HasWindFixContext.class, "WindFix"); + dataRetrieverChainDefinitions.add(windFixRetrieverChainDefinition); + final DataRetrieverChainDefinition maneuverRetrieverChainDefinition = new SimpleDataRetrieverChainDefinition<>( legOfCompetitorRetrieverChainDefinition, HasManeuverContext.class, "ManeuverSailingDomainRetrieverChain"); maneuverRetrieverChainDefinition.endWith(TrackedLegOfCompetitorRetrievalProcessor.class, ManeuverRetrievalProcessor.class, diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasGPSFixContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasGPSFixContext.java index 3ef332f9532..a78a2ec17b9 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasGPSFixContext.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasGPSFixContext.java @@ -1,22 +1,24 @@ package com.sap.sailing.datamining.data; -import java.util.Locale; - +import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.tracking.GPSFixMoving; +import com.sap.sse.common.TimePoint; import com.sap.sse.datamining.annotations.Connector; -import com.sap.sse.datamining.annotations.Dimension; -import com.sap.sse.datamining.shared.impl.dto.ClusterDTO; -import com.sap.sse.i18n.ResourceBundleStringMessages; -public interface HasGPSFixContext { - +public interface HasGPSFixContext extends HasWindOnTrackedLeg { @Connector(scanForStatistics=false) public HasTrackedLegOfCompetitorContext getTrackedLegOfCompetitorContext(); @Connector(ordinal=1) public GPSFixMoving getGPSFix(); - - @Dimension(messageKey="WindStrengthInBeaufort", ordinal=11) - public ClusterDTO getWindStrengthAsBeaufortCluster(Locale locale, ResourceBundleStringMessages stringMessages); + @Override + default TimePoint getTimePoint() { + return getGPSFix().getTimePoint(); + } + + @Override + default Position getPosition() { + return getGPSFix().getPosition(); + } } \ No newline at end of file diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasManeuver.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasManeuver.java new file mode 100755 index 00000000000..3d0842c61d2 --- /dev/null +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasManeuver.java @@ -0,0 +1,21 @@ +package com.sap.sailing.datamining.data; + +import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.Positioned; +import com.sap.sailing.domain.tracking.Maneuver; +import com.sap.sse.common.TimePoint; +import com.sap.sse.common.Timed; + +public interface HasManeuver extends Positioned, Timed { + Maneuver getManeuver(); + + @Override + default TimePoint getTimePoint() { + return getManeuver().getTimePoint(); + } + + @Override + default Position getPosition() { + return getManeuver().getPosition(); + } +} diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasManeuverContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasManeuverContext.java index 6ec4fba870f..1850038c4e1 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasManeuverContext.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasManeuverContext.java @@ -2,20 +2,17 @@ package com.sap.sailing.datamining.data; import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.ManeuverType; +import com.sap.sailing.domain.common.NauticalSide; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.Tack; -import com.sap.sailing.domain.tracking.Maneuver; import com.sap.sse.datamining.annotations.Connector; import com.sap.sse.datamining.annotations.Dimension; import com.sap.sse.datamining.annotations.Statistic; -public interface HasManeuverContext { - +public interface HasManeuverContext extends HasWindOnTrackedLeg, HasManeuver, HasTrackedLegOfCompetitor { @Connector(scanForStatistics=false) HasTrackedLegOfCompetitorContext getTrackedLegOfCompetitorContext(); - - Maneuver getManeuver(); - + //TODO Clean-Up: // Move Dimensions and Statistics to Maneuver and connect to it @Dimension(messageKey="ManeuverType", ordinal=12) @@ -31,6 +28,12 @@ public interface HasManeuverContext { @Statistic(messageKey="DirectionChange", resultDecimals=2, ordinal=2) Double getDirectionChangeInDegrees(); + + @Statistic(messageKey="AbsoluteDirectionChange", resultDecimals=2, ordinal=2) + Double getAbsoluteDirectionChangeInDegrees(); + + @Dimension(messageKey="ToSide") + NauticalSide getToSide(); @Statistic(messageKey="ManeuverLoss", resultDecimals=0, ordinal=3) Distance getManeuverLoss(); diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasMarkPassingContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasMarkPassingContext.java index 8712a394a45..1c1d1c3fb97 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasMarkPassingContext.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasMarkPassingContext.java @@ -4,18 +4,14 @@ import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.common.NauticalSide; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.Tack; -import com.sap.sailing.domain.tracking.MarkPassingManeuver; import com.sap.sse.datamining.annotations.Connector; import com.sap.sse.datamining.annotations.Dimension; import com.sap.sse.datamining.annotations.Statistic; -public interface HasMarkPassingContext { - +public interface HasMarkPassingContext extends HasWindOnTrackedLeg, HasManeuver, HasTrackedLegOfCompetitor { @Connector(scanForStatistics=false) HasTrackedLegOfCompetitorContext getTrackedLegOfCompetitorContext(); - - MarkPassingManeuver getManeuver(); - + //TODO Clean-Up: // Move Dimensions and Statistics to Maneuver/MarkPassingManeuver and connect to it // Find a way to implement HasManeuverContext and HasMarkPassingManeuver with a base class diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasRaceOfCompetitorContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasRaceOfCompetitorContext.java index 97bde57e8e8..3ce44769cd7 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasRaceOfCompetitorContext.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasRaceOfCompetitorContext.java @@ -16,6 +16,7 @@ public interface HasRaceOfCompetitorContext { public HasTrackedRaceContext getTrackedRaceContext(); @Connector(messageKey="Competitor") + @Statistic(messageKey="") public Competitor getCompetitor(); @Dimension(messageKey="CompetitorSearchTag", ordinal=11) // TODO Clean me: Move Dimension to Competitor when possible diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasTrackedLegOfCompetitor.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasTrackedLegOfCompetitor.java new file mode 100755 index 00000000000..e09b87e5133 --- /dev/null +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasTrackedLegOfCompetitor.java @@ -0,0 +1,5 @@ +package com.sap.sailing.datamining.data; + +public interface HasTrackedLegOfCompetitor { + HasTrackedLegOfCompetitorContext getTrackedLegOfCompetitorContext(); +} diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasTrackedLegOfCompetitorContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasTrackedLegOfCompetitorContext.java index 4f5cb3a3ea9..6b76088883c 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasTrackedLegOfCompetitorContext.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasTrackedLegOfCompetitorContext.java @@ -8,8 +8,7 @@ import com.sap.sse.datamining.annotations.Dimension; import com.sap.sse.datamining.annotations.Statistic; import com.sap.sse.datamining.shared.impl.dto.ClusterDTO; -public interface HasTrackedLegOfCompetitorContext { - +public interface HasTrackedLegOfCompetitorContext extends HasWindOnTrackedLeg { @Connector(scanForStatistics=false) public HasTrackedLegContext getTrackedLegContext(); diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasWind.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasWind.java new file mode 100755 index 00000000000..b7e817bca64 --- /dev/null +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasWind.java @@ -0,0 +1,23 @@ +package com.sap.sailing.datamining.data; + +import java.util.Locale; + +import com.sap.sailing.datamining.Activator; +import com.sap.sailing.domain.common.Positioned; +import com.sap.sailing.domain.common.Wind; +import com.sap.sse.common.Timed; +import com.sap.sse.datamining.annotations.Dimension; +import com.sap.sse.datamining.data.Cluster; +import com.sap.sse.datamining.shared.impl.dto.ClusterDTO; +import com.sap.sse.i18n.ResourceBundleStringMessages; + +public interface HasWind extends Positioned, Timed { + Wind getWind(); + + @Dimension(messageKey="WindStrengthInBeaufort", ordinal=11) + default ClusterDTO getWindStrengthAsBeaufortCluster(Locale locale, ResourceBundleStringMessages stringMessages) { + Wind wind = getWind(); + Cluster cluster = Activator.getClusterGroups().getWindStrengthInBeaufortClusterGroup().getClusterFor(wind); + return new ClusterDTO(cluster.asLocalizedString(locale, stringMessages)); + } +} diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasWindFixContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasWindFixContext.java new file mode 100755 index 00000000000..4235fdd3089 --- /dev/null +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasWindFixContext.java @@ -0,0 +1,35 @@ +package com.sap.sailing.datamining.data; + +import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.Wind; +import com.sap.sse.common.TimePoint; +import com.sap.sse.datamining.annotations.Connector; +import com.sap.sse.datamining.annotations.Dimension; +import com.sap.sse.datamining.annotations.Statistic; + +public interface HasWindFixContext extends HasWind { + @Connector(scanForStatistics=false) + public HasTrackedRaceContext getTrackedRaceContext(); + + @Statistic(messageKey="") + public Wind getWind(); + + @Statistic(messageKey="windFrom", resultDecimals=1) + double getWindFromDegrees(); + + @Statistic(messageKey="windSpeedKnots", resultDecimals=1) + double getWindSpeedInKnots(); + + @Override + default TimePoint getTimePoint() { + return getWind().getTimePoint(); + } + + @Override + default Position getPosition() { + return getWind().getPosition(); + } + + @Dimension(messageKey="WindSourceType") + String getWindSourceType(); +} diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasWindOnTrackedLeg.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasWindOnTrackedLeg.java new file mode 100755 index 00000000000..bca85948b78 --- /dev/null +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasWindOnTrackedLeg.java @@ -0,0 +1,17 @@ +package com.sap.sailing.datamining.data; + +import com.sap.sailing.domain.common.Wind; + +public interface HasWindOnTrackedLeg extends HasTrackedLegOfCompetitor, HasWind { + default Wind getWind() { + if (getWindInternal() == null) { + setWindInternal(getTrackedLegOfCompetitorContext().getTrackedLegContext().getTrackedRaceContext().getTrackedRace() + .getWind(getPosition(), getTimePoint())); + } + return getWindInternal(); + } + + Wind getWindInternal(); + + void setWindInternal(Wind wind); +} diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasWindTrackContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasWindTrackContext.java new file mode 100755 index 00000000000..4a1a4ad41af --- /dev/null +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasWindTrackContext.java @@ -0,0 +1,18 @@ +package com.sap.sailing.datamining.data; + +import com.sap.sailing.domain.tracking.WindTrack; +import com.sap.sse.datamining.annotations.Connector; +import com.sap.sse.datamining.annotations.Dimension; + +public interface HasWindTrackContext { + @Connector(scanForStatistics=false) + public HasTrackedRaceContext getTrackedRaceContext(); + + @Dimension(messageKey="WindSourceType") + String getWindSourceType(); + + @Dimension(messageKey="WindSourceName") + String getWindSourceName(); + + WindTrack getWindTrack(); +} diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/WindFixRetrievalProcessor.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/WindFixRetrievalProcessor.java new file mode 100755 index 00000000000..ae337f4561d --- /dev/null +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/WindFixRetrievalProcessor.java @@ -0,0 +1,44 @@ +package com.sap.sailing.datamining.impl.components; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.ExecutorService; + +import com.sap.sailing.datamining.data.HasWindFixContext; +import com.sap.sailing.datamining.data.HasWindTrackContext; +import com.sap.sailing.datamining.impl.data.WindFixWithContext; +import com.sap.sailing.domain.common.Wind; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.domain.tracking.WindTrack; +import com.sap.sse.datamining.components.Processor; +import com.sap.sse.datamining.impl.components.AbstractRetrievalProcessor; + +/** + * Retrieves all wind fixes from a {@link TrackedRace} that are provided by wind sources that are not currently + * {@link TrackedRace#getWindSourcesToExclude() excluded}. + * + * @author Axel Uhl (d043530) + * + */ +public class WindFixRetrievalProcessor extends AbstractRetrievalProcessor { + + public WindFixRetrievalProcessor(ExecutorService executor, Collection> resultReceivers, int retrievalLevel) { + super(HasWindTrackContext.class, HasWindFixContext.class, executor, resultReceivers, retrievalLevel); + } + + @Override + protected Iterable retrieveData(HasWindTrackContext element) { + Collection windFixesWithContext = new ArrayList<>(); + final WindTrack windTrack = element.getWindTrack(); + windTrack.lockForRead(); + try { + for (final Wind wind : windTrack.getFixes()) { + windFixesWithContext.add(new WindFixWithContext(element.getTrackedRaceContext(), wind, element.getWindSourceType())); + } + } finally { + windTrack.unlockAfterRead(); + } + return windFixesWithContext; + } + +} diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/WindTrackRetrievalProcessor.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/WindTrackRetrievalProcessor.java new file mode 100755 index 00000000000..ef9b8514098 --- /dev/null +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/WindTrackRetrievalProcessor.java @@ -0,0 +1,42 @@ +package com.sap.sailing.datamining.impl.components; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.ExecutorService; + +import com.sap.sailing.datamining.data.HasTrackedRaceContext; +import com.sap.sailing.datamining.data.HasWindTrackContext; +import com.sap.sailing.datamining.impl.data.WindTrackWithContext; +import com.sap.sailing.domain.common.WindSource; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.domain.tracking.WindTrack; +import com.sap.sse.datamining.components.Processor; +import com.sap.sse.datamining.impl.components.AbstractRetrievalProcessor; + +/** + * Retrieves all wind fixes from a {@link TrackedRace} that are provided by wind sources that are not currently + * {@link TrackedRace#getWindSourcesToExclude() excluded}. + * + * @author Axel Uhl (d043530) + * + */ +public class WindTrackRetrievalProcessor extends AbstractRetrievalProcessor { + + public WindTrackRetrievalProcessor(ExecutorService executor, Collection> resultReceivers, int retrievalLevel) { + super(HasTrackedRaceContext.class, HasWindTrackContext.class, executor, resultReceivers, retrievalLevel); + } + + @Override + protected Iterable retrieveData(HasTrackedRaceContext element) { + Collection windTracksWithContext = new ArrayList<>(); + final TrackedRace trackedRace = element.getTrackedRace(); + for (final WindSource windSource : trackedRace.getWindSources()) { + if (!trackedRace.getWindSourcesToExclude().contains(windSource)) { + final WindTrack windTrack = trackedRace.getOrCreateWindTrack(windSource); + windTracksWithContext.add(new WindTrackWithContext(element, windTrack, windSource)); + } + } + return windTracksWithContext; + } + +} diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/aggregators/ParallelBearingAverageDegreesAggregationProcessor.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/aggregators/ParallelBearingAverageDegreesAggregationProcessor.java new file mode 100755 index 00000000000..9907b3fcc01 --- /dev/null +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/aggregators/ParallelBearingAverageDegreesAggregationProcessor.java @@ -0,0 +1,56 @@ +package com.sap.sailing.datamining.impl.components.aggregators; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ExecutorService; + +import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.BearingCluster; +import com.sap.sse.datamining.components.AggregationProcessorDefinition; +import com.sap.sse.datamining.components.Processor; +import com.sap.sse.datamining.impl.components.GroupedDataEntry; +import com.sap.sse.datamining.impl.components.SimpleAggregationProcessorDefinition; +import com.sap.sse.datamining.impl.components.aggregators.AbstractParallelGroupedDataStoringAggregationProcessor; +import com.sap.sse.datamining.shared.GroupKey; + +public class ParallelBearingAverageDegreesAggregationProcessor + extends AbstractParallelGroupedDataStoringAggregationProcessor { + + private static final AggregationProcessorDefinition DEFINITION = + new SimpleAggregationProcessorDefinition<>(Bearing.class, Double.class, "Average", ParallelBearingAverageDegreesAggregationProcessor.class); + + private final Map results; + + public static AggregationProcessorDefinition getDefinition() { + return DEFINITION; + } + + public ParallelBearingAverageDegreesAggregationProcessor(ExecutorService executor, + Collection, ?>> resultReceivers) { + super(executor, resultReceivers, "Average"); + results = new HashMap<>(); + } + + @Override + protected void storeElement(GroupedDataEntry element) { + BearingCluster cluster = results.get(element.getKey()); + if (cluster == null) { + cluster = new BearingCluster(); + results.put(element.getKey(), cluster); + } + cluster.add(element.getDataEntry()); + } + + @Override + protected Map aggregateResult() { + Map result = new HashMap<>(); + for (Entry clusterEntry : results.entrySet()) { + GroupKey key = clusterEntry.getKey(); + result.put(key, clusterEntry.getValue().getAverage().getDegrees()); + } + return result; + } + +} diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/aggregators/ParallelDistanceAverageAggregationProcessor.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/aggregators/ParallelDistanceAverageAggregationProcessor.java index 0f2e531e15d..de4acb69a79 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/aggregators/ParallelDistanceAverageAggregationProcessor.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/aggregators/ParallelDistanceAverageAggregationProcessor.java @@ -7,7 +7,6 @@ import java.util.Map.Entry; import java.util.concurrent.ExecutorService; import com.sap.sailing.domain.common.Distance; -import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sse.datamining.components.AggregationProcessorDefinition; import com.sap.sse.datamining.components.Processor; import com.sap.sse.datamining.impl.components.GroupedDataEntry; @@ -56,7 +55,7 @@ public class ParallelDistanceAverageAggregationProcessor Map sumAggregation = sumAggregationProcessor.getResult(); for (Entry sumAggregationEntry : sumAggregation.entrySet()) { GroupKey key = sumAggregationEntry.getKey(); - result.put(key, new MeterDistance(sumAggregationEntry.getValue().getMeters() / elementAmountPerKey.get(key).doubleValue())); + result.put(key, sumAggregationEntry.getValue().scale(1. / elementAmountPerKey.get(key).doubleValue())); } return result; } diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/aggregators/ParallelDistanceSumAggregationProcessor.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/aggregators/ParallelDistanceSumAggregationProcessor.java index 336aa6a59de..b5d067050d1 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/aggregators/ParallelDistanceSumAggregationProcessor.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/aggregators/ParallelDistanceSumAggregationProcessor.java @@ -44,5 +44,4 @@ public class ParallelDistanceSumAggregationProcessor extends protected Map getResult() { return results; } - } diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/GPSFixWithContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/GPSFixWithContext.java index 65830923d3e..d68771d9e96 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/GPSFixWithContext.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/GPSFixWithContext.java @@ -1,18 +1,13 @@ package com.sap.sailing.datamining.impl.data; -import java.util.Locale; - -import com.sap.sailing.datamining.Activator; import com.sap.sailing.datamining.data.HasGPSFixContext; import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext; import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.common.tracking.GPSFixMoving; -import com.sap.sse.datamining.data.Cluster; -import com.sap.sse.datamining.shared.impl.dto.ClusterDTO; -import com.sap.sse.i18n.ResourceBundleStringMessages; public class GPSFixWithContext implements HasGPSFixContext { - + private static final long serialVersionUID = -4537126043228674949L; + private final HasTrackedLegOfCompetitorContext trackedLegOfCompetitorContext; private final GPSFixMoving gpsFix; @@ -32,20 +27,14 @@ public class GPSFixWithContext implements HasGPSFixContext { public GPSFixMoving getGPSFix() { return gpsFix; } - - @Override - public ClusterDTO getWindStrengthAsBeaufortCluster(Locale locale, ResourceBundleStringMessages stringMessages) { - Wind wind = getWind(); - Cluster cluster = Activator.getClusterGroups().getWindStrengthInBeaufortClusterGroup().getClusterFor(wind); - return new ClusterDTO(cluster.asLocalizedString(locale, stringMessages)); - } - private Wind getWind() { - if (wind == null) { - wind = getTrackedLegOfCompetitorContext().getTrackedLegContext().getTrackedRaceContext().getTrackedRace() - .getWind(gpsFix.getPosition(), gpsFix.getTimePoint()); - } + @Override + public Wind getWindInternal() { return wind; } - + + @Override + public void setWindInternal(Wind wind) { + this.wind = wind; + } } \ No newline at end of file diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/ManeuverWithContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/ManeuverWithContext.java index 5d1b975b27b..65d82a7772d 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/ManeuverWithContext.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/ManeuverWithContext.java @@ -4,14 +4,17 @@ import com.sap.sailing.datamining.data.HasManeuverContext; import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext; import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.ManeuverType; +import com.sap.sailing.domain.common.NauticalSide; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.Tack; +import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.tracking.Maneuver; public class ManeuverWithContext implements HasManeuverContext { - + private static final long serialVersionUID = 7717196485074392156L; private final HasTrackedLegOfCompetitorContext trackedLegOfCompetitor; private final Maneuver maneuver; + private Wind wind; public ManeuverWithContext(HasTrackedLegOfCompetitorContext trackedLegOfCompetitor, Maneuver maneuver) { this.trackedLegOfCompetitor = trackedLegOfCompetitor; @@ -53,9 +56,28 @@ public class ManeuverWithContext implements HasManeuverContext { return getManeuver().getDirectionChangeInDegrees(); } + @Override + public Double getAbsoluteDirectionChangeInDegrees() { + return Math.abs(getManeuver().getDirectionChangeInDegrees()); + } + + @Override + public NauticalSide getToSide() { + return getManeuver().getDirectionChangeInDegrees() >= 0 ? NauticalSide.STARBOARD : NauticalSide.PORT; + } + @Override public Distance getManeuverLoss() { return getManeuver().getManeuverLoss(); } + @Override + public Wind getWindInternal() { + return wind; + } + + @Override + public void setWindInternal(Wind wind) { + this.wind = wind; + } } diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/MarkPassingWithContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/MarkPassingWithContext.java index 442faad5dc8..090e8a20a1e 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/MarkPassingWithContext.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/MarkPassingWithContext.java @@ -7,18 +7,20 @@ import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.common.NauticalSide; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.Tack; +import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.tracking.MarkPassingManeuver; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sse.common.Util; public class MarkPassingWithContext implements HasMarkPassingContext { - + private static final long serialVersionUID = -337042113749307686L; private final HasTrackedLegOfCompetitorContext trackedLegOfCompetitor; private final MarkPassingManeuver maneuver; private Double absoluteRank; private boolean rankHasBeenInitialized; + private Wind wind; public MarkPassingWithContext(HasTrackedLegOfCompetitorContext trackedLegOfCompetitor, MarkPassingManeuver maneuver) { this.trackedLegOfCompetitor = trackedLegOfCompetitor; @@ -84,4 +86,13 @@ public class MarkPassingWithContext implements HasMarkPassingContext { return absoluteRank; } + @Override + public Wind getWindInternal() { + return wind; + } + + @Override + public void setWindInternal(Wind wind) { + this.wind = wind; + } } diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/TrackedLegOfCompetitorWithContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/TrackedLegOfCompetitorWithContext.java index 09e719dbe9d..80b3f9f67fa 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/TrackedLegOfCompetitorWithContext.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/TrackedLegOfCompetitorWithContext.java @@ -6,7 +6,10 @@ import com.sap.sailing.datamining.data.HasTrackedLegContext; import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.common.Distance; +import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.leaderboard.Leaderboard; +import com.sap.sailing.domain.tracking.TrackedLeg; import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sse.common.TimePoint; @@ -15,6 +18,7 @@ import com.sap.sse.datamining.data.Cluster; import com.sap.sse.datamining.shared.impl.dto.ClusterDTO; public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompetitorContext { + private static final long serialVersionUID = 5944904146286262768L; private final HasTrackedLegContext trackedLegContext; @@ -25,6 +29,7 @@ public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompeti private boolean isRankAtStartInitialized; private Double rankAtFinish; private boolean isRankAtFinishInitialized; + private Wind wind; public TrackedLegOfCompetitorWithContext(HasTrackedLegContext trackedLegContext, TrackedLegOfCompetitor trackedLegOfCompetitor) { this.trackedLegContext = trackedLegContext; @@ -120,5 +125,52 @@ public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompeti return (finishTime.asMillis() - startTime.asMillis()) / 1000; } + + @Override + public Wind getWindInternal() { + return wind; + } + + @Override + public void setWindInternal(Wind wind) { + this.wind = wind; + } + + @Override + public Position getPosition() { + final TrackedLeg trackedLeg = getTrackedLegContext().getTrackedLeg(); + final TrackedRace trackedRace = trackedLeg.getTrackedRace(); + final TimePoint timepoint = getTimePointBetweenLegStartAndLegFinish(trackedRace); + final Position result; + if (timepoint == null) { + result = null; + } else { + result = trackedLeg.getMiddleOfLeg(timepoint); + } + return result; + } + + private TimePoint getTimePointBetweenLegStartAndLegFinish(final TrackedRace trackedRace) { + final TimePoint competitorLegStartTime = getTrackedLegOfCompetitor().getStartTime(); + final TimePoint competitorLegEndTime = getTrackedLegOfCompetitor().getFinishTime(); + final TimePoint startTime = competitorLegStartTime != null ? competitorLegStartTime : + trackedRace.getStartOfRace() != null ? trackedRace.getStartOfRace() : trackedRace.getStartOfTracking(); + final TimePoint endTime = competitorLegEndTime != null ? competitorLegEndTime : + trackedRace.getEndOfRace() != null ? trackedRace.getEndOfRace() : trackedRace.getEndOfTracking(); + final TimePoint timepoint = endTime == null ? startTime : startTime == null ? null : startTime.plus(startTime.until(endTime).divide(2)); + return timepoint; + } + + @Override + public TimePoint getTimePoint() { + final TrackedLeg trackedLeg = getTrackedLegContext().getTrackedLeg(); + final TrackedRace trackedRace = trackedLeg.getTrackedRace(); + return getTimePointBetweenLegStartAndLegFinish(trackedRace); + } + + @Override + public HasTrackedLegOfCompetitorContext getTrackedLegOfCompetitorContext() { + return this; + } } diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/WindFixWithContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/WindFixWithContext.java new file mode 100755 index 00000000000..2a8f42a3f98 --- /dev/null +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/WindFixWithContext.java @@ -0,0 +1,52 @@ +package com.sap.sailing.datamining.impl.data; + +import com.sap.sailing.datamining.data.HasTrackedRaceContext; +import com.sap.sailing.datamining.data.HasWindFixContext; +import com.sap.sailing.domain.common.Wind; + +public class WindFixWithContext implements HasWindFixContext { + private static final long serialVersionUID = -4537126043228674949L; + + private final HasTrackedRaceContext trackedRaceContext; + + private final String windSourceType; + + private final Wind wind; + + public WindFixWithContext(HasTrackedRaceContext trackedRaceContext, Wind wind, String windSourceType) { + this.trackedRaceContext = trackedRaceContext; + this.wind = wind; + this.windSourceType = windSourceType; + } + + @Override + public HasTrackedRaceContext getTrackedRaceContext() { + return trackedRaceContext; + } + + @Override + public Wind getWind() { + return wind; + } + + @Override + public double getWindFromDegrees() { + return getWind().getFrom().getDegrees(); + } + + @Override + public double getWindSpeedInKnots() { + return getWind().getKnots(); + } + + @Override + public String getWindSourceType() { + return windSourceType; + } + + @Override + public String toString() { + return "WindFixWithContext [trackedRaceContext=" + trackedRaceContext + ", windSourceType=" + windSourceType + + ", wind=" + wind + "]"; + } +} diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/WindTrackWithContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/WindTrackWithContext.java new file mode 100755 index 00000000000..1c7291c0551 --- /dev/null +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/WindTrackWithContext.java @@ -0,0 +1,51 @@ +package com.sap.sailing.datamining.impl.data; + +import com.sap.sailing.datamining.data.HasTrackedRaceContext; +import com.sap.sailing.datamining.data.HasWindTrackContext; +import com.sap.sailing.domain.common.WindSource; +import com.sap.sailing.domain.tracking.WindTrack; + +public class WindTrackWithContext implements HasWindTrackContext { + + private final HasTrackedRaceContext trackedRaceContext; + + private final String windSourceType; + + private final String windSourceName; + + private final WindTrack windTrack; + + public WindTrackWithContext(HasTrackedRaceContext trackedRaceContext, WindTrack windTrack, WindSource windSource) { + this.trackedRaceContext = trackedRaceContext; + this.windTrack = windTrack; + this.windSourceType = windSource.getType().name(); + this.windSourceName = windSource.getType().name()+(windSource.getId()==null?"":" ("+windSource.getId()+")"); + } + + @Override + public HasTrackedRaceContext getTrackedRaceContext() { + return trackedRaceContext; + } + + @Override + public String getWindSourceType() { + return windSourceType; + } + + @Override + public String toString() { + return "WindTrackWithContext [trackedRaceContext=" + trackedRaceContext + ", windSourceType=" + windSourceType + + ", windSourceName=" + windSourceName + "]"; + } + + @Override + public String getWindSourceName() { + return windSourceName; + } + + @Override + public WindTrack getWindTrack() { + return windTrack; + } + +} diff --git a/java/com.sap.sailing.declination.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.declination.test/META-INF/MANIFEST.MF index 4274aede4e5..471e6294cc2 100644 --- a/java/com.sap.sailing.declination.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.declination.test/META-INF/MANIFEST.MF @@ -6,7 +6,7 @@ Bundle-Version: 1.0.0.qualifier Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Fragment-Host: com.sap.sailing.declination -Require-Bundle: org.junit4;bundle-version="4.8.2" +Require-Bundle: org.junit;bundle-version="4.8.2" Import-Package: com.sap.sailing.domain.common, com.sap.sailing.domain.common.impl, com.sap.sailing.domain.common.quadtree diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BoatClassMasterdata.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BoatClassMasterdata.java index 0bf539a6b6a..cb7839f8336 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BoatClassMasterdata.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BoatClassMasterdata.java @@ -16,7 +16,7 @@ public enum BoatClassMasterdata { _49ER ("49er", true, 4.88, 1.93, BoatHullType.MONOHULL, true), _49ERFX ("49erFX", true, 4.88, 1.93, BoatHullType.MONOHULL, true, "49FX"), _420 ("420", true, 4.20, 1.65, BoatHullType.MONOHULL, true, "420er", "420M", "420W"), - _470 ("470", true, 4.70, 1.68, BoatHullType.MONOHULL, true, "470er", "470M", "470W"), + _470 ("470", true, 4.70, 1.68, BoatHullType.MONOHULL, true, "470er", "470M", "470W", "470 - M", "470 - W"), _5O5 ("5O5", true, 5.03, 1.88, BoatHullType.MONOHULL, true, "505", "5o5", "505er", "5o5er"), _5_5MR ("5.5mR", true, 9.50, 1.92, BoatHullType.MONOHULL, true, "5.5 Meter", "5.5 Metre", "5.5", "5.5M", "5.5-metre", "5.5 metre"), A_CAT ("A-Catamaran", true, 5.49, 2.30, BoatHullType.CATAMARAN, false, "A-Cat", "ACat", "A-Class Catamaran"), @@ -85,7 +85,7 @@ public enum BoatClassMasterdata { RS500 ("RS 500", true, 4.34, 1.58, BoatHullType.MONOHULL, true, "RS500", "RS_500"), RS800 ("RS 800", true, 4.80, 1.88, BoatHullType.MONOHULL, true, "RS800", "RS_800"), RS_AERO ("RS Aero", true, 4.00, 1.40, BoatHullType.MONOHULL, false, "RSAERO", "RS_Aero"), - RS_X ("RS:X", true, 2.86, 0.93, BoatHullType.SURFERBOARD, false, "RS-X", "RSX", "RS:X"), + RS_X ("RS:X", true, 2.86, 0.93, BoatHullType.SURFERBOARD, false, "RS-X", "RSX", "RS:X", "RS:X Men", "RS:X Woman"), RS_FEVA ("RS Feva", true, 3.64, 1.42, BoatHullType.MONOHULL, true, "RSFeva"), SONAR ("Sonar", true, 7.01, 2.39, BoatHullType.MONOHULL, true), SOLING ("Soling", true, 8.15, 1.91, BoatHullType.MONOHULL, true), @@ -100,6 +100,7 @@ public enum BoatClassMasterdata { TORNADO ("Tornado Catamaran", true, 6.10, 3.02, BoatHullType.CATAMARAN, true, "Tornado", "Tornado Cat"), TRIAS ("Trias", true, 9.20, 2.12, BoatHullType.MONOHULL, true), VIPER_640 ("Viper 640", true, 6.43, 2.49, BoatHullType.MONOHULL, true), + VO60 ("VO60", true, 19.5, 5.25, BoatHullType.MONOHULL, true, "Volvo Ocean 60", "VolvoOcean60", "W60", "Whitbread60", "Whitbread 60"), VX_ONE ("VX ONE", true, 5.79, 2.19, BoatHullType.MONOHULL, true, "VX-ONE"), X_99 ("X-99", true, 9.96, 2.95, BoatHullType.MONOHULL, true, "X99"), diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DataImportSubProgress.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DataImportSubProgress.java index 444b97bc7de..7d8801dbdec 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DataImportSubProgress.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DataImportSubProgress.java @@ -58,6 +58,12 @@ public enum DataImportSubProgress { return messages.importWindTracks(); } }, + IMPORT_SENSOR_FIXES { + @Override + public String getMessage(CommonStringMessages messages) { + return messages.importSensorFixes(); + } + }, UPDATE_EVENT_LEADERBOARD_GROUP_LINKS { @Override public String getMessage(CommonStringMessages messages) { diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/LegType.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/LegType.java index a2f227c8ab1..478e193eb0f 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/LegType.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/LegType.java @@ -1,3 +1,6 @@ package com.sap.sailing.domain.common; -public enum LegType { UPWIND, DOWNWIND, REACHING } \ No newline at end of file +public enum LegType { UPWIND, DOWNWIND, REACHING; + + public static double UPWIND_DOWNWIND_TOLERANCE_IN_DEG=45.; // TracTrac does 22.5, Marcus Baur suggest 40; Nils Schröder suggests 60 +} \ No newline at end of file diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Moving.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Moving.java index 40e9627ddbf..5f8c13504dd 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Moving.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Moving.java @@ -3,6 +3,6 @@ package com.sap.sailing.domain.common; import com.sap.sse.datamining.annotations.Connector; public interface Moving { - @Connector(messageKey="Speed") + @Connector(messageKey="") SpeedWithBearing getSpeed(); } diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Speed.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Speed.java index 74ead26ede8..69b2dad35e1 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Speed.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/Speed.java @@ -67,7 +67,7 @@ public interface Speed extends Comparable, Serializable { } }; - @Statistic(messageKey="", resultDecimals=2) + @Statistic(messageKey="speedInKnots", resultDecimals=2) double getKnots(); double getMetersPerSecond(); diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/SpeedWithBearing.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/SpeedWithBearing.java index afda577f31a..d3de796790d 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/SpeedWithBearing.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/SpeedWithBearing.java @@ -1,10 +1,12 @@ package com.sap.sailing.domain.common; import com.sap.sse.common.TimePoint; +import com.sap.sse.datamining.annotations.Statistic; public interface SpeedWithBearing extends Speed { + @Statistic(messageKey="bearing", resultDecimals=1) Bearing getBearing(); /** diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/TargetTimeInfo.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/TargetTimeInfo.java new file mode 100755 index 00000000000..2c1af7b0cc1 --- /dev/null +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/TargetTimeInfo.java @@ -0,0 +1,92 @@ +package com.sap.sailing.domain.common; + +import com.sap.sse.common.Duration; +import com.sap.sse.common.TimePoint; + +/** + * Information about the time competitors of a race are expected to need for the legs of a race. This is based on a + * {@link TrackedRace} with {@link TrackedLeg}s and + * {@link TrackedRace#getWind(com.sap.sailing.domain.common.Position, com.sap.sse.common.TimePoint) wind information}. + * Furthermore, a valid {@link PolarDataService} instance must be bound to the {@link TrackedRace} to make predictions + * possible. + *

+ * + * In order to give a rationale for the response, in addition to the total expected duration the expected durations for + * each leg are specified, together with the {@link Wind} data used for the leg, leg distance, the true wind angle for + * the leg's direction and the leg type that this leads to. + * + * @author Axel Uhl (d043530) + * + */ +public interface TargetTimeInfo { + public interface LegTargetTimeInfo { + /** + * The distance between the waypoints delimiting the leg, at the {@link #getExpectedStartTimePoint() expected + * time point} at which the competitor starts the leg. + */ + Distance getDistance(); + + /** + * True wind at the position in the middle between the two positions at which the waypoints delimiting + * the leg are at {@link #getExpectedStartTimePoint()}. + */ + Wind getWind(); + + /** + * The bearing from the leg's start waypoint to the leg's end waypoint at {@link #getExpectedStartTimePoint()}/ + */ + Bearing getLegBearing(); + + /** + * The angle between the wind direction (from) and the bearing from the leg's start waypoint position at + * {@link #getExpectedStartTimePoint()} and the leg's end waypoint position at + * {@link #getExpectedStartTimePoint()}. For example, if a windward mark is a few meters off to the left, + * viewed from the leeward start line, then the wind comes a little more from the starboard side compared + * to the course middle line, therefore would have a true wind angle to the leg of, say 5deg. + */ + Bearing getTrueWindAngleToLeg(); + + /** + * The time a competitor is expected to need to sail the leg in the {@link #getWind() wind conditions expected}. + */ + Duration getExpectedDuration(); + + /** + * The time point when the competitor is expected to enter this leg. For the race's first leg + * this equals the {@link TargetTimeInfo#getExpectedStartTimePoint()} result. + */ + TimePoint getExpectedStartTimePoint(); + + /** + * The type of the leg at {@link #getExpectedStartTimePoint()}, given the {@link #getWind() wind conditions} + * and the {@link #getLegBearing() leg bearing} at that point in time. + */ + LegType getLegType(); + } + + /** + * The time point when a competitor whose hypothetical race is described + * by this object starts. This time is the basis for determining wind angles + * as input for the polar / VPP calculations. Wind is taken on each leg + * during the expected time at which the competitor starts that leg. + */ + TimePoint getExpectedStartTimePoint(); + + /** + * The expected total duration; equals the sum of the {@link LegTargetTimeInfo#getExpectedDuration() leg durations} + * as aggregated across {@link #getLegTargetTimes()}. + */ + Duration getExpectedDuration(); + + /** + * The per-leg information about the expected hypothetical sail around the course. The element + * order matches that of the {@link Course#getLegs() legs in the race course}. + */ + Iterable getLegTargetTimes(); + + /** + * The {@link Duration#plus(Duration) sum} of {@link LegTargetTimeInfo#getExpectedDuration() expected leg durations} + * across all legs of type {@code spentInLegsOfType}. + */ + Duration getExpectedDuration(LegType spentInLegsOfType); +} diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/abstractlog/TimePointSpecificationFoundInLogImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/abstractlog/TimePointSpecificationFoundInLogImpl.java index f14d3650a26..d3306a5d835 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/abstractlog/TimePointSpecificationFoundInLogImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/abstractlog/TimePointSpecificationFoundInLogImpl.java @@ -17,4 +17,29 @@ public class TimePointSpecificationFoundInLogImpl implements TimePointSpecificat public TimePoint getTimePoint() { return timePoint; } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((timePoint == null) ? 0 : timePoint.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + TimePointSpecificationFoundInLogImpl other = (TimePointSpecificationFoundInLogImpl) obj; + if (timePoint == null) { + if (other.timePoint != null) + return false; + } else if (!timePoint.equals(other.timePoint)) + return false; + return true; + } } diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/BoatClassDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/BoatClassDTO.java index b4e6fdc1cd4..02a33457d7b 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/BoatClassDTO.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/BoatClassDTO.java @@ -14,24 +14,28 @@ public class BoatClassDTO extends NamedDTO implements Serializable, Comparable legInfos; + + public static class LegTargetTimeInfoImpl implements LegTargetTimeInfo { + private final Distance distance; + private final Wind wind; + private final Bearing legBearing; + private final Duration expectedDuration; + private final TimePoint expectedStartTimePoint; + private final LegType legType; + + public LegTargetTimeInfoImpl(Distance distance, Wind wind, Bearing legBearing, Duration expectedDuration, + TimePoint expectedStartTimePoint, LegType legType) { + super(); + this.distance = distance; + this.wind = wind; + this.legBearing = legBearing; + this.expectedDuration = expectedDuration; + this.expectedStartTimePoint = expectedStartTimePoint; + this.legType = legType; + } + + @Override + public Distance getDistance() { + return distance; + } + + @Override + public Wind getWind() { + return wind; + } + + @Override + public Bearing getLegBearing() { + return legBearing; + } + + @Override + public Bearing getTrueWindAngleToLeg() { + return getLegBearing().getDifferenceTo(getWind().getBearing().reverse()); + } + + @Override + public Duration getExpectedDuration() { + return expectedDuration; + } + + @Override + public TimePoint getExpectedStartTimePoint() { + return expectedStartTimePoint; + } + + @Override + public LegType getLegType() { + return legType; + } + + @Override + public String toString() { + return "LegTargetTimeInfoImpl [distance=" + distance + ", wind=" + wind + ", legBearing=" + legBearing + + ", expectedDuration=" + expectedDuration + ", expectedStartTimePoint=" + expectedStartTimePoint + + ", legType=" + legType + "]"; + } + } + + public TargetTimeInfoImpl(Iterable legInfos) { + super(); + this.legInfos = legInfos; + } + + @Override + public TimePoint getExpectedStartTimePoint() { + return Util.isEmpty(legInfos) ? null : legInfos.iterator().next().getExpectedStartTimePoint(); + } + + @Override + public Duration getExpectedDuration() { + Duration result = Duration.NULL; + for (final LegTargetTimeInfo legInfo : legInfos) { + result = result.plus(legInfo.getExpectedDuration()); + } + return result; + } + + @Override + public Iterable getLegTargetTimes() { + return legInfos; + } + + @Override + public Duration getExpectedDuration(final LegType spentInLegsOfType) { + Duration result = Duration.NULL; + for (final LegTargetTimeInfo legInfo : legInfos) { + if (legInfo.getLegType() == spentInLegsOfType) { + result = result.plus(legInfo.getExpectedDuration()); + } + } + return result; + } + + @Override + public String toString() { + return "TargetTimeInfoImpl [legInfos=" + legInfos + "]"; + } +} diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/security/Permission.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/security/Permission.java index d03bfa17191..b0e3272131a 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/security/Permission.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/security/Permission.java @@ -37,6 +37,7 @@ public enum Permission implements com.sap.sse.security.shared.Permission { LEADERBOARD, LEADERBOARD_GROUP, TRACKED_RACE, + DATA_MINING, ; // TODO once we can use Java8 here, move this up into a "default" method on the Permission interface diff --git a/java/com.sap.sailing.domain.deckmanadapter.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.deckmanadapter.test/META-INF/MANIFEST.MF index b67aed88b96..d2447cc1845 100644 --- a/java/com.sap.sailing.domain.deckmanadapter.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.deckmanadapter.test/META-INF/MANIFEST.MF @@ -6,4 +6,4 @@ Bundle-Version: 1.0.0.qualifier Bundle-Vendor: SAP Fragment-Host: com.sap.sailing.domain.deckmanadapter Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Require-Bundle: org.junit4 +Require-Bundle: org.junit;bundle-version="4.8.2" diff --git a/java/com.sap.sailing.domain.igtimiadapter.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.igtimiadapter.test/META-INF/MANIFEST.MF index f61ce095465..5595d14983e 100644 --- a/java/com.sap.sailing.domain.igtimiadapter.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.igtimiadapter.test/META-INF/MANIFEST.MF @@ -6,7 +6,7 @@ Bundle-SymbolicName: com.sap.sailing.domain.igtimiadapter.test Bundle-Version: 1.0.0.qualifier Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Require-Bundle: org.junit4;bundle-version="4.8.2", +Require-Bundle: org.junit;bundle-version="4.8.2", com.sap.sailing.domain.common, com.sap.sse.mongodb, com.sap.sse.common diff --git a/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/impl/WebSocketServerListTest.java b/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/impl/WebSocketServerListTest.java index 0349411a1a8..83be473e440 100755 --- a/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/impl/WebSocketServerListTest.java +++ b/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/impl/WebSocketServerListTest.java @@ -23,7 +23,7 @@ public class WebSocketServerListTest { @Test public void testWebSocketServerList() throws IllegalStateException, ClientProtocolException, IOException, ParseException, URISyntaxException { final ClientImpl testAppClient = new ClientImpl("7fcdd217e0aa16090edb4ad55b09ec43b2021090e209541fc9b7003c2a2b70c6", - "aa569cf4909bdc7b0e04b11873f3c4ea20687421e010fcc25b771cca9e6f3f9a", "http://127.0.0.1:8888/igtimi/oauth/v1/authorizationcallback"); + "aa569cf4909bdc7b0e04b11873f3c4ea20687421e010fcc25b771cca9e6f3f9a", "http", "127.0.0.1", "8888", "/igtimi/oauth/v1/authorizationcallback"); MongoDBConfiguration mongoTestConfig = MongoDBConfiguration.getDefaultTestConfiguration(); MongoDBService mongoTestService = mongoTestConfig.getService(); final IgtimiConnectionFactoryImpl igtimiConnectionFactory = new IgtimiConnectionFactoryImpl(testAppClient, PersistenceFactory.INSTANCE.getDomainObjectFactory(mongoTestService), diff --git a/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/oauth/SignInTest.java b/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/oauth/SignInTest.java index 46b52a07481..634834a9913 100755 --- a/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/oauth/SignInTest.java +++ b/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/oauth/SignInTest.java @@ -37,7 +37,7 @@ public class SignInTest { IllegalAccessException, ClassCastException, ParseException { // use the credentials of "Another Test App" final Client testAppClient = new ClientImpl("7fcdd217e0aa16090edb4ad55b09ec43b2021090e209541fc9b7003c2a2b70c6", - "aa569cf4909bdc7b0e04b11873f3c4ea20687421e010fcc25b771cca9e6f3f9a", "http://127.0.0.1:8888/igtimi/oauth/v1/authorizationcallback"); + "aa569cf4909bdc7b0e04b11873f3c4ea20687421e010fcc25b771cca9e6f3f9a", "http", "127.0.0.1", "8888", "/igtimi/oauth/v1/authorizationcallback"); MongoDBConfiguration mongoTestConfig = MongoDBConfiguration.getDefaultTestConfiguration(); MongoDBService mongoTestService = mongoTestConfig.getService(); final IgtimiConnectionFactoryImpl igtimiConnectionFactory = new IgtimiConnectionFactoryImpl(testAppClient, PersistenceFactory.INSTANCE.getDomainObjectFactory(mongoTestService), @@ -55,7 +55,7 @@ public class SignInTest { InstantiationException, IllegalAccessException, ClassCastException { // use the credentials of "Another Test App" final Client testAppClient = new ClientImpl("a4cecd8593e12d43a03433a6db0eea243a411749f93c278dce6a26d4804eebd2", - "4d66022d1ec3e2991f8053514495b61cc076ff02d664f0dc8f3df9150c3864ef", "http://1.2.3.4"); + "4d66022d1ec3e2991f8053514495b61cc076ff02d664f0dc8f3df9150c3864ef", "http", "1.2.3.4", null, "/"); MongoDBConfiguration mongoTestConfig = MongoDBConfiguration.getDefaultTestConfiguration(); MongoDBService mongoTestService = mongoTestConfig.getService(); final IgtimiConnectionFactoryImpl igtimiConnectionFactory = new IgtimiConnectionFactoryImpl(testAppClient, PersistenceFactory.INSTANCE.getDomainObjectFactory(mongoTestService), @@ -71,7 +71,7 @@ public class SignInTest { InstantiationException, IllegalAccessException, ClassCastException, ParseException { // use the credentials of "Another Test App" final Client testAppClient = new ClientImpl("a4cecd8593e12d43a03433a6db0eea243a411749f93c278dce6a26d4804eebd2", - "4d66022d1ec3e2991f8053514495b61cc076ff02d664f0dc8f3df9150c3864ef", "http://1.2.3.4"); + "4d66022d1ec3e2991f8053514495b61cc076ff02d664f0dc8f3df9150c3864ef", "http", "1.2.3.4", null, "/"); MongoDBConfiguration mongoTestConfig = MongoDBConfiguration.getDefaultTestConfiguration(); MongoDBService mongoTestService = mongoTestConfig.getService(); final IgtimiConnectionFactory igtimiConnectionFactory = new IgtimiConnectionFactoryImpl(testAppClient, PersistenceFactory.INSTANCE.getDomainObjectFactory(mongoTestService), diff --git a/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/websocket/WebSocketTest.java b/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/websocket/WebSocketTest.java index eb3f52bf7fa..6a16cf8bb4e 100755 --- a/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/websocket/WebSocketTest.java +++ b/java/com.sap.sailing.domain.igtimiadapter.test/src/com/sap/sailing/domain/igtimiadapter/websocket/WebSocketTest.java @@ -155,7 +155,7 @@ public class WebSocketTest { public void testWebSocketConnect() throws Exception { final List allFixesReceived = new ArrayList<>(); final Client client = new ClientImpl("7fcdd217e0aa16090edb4ad55b09ec43b2021090e209541fc9b7003c2a2b70c6", - "aa569cf4909bdc7b0e04b11873f3c4ea20687421e010fcc25b771cca9e6f3f9a", "http://127.0.0.1:8888/igtimi/oauth/v1/authorizationcallback"); + "aa569cf4909bdc7b0e04b11873f3c4ea20687421e010fcc25b771cca9e6f3f9a", "http", "127.0.0.1", "8888", "/igtimi/oauth/v1/authorizationcallback"); MongoDBConfiguration mongoTestConfig = MongoDBConfiguration.getDefaultTestConfiguration(); MongoDBService mongoTestService = mongoTestConfig.getService(); final IgtimiConnectionFactory igtimiConnectionFactory = new IgtimiConnectionFactoryImpl(client, PersistenceFactory.INSTANCE.getDomainObjectFactory(mongoTestService), diff --git a/java/com.sap.sailing.domain.igtimiadapter/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.igtimiadapter/META-INF/MANIFEST.MF index ad8f6ed8179..df6b9e37db7 100644 --- a/java/com.sap.sailing.domain.igtimiadapter/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.igtimiadapter/META-INF/MANIFEST.MF @@ -8,8 +8,8 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Require-Bundle: org.json.simple;bundle-version="1.1.0", com.sun.jersey;bundle-version="1.17.0", org.objectweb.asm;bundle-version="3.3.1", - org.apache.httpcomponents.httpclient;bundle-version="4.2.5", - org.apache.httpcomponents.httpcore;bundle-version="4.0.0", + org.apache.httpcomponents.httpclient;bundle-version="[4.2.5,4.3.0)", + org.apache.httpcomponents.httpcore;bundle-version="[4.0.0,4.3.0)", com.sap.sailing.domain.common, com.sap.sailing.domain.igtimiadapter.persistence, com.sap.sailing.domain.shared.android, diff --git a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/Client.java b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/Client.java index ec6656a32d9..f76da91101d 100755 --- a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/Client.java +++ b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/Client.java @@ -18,5 +18,7 @@ public interface Client { String getSecret(); - String getRedirectUri(); + String getDefaultRedirectUri(); + + String getRedirectUri(String redirectProtocol, String redirectHost, String redirectPort); } diff --git a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/IgtimiConnectionFactory.java b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/IgtimiConnectionFactory.java index a3c479940a3..04bc60e64b3 100755 --- a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/IgtimiConnectionFactory.java +++ b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/IgtimiConnectionFactory.java @@ -32,8 +32,11 @@ public interface IgtimiConnectionFactory { * Obtains a URL that a user agent (e.g., a web browser) can be sent to in order to allow that user to authenticate * and then authorize this factory's {@link Client} for accessing the user's Igtimi data. The URL is chosen such that * it redirects to the {@link AuthorizationCallback} with the code response type. + * @param redirectProtocol TODO + * @param redirectHost TODO + * @param redirectPort TODO */ - String getAuthorizationUrl(); + String getAuthorizationUrl(String redirectProtocol, String redirectHost, String redirectPort); /** * Using the "account" service, retrieves the {@link Account} information for this access token and stores the token diff --git a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/Activator.java b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/Activator.java index 66140374735..f9c2941f1e6 100755 --- a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/Activator.java +++ b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/Activator.java @@ -38,10 +38,9 @@ public class Activator implements BundleActivator { private static final String DEFAULT_CLIENT_ID = "d29eae61621af3057db0e638232a027e96b1d2291b1b89a1481dfcac075b0bf4"; private static final String DEFAULT_CLIENT_SECRET = "537dbd14a84fcb470c91d85e8c4f8f7a356ac5ffc8727594d1bfe900ee5942ef"; - private static final String DEFAULT_CLIENT_REDIRECT_URI = "http://sapsailing.com/igtimi/oauth/v1/authorizationcallback"; + private static final String CLIENT_REDIRECT_PATH = "/igtimi/oauth/v1/authorizationcallback"; private static final String CLIENT_ID_PROPERTY_NAME = "igtimi.client.id"; private static final String CLIENT_SECRET_PROPERTY_NAME = "igtimi.client.secret"; - private static final String CLIENT_REDIRECT_URI_PROPERTY_NAME = "igtimi.client.redirecturi"; private final Future connectionFactory; private final Future windTrackerFactory; private final ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactoryWithPriority(Thread.NORM_PRIORITY, /* daemon */ true)); @@ -50,8 +49,7 @@ public class Activator implements BundleActivator { logger.info(getClass().getName()+" constructor"); final String clientId = System.getProperty(CLIENT_ID_PROPERTY_NAME, DEFAULT_CLIENT_ID); final String clientSecret = System.getProperty(CLIENT_SECRET_PROPERTY_NAME, DEFAULT_CLIENT_SECRET); - final String clientRedirectUri = System.getProperty(CLIENT_REDIRECT_URI_PROPERTY_NAME, DEFAULT_CLIENT_REDIRECT_URI); - final Client client = new ClientImpl(clientId, clientSecret, clientRedirectUri); + final Client client = new ClientImpl(clientId, clientSecret, "https", "www.sapsailing.com", /* port==null means default */ null, CLIENT_REDIRECT_PATH); final DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory(); final MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(); connectionFactory = executor.submit(new Callable() { diff --git a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/ClientImpl.java b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/ClientImpl.java index 2ea4ea52b55..95c00a9b009 100755 --- a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/ClientImpl.java +++ b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/ClientImpl.java @@ -5,13 +5,19 @@ import com.sap.sailing.domain.igtimiadapter.Client; public class ClientImpl implements Client { private final String id; private final String secret; - private final String redirectUri; + private final String defaultRedirectProtocol; + private final String defaultRedirectHostname; + private final String defaultRedirectPort; + private final String redirectUriPath; - public ClientImpl(String id, String secret, String redirectUri) { + public ClientImpl(String id, String secret, String defaultRedirectProtocol, String defaultRedirectHostname, String defaultRedirectPort, String redirectUriPath) { super(); this.id = id; this.secret = secret; - this.redirectUri = redirectUri; + this.defaultRedirectProtocol = defaultRedirectProtocol; + this.defaultRedirectHostname = defaultRedirectHostname; + this.defaultRedirectPort = defaultRedirectPort; + this.redirectUriPath = redirectUriPath; } @Override @@ -25,7 +31,13 @@ public class ClientImpl implements Client { } @Override - public String getRedirectUri() { - return redirectUri; + public String getDefaultRedirectUri() { + return getRedirectUri(defaultRedirectProtocol, defaultRedirectHostname, defaultRedirectPort); + } + + @Override + public String getRedirectUri(String redirectProtocol, String redirectHost, String redirectPort) { + return redirectProtocol+"://"+redirectHost+(redirectPort==null||redirectPort.isEmpty()?"":(":"+redirectPort))+ + (redirectUriPath.startsWith("/")?redirectUriPath:"/"+redirectUriPath); } } diff --git a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/IgtimiConnectionFactoryImpl.java b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/IgtimiConnectionFactoryImpl.java index 9d7c3f12d43..42f672b98ef 100755 --- a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/IgtimiConnectionFactoryImpl.java +++ b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/IgtimiConnectionFactoryImpl.java @@ -112,7 +112,7 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory { } private String getOauthAuthorizeUrl() throws UnsupportedEncodingException { - return getBaseUrl()+"/oauth/authorize?response_type=code&client_id="+getClient().getId()+"&redirect_uri="+URLEncoder.encode(getClient().getRedirectUri(), "UTF-8"); + return getBaseUrl()+"/oauth/authorize?response_type=code&client_id="+getClient().getId()+"&redirect_uri="+URLEncoder.encode(getClient().getDefaultRedirectUri(), "UTF-8"); } /** @@ -320,8 +320,9 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory { } @Override - public String getAuthorizationUrl() { - return getBaseUrl()+"/oauth/authorize?response_type=code&client_id="+getClient().getId()+"&redirect_uri="+client.getRedirectUri(); + public String getAuthorizationUrl(String redirectProtocol, String redirectHost, String redirectPort) { + return getBaseUrl()+"/oauth/authorize?response_type=code&client_id="+getClient().getId()+ + "&redirect_uri="+client.getRedirectUri(redirectProtocol, redirectHost, redirectPort); } private Client getClient() { @@ -344,7 +345,7 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory { urlParameters.add(new BasicNameValuePair("client_id", getClient().getId())); urlParameters.add(new BasicNameValuePair("client_secret", getClient().getSecret())); urlParameters.add(new BasicNameValuePair("code", code)); - urlParameters.add(new BasicNameValuePair("redirect_uri", getClient().getRedirectUri())); + urlParameters.add(new BasicNameValuePair("redirect_uri", getClient().getDefaultRedirectUri())); post.setEntity(new UrlEncodedFormEntity(urlParameters)); HttpResponse response = client.execute(post); JSONObject accessTokenJson = ConnectivityUtils.getJsonFromResponse(response); diff --git a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/websocket/WebSocketConnectionManager.java b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/websocket/WebSocketConnectionManager.java index fdfb8fce645..662cece766a 100755 --- a/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/websocket/WebSocketConnectionManager.java +++ b/java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/websocket/WebSocketConnectionManager.java @@ -170,9 +170,7 @@ public class WebSocketConnectionManager implements LiveDataConnection { @Override public void onWebSocketText(String message) { - if (logger.isLoggable(Level.FINEST)) { - logger.finest("Received "+message+" in "+this); - } + logger.finest(()->"Received "+message+" in "+this); if (message.equals("1")) { logger.fine("Received server heartbeat for "+this); receivedServerHeartbeatInInterval = true; @@ -193,9 +191,7 @@ public class WebSocketConnectionManager implements LiveDataConnection { } } } - if (logger.isLoggable(Level.FINEST)) { - logger.finest("Received fixes"+fixes+" for "+this); - } + logger.finest(()->"Received fixes"+fixes+" for "+this); notifyListeners(fixes); } catch (ParseException e) { logger.log(Level.SEVERE, "Error trying to parse a web socket data package coming from Igtimi "+this, e); diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/DomainObjectFactory.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/DomainObjectFactory.java index 2278bf700a4..751457a70e2 100755 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/DomainObjectFactory.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/DomainObjectFactory.java @@ -1,10 +1,13 @@ package com.sap.sailing.domain.persistence; +import java.net.MalformedURLException; +import java.net.URISyntaxException; import java.net.URL; import java.util.Collection; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.function.Consumer; import com.mongodb.DBObject; import com.sap.sailing.domain.abstractlog.race.RaceLog; @@ -28,8 +31,14 @@ import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver; import com.sap.sailing.domain.leaderboard.LeaderboardRegistry; import com.sap.sailing.domain.racelog.RaceLogIdentifier; import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier; +import com.sap.sailing.domain.tracking.RaceHandle; +import com.sap.sailing.domain.tracking.RaceTracker; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.domain.tracking.TrackedRegatta; import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; import com.sap.sailing.domain.tracking.WindTrack; +import com.sap.sse.common.NoCorrespondingServiceRegisteredException; import com.sap.sse.common.Util.Pair; /** @@ -135,4 +144,33 @@ public interface DomainObjectFactory { Iterable> loadAllDeviceConfigurations(); Map> loadResultUrls(); + + /** + * Loads all {@link RaceTrackingConnectivityParameters} objects from the database telling how to re-load those races + * that were {@link MongoObjectFactory#addConnectivityParametersForRaceToRestore(RaceTrackingConnectivityParameters) + * marked as to be restored} before the server got re-started. Callers that would like to use the result to + * re-connect to those races should take care not to flood servers with requests. Instead, ideally, callers would + * monitor the loading {@link TrackedRace#getStatus() status} or the resulting {@link TrackedRace}s (see + * {@link RaceTracker#getRaceHandle()}, {@link RaceHandle#getRace(long)}, {@link RaceHandle#getTrackedRegatta()} and + * {@link TrackedRegatta#getTrackedRace(RaceDefinition)}. + * + * @param callback + * invoked for each connectivity params object successfully resolved; this pattern is preferred over a + * non-{@code void} return type because resolving the connectivity parameters objects itself happens + * through a callback pattern that may be triggered asynchronously by the services for handling the + * respective parameter types becoming available only later. For example, during the OSGi startup phase, + * when the {@code RacingEventService} is launched, not all persistence bundles will have run their + * activators; some may even depend on the {@code RacingEventService} and therefore won't start their + * activators before the activator of {@code RacingEventService} has completed. This would either result + * in a {@link NoCorrespondingServiceRegisteredException} or would have to be handled by not treating + * those connectivity parameter objects which defeats the whole purpose of this method. + * @return the number of connectivity parameter objects found in the database; callers may use this to understand + * the progress with the {@code callback} invocations of which eventually there should be this many, at + * least if all handlers have ultimately become available. + * + * @see MongoObjectFactory#addConnectivityParametersForRaceToRestore(RaceTrackingConnectivityParameters) + * @see MongoObjectFactory#removeConnectivityParametersForRaceToRestore(RaceTrackingConnectivityParameters) + */ + int loadConnectivityParametersForRacesToRestore(Consumer callback) + throws MalformedURLException, URISyntaxException; } diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/MongoObjectFactory.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/MongoObjectFactory.java index f82d9fe430f..18638c6300f 100755 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/MongoObjectFactory.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/MongoObjectFactory.java @@ -19,6 +19,7 @@ import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.LeaderboardGroup; import com.sap.sailing.domain.racelog.RaceLogIdentifier; import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRegatta; @@ -149,4 +150,31 @@ public interface MongoObjectFactory { void removeResultUrl(String resultProviderName, URL url); + + /** + * Updates the database such that the next call to + * {@link DomainObjectFactory#loadConnectivityParametersForRacesToRestore(Consumer)} won't return an object equivalent to + * {@code params} anymore; in other words, the race whose connectivity parameters are described by {@code params} + * will no longer be considered as to be restored. + * + * @see #addConnectivityParametersForRaceToRestore(RaceTrackingConnectivityParameters) + */ + void removeConnectivityParametersForRaceToRestore(RaceTrackingConnectivityParameters params); + + /** + * Updates the database such that the next call to + * {@link DomainObjectFactory#loadConnectivityParametersForRacesToRestore(Consumer)} will return an object equivalent to + * {@code params}; in other words, the race whose connectivity parameters are described by {@code params} will be + * considered as to be restored. + * + * @see #removeConnectivityParametersForRaceToRestore(RaceTrackingConnectivityParameters) + */ + void addConnectivityParametersForRaceToRestore(RaceTrackingConnectivityParameters params); + + /** + * Removes all {@link RaceTrackingConnectivityParameters} objects from those to restore; short for calling + * {@link #removeConnectivityParametersForRaceToRestore(RaceTrackingConnectivityParameters)} for all parameter + * objects obtained through {@link DomainObjectFactory#loadConnectivityParametersForRacesToRestore(Consumer)}. + */ + void removeAllConnectivityParametersForRacesToRestore(); } diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/CollectionNames.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/CollectionNames.java index eeacdadee93..2e96c76cc15 100755 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/CollectionNames.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/CollectionNames.java @@ -2,6 +2,7 @@ package com.sap.sailing.domain.persistence.impl; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.common.tracking.GPSFix; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; /** * Defines literals providing the names for MongoDB collections. The literal documentation described the semantics @@ -100,4 +101,10 @@ public enum CollectionNames { * URLs providing race results */ RESULT_URLS, + + /** + * DB representations of {@link RaceTrackingConnectivityParameters} objects, serialized in a type-specific + * way by corresponding implementations of the RaceTrackingConnectivityParametersMongoHandler} interface. + */ + CONNECTIVITY_PARAMS_FOR_RACES_TO_BE_RESTORED, } 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 3ea18ed2644..62284deb8e4 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 @@ -2,6 +2,7 @@ package com.sap.sailing.domain.persistence.impl; import java.io.Serializable; import java.net.MalformedURLException; +import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; @@ -14,6 +15,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.UUID; +import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; @@ -199,6 +201,8 @@ import com.sap.sailing.domain.ranking.RankingMetricConstructor; import com.sap.sailing.domain.ranking.RankingMetricsFactory; import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier; import com.sap.sailing.domain.regattalog.RegattaLogStore; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler; import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; import com.sap.sailing.domain.tracking.WindTrack; import com.sap.sailing.domain.tracking.impl.WindTrackImpl; @@ -235,8 +239,9 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { private final DB database; private final DomainFactory baseDomainFactory; - private final TypeBasedServiceFinder deviceIdentifierServiceFinder; private final TypeBasedServiceFinderFactory serviceFinderFactory; + private final TypeBasedServiceFinder deviceIdentifierServiceFinder; + private final TypeBasedServiceFinder raceTrackingConnectivityParamsServiceFinder; /** * Uses null as the {@link TypeBasedServiceFinder}, meaning that no {@link DeviceIdentifier}s can be loaded @@ -252,8 +257,10 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { if (serviceFinderFactory != null) { this.deviceIdentifierServiceFinder = serviceFinderFactory.createServiceFinder(DeviceIdentifierMongoHandler.class); this.deviceIdentifierServiceFinder.setFallbackService(new PlaceHolderDeviceIdentifierMongoHandler()); + this.raceTrackingConnectivityParamsServiceFinder = serviceFinderFactory.createServiceFinder(RaceTrackingConnectivityParametersHandler.class); } else { this.deviceIdentifierServiceFinder = null; + this.raceTrackingConnectivityParamsServiceFinder = null; } this.baseDomainFactory = baseDomainFactory; this.competitorDeserializer = CompetitorJsonDeserializer.create(baseDomainFactory); @@ -2276,4 +2283,29 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { } return modified; } + + @Override + public int loadConnectivityParametersForRacesToRestore(Consumer callback) throws MalformedURLException, URISyntaxException { + final DBCollection collection = database.getCollection(CollectionNames.CONNECTIVITY_PARAMS_FOR_RACES_TO_BE_RESTORED.name()); + int count = 0; + for (final DBObject o : collection.find()) { + count++; + final String type = (String) o.get(TypeBasedServiceFinder.TYPE); + raceTrackingConnectivityParamsServiceFinder.applyServiceWhenAvailable(type, connectivityParamsPersistenceService -> { + final Map map = new HashMap<>(); + for (final String key : o.keySet()) { + if (!key.equals(TypeBasedServiceFinder.TYPE)) { + map.put(key, o.get(key)); + } + } + try { + callback.accept(connectivityParamsPersistenceService.mapTo(map)); + } catch (Exception e) { + logger.log(Level.SEVERE, "Exception trying to load race restore connectivity parameters " + + o + " with handler " + connectivityParamsPersistenceService, e); + } + }); + } + return count; + } } 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 ffa4121f5d2..575635388ba 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 @@ -105,6 +105,8 @@ import com.sap.sailing.domain.persistence.racelog.tracking.impl.PlaceHolderDevic import com.sap.sailing.domain.racelog.RaceLogIdentifier; import com.sap.sailing.domain.racelogtracking.DeviceIdentifier; import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRegatta; import com.sap.sailing.domain.tracking.WindTrack; @@ -129,6 +131,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory { private final DB database; private final CompetitorJsonSerializer competitorSerializer = CompetitorJsonSerializer.create(); private final TypeBasedServiceFinder deviceIdentifierServiceFinder; + private final TypeBasedServiceFinder raceTrackingConnectivityParamsServiceFinder; /** * Uses null for the device type service finder and hence will be unable to store device identifiers. @@ -144,8 +147,10 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory { if (serviceFinderFactory != null) { this.deviceIdentifierServiceFinder = serviceFinderFactory.createServiceFinder(DeviceIdentifierMongoHandler.class); this.deviceIdentifierServiceFinder.setFallbackService(new PlaceHolderDeviceIdentifierMongoHandler()); + this.raceTrackingConnectivityParamsServiceFinder = serviceFinderFactory.createServiceFinder(RaceTrackingConnectivityParametersHandler.class); } else { this.deviceIdentifierServiceFinder = null; + this.raceTrackingConnectivityParamsServiceFinder = null; } } @@ -1516,4 +1521,50 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory { result.put(FieldNames.SAILORS_INFO_LOCALE.name(), locale != null ? locale.toLanguageTag() : null); return result; } + + @Override + public void removeConnectivityParametersForRaceToRestore(RaceTrackingConnectivityParameters params) { + if (raceTrackingConnectivityParamsServiceFinder != null) { + final String typeIdentifier = params.getTypeIdentifier(); + final RaceTrackingConnectivityParametersHandler paramsPersistenceService = raceTrackingConnectivityParamsServiceFinder.findService(typeIdentifier); + if (paramsPersistenceService != null) { + final DBCollection collection = database.getCollection(CollectionNames.CONNECTIVITY_PARAMS_FOR_RACES_TO_BE_RESTORED.name()); + DBObject key = new BasicDBObject(); + key.putAll(paramsPersistenceService.getKey(params)); + collection.remove(key, WriteConcern.SAFE); + } else { + logger.warning("Couldn't find a persistence service for connectivity parameters of type "+typeIdentifier); + } + } else { + logger.warning("No connectivity parameters service finder set; unable to remove connectivity parameters from persistent store for " + + params + " of type " + params.getTypeIdentifier()); + } + } + + @Override + public void addConnectivityParametersForRaceToRestore(RaceTrackingConnectivityParameters params) { + final String typeIdentifier = params.getTypeIdentifier(); + if (raceTrackingConnectivityParamsServiceFinder == null) { + logger.warning("No service finder has been configured to find connectivity parameter persistence services. Can't add connectivity parameters to DB for restore."); + } else { + try { + final RaceTrackingConnectivityParametersHandler paramsPersistenceService = raceTrackingConnectivityParamsServiceFinder.findService(typeIdentifier); + final DBCollection collection = database.getCollection(CollectionNames.CONNECTIVITY_PARAMS_FOR_RACES_TO_BE_RESTORED.name()); + DBObject key = new BasicDBObject(); + key.putAll(paramsPersistenceService.getKey(params)); + DBObject dbObject = new BasicDBObject(); + dbObject.putAll(paramsPersistenceService.mapFrom(params)); + collection.update(key, dbObject, /* upsert */ true, /* multi */ false, WriteConcern.SAFE); + } catch (NoCorrespondingServiceRegisteredException e) { + logger.log(Level.WARNING, "Couldn't find a persistence service for connectivity parameters of type "+typeIdentifier+ + ". Couldn't store race "+params.getTrackerID()+" for restoring.", e); + } + } + } + + @Override + public void removeAllConnectivityParametersForRacesToRestore() { + database.getCollection(CollectionNames.CONNECTIVITY_PARAMS_FOR_RACES_TO_BE_RESTORED.name()).drop(); + } + } diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.racelogtrackingadapter.test/META-INF/MANIFEST.MF index 3f9cbc1525d..1e49750b8e5 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/META-INF/MANIFEST.MF @@ -6,7 +6,7 @@ Bundle-Version: 1.0.0.qualifier Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Fragment-Host: com.sap.sailing.domain.racelogtrackingadapter -Require-Bundle: org.junit4;bundle-version="4.8.2", +Require-Bundle: org.junit;bundle-version="4.8.2", com.sap.sailing.domain.racelogtrackingadapter.testsupport, org.mockito.mockito-core;bundle-version="1.9.5", org.objenesis;bundle-version="1.3.0", 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 ff6a03d662b..81bf91073eb 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 @@ -78,7 +78,7 @@ public class CreateAndTrackWithRaceLogTest { @Before public void setup() { - service = new RacingEventServiceImpl(true, new MockSmartphoneImeiServiceFinderFactory()); + service = new RacingEventServiceImpl(/* clearPersistentCompetitorStore */ true, new MockSmartphoneImeiServiceFinderFactory(), /* restoreTrackedRaces */ false); sensorFixStore = service.getSensorFixStore(); service.getMongoObjectFactory().getDatabase().dropDatabase(); author = service.getServerAuthor(); @@ -113,7 +113,7 @@ public class CreateAndTrackWithRaceLogTest { Exception { RaceColumn column = leaderboard.getRaceColumnByName(columnName); exception.expect(NotDenotedForRaceLogTrackingException.class); - adapter.startTracking(service, leaderboard, column, fleet); + adapter.startTracking(service, leaderboard, column, fleet, /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false); } private void testSize(Track track, int expected) { @@ -184,7 +184,7 @@ public class CreateAndTrackWithRaceLogTest { raceLog.add(new RaceLogRegisterCompetitorEventImpl(t(), author, 0, comp1)); raceLog.add(new RaceLogStartOfTrackingEventImpl(t(0), author, /* passId */ 0)); // start tracking - adapter.startTracking(service, leaderboard, column, fleet); + adapter.startTracking(service, leaderboard, column, fleet, /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false); // now there is a tracked race @@ -226,7 +226,7 @@ public class CreateAndTrackWithRaceLogTest { raceLog.add(new RaceLogStartOfTrackingEventImpl(t(0), author, /* passId */ 0)); // start tracking - adapter.startTracking(service, leaderboard, column, fleet); + adapter.startTracking(service, leaderboard, column, fleet, /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false); // now there is a trackedrace TrackedRace race = column.getTrackedRace(fleet); diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/META-INF/MANIFEST.MF index ab5f50f9c87..29e3390faaa 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/META-INF/MANIFEST.MF @@ -15,7 +15,7 @@ Require-Bundle: com.sap.sailing.domain.shared.android, org.json.simple;bundle-version="1.1.0", org.mockito.mockito-core;bundle-version="1.9.5", org.hamcrest;bundle-version="1.1.0", - org.junit4;bundle-version="4.8.2", + org.junit;bundle-version="4.8.2", com.sap.sse.common, com.sap.sailing.server.gateway.serialization.shared.android Export-Package: com.sap.sailing.domain.racelog.tracking.test.mock diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/AbstractTypeBasedServiceFinder.java b/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/AbstractTypeBasedServiceFinder.java new file mode 100644 index 00000000000..05f76a3f1f4 --- /dev/null +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/AbstractTypeBasedServiceFinder.java @@ -0,0 +1,11 @@ +package com.sap.sailing.domain.racelog.tracking.test.mock; + +import com.sap.sse.common.TypeBasedServiceFinder; + +public abstract class AbstractTypeBasedServiceFinder implements TypeBasedServiceFinder { + @Override + public void applyServiceWhenAvailable(String type, com.sap.sse.common.TypeBasedServiceFinder.Callback callback) { + final ServiceT service = findService(type); + callback.withService(service); + } +} diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/MockEmptyServiceFinder.java b/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/MockEmptyServiceFinder.java index 354a654036f..b3d626619a8 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/MockEmptyServiceFinder.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/MockEmptyServiceFinder.java @@ -4,9 +4,8 @@ import java.util.Collections; import java.util.Set; import com.sap.sse.common.NoCorrespondingServiceRegisteredException; -import com.sap.sse.common.TypeBasedServiceFinder; -public class MockEmptyServiceFinder implements TypeBasedServiceFinder { +public class MockEmptyServiceFinder extends AbstractTypeBasedServiceFinder { private ServiceType fallback; @Override diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/MockGPSFixMongoServiceFinder.java b/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/MockGPSFixMongoServiceFinder.java index 587b128c16d..241b5fff5f3 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/MockGPSFixMongoServiceFinder.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/MockGPSFixMongoServiceFinder.java @@ -15,9 +15,8 @@ import com.sap.sailing.domain.persistence.racelog.tracking.FixMongoHandler; import com.sap.sailing.domain.persistence.racelog.tracking.impl.DoubleVectorFixMongoHandlerImpl; import com.sap.sailing.domain.persistence.racelog.tracking.impl.GPSFixMongoHandlerImpl; import com.sap.sailing.domain.persistence.racelog.tracking.impl.GPSFixMovingMongoHandlerImpl; -import com.sap.sse.common.TypeBasedServiceFinder; -public class MockGPSFixMongoServiceFinder implements TypeBasedServiceFinder> { +public class MockGPSFixMongoServiceFinder extends AbstractTypeBasedServiceFinder> { private final MongoObjectFactoryImpl mof = new MongoObjectFactoryImpl(null); private final DomainObjectFactoryImpl dof = new DomainObjectFactoryImpl(null, null); private final FixMongoHandler movingHandler = new GPSFixMovingMongoHandlerImpl(mof, dof); diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/MockServiceFinder.java b/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/MockServiceFinder.java index 94a016826a8..88aa2ff2ce3 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/MockServiceFinder.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.testsupport/src/com/sap/sailing/domain/racelog/tracking/test/mock/MockServiceFinder.java @@ -3,9 +3,7 @@ package com.sap.sailing.domain.racelog.tracking.test.mock; import java.util.Collections; import java.util.Set; -import com.sap.sse.common.TypeBasedServiceFinder; - -public class MockServiceFinder implements TypeBasedServiceFinder { +public class MockServiceFinder extends AbstractTypeBasedServiceFinder { private final T handler; private T fallback; diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.racelogtrackingadapter/META-INF/MANIFEST.MF index bf0d461cbab..e478d0a6104 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/META-INF/MANIFEST.MF @@ -32,5 +32,5 @@ Import-Package: com.google.zxing;version="3.1.0", org.osgi.framework;version="1.6.0", org.osgi.util.tracker;version="1.5.1" Export-Package: com.sap.sailing.domain.racelogtracking, - com.sap.sailing.domain.racelogtracking.impl;x-friends:="com.sap.sailing.domain.racelogtrackingadapter.testsupport,com.sap.sailing.gwt.ui.test" + com.sap.sailing.domain.racelogtracking.impl;x-friends:="com.sap.sailing.domain.racelogtrackingadapter.testsupport,com.sap.sailing.gwt.ui.test,com.sap.sailing.mongodb.test" Bundle-Activator: com.sap.sailing.domain.racelogtracking.impl.Activator diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/XLF/TrackingStringMessages_ja.properties.xlf b/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/XLF/TrackingStringMessages_ja.properties.xlf index 9ce996e6326..292821aeb53 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/XLF/TrackingStringMessages_ja.properties.xlf +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/XLF/TrackingStringMessages_ja.properties.xlf @@ -1,6 +1,6 @@ - +

H4sIAAAAAAAAAF1RQU7DQAy85xW+cakQ50o5FCSkSEBLk8J5m7iN1c16tXZbIsTf2U1SWnHzzjjjmYkGUx/I7Qt3IjVK7J455NWEwhWGHYdMauPe10/c4DJ8kJBugq14jXsSxbCQ/NUcEOQYEHo+QmtOCNoifD/8wMJ7ICdqrMXmHqoWHSS9YWHH1vI5XRzlgQPUlurDwFpyBzhKoqNsAOlMUN+yi+oMYToPRrIz2po7rDj/HKe4kNGy3AgGyUuN30F8JjOZcU1gam6pxQiNtI2aLmY/oe2HsFVL8hKt5MVuiGdizICmSb40ctAZssBjogZPVCOcSdvhneIbm9b7aw0zEOq87aesW4wlpEiS3Myz7ZH7Cl2DIU8j6DBnxvtSOWAxylT4pRdLDbs7vfaejvaoM/AWjeDl8DxJFCz5pHCtxPupgj/qtpJkYhXTxl/t/ZvpMC8XKyhj6lTBY7I40plErHAl7Vv9vwoTnv0Cs+RzW3sCAAA= diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/XLF/TrackingStringMessages_ru.properties.xlf b/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/XLF/TrackingStringMessages_ru.properties.xlf index dc102932e7b..2cdd485da8c 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/XLF/TrackingStringMessages_ru.properties.xlf +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/XLF/TrackingStringMessages_ru.properties.xlf @@ -1,6 +1,6 @@ - +
H4sIAAAAAAAAAF1RQU7DQAy85xW+cakQ50o5FCSkSEBLk8J5m7iN1c16tXZbIsTf2U1SWnHzzjjjmYkGUx/I7Qt3IjVK7J455NWEwhWGHYdMauPe10/c4DJ8kJBugq14jXsSxbCQ/NUcEOQYEHo+QmtOCNoifD/8wMJ7ICdqrMXmHqoWHSS9YWHH1vI5XRzlgQPUlurDwFpyBzhKoqNsAOlMUN+yi+oMYToPRrIz2po7rDj/HKe4kNGy3AgGyUuN30F8JjOZcU1gam6pxQiNtI2aLmY/oe2HsFVL8hKt5MVuiGdizICmSb40ctAZssBjogZPVCOcSdvhneIbm9b7aw0zEOq87aesW4wlpEiS3Myz7ZH7Cl2DIU8j6DBnxvtSOWAxylT4pRdLDbs7vfaejvaoM/AWjeDl8DxJFCz5pHCtxPupgj/qtpJkYhXTxl/t/ZvpMC8XKyhj6lTBY7I40plErHAl7Vv9vwoTnv0Cs+RzW3sCAAA= diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/XLF/TrackingStringMessages_zh_CN.properties.xlf b/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/XLF/TrackingStringMessages_zh_CN.properties.xlf index 29f86428c5b..b3e295d9d1b 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/XLF/TrackingStringMessages_zh_CN.properties.xlf +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/XLF/TrackingStringMessages_zh_CN.properties.xlf @@ -1,6 +1,6 @@ - +
H4sIAAAAAAAAAF1RQU7DQAy85xW+cakQ50o5FCSkSEBLk8J5m7iN1c16tXZbIsTf2U1SWnHzzjjjmYkGUx/I7Qt3IjVK7J455NWEwhWGHYdMauPe10/c4DJ8kJBugq14jXsSxbCQ/NUcEOQYEHo+QmtOCNoifD/8wMJ7ICdqrMXmHqoWHSS9YWHH1vI5XRzlgQPUlurDwFpyBzhKoqNsAOlMUN+yi+oMYToPRrIz2po7rDj/HKe4kNGy3AgGyUuN30F8JjOZcU1gam6pxQiNtI2aLmY/oe2HsFVL8hKt5MVuiGdizICmSb40ctAZssBjogZPVCOcSdvhneIbm9b7aw0zEOq87aesW4wlpEiS3Myz7ZH7Cl2DIU8j6DBnxvtSOWAxylT4pRdLDbs7vfaejvaoM/AWjeDl8DxJFCz5pHCtxPupgj/qtpJkYhXTxl/t/ZvpMC8XKyhj6lTBY7I40plErHAl7Vv9vwoTnv0Cs+RzW3sCAAA= diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/RaceLogTrackingAdapter.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/RaceLogTrackingAdapter.java index d728bf31f88..cbeada5a0f1 100755 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/RaceLogTrackingAdapter.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/RaceLogTrackingAdapter.java @@ -39,8 +39,10 @@ public interface RaceLogTrackingAdapter { *
  • Is a {@link RaceLogRaceTracker} already listening for this racelog? If not, add one.
  • *
  • Is a {@link RaceLogStartTrackingEvent} present in the racelog? If not, add one
  • * + * @param trackWind TODO + * @param correctWindDirectionByMagneticDeclination TODO */ - RaceHandle startTracking(RacingEventService service, Leaderboard leaderboard, RaceColumn raceColumn, Fleet fleet) + RaceHandle startTracking(RacingEventService service, Leaderboard leaderboard, RaceColumn raceColumn, Fleet fleet, boolean trackWind, boolean correctWindDirectionByMagneticDeclination) throws NotDenotedForRaceLogTrackingException, Exception; RaceLogTrackingState getRaceLogTrackingState(RacingEventService service, RaceColumn raceColumn, Fleet fleet); diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/Activator.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/Activator.java index 6dc817c4c0a..c2b99f36dc3 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/Activator.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/Activator.java @@ -22,6 +22,7 @@ import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory; import com.sap.sailing.domain.racelogtracking.SmartphoneUUIDIdentifier; import com.sap.sailing.domain.racelogtracking.impl.fixtracker.RegattaLogFixTrackerRegattaListener; import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifier; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler; import com.sap.sailing.domain.tracking.TrackedRegattaListener; import com.sap.sailing.server.MasterDataImportClassLoaderService; import com.sap.sailing.server.RacingEventService; @@ -97,7 +98,14 @@ public class Activator implements BundleActivator { regattaLogSensorDataTrackerTrackedRegattaListener, null)); registrations.add(context.registerService(Replicable.class, regattaLogSensorDataTrackerTrackedRegattaListener, null)); - + new Thread(()->{ + try { + registrations.add(context.registerService(RaceTrackingConnectivityParametersHandler.class, + new RaceLogConnectivityParamsHandler(racingEventServiceTracker.waitForService(0)), getDict(RaceLogConnectivityParams.TYPE))); + } catch (Exception e) { + logger.log(Level.SEVERE, "Exception trying to register race log tracking connectivity params persistence handler", e); + } + }, "RaceLog tracking activator registering connectivity params persistence handler").start(); logger.log(Level.INFO, "Started "+context.getBundle().getSymbolicName()); } diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogConnectivityParams.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogConnectivityParams.java index 95e833a6835..e96a9b2faa8 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogConnectivityParams.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogConnectivityParams.java @@ -22,12 +22,17 @@ import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.regattalike.HasRegattaLike; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; import com.sap.sailing.domain.tracking.RaceTracker; -import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; import com.sap.sailing.domain.tracking.WindStore; +import com.sap.sailing.domain.tracking.impl.AbstractRaceTrackingConnectivityParameters; import com.sap.sailing.server.RacingEventService; -public class RaceLogConnectivityParams implements RaceTrackingConnectivityParameters { +public class RaceLogConnectivityParams extends AbstractRaceTrackingConnectivityParameters { + /** + * A type identifier that needs to be unique for the + */ + public static final String TYPE = "RACE_LOG_TRACKING"; + private final RacingEventService service; private final RaceColumn raceColumn; private final Fleet fleet; @@ -37,7 +42,9 @@ public class RaceLogConnectivityParams implements RaceTrackingConnectivityParame private final DomainFactory domainFactory; public RaceLogConnectivityParams(RacingEventService service, Regatta regatta, RaceColumn raceColumn, Fleet fleet, - Leaderboard leaderboard, long delayToLiveInMillis, DomainFactory domainFactory) throws RaceNotCreatedException { + Leaderboard leaderboard, long delayToLiveInMillis, DomainFactory domainFactory, boolean trackWind, + boolean correctWindDirectionByMagneticDeclination) throws RaceNotCreatedException { + super(trackWind, correctWindDirectionByMagneticDeclination); this.service = service; this.regatta = regatta; this.raceColumn = raceColumn; @@ -45,12 +52,16 @@ public class RaceLogConnectivityParams implements RaceTrackingConnectivityParame this.leaderboard = leaderboard; this.delayToLiveInMillis = delayToLiveInMillis; this.domainFactory = domainFactory; - if (!new RaceLogTrackingStateAnalyzer(getRaceLog()).analyze().isForTracking()) { throw new RaceNotCreatedException(String.format("Racelog (%s) is not denoted for tracking", getRaceLog())); } } + @Override + public String getTypeIdentifier() { + return TYPE; + } + @Override public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore, RaceLogResolver raceLogResolver) { @@ -70,7 +81,7 @@ public class RaceLogConnectivityParams implements RaceTrackingConnectivityParame throw new RaceNotCreatedException("No regatta for race-log tracked race"); } DynamicTrackedRegatta trackedRegatta = trackedRegattaRegistry.getOrCreateTrackedRegatta(regatta); - return new RaceLogRaceTracker(trackedRegatta, this, windStore, raceLogResolver); + return new RaceLogRaceTracker(trackedRegatta, this, windStore, raceLogResolver, this); } @Override diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogConnectivityParamsHandler.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogConnectivityParamsHandler.java new file mode 100644 index 00000000000..e3ef23fa681 --- /dev/null +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogConnectivityParamsHandler.java @@ -0,0 +1,71 @@ +package com.sap.sailing.domain.racelogtracking.impl; + +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; + +import com.sap.sailing.domain.base.DomainFactory; +import com.sap.sailing.domain.base.RaceColumn; +import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; +import com.sap.sailing.domain.tracking.impl.AbstractRaceTrackingConnectivityParametersHandler; +import com.sap.sailing.server.RacingEventService; +import com.sap.sse.common.TypeBasedServiceFinder; + +/** + * Handles mapping TracTrac connectivity parameters from and to a map with {@link String} keys. The + * "param URL" is considered the {@link #getKey(RaceTrackingConnectivityParameters) key} for these objects.

    + * + * Lives in the same package as {@link RaceTrackingConnectivityParameters} for package-private access to + * its members. + * + * @author Axel Uhl (d043530) + * + */ +public class RaceLogConnectivityParamsHandler extends AbstractRaceTrackingConnectivityParametersHandler { + private static final String FLEET_NAME = "fleetName"; + private static final String RACE_COLUMN_NAME = "raceColumnName"; + private static final String LEADERBOARD_NAME = "leaderboardName"; + private static final String DELAY_TO_LIVE_IN_MILLIS = "delayToLiveInMillis"; + private final DomainFactory domainFactory; + private final RacingEventService racingEventService; + + public RaceLogConnectivityParamsHandler(RacingEventService racingEventService) { + super(); + this.racingEventService = racingEventService; + this.domainFactory = racingEventService.getBaseDomainFactory(); + } + + @Override + public Map mapFrom(RaceTrackingConnectivityParameters params) { + assert params instanceof RaceLogConnectivityParams; + final RaceLogConnectivityParams rlParams = (RaceLogConnectivityParams) params; + final Map result = getKey(params); + result.put(DELAY_TO_LIVE_IN_MILLIS, rlParams.getDelayToLiveInMillis()); + addWindTrackingParameters(rlParams, result); + return result; + } + + @Override + public RaceTrackingConnectivityParameters mapTo(Map map) throws MalformedURLException, URISyntaxException { + final RegattaLeaderboard leaderboard = (RegattaLeaderboard) racingEventService.getLeaderboardByName((String) map.get(LEADERBOARD_NAME)); + final RaceColumn raceColumn = leaderboard.getRaceColumnByName((String) map.get(RACE_COLUMN_NAME)); + return new RaceLogConnectivityParams(racingEventService, leaderboard.getRegatta(), raceColumn, + raceColumn.getFleetByName((String) map.get(FLEET_NAME)), leaderboard, + ((Number) map.get(DELAY_TO_LIVE_IN_MILLIS)).longValue(), domainFactory, isTrackWind(map), + isCorrectWindDirectionByMagneticDeclination(map)); + } + + @Override + public Map getKey(RaceTrackingConnectivityParameters params) { + assert params instanceof RaceLogConnectivityParams; + final RaceLogConnectivityParams rlParams = (RaceLogConnectivityParams) params; + final Map result = new HashMap<>(); + result.put(TypeBasedServiceFinder.TYPE, params.getTypeIdentifier()); + result.put(LEADERBOARD_NAME, rlParams.getLeaderboard().getName()); + result.put(RACE_COLUMN_NAME, rlParams.getRaceColumn().getName()); + result.put(FLEET_NAME, rlParams.getFleet().getName()); + return result; + } +} 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 9d0b8d79ec2..eaed45e622f 100755 --- 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 @@ -7,7 +7,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -98,7 +97,8 @@ public class RaceLogRaceTracker extends AbstractRaceTrackerBaseImpl { private EndOfTrackingController endOfTrackingController; public RaceLogRaceTracker(DynamicTrackedRegatta regatta, RaceLogConnectivityParams params, WindStore windStore, - RaceLogResolver raceLogResolver) { + RaceLogResolver raceLogResolver, RaceLogConnectivityParams connectivityParams) { + super(connectivityParams); this.params = params; this.windStore = windStore; this.trackedRegatta = regatta; @@ -194,17 +194,17 @@ public class RaceLogRaceTracker extends AbstractRaceTrackerBaseImpl { } @Override - public Set getRaces() { - return trackedRace == null ? null : Collections.singleton(trackedRace.getRace()); + public RaceDefinition getRace() { + return trackedRace == null ? null : trackedRace.getRace(); } @Override - public Set getRaceIdentifiers() { - return trackedRace == null ? null : Collections.singleton(trackedRace.getRaceIdentifier()); + public RegattaAndRaceIdentifier getRaceIdentifier() { + return trackedRace == null ? null : trackedRace.getRaceIdentifier(); } @Override - public RaceHandle getRacesHandle() { + public RaceHandle getRaceHandle() { return new RaceLogRacesHandle(this); } @@ -312,6 +312,7 @@ public class RaceLogRaceTracker extends AbstractRaceTrackerBaseImpl { trackedRace.addStartTimeChangedListener(startOfTrackingController); endOfTrackingController = new EndOfTrackingController(trackedRace, raceLog, raceLogEventAuthor); trackedRace.addListener(endOfTrackingController); + notifyRaceCreationListeners(); logger.info(String.format("Started tracking race-log race (%s)", raceLog)); // this wakes up all waiting race handles synchronized (this) { diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogRacesHandle.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogRacesHandle.java index ff375ff8e66..2d0c26c79ac 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogRacesHandle.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogRacesHandle.java @@ -1,7 +1,5 @@ package com.sap.sailing.domain.racelogtracking.impl; -import java.util.Set; - import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; @@ -28,12 +26,7 @@ public class RaceLogRacesHandle implements RaceHandle { @Override public RaceDefinition getRace(long timeoutInMilliseconds) { long start = System.currentTimeMillis(); - Set raceDefs = tracker.getRaces(); - - RaceDefinition result = null; - if (raceDefs != null && !raceDefs.isEmpty()) { - result = tracker.getRaces().iterator().next(); - } + RaceDefinition result = tracker.getRace(); boolean interrupted = false; synchronized (tracker) { while ((timeoutInMilliseconds == -1 || System.currentTimeMillis() - start < timeoutInMilliseconds) @@ -47,13 +40,13 @@ public class RaceLogRacesHandle implements RaceHandle { tracker.wait(timeToWait); } } - result = tracker.getRaces().isEmpty() ? null : tracker.getRaces().iterator().next(); + result = tracker.getRace(); } catch (InterruptedException e) { interrupted = true; } } } - return result == null ? null : result; + return result; } @Override diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogTrackingAdapterImpl.java b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogTrackingAdapterImpl.java index 394e1b5a893..f3fbae990b0 100755 --- a/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogTrackingAdapterImpl.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogTrackingAdapterImpl.java @@ -96,7 +96,8 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter { @Override public RaceHandle startTracking(RacingEventService service, Leaderboard leaderboard, RaceColumn raceColumn, - Fleet fleet) throws NotDenotedForRaceLogTrackingException, Exception { + Fleet fleet, boolean trackWind, boolean correctWindDirectionByMagneticDeclination) + throws NotDenotedForRaceLogTrackingException, Exception { RaceLog raceLog = raceColumn.getRaceLog(fleet); RaceLogTrackingState raceLogTrackingState = new RaceLogTrackingStateAnalyzer(raceLog).analyze(); if (!raceLogTrackingState.isForTracking()) { @@ -110,7 +111,7 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter { } final RaceHandle result; if (!isRaceLogRaceTrackerAttached(service, raceLog)) { - result = addTracker(service, regatta, leaderboard, raceColumn, fleet, -1); + result = addTracker(service, regatta, leaderboard, raceColumn, fleet, -1, trackWind, correctWindDirectionByMagneticDeclination); } else { result = null; } @@ -123,16 +124,15 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter { * and tracking begins. Otherwise, the {@code RaceLogRaceTracker} waits until a {@code StartTrackingEvent} is added * to perform these actions. The race first has to be denoted for racelog tracking. */ - private RaceHandle addTracker(RacingEventService service, RegattaIdentifier regattaToAddTo, - Leaderboard leaderboard, RaceColumn raceColumn, Fleet fleet, long timeoutInMilliseconds) - throws RaceLogRaceTrackerExistsException, Exception { + private RaceHandle addTracker(RacingEventService service, RegattaIdentifier regattaToAddTo, Leaderboard leaderboard, + RaceColumn raceColumn, Fleet fleet, long timeoutInMilliseconds, boolean trackWind, + boolean correctWindDirectionByMagneticDeclination) throws RaceLogRaceTrackerExistsException, Exception { RaceLog raceLog = raceColumn.getRaceLog(fleet); assert !isRaceLogRaceTrackerAttached(service, raceLog) : new RaceLogRaceTrackerExistsException( leaderboard.getName() + " - " + raceColumn.getName() + " - " + fleet.getName()); - Regatta regatta = regattaToAddTo == null ? null : service.getRegatta(regattaToAddTo); RaceLogConnectivityParams params = new RaceLogConnectivityParams(service, regatta, raceColumn, fleet, - leaderboard, delayToLiveInMillis, domainFactory); + leaderboard, delayToLiveInMillis, domainFactory, trackWind, correctWindDirectionByMagneticDeclination); return service.addRace(regattaToAddTo, params, timeoutInMilliseconds); } diff --git a/java/com.sap.sailing.domain.shared.android.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.shared.android.test/META-INF/MANIFEST.MF index 1b6d41de6cb..34b8773f204 100644 --- a/java/com.sap.sailing.domain.shared.android.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.shared.android.test/META-INF/MANIFEST.MF @@ -8,7 +8,7 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Require-Bundle: com.sap.sailing.domain.common;bundle-version="1.0.0", com.sap.sailing.domain.shared.android;bundle-version="1.0.0", org.hamcrest;bundle-version="1.1.0", - org.junit4;bundle-version="4.8.2", + org.junit;bundle-version="4.8.2", org.objenesis;bundle-version="1.3.0", org.mockito.mockito-core;bundle-version="1.9.5", com.sap.sailing.domain.test, diff --git a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/base/racegroup/impl/CurrentRaceFilterImplTest.java b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/base/racegroup/impl/CurrentRaceFilterImplTest.java index 6c8b7394dac..a1acdfca3ab 100644 --- a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/base/racegroup/impl/CurrentRaceFilterImplTest.java +++ b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/base/racegroup/impl/CurrentRaceFilterImplTest.java @@ -413,7 +413,7 @@ public class CurrentRaceFilterImplTest { for (int i=0; i < numberOfRaces; i++) { _505RaceCells.add(new RaceCellImpl(raceColumnNamePrefix+(i+firstRaceColumnNumber), /* race log */ null, /* factor */ firstRaceColumnNumber, /* explicitFactor */ null, - /* zeroBasedIndexInFleet */ i)); + /* zeroBasedIndexInFleet */ i, /* targetTime */ null)); } final RaceRowImpl _505RaceRow = new RaceRowImpl(new FleetImpl(fleetName), _505RaceCells); return _505RaceRow; diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogEndOfTrackingEvent.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogEndOfTrackingEvent.java index c3811c991b7..bac8c65d7ec 100755 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogEndOfTrackingEvent.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogEndOfTrackingEvent.java @@ -1,6 +1,7 @@ package com.sap.sailing.domain.abstractlog.race; +import com.sap.sailing.domain.abstractlog.Revokable; -public interface RaceLogEndOfTrackingEvent extends RaceLogEvent { +public interface RaceLogEndOfTrackingEvent extends RaceLogEvent, Revokable { } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogStartOfTrackingEvent.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogStartOfTrackingEvent.java index db3e9cce334..6b73a14a437 100755 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogStartOfTrackingEvent.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogStartOfTrackingEvent.java @@ -1,5 +1,7 @@ package com.sap.sailing.domain.abstractlog.race; -public interface RaceLogStartOfTrackingEvent extends RaceLogEvent { +import com.sap.sailing.domain.abstractlog.Revokable; + +public interface RaceLogStartOfTrackingEvent extends RaceLogEvent, Revokable { } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/ConfirmedFinishPositioningListFinder.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/ConfirmedFinishPositioningListFinder.java index 381df5b1c36..47c18c85d70 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/ConfirmedFinishPositioningListFinder.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/ConfirmedFinishPositioningListFinder.java @@ -13,12 +13,30 @@ public class ConfirmedFinishPositioningListFinder extends RaceLogAnalyzer> { + + public TrackingTimesEventFinder(RaceLog raceLog) { + super(raceLog); + } + + @Override + protected Util.Pair performAnalysis() { + boolean startOfTrackingFound = false; + boolean endOfTrackingFound = false; + RaceLogStartOfTrackingEvent start = null; + RaceLogEndOfTrackingEvent end = null; + final RaceLog raceLog = getLog(); + for (RaceLogEvent event : raceLog.getUnrevokedEventsDescending()) { + if (!startOfTrackingFound && event instanceof RaceLogStartOfTrackingEvent) { + start = (RaceLogStartOfTrackingEvent) event; + startOfTrackingFound = true; + } + if (!endOfTrackingFound && event instanceof RaceLogEndOfTrackingEvent) { + end = (RaceLogEndOfTrackingEvent) event; + endOfTrackingFound = true; + } + if (startOfTrackingFound && endOfTrackingFound) { + break; + } + } + final Pair result; + if (startOfTrackingFound || endOfTrackingFound) { + result = new Util.Pair<>(start, end); + } else { + result = null; + } + return result; + } + +} diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/TrackingTimesFinder.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/TrackingTimesFinder.java index bf5e5c1b6dc..8f260e49ad2 100755 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/TrackingTimesFinder.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/TrackingTimesFinder.java @@ -2,7 +2,6 @@ package com.sap.sailing.domain.abstractlog.race.analyzing.impl; import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.RaceLogEndOfTrackingEvent; -import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; import com.sap.sailing.domain.abstractlog.race.RaceLogStartOfTrackingEvent; import com.sap.sailing.domain.common.abstractlog.TimePointSpecificationFoundInLog; import com.sap.sailing.domain.common.abstractlog.TimePointSpecificationFoundInLogImpl; @@ -10,38 +9,19 @@ import com.sap.sse.common.Util; import com.sap.sse.common.Util.Pair; public class TrackingTimesFinder extends RaceLogAnalyzer> { - + private final TrackingTimesEventFinder trackingTimesEventFinder; + public TrackingTimesFinder(RaceLog raceLog) { super(raceLog); + this.trackingTimesEventFinder = new TrackingTimesEventFinder(raceLog); } @Override protected Util.Pair performAnalysis() { - boolean startOfTrackingFound = false; - boolean endOfTrackingFound = false; - TimePointSpecificationFoundInLog start = null; - TimePointSpecificationFoundInLog end = null; - final RaceLog raceLog = getLog(); - for (RaceLogEvent event : raceLog.getUnrevokedEventsDescending()) { - if (!startOfTrackingFound && event instanceof RaceLogStartOfTrackingEvent) { - start = new TimePointSpecificationFoundInLogImpl(event.getLogicalTimePoint()); - startOfTrackingFound = true; - } - if (!endOfTrackingFound && event instanceof RaceLogEndOfTrackingEvent) { - end = new TimePointSpecificationFoundInLogImpl(event.getLogicalTimePoint()); - endOfTrackingFound = true; - } - if (startOfTrackingFound && endOfTrackingFound) { - break; - } - } - final Pair result; - if (startOfTrackingFound || endOfTrackingFound) { - result = new Util.Pair<>(start, end); - } else { - result = null; - } - return result; + Pair preResult = trackingTimesEventFinder.performAnalysis(); + return preResult == null ? null : + new Util.Pair( + preResult.getA()==null?null:new TimePointSpecificationFoundInLogImpl(preResult.getA().getLogicalTimePoint()), + preResult.getB()==null?null:new TimePointSpecificationFoundInLogImpl(preResult.getB().getLogicalTimePoint())); } - } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/RaceStateChangedListener.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/RaceStateChangedListener.java index ba12a17a34f..81cd4038a1f 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/RaceStateChangedListener.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/RaceStateChangedListener.java @@ -1,6 +1,5 @@ package com.sap.sailing.domain.abstractlog.race.state; - public interface RaceStateChangedListener { void onRacingProcedureChanged(ReadonlyRaceState state); diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/SharedDomainFactory.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/SharedDomainFactory.java index 1af85ef3f12..90900c5011e 100755 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/SharedDomainFactory.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/SharedDomainFactory.java @@ -110,4 +110,6 @@ public interface SharedDomainFactory extends CompetitorFactory { ControlPointWithTwoMarks getOrCreateControlPointWithTwoMarks(String id, String name, Mark left, Mark right); RaceLogResolver getRaceLogResolver(); + + Mark getOrCreateMark(String name, MarkType markType); } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/BoatClassImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/BoatClassImpl.java index 657f8a55a67..dc19f20d65a 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/BoatClassImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/BoatClassImpl.java @@ -48,7 +48,9 @@ public class BoatClassImpl extends NamedImpl implements BoatClass { public BoatClassImpl(String name, boolean typicallyStartsUpwind) { this(name, typicallyStartsUpwind, /* displayName */ null, - /* hull length */ new MeterDistance(5), /* hullBeam */ null, /* hullType */ null); + // use the typical dinghy parameters as default + /* hull length */ new MeterDistance(5), /* hullBeam */ new MeterDistance(1.8), + /* hullType */ BoatHullType.MONOHULL); } public BoatClassImpl(String name, BoatClassMasterdata masterData) { diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/SharedDomainFactoryImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/SharedDomainFactoryImpl.java index f35232cf6f6..0dd1ae120b8 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/SharedDomainFactoryImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/SharedDomainFactoryImpl.java @@ -154,14 +154,23 @@ public class SharedDomainFactoryImpl implements SharedDomainFactory { return getOrCreateMark(name, name); } + @Override + public Mark getOrCreateMark(String name, MarkType markType) { + return getOrCreateMark(name, name, markType); + } + + private Mark getOrCreateMark(Serializable id, String name, MarkType markType) { + return getOrCreateMark(id, name, markType, /* color */ null, /* shape */ null, /* pattern */ null); + } + @Override public Mark getOrCreateMark(Serializable id, String name) { - return getOrCreateMark(id, name, null, null, null, null); + return getOrCreateMark(id, name, /* type */ null, /* color */ null, /* shape */ null, /* pattern */ null); } @Override public Mark getOrCreateMark(String toStringRepresentationOfID, String name) { - return getOrCreateMark(toStringRepresentationOfID, name, null, null, null, null); + return getOrCreateMark(toStringRepresentationOfID, name, /* type */ null, /* color */ null, /* shape */ null, /* pattern */ null); } @Override diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TransientCompetitorStoreImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TransientCompetitorStoreImpl.java index 095928e6583..75a5e22baab 100755 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TransientCompetitorStoreImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/TransientCompetitorStoreImpl.java @@ -250,7 +250,8 @@ public class TransientCompetitorStoreImpl implements CompetitorStore, Serializab c.getFlagImage() == null ? null : c.getFlagImage().toString(), new BoatDTO(c.getBoat().getName(), c.getBoat().getSailID(), c.getBoat().getColor()), new BoatClassDTO(c.getBoat().getBoatClass() - .getName(), c.getBoat().getBoatClass().getDisplayName(), c.getBoat().getBoatClass().getHullLength()), + .getName(), c.getBoat().getBoatClass().getDisplayName(), c.getBoat().getBoatClass().getHullLength(), + c.getBoat().getBoatClass().getHullBeam()), c.getTimeOnTimeFactor(), c.getTimeOnDistanceAllowancePerNauticalMile(), c.getSearchTag()); weakCompetitorDTOCache.put(c, competitorDTO); } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/RaceCell.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/RaceCell.java index 833f0a6fac2..f55fef33e8c 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/RaceCell.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/RaceCell.java @@ -1,6 +1,7 @@ package com.sap.sailing.domain.base.racegroup; import com.sap.sailing.domain.abstractlog.race.RaceLog; +import com.sap.sailing.domain.common.TargetTimeInfo; import com.sap.sse.common.Named; import com.sap.sse.common.Util; @@ -33,4 +34,11 @@ public interface RaceCell extends Named { * index 0. */ int getZeroBasedIndexInFleet(); + + /** + * If there is a tracked race for this race and we have sufficient information about wind and the boat class's + * polar / VPP data this method will return an estimated duration for how long the race will take based on the + * current course. + */ + TargetTimeInfo getTargetTime(); } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/impl/RaceCellImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/impl/RaceCellImpl.java index cb69b6d7b83..c645efb79e7 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/impl/RaceCellImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/impl/RaceCellImpl.java @@ -2,6 +2,7 @@ package com.sap.sailing.domain.base.racegroup.impl; import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.base.racegroup.RaceCell; +import com.sap.sailing.domain.common.TargetTimeInfo; import com.sap.sse.common.Util; import com.sap.sse.common.impl.NamedImpl; @@ -21,6 +22,8 @@ public class RaceCellImpl extends NamedImpl implements RaceCell { private final Double explicitFactor; private final int zeroBasedIndexInFleet; + + private final TargetTimeInfo targetTime; /** * @param zeroBasedIndexInFleet @@ -34,12 +37,13 @@ public class RaceCellImpl extends NamedImpl implements RaceCell { * except in case the first race column is a "virtual" one that holds a non-discardable carry-forward * result. In this case, the second Race Column, which is the first "non-virtual" one, receives index 0. */ - public RaceCellImpl(String raceColumnName, RaceLog raceLog, double factor, Double explicitFactor, int zeroBasedIndexInFleet) { + public RaceCellImpl(String raceColumnName, RaceLog raceLog, double factor, Double explicitFactor, int zeroBasedIndexInFleet, TargetTimeInfo targetTime) { super(raceColumnName); this.raceLog = raceLog; this.factor = factor; this.explicitFactor = explicitFactor; this.zeroBasedIndexInFleet = zeroBasedIndexInFleet; + this.targetTime = targetTime; } @Override @@ -70,9 +74,14 @@ public class RaceCellImpl extends NamedImpl implements RaceCell { return zeroBasedIndexInFleet; } + @Override + public TargetTimeInfo getTargetTime() { + return targetTime; + } + @Override public String toString() { return "RaceCellImpl [factor=" + factor + ", explicitFactor=" + explicitFactor + ", zeroBasedIndexInFleet=" - + zeroBasedIndexInFleet + ", getName()=" + getName() + "]"; + + zeroBasedIndexInFleet + ", name=" + getName() + ", targetTime: " + getTargetTime() + "]"; } } diff --git a/java/com.sap.sailing.domain.swisstimingadapter.persistence/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.swisstimingadapter.persistence/META-INF/MANIFEST.MF index e327c07a9c4..faaf538f3ae 100644 --- a/java/com.sap.sailing.domain.swisstimingadapter.persistence/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.swisstimingadapter.persistence/META-INF/MANIFEST.MF @@ -10,8 +10,12 @@ Require-Bundle: com.sap.sailing.domain.swisstimingadapter, org.mongodb.mongo-java-driver;bundle-version="2.13.0", com.sap.sse.mongodb, com.sap.sailing.domain, - com.sap.sailing.domain.common + com.sap.sailing.domain.common, + com.sap.sse.common, + com.sap.sailing.domain.persistence, + com.sap.sse Export-Package: com.sap.sailing.domain.swisstimingadapter.persistence, - com.sap.sailing.domain.swisstimingadapter.persistence.impl; x-friends:="com.sap.sailing.domain.swisstimingadapter.test" -Import-Package: org.osgi.framework + com.sap.sailing.domain.swisstimingadapter.persistence.impl;x-friends:="com.sap.sailing.domain.swisstimingadapter.test,com.sap.sailing.mongodb.test" +Import-Package: org.osgi.framework, + org.osgi.util.tracker;version="1.5.1" Bundle-Activator: com.sap.sailing.domain.swisstimingadapter.persistence.impl.Activator diff --git a/java/com.sap.sailing.domain.swisstimingadapter.persistence/src/com/sap/sailing/domain/swisstimingadapter/persistence/impl/Activator.java b/java/com.sap.sailing.domain.swisstimingadapter.persistence/src/com/sap/sailing/domain/swisstimingadapter/persistence/impl/Activator.java index d2714ee33bc..5514e93770a 100755 --- a/java/com.sap.sailing.domain.swisstimingadapter.persistence/src/com/sap/sailing/domain/swisstimingadapter/persistence/impl/Activator.java +++ b/java/com.sap.sailing.domain.swisstimingadapter.persistence/src/com/sap/sailing/domain/swisstimingadapter/persistence/impl/Activator.java @@ -1,16 +1,52 @@ package com.sap.sailing.domain.swisstimingadapter.persistence.impl; +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.logging.Level; +import java.util.logging.Logger; + import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; +import org.osgi.util.tracker.ServiceTracker; +import com.sap.sailing.domain.persistence.DomainObjectFactory; +import com.sap.sailing.domain.persistence.MongoObjectFactory; +import com.sap.sailing.domain.persistence.MongoRaceLogStoreFactory; +import com.sap.sailing.domain.persistence.MongoRegattaLogStoreFactory; +import com.sap.sailing.domain.swisstimingadapter.impl.SwissTimingAdapterFactoryImpl; +import com.sap.sailing.domain.swisstimingadapter.impl.SwissTimingTrackingConnectivityParameters; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler; +import com.sap.sse.common.TypeBasedServiceFinder; import com.sap.sse.mongodb.MongoDBService; +import com.sap.sse.util.ServiceTrackerFactory; public class Activator implements BundleActivator { + private static final Logger logger = Logger.getLogger(Activator.class.getName()); + @Override public void start(BundleContext context) throws Exception { for (CollectionNames name : CollectionNames.values()) { MongoDBService.INSTANCE.registerExclusively(CollectionNames.class, name.name()); } + new Thread(() -> { + final ServiceTracker mongoObjectFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, MongoObjectFactory.class); + final ServiceTracker domainObjectFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, DomainObjectFactory.class); + try { + final MongoObjectFactory mongoObjectFactory = mongoObjectFactoryServiceTracker.waitForService(0); + final DomainObjectFactory domainObjectFactory = domainObjectFactoryServiceTracker.waitForService(0); + final Dictionary properties = new Hashtable(); + final com.sap.sailing.domain.swisstimingadapter.DomainFactory domainFactory = new SwissTimingAdapterFactoryImpl().getOrCreateSwissTimingAdapter( + domainObjectFactory.getBaseDomainFactory()).getSwissTimingDomainFactory(); + final SwissTimingConnectivityParamsHandler paramsHandler = new SwissTimingConnectivityParamsHandler( + MongoRaceLogStoreFactory.INSTANCE.getMongoRaceLogStore(mongoObjectFactory, domainObjectFactory), + MongoRegattaLogStoreFactory.INSTANCE.getMongoRegattaLogStore(mongoObjectFactory, domainObjectFactory), + domainFactory); + properties.put(TypeBasedServiceFinder.TYPE, SwissTimingTrackingConnectivityParameters.TYPE); + context.registerService(RaceTrackingConnectivityParametersHandler.class, paramsHandler, properties); + } catch (Exception e) { + logger.log(Level.SEVERE, "Exception trying to register SwissTiming RaceTrackingConnectivityParametersHandler implementation", e); + } + }, getClass().getName() + " registering connectivity handler").start(); } @Override diff --git a/java/com.sap.sailing.domain.swisstimingadapter.persistence/src/com/sap/sailing/domain/swisstimingadapter/persistence/impl/SwissTimingConnectivityParamsHandler.java b/java/com.sap.sailing.domain.swisstimingadapter.persistence/src/com/sap/sailing/domain/swisstimingadapter/persistence/impl/SwissTimingConnectivityParamsHandler.java new file mode 100644 index 00000000000..a643a9db27f --- /dev/null +++ b/java/com.sap.sailing.domain.swisstimingadapter.persistence/src/com/sap/sailing/domain/swisstimingadapter/persistence/impl/SwissTimingConnectivityParamsHandler.java @@ -0,0 +1,192 @@ +package com.sap.sailing.domain.swisstimingadapter.persistence.impl; + +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.mongodb.BasicDBList; +import com.mongodb.BasicDBObject; +import com.mongodb.DBObject; +import com.sap.sailing.domain.racelog.RaceLogStore; +import com.sap.sailing.domain.regattalog.RegattaLogStore; +import com.sap.sailing.domain.swisstimingadapter.Competitor; +import com.sap.sailing.domain.swisstimingadapter.CrewMember; +import com.sap.sailing.domain.swisstimingadapter.DomainFactory; +import com.sap.sailing.domain.swisstimingadapter.StartList; +import com.sap.sailing.domain.swisstimingadapter.SwissTimingFactory; +import com.sap.sailing.domain.swisstimingadapter.impl.CompetitorWithID; +import com.sap.sailing.domain.swisstimingadapter.impl.CompetitorWithoutID; +import com.sap.sailing.domain.swisstimingadapter.impl.CrewMemberImpl; +import com.sap.sailing.domain.swisstimingadapter.impl.StartListImpl; +import com.sap.sailing.domain.swisstimingadapter.impl.SwissTimingTrackingConnectivityParameters; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; +import com.sap.sailing.domain.tracking.impl.AbstractRaceTrackingConnectivityParametersHandler; +import com.sap.sse.common.TypeBasedServiceFinder; + +/** + * Handles mapping TracTrac connectivity parameters from and to a map with {@link String} keys. The + * "param URL" is considered the {@link #getKey(RaceTrackingConnectivityParameters) key} for these objects.

    + * + * Lives in the same package as {@link RaceTrackingConnectivityParameters} for package-private access to + * its members. + * + * @author Axel Uhl (d043530) + * + */ +public class SwissTimingConnectivityParamsHandler extends AbstractRaceTrackingConnectivityParametersHandler { + private static final String CREW_MEMBER_POSITION = "crewMemberPosition"; + private static final String CREW_MEMBER_NATIONALITY = "crewMemberNationality"; + private static final String CREW_MEMBER_NAME = "crewMemberName"; + private static final String COMPETITOR_CREW = "competitorCrew"; + private static final String COMPETITOR_THREE_LETTER_IOC_CODE = "competitorThreeLetterIocCode"; + private static final String COMPETITOR_NAME = "competitorName"; + private static final String COMPETITOR_BOAT_ID = "competitorBoatId"; + private static final String COMPETITOR_ID = "competitorId"; + private static final String USE_INTERNAL_MARK_PASSING_ALGORITHM = "useInternalMarkPassingAlgorithm"; + private static final String DELAY_TO_LIVE_IN_MILLIS = "delayToLiveInMillis"; + private static final String START_LIST = "startList"; + private static final String RACE_NAME = "raceName"; + private static final String RACE_ID = "raceID"; + private static final String RACE_DESCRIPTION = "raceDescription"; + private static final String PORT = "port"; + private static final String BOAT_CLASS_NAME = "boatClassName"; + private static final String HOSTNAME = "hostname"; + private final RaceLogStore raceLogStore; + private final RegattaLogStore regattaLogStore; + private final DomainFactory domainFactory; + private final SwissTimingFactory swissTimingFactory; + + public SwissTimingConnectivityParamsHandler(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, DomainFactory domainFactory) { + super(); + this.raceLogStore = raceLogStore; + this.regattaLogStore = regattaLogStore; + this.domainFactory = domainFactory; + this.swissTimingFactory = SwissTimingFactory.INSTANCE; + } + + @Override + public Map mapFrom(RaceTrackingConnectivityParameters params) { + assert params instanceof SwissTimingTrackingConnectivityParameters; + final SwissTimingTrackingConnectivityParameters stParams = (SwissTimingTrackingConnectivityParameters) params; + final Map result = getKey(params); + result.put(BOAT_CLASS_NAME, stParams.getBoatClass()==null?null:stParams.getBoatClass().getName()); + result.put(RACE_DESCRIPTION, stParams.getRaceDescription()); + result.put(RACE_NAME, stParams.getRaceName()); + result.put(START_LIST, createStartListDBObject(stParams)); + result.put(DELAY_TO_LIVE_IN_MILLIS, stParams.getDelayToLiveInMillis()); + result.put(USE_INTERNAL_MARK_PASSING_ALGORITHM, stParams.isUseInternalMarkPassingAlgorithm()); + addWindTrackingParameters(stParams, result); + return result; + } + + private BasicDBList createStartListDBObject(final SwissTimingTrackingConnectivityParameters stParams) { + final BasicDBList startListDbObject; + if (stParams.getStartList() != null) { + startListDbObject = new BasicDBList(); + for (final Competitor competitor : stParams.getStartList().getCompetitors()) { + DBObject competitorDBObject = createCompetitorDBObject(competitor); + startListDbObject.add(competitorDBObject); + } + } else { + startListDbObject = null; + } + return startListDbObject; + } + + private StartList createStartListFromDBObject(String raceId, BasicDBList startListDBObject) { + List competitors = new ArrayList<>(); + for (final Object competitorObject : startListDBObject) { + final DBObject competitorDBObject = (DBObject) competitorObject; + competitors.add(createCompetitorFromDBObject(competitorDBObject)); + } + return new StartListImpl(raceId, competitors); + } + + private DBObject createCompetitorDBObject(Competitor competitor) { + final DBObject result = new BasicDBObject(); + result.put(COMPETITOR_ID, competitor.getID()); + result.put(COMPETITOR_BOAT_ID, competitor.getBoatID()); + result.put(COMPETITOR_NAME, competitor.getName()); + result.put(COMPETITOR_THREE_LETTER_IOC_CODE, competitor.getThreeLetterIOCCode()); + result.put(COMPETITOR_CREW, createCrewDbObject(competitor.getCrew())); + return result; + } + + private BasicDBList createCrewDbObject(List crew) { + final BasicDBList result; + if (crew == null) { + result = null; + } else { + result = new BasicDBList(); + for (final CrewMember crewMember : crew) { + final DBObject crewMemberDBObject = new BasicDBObject(); + crewMemberDBObject.put(CREW_MEMBER_NAME, crewMember.getName()); + crewMemberDBObject.put(CREW_MEMBER_NATIONALITY, crewMember.getNationality()); + crewMemberDBObject.put(CREW_MEMBER_POSITION, crewMember.getPosition()); + result.add(crewMemberDBObject); + } + } + return result; + } + + private List createCrewFromDBObject(BasicDBList crewDBObject) { + final List result; + if (crewDBObject == null) { + result = null; + } else { + result = new ArrayList<>(); + for (final Object o : crewDBObject) { + final DBObject crewMemberDBObject = (DBObject) o; + result.add(new CrewMemberImpl((String) crewMemberDBObject.get(CREW_MEMBER_NAME), + (String) crewMemberDBObject.get(CREW_MEMBER_NATIONALITY), + (String) crewMemberDBObject.get(CREW_MEMBER_POSITION))); + } + } + return result; + } + + private Competitor createCompetitorFromDBObject(DBObject competitorDBObject) { + final Competitor result; + final String competitorId = (String) competitorDBObject.get(COMPETITOR_ID); + final String boatID = (String) competitorDBObject.get(COMPETITOR_BOAT_ID); + final String threeLetterIOCCode = (String) competitorDBObject.get(COMPETITOR_THREE_LETTER_IOC_CODE); + final String name = (String) competitorDBObject.get(COMPETITOR_NAME); + final BasicDBList crewDBObject = (BasicDBList) competitorDBObject.get(COMPETITOR_CREW); + if (competitorId == null) { + result = new CompetitorWithoutID(boatID, threeLetterIOCCode, name); + } else { + result = new CompetitorWithID(competitorId, boatID, threeLetterIOCCode, name, createCrewFromDBObject(crewDBObject)); + } + return result; + } + + @Override + public RaceTrackingConnectivityParameters mapTo(Map map) throws MalformedURLException, URISyntaxException { + return new SwissTimingTrackingConnectivityParameters( + (String) map.get(HOSTNAME), + ((Number) map.get(PORT)).intValue(), + (String) map.get(RACE_ID), + (String) map.get(RACE_NAME), + (String) map.get(RACE_DESCRIPTION), + domainFactory.getBaseDomainFactory().getOrCreateBoatClass((String) map.get(BOAT_CLASS_NAME)), + map.get(START_LIST) == null ? null : createStartListFromDBObject((String) map.get(RACE_ID), (BasicDBList) map.get(START_LIST)), + ((Number) map.get(DELAY_TO_LIVE_IN_MILLIS)).longValue(), + swissTimingFactory, domainFactory, raceLogStore, regattaLogStore, + (boolean) map.get(USE_INTERNAL_MARK_PASSING_ALGORITHM), isTrackWind(map), isCorrectWindDirectionByMagneticDeclination(map)); + } + + @Override + public Map getKey(RaceTrackingConnectivityParameters params) { + assert params instanceof SwissTimingTrackingConnectivityParameters; + final SwissTimingTrackingConnectivityParameters stParams = (SwissTimingTrackingConnectivityParameters) params; + final Map result = new HashMap<>(); + result.put(TypeBasedServiceFinder.TYPE, params.getTypeIdentifier()); + result.put(HOSTNAME, stParams.getHostname()); + result.put(PORT, stParams.getPort()); + result.put(RACE_ID, stParams.getRaceID()); + return result; + } +} diff --git a/java/com.sap.sailing.domain.swisstimingadapter.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.swisstimingadapter.test/META-INF/MANIFEST.MF index 719c89203da..24425cd678b 100644 --- a/java/com.sap.sailing.domain.swisstimingadapter.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.swisstimingadapter.test/META-INF/MANIFEST.MF @@ -16,7 +16,7 @@ Require-Bundle: com.sap.sailing.domain, com.sap.sailing.domain.persistence, com.sap.sailing.domain.tractracadapter, com.sap.sailing.domain.swisstimingadapter.persistence;bundle-version="1.0.0", - org.junit4;bundle-version="4.8.2", + org.junit;bundle-version="4.8.2", org.hamcrest.core;bundle-version="1.1.0", com.sap.sse, com.sap.sse.common diff --git a/java/com.sap.sailing.domain.swisstimingadapter/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.swisstimingadapter/META-INF/MANIFEST.MF index ce94c7a2bf0..b382124711f 100644 --- a/java/com.sap.sailing.domain.swisstimingadapter/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.swisstimingadapter/META-INF/MANIFEST.MF @@ -16,7 +16,7 @@ Require-Bundle: com.sap.sailing.domain, com.sap.sailing.xrr.schema, com.sap.sailing.domain.shared.android Export-Package: com.sap.sailing.domain.swisstimingadapter, - com.sap.sailing.domain.swisstimingadapter.impl;x-friends:="com.sap.sailing.server.test" + com.sap.sailing.domain.swisstimingadapter.impl;x-friends:="com.sap.sailing.server.test,com.sap.sailing.mongodb.test,com.sap.sailing.domain.swisstimingadapter.persistence" Import-Package: com.sap.sailing.xrr.schema, org.osgi.framework Bundle-Activator: com.sap.sailing.domain.swisstimingadapter.impl.Activator diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/CrewMember.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/CrewMember.java index 0406a38b8ff..73c926d3717 100644 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/CrewMember.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/CrewMember.java @@ -1,9 +1,9 @@ package com.sap.sailing.domain.swisstimingadapter; public interface CrewMember { - public String getName(); - - public String getNationality(); + public String getName(); - public String getPosition(); + public String getNationality(); + + public String getPosition(); } 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 929614077f3..95cfd8093db 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 @@ -10,6 +10,7 @@ import com.sap.sailing.domain.base.Nationality; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Waypoint; +import com.sap.sailing.domain.common.MarkType; import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.regattalog.RegattaLogStore; @@ -31,6 +32,9 @@ public interface DomainFactory { com.sap.sailing.domain.base.DomainFactory getBaseDomainFactory(); + /** + * @param boatClass if {@code null}, the boat class will be inferred from the Race ID + */ Regatta getOrCreateDefaultRegatta(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, String raceID, BoatClass boatClass, TrackedRegattaRegistry trackedRegattaRegistry); Nationality getOrCreateNationality(String threeLetterIOCCode); @@ -62,11 +66,10 @@ public interface DomainFactory { RaceTrackingConnectivityParameters createTrackingConnectivityParameters(String hostname, int port, String raceID, String raceName, String raceDescription, BoatClass boatClass, StartList startList, long delayToLiveInMillis, SwissTimingFactory swissTimingFactory, DomainFactory domainFactory, - RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, boolean useInternalMarkPassingAlgorithm); + RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, boolean useInternalMarkPassingAlgorithm, boolean trackWind, boolean correctWindDirectionByMagneticDeclination); - ControlPoint getOrCreateControlPoint(Iterable devices); - - RaceDefinition createRaceDefinition(Regatta regatta, String raceID, Iterable competitors, - List courseDefinition); + ControlPoint getOrCreateControlPoint(Iterable devices, MarkType markType); + RaceDefinition createRaceDefinition(Regatta regatta, String swissTimingRaceID, Iterable competitors, + List courseDefinition, String raceName, String raceIdForRaceDefinition); } diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/SwissTimingAdapter.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/SwissTimingAdapter.java index 4d75dbecd80..e275b3573c8 100755 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/SwissTimingAdapter.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/SwissTimingAdapter.java @@ -26,10 +26,12 @@ public interface SwissTimingAdapter { * is specified, a regatta lookup is performed with that identifier; if the regatta is found, it is used * to add the races to. Otherwise, a default regatta as described above will be created and used. * @param useInternalMarkPassingAlgorithm use our own instead of the SwissTiming-provided mark passing / split times + * @param trackWind TODO + * @param correctWindDirectionByMagneticDeclination TODO */ RaceHandle addSwissTimingRace(TrackerManager trackerManager, RegattaIdentifier regattaToAddTo, String raceID, String raceName, String raceDescription, BoatClass boatClass, String hostname, int port, - StartList startList, RaceLogStore logStore, RegattaLogStore regattaLogStore, long timeoutInMilliseconds, boolean useInternalMarkPassingAlgorithm) + StartList startList, RaceLogStore logStore, RegattaLogStore regattaLogStore, long timeoutInMilliseconds, boolean useInternalMarkPassingAlgorithm, boolean trackWind, boolean correctWindDirectionByMagneticDeclination) throws InterruptedException, UnknownHostException, IOException, ParseException, Exception; StartList readStartListForRace(String raceId, RegattaResults regattaResults); diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/SwissTimingFactory.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/SwissTimingFactory.java index 8f3780103c4..4c99a0b2c3e 100755 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/SwissTimingFactory.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/SwissTimingFactory.java @@ -10,6 +10,7 @@ import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.regattalog.RegattaLogStore; import com.sap.sailing.domain.swisstimingadapter.impl.SwissTimingFactoryImpl; +import com.sap.sailing.domain.swisstimingadapter.impl.SwissTimingTrackingConnectivityParameters; import com.sap.sailing.domain.tracking.RaceTracker; import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; import com.sap.sailing.domain.tracking.WindStore; @@ -54,14 +55,15 @@ public interface SwissTimingFactory { BoatClass boatClass, String hostname, int port, StartList startList, long delayToLiveInMillis, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, boolean useInternalMarkPassingAlgorithm, DomainFactory domainFactory, - TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws InterruptedException, UnknownHostException, + TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, SwissTimingTrackingConnectivityParameters connectivityParams) throws InterruptedException, UnknownHostException, IOException, ParseException; RaceTracker createRaceTracker(Regatta regatta, String raceID, String raceName, String raceDescription, BoatClass boatClass, String hostname, int port, StartList startList, long delayToLiveInMillis, - WindStore windStore, boolean useInternalMarkPassingAlgorithm, - DomainFactory domainFactory, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws UnknownHostException, - InterruptedException, IOException, ParseException; + WindStore windStore, boolean useInternalMarkPassingAlgorithm, DomainFactory domainFactory, + TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, RaceLogStore raceLogStore, + RegattaLogStore regattaLogStore, SwissTimingTrackingConnectivityParameters connectivityParams) + throws UnknownHostException, InterruptedException, IOException, ParseException; Race createRace(String raceId, String raceName, String description, BoatClass boatClass); diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/AbstractCompetitor.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/AbstractCompetitor.java new file mode 100644 index 00000000000..a36fdf74a26 --- /dev/null +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/AbstractCompetitor.java @@ -0,0 +1,72 @@ +package com.sap.sailing.domain.swisstimingadapter.impl; + +import com.sap.sailing.domain.swisstimingadapter.Competitor; + +public abstract class AbstractCompetitor implements Competitor { + private final String boatID; + private final String threeLetterIOCCode; + private final String name; + + public AbstractCompetitor(String boatID, String threeLetterIOCCode, String name) { + super(); + this.boatID = boatID; + this.threeLetterIOCCode = threeLetterIOCCode; + this.name = name; + } + + @Override + public String getBoatID() { + return boatID; + } + + @Override + public String getThreeLetterIOCCode() { + return threeLetterIOCCode; + } + + @Override + public String getName() { + return name; + } + + public String getID() { + return null; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((boatID == null) ? 0 : boatID.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((threeLetterIOCCode == null) ? 0 : threeLetterIOCCode.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AbstractCompetitor other = (AbstractCompetitor) obj; + if (boatID == null) { + if (other.boatID != null) + return false; + } else if (!boatID.equals(other.boatID)) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (threeLetterIOCCode == null) { + if (other.threeLetterIOCCode != null) + return false; + } else if (!threeLetterIOCCode.equals(other.threeLetterIOCCode)) + return false; + return true; + } +} diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/CompetitorWithID.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/CompetitorWithID.java index 717d4f38716..a90b0d09e91 100644 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/CompetitorWithID.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/CompetitorWithID.java @@ -2,54 +2,61 @@ package com.sap.sailing.domain.swisstimingadapter.impl; import java.util.List; -import com.sap.sailing.domain.swisstimingadapter.Competitor; import com.sap.sailing.domain.swisstimingadapter.CrewMember; -public class CompetitorWithID implements Competitor { - private final String boatID; - private final String threeLetterIOCCode; - private final String name; +public class CompetitorWithID extends AbstractCompetitor { private final String id; - private List crew; public CompetitorWithID(String id, String boatID, String threeLetterIOCCode, String name, List crew) { - super(); - this.boatID = boatID; - this.threeLetterIOCCode = threeLetterIOCCode; - this.name = name; + super(boatID, threeLetterIOCCode, name); this.id = id; this.crew = crew; } - @Override - public String getBoatID() { - return boatID; - } - - @Override - public String getThreeLetterIOCCode() { - return threeLetterIOCCode; - } - - @Override - public String getName() { - return name; - } - public String getID() { return id; } - @Override - public String toString() { - return "CompetitorWithID [boatID=" + boatID + ", threeLetterIOCCode=" + threeLetterIOCCode + ", name=" + name - + ", id=" + id + "]"; - } - @Override public List getCrew() { return crew; } + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((crew == null) ? 0 : crew.hashCode()); + result = prime * result + ((id == null) ? 0 : id.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + CompetitorWithID other = (CompetitorWithID) obj; + if (crew == null) { + if (other.crew != null) + return false; + } else if (!crew.equals(other.crew)) + return false; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + return true; + } + + @Override + public String toString() { + return "CompetitorWithID [boatID=" + getBoatID() + ", threeLetterIOCCode=" + getThreeLetterIOCCode() + ", name=" + getName() + + ", id=" + id + ", crew=" + getCrew() + "]"; + } } diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/CompetitorWithoutID.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/CompetitorWithoutID.java index 80b484c798f..ba734b1fdf2 100755 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/CompetitorWithoutID.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/CompetitorWithoutID.java @@ -2,49 +2,25 @@ package com.sap.sailing.domain.swisstimingadapter.impl; import java.util.List; -import com.sap.sailing.domain.swisstimingadapter.Competitor; import com.sap.sailing.domain.swisstimingadapter.CrewMember; -public class CompetitorWithoutID implements Competitor { - private final String boatID; - private final String threeLetterIOCCode; - private final String name; - +public class CompetitorWithoutID extends AbstractCompetitor { public CompetitorWithoutID(String boatID, String threeLetterIOCCode, String name) { - super(); - this.boatID = boatID; - this.threeLetterIOCCode = threeLetterIOCCode; - this.name = name; + super(boatID, threeLetterIOCCode, name); } @Override - public String getBoatID() { - return boatID; - } - - @Override - public String getThreeLetterIOCCode() { - return threeLetterIOCCode; - } - - @Override - public String getName() { - return name; - } - public String getID() { return null; } - @Override - public String toString() { - return "CompetitorWithoutID [boatID=" + boatID + ", threeLetterIOCCode=" - + threeLetterIOCCode + ", name=" + name + "]"; - } - - @Override - public List getCrew() { - return null; - } + @Override + public List getCrew() { + return null; + } + @Override + public String toString() { + return "CompetitorWithoutID [boatID=" + getBoatID() + ", threeLetterIOCCode=" + getThreeLetterIOCCode() + ", name=" + getName() + "]"; + } } diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/CrewMemberImpl.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/CrewMemberImpl.java index edd06e5ca15..eebe63cbbc8 100644 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/CrewMemberImpl.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/CrewMemberImpl.java @@ -3,29 +3,70 @@ package com.sap.sailing.domain.swisstimingadapter.impl; import com.sap.sailing.domain.swisstimingadapter.CrewMember; public class CrewMemberImpl implements CrewMember { - private String name; - private String nationality; - private String position; + private String name; + private String nationality; + private String position; - public CrewMemberImpl(String name, String nationality, String position) { - super(); - this.name = name; - this.nationality = nationality; - this.position = position; - } + public CrewMemberImpl(String name, String nationality, String position) { + super(); + this.name = name; + this.nationality = nationality; + this.position = position; + } - @Override - public String getName() { - return name; - } + @Override + public String getName() { + return name; + } - @Override - public String getNationality() { - return nationality; - } + @Override + public String getNationality() { + return nationality; + } - public String getPosition() { - return position; - } + public String getPosition() { + return position; + } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((nationality == null) ? 0 : nationality.hashCode()); + result = prime * result + ((position == null) ? 0 : position.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + CrewMemberImpl other = (CrewMemberImpl) obj; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (nationality == null) { + if (other.nationality != null) + return false; + } else if (!nationality.equals(other.nationality)) + return false; + if (position == null) { + if (other.position != null) + return false; + } else if (!position.equals(other.position)) + return false; + return true; + } + + @Override + public String toString() { + return "CrewMemberImpl [name=" + name + ", nationality=" + nationality + ", position=" + position + "]"; + } } 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 fd218645037..57c5daaa79f 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 @@ -28,6 +28,7 @@ import com.sap.sailing.domain.base.impl.PersonImpl; import com.sap.sailing.domain.base.impl.RaceDefinitionImpl; import com.sap.sailing.domain.base.impl.RegattaImpl; import com.sap.sailing.domain.base.impl.TeamImpl; +import com.sap.sailing.domain.common.MarkType; import com.sap.sailing.domain.common.PassingInstruction; import com.sap.sailing.domain.common.ScoringSchemeType; import com.sap.sailing.domain.common.tracking.GPSFixMoving; @@ -176,17 +177,17 @@ public class DomainFactoryImpl implements DomainFactory { } @Override - public RaceDefinition createRaceDefinition(Regatta regatta, String raceID, Iterable competitors, - List courseDefinition) { + public RaceDefinition createRaceDefinition(Regatta regatta, String swissTimingRaceID, Iterable competitors, + List courseDefinition, String raceName, String raceIdForRaceDefinition) { List waypoints = new ArrayList<>(); for (ControlPoint controlPoint : courseDefinition) { Waypoint waypoint = baseDomainFactory.createWaypoint(controlPoint, /* passingInstruction */ PassingInstruction.None); waypoints.add(waypoint); } com.sap.sailing.domain.base.Course domainCourse = new CourseImpl("Course", waypoints); - BoatClass boatClass = getRaceTypeFromRaceID(raceID).getBoatClass(); - logger.info("Creating RaceDefinitionImpl for race "+raceID); - RaceDefinition result = new RaceDefinitionImpl(regatta.getId().toString()+"/"+raceID, domainCourse, boatClass, competitors); + BoatClass boatClass = getRaceTypeFromRaceID(swissTimingRaceID).getBoatClass(); + logger.info("Creating RaceDefinitionImpl for race "+swissTimingRaceID); + RaceDefinition result = new RaceDefinitionImpl(raceName, domainCourse, boatClass, competitors, raceIdForRaceDefinition); regatta.addRace(result); return result; } @@ -264,7 +265,7 @@ public class DomainFactoryImpl implements DomainFactory { private com.sap.sailing.domain.base.Course createCourse(String courseName, Course course) { List waypoints = new ArrayList(); for (Mark mark : course.getMarks()) { - ControlPoint controlPoint = getOrCreateControlPoint(mark.getDevices()); + ControlPoint controlPoint = getOrCreateControlPoint(mark.getDevices(), getMarkType(mark.getMarkType())); Waypoint waypoint = baseDomainFactory.createWaypoint(controlPoint, /* passingInstruction */ PassingInstruction.None); waypoints.add(waypoint); } @@ -272,15 +273,28 @@ public class DomainFactoryImpl implements DomainFactory { return result; } + /** + * Converts a mark type as defined by SwissTiming into a {@link MarkType} as defined by the domain model + */ + private MarkType getMarkType(com.sap.sailing.domain.swisstimingadapter.Mark.MarkType markType) { + final MarkType result; + if (markType == null) { + result = null; + } else { + result = MarkType.BUOY; + } + return result; + } + @Override - public ControlPoint getOrCreateControlPoint(Iterable devices) { + public ControlPoint getOrCreateControlPoint(Iterable devices, MarkType markType) { ControlPoint result; synchronized (controlPointCache) { result = controlPointCache.get(devices); if (result == null) { switch (Util.size(devices)) { case 1: - result = getOrCreateMark(devices.iterator().next()); + result = getOrCreateMark(devices.iterator().next(), markType); break; case 2: Iterator markNameIter = devices.iterator(); @@ -299,6 +313,10 @@ public class DomainFactoryImpl implements DomainFactory { return result; } + private ControlPoint getOrCreateMark(String name, MarkType markType) { + return baseDomainFactory.getOrCreateMark(name, markType); + } + /** * @param trackerID * the "device name" and the "sail number" in case of an {@link MessageType#RPD RPD} message, used as the mark's @@ -321,7 +339,7 @@ public class DomainFactoryImpl implements DomainFactory { List> newDomainControlPoints = new ArrayList>(); for (Mark mark : marks) { // TODO bug 1043: propagate the mark names to the waypoint names - com.sap.sailing.domain.base.ControlPoint domainControlPoint = getOrCreateControlPoint(mark.getDevices()); + com.sap.sailing.domain.base.ControlPoint domainControlPoint = getOrCreateControlPoint(mark.getDevices(), getMarkType(mark.getMarkType())); newDomainControlPoints.add(new com.sap.sse.common.Util.Pair<>(domainControlPoint, PassingInstruction.None)); } courseToUpdate.update(newDomainControlPoints, baseDomainFactory); @@ -359,10 +377,10 @@ public class DomainFactoryImpl implements DomainFactory { public RaceTrackingConnectivityParameters createTrackingConnectivityParameters(String hostname, int port, String raceID, String raceName, String raceDescription, BoatClass boatClass, StartList startList, long delayToLiveInMillis, SwissTimingFactory swissTimingFactory, DomainFactory domainFactory, - RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, boolean useInternalMarkPassingAlgorithm) { + RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, boolean useInternalMarkPassingAlgorithm, + boolean trackWind, boolean correctWindDirectionByMagneticDeclination) { return new SwissTimingTrackingConnectivityParameters(hostname, port, raceID, raceName, raceDescription, boatClass, startList, delayToLiveInMillis, swissTimingFactory, domainFactory, raceLogStore, - regattaLogStore, useInternalMarkPassingAlgorithm); + regattaLogStore, useInternalMarkPassingAlgorithm, trackWind, correctWindDirectionByMagneticDeclination); } - } diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingAdapterImpl.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingAdapterImpl.java index 5068ea0a08e..d43f6119d75 100755 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingAdapterImpl.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingAdapterImpl.java @@ -59,94 +59,96 @@ public class SwissTimingAdapterImpl implements SwissTimingAdapter { @Override public List getSwissTimingRaceRecords(String hostname, - int port) throws InterruptedException, UnknownHostException, IOException, - ParseException { + int port) throws InterruptedException, UnknownHostException, IOException, ParseException { List result = new ArrayList(); // TODO --> Frank: this needs to come from Manage2Sail and its JSON document somehow... return result; } @Override - public RaceHandle addSwissTimingRace(TrackerManager trackerManager, RegattaIdentifier regattaToAddTo, - String raceID, String raceName, String raceDescription, BoatClass boatClass, String hostname, int port, + public RaceHandle addSwissTimingRace(TrackerManager trackerManager, RegattaIdentifier regattaToAddTo, String raceID, + String raceName, String raceDescription, BoatClass boatClass, String hostname, int port, StartList startList, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, long timeoutInMilliseconds, - boolean useInternalMarkPassingAlgorithm) - throws Exception { - return trackerManager.addRace(regattaToAddTo, swissTimingDomainFactory.createTrackingConnectivityParameters( - hostname, port, raceID, raceName, raceDescription, boatClass, startList, - DEFAULT_SWISSTIMING_LIVE_DELAY_IN_MILLISECONDS, swissTimingFactory, swissTimingDomainFactory, - raceLogStore, regattaLogStore, useInternalMarkPassingAlgorithm), timeoutInMilliseconds); + boolean useInternalMarkPassingAlgorithm, boolean trackWind, boolean correctWindDirectionByMagneticDeclination) throws Exception { + return trackerManager.addRace(regattaToAddTo, + swissTimingDomainFactory.createTrackingConnectivityParameters(hostname, port, raceID, raceName, + raceDescription, boatClass, startList, DEFAULT_SWISSTIMING_LIVE_DELAY_IN_MILLISECONDS, + swissTimingFactory, swissTimingDomainFactory, raceLogStore, regattaLogStore, + useInternalMarkPassingAlgorithm, trackWind, correctWindDirectionByMagneticDeclination), + timeoutInMilliseconds); } @Override public StartList readStartListForRace(String raceId, RegattaResults regattaResults) { - StartList result = null; + StartList result = null; List competitors = new ArrayList(); - Map persons = new HashMap(); - Map boats = new HashMap(); - Map teams = new HashMap(); - // collect boats, persons and teams + Map persons = new HashMap(); + Map boats = new HashMap(); + Map teams = new HashMap(); + // collect boats, persons and teams for (Object o : regattaResults.getPersonOrBoatOrTeam()) { - if(o instanceof Person) { - Person p = (Person) o; - persons.put(p.getPersonID(), p); - } else if (o instanceof Boat) { - Boat b = (Boat) o; - boats.put(b.getBoatID(), b); - } else if (o instanceof Team) { - Team t = (Team) o; - teams.put(t.getTeamID(), t); - } + if (o instanceof Person) { + Person p = (Person) o; + persons.put(p.getPersonID(), p); + } else if (o instanceof Boat) { + Boat b = (Boat) o; + boats.put(b.getBoatID(), b); + } else if (o instanceof Team) { + Team t = (Team) o; + teams.put(t.getTeamID(), t); + } } - + for (Object o : regattaResults.getPersonOrBoatOrTeam()) { if (o instanceof Event) { - Event event = (Event) o; + Event event = (Event) o; for (Object eventO : event.getRaceOrDivisionOrRegattaSeriesResult()) { if (eventO instanceof Division) { Division division = (Division) eventO; - for(Object seriesResultOrRaceResultOrTRResult: division.getSeriesResultOrRaceResultOrTRResult()) { - if(seriesResultOrRaceResultOrTRResult instanceof RaceResult) { - RaceResult raceResult = (RaceResult) seriesResultOrRaceResultOrTRResult; - if(raceResult.getRaceID().equals(raceId)) { - Team team = teams.get(raceResult.getTeamID()); - if(team != null) { - Boat boat = boats.get(team.getBoatID()); - if(boat != null) { - List crew = new ArrayList(); - for(Crew crewMember: team.getCrew()) { - Person person = persons.get(crewMember.getPersonID()); - crew.add(new CrewMemberImpl(person.getGivenName() + " " + person.getFamilyName(), - person.getNOC().name(), crewMember.getPosition().name())); - } - Crew firstCrewMember = team.getCrew().get(0); - Person person = persons.get(firstCrewMember.getPersonID()); - String nationality = person.getNOC().name(); - CompetitorWithID competitor = new CompetitorWithID(team.getTeamID(), boat.getSailNumber(), nationality, team.getTeamName(), crew); - competitors.add(competitor); - } - } - } - } + for (Object seriesResultOrRaceResultOrTRResult : division.getSeriesResultOrRaceResultOrTRResult()) { + if (seriesResultOrRaceResultOrTRResult instanceof RaceResult) { + RaceResult raceResult = (RaceResult) seriesResultOrRaceResultOrTRResult; + if (raceResult.getRaceID().equals(raceId)) { + Team team = teams.get(raceResult.getTeamID()); + if (team != null) { + Boat boat = boats.get(team.getBoatID()); + if (boat != null) { + List crew = new ArrayList(); + for (Crew crewMember : team.getCrew()) { + Person person = persons.get(crewMember.getPersonID()); + crew.add(new CrewMemberImpl( + person.getGivenName() + " " + person.getFamilyName(), + person.getNOC().name(), crewMember.getPosition().name())); + } + Crew firstCrewMember = team.getCrew().get(0); + Person person = persons.get(firstCrewMember.getPersonID()); + String nationality = person.getNOC().name(); + CompetitorWithID competitor = new CompetitorWithID(team.getTeamID(), + boat.getSailNumber(), nationality, team.getTeamName(), crew); + competitors.add(competitor); + } + } + } + } } } } } } - if(!competitors.isEmpty()) { - result = new StartListImpl(raceId, competitors); + if (!competitors.isEmpty()) { + result = new StartListImpl(raceId, competitors); } - return result; } - + @Override public RegattaResults readRegattaEntryListFromXrrUrl(String xrrEntryListUrl) throws IOException, JAXBException { - // try to read the entry list from manage2sail + // try to read the entry list from manage2sail URL regattaEntryListUrl = new URL(xrrEntryListUrl); URLConnection regattaEntryListConn = regattaEntryListUrl.openConnection(); - RegattaResults regattaResults = ParserFactory.INSTANCE.createParser((InputStream) regattaEntryListConn.getContent(), xrrEntryListUrl).parse(); + RegattaResults regattaResults = ParserFactory.INSTANCE + .createParser((InputStream) regattaEntryListConn.getContent(), xrrEntryListUrl).parse(); return regattaResults; } } diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingFactoryImpl.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingFactoryImpl.java index b0473ba08e5..81a626ed55a 100755 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingFactoryImpl.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingFactoryImpl.java @@ -92,22 +92,23 @@ public class SwissTimingFactoryImpl implements SwissTimingFactory { BoatClass boatClass, String hostname, int port, StartList startList, long delayToLiveInMillis, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, boolean useInternalMarkPassingAlgorithm, DomainFactory domainFactory, - TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) + TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, SwissTimingTrackingConnectivityParameters connectivityParams) throws InterruptedException, UnknownHostException, IOException, ParseException { return new SwissTimingRaceTrackerImpl(raceID, raceName, raceDescription, boatClass, hostname, port, startList, raceLogStore, regattaLogStore, windStore, domainFactory, this, - trackedRegattaRegistry, raceLogResolver, delayToLiveInMillis, useInternalMarkPassingAlgorithm); + trackedRegattaRegistry, raceLogResolver, delayToLiveInMillis, useInternalMarkPassingAlgorithm, connectivityParams); } @Override public RaceTracker createRaceTracker(Regatta regatta, String raceID, String raceName, String raceDescription, BoatClass boatClass, String hostname, int port, StartList startList, long delayToLiveInMillis, - WindStore windStore, boolean useInternalMarkPassingAlgorithm, - DomainFactory domainFactory, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) + WindStore windStore, boolean useInternalMarkPassingAlgorithm, DomainFactory domainFactory, + TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, RaceLogStore raceLogStore, + RegattaLogStore regattaLogStore, SwissTimingTrackingConnectivityParameters connectivityParams) throws UnknownHostException, InterruptedException, IOException, ParseException { return new SwissTimingRaceTrackerImpl(regatta, raceID, raceName, raceDescription, boatClass, hostname, port, - startList, windStore, domainFactory, this, trackedRegattaRegistry, raceLogResolver, delayToLiveInMillis, - useInternalMarkPassingAlgorithm); + startList, windStore, domainFactory, this, trackedRegattaRegistry, raceLogStore, regattaLogStore, + raceLogResolver, delayToLiveInMillis, useInternalMarkPassingAlgorithm, connectivityParams); } @Override 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 d0e7098d876..ffe677df1c0 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 @@ -10,7 +10,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.NavigableSet; -import java.util.Set; import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -112,25 +111,34 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl protected SwissTimingRaceTrackerImpl(String raceID, String raceName, String raceDescription, BoatClass boatClass, String hostname, int port, StartList startList, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, DomainFactory domainFactory, SwissTimingFactory factory, - TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, long delayToLiveInMillis, boolean useInternalMarkPassingAlgorithm) throws InterruptedException, + TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, long delayToLiveInMillis, + boolean useInternalMarkPassingAlgorithm, SwissTimingTrackingConnectivityParameters connectivityParams) throws InterruptedException, UnknownHostException, IOException, ParseException { - this(domainFactory.getOrCreateDefaultRegatta(raceLogStore, regattaLogStore, raceID, - boatClass, trackedRegattaRegistry), raceID, raceName, - raceDescription, boatClass, hostname, port, startList, windStore, domainFactory, factory, - trackedRegattaRegistry, - raceLogResolver, delayToLiveInMillis, useInternalMarkPassingAlgorithm); + this(/* regatta */ null, raceID, raceName, raceDescription, boatClass, hostname, port, startList, windStore, + domainFactory, factory, trackedRegattaRegistry, raceLogStore, regattaLogStore, raceLogResolver, + delayToLiveInMillis, useInternalMarkPassingAlgorithm, connectivityParams); } protected SwissTimingRaceTrackerImpl(Regatta regatta, String raceID, String raceName, String raceDescription, BoatClass boatClass, String hostname, int port, StartList startList, WindStore windStore, - DomainFactory domainFactory, SwissTimingFactory factory, - TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, - long delayToLiveInMillis, boolean useInternalMarkPassingAlgorithm) throws InterruptedException, UnknownHostException, IOException, - ParseException { - super(); + DomainFactory domainFactory, SwissTimingFactory factory, TrackedRegattaRegistry trackedRegattaRegistry, + RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, RaceLogResolver raceLogResolver, + long delayToLiveInMillis, boolean useInternalMarkPassingAlgorithm, SwissTimingTrackingConnectivityParameters connectivityParams) + throws InterruptedException, UnknownHostException, IOException, ParseException { + super(connectivityParams); this.raceLogResolver = raceLogResolver; this.tmdMessageQueue = new TMDMessageQueue(this); - this.regatta = regatta; + final Regatta effectiveRegatta; + // Try to find a pre-associated event based on the Race ID + if (regatta == null) { + effectiveRegatta = trackedRegattaRegistry.getRememberedRegattaForRace(raceID); + } else { + effectiveRegatta = regatta; + } + // if regatta is still null, no previous assignment of any of the races in this TracTrac event to a Regatta was + // found; in this case, create a default regatta based on the TracTrac event data + this.regatta = effectiveRegatta == null ? domainFactory.getOrCreateDefaultRegatta(raceLogStore, regattaLogStore, + raceID, boatClass, trackedRegattaRegistry) : effectiveRegatta; this.connector = factory.getOrCreateSailMasterConnector(hostname, port, raceID, raceName, raceDescription, boatClass); this.domainFactory = domainFactory; this.raceID = raceID; @@ -142,11 +150,10 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl this.windStore = windStore; this.id = createID(raceID, hostname, port); connector.addSailMasterListener(this); - trackedRegatta = trackedRegattaRegistry.getOrCreateTrackedRegatta(regatta); + trackedRegatta = trackedRegattaRegistry.getOrCreateTrackedRegatta(this.regatta); this.delayToLiveInMillis = delayToLiveInMillis; this.competitorsByBoatId = new HashMap(); this.useInternalMarkPassingAlgorithm = useInternalMarkPassingAlgorithm; - } @Override @@ -168,13 +175,12 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl } @Override - public Set getRaces() { - final Set emptySet = Collections.emptySet(); - return race==null?emptySet:Collections.singleton(race); + public RaceDefinition getRace() { + return race; } @Override - public RaceHandle getRacesHandle() { + public RaceHandle getRaceHandle() { return new RaceHandle() { @Override public Regatta getRegatta() { @@ -479,6 +485,7 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl assert SwissTimingRaceTrackerImpl.this.race == race; } }, useInternalMarkPassingAlgorithm, raceLogResolver); + notifyRaceCreationListeners(); logger.info("Created SwissTiming RaceDefinition and TrackedRace for "+race.getName()); } diff --git a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingTrackingConnectivityParameters.java b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingTrackingConnectivityParameters.java index ee8935c98fa..3c54e7020ef 100755 --- a/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingTrackingConnectivityParameters.java +++ b/java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingTrackingConnectivityParameters.java @@ -10,11 +10,13 @@ import com.sap.sailing.domain.swisstimingadapter.DomainFactory; import com.sap.sailing.domain.swisstimingadapter.StartList; import com.sap.sailing.domain.swisstimingadapter.SwissTimingFactory; import com.sap.sailing.domain.tracking.RaceTracker; -import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; import com.sap.sailing.domain.tracking.WindStore; +import com.sap.sailing.domain.tracking.impl.AbstractRaceTrackingConnectivityParameters; -public class SwissTimingTrackingConnectivityParameters implements RaceTrackingConnectivityParameters { +public class SwissTimingTrackingConnectivityParameters extends AbstractRaceTrackingConnectivityParameters { + public static final String TYPE = "SWISS_TIMING"; + private final String hostname; private final int port; private final String raceID; @@ -32,8 +34,8 @@ public class SwissTimingTrackingConnectivityParameters implements RaceTrackingCo public SwissTimingTrackingConnectivityParameters(String hostname, int port, String raceID, String raceName, String raceDescription, BoatClass boatClass, StartList startList, long delayToLiveInMillis, SwissTimingFactory swissTimingFactory, DomainFactory domainFactory, RaceLogStore raceLogStore, - RegattaLogStore regattaLogStore, boolean useInternalMarkPassingAlgorithm) { - super(); + RegattaLogStore regattaLogStore, boolean useInternalMarkPassingAlgorithm, boolean trackWind, boolean correctWindDirectionByMagneticDeclination) { + super(trackWind, correctWindDirectionByMagneticDeclination); this.hostname = hostname; this.port = port; this.raceID = raceID; @@ -49,13 +51,18 @@ public class SwissTimingTrackingConnectivityParameters implements RaceTrackingCo this.useInternalMarkPassingAlgorithm = useInternalMarkPassingAlgorithm; } + @Override + public String getTypeIdentifier() { + return TYPE; + } + @Override public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore, RaceLogResolver raceLogResolver) throws Exception { return swissTimingFactory.createRaceTracker(raceID, raceName, raceDescription, boatClass, hostname, port, startList, delayToLiveInMillis, raceLogStore, regattaLogStore, windStore, useInternalMarkPassingAlgorithm, - domainFactory, trackedRegattaRegistry, raceLogResolver); + domainFactory, trackedRegattaRegistry, raceLogResolver, this); } @Override @@ -63,7 +70,7 @@ public class SwissTimingTrackingConnectivityParameters implements RaceTrackingCo WindStore windStore, RaceLogResolver raceLogResolver) throws Exception { return swissTimingFactory.createRaceTracker(regatta, raceID, raceName, raceDescription, boatClass, hostname, port, startList, delayToLiveInMillis, windStore, useInternalMarkPassingAlgorithm, - domainFactory, trackedRegattaRegistry, raceLogResolver); + domainFactory, trackedRegattaRegistry, raceLogResolver, raceLogStore, regattaLogStore, this); } @Override @@ -75,4 +82,36 @@ public class SwissTimingTrackingConnectivityParameters implements RaceTrackingCo public long getDelayToLiveInMillis() { return delayToLiveInMillis; } + + public String getHostname() { + return hostname; + } + + public int getPort() { + return port; + } + + public String getRaceID() { + return raceID; + } + + public String getRaceName() { + return raceName; + } + + public String getRaceDescription() { + return raceDescription; + } + + public BoatClass getBoatClass() { + return boatClass; + } + + public StartList getStartList() { + return startList; + } + + public boolean isUseInternalMarkPassingAlgorithm() { + return useInternalMarkPassingAlgorithm; + } } diff --git a/java/com.sap.sailing.domain.swisstimingreplayadapter.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.swisstimingreplayadapter.test/META-INF/MANIFEST.MF index ead28dc4b0d..32530b5c375 100644 --- a/java/com.sap.sailing.domain.swisstimingreplayadapter.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.swisstimingreplayadapter.test/META-INF/MANIFEST.MF @@ -9,6 +9,6 @@ Fragment-Host: com.sap.sailing.domain.swisstimingreplayadapter Import-Package: org.json.simple, org.json.simple.parser Require-Bundle: com.sap.sailing.domain.test, - org.junit4;bundle-version="4.8.2", + org.junit;bundle-version="4.8.2", com.sap.sse.common, org.mockito.mockito-core;bundle-version="1.10.14" diff --git a/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingRaceConfigurationTest.java b/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingRaceConfigurationTest.java index 0de4deffcde..2f261e29719 100644 --- a/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingRaceConfigurationTest.java +++ b/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingRaceConfigurationTest.java @@ -17,9 +17,6 @@ import java.util.Map; import org.junit.Ignore; import org.junit.Test; -import static org.mockito.Mockito.mock; - -import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; import com.sap.sailing.domain.swisstimingadapter.DomainFactory; import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayRace; import com.sap.sailing.domain.swisstimingreplayadapter.impl.SwissTimingRaceConfig; @@ -33,13 +30,13 @@ public class SwissTimingRaceConfigurationTest { @Test public void testLoadConfigurations() throws IOException, ParseException, org.json.simple.parser.ParseException { InputStream inputStream = getClass().getResourceAsStream(JSON_URL); - List races = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE, mock(RaceLogResolver.class)).parseJSONObject(inputStream , JSON_URL); + List races = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE).parseJSONObject(inputStream , JSON_URL); Map configsById = new HashMap(); for (SwissTimingReplayRace race : races) { URL configFileURL = new URL(MessageFormat.format(SwissTimingReplayServiceImpl.RACE_CONFIG_URL_TEMPLATE, race.getRaceId())); URLConnection connection = configFileURL.openConnection(); InputStream configDataStream = connection.getInputStream(); - SwissTimingRaceConfig raceConfig = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE, mock(RaceLogResolver.class)).loadRaceConfig(configDataStream); + SwissTimingRaceConfig raceConfig = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE).loadRaceConfig(configDataStream); configsById.put(race.getRaceId(), raceConfig); } } @@ -49,7 +46,7 @@ public class SwissTimingRaceConfigurationTest { URL configFileURL = new URL(MessageFormat.format(SwissTimingReplayServiceImpl.RACE_CONFIG_URL_TEMPLATE, "446483")); URLConnection connection = configFileURL.openConnection(); InputStream configDataStream = connection.getInputStream(); - SwissTimingRaceConfig config_446483 = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE, mock(RaceLogResolver.class)).loadRaceConfig(configDataStream); + SwissTimingRaceConfig config_446483 = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE).loadRaceConfig(configDataStream); assertNotNull(config_446483); assertEquals("GB", config_446483.country_code); assertNull(config_446483.event_name); @@ -66,7 +63,7 @@ public class SwissTimingRaceConfigurationTest { URL configFileURL = new URL(MessageFormat.format(SwissTimingReplayServiceImpl.RACE_CONFIG_URL_TEMPLATE, "6260")); URLConnection connection = configFileURL.openConnection(); InputStream configDataStream = connection.getInputStream(); - SwissTimingRaceConfig config_446483 = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE, mock(RaceLogResolver.class)).loadRaceConfig(configDataStream); + SwissTimingRaceConfig config_446483 = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE).loadRaceConfig(configDataStream); assertNotNull(config_446483); assertEquals("GB", config_446483.country_code); assertEquals("London 2012 Olympic Games", config_446483.event_name); diff --git a/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingReplayAdapterServiceTest.java b/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingReplayAdapterServiceTest.java index e5d6c133ebd..1cdb1ec1218 100644 --- a/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingReplayAdapterServiceTest.java +++ b/java/com.sap.sailing.domain.swisstimingreplayadapter.test/src/com/sap/sailing/domain/swisstimingreplayadapter/test/SwissTimingReplayAdapterServiceTest.java @@ -20,6 +20,8 @@ import org.junit.Test; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.common.Distance; +import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; +import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; import com.sap.sailing.domain.swisstimingadapter.DomainFactory; import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayRace; import com.sap.sailing.domain.swisstimingreplayadapter.impl.SwissTimingReplayParserImpl; @@ -35,7 +37,7 @@ public class SwissTimingReplayAdapterServiceTest { public void testLoadRaceJson() throws Exception { String swissTimingUrlText = "/2012_OSG.json"; InputStream inputStream = getClass().getResourceAsStream(swissTimingUrlText); - final SwissTimingReplayServiceImpl swissTimingReplayService = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE, mock(RaceLogResolver.class)); + final SwissTimingReplayServiceImpl swissTimingReplayService = new SwissTimingReplayServiceImpl(DomainFactory.INSTANCE); List races = swissTimingReplayService.parseJSONObject(inputStream , swissTimingUrlText); assertEquals(201, races.size()); assertEquals("446483", races.get(0).getRaceId()); @@ -179,8 +181,9 @@ public class SwissTimingReplayAdapterServiceTest { @Test public void testRaceData_SAW010955_20120802_WithDomainAdapter() throws Exception { - SwissTimingReplayToDomainAdapter replayListener = new SwissTimingReplayToDomainAdapter(null, DomainFactory.INSTANCE, - new DummyTrackedRegattaRegistry(), /* useInternalMarkPassingAlgorithm */ false, mock(RaceLogResolver.class)); + SwissTimingReplayToDomainAdapter replayListener = new SwissTimingReplayToDomainAdapter(null, /* raceName */ "SAW005906", + "SAW005906", /* boatClass */ null, DomainFactory.INSTANCE, new DummyTrackedRegattaRegistry(), /* useInternalMarkPassingAlgorithm */ false, + mock(RaceLogResolver.class), EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, /* tracker creator */ null); new SwissTimingReplayParserImpl().readData(getClass().getResourceAsStream("/SAW005906.20120805.replay"), replayListener); Iterable trackedRaces = replayListener.getTrackedRaces(); assertFalse(Util.isEmpty(trackedRaces)); @@ -193,8 +196,9 @@ public class SwissTimingReplayAdapterServiceTest { @Test public void testStartPerformanceDetection() throws Exception { - SwissTimingReplayToDomainAdapter replayListener = new SwissTimingReplayToDomainAdapter(null, DomainFactory.INSTANCE, - new DummyTrackedRegattaRegistry(), /* useInternalMarkPassingAlgorithm */ false, mock(RaceLogResolver.class)); + SwissTimingReplayToDomainAdapter replayListener = new SwissTimingReplayToDomainAdapter(null, /* raceName */ "SAW005905", + "SAW005905", /* boatClass */ null, DomainFactory.INSTANCE, new DummyTrackedRegattaRegistry(), /* useInternalMarkPassingAlgorithm */ false, + mock(RaceLogResolver.class), EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, /* tracker creator */ null); new SwissTimingReplayParserImpl().readData(getClass().getResourceAsStream("/SAW005905.20120805.replay"), replayListener); Iterable trackedRaces = replayListener.getTrackedRaces(); TrackedRace trackedRace = trackedRaces.iterator().next(); diff --git a/java/com.sap.sailing.domain.swisstimingreplayadapter/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.swisstimingreplayadapter/META-INF/MANIFEST.MF index 0507cad7850..aa480ccf2a8 100644 --- a/java/com.sap.sailing.domain.swisstimingreplayadapter/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.swisstimingreplayadapter/META-INF/MANIFEST.MF @@ -13,7 +13,10 @@ Require-Bundle: com.sap.sailing.domain.common, com.googlecode.java-diff-utils;bundle-version="1.3.0", com.sap.sse, com.sap.sse.common, - com.sap.sailing.domain.shared.android -Export-Package: com.sap.sailing.domain.swisstimingreplayadapter -Import-Package: org.osgi.framework;version="1.7.0" + com.sap.sailing.domain.shared.android, + com.sap.sailing.domain.persistence +Export-Package: com.sap.sailing.domain.swisstimingreplayadapter, + com.sap.sailing.domain.swisstimingreplayadapter.impl;x-friends:="com.sap.sailing.mongodb.test" +Import-Package: org.osgi.framework;version="1.7.0", + org.osgi.util.tracker;version="1.5.1" Bundle-Activator: com.sap.sailing.domain.swisstimingreplayadapter.impl.Activator diff --git a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/SwissTimingReplayService.java b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/SwissTimingReplayService.java index 7000cd9e137..2553cc2d0d0 100755 --- a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/SwissTimingReplayService.java +++ b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/SwissTimingReplayService.java @@ -1,14 +1,20 @@ package com.sap.sailing.domain.swisstimingreplayadapter; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URISyntaxException; import java.text.DateFormat; import java.text.ParseException; import java.util.List; -import com.sap.sailing.domain.base.Regatta; +import com.sap.sailing.domain.common.RegattaIdentifier; +import com.sap.sailing.domain.racelog.RaceLogStore; +import com.sap.sailing.domain.regattalog.RegattaLogStore; import com.sap.sailing.domain.swisstimingreplayadapter.impl.SwissTimingRaceConfig; import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; +import com.sap.sailing.domain.tracking.TrackerManager; public interface SwissTimingReplayService { @@ -22,6 +28,30 @@ public interface SwissTimingReplayService { DateFormat getStartTimeFormat(); + /** + * @param regattaToAddTo + * the regatta to which the race shall be added; if null, a default regatta will be + * created/used based on the {@code regattaName} and {@code boatClassName} parameter values + * @param link + * the URL without the implicit "http://" prefix, as obtained, e.g., from + * {@link SwissTimingReplayRace#getLink()}. + * @param raceName TODO + * @param raceID + * the SwissTiming ID for the race + * @param trackerManager TODO + * @param useInternalMarkPassingAlgorithm + * use our own instead of the SwissTiming-provided mark rounding / split times + * @param raceLogStore TODO + * @param regattaLogStore TODO + * @param boatClassNameName + * only required if {@code regattaToAddTo} is {@code null}; used for the retrieval/creation of a default + * regatta + */ + void loadRaceData(RegattaIdentifier regattaToAddTo, String link, String raceName, String raceID, + String boatClassName, TrackerManager trackerManager, TrackedRegattaRegistry trackedRegattaRegistry, + boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore) + throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception; + /** * @param link * the URL without the implicit "http://" prefix, as obtained, e.g., from @@ -30,17 +60,6 @@ public interface SwissTimingReplayService { * @param replayListener * the listener to receive all persing events */ - void loadRaceData(String link, SwissTimingReplayListener replayListener); - - /** - * @param link - * the URL without the implicit "http://" prefix, as obtained, e.g., from - * {@link SwissTimingReplayRace#getLink()}. - * @param regatta - * the regatta to which the race shall be added; if null, a default regatta will be - * created/used - * @param useInternalMarkPassingAlgorithm use our own instead of the SwissTiming-provided mark rounding / split times - */ - void loadRaceData(String link, Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry, boolean useInternalMarkPassingAlgorithm); + void loadRaceData(String link, SwissTimingReplayListener listener); } diff --git a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/Activator.java b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/Activator.java index 28bb91703a6..14c4ad44cc1 100755 --- a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/Activator.java +++ b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/Activator.java @@ -1,12 +1,24 @@ package com.sap.sailing.domain.swisstimingreplayadapter.impl; +import java.util.Dictionary; +import java.util.Hashtable; import java.util.logging.Level; import java.util.logging.Logger; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; +import org.osgi.util.tracker.ServiceTracker; +import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; +import com.sap.sailing.domain.persistence.DomainObjectFactory; +import com.sap.sailing.domain.persistence.MongoObjectFactory; +import com.sap.sailing.domain.persistence.MongoRaceLogStoreFactory; +import com.sap.sailing.domain.persistence.MongoRegattaLogStoreFactory; +import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapterFactory; import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayServiceFactory; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler; +import com.sap.sse.common.TypeBasedServiceFinder; +import com.sap.sse.util.ServiceTrackerFactory; public class Activator implements BundleActivator { private static final Logger logger = Logger.getLogger(Activator.class.getName()); @@ -21,6 +33,30 @@ public class Activator implements BundleActivator { public void start(BundleContext context) throws Exception { // register the racing service in the OSGi registry context.registerService(SwissTimingReplayServiceFactory.class.getName(), swissTimingReplayServiceFactory, null); + new Thread(() -> { + final ServiceTracker mongoObjectFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, MongoObjectFactory.class); + final ServiceTracker domainObjectFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, DomainObjectFactory.class); + final ServiceTracker swissTimingAdapterFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, SwissTimingAdapterFactory.class); + final ServiceTracker raceLogResolverServiceTracker = ServiceTrackerFactory.createAndOpen(context, RaceLogResolver.class); + try { + final MongoObjectFactory mongoObjectFactory = mongoObjectFactoryServiceTracker.waitForService(0); + final DomainObjectFactory domainObjectFactory = domainObjectFactoryServiceTracker.waitForService(0); + final SwissTimingAdapterFactory swissTimingAdapterFactory = swissTimingAdapterFactoryServiceTracker.waitForService(0); + final RaceLogResolver raceLogResolver = raceLogResolverServiceTracker.waitForService(0); + final Dictionary properties = new Hashtable(); + final com.sap.sailing.domain.swisstimingadapter.DomainFactory domainFactory = swissTimingAdapterFactory + .getOrCreateSwissTimingAdapter(domainObjectFactory.getBaseDomainFactory()) + .getSwissTimingDomainFactory(); + final SwissTimingReplayConnectivityParamsHandler paramsHandler = new SwissTimingReplayConnectivityParamsHandler( + MongoRaceLogStoreFactory.INSTANCE.getMongoRaceLogStore(mongoObjectFactory, domainObjectFactory), + MongoRegattaLogStoreFactory.INSTANCE.getMongoRegattaLogStore(mongoObjectFactory, domainObjectFactory), + domainFactory, swissTimingReplayServiceFactory.createSwissTimingReplayService(domainFactory, raceLogResolver)); + properties.put(TypeBasedServiceFinder.TYPE, SwissTimingReplayConnectivityParameters.TYPE); + context.registerService(RaceTrackingConnectivityParametersHandler.class, paramsHandler, properties); + } catch (Exception e) { + logger.log(Level.SEVERE, "Exception trying to register SwissTiming Replay RaceTrackingConnectivityParametersHandler implementation", e); + } + }, getClass().getName() + " registering connectivity handler").start(); logger.log(Level.INFO, "Started "+context.getBundle().getSymbolicName()); } diff --git a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayAdapter.java b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayAdapter.java index 648886923ef..07b1ecfee16 100755 --- a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayAdapter.java +++ b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayAdapter.java @@ -3,7 +3,7 @@ package com.sap.sailing.domain.swisstimingreplayadapter.impl; import com.sap.sailing.domain.swisstimingreplayadapter.CompetitorStatus; import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayListener; -public class SwissTimingReplayAdapter implements SwissTimingReplayListener { +public abstract class SwissTimingReplayAdapter implements SwissTimingReplayListener { @Override public void keyFrameIndex(int keyFrameIndex) {} diff --git a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayConnectivityParameters.java b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayConnectivityParameters.java new file mode 100644 index 00000000000..613c36896c7 --- /dev/null +++ b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayConnectivityParameters.java @@ -0,0 +1,192 @@ +package com.sap.sailing.domain.swisstimingreplayadapter.impl; + +import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; +import com.sap.sailing.domain.base.RaceDefinition; +import com.sap.sailing.domain.base.Regatta; +import com.sap.sailing.domain.racelog.RaceLogStore; +import com.sap.sailing.domain.regattalog.RegattaLogStore; +import com.sap.sailing.domain.swisstimingadapter.DomainFactory; +import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayService; +import com.sap.sailing.domain.tracking.AbstractRaceTrackerImpl; +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.WindStore; +import com.sap.sailing.domain.tracking.impl.AbstractRaceTrackingConnectivityParameters; +import com.sap.sse.common.Util; + +public class SwissTimingReplayConnectivityParameters extends AbstractRaceTrackingConnectivityParameters { + public static final String TYPE = "SWISS_TIMING_REPLAY"; + + private final boolean useInternalMarkPassingAlgorithm; + private final DomainFactory domainFactory; + private final String boatClassName; + private final RaceLogStore raceLogStore; + private final RegattaLogStore regattaLogStore; + private final String raceName; + private final String raceID; + private final String link; + private final SwissTimingReplayService replayService; + + class SwissTimingReplayRaceTracker extends AbstractRaceTrackerImpl { + private final WindStore windStore; + private SwissTimingReplayToDomainAdapter listener; + + public SwissTimingReplayRaceTracker(WindStore windStore, SwissTimingReplayToDomainAdapter listener, + SwissTimingReplayConnectivityParameters connectivityParams) { + super(connectivityParams); + this.windStore = windStore; + this.listener = listener; + } + + @Override + public Regatta getRegatta() { + return listener.getRegatta(); + } + + @Override + public void notifyRaceCreationListeners() { + super.notifyRaceCreationListeners(); + } + + @Override + public RaceDefinition getRace() { + RaceDefinition race; + try { + race = listener.getRaceDefinition(raceID, 1 /* very short timeout */); + return race; + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public DynamicTrackedRegatta getTrackedRegatta() { + return listener.getTrackedRegatta(); + } + + @Override + public RaceHandle getRaceHandle() { + return new RaceHandle() { + @Override + public Regatta getRegatta() { + return listener.getRegatta(); + } + + @Override + public RaceDefinition getRace() { + try { + return listener.getRaceDefinition(raceID); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public RaceDefinition getRace(long timeoutInMilliseconds) { + try { + return listener.getRaceDefinition(raceID, timeoutInMilliseconds); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public DynamicTrackedRegatta getTrackedRegatta() { + return SwissTimingReplayRaceTracker.this.getTrackedRegatta(); + } + + @Override + public RaceTracker getRaceTracker() { + return SwissTimingReplayRaceTracker.this; + } + }; + } + + @Override + public WindStore getWindStore() { + return windStore; + } + + @Override + public Object getID() { + return getTrackerID(); + } + } + + public SwissTimingReplayConnectivityParameters(String link, String raceName, String raceID, String boatClassName, + boolean useInternalMarkPassingAlgorithm, DomainFactory domainFactory, + SwissTimingReplayService replayService, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore) { + super(/* trackWind: never track live wind with a SwissTiming Replay race */ false, /* correctWindDirectionByMagneticDeclination */ false); + this.link = link; + this.raceName = raceName; + this.raceID = raceID; + this.boatClassName = boatClassName; + this.useInternalMarkPassingAlgorithm = useInternalMarkPassingAlgorithm; + this.domainFactory = domainFactory; + this.replayService = replayService; + this.raceLogStore = raceLogStore; + this.regattaLogStore = regattaLogStore; + } + + @Override + public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, final WindStore windStore, + RaceLogResolver raceLogResolver) throws Exception { + SwissTimingReplayToDomainAdapter listener = new SwissTimingReplayToDomainAdapter(/* regatta */ null, raceName, + raceID, domainFactory.getBaseDomainFactory().getOrCreateBoatClass(boatClassName), domainFactory, + trackedRegattaRegistry, useInternalMarkPassingAlgorithm, raceLogResolver, raceLogStore, + regattaLogStore, l->new SwissTimingReplayRaceTracker(windStore, l, this)); + replayService.loadRaceData(link, listener); + return listener.getTracker(); + } + + @Override + public RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry, + WindStore windStore, RaceLogResolver raceLogResolver) throws Exception { + SwissTimingReplayToDomainAdapter listener = new SwissTimingReplayToDomainAdapter(regatta, raceName, + raceID, domainFactory.getBaseDomainFactory().getOrCreateBoatClass(boatClassName), + domainFactory, trackedRegattaRegistry, useInternalMarkPassingAlgorithm, raceLogResolver, + raceLogStore, regattaLogStore, l->new SwissTimingReplayRaceTracker(windStore, l, this)); + replayService.loadRaceData(link, listener); + return listener.getTracker(); + } + + @Override + public Object getTrackerID() { + return new Util.Pair<>(link, raceID); + } + + /** + * There is no live tracking with this SwissTiming replay connector + */ + @Override + public long getDelayToLiveInMillis() { + return 0; + } + + @Override + public String getTypeIdentifier() { + return TYPE; + } + + public boolean isUseInternalMarkPassingAlgorithm() { + return useInternalMarkPassingAlgorithm; + } + + public String getBoatClassName() { + return boatClassName; + } + + public String getRaceName() { + return raceName; + } + + public String getRaceID() { + return raceID; + } + + public String getLink() { + return link; + } +} diff --git a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayConnectivityParamsHandler.java b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayConnectivityParamsHandler.java new file mode 100644 index 00000000000..8ce4bf01492 --- /dev/null +++ b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayConnectivityParamsHandler.java @@ -0,0 +1,78 @@ +package com.sap.sailing.domain.swisstimingreplayadapter.impl; + +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; + +import com.sap.sailing.domain.racelog.RaceLogStore; +import com.sap.sailing.domain.regattalog.RegattaLogStore; +import com.sap.sailing.domain.swisstimingadapter.DomainFactory; +import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayService; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; +import com.sap.sailing.domain.tracking.impl.AbstractRaceTrackingConnectivityParametersHandler; +import com.sap.sse.common.TypeBasedServiceFinder; + +/** + * Handles mapping TracTrac connectivity parameters from and to a map with {@link String} keys. The + * "param URL" is considered the {@link #getKey(RaceTrackingConnectivityParameters) key} for these objects.

    + * + * Lives in the same package as {@link RaceTrackingConnectivityParameters} for package-private access to + * its members. + * + * @author Axel Uhl (d043530) + * + */ +public class SwissTimingReplayConnectivityParamsHandler extends AbstractRaceTrackingConnectivityParametersHandler { + private static final String BOAT_CLASS_NAME = "boatClassName"; + private static final String RACE_ID = "raceId"; + private static final String RACE_NAME = "raceName"; + private static final String LINK = "link"; + private static final String USE_INTERNAL_MARK_PASSING_ALGORITHM = "useInternalMarkPassingAlgorithm"; + private static final String DELAY_TO_LIVE_IN_MILLIS = "delayToLiveInMillis"; + private final RaceLogStore raceLogStore; + private final RegattaLogStore regattaLogStore; + private final DomainFactory domainFactory; + private final SwissTimingReplayService replayService; + + public SwissTimingReplayConnectivityParamsHandler(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, + DomainFactory domainFactory, SwissTimingReplayService replayService) { + super(); + this.raceLogStore = raceLogStore; + this.regattaLogStore = regattaLogStore; + this.domainFactory = domainFactory; + this.replayService = replayService; + } + + @Override + public Map mapFrom(RaceTrackingConnectivityParameters params) { + assert params instanceof SwissTimingReplayConnectivityParameters; + final SwissTimingReplayConnectivityParameters stParams = (SwissTimingReplayConnectivityParameters) params; + final Map result = getKey(params); + result.put(DELAY_TO_LIVE_IN_MILLIS, stParams.getDelayToLiveInMillis()); + result.put(USE_INTERNAL_MARK_PASSING_ALGORITHM, stParams.isUseInternalMarkPassingAlgorithm()); + result.put(RACE_NAME, stParams.getRaceName()); + result.put(RACE_ID, stParams.getRaceID()); + result.put(BOAT_CLASS_NAME, stParams.getBoatClassName()); + addWindTrackingParameters(stParams, result); + return result; + } + + @Override + public RaceTrackingConnectivityParameters mapTo(Map map) throws MalformedURLException, URISyntaxException { + return new SwissTimingReplayConnectivityParameters((String) map.get(LINK), (String) map.get(RACE_NAME), + (String) map.get(RACE_ID), (String) map.get(BOAT_CLASS_NAME), + (boolean) map.get(USE_INTERNAL_MARK_PASSING_ALGORITHM), domainFactory, replayService, raceLogStore, + regattaLogStore); + } + + @Override + public Map getKey(RaceTrackingConnectivityParameters params) { + assert params instanceof SwissTimingReplayConnectivityParameters; + final SwissTimingReplayConnectivityParameters stParams = (SwissTimingReplayConnectivityParameters) params; + final Map result = new HashMap<>(); + result.put(TypeBasedServiceFinder.TYPE, params.getTypeIdentifier()); + result.put(LINK, stParams.getLink()); + return result; + } +} diff --git a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayParserImpl.java b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayParserImpl.java index bdb1d70be5c..115fa596777 100644 --- a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayParserImpl.java +++ b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayParserImpl.java @@ -15,7 +15,6 @@ public class SwissTimingReplayParserImpl implements SwissTimingReplayParser { private static final long serialVersionUID = 1L; public UnknownMessageIdentificationCode(byte messageIdentificationCode, long position) { super("Unknown message identification code: " + messageIdentificationCode+" at position "+position); - } } diff --git a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayServiceFactoryImpl.java b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayServiceFactoryImpl.java index 5334e7ea2f0..df45fcb2d30 100755 --- a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayServiceFactoryImpl.java +++ b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayServiceFactoryImpl.java @@ -19,7 +19,7 @@ public class SwissTimingReplayServiceFactoryImpl implements SwissTimingReplaySer public SwissTimingReplayService createSwissTimingReplayService(DomainFactory domainFactory, RaceLogResolver raceLogResolver) { SwissTimingReplayService result = servicesForDomainFactories.get(domainFactory); if (result == null) { - result = new SwissTimingReplayServiceImpl(domainFactory, raceLogResolver); + result = new SwissTimingReplayServiceImpl(domainFactory); servicesForDomainFactories.put(domainFactory, result); } return result; diff --git a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayServiceImpl.java b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayServiceImpl.java index 3d5fe7ff094..b8994dcef23 100644 --- a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayServiceImpl.java +++ b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayServiceImpl.java @@ -1,9 +1,12 @@ package com.sap.sailing.domain.swisstimingreplayadapter.impl; import java.io.ByteArrayInputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URISyntaxException; import java.net.URL; import java.text.DateFormat; import java.text.MessageFormat; @@ -18,13 +21,15 @@ import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; -import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; -import com.sap.sailing.domain.base.Regatta; +import com.sap.sailing.domain.common.RegattaIdentifier; +import com.sap.sailing.domain.racelog.RaceLogStore; +import com.sap.sailing.domain.regattalog.RegattaLogStore; import com.sap.sailing.domain.swisstimingadapter.DomainFactory; import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayListener; import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayRace; import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayService; import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; +import com.sap.sailing.domain.tracking.TrackerManager; import com.sap.sse.util.ByteArrayOutputStreamWithVisibleBuffer; public class SwissTimingReplayServiceImpl implements SwissTimingReplayService { @@ -35,11 +40,9 @@ public class SwissTimingReplayServiceImpl implements SwissTimingReplayService { private static final TimeZone DEFAULT_TIMEZONE = TimeZone.getTimeZone("GMT"); private final DomainFactory domainFactory; - private final RaceLogResolver raceLogResolver; - public SwissTimingReplayServiceImpl(DomainFactory domainFactory, RaceLogResolver raceLogResolver) { + public SwissTimingReplayServiceImpl(DomainFactory domainFactory) { this.domainFactory = domainFactory; - this.raceLogResolver = raceLogResolver; } @Override @@ -146,10 +149,11 @@ public class SwissTimingReplayServiceImpl implements SwissTimingReplayService { } @Override - public void loadRaceData(String link, Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry, - boolean useInternalMarkPassingAlgorithm) { - SwissTimingReplayListener listener = new SwissTimingReplayToDomainAdapter(regatta, domainFactory, - trackedRegattaRegistry, useInternalMarkPassingAlgorithm, raceLogResolver); - loadRaceData(link, listener); + public void loadRaceData(RegattaIdentifier regattaToAddTo, String link, String raceName, String raceID, + String boatClassName, TrackerManager trackerManager, TrackedRegattaRegistry trackedRegattaRegistry, + boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore) + throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception { + trackerManager.addRace(regattaToAddTo, new SwissTimingReplayConnectivityParameters(link, raceName, raceID, + boatClassName, useInternalMarkPassingAlgorithm, domainFactory, this, raceLogStore, regattaLogStore), /* timeout */ 0); } } diff --git a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayToDomainAdapter.java b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayToDomainAdapter.java index da048fee42f..8f11fb7c975 100755 --- a/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayToDomainAdapter.java +++ b/java/com.sap.sailing.domain.swisstimingreplayadapter/src/com/sap/sailing/domain/swisstimingreplayadapter/impl/SwissTimingReplayToDomainAdapter.java @@ -16,6 +16,7 @@ import java.util.TimeZone; import java.util.logging.Logger; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; +import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.ControlPoint; import com.sap.sailing.domain.base.Course; @@ -37,14 +38,18 @@ import com.sap.sailing.domain.common.impl.WindImpl; import com.sap.sailing.domain.common.impl.WindSourceWithAdditionalID; import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; -import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore; -import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore; +import com.sap.sailing.domain.racelog.RaceLogStore; +import com.sap.sailing.domain.regattalog.RegattaLogStore; import com.sap.sailing.domain.swisstimingadapter.DomainFactory; import com.sap.sailing.domain.swisstimingreplayadapter.CompetitorStatus; import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayListener; import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayParser; +import com.sap.sailing.domain.swisstimingreplayadapter.impl.SwissTimingReplayConnectivityParameters.SwissTimingReplayRaceTracker; +import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; import com.sap.sailing.domain.tracking.DynamicTrackedRace; +import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; import com.sap.sailing.domain.tracking.MarkPassing; +import com.sap.sailing.domain.tracking.RaceTracker; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; import com.sap.sailing.domain.tracking.TrackingDataLoader; @@ -77,11 +82,14 @@ public class SwissTimingReplayToDomainAdapter extends SwissTimingReplayAdapter i private final DomainFactory domainFactory; - private final Map racePerRaceID; + /** + * Can be waited on; additions are {@link Object#notifyAll() notified}. + */ + private final Map racePerRaceIdForRaceDefinition; private final Map trackedRacePerRaceID; /** - * The last race ID received from {@link #raceID(String)}. Used as key into {@link #racePerRaceID} and + * The last race ID received from {@link #raceID(String)}. Used as key into {@link #racePerRaceIdForRaceDefinition} and * {@link #trackedRacePerRaceID} for storing data from subsequent messages. */ private String currentRaceID; @@ -140,18 +148,57 @@ public class SwissTimingReplayToDomainAdapter extends SwissTimingReplayAdapter i private final RaceLogResolver raceLogResolver; + private final String raceName; + + private final String raceIdForRaceDefinition; + + /** + * Optional callback to which to announce the arrival of the {@link RaceDefinition} + */ + private final DynamicRaceDefinitionSet dynamicRaceDefinitionSet; + + private final SwissTimingReplayRaceTracker tracker; + + @FunctionalInterface + public static interface TrackerConstructor { + SwissTimingReplayRaceTracker createTracker(SwissTimingReplayToDomainAdapter adapter); + } + /** * @param regatta * the regatta to associate the race(s) received by the listener with, or null to force the * use / creation of a default regatta per race - * @param useInternalMarkPassingAlgorithm use our own instead of the SwissTiming-provided mark rounding / split times + * @param useInternalMarkPassingAlgorithm + * use our own instead of the SwissTiming-provided mark rounding / split times + * @param trackerConstructor + * if not {@code null} this object is used to construct the {@link SwissTimingReplayRaceTracker} that can + * be obtained from {@link #getTracker()}. This tracker's {@link RaceTracker.RaceCreationListener}s will + * be {@link SwissTimingReplayRaceTracker#notifyRaceCreationListeners() notified} when this adapter has + * created the race. */ - public SwissTimingReplayToDomainAdapter(Regatta regatta, DomainFactory domainFactory, - TrackedRegattaRegistry trackedRegattaRegistry, boolean useInternalMarkPassingAlgorithm, RaceLogResolver raceLogResolver) { + public SwissTimingReplayToDomainAdapter(Regatta regatta, String raceName, String raceIdForRaceDefinition, + BoatClass boatClass, DomainFactory domainFactory, + TrackedRegattaRegistry trackedRegattaRegistry, boolean useInternalMarkPassingAlgorithm, RaceLogResolver raceLogResolver, + RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, TrackerConstructor trackerConstructor) { + this.tracker = trackerConstructor == null ? null : trackerConstructor.createTracker(this); this.raceLogResolver = raceLogResolver; - this.regatta = regatta; + // when the race is created, notify the tracker's race creation listeners + this.dynamicRaceDefinitionSet = trackerConstructor == null ? null : (race, trackedRace)->this.tracker.notifyRaceCreationListeners(); + this.raceName = raceName; + this.raceIdForRaceDefinition = raceIdForRaceDefinition; + final Regatta effectiveRegatta; + // Try to find a pre-associated event based on the Race ID + if (regatta == null) { + effectiveRegatta = trackedRegattaRegistry.getRememberedRegattaForRace(raceIdForRaceDefinition); + } else { + effectiveRegatta = regatta; + } + // if regatta is still null, no previous assignment of any of the races in this TracTrac event to a Regatta was + // found; in this case, create a default regatta based on the TracTrac event data + this.regatta = effectiveRegatta == null ? domainFactory.getOrCreateDefaultRegatta(raceLogStore, regattaLogStore, + raceIdForRaceDefinition, boatClass, trackedRegattaRegistry) : effectiveRegatta; this.trackedRegattaRegistry = trackedRegattaRegistry; - racePerRaceID = new HashMap<>(); + racePerRaceIdForRaceDefinition = new HashMap<>(); trackedRacePerRaceID = new HashMap<>(); bestStartTimePerRaceID = new HashMap<>(); raceTimePerRaceID = new HashMap<>(); @@ -165,6 +212,14 @@ public class SwissTimingReplayToDomainAdapter extends SwissTimingReplayAdapter i this.useInternalMarkPassingAlgorithm = useInternalMarkPassingAlgorithm; } + public RaceTracker getTracker() { + return tracker; + } + + public Regatta getRegatta() { + return regatta; + } + public Iterable getTrackedRaces() { return trackedRacePerRaceID.values(); } @@ -273,7 +328,7 @@ public class SwissTimingReplayToDomainAdapter extends SwissTimingReplayAdapter i if (id2 != null && !id2.trim().isEmpty()) { markNames.add(id2.trim()); } - final ControlPoint controlPoint = domainFactory.getOrCreateControlPoint(markNames); + final ControlPoint controlPoint = domainFactory.getOrCreateControlPoint(markNames, getMarkType(markType)); if (index == 0) { currentCourseDefinition = new ArrayList<>(); } @@ -290,9 +345,32 @@ public class SwissTimingReplayToDomainAdapter extends SwissTimingReplayAdapter i } } + /** + * Produces a domain {@link com.sap.sailing.domain.common.MarkType} from a SwissTiming replay {@link MarkType mark type}. + */ + private com.sap.sailing.domain.common.MarkType getMarkType(MarkType markType) { + final com.sap.sailing.domain.common.MarkType result; + if (markType == null) { + result = null; + } else { + if (markType.isBoat()) { + if (markType.isStart()) { + result = com.sap.sailing.domain.common.MarkType.STARTBOAT; + } else if (markType.isFinish()) { + result = com.sap.sailing.domain.common.MarkType.FINISHBOAT; + } else { + result = com.sap.sailing.domain.common.MarkType.BUOY; + } + } else { + result = com.sap.sailing.domain.common.MarkType.BUOY; + } + } + return result; + } + @Override public void trackersCount(short trackersCount) { - RaceDefinition race = racePerRaceID.get(currentRaceID); + RaceDefinition race = racePerRaceIdForRaceDefinition.get(raceIdForRaceDefinition); if (race == null) { createRace(); } else { @@ -311,13 +389,13 @@ public class SwissTimingReplayToDomainAdapter extends SwissTimingReplayAdapter i } private void createRace() { - final Regatta myRegatta = regatta != null ? regatta : domainFactory.getOrCreateDefaultRegatta( - EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, - currentRaceID, domainFactory.getRaceTypeFromRaceID(currentRaceID).getBoatClass(), trackedRegattaRegistry); - RaceDefinition race = domainFactory.createRaceDefinition(myRegatta, - currentRaceID, competitorsPerRaceID.get(currentRaceID), currentCourseDefinition); - racePerRaceID.put(currentRaceID, race); - DynamicTrackedRace trackedRace = trackedRegattaRegistry.getOrCreateTrackedRegatta(myRegatta). + RaceDefinition race = domainFactory.createRaceDefinition(regatta, + currentRaceID, competitorsPerRaceID.get(currentRaceID), currentCourseDefinition, raceName, raceIdForRaceDefinition); + synchronized (racePerRaceIdForRaceDefinition) { + racePerRaceIdForRaceDefinition.put(raceIdForRaceDefinition, race); + racePerRaceIdForRaceDefinition.notifyAll(); + } + DynamicTrackedRace trackedRace = getTrackedRegatta(). createTrackedRace(race, Collections. emptyList(), EmptyWindStore.INSTANCE, TrackedRace.DEFAULT_LIVE_DELAY_IN_MILLISECONDS, WindTrack.DEFAULT_MILLISECONDS_OVER_WHICH_TO_AVERAGE_WIND, @@ -329,6 +407,46 @@ public class SwissTimingReplayToDomainAdapter extends SwissTimingReplayAdapter i trackedRace.setStartTimeReceived(bestStartTimeKnownSoFar); } trackedRacePerRaceID.put(currentRaceID, trackedRace); + if (dynamicRaceDefinitionSet != null) { + dynamicRaceDefinitionSet.addRaceDefinition(race, trackedRace); + } + } + + public DynamicTrackedRegatta getTrackedRegatta() { + return trackedRegattaRegistry.getOrCreateTrackedRegatta(regatta); + } + + /** + * Waits synchronously until a race with ID {@code raceID} was added to {@link #racePerRaceIdForRaceDefinition}. + */ + public RaceDefinition getRaceDefinition(String raceID) throws InterruptedException { + return getRaceDefinition(raceID, 0); + } + + /** + * Waits synchronously until a race with ID {@code raceID} was added to {@link #racePerRaceIdForRaceDefinition} + * + * @param timeoutInMillis + * if 0, waits until a race for {@code raceID} was added to {@link #racePerRaceIdForRaceDefinition}; otherwise the call + * returns after so many milliseconds at the latest, even if no race with that ID was added, in which + * case the method returns {@code null} + */ + public RaceDefinition getRaceDefinition(String raceIdForRaceDefinition, long timeoutInMillis) throws InterruptedException { + final RaceDefinition result; + long targetTime = 0; + if (timeoutInMillis != 0) { + targetTime = System.currentTimeMillis() + timeoutInMillis; + } + synchronized (racePerRaceIdForRaceDefinition) { + while (!racePerRaceIdForRaceDefinition.containsKey(raceIdForRaceDefinition)) { + racePerRaceIdForRaceDefinition.wait(timeoutInMillis == 0 ? 0 : Math.max(1, targetTime-System.currentTimeMillis())); + if (timeoutInMillis != 0 && System.currentTimeMillis() > targetTime) { + break; // if a real timeout has been specified, don't wait again + } + } + result = racePerRaceIdForRaceDefinition.get(raceIdForRaceDefinition); + } + return result; } @Override diff --git a/java/com.sap.sailing.domain.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.test/META-INF/MANIFEST.MF index 321c675e4bb..1b5afb4c9c8 100644 --- a/java/com.sap.sailing.domain.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.test/META-INF/MANIFEST.MF @@ -15,7 +15,7 @@ Require-Bundle: com.sap.sailing.domain, com.sap.sailing.domain.test, org.hamcrest;bundle-version="1.1.0", org.objenesis;bundle-version="1.3.0", - org.junit4;bundle-version="4.8.2", + org.junit;bundle-version="4.8.2", org.json.simple;bundle-version="1.1.0", com.sap.sse.common, com.sap.sse.replication, diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/LeaderboardDTODiffingTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/LeaderboardDTODiffingTest.java index 080fde075c1..8113344b2a3 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/LeaderboardDTODiffingTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/LeaderboardDTODiffingTest.java @@ -23,6 +23,7 @@ import java.util.Set; import org.junit.Before; import org.junit.Test; +import com.sap.sailing.domain.common.BoatClassMasterdata; import com.sap.sailing.domain.common.dto.BoatClassDTO; import com.sap.sailing.domain.common.dto.BoatDTO; import com.sap.sailing.domain.common.dto.CompetitorDTO; @@ -35,7 +36,6 @@ import com.sap.sailing.domain.common.dto.LeaderboardRowDTO; import com.sap.sailing.domain.common.dto.LegEntryDTO; import com.sap.sailing.domain.common.dto.RaceColumnDTO; import com.sap.sailing.domain.common.dto.RaceDTO; -import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sailing.domain.test.StoredTrackBasedTest; import com.sap.sse.common.Cloner; import com.sap.sse.common.Color; @@ -235,7 +235,8 @@ public class LeaderboardDTODiffingTest { public void testCompetitorListChange() { newVersion.competitors = new ArrayList(newVersion.competitors); // clone competitor list so it's not identical to that of previous version CompetitorDTO somebodyNew = new CompetitorDTOImpl("Someone New", Color.RED, "someone@nobody.de", "DE", "GER", "Germany", "912p09871203987", - /* imageURL */ null, /* flagImageURL */ null, new BoatDTO("LSC", "GER 1234"), new BoatClassDTO("505", new MeterDistance(5.05)), /* timeOnTimeFactor */ null, + /* imageURL */ null, /* flagImageURL */ null, new BoatDTO("LSC", "GER 1234"), new BoatClassDTO("505", + BoatClassMasterdata._5O5.getHullLength(), BoatClassMasterdata._5O5.getHullBeam()), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); newVersion.competitors.add(13, somebodyNew); // insert a competitor; this should mess up all others' indexes; check if this works CompetitorDTO wolfgang = getPreviousCompetitorByName("HUNGER +JESS"); @@ -256,7 +257,8 @@ public class LeaderboardDTODiffingTest { newVersion.setSuppressedCompetitors(newSuppressedCompetitors); newVersion.competitors = new ArrayList(newVersion.competitors); // clone competitor list so it's not identical to that of previous version CompetitorDTO somebodyNew = new CompetitorDTOImpl("Someone New", Color.RED, "someone@nobody.de", "DE", "GER", "Germany", "912p09871203987", - /* imageURL */ null, /* flagImageURL */ null, new BoatDTO("LSC", "GER 1234"), new BoatClassDTO("505", new MeterDistance(5.05)), /* timeOnTimeFactor */ null, + /* imageURL */ null, /* flagImageURL */ null, new BoatDTO("LSC", "GER 1234"), new BoatClassDTO("505", + BoatClassMasterdata._5O5.getHullLength(), BoatClassMasterdata._5O5.getHullBeam()), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); newVersion.setSuppressed(newVersion.competitors.get(13), true); // suppress an existing competitor; compaction should reduce this to a single number only newVersion.setSuppressed(somebodyNew, true); // check that mixed mode with existing and new competitors works as well @@ -279,7 +281,8 @@ public class LeaderboardDTODiffingTest { public void testDisplayNameChange() { newVersion.competitors = new ArrayList(newVersion.competitors); // clone competitor list so it's not identical to that of previous version CompetitorDTO somebodyNew = new CompetitorDTOImpl("Someone New", Color.RED, "someone@nobody.de", "DE", "GER", "Germany", "912p09871203987", - /* imageURL */ null, /* flagImageURL */ null, new BoatDTO("LSC", "GER 1234"), new BoatClassDTO("505", new MeterDistance(5.05)), /* timeOnTimeFactor */ null, + /* imageURL */ null, /* flagImageURL */ null, new BoatDTO("LSC", "GER 1234"), new BoatClassDTO("505", + BoatClassMasterdata._5O5.getHullLength(), BoatClassMasterdata._5O5.getHullBeam()), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); newVersion.competitors.add(somebodyNew); newVersion.competitorDisplayNames = new HashMap(newVersion.competitorDisplayNames); @@ -309,7 +312,8 @@ public class LeaderboardDTODiffingTest { newVersion.setCompetitorsFromBestToWorst(r9, newOrdering); newVersion.competitors = new ArrayList(newVersion.competitors); // clone competitor list so it's not identical to that of previous version CompetitorDTO somebodyNew = new CompetitorDTOImpl("Someone New", Color.RED, "someone@nobody.de", "DE", "GER", "Germany", "912p09871203987", - /* imageURL */ null, /* flagImageURL */ null, new BoatDTO("LSC", "GER 1234"), new BoatClassDTO("505", new MeterDistance(5.05)), /* timeOnTimeFactor */ null, + /* imageURL */ null, /* flagImageURL */ null, new BoatDTO("LSC", "GER 1234"), new BoatClassDTO("505", + BoatClassMasterdata._5O5.getHullLength(), BoatClassMasterdata._5O5.getHullBeam()), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); newVersion.competitors.add(somebodyNew); newOrdering.add(somebodyNew); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java index 02f80b054c4..21221371915 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/MultipleClassesInRegattaTest.java @@ -62,7 +62,7 @@ public class MultipleClassesInRegattaTest { false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE, tracTracUsername, tracTracPassword, TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY, - new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class)); + new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class), /* connectivityParams */ null, /* preferReplayIfAvailable */ false); kiwotest2 = domainFactory .createRaceTracker( new URL( @@ -73,7 +73,7 @@ public class MultipleClassesInRegattaTest { false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE, tracTracUsername, tracTracPassword, TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY, - new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class)); + new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class), /* connectivityParams */ null, /* preferReplayIfAvailable */ false); kiwotest3 = domainFactory .createRaceTracker( new URL( @@ -84,7 +84,7 @@ public class MultipleClassesInRegattaTest { false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE, tracTracUsername, tracTracPassword, TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY, - new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class)); + new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class), /* connectivityParams */ null, /* preferReplayIfAvailable */ false); weym470may112014_2 = domainFactory .createRaceTracker( new URL( @@ -95,7 +95,7 @@ public class MultipleClassesInRegattaTest { false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE, tracTracUsername, tracTracPassword, TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY, - new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class)); + new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class), /* connectivityParams */ null, /* preferReplayIfAvailable */ false); assertEquals("STG", kiwotest1.getRegatta().getBoatClass().getName()); assertEquals("5O5", kiwotest2.getRegatta().getBoatClass().getName()); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TargetTimeEstimationTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TargetTimeEstimationTest.java index d1b4b9ca77f..64df6e5594d 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TargetTimeEstimationTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TargetTimeEstimationTest.java @@ -48,6 +48,7 @@ import com.sap.sse.common.impl.MillisecondsTimePoint; public class TargetTimeEstimationTest { + @SuppressWarnings("unchecked") // the problem here is any(Set.class) which cannot infer the type arguments @Test public void simpleReachTargetTimeEstimation() throws NotEnoughDataHasBeenAddedException, NoWindException { // Setup mock objects @@ -63,11 +64,11 @@ public class TargetTimeEstimationTest { Bearing legBearing = startOfLeg.getBearingGreatCircle(endOfLeg); SpeedWithConfidence boatSpeedWithConfidence = new SpeedWithConfidenceImpl(new KnotSpeedImpl(12), 1, null); DynamicTrackedRaceImpl trackedRace = mock(DynamicTrackedRaceImpl.class); - when(trackedRace.getCenterOfCourse(timepoint)).thenReturn(centerOfCourse); WindSource source = mock(WindSource.class); Set sources = Collections.singleton(source); when(trackedRace.getWindSources(WindSourceType.TRACK_BASED_ESTIMATION)).thenReturn(sources); - when(trackedRace.getWind(any(Position.class), eq(timepoint), eq(sources))).thenReturn(wind); + when(trackedRace.getWind(any(Position.class), eq(timepoint))).thenReturn(wind); + when(trackedRace.getWind(any(Position.class), eq(timepoint), any(Set.class))).thenReturn(wind); RaceDefinition race = mock(RaceDefinition.class); when(race.getBoatClass()).thenReturn(mockedBoatClass); @@ -79,20 +80,24 @@ public class TargetTimeEstimationTest { Waypoint to = mock(Waypoint.class); when(leg.getFrom()).thenReturn(from); when(leg.getTo()).thenReturn(to); + when(trackedRace.getApproximatePosition(from, timepoint)).thenReturn(startOfLeg); + when(trackedRace.getApproximatePosition(to, timepoint)).thenReturn(endOfLeg); when(trackedRace.getApproximatePosition(from, timepoint)).thenReturn(startOfLeg); when(trackedRace.getApproximatePosition(to, timepoint)).thenReturn(endOfLeg); - when(mockedPolars.getSpeed(mockedBoatClass, wind, windBearing.getDifferenceTo(legBearing))).thenReturn(boatSpeedWithConfidence); + when(mockedPolars.getSpeed(mockedBoatClass, wind, legBearing.getDifferenceTo(windBearing.reverse()))).thenReturn(boatSpeedWithConfidence); HashSet competitors = new HashSet(); TrackedLeg trackedLeg = new TrackedLegImpl(trackedRace, leg, competitors); //Actual test of functionality - Duration duration = trackedLeg.getEstimatedTimeToComplete(mockedPolars, timepoint, new MarkPositionAtTimePointCacheImpl(trackedRace, timepoint)); + Duration duration = trackedLeg.getEstimatedTimeToComplete(mockedPolars, timepoint, + new MarkPositionAtTimePointCacheImpl(trackedRace, timepoint)).getExpectedDuration(); assertEquals(75494, duration.asMillis(), 100); } + @SuppressWarnings("unchecked") // the problem here is any(Set.class) which cannot infer the type arguments @Test public void simpleUpwindTargetTimeEstimation() throws NotEnoughDataHasBeenAddedException, NoWindException { // Setup mock objects @@ -108,10 +113,8 @@ public class TargetTimeEstimationTest { DynamicTrackedRaceImpl trackedRace = mock(DynamicTrackedRaceImpl.class); when(trackedRace.getCenterOfCourse(timepoint)).thenReturn(centerOfCourse); - WindSource source = mock(WindSource.class); - Set sources = Collections.singleton(source); - when(trackedRace.getWindSources(WindSourceType.TRACK_BASED_ESTIMATION)).thenReturn(sources); - when(trackedRace.getWind(any(Position.class), eq(timepoint), eq(sources))).thenReturn(wind); + when(trackedRace.getWind(any(Position.class), eq(timepoint))).thenReturn(wind); + when(trackedRace.getWind(any(Position.class), eq(timepoint), any(Set.class))).thenReturn(wind); RaceDefinition race = mock(RaceDefinition.class); when(race.getBoatClass()).thenReturn(mockedBoatClass); @@ -141,10 +144,12 @@ public class TargetTimeEstimationTest { TrackedLeg trackedLeg = new TrackedLegImpl(trackedRace, leg, competitors); //Actual test of functionality - Duration duration = trackedLeg.getEstimatedTimeToComplete(mockedPolars, timepoint, new MarkPositionAtTimePointCacheImpl(trackedRace, timepoint)); + Duration duration = trackedLeg.getEstimatedTimeToComplete(mockedPolars, timepoint, + new MarkPositionAtTimePointCacheImpl(trackedRace, timepoint)).getExpectedDuration(); assertEquals(213513, duration.asMillis(), 100); } + @SuppressWarnings("unchecked") // the problem here is any(Set.class) which cannot infer the type arguments @Test public void simpleDownwindTargetTimeEstimation() throws NotEnoughDataHasBeenAddedException, NoWindException { // Setup mock objects @@ -160,10 +165,8 @@ public class TargetTimeEstimationTest { DynamicTrackedRaceImpl trackedRace = mock(DynamicTrackedRaceImpl.class); when(trackedRace.getCenterOfCourse(timepoint)).thenReturn(centerOfCourse); - WindSource source = mock(WindSource.class); - Set sources = Collections.singleton(source); - when(trackedRace.getWindSources(WindSourceType.TRACK_BASED_ESTIMATION)).thenReturn(sources); - when(trackedRace.getWind(any(Position.class), eq(timepoint), eq(sources))).thenReturn(wind); + when(trackedRace.getWind(any(Position.class), eq(timepoint))).thenReturn(wind); + when(trackedRace.getWind(any(Position.class), eq(timepoint), any(Set.class))).thenReturn(wind); RaceDefinition race = mock(RaceDefinition.class); when(race.getBoatClass()).thenReturn(mockedBoatClass); @@ -193,7 +196,8 @@ public class TargetTimeEstimationTest { TrackedLeg trackedLeg = new TrackedLegImpl(trackedRace, leg, competitors); //Actual test of functionality - Duration duration = trackedLeg.getEstimatedTimeToComplete(mockedPolars, timepoint, new MarkPositionAtTimePointCacheImpl(trackedRace, timepoint)); + Duration duration = trackedLeg.getEstimatedTimeToComplete(mockedPolars, timepoint, + new MarkPositionAtTimePointCacheImpl(trackedRace, timepoint)).getExpectedDuration(); assertEquals(95090, duration.asMillis(), 100); } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/UnicodeCharactersInCompetitorNamesTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/UnicodeCharactersInCompetitorNamesTest.java index e1803615e5e..0c364121b16 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/UnicodeCharactersInCompetitorNamesTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/UnicodeCharactersInCompetitorNamesTest.java @@ -70,9 +70,9 @@ public class UnicodeCharactersInCompetitorNamesTest { /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/ false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE, "tracTest", "tracTest", "", "", - new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class)); + new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class), /* connectivityParams */ null, /* preferReplayIfAvailable */ false); - Iterable competitors = fourtyninerYellow_2.getRacesHandle().getRace().getCompetitors(); + Iterable competitors = fourtyninerYellow_2.getRaceHandle().getRace().getCompetitors(); for (Competitor competitor : competitors) { System.out.println(competitor.getName()); } 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 23b7ebb5e21..bb4298e6ecd 100644 --- 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 @@ -36,6 +36,7 @@ import com.sap.sailing.domain.common.RegattaIdentifier; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.Tack; +import com.sap.sailing.domain.common.TargetTimeInfo; import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.WindSourceType; @@ -1035,7 +1036,7 @@ public class MockedTrackedRace implements DynamicTrackedRace { } @Override - public Duration getEstimatedTimeToComplete(TimePoint timepoint) throws NotEnoughDataHasBeenAddedException, + public TargetTimeInfo getEstimatedTimeToComplete(TimePoint timepoint) throws NotEnoughDataHasBeenAddedException, NoWindException { return null; } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java index 9f596ddc595..ecb3dab6078 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java @@ -27,6 +27,7 @@ import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.Tack; +import com.sap.sailing.domain.common.TargetTimeInfo; import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.WindSourceType; @@ -59,7 +60,6 @@ import com.sap.sailing.domain.tracking.WindStore; import com.sap.sailing.domain.tracking.WindTrack; import com.sap.sailing.domain.tracking.WindWithConfidence; import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl; -import com.sap.sse.common.Duration; import com.sap.sse.common.IsManagedByCache; import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util; @@ -644,7 +644,7 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace { } @Override - public Duration getEstimatedTimeToComplete(TimePoint timepoint) throws NotEnoughDataHasBeenAddedException, + public TargetTimeInfo getEstimatedTimeToComplete(TimePoint timepoint) throws NotEnoughDataHasBeenAddedException, NoWindException { return null; } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/tracking/RaceTrackerListenersTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/tracking/RaceTrackerListenersTest.java new file mode 100644 index 00000000000..fac170ca31b --- /dev/null +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/tracking/RaceTrackerListenersTest.java @@ -0,0 +1,28 @@ +package com.sap.sailing.domain.tracking; + +import java.util.ConcurrentModificationException; + +import org.junit.Test; + +public class RaceTrackerListenersTest { + + /** + * Regression test for bug 4039. Removing a listener while the listener was called resulted in a + * {@link ConcurrentModificationException} if there is another listener in the listeners collection. + */ + @Test + public void testThatListenersCanBeRemovedInListener() { + final RaceTrackerListeners listeners = new RaceTrackerListeners(); + for (int i = 0; i < 2; i++) { + listeners.addListener(new RaceTracker.Listener() { + + @Override + public void onTrackerWillStop(boolean preemptive) { + listeners.removeListener(this); + } + }); + } + listeners.onTrackerWillStop(false); + } + +} diff --git a/java/com.sap.sailing.domain.tractracadapter.persistence/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.tractracadapter.persistence/META-INF/MANIFEST.MF index 682a42bbc63..0f63097dbe2 100644 --- a/java/com.sap.sailing.domain.tractracadapter.persistence/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.tractracadapter.persistence/META-INF/MANIFEST.MF @@ -11,7 +11,11 @@ Require-Bundle: org.mongodb.mongo-java-driver;bundle-version="2.13.0", com.sap.sailing.domain.tractracadapter, com.sap.sailing.domain, com.sap.sailing.domain.common, - com.sap.sse.common -Export-Package: com.sap.sailing.domain.tractracadapter.persistence -Import-Package: org.osgi.framework + com.sap.sse.common, + com.sap.sailing.domain.persistence, + com.sap.sse +Export-Package: com.sap.sailing.domain.tractracadapter.persistence, + com.sap.sailing.domain.tractracadapter.persistence.impl;x-friends:="com.sap.sailing.mongodb.test" +Import-Package: org.osgi.framework, + org.osgi.util.tracker;version="1.5.1" Bundle-Activator: com.sap.sailing.domain.tractracadapter.persistence.impl.Activator diff --git a/java/com.sap.sailing.domain.tractracadapter.persistence/src/com/sap/sailing/domain/tractracadapter/persistence/impl/Activator.java b/java/com.sap.sailing.domain.tractracadapter.persistence/src/com/sap/sailing/domain/tractracadapter/persistence/impl/Activator.java index 46c4c82cd96..32dc23b47bd 100755 --- a/java/com.sap.sailing.domain.tractracadapter.persistence/src/com/sap/sailing/domain/tractracadapter/persistence/impl/Activator.java +++ b/java/com.sap.sailing.domain.tractracadapter.persistence/src/com/sap/sailing/domain/tractracadapter/persistence/impl/Activator.java @@ -1,16 +1,61 @@ package com.sap.sailing.domain.tractracadapter.persistence.impl; +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.logging.Level; +import java.util.logging.Logger; + import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; +import org.osgi.util.tracker.ServiceTracker; +import com.sap.sailing.domain.base.DomainFactory; +import com.sap.sailing.domain.persistence.DomainObjectFactory; +import com.sap.sailing.domain.persistence.MongoObjectFactory; +import com.sap.sailing.domain.persistence.MongoRaceLogStoreFactory; +import com.sap.sailing.domain.persistence.MongoRegattaLogStoreFactory; +import com.sap.sailing.domain.racelog.RaceLogStore; +import com.sap.sailing.domain.regattalog.RegattaLogStore; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler; +import com.sap.sailing.domain.tractracadapter.TracTracAdapterFactory; +import com.sap.sailing.domain.tractracadapter.impl.RaceTrackingConnectivityParametersImpl; +import com.sap.sse.common.TypeBasedServiceFinder; import com.sap.sse.mongodb.MongoDBService; +import com.sap.sse.util.ServiceTrackerFactory; public class Activator implements BundleActivator { + private static final Logger logger = Logger.getLogger(Activator.class.getName()); + + /** + * Requires a base {@link DomainFactory} as well as a {@link RaceLogStore} and a {@link RegattaLogStore}. In order + * to instantiate those two stores, the method requires a {@link MongoObjectFactory} and a {@link DomainObjectFactory} + * which are waited for as services from the OSGi registry. This happens in a separate thread that the {@link #start(BundleContext)} + * method launches. + */ @Override public void start(BundleContext context) throws Exception { for (CollectionNames name : CollectionNames.values()) { MongoDBService.INSTANCE.registerExclusively(CollectionNames.class, name.name()); } + new Thread(() -> { + final ServiceTracker mongoObjectFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, MongoObjectFactory.class); + final ServiceTracker domainObjectFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, DomainObjectFactory.class); + try { + final MongoObjectFactory mongoObjectFactory = mongoObjectFactoryServiceTracker.waitForService(0); + final DomainObjectFactory domainObjectFactory = domainObjectFactoryServiceTracker.waitForService(0); + final Dictionary properties = new Hashtable(); + final com.sap.sailing.domain.tractracadapter.DomainFactory domainFactory = TracTracAdapterFactory.INSTANCE.getOrCreateTracTracAdapter( + domainObjectFactory.getBaseDomainFactory()).getTracTracDomainFactory(); + final TracTracConnectivityParamsHandler paramsHandler = new TracTracConnectivityParamsHandler( + MongoRaceLogStoreFactory.INSTANCE.getMongoRaceLogStore(mongoObjectFactory, domainObjectFactory), + MongoRegattaLogStoreFactory.INSTANCE.getMongoRegattaLogStore(mongoObjectFactory, domainObjectFactory), + domainFactory); + properties.put(TypeBasedServiceFinder.TYPE, RaceTrackingConnectivityParametersImpl.TYPE); + context.registerService(RaceTrackingConnectivityParametersHandler.class, paramsHandler, properties); + } catch (Exception e) { + logger.log(Level.SEVERE, "Exception trying to register TracTrac RaceTrackingConnectivityParametersHandler implementation", e); + } + }, getClass().getName() + " registering connectivity handler").start(); } @Override 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 new file mode 100644 index 00000000000..07349531071 --- /dev/null +++ b/java/com.sap.sailing.domain.tractracadapter.persistence/src/com/sap/sailing/domain/tractracadapter/persistence/impl/TracTracConnectivityParamsHandler.java @@ -0,0 +1,105 @@ +package com.sap.sailing.domain.tractracadapter.persistence.impl; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +import com.sap.sailing.domain.racelog.RaceLogStore; +import com.sap.sailing.domain.regattalog.RegattaLogStore; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; +import com.sap.sailing.domain.tracking.impl.AbstractRaceTrackingConnectivityParametersHandler; +import com.sap.sailing.domain.tractracadapter.DomainFactory; +import com.sap.sailing.domain.tractracadapter.impl.RaceTrackingConnectivityParametersImpl; +import com.sap.sse.common.TypeBasedServiceFinder; +import com.sap.sse.common.impl.MillisecondsDurationImpl; +import com.sap.sse.common.impl.MillisecondsTimePoint; + +/** + * Handles mapping TracTrac connectivity parameters from and to a map with {@link String} keys. The + * "param URL" is considered the {@link #getKey(RaceTrackingConnectivityParameters) key} for these objects.

    + * + * Lives in the same package as {@link RaceTrackingConnectivityParameters} for package-private access to + * its members. + * + * @author Axel Uhl (d043530) + * + */ +public class TracTracConnectivityParamsHandler extends AbstractRaceTrackingConnectivityParametersHandler { + private static final String USE_INTERNAL_MARK_PASSING_ALGORITHM = "useInternalMarkPassingAlgorithm"; + private static final String TRAC_TRAC_USERNAME = "tracTracUsername"; + private static final String TRAC_TRAC_PASSWORD = "tracTracPassword"; + private static final String STORED_URI = "storedURI"; + private static final String START_OF_TRACKING_MILLIS = "startOfTrackingMillis"; + private static final String RACE_VISIBILITY = "raceVisibility"; + private static final String RACE_STATUS = "raceStatus"; + private static final String PARAM_URL = "paramURL"; + private static final String OFFSET_TO_START_TIME_OF_SIMULATED_RACE_MILLIS = "offsetToStartTimeOfSimulatedRaceMillis"; + private static final String LIVE_URI = "liveURI"; + private static final String END_OF_TRACKING_MILLIS = "endOfTrackingMillis"; + private static final String DELAY_TO_LIVE_IN_MILLIS = "delayToLiveInMillis"; + private static final String COURSE_DESIGN_UPDATE_URI = "courseDesignUpdateURI"; + private final RaceLogStore raceLogStore; + private final RegattaLogStore regattaLogStore; + private final DomainFactory domainFactory; + + public TracTracConnectivityParamsHandler(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, DomainFactory domainFactory) { + super(); + this.raceLogStore = raceLogStore; + this.regattaLogStore = regattaLogStore; + this.domainFactory = domainFactory; + } + + @Override + public Map mapFrom(RaceTrackingConnectivityParameters params) { + assert params instanceof RaceTrackingConnectivityParametersImpl; + final RaceTrackingConnectivityParametersImpl ttParams = (RaceTrackingConnectivityParametersImpl) params; + final Map result = getKey(params); + result.put(COURSE_DESIGN_UPDATE_URI, ttParams.getCourseDesignUpdateURI()==null?null:ttParams.getCourseDesignUpdateURI().toString()); + result.put(DELAY_TO_LIVE_IN_MILLIS, ttParams.getDelayToLiveInMillis()); + result.put(END_OF_TRACKING_MILLIS, ttParams.getEndOfTracking()==null?null:ttParams.getEndOfTracking().asMillis()); + result.put(LIVE_URI, ttParams.getLiveURI()==null?null:ttParams.getLiveURI().toString()); + result.put(OFFSET_TO_START_TIME_OF_SIMULATED_RACE_MILLIS, ttParams.getOffsetToStartTimeOfSimulatedRace()==null?null:ttParams.getOffsetToStartTimeOfSimulatedRace().asMillis()); + result.put(RACE_STATUS, ttParams.getRaceStatus()); + result.put(RACE_VISIBILITY, ttParams.getRaceVisibility()); + result.put(START_OF_TRACKING_MILLIS, ttParams.getStartOfTracking()==null?null:ttParams.getStartOfTracking().asMillis()); + result.put(STORED_URI, ttParams.getStoredURI()==null?null:ttParams.getStoredURI().toString()); + result.put(TRAC_TRAC_PASSWORD, ttParams.getTracTracPassword()); + result.put(TRAC_TRAC_USERNAME, ttParams.getTracTracUsername().toString()); + result.put(USE_INTERNAL_MARK_PASSING_ALGORITHM, ttParams.isUseInternalMarkPassingAlgorithm()); + addWindTrackingParameters(ttParams, result); + return result; + } + + @Override + public RaceTrackingConnectivityParameters mapTo(Map map) throws MalformedURLException, URISyntaxException { + return new RaceTrackingConnectivityParametersImpl( + new URL(map.get(PARAM_URL).toString()), + map.get(LIVE_URI) == null ? null : new URI(map.get(LIVE_URI).toString()), + map.get(STORED_URI) == null ? null : new URI(map.get(STORED_URI).toString()), + map.get(COURSE_DESIGN_UPDATE_URI) == null ? null : new URI(map.get(COURSE_DESIGN_UPDATE_URI).toString()), + map.get(START_OF_TRACKING_MILLIS) == null ? null : new MillisecondsTimePoint(((Number) map.get(START_OF_TRACKING_MILLIS)).longValue()), + map.get(END_OF_TRACKING_MILLIS) == null ? null : new MillisecondsTimePoint(((Number) map.get(END_OF_TRACKING_MILLIS)).longValue()), + ((Number) map.get(DELAY_TO_LIVE_IN_MILLIS)).longValue(), + map.get(OFFSET_TO_START_TIME_OF_SIMULATED_RACE_MILLIS) == null ? null : new MillisecondsDurationImpl(((Number) map.get(OFFSET_TO_START_TIME_OF_SIMULATED_RACE_MILLIS)).longValue()), + (Boolean) map.get(USE_INTERNAL_MARK_PASSING_ALGORITHM), + raceLogStore, regattaLogStore, domainFactory, + map.get(TRAC_TRAC_USERNAME)==null?null:map.get(TRAC_TRAC_USERNAME).toString(), + map.get(TRAC_TRAC_PASSWORD)==null?null:map.get(TRAC_TRAC_PASSWORD).toString(), + map.get(RACE_STATUS)==null?null:map.get(RACE_STATUS).toString(), + map.get(RACE_VISIBILITY)==null?null:map.get(RACE_VISIBILITY).toString(), isTrackWind(map), + isCorrectWindDirectionByMagneticDeclination(map), /* preferReplayIfAvailable */ true); + } + + @Override + public Map getKey(RaceTrackingConnectivityParameters params) { + assert params instanceof RaceTrackingConnectivityParametersImpl; + final RaceTrackingConnectivityParametersImpl ttParams = (RaceTrackingConnectivityParametersImpl) params; + final Map result = new HashMap<>(); + result.put(TypeBasedServiceFinder.TYPE, params.getTypeIdentifier()); + result.put(PARAM_URL, ttParams.getParamURL().toString()); + return result; + } +} diff --git a/java/com.sap.sailing.domain.tractracadapter/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.tractracadapter/META-INF/MANIFEST.MF index bb6a0a88bc7..fc427842d8f 100644 --- a/java/com.sap.sailing.domain.tractracadapter/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.tractracadapter/META-INF/MANIFEST.MF @@ -19,14 +19,15 @@ Export-Package: com.sap.sailing.domain.tractracadapter, x-friends:="com.sap.sailing.domain.test, com.sap.sailing.domain.swisstimingadapter.test, com.sap.sailing.server.test, - com.sap.sailing.mongodb.test" + com.sap.sailing.mongodb.test, + com.sap.sailing.domain.tractracadapter.persistence" Bundle-ActivationPolicy: lazy Import-Package: org.apache.http, - org.apache.http.client, - org.apache.http.client.entity, - org.apache.http.client.methods, - org.apache.http.impl.client, - org.apache.http.message, + org.apache.http.client;version="[4.2.5,4.3.0)", + org.apache.http.client.entity;version="[4.2.5,4.3.0)", + org.apache.http.client.methods;version="[4.2.5,4.3.0)", + org.apache.http.impl.client;version="[4.2.5,4.3.0)", + org.apache.http.message;version="[4.2.5,4.3.0)", org.json.simple, org.json.simple.parser, org.osgi.framework;version="1.7.0" 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 863458c7fc9..26a0a6edc46 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 @@ -42,6 +42,7 @@ import com.sap.sailing.domain.tracking.WindTrack; import com.sap.sailing.domain.tracking.impl.EmptyWindStore; import com.sap.sailing.domain.tractracadapter.impl.DomainFactoryImpl; import com.sap.sailing.domain.tractracadapter.impl.RaceCourseReceiver; +import com.sap.sailing.domain.tractracadapter.impl.RaceTrackingConnectivityParametersImpl; import com.sap.sailing.domain.tractracadapter.impl.Simulator; import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; @@ -123,13 +124,14 @@ public interface DomainFactory { * respond with the {@link RaceDefinition} when its {@link DomainFactory#getRaceID(IRace)} is called with the * TracTrac {@link IEvent} as argument that is used for its tracking. *

    + * * @param startOfTracking * if null, all stored data from the "beginning of time" will be loaded that the event has * to provide, particularly for the mark positions which are stored per event, not per race; otherwise, * particularly the mark position loading will be constrained to this start time. * @param endOfTracking - * if null, all stored data until the "end of time" will be loaded that the event has - * to provide, particularly for the mark positions which are stored per event, not per race; otherwise, + * if null, all stored data until the "end of time" will be loaded that the event has to + * provide, particularly for the mark positions which are stored per event, not per race; otherwise, * particularly the mark position loading will be constrained to this end time. * @param offsetToStartTimeOfSimulatedRace * if non-null, the {@link Simulator} will be used with this duration as start offset @@ -137,12 +139,19 @@ public interface DomainFactory { * Provides the capability to obtain the {@link WindTrack}s for the different wind sources. A trivial * implementation is {@link EmptyWindStore} which simply provides new, empty tracks. This is always * available but loses track of the wind, e.g., during server restarts. + * @param preferReplayIfAvailable + * when a non-{@code null} {@code storedURI} and/or {@code liveURI} are provided and the {@link IRace} + * specifies something different and claims to be in replay mode ({@link IRace#getConnectionType} is + * {@code File}) then if this parameter is {@code true} the race will be loaded from the replay file + * instead of the {@code storedURI}/{@code liveURI} specified. This is particularly useful for restoring + * races if since the last connection the race was migrated to a replay file format. */ TracTracRaceTracker createRaceTracker(URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, String tracTracUsername, String tracTracPassword, - String raceStatus, String raceVisibility, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) + String raceStatus, String raceVisibility, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, + RaceTrackingConnectivityParametersImpl connectivityParams, boolean preferReplayIfAvailable) throws MalformedURLException, FileNotFoundException, URISyntaxException, CreateModelException, SubscriberInitializationException; /** @@ -151,12 +160,19 @@ public interface DomainFactory { * * @param offsetToStartTimeOfSimulatedRace * if non-null, the {@link Simulator} will be used with this duration as start offset + * @param preferReplayIfAvailable + * when a non-{@code null} {@code storedURI} and/or {@code liveURI} are provided and the {@link IRace} + * specifies something different and claims to be in replay mode ({@link IRace#getConnectionType} is + * {@code File}) then if this parameter is {@code true} the race will be loaded from the replay file + * instead of the {@code storedURI}/{@code liveURI} specified. This is particularly useful for restoring + * races if since the last connection the race was migrated to a replay file format. */ RaceTracker createRaceTracker(Regatta regatta, URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, String tracTracUsername, String tracTracPassword, - String raceStatus, String raceVisibility, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) + String raceStatus, String raceVisibility, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, + RaceTrackingConnectivityParametersImpl connectivityParams, boolean preferReplayIfAvailable) throws MalformedURLException, FileNotFoundException, URISyntaxException, CreateModelException, SubscriberInitializationException; @@ -264,11 +280,18 @@ public interface DomainFactory { /** * @param offsetToStartTimeOfSimulatedRace * if non-null, the {@link Simulator} will be used with this duration as start offset + * @param preferReplayIfAvailable + * when a non-{@code null} {@code storedURI} and/or {@code liveURI} are provided and the {@link IRace} + * specifies something different and claims to be in replay mode ({@link IRace#getConnectionType} is + * {@code File}) then if this parameter is {@code true} the race will be loaded from the replay file + * instead of the {@code storedURI}/{@code liveURI} specified. This is particularly useful for restoring + * races if since the last connection the race was migrated to a replay file format. */ RaceTrackingConnectivityParameters createTrackingConnectivityParameters(URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, - String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility); + String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility, boolean trackWind, boolean correctWindDirectionByMagneticDeclination, + boolean preferReplayIfAvailable); /** * Removes all knowledge about tractracRace which includes removing it from the race cache, from the * {@link com.sap.sailing.domain.base.Regatta} and, if a {@link TrackedRace} for the corresponding diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/TracTracAdapter.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/TracTracAdapter.java index ca159e2ca8e..73c4570a572 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/TracTracAdapter.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/TracTracAdapter.java @@ -48,11 +48,13 @@ public interface TracTracAdapter { * @param timeoutInMilliseconds * if the race definition is not received for the race within this time, the race tracker for that race * is stopped; use -1 to wait forever + * @param trackWind TODO + * @param correctWindDirectionByMagneticDeclination TODO */ RaceHandle addTracTracRace(TrackerManager trackerManager, URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, long timeoutInMilliseconds, String tracTracUsername, String tracTracPassword, String raceStatus, - String raceVisibility) throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception; + String raceVisibility, boolean trackWind, boolean correctWindDirectionByMagneticDeclination) throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception; /** * Same as {@link #addTracTracRace(URL, URI, URI, WindStore, long)}, only that start and end of tracking are @@ -62,43 +64,25 @@ public interface TracTracAdapter { * @param trackerManager * the race will be added to that manager * @param regattaToAddTo - * if null, an existing regatta by the name of the TracTrac event with the boat class name - * appended in parentheses will be looked up; if not found, a default regatta with that name will be - * created, with a single default series and a single default fleet. If a valid {@link RegattaIdentifier} - * is specified, a regatta lookup is performed with that identifier; if the regatta is found, it is used - * to add the races to. Otherwise, a default regatta as described above will be created and used. + * if null, the regatta into which the race has previously been loaded will be looked up; if + * found, the race will be loaded into that regatta; otherwise, an existing regatta by the name of the + * TracTrac event with the boat class name appended in parentheses will be looked up; if not found, a + * default regatta with that name will be created, with a single default series and a single default + * fleet. If a valid {@link RegattaIdentifier} is specified, a regatta lookup is performed with that + * identifier; if the regatta is found, it is used to add the races to. Otherwise, a default regatta as + * described above will be created and used. * @param offsetToStartTimeOfSimulatedRace * if non-null, the {@link Simulator} will be used with this duration as start offset + * @param trackWind TODO + * @param correctWindDirectionByMagneticDeclination TODO */ RaceHandle addTracTracRace(TrackerManager trackerManager, RegattaIdentifier regattaToAddTo, URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI, TimePoint trackingStartTime, TimePoint trackingEndTime, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, long timeoutForReceivingRaceDefinitionInMilliseconds, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, - String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility) + String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility, boolean trackWind, boolean correctWindDirectionByMagneticDeclination) throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception; - /** - * Defines the regatta and for each race listed in the JSON document that is not already being tracked by this service - * creates a {@link TracTracRaceTracker} that starts tracking the respective race. The {@link RaceDefinition}s obtained this - * way are all grouped into the single {@link Regatta} produced for the event listed in the JSON response. Note that - * the many race trackers will have their TracTrac Event each, all with the same name, meaning the same - * event but being distinct. - * @param trackerManager TODO - * @param jsonURL - * URL of a JSON response that contains an "event" object telling the event's name and ID, as well as a - * JSON array named "races" which tells ID and replay URL for the race. From those replay URLs the - * paramURL for the Java client can be derived. - * @param timeoutInMilliseconds - * if a race definition is not received for a race of this event within this time, the race tracker for - * that race is stopped; use -1 to wait forever - * @param raceLogStore TODO - */ - Regatta addRegatta(TrackerManager trackerManager, URL jsonURL, URI liveURI, URI storedURI, - URI courseDesignUpdateURI, WindStore windStore, long timeoutInMilliseconds, String tracTracUsername, - String tracTracPassword, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore) - throws MalformedURLException, FileNotFoundException, URISyntaxException, IOException, ParseException, - org.json.simple.parser.ParseException, Exception; - /** * For the JSON URL of an account / event, lists the paramURLs that can be used for * {@link #addTracTracRace(URL, URI, URI, WindStore, long)} calls to individually start tracking races of this 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 afadc3c4379..4a9e65c3548 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 @@ -654,15 +654,16 @@ public class DomainFactoryImpl implements DomainFactory { @Override public TracTracRaceTracker createRaceTracker(URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, - Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, - RegattaLogStore regattaLogStore, WindStore windStore, String tracTracUsername, + Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, + RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility, - TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws MalformedURLException, FileNotFoundException, - URISyntaxException, CreateModelException, SubscriberInitializationException { + TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, + RaceTrackingConnectivityParametersImpl connectivityParams, boolean preferReplayIfAvailable) throws MalformedURLException, + FileNotFoundException, URISyntaxException, CreateModelException, SubscriberInitializationException { return new TracTracRaceTrackerImpl(this, paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking, - endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, windStore, - tracTracUsername, tracTracPassword, raceStatus, raceVisibility, trackedRegattaRegistry, - raceLogResolver); + endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, + raceLogStore, regattaLogStore, windStore, tracTracUsername, tracTracPassword, raceStatus, + raceVisibility, trackedRegattaRegistry, raceLogResolver, connectivityParams, preferReplayIfAvailable); } @Override @@ -671,13 +672,14 @@ public class DomainFactoryImpl implements DomainFactory { Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility, - TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) + TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, RaceTrackingConnectivityParametersImpl connectivityParams, + boolean preferReplayIfAvailable) throws MalformedURLException, FileNotFoundException, URISyntaxException, CreateModelException, SubscriberInitializationException { return new TracTracRaceTrackerImpl(regatta, this, paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, windStore, tracTracUsername, tracTracPassword, raceStatus, - raceVisibility, trackedRegattaRegistry, raceLogResolver); + raceVisibility, trackedRegattaRegistry, raceLogResolver, connectivityParams, preferReplayIfAvailable); } @Override @@ -695,14 +697,16 @@ public class DomainFactoryImpl implements DomainFactory { URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, String tracTracUsername, String tracTracPassword, String raceStatus, - String raceVisibility) { + String raceVisibility, boolean trackWind, boolean correctWindDirectionByMagneticDeclination, boolean preferReplayIfAvailable) { return new RaceTrackingConnectivityParametersImpl(paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, - regattaLogStore, this, tracTracUsername, tracTracPassword, raceStatus, raceVisibility); + regattaLogStore, this, tracTracUsername, tracTracPassword, raceStatus, raceVisibility, trackWind, correctWindDirectionByMagneticDeclination, + preferReplayIfAvailable); } @Override - public JSONService parseJSONURLForOneRaceRecord(URL jsonURL, String raceId, boolean loadClientParams) throws IOException, ParseException, org.json.simple.parser.ParseException, URISyntaxException { + public JSONService parseJSONURLForOneRaceRecord(URL jsonURL, String raceId, boolean loadClientParams) + throws IOException, ParseException, org.json.simple.parser.ParseException, URISyntaxException { return new JSONServiceImpl(jsonURL, raceId, loadClientParams); } 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 91d819e7da0..3049a3a8432 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 @@ -11,16 +11,19 @@ import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.regattalog.RegattaLogStore; import com.sap.sailing.domain.tracking.RaceTracker; -import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; import com.sap.sailing.domain.tracking.WindStore; +import com.sap.sailing.domain.tracking.impl.AbstractRaceTrackingConnectivityParameters; import com.sap.sailing.domain.tractracadapter.DomainFactory; import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; import com.tractrac.model.lib.api.event.CreateModelException; +import com.tractrac.model.lib.api.event.IRace; import com.tractrac.subscription.lib.api.SubscriberInitializationException; -public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConnectivityParameters { +public class RaceTrackingConnectivityParametersImpl extends AbstractRaceTrackingConnectivityParameters { + public static final String TYPE = "TRAC_TRAC"; + private final URL paramURL; private final URI liveURI; private final URI storedURI; @@ -37,13 +40,22 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne private final String raceStatus; private final String raceVisibility; private final boolean useInternalMarkPassingAlgorithm; + private final boolean preferReplayIfAvailable; + /** + * @param preferReplayIfAvailable + * when a non-{@code null} {@code storedURI} and/or {@code liveURI} are provided and the {@link IRace} + * specifies something different and claims to be in replay mode ({@link IRace#getConnectionType} is + * {@code File}) then if this parameter is {@code true} the race will be loaded from the replay file + * instead of the {@code storedURI}/{@code liveURI} specified. This is particularly useful for restoring + * races if since the last connection the race was migrated to a replay file format. + */ public RaceTrackingConnectivityParametersImpl(URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, DomainFactory domainFactory, String tracTracUsername, String tracTracPassword, String raceStatus, - String raceVisibility) { - super(); + String raceVisibility, boolean trackWind, boolean correctWindDirectionByMagneticDeclination, boolean preferReplayIfAvailable) { + super(trackWind, correctWindDirectionByMagneticDeclination); this.paramURL = paramURL; this.liveURI = liveURI; this.storedURI = storedURI; @@ -60,6 +72,12 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne this.raceStatus = raceStatus; this.raceVisibility = raceVisibility; this.useInternalMarkPassingAlgorithm = useInternalMarkPassingAlgorithm; + this.preferReplayIfAvailable = preferReplayIfAvailable; + } + + @Override + public String getTypeIdentifier() { + return TYPE; } @Override @@ -69,7 +87,7 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne RaceTracker tracker = domainFactory.createRaceTracker(paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, windStore, tracTracUsername, tracTracPassword, raceStatus, - raceVisibility, trackedRegattaRegistry, raceLogResolver); + raceVisibility, trackedRegattaRegistry, raceLogResolver, this, preferReplayIfAvailable); return tracker; } @@ -79,7 +97,7 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne RaceTracker tracker = domainFactory.createRaceTracker(regatta, paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, windStore, tracTracUsername, tracTracPassword, raceStatus, - raceVisibility, trackedRegattaRegistry, raceLogResolver); + raceVisibility, trackedRegattaRegistry, raceLogResolver, this, preferReplayIfAvailable); return tracker; } @@ -93,4 +111,52 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne return delayToLiveInMillis; } + public URL getParamURL() { + return paramURL; + } + + public URI getLiveURI() { + return liveURI; + } + + public URI getStoredURI() { + return storedURI; + } + + public URI getCourseDesignUpdateURI() { + return courseDesignUpdateURI; + } + + public TimePoint getStartOfTracking() { + return startOfTracking; + } + + public TimePoint getEndOfTracking() { + return endOfTracking; + } + + public Duration getOffsetToStartTimeOfSimulatedRace() { + return offsetToStartTimeOfSimulatedRace; + } + + public String getTracTracUsername() { + return tracTracUsername; + } + + public String getTracTracPassword() { + return tracTracPassword; + } + + public String getRaceStatus() { + return raceStatus; + } + + public String getRaceVisibility() { + return raceVisibility; + } + + public boolean isUseInternalMarkPassingAlgorithm() { + return useInternalMarkPassingAlgorithm; + } + } diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracAdapterImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracAdapterImpl.java index 0d7fd26a6ec..43a1aac95b6 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracAdapterImpl.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracAdapterImpl.java @@ -8,14 +8,12 @@ import java.text.ParseException; import java.util.List; import java.util.logging.Logger; -import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.common.RegattaIdentifier; import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.regattalog.RegattaLogStore; import com.sap.sailing.domain.tracking.RaceHandle; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackerManager; -import com.sap.sailing.domain.tracking.WindStore; import com.sap.sailing.domain.tractracadapter.DomainFactory; import com.sap.sailing.domain.tractracadapter.JSONService; import com.sap.sailing.domain.tractracadapter.RaceRecord; @@ -51,14 +49,15 @@ public class TracTracAdapterImpl implements TracTracAdapter { public RaceHandle addTracTracRace(TrackerManager trackerManager, URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, long timeoutInMilliseconds, String tracTracUsername, String tracTracPassword, String raceStatus, - String raceVisibility) throws Exception { + String raceVisibility, boolean trackWind, boolean correctWindDirectionByMagneticDeclination) throws Exception { return trackerManager.addRace( /* regattaToAddTo */null, getTracTracDomainFactory().createTrackingConnectivityParameters(paramURL, liveURI, storedURI, courseDesignUpdateURI, /* startOfTracking */null, /* endOfTracking */null, delayToLiveInMillis, /* offsetToStartTimeOfSimulatedRace */null, /* ignoreTracTracMarkPassings */ false, - raceLogStore, regattaLogStore, tracTracUsername, tracTracPassword, raceStatus, raceVisibility), + raceLogStore, regattaLogStore, tracTracUsername, tracTracPassword, raceStatus, raceVisibility, trackWind, correctWindDirectionByMagneticDeclination, + /* preferReplayIfAvailable */ false), timeoutInMilliseconds); } @@ -66,14 +65,15 @@ public class TracTracAdapterImpl implements TracTracAdapter { public RaceHandle addTracTracRace(TrackerManager trackerManager, RegattaIdentifier regattaToAddTo, URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, long timeoutInMilliseconds, - Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, String tracTracUsername, String tracTracPassword, String raceStatus, - String raceVisibility) throws Exception { + Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, String tracTracUsername, + String tracTracPassword, String raceStatus, String raceVisibility, boolean trackWind, + boolean correctWindDirectionByMagneticDeclination) throws Exception { return trackerManager.addRace( regattaToAddTo, getTracTracDomainFactory().createTrackingConnectivityParameters(paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, tracTracUsername, tracTracPassword, - raceStatus, raceVisibility), timeoutInMilliseconds); + raceStatus, raceVisibility, trackWind, correctWindDirectionByMagneticDeclination, /* preferReplayIfAvailable */ false), timeoutInMilliseconds); } @Override @@ -85,21 +85,6 @@ public class TracTracAdapterImpl implements TracTracAdapter { return new Util.Pair>(jsonService.getEventName(), jsonService.getRaceRecords()); } - @Override - public Regatta addRegatta(TrackerManager trackerManager, URL jsonURL, URI liveURI, URI storedURI, - URI courseDesignUpdateURI, WindStore windStore, long timeoutInMilliseconds, String tracTracUsername, - String tracTracPassword, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore) throws Exception { - JSONService jsonService = getTracTracDomainFactory().parseJSONURLWithRaceRecords(jsonURL, true); - Regatta regatta = null; - for (RaceRecord rr : jsonService.getRaceRecords()) { - URL paramURL = rr.getParamURL(); - regatta = addTracTracRace(trackerManager, paramURL, liveURI, storedURI, courseDesignUpdateURI, - raceLogStore, regattaLogStore, timeoutInMilliseconds, tracTracUsername, "", "", - tracTracPassword).getRegatta(); - } - return regatta; - } - @Override public RaceRecord getSingleTracTracRaceRecord(URL jsonURL, String raceId, boolean loadClientParams) throws Exception { 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 48061878442..159e1879197 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 @@ -195,7 +195,7 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl private final Set receivers; private final DomainFactory domainFactory; private final WindStore windStore; - private final Set races; + private RaceDefinition race; private final DynamicTrackedRegatta trackedRegatta; private TrackedRaceStatus lastStatus; private Map> lastProgressPerID; @@ -252,79 +252,125 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl * available but loses track of the wind, e.g., during server restarts. * @param trackedRegattaRegistry * used to create the {@link TrackedRegatta} for the domain event + * @param preferReplayIfAvailable + * when a non-{@code null} {@code storedURI} and/or {@code liveURI} are provided and the {@link IRace} + * specifies something different and claims to be in replay mode ({@link IRace#getConnectionType} is + * {@code File}) then if this parameter is {@code true} the race will be loaded from the replay file + * instead of the {@code storedURI}/{@code liveURI} specified. This is particularly useful for restoring + * races if since the last connection the race was migrated to a replay file format. */ protected TracTracRaceTrackerImpl(DomainFactory domainFactory, URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, String tracTracUsername, String tracTracPassword, - String raceStatus, String raceVisibility, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) + String raceStatus, String raceVisibility, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, RaceTrackingConnectivityParametersImpl connectivityParams, + boolean preferReplayIfAvailable) throws URISyntaxException, MalformedURLException, FileNotFoundException, CreateModelException, SubscriberInitializationException { this(ModelLocator.getEventFactory().createRace(new URI(paramURL.toString())), domainFactory, paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, windStore, tracTracUsername, - tracTracPassword, raceStatus, raceVisibility, trackedRegattaRegistry, raceLogResolver); + tracTracPassword, raceStatus, raceVisibility, trackedRegattaRegistry, raceLogResolver, connectivityParams, preferReplayIfAvailable); } + /** + * @param preferReplayIfAvailable + * when a non-{@code null} {@code storedURI} and/or {@code liveURI} are provided and the {@link IRace} + * specifies something different and claims to be in replay mode ({@link IRace#getConnectionType} is + * {@code File}) then if this parameter is {@code true} the race will be loaded from the replay file + * instead of the {@code storedURI}/{@code liveURI} specified. This is particularly useful for restoring + * races if since the last connection the race was migrated to a replay file format. + */ private TracTracRaceTrackerImpl(IRace tractracRace, DomainFactory domainFactory, URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility, - TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws URISyntaxException, MalformedURLException, + TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, RaceTrackingConnectivityParametersImpl connectivityParams, + boolean preferReplayIfAvailable) throws URISyntaxException, MalformedURLException, FileNotFoundException, SubscriberInitializationException { - this(tractracRace, null, domainFactory, paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking, + this(tractracRace, /* regatta */ null, domainFactory, paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, windStore, tracTracUsername, tracTracPassword, raceStatus, raceVisibility, trackedRegattaRegistry, - raceLogResolver); + raceLogResolver, connectivityParams, preferReplayIfAvailable); } /** * Use this constructor if the {@link Regatta} in which to arrange the {@link RaceDefinition}s created by this - * tracker is already known up-front, particularly if it has a specific configuration to use. Other constructors - * may create a default {@link Regatta} with only a single default {@link Series} and {@link Fleet} which may not - * always be what you want. + * tracker is already known up-front, particularly if it has a specific configuration to use. Other constructors may + * create a default {@link Regatta} with only a single default {@link Series} and {@link Fleet} which may not always + * be what you want. + * + * @param preferReplayIfAvailable + * when a non-{@code null} {@code storedURI} and/or {@code liveURI} are provided and the {@link IRace} + * specifies something different and claims to be in replay mode ({@link IRace#getConnectionType} is + * {@code File}) then if this parameter is {@code true} the race will be loaded from the replay file + * instead of the {@code storedURI}/{@code liveURI} specified. This is particularly useful for restoring + * races if since the last connection the race was migrated to a replay file format. */ protected TracTracRaceTrackerImpl(Regatta regatta, DomainFactory domainFactory, URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean ignoreTracTracMarkPassings, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility, - TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws URISyntaxException, + TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, RaceTrackingConnectivityParametersImpl connectivityParams, + boolean preferReplayIfAvailable) throws URISyntaxException, MalformedURLException, FileNotFoundException, CreateModelException, SubscriberInitializationException { this(ModelLocator.getEventFactory().createRace(new URI(paramURL.toString())), regatta, domainFactory, paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, ignoreTracTracMarkPassings, raceLogStore, regattaLogStore, windStore, tracTracUsername, - tracTracPassword, raceStatus, raceVisibility, trackedRegattaRegistry, raceLogResolver); + tracTracPassword, raceStatus, raceVisibility, trackedRegattaRegistry, raceLogResolver, connectivityParams, preferReplayIfAvailable); + } + + private boolean isReplayRace(IRace tractracRace) { + // TODO bug 4037: change into tractracRace.getConnectionType() == File once TracAPI 3.3.2 is available + return tractracRace.getStoredURI() != null && tractracRace.getStoredURI().toString().toLowerCase().endsWith(".mtb"); } /** - * * @param regatta * if null, then domainFactory.getOrCreateRegatta(tractracEvent) will be used * to obtain a default regatta * @param offsetToStartTimeOfSimulatedRace - * if not null, the connector will adjust the time stamps of all events received such that the - * first mark passing for the first waypoint will be set to "now." It will delay the forwarding of all - * events received such that they seem to be sent in "real-time" + offsetToStartTimeOfSimulatedRace So, more or less the time points - * attached to the events sent to the receivers will again approximate the wall time. + * if not null, the connector will adjust the time stamps of all events received such that + * the first mark passing for the first waypoint will be set to "now." It will delay the forwarding of + * all events received such that they seem to be sent in "real-time" + + * offsetToStartTimeOfSimulatedRace So, more or less the time points attached to the events + * sent to the receivers will again approximate the wall time. + * @param preferReplayIfAvailable + * when a non-{@code null} {@code storedURI} and/or {@code liveURI} are provided and the {@link IRace} + * specifies something different and claims to be in replay mode ({@link IRace#getConnectionType} is + * {@code File}) then if this parameter is {@code true} the race will be loaded from the replay file + * instead of the {@code storedURI}/{@code liveURI} specified. This is particularly useful for restoring + * races if since the last connection the race was migrated to a replay file format. */ private TracTracRaceTrackerImpl(IRace tractracRace, final Regatta regatta, DomainFactory domainFactory, URL paramURL, URI liveURI, URI storedURI, URI tracTracUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility, - TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws URISyntaxException, MalformedURLException, + TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver, RaceTrackingConnectivityParametersImpl connectivityParams, + boolean preferReplayIfAvailable) throws URISyntaxException, MalformedURLException, FileNotFoundException, SubscriberInitializationException { - super(); + super(connectivityParams); this.trackedRegattaRegistry = trackedRegattaRegistry; this.tractracRace = tractracRace; this.tractracEvent = tractracRace.getEvent(); + if (preferReplayIfAvailable && isReplayRace(tractracRace)) { + if (!Util.equalsWithNull(liveURI, tractracRace.getLiveURI()) + || !Util.equalsWithNull(storedURI, tractracRace.getStoredURI())) { + logger.info("Replay format available and preferred for race " + tractracRace.getName() + + "; using storedURI " + tractracRace.getStoredURI() + " instead of " + storedURI + + " and liveURI " + tractracRace.getLiveURI() + " instead of " + liveURI); + liveURI = tractracRace.getLiveURI(); + storedURI = tractracRace.getStoredURI(); + } + } this.id = createID(paramURL, liveURI, storedURI); isLiveTracking = liveURI != null; - this.races = new HashSet(); + this.race = null; // no race received yet this.domainFactory = domainFactory; this.lastProgressPerID = new HashMap<>(); if (offsetToStartTimeOfSimulatedRace != null) { @@ -375,8 +421,8 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl public void updateRace(IRace race) { if (Util.equalsWithNull(race, TracTracRaceTrackerImpl.this.tractracRace)) { int delayToLiveInMillis = race.getLiveDelay()*1000; - for (RaceDefinition raceDefinition : getRaces()) { - DynamicTrackedRace trackedRace = getTrackedRegatta().getExistingTrackedRace(raceDefinition); + if (getRace() != null) { + DynamicTrackedRace trackedRace = getTrackedRegatta().getExistingTrackedRace(getRace()); if (trackedRace != null) { trackedRace.setDelayToLiveInMillis(delayToLiveInMillis); } @@ -385,13 +431,15 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl } }); // Start live and stored data streams - Regatta effectiveRegatta = regatta; + final Regatta effectiveRegatta; raceSubscriber = subscriberFactory.createRaceSubscriber(tractracRace, liveURI, storedURI); raceSubscriber.subscribeConnectionStatus(this); // Try to find a pre-associated event based on the Race ID - if (effectiveRegatta == null) { + if (regatta == null) { Serializable raceID = domainFactory.getRaceID(tractracRace); effectiveRegatta = trackedRegattaRegistry.getRememberedRegattaForRace(raceID); + } else { + effectiveRegatta = regatta; } // removeRace may detach the domain regatta from the domain factory if that // removed the last race; therefore, it's important to getOrCreate the @@ -517,13 +565,13 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl } @Override - public RaceHandle getRacesHandle() { + public RaceHandle getRaceHandle() { return new RaceHandleImpl(domainFactory, tractracRace, getTrackedRegatta(), this); } @Override - public Set getRaces() { - return races; + public RaceDefinition getRace() { + return race; } protected void addListenersForStoredDataAndStartController(Iterable listenersForStoredData) { @@ -579,8 +627,8 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl * @see #updateStatusOfTrackedRace(DynamicTrackedRace) */ private void updateStatusOfTrackedRaces() { - for (RaceDefinition race : getRaces()) { - DynamicTrackedRace trackedRace = getTrackedRegatta().getExistingTrackedRace(race); + if (getRace() != null) { + DynamicTrackedRace trackedRace = getTrackedRegatta().getExistingTrackedRace(getRace()); if (trackedRace != null) { updateStatusOfTrackedRace(trackedRace); } @@ -635,7 +683,7 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl } } } - logger.info("Stored data progress in tracker "+getID()+" for race(s) "+getRaces()+": "+progress); + logger.info("Stored data progress in tracker "+getID()+" for race(s) "+getRace()+": "+progress); lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.LOADING, progress); if (progress==1.0) { new AbstractLoadingQueueDoneCallBack(receivers) { @@ -651,27 +699,28 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl } @Override - public void addRaceDefinition(RaceDefinition race, DynamicTrackedRace trackedRace) { - races.add(race); + public void addRaceDefinition(final RaceDefinition race, final DynamicTrackedRace trackedRace) { + this.race = race; updateStatusOfTrackedRace(trackedRace); + notifyRaceCreationListeners(); } @Override public void gotLiveDataEvent(ILiveDataEvent liveDataEvent) { - logger.info("Status change in tracker "+getID()+" for race(s) "+getRaces()+": "+liveDataEvent); + logger.info("Status change in tracker "+getID()+" for race(s) "+getRace()+": "+liveDataEvent); } @Override public void gotStoredDataEvent(IStoredDataEvent storedDataEvent) { - logger.info("Status change in tracker "+getID()+" for race(s) "+getRaces()+": "+storedDataEvent); + logger.info("Status change in tracker "+getID()+" for race(s) "+getRace()+": "+storedDataEvent); switch (storedDataEvent.getType()) { case Begin: - logger.info("Stored data begin in tracker "+getID()+" for race(s) "+getRaces()); + logger.info("Stored data begin in tracker "+getID()+" for race(s) "+getRace()); lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.LOADING, 0); updateStatusOfTrackedRaces(); break; case End: - logger.info("Stored data end in tracker "+getID()+" for race(s) "+getRaces()); + logger.info("Stored data end in tracker "+getID()+" for race(s) "+getRace()); if (isLiveTracking) { lastStatus = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.TRACKING, 1); updateStatusOfTrackedRaces(); @@ -681,14 +730,14 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl storedDataProgress(storedDataEvent.getProgress()); break; case Error: - logger.warning("Error with stored data in tracker "+getID()+" for race(s) "+getRaces()+": "+storedDataEvent.getError()); + logger.warning("Error with stored data in tracker "+getID()+" for race(s) "+getRace()+": "+storedDataEvent.getError()); break; } } @Override public void stopped(Object o) { - logger.info("stopped TracTrac tracking in tracker " + getID() + " for " + getRaces() + " while in status " + logger.info("stopped TracTrac tracking in tracker " + getID() + " for " + getRace() + " while in status " + lastStatus); new AbstractLoadingQueueDoneCallBack(receivers) { @Override @@ -697,13 +746,13 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl updateStatusOfTrackedRaces(); if (!stopped) { try { - for (RaceDefinition race : getRaces()) { + if (getRace() != null) { // See also bug 1517; with TracAPI we assume that when stopped(IEvent) is called by the // TracAPI then // all subscriptions have received all their data and it's therefore safe to stop all // subscriptions // at this point without missing any data. - trackedRegattaRegistry.stopTracking(regatta, race); + trackedRegattaRegistry.stopTracking(regatta, getRace()); } } catch (InterruptedException | IOException e) { logger.log(Level.INFO, "Interrupted while trying to stop tracker " + this, e); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/caching/LiveLeaderboardUpdater.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/caching/LiveLeaderboardUpdater.java index c1b211c7d3b..f5e3a3409b1 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/caching/LiveLeaderboardUpdater.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/caching/LiveLeaderboardUpdater.java @@ -169,9 +169,7 @@ public class LiveLeaderboardUpdater implements Runnable { result = currentLiveLeaderboard; } if (result == null) { - if (logger.isLoggable(Level.FINEST)) { - logger.finest("waiting for leaderboard for "+namesOfRaceColumnsForWhichToLoadLegDetails+" and addOverallDetails="+addOverallDetails); - } + logger.finest(()->"waiting for leaderboard for "+namesOfRaceColumnsForWhichToLoadLegDetails+" and addOverallDetails="+addOverallDetails); ensureRunning(); try { this.wait(); @@ -184,18 +182,12 @@ public class LiveLeaderboardUpdater implements Runnable { if (columnNamesForWhichCurrentLiveLeaderboardHasTheDetails.containsAll(namesOfRaceColumnsForWhichToLoadLegDetails) && (!addOverallDetails || currentLiveLeaderboardHasOverallDetails)) { result = currentLiveLeaderboard; - if (logger.isLoggable(Level.FINEST)) { - logger.finest("successfully waited for leaderboard for "+namesOfRaceColumnsForWhichToLoadLegDetails+" and addOverallDetails="+addOverallDetails); - } + logger.finest(()->"successfully waited for leaderboard for "+namesOfRaceColumnsForWhichToLoadLegDetails+" and addOverallDetails="+addOverallDetails); } else { - if (logger.isLoggable(Level.FINEST)) { - logger.finest("waiting for leaderboard for "+namesOfRaceColumnsForWhichToLoadLegDetails+" and addOverallDetails="+addOverallDetails+" unsuccessful. Need to try again..."); - } + logger.finest(()->"waiting for leaderboard for "+namesOfRaceColumnsForWhichToLoadLegDetails+" and addOverallDetails="+addOverallDetails+" unsuccessful. Need to try again..."); } } else { - if (logger.isLoggable(Level.FINEST)) { - logger.finest("leaderboard for "+namesOfRaceColumnsForWhichToLoadLegDetails+" and addOverallDetails="+addOverallDetails+" was provided in the meantime"); - } + logger.finest(()->"leaderboard for "+namesOfRaceColumnsForWhichToLoadLegDetails+" and addOverallDetails="+addOverallDetails+" was provided in the meantime"); } // now we either have a result (good, we're done), or the thread stopped running (then we need to renew the request) // or the thread still runs but the result may have expired and therefore be null (renew the request) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/MarkPassingCalculator.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/MarkPassingCalculator.java index 8539ef86d47..8b76e2442b1 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/MarkPassingCalculator.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/MarkPassingCalculator.java @@ -9,6 +9,7 @@ import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; import java.util.logging.Level; import java.util.logging.Logger; @@ -42,6 +43,7 @@ import com.sap.sse.util.ThreadPoolUtil; * signals that the race is over (after {@link #stop()} is called. * * @author Nicolas Klose + * @author Axel Uhl (d043530) * */ public class MarkPassingCalculator { @@ -51,17 +53,37 @@ public class MarkPassingCalculator { private static final Logger logger = Logger.getLogger(MarkPassingCalculator.class.getName()); private final MarkPassingUpdateListener listener; private final static ExecutorService executor = ThreadPoolUtil.INSTANCE.getDefaultBackgroundTaskThreadPoolExecutor(); + private final LinkedBlockingQueue queue; + + /** + * An "end marker" that can be {@link #enqueueUpdate(StorePositionUpdateStrategy) enqueued} in order to tell the {@link Listen} + * thread to stop. + */ + private final StorePositionUpdateStrategy endMarker = new StorePositionUpdateStrategy() { + @Override + public void storePositionUpdate(Map> competitorFixes, + Map> markFixes, List addedWaypoints, List removedWaypoints, + IntHolder smallestChangedWaypointIndex, List> fixedMarkPassings, + List> removedMarkPassings, + List> suppressedMarkPassings, List unSuppressedMarkPassings, CandidateFinder candidateFinder, CandidateChooser candidateChooser) { + } + }; private boolean suspended = false; /** * If the constructor is called with the {@code listen} parameter set to {@code true}, this field will hold a thread - * when the constructor has terminated normally. The thread may not yet have been started because, depending on the + * when objects were added to the {@link #queue} and are being processed. When the thread has completed processing + * all updates from the {@link #queue} it will terminate and set this field to {@code null}. The decision to create + * and stop the thread goes hand in hand with setting or, respectively, clearing this field while holding this object's + * monitor ({@code synchronized}).

    + * + * The thread may not yet have been started because, depending on the * constructor's {@code waitForInitialMarkPassingCalculation} argument, start-up may be asynchronous, but eventually * it will be started at some point. The {@link #waitUntilStopped} method can be used to wait until the thread has * started and then terminated again. */ - private final Thread listenerThread; + private Thread listenerThread; private final Listen listen; @@ -72,18 +94,18 @@ public class MarkPassingCalculator { public MarkPassingCalculator(DynamicTrackedRace race, boolean doListen, boolean waitForInitialMarkPassingCalculation) { if (doListen) { - listener = new MarkPassingUpdateListener(race); + queue = new LinkedBlockingQueue<>(); + listener = new MarkPassingUpdateListener(race, this); } else { listener = null; + queue = null; // no queue is needed if no change listener is in use } this.race = race; finder = new CandidateFinderImpl(race, executor); chooser = new CandidateChooserImpl(race); - if (doListen) { - listen = new Listen(race.getRace().getName()); - listenerThread = new Thread(listen, "MarkPassingCalculator for race " + race.getRace().getName()); + if (listener != null) { + listen = new Listen(); } else { - listenerThread = null; listen = null; } Thread t = new Thread(() -> { @@ -100,12 +122,15 @@ public class MarkPassingCalculator { } catch (Exception e) { logger.log(Level.SEVERE, "Error trying to compute initial set of mark passings for race "+race.getRace().getName(), e); } - if (doListen) { - listenerThread.setDaemon(true); - listenerThread.start(); - synchronized (listenerThread) { - listenerThreadStarted = true; - listenerThread.notifyAll(); + if (listener != null) { + synchronized (MarkPassingCalculator.this) { + if (listenerThread == null) { + listenerThread = createAndStartListenerThread(); + synchronized (listenerThread) { + listenerThreadStarted = true; + listenerThread.notifyAll(); + } + } } } }, "MarkPassingCalculator for race "+race.getRace().getName()+" initialization"); @@ -115,6 +140,13 @@ public class MarkPassingCalculator { t.start(); } } + + private Thread createAndStartListenerThread() { + final Thread result = new Thread(listen, "MarkPassingCalculator for race " + race.getRace().getName()); + result.setDaemon(true); + result.start(); + return result; + } /** * Waits for the listening thread to terminate. If no listening thread has been requested ({@code listen} constructor @@ -179,8 +211,8 @@ public class MarkPassingCalculator { */ private final NamedReentrantReadWriteLock lock; - public Listen(String raceName) { - this.raceName = raceName; + public Listen() { + this.raceName = race.getRace().getName(); lock = new NamedReentrantReadWriteLock("lock for calculation thread (" + Listen.class.getSimpleName() + ")", /* fair */ false); } @@ -209,27 +241,40 @@ public class MarkPassingCalculator { while (!finished) { try { logger.finer("MPC for "+raceName+" is checking the queue"); - List allNewFixInsertions = new ArrayList<>(); - try { - allNewFixInsertions.add(listener.getQueue().take()); - } catch (InterruptedException e) { - logger.log(Level.SEVERE, "MarkPassingCalculator for "+raceName+" threw exception " + e.getMessage() - + " while waiting for new GPSFixes"); - } - LockUtil.lockForWrite(lock); - listener.getQueue().drainTo(allNewFixInsertions); - logger.fine("MPC for "+raceName+" received "+ allNewFixInsertions.size()+" new updates."); - for (StorePositionUpdateStrategy fixInsertion : allNewFixInsertions) { - if (listener.isEndMarker(fixInsertion)) { - logger.info("Stopping "+MarkPassingCalculator.this+"'s listener for race "+raceName); + synchronized (MarkPassingCalculator.this) { + // check if queue is empty while owning the MarkPassingCalculator's monitor; + // if the queue is empty, set listenerThread to null while under the monitor + // and decide to finish the thread; otherwise, take the next object + if (queue.isEmpty()) { finished = true; - break; - } else { - fixInsertion.storePositionUpdate(competitorFixes, markFixes, addedWaypoints, removedWaypoints, - smallestChangedWaypointIndex, fixedMarkPassings, removedFixedMarkPassings, - suppressedMarkPassings, unsuppressedMarkPassings, finder, chooser); + listenerThread = null; } } + LockUtil.lockForWrite(lock); + if (!finished) { + List allNewFixInsertions = new ArrayList<>(); + try { + allNewFixInsertions.add(queue.take()); + } catch (InterruptedException e) { + logger.log(Level.SEVERE, "MarkPassingCalculator for "+raceName+" threw exception " + e.getMessage() + + " while waiting for new GPSFixes"); + } + queue.drainTo(allNewFixInsertions); + logger.fine("MPC for "+raceName+" received "+ allNewFixInsertions.size()+" new updates."); + for (StorePositionUpdateStrategy fixInsertion : allNewFixInsertions) { + if (isEndMarker(fixInsertion)) { + logger.info("Stopping "+MarkPassingCalculator.this+"'s listener for race "+raceName); + finished = true; + break; + } else { + fixInsertion.storePositionUpdate(competitorFixes, markFixes, addedWaypoints, removedWaypoints, + smallestChangedWaypointIndex, fixedMarkPassings, removedFixedMarkPassings, + suppressedMarkPassings, unsuppressedMarkPassings, finder, chooser); + } + } + } + // in suspended mode we've stored the update contents in competitorFixes, markFixes, etc. but + // haven't processed them yet with the CandidateFinder or CandidateChooser. if (!finished && !suspended) { if (smallestChangedWaypointIndex.value != -1) { final Course course = race.getRace().getCourse(); @@ -368,15 +413,11 @@ public class MarkPassingCalculator { @Override public void run() { try { - if (logger.isLoggable(Level.FINER)) { - logger.finer("Calculating MarkPassings for race "+raceName+", competitor " + c + " (" + Util.size(fixes) + " new fixes)"); - } + logger.finer(()->"Calculating MarkPassings for race "+raceName+", competitor " + c + " (" + Util.size(fixes) + " new fixes)"); Util.Pair, Iterable> candidateDeltas = finder.getCandidateDeltas(c, fixes); - if (logger.isLoggable(Level.FINER)) { - logger.finer("Received " + Util.size(candidateDeltas.getA()) + " new Candidates for race "+raceName+ - " and competitor "+c+" and will remove " - + Util.size(candidateDeltas.getB()) + " old Candidates for " + c); - } + logger.finer(()->"Received " + Util.size(candidateDeltas.getA()) + " new Candidates for race "+raceName+ + " and competitor "+c+" and will remove " + + Util.size(candidateDeltas.getB()) + " old Candidates for " + c); chooser.calculateMarkPassDeltas(c, candidateDeltas.getA(), candidateDeltas.getB()); } catch (Exception e) { // make sure the exception is logged and not only "swallowed" and suppressed by an invokeAll(...) statement @@ -403,7 +444,7 @@ public class MarkPassingCalculator { public void resume() { logger.finest("Resumed MarkPassingCalculator"); suspended = false; - listener.getQueue().add(new StorePositionUpdateStrategy() { + queue.add(new StorePositionUpdateStrategy() { @Override public void storePositionUpdate(Map> competitorFixes, Map> markFixes, List addedWaypoints, List removedWaypoints, @@ -414,9 +455,25 @@ public class MarkPassingCalculator { } }); } + + private boolean isEndMarker(StorePositionUpdateStrategy endMarkerCandidate) { + return endMarkerCandidate == endMarker; + } + + void enqueueUpdate(StorePositionUpdateStrategy update) { + // TODO bug 3908: manage listener thread here; if this is the first update to enter an empty queue, launch the thread; the thread will terminate after picking the last update from the queue + synchronized (this) { + final boolean wasQueueEmpty = queue.isEmpty(); + queue.add(update); + if (wasQueueEmpty && listenerThread == null) { + listenerThread = createAndStartListenerThread(); + } + } + } public void stop() { - listener.stop(); + logger.info("Stopping " + this + " for race " + race.getRace().getName()); + enqueueUpdate(endMarker); } public void recalculateEverything() { diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/MarkPassingUpdateListener.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/MarkPassingUpdateListener.java index 4c0ccc67f2f..127eb849f26 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/MarkPassingUpdateListener.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/MarkPassingUpdateListener.java @@ -4,9 +4,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.logging.Logger; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Mark; @@ -30,36 +27,22 @@ import com.sap.sse.common.util.IntHolder; * */ public class MarkPassingUpdateListener extends AbstractRaceChangeListener { - private static final Logger logger = Logger.getLogger(MarkPassingUpdateListener.class.getName()); - private LinkedBlockingQueue queue; - private final String raceName; - - private final StorePositionUpdateStrategy endMarker = new StorePositionUpdateStrategy() { - @Override - public void storePositionUpdate(Map> competitorFixes, - Map> markFixes, List addedWaypoints, List removedWaypoints, - IntHolder smallestChangedWaypointIndex, List> fixedMarkPassings, - List> removedMarkPassings, - List> suppressedMarkPassings, List unSuppressedMarkPassings, CandidateFinder candidateFinder, CandidateChooser candidateChooser) { - } - }; + private final MarkPassingCalculator markPassingCalculator; /** * Adds itself automatically as a Listener on the race and its course. + * + * @param markPassingCalculator + * the mark passing calculator to send updates to for enqueuing */ - public MarkPassingUpdateListener(DynamicTrackedRace race) { - raceName = race.getRace().getName(); - queue = new LinkedBlockingQueue<>(); + public MarkPassingUpdateListener(DynamicTrackedRace race, MarkPassingCalculator markPassingCalculator) { + this.markPassingCalculator = markPassingCalculator; race.addListener(this); } - public BlockingQueue getQueue() { - return queue; - } - @Override public void competitorPositionChanged(final GPSFixMoving fix, final Competitor competitor) { - queue.add(new StorePositionUpdateStrategy() { + markPassingCalculator.enqueueUpdate(new StorePositionUpdateStrategy() { @Override public void storePositionUpdate(Map> competitorFixes, Map> markFixes, List addedWaypoints, List removedWaypoints, @@ -79,7 +62,7 @@ public class MarkPassingUpdateListener extends AbstractRaceChangeListener { @Override public void markPositionChanged(final GPSFix fix, final Mark mark, boolean firstInTrack) { - queue.add(new StorePositionUpdateStrategy() { + markPassingCalculator.enqueueUpdate(new StorePositionUpdateStrategy() { @Override public void storePositionUpdate(Map> competitorFixes, Map> markFixes, List addedWaypoints, List removedWaypoints, @@ -97,18 +80,9 @@ public class MarkPassingUpdateListener extends AbstractRaceChangeListener { }); } - public boolean isEndMarker(StorePositionUpdateStrategy endMarkerCandidate) { - return endMarkerCandidate == endMarker; - } - - public void stop() { - logger.info("Stopping " + this + " for race " + raceName); - queue.add(endMarker); - } - @Override public void waypointAdded(final int zeroBasedIndex, final Waypoint waypointThatGotAdded) { - queue.add(new StorePositionUpdateStrategy() { + markPassingCalculator.enqueueUpdate(new StorePositionUpdateStrategy() { @Override public void storePositionUpdate(Map> competitorFixes, Map> markFixes, List addedWaypoints, List removedWaypoints, @@ -126,7 +100,7 @@ public class MarkPassingUpdateListener extends AbstractRaceChangeListener { @Override public void waypointRemoved(final int zeroBasedIndex, final Waypoint waypointThatGotRemoved) { - queue.add(new StorePositionUpdateStrategy() { + markPassingCalculator.enqueueUpdate(new StorePositionUpdateStrategy() { @Override public void storePositionUpdate(Map> competitorFixes, Map> markFixes, List addedWaypoints, List removedWaypoints, @@ -144,7 +118,7 @@ public class MarkPassingUpdateListener extends AbstractRaceChangeListener { public void addFixedPassing(final Competitor c, final Integer zeroBasedIndexOfWaypoint, final TimePoint timePointOfFixedPassing) { - queue.add(new StorePositionUpdateStrategy() { + markPassingCalculator.enqueueUpdate(new StorePositionUpdateStrategy() { @Override public void storePositionUpdate(Map> competitorFixes, Map> markFixes, List addedWaypoints, List removedWaypoints, @@ -159,7 +133,7 @@ public class MarkPassingUpdateListener extends AbstractRaceChangeListener { } public void removeFixedPassing(final Competitor c, final Integer zeroBasedIndexOfWaypoint) { - queue.add(new StorePositionUpdateStrategy() { + markPassingCalculator.enqueueUpdate(new StorePositionUpdateStrategy() { @Override public void storePositionUpdate(Map> competitorFixes, Map> markFixes, List addedWaypoints, List removedWaypoints, @@ -174,7 +148,7 @@ public class MarkPassingUpdateListener extends AbstractRaceChangeListener { } public void addSuppressedPassing(final Competitor c, final Integer zeroBasedIndexOfWaypoint) { - queue.add(new StorePositionUpdateStrategy() { + markPassingCalculator.enqueueUpdate(new StorePositionUpdateStrategy() { @Override public void storePositionUpdate(Map> competitorFixes, Map> markFixes, List addedWaypoints, List removedWaypoints, @@ -189,7 +163,7 @@ public class MarkPassingUpdateListener extends AbstractRaceChangeListener { } public void removeSuppressedPassing(final Competitor c) { - queue.add(new StorePositionUpdateStrategy() { + markPassingCalculator.enqueueUpdate(new StorePositionUpdateStrategy() { @Override public void storePositionUpdate(Map> competitorFixes, Map> markFixes, List addedWaypoints, List removedWaypoints, @@ -204,7 +178,7 @@ public class MarkPassingUpdateListener extends AbstractRaceChangeListener { @Override public void startOfRaceChanged(TimePoint oldStartOfRace, TimePoint newStartOfRace) { - queue.add(new StorePositionUpdateStrategy() { + markPassingCalculator.enqueueUpdate(new StorePositionUpdateStrategy() { @Override public void storePositionUpdate(Map> competitorFixes, Map> markFixes, List addedWaypoints, List removedWaypoints, @@ -223,7 +197,7 @@ public class MarkPassingUpdateListener extends AbstractRaceChangeListener { @Override public void finishedTimeChanged(TimePoint oldFinishedTime, TimePoint newFinishedTime) { - queue.add(new StorePositionUpdateStrategy() { + markPassingCalculator.enqueueUpdate(new StorePositionUpdateStrategy() { @Override public void storePositionUpdate(Map> competitorFixes, Map> markFixes, List addedWaypoints, List removedWaypoints, diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateChooserImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateChooserImpl.java index 272e6db711b..b40cefa97b3 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateChooserImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateChooserImpl.java @@ -13,7 +13,6 @@ import java.util.NavigableSet; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; -import java.util.logging.Level; import java.util.logging.Logger; import com.sap.sailing.domain.base.Competitor; @@ -370,9 +369,7 @@ public class CandidateChooserImpl implements CandidateChooser { // The most likely edge taking us to currentMostLikelyEdge.getEnd() is found. Remember it. candidateWithParentAndHighestTotalProbability.put(currentMostLikelyEdge.getEnd(), new Util.Pair( currentMostLikelyEdge.getStart(), currentHighestProbability)); - if (logger.isLoggable(Level.FINEST)) { - logger.finest("Added "+ currentMostLikelyEdge + " as most likely edge for " + c); - } + logger.finest(()->"Added "+ currentMostLikelyEdge + " as most likely edge for " + c); endFound = currentMostLikelyEdge.getEnd() == endOfFixedInterval; if (!endFound) { // the end of the segment was not yet found; add edges leading away from @@ -426,9 +423,7 @@ public class CandidateChooserImpl implements CandidateChooser { newMarkPassings.add(newMarkPassing); } } - if (logger.isLoggable(Level.FINE)) { - logger.fine("Updating MarkPasses for " + c + " in case "+race.getRace().getName()); - } + logger.fine(()->"Updating MarkPasses for " + c + " in case "+race.getRace().getName()); race.updateMarkPassings(c, newMarkPassings); } } @@ -542,9 +537,7 @@ public class CandidateChooserImpl implements CandidateChooser { private void removeCandidates(Competitor c, Iterable wrongCandidates) { for (Candidate can : wrongCandidates) { - if (logger.isLoggable(Level.FINEST)) { - logger.finest("Removing all edges containing " + can + "of "+ c); - } + logger.finest(()->"Removing all edges containing " + can + "of "+ c); candidates.get(c).remove(can); Map> edges = allEdges.get(c); edges.remove(can); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polars/PolarDataService.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polars/PolarDataService.java index 5329886f015..3843f2bcc65 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polars/PolarDataService.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polars/PolarDataService.java @@ -170,14 +170,15 @@ public interface PolarDataService { void unregisterListener(BoatClass boatClass, PolarsChangedListener listener); + /** + * Announces a base {@link DomainFactory} to this polar data service that it now can start using. Calling this + * method with a non-{@code null} parameter will unblock all {@link #runWithDomainFactory} calls. + */ void registerDomainFactory(DomainFactory domainFactory); - void unregisterDomainFactory(DomainFactory domainFactory); - /** * When called, the method blocks until a {@link DomainFactory} has been {@link #registerDomainFactory(DomainFactory) registered} * with this service, then lets the {@code consumer} accept that domain factory. - * @throws InterruptedException */ void runWithDomainFactory(Consumer consumer) throws InterruptedException; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/AbstractRaceTrackerBaseImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/AbstractRaceTrackerBaseImpl.java index 20a899d1b3d..9fe94a359a2 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/AbstractRaceTrackerBaseImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/AbstractRaceTrackerBaseImpl.java @@ -2,12 +2,27 @@ package com.sap.sailing.domain.tracking; import java.io.IOException; import java.net.MalformedURLException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import com.sap.sailing.domain.base.RaceDefinition; /** * Base class for all {@link RaceTracker}s that must implement listener notifications */ public abstract class AbstractRaceTrackerBaseImpl implements RaceTracker { private final RaceTrackerListeners listeners = new RaceTrackerListeners(); + private final Set raceCreationListeners = Collections + .newSetFromMap(new ConcurrentHashMap()); + + private final RaceTrackingConnectivityParameters connectivityParams; + + public AbstractRaceTrackerBaseImpl(RaceTrackingConnectivityParameters connectivityParams) { + super(); + this.connectivityParams = connectivityParams; + } /** * Ensure stop method does notify all listeners after tracker stopped. @@ -23,11 +38,6 @@ public abstract class AbstractRaceTrackerBaseImpl implements RaceTracker { /** * Template stop method for subclasses. - * - * @param preemptive - * @throws MalformedURLException - * @throws IOException - * @throws InterruptedException */ protected void onStop(boolean preemptive) throws MalformedURLException, IOException, InterruptedException { } @@ -41,4 +51,44 @@ public abstract class AbstractRaceTrackerBaseImpl implements RaceTracker { public void remove(Listener listener) { listeners.removeListener(listener); } + + @Override + public void add(RaceCreationListener listener) { + final RaceDefinition race; + synchronized (raceCreationListeners) { + race = getRace(); + if (race == null) { + raceCreationListeners.add(listener); + } + } + if (race != null) { // listener hasn't been added above; notify immediately + listener.onRaceCreated(this); + } + } + + @Override + public void remove(RaceCreationListener listener) { + synchronized (raceCreationListeners) { + raceCreationListeners.remove(listener); + } + } + + /** + * Notifies all {@link RaceCreationListener}s registered and removes them. Must be called + * after {@link #getRace()} has started returning a valid, non-{@code null} race. + */ + protected void notifyRaceCreationListeners() { + assert getRace() != null; + final Set listenersToNotify; + synchronized (raceCreationListeners) { + listenersToNotify = new HashSet<>(raceCreationListeners); + raceCreationListeners.clear(); + } + listenersToNotify.forEach(l->l.onRaceCreated(this)); + } + + @Override + public RaceTrackingConnectivityParameters getConnectivityParams() { + return connectivityParams; + } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/AbstractRaceTrackerImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/AbstractRaceTrackerImpl.java index 158b784f312..a2ce0922d8c 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/AbstractRaceTrackerImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/AbstractRaceTrackerImpl.java @@ -1,25 +1,28 @@ package com.sap.sailing.domain.tracking; -import java.util.HashSet; -import java.util.Set; - import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; public abstract class AbstractRaceTrackerImpl extends AbstractRaceTrackerBaseImpl { + public AbstractRaceTrackerImpl(RaceTrackingConnectivityParameters connectivityParams) { + super(connectivityParams); + } + @Override - public Set getRaceIdentifiers() { - Set result = new HashSet(); - final Set races = getRaces(); - if (races != null) { - for (RaceDefinition race : races) { - TrackedRace trackedRace = getTrackedRegatta().getTrackedRace(race); - if (trackedRace != null) { - result.add(trackedRace.getRaceIdentifier()); - } + public RegattaAndRaceIdentifier getRaceIdentifier() { + final RegattaAndRaceIdentifier result; + final RaceDefinition race = getRace(); + if (race != null) { + TrackedRace trackedRace = getTrackedRegatta().getTrackedRace(race); + if (trackedRace != null) { + result = trackedRace.getRaceIdentifier(); + } else { + result = null; } + } else { + result = null; } return result; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicRaceDefinitionSet.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicRaceDefinitionSet.java index 1c6d11f66ac..a9497e47c14 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicRaceDefinitionSet.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicRaceDefinitionSet.java @@ -6,7 +6,7 @@ import com.sap.sailing.domain.base.RaceDefinition; * Particularly the way the TracTrac connector was originally designed, the set of {@link RaceDefinition} objects * received from connecting to a particular TracTrac event is not known up-front. We even wait (for historic reasons, * because the course definition used to be immutable) for the course to be received before creating the - * {@link RaceDefinition} object. Therefore, a {@link RaceTracker#getRaces()} call needs to understand which + * {@link RaceDefinition} object. Therefore, a {@link RaceTracker#getRace()} call needs to understand which * {@link RaceDefinition} objects were created so far.

    * * This interface is used particularly by the race course receiver in the TracTrac case to update a tracker's set @@ -15,6 +15,7 @@ import com.sap.sailing.domain.base.RaceDefinition; * @author Axel Uhl (d043530) * */ +@FunctionalInterface public interface DynamicRaceDefinitionSet { void addRaceDefinition(RaceDefinition race, DynamicTrackedRace trackedRace); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/LineDetails.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/LineDetails.java index 8f10b68174b..ac829d2eb67 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/LineDetails.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/LineDetails.java @@ -24,7 +24,7 @@ public interface LineDetails { Distance getLength(); - Bearing getAbsoluteAngleDifferenceToTrueWind(); + Bearing getAngleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind(); NauticalSide getAdvantageousSideWhileApproachingLine(); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTracker.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTracker.java index 347b8ec2a4f..02b4b3b6258 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTracker.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTracker.java @@ -3,7 +3,6 @@ package com.sap.sailing.domain.tracking; import java.io.IOException; import java.net.MalformedURLException; import java.util.Map; -import java.util.Set; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; @@ -13,8 +12,8 @@ import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; * Centerpiece of a tracking adapter. A tracker is responsible for receiving tracking data for one or more * {@link RaceDefinition races} that are {@link Regatta#getAllRaces() part of} a common {@link #getRegatta() Event}. Some * tracker architectures may not be able to deliver all data for the {@link RaceDefinition} when created or started. - * Therefore, {@link #getRaces()} may return null if the race information hasn't been received by the - * tracker yet. Through the {@link RaceHandle} returned by {@link #getRacesHandle()} it is also possible to perform a + * Therefore, {@link #getRace()} may return null if the race information hasn't been received by the + * tracker yet. Through the {@link RaceHandle} returned by {@link #getRaceHandle()} it is also possible to perform a * {@link RaceHandle#getRace() blocking get} for the race tracked by this tracker. *

    * @@ -56,17 +55,17 @@ public interface RaceTracker { com.sap.sailing.domain.base.Regatta getRegatta(); /** - * Returns the races being tracked by this tracker. Non-blocking call that returns null or an empty set - * if the {@link RaceDefinition} for a TracTrac Event hasn't been created yet, e.g., because the course definition + * Returns the races being tracked by this tracker. Non-blocking call that returns null + * if the {@link RaceDefinition} hasn't been created yet, e.g., because the course definition * hasn't been received yet or the listener for receiving course information hasn't been registered (yet). Also - * returns races that have been removed from containing structures which may lead this tracker to no longer update + * returns a race that may have been removed from containing structures which may lead this tracker to no longer update * their {@link TrackedRace} with new data. */ - Set getRaces(); + RaceDefinition getRace(); - Set getRaceIdentifiers(); + RegattaAndRaceIdentifier getRaceIdentifier(); - RaceHandle getRacesHandle(); + RaceHandle getRaceHandle(); DynamicTrackedRegatta getTrackedRegatta(); @@ -80,6 +79,7 @@ public interface RaceTracker { /** * Listener interface for race tracker related events */ + @FunctionalInterface interface Listener { /** * Tracker has stopped event, see {@link RaceTracker#stop(boolean)} method @@ -88,20 +88,55 @@ public interface RaceTracker { */ void onTrackerWillStop(boolean preemptive); } + + @FunctionalInterface + interface RaceCreationListener { + /** + * Tracker has received its {@link RaceDefinition}, so that now {@link RaceTracker#getRace} no longer returns + * {@code null} but a valid {@link RaceDefinition} + * + * @param tracker + * this tracker is passed to the listener which can then obtain the {@link RaceTracker#getRace() + * race} and the {@link RaceTracker#getConnectivityParams() connectivity parameters}, etc. If the + * {@link RaceTracker} already has created its race, this method is called immediately upon + * {@link RaceTracker#add(Listener) adding} this listener so that the listener will always receive + * the call for a valid race if the race is created at any point in time, regardless the point in + * time of the listener registration. This helps avoid race conditions. + */ + void onRaceCreated(RaceTracker tracker); + } /** * Register a new RaceTracker.Listener for this race tracker. * - * @param newListener * @return true if listener has been added */ boolean add(RaceTracker.Listener newListener); /** - * Remove listener from racetracker + * Remove listener from race tracker * - * @param newListener + * @param listener * @return the listener registration for listener removal */ - void remove(RaceTracker.Listener newListener); + void remove(RaceTracker.Listener listener); + + /** + * Adds a listener that will be {@link RaceCreationListener#onRaceCreated(RaceTracker) notified} when the + * {@link #getRace()} method starts returning a valid, non-{@code null} race. The listener will be notified + * immediately if {@link #getRace()} already yields a valid {@link RaceDefinition} when calling this method so + * that the listener will be notified at least once. The listener will automatically be removed after it has been + * notified. + */ + void add(RaceTracker.RaceCreationListener listener); + + void remove(RaceTracker.RaceCreationListener listener); + + /** + * The connectivity parameters used to create this tracker. Can be used, e.g., to add or remove those parameters to + * the set of trackers to restore after a server restart. May return {@code null}, e.g., in case the tracker was + * created by a test case that did not use a {@link RaceTrackingConnectivityParameters} object to describe what to + * track. + */ + RaceTrackingConnectivityParameters getConnectivityParams(); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTrackerListeners.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTrackerListeners.java index f42771c89ec..ff9a95a6d30 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTrackerListeners.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTrackerListeners.java @@ -27,9 +27,13 @@ public class RaceTrackerListeners implements RaceTracker.Listener { } @Override - public synchronized void onTrackerWillStop(boolean preemptive) { - isStopped.set(true); - isStoppedPreemptive.set(preemptive); - registeredListeners.forEach(l -> l.onTrackerWillStop(preemptive)); + public void onTrackerWillStop(boolean preemptive) { + Iterable listenersToInform; + synchronized (this) { + isStopped.set(true); + isStoppedPreemptive.set(preemptive); + listenersToInform = new HashSet<>(registeredListeners); + } + listenersToInform.forEach(l -> l.onTrackerWillStop(preemptive)); } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTrackingConnectivityParameters.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTrackingConnectivityParameters.java index 05376e6f043..f93855776b8 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTrackingConnectivityParameters.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTrackingConnectivityParameters.java @@ -5,21 +5,26 @@ import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Series; - - - /** * Different tracking providers require different sets of arguments to start tracking a race. * This interface represents the functionality required to start tracking a race, agnostic of the * particular parameters and ways of launching the connector. Use {@link #createRaceTracker()} to - * create the tracker that starts tracking the race identified by these tracking parameters. + * create the tracker that starts tracking the race identified by these tracking parameters.

    + * + * The parameters also tell whether wind tracking shall be activated for this race (see {@link #isTrackWind()}). + * This property can be {@link #setTrackWind(boolean) updated}, making sense in case tracking a live race + * is stopped in a running server, stopping the wind tracker and therefore not requiring wind tracking + * to be activated anymore when the race is restored during server restart. * * @author Axel Uhl (d043530) * */ public interface RaceTrackingConnectivityParameters { /** - * Starts a {@link RaceTracker} using the connectivity parameters provided by this object. + * Starts a {@link RaceTracker} using the connectivity parameters provided by this object. As no specific + * {@link Regatta} is provided, this will first look up a regatta for the race from + * {@link TrackedRegattaRegistry#getRememberedRegattaForRace(java.io.Serializable)} and if not found will look up or + * create a default regatta based on race data such as an event name and the boat class. */ RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore, RaceLogResolver raceLogResolver) throws Exception; @@ -43,4 +48,16 @@ public interface RaceTrackingConnectivityParameters { * Gets the configured delay time to the 'live' timepoint for this tracker */ public long getDelayToLiveInMillis(); + + /** + * A non-{@code null} unique type identifier that helps finding services that need to deal with connectivity + * parameters in a type-specific way + */ + public String getTypeIdentifier(); + + boolean isTrackWind(); + + void setTrackWind(boolean trackWind); + + boolean isCorrectWindDirectionByMagneticDeclination(); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTrackingConnectivityParametersHandler.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTrackingConnectivityParametersHandler.java new file mode 100644 index 00000000000..a47f15c71d7 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceTrackingConnectivityParametersHandler.java @@ -0,0 +1,48 @@ +package com.sap.sailing.domain.tracking; + +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.util.Map; +import java.util.UUID; + +import org.json.simple.JSONObject; + +import com.sap.sse.common.TypeBasedServiceFinder; + +/** + * Handler for mapping objects of classes that implement the {@link RaceTrackingConnectivityParameters} interface + * to / from maps using {@link String}s as keys, such as {@link JSONObject} or {@link BasicDBObject}. This can be + * used to save and restore the races currently being tracked by a server, based on the tracking connectivity + * data provided when last tracked. + * + * @author Axel Uhl (d043530) + * + */ +public interface RaceTrackingConnectivityParametersHandler { + /** + * Produces a map that has the value for the {@link TypeBasedServiceFinder#TYPE} key set such that this handler + * can be {@link TypeBasedServiceFinder#findService(String) found} based on that value again; furthermore, all + * other key/value pairs in the map returned allow the {@link #mapTo(Map)} method of this handler to restore + * a {@link RaceTrackingConnectivityParameters} object that is equivalent to {@code params}.

    + * + * The types of the value objects must be restricted to thos types that MongoDB can handle as field values, + * in particular {@link String}, {@link Number} and {@link UUID}. + */ + Map mapFrom(RaceTrackingConnectivityParameters params); + + /** + * Produces a {@link RaceTrackingConnectivityParameters} object from the contents of {@code map}, ignoring the + * {@link TypeBasedServiceFinder#TYPE} key which has enabled {@link TypeBasedServiceFinder#findService(String) + * finding} this service instance in the first place. The object returned is equivalent to one that has + * produced {@code map} by passing it to this service's {@link #mapFrom(RaceTrackingConnectivityParameters)} + * method. + */ + RaceTrackingConnectivityParameters mapTo(Map map) throws MalformedURLException, URISyntaxException; + + /** + * @return a map that contains the {@link TypeBasedServiceFinder#TYPE} identifier as taken from + * {@link RaceTrackingConnectivityParameters#getTypeIdentifier()} as well as a non-empty set of further + * entries that uniquely identify the {@code params} object in the database, in particular for removing. + */ + Map getKey(RaceTrackingConnectivityParameters params); +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedLeg.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedLeg.java index da4592948ff..30ec3c702c0 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedLeg.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedLeg.java @@ -10,10 +10,9 @@ import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.LegType; import com.sap.sailing.domain.common.NoWindException; import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.TargetTimeInfo.LegTargetTimeInfo; import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.PolarDataService; -import com.sap.sailing.domain.tracking.impl.TrackedLegImpl; -import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util.Pair; @@ -27,7 +26,7 @@ public interface TrackedLeg extends Serializable { TrackedRace getTrackedRace(); /** - * Determines whether the current {@link #getLeg() leg} is +/- {@link #UPWIND_DOWNWIND_TOLERANCE_IN_DEG} degrees + * Determines whether the current {@link #getLeg() leg} is +/- {@link LegType#UPWIND_DOWNWIND_TOLERANCE_IN_DEG} degrees * collinear with the current wind's bearing. */ boolean isUpOrDownwindLeg(TimePoint at) throws NoWindException; @@ -101,7 +100,7 @@ public interface TrackedLeg extends Serializable { Distance getGreatCircleDistance(TimePoint timePoint, MarkPositionAtTimePointCache markPositionCache); /** - * If the current {@link #getLeg() leg} is +/- {@link TrackedLegImpl#UPWIND_DOWNWIND_TOLERANCE_IN_DEG} degrees + * If the current {@link #getLeg() leg} is +/- {@link LegType#UPWIND_DOWNWIND_TOLERANCE_IN_DEG} degrees * collinear with the wind's bearing, pos1 is projected onto the line crossing pos2 in the * wind's bearing, and the distance from the projection to pos2 is returned. Otherwise, it is assumed * that the leg is neither an upwind nor a downwind leg, and hence the along-track distance to mark is @@ -164,7 +163,7 @@ public interface TrackedLeg extends Serializable { * is not available * @throws NoWindException no wind available. unable to determine legtypes for given timepoint */ - Duration getEstimatedTimeToComplete(PolarDataService polarDataService, TimePoint timepoint, MarkPositionAtTimePointCache markPositionCache) + LegTargetTimeInfo getEstimatedTimeToComplete(PolarDataService polarDataService, TimePoint timepoint, MarkPositionAtTimePointCache markPositionCache) throws NotEnoughDataHasBeenAddedException, NoWindException; /** diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java index c9f4e7ec66d..a456b3f8d49 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java @@ -32,6 +32,7 @@ import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.Tack; +import com.sap.sailing.domain.common.TargetTimeInfo; import com.sap.sailing.domain.common.TimingConstants; import com.sap.sailing.domain.common.TrackedRaceStatusEnum; import com.sap.sailing.domain.common.Wind; @@ -923,13 +924,13 @@ public interface TrackedRace extends Serializable, IsManagedByCachenull or no regatta by that identifier is found, an existing regatta by the name of the - * TracTrac event with the boat class name appended in parentheses will be looked up; if not found, a - * default regatta with that name will be created, with a single default series and a single default - * fleet. If a valid {@link RegattaIdentifier} is specified, a regatta lookup is performed with that - * identifier; if the regatta is found, it is used to add the races to, and + * if null or no regatta by that identifier is found, the regatta into which the race has + * previously been loaded will be looked up; if found, the race will be loaded into that regatta; + * otherwise, an existing default regatta will be looked up; if not found, a default regatta with that + * name will be created, with a single default series and a single default fleet. If a valid + * {@link RegattaIdentifier} is specified, a regatta lookup is performed with that identifier; if the + * regatta is found, it is used to add the races to, and * {@link #setRegattaForRace(Regatta, RaceDefinition)} is called to remember the association * persistently. Otherwise, a default regatta as described above will be created and used. */ diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/AbstractRaceTrackingConnectivityParameters.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/AbstractRaceTrackingConnectivityParameters.java new file mode 100644 index 00000000000..cc90c62286b --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/AbstractRaceTrackingConnectivityParameters.java @@ -0,0 +1,29 @@ +package com.sap.sailing.domain.tracking.impl; + +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; + +public abstract class AbstractRaceTrackingConnectivityParameters implements RaceTrackingConnectivityParameters { + private boolean trackWind; + private final boolean correctWindDirectionByMagneticDeclination; + + public AbstractRaceTrackingConnectivityParameters(boolean trackWind, boolean correctWindDirectionByMagneticDeclination) { + super(); + this.trackWind = trackWind; + this.correctWindDirectionByMagneticDeclination = correctWindDirectionByMagneticDeclination; + } + + @Override + public boolean isTrackWind() { + return trackWind; + } + + @Override + public void setTrackWind(boolean trackWind) { + this.trackWind = trackWind; + } + + @Override + public boolean isCorrectWindDirectionByMagneticDeclination() { + return correctWindDirectionByMagneticDeclination; + } +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/AbstractRaceTrackingConnectivityParametersHandler.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/AbstractRaceTrackingConnectivityParametersHandler.java new file mode 100644 index 00000000000..447873b891b --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/AbstractRaceTrackingConnectivityParametersHandler.java @@ -0,0 +1,25 @@ +package com.sap.sailing.domain.tracking.impl; + +import java.util.Map; + +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler; + +public abstract class AbstractRaceTrackingConnectivityParametersHandler + implements RaceTrackingConnectivityParametersHandler { + private static final String CORRECT_WIND_DIRECTION_BY_MAGNETIC_DECLINATION = "correctWindDirectionByMagneticDeclination"; + private static final String TRACK_WIND = "trackWind"; + + protected void addWindTrackingParameters(RaceTrackingConnectivityParameters params, Map map) { + map.put(TRACK_WIND, params.isTrackWind()); + map.put(CORRECT_WIND_DIRECTION_BY_MAGNETIC_DECLINATION, params.isCorrectWindDirectionByMagneticDeclination()); + } + + protected boolean isTrackWind(Map from) { + return from.containsKey(TRACK_WIND) ? (Boolean) from.get(TRACK_WIND) : false; + } + + protected boolean isCorrectWindDirectionByMagneticDeclination(Map from) { + return from.containsKey(CORRECT_WIND_DIRECTION_BY_MAGNETIC_DECLINATION) ? (Boolean) from.get(CORRECT_WIND_DIRECTION_BY_MAGNETIC_DECLINATION) : true; + } +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceLogListener.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceLogListener.java index 3726f3f6624..2547b155557 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceLogListener.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceLogListener.java @@ -56,6 +56,9 @@ public class DynamicTrackedRaceLogListener extends BaseRaceLogEventVisitor { private static final Logger logger = Logger.getLogger(DynamicTrackedRaceLogListener.class.getName()); + /** + * Race logs as keys, {@link ReadonlyRaceState} objects wrapping their respective key race log as values + */ private ConcurrentMap raceLogs = new ConcurrentHashMap<>(); private DynamicTrackedRace trackedRace; diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GPSFixTrackImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GPSFixTrackImpl.java index aaa22122ffe..12e0b505735 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GPSFixTrackImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GPSFixTrackImpl.java @@ -1062,15 +1062,13 @@ public class GPSFixTrackImpl extends MappedTra FixType last; lockForRead(); try { - if (logger.isLoggable(Level.FINEST)) { - logger.finest("GPS fix "+fix+" for "+getTrackedItem()+", isValid="+isValid(getInternalRawFixes(), fix)+ - ", time/distance/speed from last: "+ - ((last=getInternalRawFixes().lower(fix))==null - ? "null" - : (fix.getTimePoint().asMillis()-last.getTimePoint().asMillis()+"ms/"+ - fix.getPosition().getDistance(last.getPosition())) + "/"+ - fix.getPosition().getDistance(last.getPosition()).inTime(fix.getTimePoint().asMillis()-last.getTimePoint().asMillis()))); - } + logger.finest("GPS fix "+fix+" for "+getTrackedItem()+", isValid="+isValid(getInternalRawFixes(), fix)+ + ", time/distance/speed from last: "+ + ((last=getInternalRawFixes().lower(fix))==null + ? "null" + : (fix.getTimePoint().asMillis()-last.getTimePoint().asMillis()+"ms/"+ + fix.getPosition().getDistance(last.getPosition())) + "/"+ + fix.getPosition().getDistance(last.getPosition()).inTime(fix.getTimePoint().asMillis()-last.getTimePoint().asMillis()))); } finally { unlockAfterRead(); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/LineDetailsImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/LineDetailsImpl.java index 20da3d93a3d..54630aaf598 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/LineDetailsImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/LineDetailsImpl.java @@ -12,20 +12,20 @@ public class LineDetailsImpl implements LineDetails { private final TimePoint timePoint; private final Waypoint waypoint; private final Distance length; - private final Bearing absoluteAngleDifferenceToTrueWind; + private final Bearing angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind; private final NauticalSide advantageousSidewhileApproachingLine; private final Distance advantage; private final Mark portMarkWhileApproachingLine; private final Mark starboardMarkWhileApproachingLine; public LineDetailsImpl(TimePoint timePoint, Waypoint waypoint, Distance length, - Bearing absoluteAngleDifferenceToTrueWind, NauticalSide advantageousSideWhileApproachingLine, + Bearing angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind, NauticalSide advantageousSideWhileApproachingLine, Distance advantage, Mark portMarkWhileApproachingLine, Mark starboardMarkWhileApproachingLine) { super(); this.timePoint = timePoint; this.waypoint = waypoint; this.length = length; - this.absoluteAngleDifferenceToTrueWind = absoluteAngleDifferenceToTrueWind; + this.angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind = angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind; this.advantageousSidewhileApproachingLine = advantageousSideWhileApproachingLine; this.advantage = advantage; this.portMarkWhileApproachingLine = portMarkWhileApproachingLine; @@ -48,8 +48,8 @@ public class LineDetailsImpl implements LineDetails { } @Override - public Bearing getAbsoluteAngleDifferenceToTrueWind() { - return absoluteAngleDifferenceToTrueWind; + public Bearing getAngleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind() { + return angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind; } @Override diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java index 85ee7d7ca06..4c521b26d60 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java @@ -31,10 +31,12 @@ import com.sap.sailing.domain.common.NoWindException; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.Tack; +import com.sap.sailing.domain.common.TargetTimeInfo; import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.WindSourceType; import com.sap.sailing.domain.common.impl.MeterDistance; +import com.sap.sailing.domain.common.impl.TargetTimeInfoImpl; import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuseCache; import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.PolarDataService; @@ -58,8 +60,6 @@ public class TrackedLegImpl implements TrackedLeg { private final static Logger logger = Logger.getLogger(TrackedLegImpl.class.getName()); - private final static double UPWIND_DOWNWIND_TOLERANCE_IN_DEG = 45; // TracTrac does 22.5, Marcus Baur suggest 40; Nils Schr�der suggests 60 - private final Leg leg; private final Map trackedLegsOfCompetitors; private TrackedRaceImpl trackedRace; @@ -191,11 +191,11 @@ public class TrackedLegImpl implements TrackedLeg { Bearing legBearing = getLegBearing(at); if (legBearing != null) { double deltaDeg = legBearing.getDifferenceTo(wind.getBearing()).getDegrees(); - if (Math.abs(deltaDeg) < UPWIND_DOWNWIND_TOLERANCE_IN_DEG) { + if (Math.abs(deltaDeg) < LegType.UPWIND_DOWNWIND_TOLERANCE_IN_DEG) { return LegType.DOWNWIND; } else { double deltaDegOpposite = legBearing.getDifferenceTo(wind.getBearing().reverse()).getDegrees(); - if (Math.abs(deltaDegOpposite) < UPWIND_DOWNWIND_TOLERANCE_IN_DEG) { + if (Math.abs(deltaDegOpposite) < LegType.UPWIND_DOWNWIND_TOLERANCE_IN_DEG) { return LegType.UPWIND; } } @@ -513,21 +513,22 @@ public class TrackedLegImpl implements TrackedLeg { } @Override - public Duration getEstimatedTimeToComplete(PolarDataService polarDataService, TimePoint timepoint, MarkPositionAtTimePointCache markPositionCache) + public TargetTimeInfo.LegTargetTimeInfo getEstimatedTimeToComplete(PolarDataService polarDataService, TimePoint timepoint, MarkPositionAtTimePointCache markPositionCache) throws NotEnoughDataHasBeenAddedException, NoWindException { assert timepoint.equals(markPositionCache.getTimePoint()); assert getTrackedRace() == markPositionCache.getTrackedRace(); - Position centralPosition = trackedRace.getCenterOfCourse(timepoint); - Wind wind = trackedRace.getWind(centralPosition, timepoint, getTrackedRace().getWindSources(WindSourceType.TRACK_BASED_ESTIMATION)); + Position centralPosition = getMiddleOfLeg(timepoint); + Wind wind = trackedRace.getWind(centralPosition, timepoint); Position from = trackedRace.getApproximatePosition(leg.getFrom(), timepoint); Position to = trackedRace.getApproximatePosition(leg.getTo(), timepoint); LegType legType = getLegType(timepoint); BoatClass boatClass = trackedRace.getRace().getBoatClass(); - Duration result; + final Bearing legBearing = from.getBearingGreatCircle(to); + Distance distance = from.getDistance(to); + Bearing trueWindAngleToLeg = legBearing.getDifferenceTo(wind.getBearing().reverse()); + final Duration result; if (legType == LegType.REACHING) { - Bearing trueWindAngle = wind.getBearing().getDifferenceTo(from.getBearingGreatCircle(to)); - SpeedWithConfidence reachSpeed = polarDataService.getSpeed(boatClass, wind, trueWindAngle); - Distance distance = from.getDistance(to); + SpeedWithConfidence reachSpeed = polarDataService.getSpeed(boatClass, wind, trueWindAngleToLeg); result = reachSpeed.getObject().getDuration(distance); } else { SpeedWithBearingWithConfidence portSpeedAndBearing = polarDataService.getAverageSpeedWithBearing( @@ -536,7 +537,7 @@ public class TrackedLegImpl implements TrackedLeg { boatClass, wind, legType, Tack.STARBOARD); result = estimateTargetTimeTacking(from, to, portSpeedAndBearing, starboardSpeedAndBearing, wind); } - return result; + return new TargetTimeInfoImpl.LegTargetTimeInfoImpl(distance, wind, legBearing, result, timepoint, legType); } private Duration estimateTargetTimeTacking(Position from, Position to, diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java index fba23c8af4a..b2258f4d2f8 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java @@ -237,7 +237,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor { } /** - * If the current {@link #getLeg() leg} is +/- {@link #UPWIND_DOWNWIND_TOLERANCE_IN_DEG} degrees collinear with the + * If the current {@link #getLeg() leg} is +/- {@link LegType#UPWIND_DOWNWIND_TOLERANCE_IN_DEG} degrees collinear with the * wind's bearing, the competitor's position is projected onto the line crossing mark in the wind's * bearing, and the distance from the projection to the mark is returned. Otherwise, it is assumed that * the leg is neither an upwind nor a downwind leg, and hence the true distance to mark is returned. A diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java index f1cf600b9a7..8f1cf87f9f9 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java @@ -82,11 +82,13 @@ import com.sap.sailing.domain.common.RegattaNameAndRaceName; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.Tack; +import com.sap.sailing.domain.common.TargetTimeInfo; import com.sap.sailing.domain.common.TimingConstants; import com.sap.sailing.domain.common.TrackedRaceStatusEnum; import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.WindSourceType; +import com.sap.sailing.domain.common.TargetTimeInfo.LegTargetTimeInfo; import com.sap.sailing.domain.common.abstractlog.TimePointSpecificationFoundInLog; import com.sap.sailing.domain.common.confidence.BearingWithConfidence; import com.sap.sailing.domain.common.confidence.BearingWithConfidenceCluster; @@ -101,6 +103,7 @@ import com.sap.sailing.domain.common.impl.CourseChangeImpl; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedImpl; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; +import com.sap.sailing.domain.common.impl.TargetTimeInfoImpl; import com.sap.sailing.domain.common.impl.WindImpl; import com.sap.sailing.domain.common.impl.WindSourceImpl; import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus; @@ -913,22 +916,17 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl TimePoint newStartTime = null; TimePoint newStartTimeWithoutInferenceFromStartMarkPassings = null; for (RaceLog raceLog : attachedRaceLogs.values()) { - if (logger.isLoggable(Level.FINEST)) { - logger.finest("Analyzing race log "+raceLog+" for race "+this.getRace().getName()); - } + logger.finest(()->"Analyzing race log "+raceLog+" for race "+this.getRace().getName()); newStartTime = new StartTimeFinder(raceLogResolver, raceLog).analyze().getStartTime(); if (newStartTime != null) { newStartTimeWithoutInferenceFromStartMarkPassings = newStartTime; - if (logger.isLoggable(Level.FINEST)) { - logger.finest("Found start time "+newStartTime+" in race log "+raceLog+" for race "+this.getRace().getName()); - } + final TimePoint finalNewStartTime = newStartTime; + logger.finest(()->"Found start time "+finalNewStartTime+" in race log "+raceLog+" for race "+this.getRace().getName()); break; } } if (newStartTime == null) { - if (logger.isLoggable(Level.FINEST)) { - logger.finest("No start time found in race logs for race "+getRace().getName()); - } + logger.finest(()->"No start time found in race logs for race "+getRace().getName()); newStartTime = getStartTimeReceived(); if (newStartTime != null) { newStartTimeWithoutInferenceFromStartMarkPassings = newStartTime; @@ -951,14 +949,12 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl if (startTimeReceived2timeOfFirstMarkPassingFirstMark > MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS) { newStartTime = new MillisecondsTimePoint(timeOfFirstMarkPassing.asMillis() - MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS); - if (logger.isLoggable(Level.FINEST)) { - logger.finest("Using start mark passings for start time of race "+this.getRace().getName()+": "+newStartTime); - } + final TimePoint finalNewStartTime = newStartTime; + logger.finest(()->"Using start mark passings for start time of race "+this.getRace().getName()+": "+finalNewStartTime); } else { newStartTime = startTimeReceived; - if (logger.isLoggable(Level.FINEST)) { - logger.finest("Using start mark received for race "+this.getRace().getName()+": "+newStartTime); - } + final TimePoint finalNewStartTime = newStartTime; + logger.finest(()->"Using start mark received for race "+this.getRace().getName()+": "+finalNewStartTime); } } } else { @@ -3937,18 +3933,22 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl } @Override - public Duration getEstimatedTimeToComplete(TimePoint timepoint) throws NotEnoughDataHasBeenAddedException, + public TargetTimeInfo getEstimatedTimeToComplete(final TimePoint timepoint) throws NotEnoughDataHasBeenAddedException, NoWindException { if (polarDataService == null) { throw new NotEnoughDataHasBeenAddedException("Target time estimation failed. No polar service available."); } Duration durationOfAllLegs = Duration.NULL; - final MarkPositionAtTimePointCache markPositionCache = new MarkPositionAtTimePointCacheImpl(this, timepoint); + TimePoint current = timepoint; + final List legTargetTimes = new ArrayList<>(); for (TrackedLeg leg : trackedLegs.values()) { - Duration durationOfLeg = leg.getEstimatedTimeToComplete(polarDataService, timepoint, markPositionCache); - durationOfAllLegs = durationOfAllLegs.plus(durationOfLeg); + final MarkPositionAtTimePointCache markPositionCache = new MarkPositionAtTimePointCacheImpl(this, current); + LegTargetTimeInfo legTargetTime = leg.getEstimatedTimeToComplete(polarDataService, current, markPositionCache); + legTargetTimes.add(legTargetTime); + durationOfAllLegs = durationOfAllLegs.plus(legTargetTime.getExpectedDuration()); + current = current.plus(legTargetTime.getExpectedDuration()); // simulate the next leg with the wind as of the projected finishing time of the previous leg } - return durationOfAllLegs; + return new TargetTimeInfoImpl(legTargetTimes); } @Override diff --git a/java/com.sap.sailing.ess40.resultimport.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.ess40.resultimport.test/META-INF/MANIFEST.MF index 68e845ecf70..b91f5ba57b6 100644 --- a/java/com.sap.sailing.ess40.resultimport.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.ess40.resultimport.test/META-INF/MANIFEST.MF @@ -8,5 +8,5 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Fragment-Host: com.sap.sailing.ess40.resultimport Import-Package: org.osgi.framework;version="1.3.0" Bundle-ActivationPolicy: lazy -Require-Bundle: org.junit4;bundle-version="4.8.2", +Require-Bundle: org.junit;bundle-version="4.8.2", com.sap.sse.common diff --git a/java/com.sap.sailing.expeditionconnector.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.expeditionconnector.test/META-INF/MANIFEST.MF index 0d26160e819..4638a24fb27 100644 --- a/java/com.sap.sailing.expeditionconnector.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.expeditionconnector.test/META-INF/MANIFEST.MF @@ -9,5 +9,5 @@ Fragment-Host: com.sap.sailing.expeditionconnector Require-Bundle: com.sap.sailing.server, com.sap.sailing.domain.test, com.sap.sailing.udpconnector, - org.junit4;bundle-version="4.8.2", + org.junit;bundle-version="4.8.2", com.sap.sse.common diff --git a/java/com.sap.sailing.feature.p2build/raceanalysis.product b/java/com.sap.sailing.feature.p2build/raceanalysis.product index 5079f298038..af13da65b55 100755 --- a/java/com.sap.sailing.feature.p2build/raceanalysis.product +++ b/java/com.sap.sailing.feature.p2build/raceanalysis.product @@ -29,6 +29,8 @@ + + @@ -40,14 +42,15 @@ - + + + - @@ -60,6 +63,7 @@ + diff --git a/java/com.sap.sailing.feature.runtime/feature.xml b/java/com.sap.sailing.feature.runtime/feature.xml index 3ac65bbd136..1389950b041 100644 --- a/java/com.sap.sailing.feature.runtime/feature.xml +++ b/java/com.sap.sailing.feature.runtime/feature.xml @@ -18,103 +18,133 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/java/com.sap.sailing.feature/feature.xml b/java/com.sap.sailing.feature/feature.xml index 8f8bc855bdc..ef4aa3fbfad 100755 --- a/java/com.sap.sailing.feature/feature.xml +++ b/java/com.sap.sailing.feature/feature.xml @@ -21,14 +21,8 @@ Computes leaderboard information for sailing races and offers RESTful APIs to al - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + { if (preferences != null) { for (BoatClassNotificationPreference pref : preferences.getBoatClasses()) { String name = pref.getBoatClass().getName(), displayName = pref.getBoatClass().getDisplayName(); - selected.add(new BoatClassDTO(name, displayName, pref.getBoatClass().getHullLength())); + selected.add(new BoatClassDTO(name, displayName, pref.getBoatClass().getHullLength(), pref.getBoatClass().getHullBeam())); notifyAboutUpcomingRaces |= pref.isNotifyAboutUpcomingRaces(); notifyAboutResults |= pref.isNotifyAboutResults(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/multiselection/AbstractSuggestedMultiSelectionBoatClassDataProvider.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/multiselection/AbstractSuggestedMultiSelectionBoatClassDataProvider.java index 6245a0eab63..6a4c4a99d48 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/multiselection/AbstractSuggestedMultiSelectionBoatClassDataProvider.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/multiselection/AbstractSuggestedMultiSelectionBoatClassDataProvider.java @@ -40,7 +40,7 @@ public abstract class AbstractSuggestedMultiSelectionBoatClassDataProvider exten List boatClasses = Arrays.asList(BoatClassMasterdata.values()); List suggestionItems = new ArrayList<>(); for (BoatClassMasterdata bcm : filter.applyFilter(queryTokens, boatClasses)) { - suggestionItems.add(new BoatClassDTO(bcm.getDisplayName(), bcm.getHullLength())); + suggestionItems.add(new BoatClassDTO(bcm.getDisplayName(), bcm.getHullLength(), bcm.getHullBeam())); } callback.setSuggestionItems(suggestionItems); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java index fb3eae8147e..f45175e8c0d 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java @@ -35,6 +35,7 @@ import com.sap.sailing.gwt.ui.shared.SecurityStylesheetResources; import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; import com.sap.sse.gwt.adminconsole.AdminConsolePanel; import com.sap.sse.gwt.adminconsole.DefaultRefreshableAdminConsolePanel; +import com.sap.sse.gwt.adminconsole.ReplicationPanel; import com.sap.sse.gwt.client.EntryPointHelper; import com.sap.sse.gwt.client.async.AsyncActionsExecutor; import com.sap.sse.gwt.client.async.MarkedAsyncCallback; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorEditDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorEditDialog.java index ca595671334..72c63417a2a 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorEditDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorEditDialog.java @@ -188,7 +188,7 @@ public class CompetitorEditDialog extends DataEntryDialog { color = new InvalidColor(iae); } } - BoatClassDTO boatClass = new BoatClassDTO(boatClassName.getValue(), Distance.NULL); + BoatClassDTO boatClass = new BoatClassDTO(boatClassName.getValue(), Distance.NULL, Distance.NULL); BoatDTO boat = new BoatDTO(name.getText(), sailId.getText()); CompetitorDTO result = new CompetitorDTOImpl(name.getText(), color, email.getText(), /* twoLetterIsoCountryCode */ null, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/IgtimiAccountsPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/IgtimiAccountsPanel.java index d0a5e3efb9c..be1d08f79a2 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/IgtimiAccountsPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/IgtimiAccountsPanel.java @@ -156,7 +156,12 @@ public class IgtimiAccountsPanel extends FlowPanel { } }); add(refreshButton); - this.sailingService.getIgtimiAuthorizationUrl(new AsyncCallback() { + final String protocol = Window.Location.getProtocol().endsWith(":") ? + Window.Location.getProtocol().substring(0, Window.Location.getProtocol().length()-1) : + Window.Location.getProtocol(); + final String hostname = Window.Location.getHostName(); + final String port = Window.Location.getPort(); + this.sailingService.getIgtimiAuthorizationUrl(protocol, hostname, port, new AsyncCallback() { @Override public void onFailure(Throwable caught) { errorReporter.reportError(IgtimiAccountsPanel.this.stringMessages.errorGettingIgtimiAuthorizationUrl(caught.getMessage()), diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceLogSetTrackingTimesDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceLogSetTrackingTimesDTO.java index ec047b07de0..b2cd1138fe7 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceLogSetTrackingTimesDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceLogSetTrackingTimesDTO.java @@ -1,6 +1,7 @@ package com.sap.sailing.gwt.ui.adminconsole; import com.google.gwt.user.client.rpc.IsSerializable; +import com.sap.sailing.domain.common.abstractlog.TimePointSpecificationFoundInLog; import com.sap.sse.common.TimePoint; public class RaceLogSetTrackingTimesDTO implements IsSerializable { @@ -10,8 +11,8 @@ public class RaceLogSetTrackingTimesDTO implements IsSerializable { public String authorName; public Integer authorPriority; public TimePoint logicalTimePoint; - public TimePoint newStartOfTracking; - public TimePoint newEndOfTracking; - public TimePoint currentStartOfTracking; - public TimePoint currentEndOfTracking; + public TimePointSpecificationFoundInLog newStartOfTracking; + public TimePointSpecificationFoundInLog newEndOfTracking; + public TimePointSpecificationFoundInLog currentStartOfTracking; + public TimePointSpecificationFoundInLog currentEndOfTracking; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaWithSeriesAndFleetsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaWithSeriesAndFleetsDialog.java index e3192b400f0..34611a7d115 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaWithSeriesAndFleetsDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaWithSeriesAndFleetsDialog.java @@ -48,7 +48,7 @@ public abstract class RegattaWithSeriesAndFleetsDialog extends AbstractRegattaWi protected RegattaDTO getResult() { RegattaDTO result = getRegattaDTO(); result.setName(nameEntryField.getText().trim()); // trim to particularly avoid trailing blanks - result.boatClass = new BoatClassDTO(boatClassEntryField.getText(), Distance.NULL); + result.boatClass = new BoatClassDTO(boatClassEntryField.getText(), Distance.NULL, Distance.NULL); return result; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SetTrackingTimesDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SetTrackingTimesDialog.java index 2e7948d8812..d65abfbc568 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SetTrackingTimesDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SetTrackingTimesDialog.java @@ -10,6 +10,7 @@ import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.CaptionPanel; +import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.HasVerticalAlignment; import com.google.gwt.user.client.ui.HorizontalPanel; @@ -20,11 +21,11 @@ import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.domain.common.abstractlog.TimePointSpecificationFoundInLog; +import com.sap.sailing.domain.common.abstractlog.TimePointSpecificationFoundInLogImpl; import com.sap.sailing.gwt.ui.client.DataEntryDialogWithBootstrap; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.BetterDateTimeBox; -import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util; import com.sap.sse.common.Util.Pair; import com.sap.sse.common.impl.MillisecondsTimePoint; @@ -44,12 +45,28 @@ public class SetTrackingTimesDialog extends DataEntryDialogWithBootstrap result) { - currentStart = result == null || result.getA() == null ? null : result.getA().getTimePoint(); - currentEnd = result == null || result.getB() == null ? null : result.getB().getTimePoint(); - updateDateTimeLabelAndTimeBoxFromDate(currentStart, currentStartLabel, startTimeBox); - updateDateTimeLabelAndTimeBoxFromDate(currentEnd, currentEndLabel, endTimeBox); + currentStart = result == null ? null : result.getA(); + currentEnd = result == null ? null : result.getB(); + updateDateTimeLabelAndTimeBoxFromDate(currentStart, currentStartLabel, startTimeBox, startTimeSetCheckbox); + updateDateTimeLabelAndTimeBoxFromDate(currentEnd, currentEndLabel, endTimeBox, endTimeSetCheckbox); } }); } - private void updateDateTimeLabelAndTimeBoxFromDate(final TimePoint timePoint, final Label label, final BetterDateTimeBox dateTimeBox) { + private void updateDateTimeLabelAndTimeBoxFromDate(final TimePointSpecificationFoundInLog timePoint, final Label label, final BetterDateTimeBox dateTimeBox, CheckBox setCheckBox) { if (timePoint == null) { label.setText(stringMessages.notAvailable()); dateTimeBox.setValue(null); + setCheckBox.setValue(false); } else { - label.setText(dateTimeFormat.format(timePoint.asDate())); - dateTimeBox.setValue(timePoint.asDate()); + label.setText(timePoint.getTimePoint()==null?"null":dateTimeFormat.format(timePoint.getTimePoint().asDate())); + dateTimeBox.setValue(timePoint.getTimePoint() == null ? null : timePoint.getTimePoint().asDate()); + setCheckBox.setValue(true); } } private Widget createInputPanel() { - Grid content = new Grid(4, 3); + Grid content = new Grid(4, 4); Button startNow = new Button(stringMessages.now()); startNow.addClickHandler(new ClickHandler() { @@ -124,16 +143,22 @@ public class SetTrackingTimesDialog extends DataEntryDialogWithBootstrapstartTimeSetCheckbox.setValue(true)); // when the start time is manipulated, assume the user wants to really set it startTimeBox.setFormat("dd/mm/yyyy hh:ii:ss"); content.setWidget(0, 0, createLabel(stringMessages.startOfTracking())); content.setWidget(0, 1, startTimeBox); - content.setWidget(0, 2, startNow); + startTimeSetCheckbox = createCheckbox(stringMessages.set()); + content.setWidget(0, 2, startTimeSetCheckbox); + content.setWidget(0, 3, startNow); endTimeBox = createDateTimeBox(null); + endTimeBox.addValueChangeHandler(e->endTimeSetCheckbox.setValue(true)); // when the end time is manipulated, assume the user wants to really set it endTimeBox.setFormat("dd/mm/yyyy hh:ii:ss"); content.setWidget(1, 0, createLabel(stringMessages.endOfTracking())); content.setWidget(1, 1, endTimeBox); - content.setWidget(1, 2, endNow); + endTimeSetCheckbox = createCheckbox(stringMessages.set()); + content.setWidget(1, 2, endTimeSetCheckbox); + content.setWidget(1, 3, endNow); authorNameBox = createTextBox("Shore"); content.setWidget(2, 0, createLabel(stringMessages.authorName())); @@ -173,10 +198,8 @@ public class SetTrackingTimesDialog extends DataEntryDialogWithBootstrap { private final StringMessages stringMessages; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SmartphoneTrackingEventManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SmartphoneTrackingEventManagementPanel.java index 5b5b2e12225..cf217320813 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SmartphoneTrackingEventManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SmartphoneTrackingEventManagementPanel.java @@ -745,11 +745,9 @@ public class SmartphoneTrackingEventManagementPanel extends AbstractLeaderboardC private void showSetTrackingTimesDialog(RaceColumnDTO raceColumn, FleetDTO fleet) { new SetTrackingTimesDialog(sailingService, errorReporter, getSelectedLeaderboardName(), raceColumn.getName(), fleet.getName(), stringMessages, new DataEntryDialog.DialogCallback() { - @Override public void ok(RaceLogSetTrackingTimesDTO editedObject) { sailingService.setTrackingTimes(editedObject, new AsyncCallback(){ - @Override public void onFailure(Throwable caught) { errorReporter.reportError("Error while setting tracking times: " + caught.getMessage()); @@ -760,9 +758,7 @@ public class SmartphoneTrackingEventManagementPanel extends AbstractLeaderboardC public void onSuccess(Void result) { refreshTrackingActionButtons(); } - }); - } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/StructureImportManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/StructureImportManagementPanel.java index e9aea45d22e..dcd634590ca 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/StructureImportManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/StructureImportManagementPanel.java @@ -342,7 +342,7 @@ public class StructureImportManagementPanel extends SimplePanel implements Regat */ private RegattaDTO createRegattaDefaults(RegattaDTO regatta) { RegattaDTO result = new RegattaDTO("Default", ScoringSchemeType.LOW_POINT); - result.boatClass = new BoatClassDTO(BoatClassDTO.DEFAULT_NAME, /* hull length */ new MeterDistance(5)); + result.boatClass = new BoatClassDTO(BoatClassDTO.DEFAULT_NAME, /* hull length */ new MeterDistance(5), /* hull beam */ new MeterDistance(1.8)); result.series = regatta.series; result.rankingMetricType = RankingMetrics.ONE_DESIGN; return result; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingReplayConnectorPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingReplayConnectorPanel.java index afcb3c08683..0a1e5326b1c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingReplayConnectorPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingReplayConnectorPanel.java @@ -196,7 +196,7 @@ public class SwissTimingReplayConnectorPanel extends AbstractEventManagementPane @Override public List getSearchableStrings(SwissTimingReplayRaceDTO t) { List strings = new ArrayList(); - strings.addAll(Arrays.asList(t.boat_class, t.flight_number, t.getName(), t.race_id, t.rsc)); + strings.addAll(Arrays.asList(t.boat_class, t.flight_number, t.getName(), t.rsc)); return strings; } }; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TracTracEventManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TracTracEventManagementPanel.java index 7b921543829..1e12553c092 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TracTracEventManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TracTracEventManagementPanel.java @@ -455,12 +455,10 @@ public class TracTracEventManagementPanel extends AbstractEventManagementPanel { private String getBoatClassNamesAsString(TracTracRaceRecordDTO object) { StringBuilder boatClassNames = new StringBuilder(); - for (String boatClassName : object.boatClassNames) { boatClassNames.append(boatClassName); boatClassNames.append(", "); } - return boatClassNames.substring(0, boatClassNames.length() - 2); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java index 6b38f957489..7f32068f28e 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java @@ -80,7 +80,6 @@ import com.sap.sailing.gwt.ui.shared.RegattaLogDTO; import com.sap.sailing.gwt.ui.shared.RegattaOverviewEntryDTO; import com.sap.sailing.gwt.ui.shared.RegattaScoreCorrectionDTO; import com.sap.sailing.gwt.ui.shared.RemoteSailingServerReferenceDTO; -import com.sap.sailing.gwt.ui.shared.ReplicationStateDTO; import com.sap.sailing.gwt.ui.shared.ScoreCorrectionProviderDTO; import com.sap.sailing.gwt.ui.shared.ServerConfigurationDTO; import com.sap.sailing.gwt.ui.shared.SimulatorResultsDTO; @@ -109,12 +108,13 @@ import com.sap.sse.gwt.client.ServerInfoDTO; import com.sap.sse.gwt.client.filestorage.FileStorageManagementGwtService; import com.sap.sse.gwt.client.media.ImageDTO; import com.sap.sse.gwt.client.media.VideoDTO; +import com.sap.sse.gwt.client.replication.RemoteReplicationService; /** * The client side stub for the RPC service. Usually, when a null date is passed to * the time-dependent service methods, an empty (non-null) result is returned. */ -public interface SailingService extends RemoteService, FileStorageManagementGwtService { +public interface SailingService extends RemoteService, FileStorageManagementGwtService, RemoteReplicationService { List getPreviousTracTracConfigurations() throws Exception; List getRegattas(); @@ -139,8 +139,6 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS void storeTracTracConfiguration(String name, String jsonURL, String liveDataURI, String storedDataURI, String courseDesignUpdateURI, String tracTracUsername, String tracTracPassword) throws Exception; - void stopTrackingEvent(RegattaIdentifier eventIdentifier) throws Exception; - void stopTrackingRaces(Iterable racesToStopTracking) throws Exception; void removeAndUntrackRaces(Iterable regattaNamesAndRaceNames); @@ -310,10 +308,6 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS void setWindSourcesToExclude(RegattaAndRaceIdentifier raceIdentifier, Iterable windSourcesToExclude); - ReplicationStateDTO getReplicaInfo(); - - void startReplicatingFromMaster(String messagingHost, String masterHost, String exchangeName, int servletPort, int messagingPort) throws Exception; - void updateRaceDelayToLive(RegattaAndRaceIdentifier regattaAndRaceIdentifier, long delayToLiveInMs); void updateRacesDelayToLive(List regattaAndRaceIdentifiers, long delayToLiveInMs); @@ -423,12 +417,6 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS boolean showOnlyCurrentlyRunningRaces, boolean showOnlyRacesOfSameDay, Duration clientTimeZoneOffset, List visibleRegattas) throws Exception; - void stopReplicatingFromMaster(); - - void stopAllReplicas(); - - void stopSingleReplicaInstance(String identifier); - void reloadRaceLog(String leaderboardName, RaceColumnDTO raceColumnDTO, FleetDTO fleet); RaceLogDTO getRaceLog(String leaderboardName, RaceColumnDTO raceColumnDTO, FleetDTO fleet); @@ -465,7 +453,7 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS Iterable getAllIgtimiAccountEmailAddresses(); - String getIgtimiAuthorizationUrl(); + String getIgtimiAuthorizationUrl(String redirectProtocol, String redirectHostname, String redirectPort); boolean authorizeAccessToIgtimiUser(String eMailAddress, String password) throws Exception; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java index c374d258320..52642309e93 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java @@ -69,7 +69,6 @@ import com.sap.sailing.gwt.ui.shared.RegattaLogDTO; import com.sap.sailing.gwt.ui.shared.RegattaOverviewEntryDTO; import com.sap.sailing.gwt.ui.shared.RegattaScoreCorrectionDTO; import com.sap.sailing.gwt.ui.shared.RemoteSailingServerReferenceDTO; -import com.sap.sailing.gwt.ui.shared.ReplicationStateDTO; import com.sap.sailing.gwt.ui.shared.SailingServiceConstants; import com.sap.sailing.gwt.ui.shared.ScoreCorrectionProviderDTO; import com.sap.sailing.gwt.ui.shared.ServerConfigurationDTO; @@ -97,11 +96,12 @@ import com.sap.sse.gwt.client.ServerInfoRetriever; import com.sap.sse.gwt.client.filestorage.FileStorageManagementGwtServiceAsync; import com.sap.sse.gwt.client.media.ImageDTO; import com.sap.sse.gwt.client.media.VideoDTO; +import com.sap.sse.gwt.client.replication.RemoteReplicationServiceAsync; /** * The async counterpart of {@link SailingService} */ -public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageManagementGwtServiceAsync { +public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageManagementGwtServiceAsync, RemoteReplicationServiceAsync { void getRegattas(AsyncCallback> callback); @@ -117,19 +117,22 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan /** * @param regattaToAddTo - * if null, an existing regatta by the name of the TracTrac event with the boat class name - * appended in parentheses will be looked up; if not found, a default regatta with that name will be - * created, with a single default series and a single default fleet. If a valid {@link RegattaIdentifier} - * is specified, a regatta lookup is performed with that identifier; if the regatta is found, it is used - * to add the races to. Otherwise, a default regatta as described above will be created and used. + * if null, the regatta into which the race has previously been loaded will be looked up; if + * found, the race will be loaded into that regatta; otherwise, an existing regatta by the name of the + * TracTrac event with the boat class name appended in parentheses will be looked up; if not found, a + * default regatta with that name will be created, with a single default series and a single default + * fleet. If a valid {@link RegattaIdentifier} is specified, a regatta lookup is performed with that + * identifier; if the regatta is found, it is used to add the races to. Otherwise, a default regatta as + * described above will be created and used. * @param liveURI * may be null or the empty string in which case the server will use the * {@link TracTracRaceRecordDTO#liveURI} from the rr race record. * @param offsetToStartTimeOfSimulatedRace - * if not null, the connector will adjust the time stamps of all events received such that the - * first mark passing for the first waypoint will be set to "now." It will delay the forwarding of all - * events received such that they seem to be sent in "real-time" plus the offsetToStartTimeOfSimulatedRace. So, more or less the time points - * attached to the events sent to the receivers will again approximate the wall time. + * if not null, the connector will adjust the time stamps of all events received such that + * the first mark passing for the first waypoint will be set to "now." It will delay the forwarding of + * all events received such that they seem to be sent in "real-time" plus the + * offsetToStartTimeOfSimulatedRace. So, more or less the time points attached to the events + * sent to the receivers will again approximate the wall time. * @param useInternalMarkPassingAlgorithm * whether or not to ignore the TracTrac-provided mark passings; if true, a separate mark * passing calculator is used, and the TracTrac-provided ones are ignored. @@ -155,8 +158,6 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan void storeTracTracConfiguration(String name, String jsonURL, String liveDataURI, String storedDataURI, String courseDesignUpdateURI, String tracTracUsername, String tracTracPassword, AsyncCallback callback); - void stopTrackingEvent(RegattaIdentifier eventIdentifier, AsyncCallback callback); - void stopTrackingRaces(Iterable racesToStopTracking, AsyncCallback asyncCallback); /** @@ -388,11 +389,6 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan Map toPerCompetitorIdAsString, boolean extrapolate, AsyncCallback callback); - void getReplicaInfo(AsyncCallback callback); - - void startReplicatingFromMaster(String messagingHost, String masterName, String exchangeName, int servletPort, - int messagingPort, AsyncCallback callback); - void getEvents(AsyncCallback> callback); void getPublicEventsOfAllSailingServers(AsyncCallback> callback); @@ -557,8 +553,6 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan void checkLeaderboardName(String leaderboardName, AsyncCallback> callback); - void stopReplicatingFromMaster(AsyncCallback asyncCallback); - void getRegattaStructureForEvent(UUID eventId, AsyncCallback> asyncCallback); void getRegattaStructureOfEvent(UUID eventId, AsyncCallback> callback); @@ -571,10 +565,6 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan boolean showOnlyRacesOfSameDay, Duration clientTimeZoneOffset, List visibleRegattas, AsyncCallback> callback); - void stopAllReplicas(AsyncCallback asyncCallback); - - void stopSingleReplicaInstance(String identifier, AsyncCallback asyncCallback); - void reloadRaceLog(String leaderboardName, RaceColumnDTO raceColumnDTO, FleetDTO fleet, AsyncCallback asyncCallback); @@ -630,7 +620,7 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan void getAllIgtimiAccountEmailAddresses(AsyncCallback> callback); - void getIgtimiAuthorizationUrl(AsyncCallback callback); + void getIgtimiAuthorizationUrl(String redirectProtocol, String redirectHostname, String redirectPort, AsyncCallback callback); void authorizeAccessToIgtimiUser(String eMailAddress, String password, AsyncCallback callback); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index 0263951e5d8..ded278b17e6 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -334,7 +334,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String messagingPortNumber(); String servletPortNumber(); String registeredAt(String string); - String replicatingFromMaster(String hostname, int jmsPort, int servletPort, String messagingHostname, String exchangeName); + String replicatingFromMaster(String hostname, int jmsPort, int servletPort, String messagingHostname, String exchangeName, String replicableIdsAsString); String showWindSpeedSeries(); String showWindDirectionSeries(); String fleet(); @@ -1761,4 +1761,6 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String couldNotSaveCompetitorRegistrations(String message); String searchTagForImportedCompetitorPlaceholder(); String version(String version); + String replicables(); + String set(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index a92cd8718a1..f999351676d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -346,7 +346,7 @@ courseMiddleLine=Course middle line messagingPortNumber=Messaging port number (0 for default) servletPortNumber=Servlet port number registeredAt=Registered at {0} -replicatingFromMaster=Replicating from master {0}:{2}, messaging port {3}:{1}, exchange {4} +replicatingFromMaster=Replicating from master {0}:{2}, messaging port {3}:{1}, exchange {4}, replicating {5} showWindSpeedSeries=Show wind speed data showWindDirectionSeries=Show wind directions data fleet=Fleet @@ -1522,7 +1522,7 @@ playstoreCampaign=utm_source=sapsailing&utm_medium=solutions&utm_content=2016.02 playstoreInsightApp=https://play.google.com/store/apps/details?id=com.sap.sailing.android.tracking.app playstoreRacecommitteeApp=https://play.google.com/store/apps/details?id=com.sap.sailing.racecommittee.app playStoreBuoyPingerApp=https://play.google.com/store/apps/details?id=com.sap.sailing.android.buoy.positioning.app -playstoreBadge=https://play.google.com/intl/en/badges/images/generic/en-play-badge.png +playstoreBadge=https://play.google.com/intl/en/badges/images/badge_new.png appstoreSapSailInsight=http://appstore.com/sapsailinsight appstoreBadgeSuffix=_en distanceFromAveragePosition=Distance from average position @@ -1561,6 +1561,7 @@ importInit=Initializing Import importWait=Waiting for other data import operations to finish importLeaderboardGroups=Importing leaderboard groups importWindTracks=Importing wind tracks +importSensorFixes=Importing sensor fixes updateEventLeaderboardGroupLinks=Updating Event-LeaderboardGroup links strategySimulatorTitle=Strategy Simulator controlSettings=Control settings @@ -1660,7 +1661,7 @@ seriesHeaderUpcoming=Upcoming eventRegattaLeaderboardLastScoreUpdate=Last Score Update eventRegattaRaceNotTracked=Not Tracked footerJobs=Jobs -footerCopyright=\u00A9 2011-2016 SAP Sailing Analytics +footerCopyright=\u00A9 2011-2017 SAP Sailing Analytics footerLanguage=SAP Sailing in footerLegal=Legal Disclosure footerPrivacy=Privacy @@ -1732,4 +1733,6 @@ errorLoadingCompetitorImportProviders=Error loading competitor import providers: errorLoadingCompetitorImportDescriptors=Error loading competitor import descriptors: {0} couldNotSaveCompetitorRegistrations=Could not save competitor registrations: {0} searchTagForImportedCompetitorPlaceholder=Use, e.g., the race+fleet name -version=Version: {0} \ No newline at end of file +version=Version: {0} +replicables=Replicating objects +set=Set \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index af8fc13a40d..306298386de 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -1,3 +1,4 @@ + manage2Sail=Manage2Sail raceCommitteeApp=Race Manager App raceViewer=Race viewer @@ -348,7 +349,7 @@ courseMiddleLine=Kursmittellinie messagingPortNumber=Messaging-Portnummer (0 für Default) servletPortNumber=Servlet Portnummer registeredAt=Registriert {0} -replicatingFromMaster=Repliziert von master {0}:{2}, Messaging Port {3}:{1}, Exchange {4} +replicatingFromMaster=Repliziert von master {0}:{2}, Messaging Port {3}:{1}, Exchange {4}, replizierte Objekte {5} showWindSpeedSeries=Daten für Windgeschwindigkeit zeigen showWindDirectionSeries=Daten für Windrichtung zeigen fleet=Flotte @@ -1501,7 +1502,7 @@ raceManagerDeviceName=Gerätename serverURLInvalid=Bitte gültige Server-URL eingeben serverURLEmpty=Server URL darf nicht leer sein marks=Bahnmarken -playstoreBadge=https://play.google.com/intl/de-de/badges/images/generic/de-play-badge.png +playstoreBadge=https://play.google.com/intl/de-de/badges/images/badge_new.png appstoreBadgeSuffix=_de addNewFix=Neuen Fix hinzufügen pleaseSelectOtherTimepoint=Bitte wählen Sie einen anderen Zeitpunkt. Am gewählten Zeitpunkt ist bereits ein Fix. @@ -1546,6 +1547,7 @@ importInit=Import wird initialisiert importWait=Warten, bis andere Datenimport-Vorgänge angeschlossen sind importLeaderboardGroups=Ranglisten-Gruppen werden importiert importWindTracks=Windmessungen werden importiert +importSensorFixes=Sensor-Fixes werden importiert updateEventLeaderboardGroupLinks=Verbindungen zwischen Veranstaltungen und Ranglisten-Gruppen werden aktualisiert strategySimulatorTitle=Strategie-Simulator controlSettings=Einstellungen @@ -1645,7 +1647,7 @@ seriesHeaderUpcoming=Demnächst eventRegattaLeaderboardLastScoreUpdate=Letztes Ergebnisupdate eventRegattaRaceNotTracked=Nicht getrackt footerJobs=Jobs -footerCopyright=\u00A9 2011-2016 SAP Sailing Analytics +footerCopyright=\u00A9 2011-2017 SAP Sailing Analytics footerLanguage=SAP Sailing in footerLegal=Impressum footerPrivacy=Datenschutz @@ -1717,4 +1719,6 @@ errorLoadingCompetitorImportProviders=Fehler beim Laden der Quellen für den Tei errorLoadingCompetitorImportDescriptors=Fehler beim Laden der zu importierenden Teilnehmerdaten: {0} couldNotSaveCompetitorRegistrations=Teilnehmerregistrierung konnte nicht gespeichert werden: {0} searchTagForImportedCompetitorPlaceholder=Z.B. Renn- und Gruppenname -version=Version: {0} \ No newline at end of file +version=Version: {0} +replicables=Replizierte Objekte +set=Setzen \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties index 84da7fb3a96..ecba3522c6d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties @@ -344,7 +344,7 @@ courseMiddleLine=コース中央航路 messagingPortNumber=メッセージングポート番号 (初期値は 0) servletPortNumber=サーブレットポート番号 registeredAt=登録時刻 {0} -replicatingFromMaster=マスタ {0}:{2}、メッセージングポート {3}:{1}、エクスチェンジ {4} から複製 +replicatingFromMaster=マスタ {0}:{2}、メッセージングポート {3}:{1}、エクスチェンジ {4} から複製、{5} を複製 showWindSpeedSeries=風速データ表示 showWindDirectionSeries=風向データ表示 fleet=フリート @@ -649,8 +649,8 @@ distanceToLineFiveSecondsBeforeStart=スタート 5 秒前のラインまでの speedOverGroundAtRaceStart=スタート時の SOG speedOverGroundFiveSecondsBeforeStart=スタート 5 秒前の SOG speedOverGroundWhenPassingStart=スタート時の SOG -distanceToLineAtRaceStartTooltip=レーススタート時刻のスタートラインまでの距離 -distanceToLineFiveSecondsBeforeStartTooltip=スタート 5 秒前のスタートラインまでの距離 +distanceToLineAtRaceStartTooltip=レーススタート時刻のスタートラインまでの距離 (競技者がスタートラインの各エンドより外側に位置している場合は近い方のエンドまでの距離) +distanceToLineFiveSecondsBeforeStartTooltip=スタート 5 秒前のスタートラインまでの距離 (競技者がスタートラインの各エンドより外側に位置している場合は近い方のエンドまでの距離) speedOverGroundAtRaceStartTooltip=レーススタート時刻の対地速力 speedOverGroundFiveSecondsBeforeStartTooltip=スタート 5 秒前の対地速力 speedOverGroundWhenPassingStartTooltip=競技者がスタートを通過したときの対地速力 @@ -1520,7 +1520,7 @@ playstoreCampaign=utm_source=sapsailing&utm_medium=solutions&utm_content=2016.02 playstoreInsightApp=https://play.google.com/store/apps/details?id=com.sap.sailing.android.tracking.app playstoreRacecommitteeApp=https://play.google.com/store/apps/details?id=com.sap.sailing.racecommittee.app playStoreBuoyPingerApp=https://play.google.com/store/apps/details?id=com.sap.sailing.android.buoy.positioning.app -playstoreBadge=https://play.google.com/intl/en/badges/images/generic/en-play-badge.png +playstoreBadge=https://play.google.com/intl/en/badges/images/badge_new.png appstoreSapSailInsight=http://appstore.com/sapsailinsight appstoreBadgeSuffix=_en distanceFromAveragePosition=平均位置からの距離 @@ -1535,6 +1535,7 @@ dragToChangePosition=ドラッグして位置を変更\n右クリックして削 moveFix=フィックスを移動 manage2SailEvent=Manage2Sail イベント noEventsYet=イベントがまだありません。 +selectCompetitorImportProvider=競技者インポートプロバイダを選択してください... selectResultImportProvider=結果インポートプロバイダを選択してください... buildVersion=ビルドバージョン serverInformation=サーバ情報 @@ -1657,7 +1658,7 @@ seriesHeaderUpcoming=予定 eventRegattaLeaderboardLastScoreUpdate=前回スコア更新 eventRegattaRaceNotTracked=未追跡 footerJobs=採用 -footerCopyright=© 2011-2016 SAP Sailing Analytics +footerCopyright=© 2011-2017 SAP Sailing Analytics footerLanguage=SAP Sailing - footerLegal=利用規約 footerPrivacy=個人情報保護 @@ -1719,3 +1720,15 @@ filterDeviceMappings=デバイスマッピングのフィルタリング raceStartAnalysis=レーススタート分析 buoyZoneRadiusInHullLengths=艇身単位のゾーン半径 (例: 2 または3) incorrectValueForRegattaBuoyZoneRadiusInHullLengths=艇身単位のゾーン半径の値が不適切 +importCompetitors=競技者のインポート +chooseWhichCompetitorsShouldBeImported=インポートする競技者を左側で選択します。単一の競技者を選択しても、同一の対象を表す既存の競技者が複数あればそれらが右側に表示されます。既存の競技者を使用するには、その競技者を選択します。マッピングが終了したら、テーブルにインポートするすべての競技者を左側で選択して、OK を押します。テーブルは、列ヘッダをクリックするかタップしてソートが可能であることに注意してください。 +existingCompetitors=既存の競技者 +importedCompetitors=インポートする競技者 +hasMatches=一致あり +filterImportedCompetitorsByNameSailRaceFleet=インポートする競技者を名前、セールナンバー、レース、またはフリートでフィルタリング +errorLoadingCompetitorImportProviders=競技者インポートプロバイダのロードでエラーが発生: {0} +errorLoadingCompetitorImportDescriptors=競技者インポート記述子のロードでエラーが発生: {0} +couldNotSaveCompetitorRegistrations=競技者登録を保存できませんでした: {0} +searchTagForImportedCompetitorPlaceholder=たとえば、レース+フリート名を使用 +version=バージョン: {0} +replicables=オブジェクトの複製 diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties index 5f738fa9b77..16a989ba604 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -344,7 +344,7 @@ courseMiddleLine=Средняя линия курса messagingPortNumber=Номер порта обмена сообщениями (0 по умолчанию) servletPortNumber=Номер порта сервлета registeredAt=Зарегистрировано в {0} -replicatingFromMaster=Репликация с главного экземпляра {0}:{2}, порт обмена сообщениями {3}:{1}, Exchange {4} +replicatingFromMaster=Репликация с главного экземпляра {0}:{2}, порт обмена сообщениями {3}:{1}, Exchange {4}, репликация {5} showWindSpeedSeries=Показать данные о скорости ветра showWindDirectionSeries=Показать данные о направлениях ветра fleet=Флот @@ -649,8 +649,8 @@ distanceToLineFiveSecondsBeforeStart=Дистанция до линии за 5 speedOverGroundAtRaceStart=SOG при старте speedOverGroundFiveSecondsBeforeStart=SOG за 5 с до старта speedOverGroundWhenPassingStart=SOG при старте -distanceToLineAtRaceStartTooltip=Дистанция до линии старта во время старта гонки -distanceToLineFiveSecondsBeforeStartTooltip=Дистанция до линии старта за пять секунд до старта +distanceToLineAtRaceStartTooltip=Дистанция до линии старта во время старта гонки; в случае проецирования участника за концы линии — дистанция до ближайшего конца линии +distanceToLineFiveSecondsBeforeStartTooltip=Дистанция до линии старта за пять секунд до старта гонки; в случае проецирования участника за концы линии — дистанция до ближайшего конца линии speedOverGroundAtRaceStartTooltip=Скорость относительно грунта во время старта гонки speedOverGroundFiveSecondsBeforeStartTooltip=Скорость относительно грунта за пять секунд до старта speedOverGroundWhenPassingStartTooltip=Скорость относительно грунта при прохождении старта участником @@ -1520,7 +1520,7 @@ playstoreCampaign=utm_source=sapsailing&utm_medium=solutions&utm_content=2016.02 playstoreInsightApp=https://play.google.com/store/apps/details?id=com.sap.sailing.android.tracking.app playstoreRacecommitteeApp=https://play.google.com/store/apps/details?id=com.sap.sailing.racecommittee.app playStoreBuoyPingerApp=https://play.google.com/store/apps/details?id=com.sap.sailing.android.buoy.positioning.app -playstoreBadge=https://play.google.com/intl/en/badges/images/generic/en-play-badge.png +playstoreBadge=https://play.google.com/intl/en/badges/images/badge_new.png appstoreSapSailInsight=http://appstore.com/sapsailinsight appstoreBadgeSuffix=_en distanceFromAveragePosition=Дистанция от средней позиции @@ -1535,6 +1535,7 @@ dragToChangePosition=Позиция изменяется перетаскива moveFix=Переместить замер manage2SailEvent=Событие Manage2Sail noEventsYet=Событий еще нет. +selectCompetitorImportProvider=Выберите поставщика импорта участников... selectResultImportProvider=Выберите поставщика импорта результатов... buildVersion=Версия сборки serverInformation=Информация о сервере @@ -1657,7 +1658,7 @@ seriesHeaderUpcoming=Скоро eventRegattaLeaderboardLastScoreUpdate=Последнее обновление оценок eventRegattaRaceNotTracked=Не отслеживается footerJobs=Работа -footerCopyright=© SAP Sailing Analytics, 2011–2016. +footerCopyright=© SAP Sailing Analytics, 2011-2017. footerLanguage=Язык SAP Sailing footerLegal=Раскрытие юридической информации footerPrivacy=Конфиденциальность @@ -1719,3 +1720,15 @@ filterDeviceMappings=Фильтровать сопоставления устр raceStartAnalysis=Анализ старта гонки buoyZoneRadiusInHullLengths=Радиус зоны буя в длинах корпуса (например, 2 или 3) incorrectValueForRegattaBuoyZoneRadiusInHullLengths=Неверное значение радиуса зоны буя в длинах корпуса +importCompetitors=Импорт участников +chooseWhichCompetitorsShouldBeImported=Слева выберите участников для импорта. При выборе одного участника справа отображаются все существующие участники, которые могут представлять одну и ту же организацию. Чтобы использовать существующего участника, выберите его. По завершении сопоставления выберите всех участников, которых требуется импортировать, в таблице слева и нажмите кнопку "ОК". Таблица можно сортировать щелчком или касанием заголовков столбцов. +existingCompetitors=Существующие участники +importedCompetitors=Участники для импорта +hasMatches=Есть соответствия +filterImportedCompetitorsByNameSailRaceFleet=Отфильтруйте участников для импорта по названию, номеру на парусе, гонке или флоту +errorLoadingCompetitorImportProviders=Ошибка при загрузке поставщиков импорта участников: {0} +errorLoadingCompetitorImportDescriptors=Ошибка при загрузке дескрипторов импорта участников: {0} +couldNotSaveCompetitorRegistrations=Не удалось сохранить регистрации участников: {0} +searchTagForImportedCompetitorPlaceholder=Используйте, например, гонку + название флота +version=Версия: {0} +replicables=Репликация объектов diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties index f2517d912c2..42bc7fe8fd1 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties @@ -344,7 +344,7 @@ courseMiddleLine=场地中线 messagingPortNumber=消息传送端口编号(默认为 0) servletPortNumber=Servlet 端口编号 registeredAt=注册日期{0} -replicatingFromMaster=正在从主服务器{0}:{2}、消息传送端口{3}:{1}、交换机{4}复制 +replicatingFromMaster=正在从主服务器{0}:{2}、消息传送端口{3}:{1}、交换机{4}复制,正在复制{5} showWindSpeedSeries=显示风速数据 showWindDirectionSeries=显示风向数据 fleet=船队 @@ -649,8 +649,8 @@ distanceToLineFiveSecondsBeforeStart=起航前 5 秒时与起航线的距离 speedOverGroundAtRaceStart=起航时的实际速度 speedOverGroundFiveSecondsBeforeStart=起航前 5 秒时的实际速度 speedOverGroundWhenPassingStart=起航时的实际速度 -distanceToLineAtRaceStartTooltip=比赛轮次开始时离开起航线的距离 -distanceToLineFiveSecondsBeforeStartTooltip=起航前五秒时离开起航线的距离 +distanceToLineAtRaceStartTooltip=比赛轮次开始时离开起航线的距离;参赛队突破航线终点时与最近航线终点的距离 +distanceToLineFiveSecondsBeforeStartTooltip=起航前五秒时离开起航线的距离;参赛队突破航线终点时与最近航线终点的距离 speedOverGroundAtRaceStartTooltip=比赛轮次开始时的实际速度 speedOverGroundFiveSecondsBeforeStartTooltip=起航前五秒时的实际速度 speedOverGroundWhenPassingStartTooltip=参赛队越过起航线时的实际速度 @@ -1520,7 +1520,7 @@ playstoreCampaign=utm_source=sapsailing&utm_medium=solutions&utm_content=2016.02 playstoreInsightApp=https://play.google.com/store/apps/details?id=com.sap.sailing.android.tracking.app playstoreRacecommitteeApp=https://play.google.com/store/apps/details?id=com.sap.sailing.racecommittee.app playStoreBuoyPingerApp=https://play.google.com/store/apps/details?id=com.sap.sailing.android.buoy.positioning.app -playstoreBadge=https://play.google.com/intl/en/badges/images/generic/en-play-badge.png +playstoreBadge=https://play.google.com/intl/en/badges/images/badge_new.png appstoreSapSailInsight=http://appstore.com/sapsailinsight appstoreBadgeSuffix=_en distanceFromAveragePosition=与平均位置的距离 @@ -1535,6 +1535,7 @@ dragToChangePosition=拖动更改位置\n右键点击获取菜单以删除 moveFix=移动修复 manage2SailEvent=Manage2Sail 活动 noEventsYet=暂无活动。 +selectCompetitorImportProvider=请选择参赛队导入提供方... selectResultImportProvider=请选择结果导入提供方... buildVersion=内部版本 serverInformation=服务器信息 @@ -1657,7 +1658,7 @@ seriesHeaderUpcoming=即将到来 eventRegattaLeaderboardLastScoreUpdate=上次得分更新 eventRegattaRaceNotTracked=未跟踪 footerJobs=招贤纳士 -footerCopyright=版权所有 © 2011-2016 SAP Sailing Analytics +footerCopyright=版权所有 © 2011-2017 SAP Sailing Analytics footerLanguage=SAP Sailing 语言 footerLegal=法律声明 footerPrivacy=隐私 @@ -1719,3 +1720,15 @@ filterDeviceMappings=筛选设备映射 raceStartAnalysis=比赛轮次开始分析 buoyZoneRadiusInHullLengths=船体长度表示的浮标区半径(例如 2 或 3) incorrectValueForRegattaBuoyZoneRadiusInHullLengths=船体长度表示的浮标区半径的值不正确 +importCompetitors=导入参赛队 +chooseWhichCompetitorsShouldBeImported=在左侧选择待导入的参赛队。选择单个参赛队会在右侧显示代表相同实体的任何现有参赛队。要使用现有参赛队,请选择。完成映射时,在左侧的表中选择待导入的所有参赛队,并按“确定”。请注意,点击或点按列标题即可对表进行排序。 +existingCompetitors=现有参赛队 +importedCompetitors=待导入的参赛队 +hasMatches=有比赛 +filterImportedCompetitorsByNameSailRaceFleet=按名称、帆号、比赛轮次或船队筛选待导入的参赛队 +errorLoadingCompetitorImportProviders=加载参赛队导入提供方时出错:{0} +errorLoadingCompetitorImportDescriptors=加载参赛队导入描述符时出错:{0} +couldNotSaveCompetitorRegistrations=无法保存参赛队注册:{0} +searchTagForImportedCompetitorPlaceholder=例如,使用比赛轮次+船队名称 +version=版本:{0} +replicables=正在复制对象 diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh_CN.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh_CN.properties index f2517d912c2..42bc7fe8fd1 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh_CN.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh_CN.properties @@ -344,7 +344,7 @@ courseMiddleLine=场地中线 messagingPortNumber=消息传送端口编号(默认为 0) servletPortNumber=Servlet 端口编号 registeredAt=注册日期{0} -replicatingFromMaster=正在从主服务器{0}:{2}、消息传送端口{3}:{1}、交换机{4}复制 +replicatingFromMaster=正在从主服务器{0}:{2}、消息传送端口{3}:{1}、交换机{4}复制,正在复制{5} showWindSpeedSeries=显示风速数据 showWindDirectionSeries=显示风向数据 fleet=船队 @@ -649,8 +649,8 @@ distanceToLineFiveSecondsBeforeStart=起航前 5 秒时与起航线的距离 speedOverGroundAtRaceStart=起航时的实际速度 speedOverGroundFiveSecondsBeforeStart=起航前 5 秒时的实际速度 speedOverGroundWhenPassingStart=起航时的实际速度 -distanceToLineAtRaceStartTooltip=比赛轮次开始时离开起航线的距离 -distanceToLineFiveSecondsBeforeStartTooltip=起航前五秒时离开起航线的距离 +distanceToLineAtRaceStartTooltip=比赛轮次开始时离开起航线的距离;参赛队突破航线终点时与最近航线终点的距离 +distanceToLineFiveSecondsBeforeStartTooltip=起航前五秒时离开起航线的距离;参赛队突破航线终点时与最近航线终点的距离 speedOverGroundAtRaceStartTooltip=比赛轮次开始时的实际速度 speedOverGroundFiveSecondsBeforeStartTooltip=起航前五秒时的实际速度 speedOverGroundWhenPassingStartTooltip=参赛队越过起航线时的实际速度 @@ -1520,7 +1520,7 @@ playstoreCampaign=utm_source=sapsailing&utm_medium=solutions&utm_content=2016.02 playstoreInsightApp=https://play.google.com/store/apps/details?id=com.sap.sailing.android.tracking.app playstoreRacecommitteeApp=https://play.google.com/store/apps/details?id=com.sap.sailing.racecommittee.app playStoreBuoyPingerApp=https://play.google.com/store/apps/details?id=com.sap.sailing.android.buoy.positioning.app -playstoreBadge=https://play.google.com/intl/en/badges/images/generic/en-play-badge.png +playstoreBadge=https://play.google.com/intl/en/badges/images/badge_new.png appstoreSapSailInsight=http://appstore.com/sapsailinsight appstoreBadgeSuffix=_en distanceFromAveragePosition=与平均位置的距离 @@ -1535,6 +1535,7 @@ dragToChangePosition=拖动更改位置\n右键点击获取菜单以删除 moveFix=移动修复 manage2SailEvent=Manage2Sail 活动 noEventsYet=暂无活动。 +selectCompetitorImportProvider=请选择参赛队导入提供方... selectResultImportProvider=请选择结果导入提供方... buildVersion=内部版本 serverInformation=服务器信息 @@ -1657,7 +1658,7 @@ seriesHeaderUpcoming=即将到来 eventRegattaLeaderboardLastScoreUpdate=上次得分更新 eventRegattaRaceNotTracked=未跟踪 footerJobs=招贤纳士 -footerCopyright=版权所有 © 2011-2016 SAP Sailing Analytics +footerCopyright=版权所有 © 2011-2017 SAP Sailing Analytics footerLanguage=SAP Sailing 语言 footerLegal=法律声明 footerPrivacy=隐私 @@ -1719,3 +1720,15 @@ filterDeviceMappings=筛选设备映射 raceStartAnalysis=比赛轮次开始分析 buoyZoneRadiusInHullLengths=船体长度表示的浮标区半径(例如 2 或 3) incorrectValueForRegattaBuoyZoneRadiusInHullLengths=船体长度表示的浮标区半径的值不正确 +importCompetitors=导入参赛队 +chooseWhichCompetitorsShouldBeImported=在左侧选择待导入的参赛队。选择单个参赛队会在右侧显示代表相同实体的任何现有参赛队。要使用现有参赛队,请选择。完成映射时,在左侧的表中选择待导入的所有参赛队,并按“确定”。请注意,点击或点按列标题即可对表进行排序。 +existingCompetitors=现有参赛队 +importedCompetitors=待导入的参赛队 +hasMatches=有比赛 +filterImportedCompetitorsByNameSailRaceFleet=按名称、帆号、比赛轮次或船队筛选待导入的参赛队 +errorLoadingCompetitorImportProviders=加载参赛队导入提供方时出错:{0} +errorLoadingCompetitorImportDescriptors=加载参赛队导入描述符时出错:{0} +couldNotSaveCompetitorRegistrations=无法保存参赛队注册:{0} +searchTagForImportedCompetitorPlaceholder=例如,使用比赛轮次+船队名称 +version=版本:{0} +replicables=正在复制对象 diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatOverlay.java index 37471e031d8..b15e2eb9be6 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatOverlay.java @@ -7,7 +7,7 @@ import com.google.gwt.maps.client.MapWidget; import com.google.gwt.maps.client.base.LatLng; import com.google.gwt.maps.client.base.Point; import com.google.gwt.maps.client.base.Size; -import com.sap.sailing.domain.common.BoatClassMasterdata; +import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.LegType; import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.common.dto.BoatClassDTO; @@ -149,43 +149,38 @@ public class BoatOverlay extends CanvasOverlayV3 { } public Util.Pair getBoatScaleAndSize(BoatClassDTO boatClass) { - BoatClassMasterdata boatClassMasterdata = BoatClassMasterdata.resolveBoatClass(boatClass.getName()); - Size boatSizeInPixel = getCorrelatedBoatSize(boatClassMasterdata); - + Size boatSizeInPixel = getCorrelatedBoatSize(boatClass.getHullLength(), boatClass.getHullBeam()); double boatHullScaleFactor = boatSizeInPixel.getWidth() / (boatVectorGraphics.getHullLengthInPx()); double boatBeamScaleFactor = boatSizeInPixel.getHeight() / (boatVectorGraphics.getBeamInPx()); - // as the canvas contains the whole boat the canvas size relates to the overall length, not the hull length double scaledWidthSize = (boatVectorGraphics.getOverallLengthInPx()) * boatHullScaleFactor; double scaledBeamSize = (boatVectorGraphics.getOverallLengthInPx()) * boatBeamScaleFactor; - return new Util.Pair(Size.newInstance(boatHullScaleFactor, boatBeamScaleFactor), Size.newInstance(scaledWidthSize + scaledWidthSize / 2.0, scaledBeamSize + scaledBeamSize / 2.0)); } - private Size getCorrelatedBoatSize(BoatClassMasterdata boatClassMasterdata) { - Size boatSizeInPixel = calculateBoundingBox(mapProjection, boatFix.position, - boatClassMasterdata.getHullLength(), boatClassMasterdata.getHullBeam()); - changeBoatSizeIfTooShortHull(boatSizeInPixel, boatClassMasterdata); - changeBoatSizeIfTooNarrowBeam(boatSizeInPixel, boatClassMasterdata); + private Size getCorrelatedBoatSize(Distance hullLength, Distance hullBeam) { + Size boatSizeInPixel = calculateBoundingBox(mapProjection, boatFix.position, hullLength, hullBeam); + changeBoatSizeIfTooShortHull(boatSizeInPixel, hullLength, hullBeam); + changeBoatSizeIfTooNarrowBeam(boatSizeInPixel, hullLength, hullBeam); return boatSizeInPixel; } - private void changeBoatSizeIfTooShortHull(Size boatSizeInPixel, BoatClassMasterdata boatClassMasterdata) { + private void changeBoatSizeIfTooShortHull(Size boatSizeInPixel, Distance hullLength, Distance hullBeam) { // the minimum boat length is related to the hull of the boat, not the overall length double minBoatHullLengthInPx = boatVectorGraphics.getMinHullLengthInPx(); if (boatSizeInPixel.getWidth() < minBoatHullLengthInPx) { - double ratioBeanHullLength = boatClassMasterdata.getHullBeam().divide(boatClassMasterdata.getHullLength()); + double ratioBeanHullLength = hullBeam.divide(hullLength); boatSizeInPixel.setHeight(minBoatHullLengthInPx * ratioBeanHullLength); boatSizeInPixel.setWidth(minBoatHullLengthInPx); } } - private void changeBoatSizeIfTooNarrowBeam(Size boatSizeInPixel, BoatClassMasterdata boatClassMasterdata) { + private void changeBoatSizeIfTooNarrowBeam(Size boatSizeInPixel, Distance hullLength, Distance hullBeam) { // if the boat gets too narrow, use the minimum beam and scale the hull length according to aspect double minBoatBeamLengthInPx = boatVectorGraphics.getMinBeamLengthInPx(); if (boatSizeInPixel.getHeight() < minBoatBeamLengthInPx) { - double ratioHullBeanLength = boatClassMasterdata.getHullLength().divide(boatClassMasterdata.getHullBeam()); + double ratioHullBeanLength = hullLength.divide(hullBeam); boatSizeInPixel.setWidth(minBoatBeamLengthInPx * ratioHullBeanLength); boatSizeInPixel.setHeight(minBoatBeamLengthInPx); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CourseMarkOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CourseMarkOverlay.java index 8645ad390d9..6739e8502e4 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CourseMarkOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CourseMarkOverlay.java @@ -115,7 +115,7 @@ public class CourseMarkOverlay extends CanvasOverlayV3 { } final Bearing rotation = markVectorGraphics.getRotationInDegrees(coursePositionsDTO); if (rotation != null) { - setCanvasRotation(rotation.getDegrees()); + setCanvasRotation(coordinateSystem.mapDegreeBearing(rotation.getDegrees())); } Point buoyPositionInPx = mapProjection.fromLatLngToDivPixel(coordinateSystem.toLatLng(position)); if (showBuoyZone && isMarkWithBuoyZone(mark) && buoyZoneRadiusInPixel > MIN_BUOYZONE_RADIUS_IN_PX) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/FixesAndTails.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/FixesAndTails.java index f25dda270c9..5d013eb7e7b 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/FixesAndTails.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/FixesAndTails.java @@ -103,7 +103,8 @@ public class FixesAndTails { } public int getFirstShownFix(CompetitorDTO competitor) { - return firstShownFix.get(competitor).get(); + final Trigger firstShownFixForCompetitor = firstShownFix.get(competitor); + return firstShownFixForCompetitor==null?null:firstShownFixForCompetitor.get(); } /** @@ -301,8 +302,11 @@ public class FixesAndTails { */ private void mergeFixes(CompetitorDTO competitorDTO, List mergeThis, final long timeForPositionTransitionMillis) { List intoThis = fixes.get(competitorDTO); - int indexOfFirstShownFix = firstShownFix.get(competitorDTO).get() == null ? -1 : firstShownFix.get(competitorDTO).get(); - int indexOfLastShownFix = lastShownFix.get(competitorDTO).get() == null ? -1 : lastShownFix.get(competitorDTO).get(); + final Trigger firstShownFixForCompetitor = firstShownFix.get(competitorDTO); + int indexOfFirstShownFix = (firstShownFixForCompetitor == null || firstShownFixForCompetitor.get() == null) ? -1 + : firstShownFixForCompetitor.get(); + final Trigger lastShownFixForCompetitor = lastShownFix.get(competitorDTO); + int indexOfLastShownFix = (lastShownFixForCompetitor == null || lastShownFixForCompetitor.get() == null) ? -1 : lastShownFixForCompetitor.get(); final Polyline tail = getTail(competitorDTO); int intoThisIndex = 0; final Comparator fixByTimePointComparator = new Comparator() { @@ -435,7 +439,8 @@ public class FixesAndTails { final Polyline tail = getTail(competitorDTO); int vertexCount = tail.getPath().getLength(); final List fixesForCompetitor = getFixes(competitorDTO); - int indexOfFirstShownFix = firstShownFix.get(competitorDTO).get() == null ? -1 : firstShownFix.get(competitorDTO).get(); + final Trigger firstShownFixForCompetitor = firstShownFix.get(competitorDTO); + int indexOfFirstShownFix = (firstShownFixForCompetitor == null || firstShownFixForCompetitor.get() == null) ? -1 : firstShownFixForCompetitor.get(); // remove fixes before what is now to be the beginning of the polyline: while (indexOfFirstShownFix != -1 && vertexCount > 0 && fixesForCompetitor.get(indexOfFirstShownFix).timepoint.before(from)) { @@ -454,7 +459,8 @@ public class FixesAndTails { vertexCount++; } // now adjust the polyline's tail: remove excess vertices that are after "to" - int indexOfLastShownFix = lastShownFix.get(competitorDTO).get() == null ? -1 : lastShownFix.get(competitorDTO).get(); + final Trigger lastShownFixForCompetitor = lastShownFix.get(competitorDTO); + int indexOfLastShownFix = (lastShownFixForCompetitor == null || lastShownFixForCompetitor.get() == null) ? -1 : lastShownFixForCompetitor.get(); while (indexOfLastShownFix != -1 && vertexCount > 0 && fixesForCompetitor.get(indexOfLastShownFix).timepoint.after(to)) { if (vertexCount-1 == 0 || (indexOfLastShownFix-1 >= 0 && !fixesForCompetitor.get(indexOfLastShownFix-1).timepoint.after(to))) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index bd658050de6..d24b7fa251c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -1529,10 +1529,10 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe final int numberOfFinishWaypointMarks = courseDTO.getFinishMarkPositions() == null ? 0 : courseDTO.getFinishMarkPositions().size(); final Position startLineLeftPosition = numberOfStartWaypointMarks == 0 ? null : courseDTO.getStartMarkPositions().get(0); final Position startLineRightPosition = numberOfStartWaypointMarks < 2 ? null : courseDTO.getStartMarkPositions().get(1); - if (courseDTO.startLineAngleToCombinedWind != null) { + if (courseDTO.startLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind != null) { startLineAdvantageText.replace(0, startLineAdvantageText.length(), " "+stringMessages.lineAngleToWindAndAdvantage( NumberFormat.getFormat("0.0").format(courseDTO.startLineLengthInMeters), - NumberFormat.getFormat("0.0").format(Math.abs(courseDTO.startLineAngleToCombinedWind)), + NumberFormat.getFormat("0.0").format(Math.abs(courseDTO.startLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind)), courseDTO.startLineAdvantageousSide.name().charAt(0)+courseDTO.startLineAdvantageousSide.name().substring(1).toLowerCase(), NumberFormat.getFormat("0.0").format(courseDTO.startLineAdvantageInMeters))); } else { @@ -1562,10 +1562,10 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe // draw the finish line final Position finishLineLeftPosition = numberOfFinishWaypointMarks == 0 ? null : courseDTO.getFinishMarkPositions().get(0); final Position finishLineRightPosition = numberOfFinishWaypointMarks < 2 ? null : courseDTO.getFinishMarkPositions().get(1); - if (courseDTO.finishLineAngleToCombinedWind != null) { + if (courseDTO.finishLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind != null) { finishLineAdvantageText.replace(0, finishLineAdvantageText.length(), " "+stringMessages.lineAngleToWindAndAdvantage( NumberFormat.getFormat("0.0").format(courseDTO.finishLineLengthInMeters), - NumberFormat.getFormat("0.0").format(Math.abs(courseDTO.finishLineAngleToCombinedWind)), + NumberFormat.getFormat("0.0").format(Math.abs(courseDTO.finishLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind)), courseDTO.finishLineAdvantageousSide.name().charAt(0)+courseDTO.finishLineAdvantageousSide.name().substring(1).toLowerCase(), NumberFormat.getFormat("0.0").format(courseDTO.finishLineAdvantageInMeters))); } else { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/DataMiningEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/DataMiningEntryPoint.java index 4e88d39267d..5df2c357e44 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/DataMiningEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/DataMiningEntryPoint.java @@ -9,6 +9,8 @@ import com.google.gwt.user.client.ui.DockLayoutPanel; import com.google.gwt.user.client.ui.RootLayoutPanel; import com.google.gwt.user.client.ui.SplitLayoutPanel; import com.google.gwt.user.client.ui.Widget; +import com.sap.sailing.domain.common.security.Permission; +import com.sap.sailing.domain.common.security.SailingPermissionsForRoleProvider; import com.sap.sailing.gwt.common.authentication.FixedSailingAuthentication; import com.sap.sailing.gwt.ui.client.AbstractSailingEntryPoint; import com.sap.sailing.gwt.ui.client.RemoteServiceMappingConstants; @@ -39,7 +41,6 @@ public class DataMiningEntryPoint extends AbstractSailingEntryPoint { Highcharts.ensureInjectedWithMore(); super.doOnModuleLoad(); session = new UUIDDataMiningSession(UUID.randomUUID()); - EntryPointHelper.registerASyncService((ServiceDefTarget) dataMiningService, RemoteServiceMappingConstants.dataMiningServiceRemotePath); createDataminingPanel(); } @@ -48,6 +49,7 @@ public class DataMiningEntryPoint extends AbstractSailingEntryPoint { SAPHeaderWithAuthentication header = new SAPHeaderWithAuthentication(getStringMessages().sapSailingAnalytics(), getStringMessages().dataMining()); GenericAuthentication genericSailingAuthentication = new FixedSailingAuthentication(getUserService(), header.getAuthenticationMenuView()); AuthorizedContentDecorator authorizedContentDecorator = new GenericAuthorizedContentDecorator(genericSailingAuthentication); + authorizedContentDecorator.setPermissionToCheck(Permission.DATA_MINING, SailingPermissionsForRoleProvider.INSTANCE); authorizedContentDecorator.setContentWidgetFactory(new WidgetFactory() { @Override public Widget get() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/masterdataimport/MasterDataImportPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/masterdataimport/MasterDataImportPanel.java index ec91acc62f1..2003f3ee495 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/masterdataimport/MasterDataImportPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/masterdataimport/MasterDataImportPanel.java @@ -71,7 +71,7 @@ public class MasterDataImportPanel extends VerticalPanel { HorizontalPanel serverAddressPanel = new HorizontalPanel(); serverAddressPanel.add(new Label(stringMessages.importRemoteHost())); hostBox = new TextBox(); - hostBox.setText("http://www.sapsailing.com/"); + hostBox.setText("https://www.sapsailing.com/"); hostBox.setWidth("300px"); serverAddressPanel.add(hostBox); fetchIdsButton = new Button(stringMessages.importFetchRemoteLgs()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/DataMiningServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/DataMiningServiceImpl.java index bcae32e7280..a929e6261d2 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/DataMiningServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/DataMiningServiceImpl.java @@ -11,10 +11,13 @@ import java.util.Locale; import java.util.Map; import java.util.Map.Entry; +import org.apache.shiro.SecurityUtils; import org.osgi.framework.BundleContext; import org.osgi.util.tracker.ServiceTracker; import com.google.gwt.user.server.rpc.RemoteServiceServlet; +import com.sap.sailing.domain.common.security.Permission; +import com.sap.sailing.domain.common.security.Permission.Mode; import com.sap.sailing.gwt.ui.datamining.DataMiningService; import com.sap.sailing.server.RacingEventService; import com.sap.sse.common.settings.SerializableSettings; @@ -68,17 +71,20 @@ public class DataMiningServiceImpl extends RemoteServiceServlet implements DataM @Override public Date getComponentsChangedTimepoint() { + SecurityUtils.getSubject().checkPermission(Permission.DATA_MINING.getStringPermissionForObjects(Mode.READ)); return getDataMiningServer().getComponentsChangedTimepoint(); } @Override public HashSet getAllStatistics(String localeInfoName) { + SecurityUtils.getSubject().checkPermission(Permission.DATA_MINING.getStringPermissionForObjects(Mode.READ)); Iterable> statistics = getDataMiningServer().getAllStatistics(); return functionsAsDTOs(statistics, localeInfoName); } @Override public HashSet getStatisticsFor(DataRetrieverChainDefinitionDTO retrieverChainDefinition, String localeInfoName) { + SecurityUtils.getSubject().checkPermission(Permission.DATA_MINING.getStringPermissionForObjects(Mode.READ, retrieverChainDefinition.getName())); Class retrievedDataType = getDataMiningServer().getDataRetrieverChainDefinitionForDTO(retrieverChainDefinition).getRetrievedDataType(); Iterable> statistics = getDataMiningServer().getStatisticsFor(retrievedDataType); return functionsAsDTOs(statistics, localeInfoName); @@ -86,6 +92,7 @@ public class DataMiningServiceImpl extends RemoteServiceServlet implements DataM @Override public HashSet getAggregatorDefinitionsFor(FunctionDTO extractionFunction, String localeInfoName) { + SecurityUtils.getSubject().checkPermission(Permission.DATA_MINING.getStringPermissionForObjects(Mode.READ, extractionFunction.getFunctionName())); Class returnType = getReturnType(extractionFunction); @SuppressWarnings("unchecked") Iterable> definitions = @@ -111,6 +118,7 @@ public class DataMiningServiceImpl extends RemoteServiceServlet implements DataM @Override public HashSet getDimensionsFor(FunctionDTO statisticToCalculate, String localeInfoName) { + SecurityUtils.getSubject().checkPermission(Permission.DATA_MINING.getStringPermissionForObjects(Mode.READ, statisticToCalculate.getFunctionName())); Class baseDataType = getBaseDataType(statisticToCalculate); Iterable> dimensions = getDataMiningServer().getDimensionsFor(baseDataType); return functionsAsDTOs(dimensions, localeInfoName); @@ -123,6 +131,7 @@ public class DataMiningServiceImpl extends RemoteServiceServlet implements DataM @Override public HashMap> getDimensionsMappedByLevelFor(DataRetrieverChainDefinitionDTO dataRetrieverChainDefinitionDTO, String localeInfoName) { + SecurityUtils.getSubject().checkPermission(Permission.DATA_MINING.getStringPermissionForObjects(Mode.READ, dataRetrieverChainDefinitionDTO.getName())); DataRetrieverChainDefinition dataRetrieverChainDefinition = getDataMiningServer().getDataRetrieverChainDefinitionForDTO(dataRetrieverChainDefinitionDTO); Map, Iterable>> dimensions = getDataMiningServer() .getDimensionsMappedByLevelFor(dataRetrieverChainDefinition); @@ -132,6 +141,7 @@ public class DataMiningServiceImpl extends RemoteServiceServlet implements DataM @Override public HashMap> getReducedDimensionsMappedByLevelFor( DataRetrieverChainDefinitionDTO dataRetrieverChainDefinitionDTO, String localeInfoName) { + SecurityUtils.getSubject().checkPermission(Permission.DATA_MINING.getStringPermissionForObjects(Mode.READ, dataRetrieverChainDefinitionDTO.getName())); DataRetrieverChainDefinition dataRetrieverChainDefinition = getDataMiningServer().getDataRetrieverChainDefinitionForDTO(dataRetrieverChainDefinitionDTO); Map, Iterable>> reducedDimensions = getDataMiningServer() .getReducedDimensionsMappedByLevelFor(dataRetrieverChainDefinition); @@ -163,12 +173,14 @@ public class DataMiningServiceImpl extends RemoteServiceServlet implements DataM @Override public ArrayList getDataRetrieverChainDefinitions(String localeInfoName) { + SecurityUtils.getSubject().checkPermission(Permission.DATA_MINING.getStringPermissionForObjects(Mode.READ)); Iterable> dataRetrieverChainDefinitions = getDataMiningServer().getDataRetrieverChainDefinitions(); return dataRetrieverChainDefinitionsAsDTOs(dataRetrieverChainDefinitions, localeInfoName); } @Override public ArrayList getDataRetrieverChainDefinitionsFor(FunctionDTO statisticToCalculate, String localeInfoName) { + SecurityUtils.getSubject().checkPermission(Permission.DATA_MINING.getStringPermissionForObjects(Mode.READ, statisticToCalculate.getFunctionName())); Class baseDataType = getBaseDataType(statisticToCalculate); @SuppressWarnings("unchecked") Iterable> dataRetrieverChainDefinitions = (Iterable>)(Iterable) getDataMiningServer().getDataRetrieverChainDefinitionsByDataType(baseDataType); @@ -192,6 +204,7 @@ public class DataMiningServiceImpl extends RemoteServiceServlet implements DataM public QueryResultDTO> getDimensionValuesFor(DataMiningSession session, DataRetrieverChainDefinitionDTO dataRetrieverChainDefinitionDTO, DataRetrieverLevelDTO retrieverLevelDTO, HashSet dimensionDTOs, HashMap retrieverSettingsDTO, HashMap>> filterSelectionDTO, String localeInfoName) { + SecurityUtils.getSubject().checkPermission(Permission.DATA_MINING.getStringPermissionForObjects(Mode.READ, dataRetrieverChainDefinitionDTO.getName())); DataMiningServer dataMiningServer = getDataMiningServer(); DataRetrieverChainDefinition retrieverChainDefinition = dataMiningServer.getDataRetrieverChainDefinitionForDTO(dataRetrieverChainDefinitionDTO); DataRetrieverLevel retrieverLevel = retrieverChainDefinition.getDataRetrieverLevel(retrieverLevelDTO.getLevel()); @@ -251,6 +264,7 @@ public class DataMiningServiceImpl extends RemoteServiceServlet implements DataM @Override public QueryResultDTO runQuery(DataMiningSession session, StatisticQueryDefinitionDTO queryDefinitionDTO) { + SecurityUtils.getSubject().checkPermission(Permission.DATA_MINING.getStringPermissionForObjects(Mode.READ, queryDefinitionDTO.getDataRetrieverChainDefinition().getName())); DataMiningServer dataMiningServer = getDataMiningServer(); StatisticQueryDefinition queryDefinition = dataMiningServer.getQueryDefinitionForDTO(queryDefinitionDTO); Query query = dataMiningServer.createQuery(queryDefinition); @@ -260,6 +274,7 @@ public class DataMiningServiceImpl extends RemoteServiceServlet implements DataM @Override public HashSet getPredefinedQueryIdentifiers() { + SecurityUtils.getSubject().checkPermission(Permission.DATA_MINING.getStringPermissionForObjects(Mode.READ)); HashSet predefinedQueryNames = new HashSet(); for (PredefinedQueryIdentifier predefinedQueryName : getDataMiningServer().getPredefinedQueryIdentifiers()) { predefinedQueryNames.add(predefinedQueryName); @@ -269,6 +284,7 @@ public class DataMiningServiceImpl extends RemoteServiceServlet implements DataM @Override public QueryResultDTO runPredefinedQuery(DataMiningSession session, PredefinedQueryIdentifier identifier, String localeInfoName) { + SecurityUtils.getSubject().checkPermission(Permission.DATA_MINING.getStringPermissionForObjects(Mode.READ, identifier.getIdentifier())); DataMiningServer dataMiningServer = getDataMiningServer(); ModifiableStatisticQueryDefinitionDTO queryDefinitionDTO = dataMiningServer.getPredefinedQueryDefinitionDTO(identifier); if (queryDefinitionDTO != null) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 8c751eea2f5..38aa9a36f42 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -44,6 +44,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.StreamSupport; import java.util.zip.GZIPInputStream; import javax.servlet.ServletContext; @@ -61,13 +62,16 @@ import com.sap.sailing.domain.abstractlog.AbstractLogEvent; import com.sap.sailing.domain.abstractlog.impl.AllEventsOfTypeFinder; import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl; import com.sap.sailing.domain.abstractlog.race.RaceLog; +import com.sap.sailing.domain.abstractlog.race.RaceLogEndOfTrackingEvent; import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; import com.sap.sailing.domain.abstractlog.race.RaceLogFixedMarkPassingEvent; import com.sap.sailing.domain.abstractlog.race.RaceLogFlagEvent; +import com.sap.sailing.domain.abstractlog.race.RaceLogStartOfTrackingEvent; import com.sap.sailing.domain.abstractlog.race.RaceLogSuppressedMarkPassingsEvent; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.AbortingFlagFinder; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.LastPublishedCourseDesignFinder; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.MarkPassingDataFinder; +import com.sap.sailing.domain.abstractlog.race.analyzing.impl.TrackingTimesEventFinder; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.TrackingTimesFinder; import com.sap.sailing.domain.abstractlog.race.impl.RaceLogCourseDesignChangedEventImpl; import com.sap.sailing.domain.abstractlog.race.impl.RaceLogEndOfTrackingEventImpl; @@ -139,11 +143,8 @@ import com.sap.sailing.domain.base.impl.CourseImpl; import com.sap.sailing.domain.base.impl.DynamicBoat; import com.sap.sailing.domain.base.impl.DynamicPerson; import com.sap.sailing.domain.base.impl.DynamicTeam; -import com.sap.sailing.domain.base.impl.FleetImpl; import com.sap.sailing.domain.base.impl.PersonImpl; -import com.sap.sailing.domain.base.impl.RegattaImpl; import com.sap.sailing.domain.base.impl.SailingServerConfigurationImpl; -import com.sap.sailing.domain.base.impl.SeriesImpl; import com.sap.sailing.domain.base.impl.TeamImpl; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.CompetitorDescriptor; @@ -260,7 +261,6 @@ import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter; import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory; import com.sap.sailing.domain.racelogtracking.impl.DeviceMappingImpl; import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; -import com.sap.sailing.domain.ranking.RankingMetricsFactory; import com.sap.sailing.domain.regattalike.HasRegattaLike; import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike; import com.sap.sailing.domain.regattalog.RegattaLogStore; @@ -286,7 +286,6 @@ import com.sap.sailing.domain.tracking.LineDetails; import com.sap.sailing.domain.tracking.Maneuver; import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.MarkPassingManeuver; -import com.sap.sailing.domain.tracking.RaceHandle; import com.sap.sailing.domain.tracking.RaceTracker; import com.sap.sailing.domain.tracking.Track; import com.sap.sailing.domain.tracking.TrackedLeg; @@ -358,9 +357,6 @@ import com.sap.sailing.gwt.ui.shared.RegattaOverviewEntryDTO; import com.sap.sailing.gwt.ui.shared.RegattaScoreCorrectionDTO; import com.sap.sailing.gwt.ui.shared.RegattaScoreCorrectionDTO.ScoreCorrectionEntryDTO; import com.sap.sailing.gwt.ui.shared.RemoteSailingServerReferenceDTO; -import com.sap.sailing.gwt.ui.shared.ReplicaDTO; -import com.sap.sailing.gwt.ui.shared.ReplicationMasterDTO; -import com.sap.sailing.gwt.ui.shared.ReplicationStateDTO; import com.sap.sailing.gwt.ui.shared.SailingServiceConstants; import com.sap.sailing.gwt.ui.shared.ScoreCorrectionProviderDTO; import com.sap.sailing.gwt.ui.shared.SeriesDTO; @@ -426,7 +422,6 @@ import com.sap.sailing.server.operationaltransformation.SetRaceIsKnownToStartUpw import com.sap.sailing.server.operationaltransformation.SetSuppressedFlagForCompetitorInLeaderboard; import com.sap.sailing.server.operationaltransformation.SetWindSourcesToExclude; import com.sap.sailing.server.operationaltransformation.StopTrackingRace; -import com.sap.sailing.server.operationaltransformation.StopTrackingRegatta; import com.sap.sailing.server.operationaltransformation.UpdateCompetitor; import com.sap.sailing.server.operationaltransformation.UpdateCompetitorDisplayNameInLeaderboard; import com.sap.sailing.server.operationaltransformation.UpdateEvent; @@ -481,12 +476,17 @@ import com.sap.sse.gwt.dispatch.servlets.ProxiedRemoteServiceServlet; import com.sap.sse.gwt.server.filestorage.FileStorageServiceDTOUtils; import com.sap.sse.gwt.shared.filestorage.FileStorageServiceDTO; import com.sap.sse.gwt.shared.filestorage.FileStorageServicePropertyErrorsDTO; +import com.sap.sse.gwt.shared.replication.ReplicaDTO; +import com.sap.sse.gwt.shared.replication.ReplicationMasterDTO; +import com.sap.sse.gwt.shared.replication.ReplicationStateDTO; import com.sap.sse.i18n.ResourceBundleStringMessages; import com.sap.sse.replication.OperationWithResult; +import com.sap.sse.replication.Replicable; import com.sap.sse.replication.ReplicationFactory; import com.sap.sse.replication.ReplicationMasterDescriptor; import com.sap.sse.replication.ReplicationService; import com.sap.sse.replication.impl.ReplicaDescriptor; +import com.sap.sse.security.SecurityService; import com.sap.sse.security.SessionUtils; import com.sap.sse.shared.media.ImageDescriptor; import com.sap.sse.shared.media.MediaUtils; @@ -882,7 +882,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S regattaDTO.endDate = regatta.getEndDate() != null ? regatta.getEndDate().asDate() : null; BoatClass boatClass = regatta.getBoatClass(); if (boatClass != null) { - regattaDTO.boatClass = new BoatClassDTO(boatClass.getName(), boatClass.getDisplayName(), boatClass.getHullLength()); + regattaDTO.boatClass = new BoatClassDTO(boatClass.getName(), boatClass.getDisplayName(), boatClass.getHullLength(), boatClass.getHullBeam()); } if (regatta.getDefaultCourseArea() != null) { regattaDTO.defaultCourseAreaUuid = regatta.getDefaultCourseArea().getId(); @@ -1092,8 +1092,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S result.add(new TracTracRaceRecordDTO(raceRecord.getID(), raceRecord.getEventName(), raceRecord.getName(), raceRecord.getTrackingStartTime().asDate(), - raceRecord - .getTrackingEndTime().asDate(), raceRecord.getRaceStartTime() == null ? null : raceRecord.getRaceStartTime().asDate(), + raceRecord.getTrackingEndTime().asDate(), raceRecord.getRaceStartTime() == null ? null : raceRecord.getRaceStartTime().asDate(), raceRecord.getBoatClassNames(), raceRecord.getRaceStatus(), raceRecord.getRaceVisibility(), raceRecord.getJsonURL().toString(), hasRememberedRegatta(raceRecord.getID()))); } @@ -1133,25 +1132,15 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } else { effectiveStoredURI = storedURI; } - final RaceHandle raceHandle = getTracTracAdapter().addTracTracRace(getService(), regattaToAddTo, - record.getParamURL(), effectiveLiveURI == null ? null : new URI(effectiveLiveURI), new URI(effectiveStoredURI), - new URI(courseDesignUpdateURI), new MillisecondsTimePoint(record.getTrackingStartTime().asMillis()), - new MillisecondsTimePoint(record.getTrackingEndTime().asMillis()), - getRaceLogStore(), getRegattaLogStore(), - RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS, offsetToStartTimeOfSimulatedRace, - useInternalMarkPassingAlgorithm, tracTracUsername, tracTracPassword, record.getRaceStatus(), record.getRaceVisibility()); - if (trackWind) { - new Thread("Wind tracking starter for race " + record.getEventName() + "/" + record.getName()) { - public void run() { - try { - startTrackingWind(raceHandle, correctWindByDeclination, - RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - }.start(); - } + getTracTracAdapter().addTracTracRace(getService(), regattaToAddTo, + record.getParamURL(), effectiveLiveURI == null ? null : new URI(effectiveLiveURI), + new URI(effectiveStoredURI), new URI(courseDesignUpdateURI), + new MillisecondsTimePoint(record.getTrackingStartTime().asMillis()), + new MillisecondsTimePoint(record.getTrackingEndTime().asMillis()), getRaceLogStore(), + getRegattaLogStore(), RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS, + offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, tracTracUsername, + tracTracPassword, record.getRaceStatus(), record.getRaceVisibility(), trackWind, + correctWindByDeclination); } } @@ -1173,11 +1162,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S courseDesignUpdateURI, tracTracUsername, tracTracPassword)); } - @Override - public void stopTrackingEvent(RegattaIdentifier regattaIdentifier) throws Exception { - getService().apply(new StopTrackingRegatta(regattaIdentifier)); - } - private RaceDefinition getRaceByName(Regatta regatta, String raceName) { if (regatta != null) { return regatta.getRaceByName(raceName); @@ -1200,24 +1184,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } } - /** - * @param timeoutInMilliseconds eventually passed to {@link RaceHandle#getRace(long)}. If the race definition - * can be obtained within this timeout, wind for the race will be tracked; otherwise, the method returns without - * taking any effect. - */ - private void startTrackingWind(RaceHandle raceHandle, boolean correctByDeclination, long timeoutInMilliseconds) { - Regatta regatta = raceHandle.getRegatta(); - if (regatta != null) { - RaceDefinition race = raceHandle.getRace(timeoutInMilliseconds); - if (race != null) { - getService().startTrackingWind(regatta, race, correctByDeclination); - } else { - log("RaceDefinition wasn't received within " + timeoutInMilliseconds + "ms for a race in regatta " - + regatta.getName() + ". Aborting wait; no wind tracking for this race."); - } - } - } - @Override public WindInfoForRaceDTO getRawWindFixes(RegattaAndRaceIdentifier raceIdentifier, Collection windSources) { WindInfoForRaceDTO result = null; @@ -1527,12 +1493,12 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S @Override public boolean getPolarResults(RegattaAndRaceIdentifier raceIdentifier) { final boolean result; - TrackedRace trackedRace = getExistingTrackedRace(raceIdentifier); - if (trackedRace == null) { + final TrackedRace trackedRace = getExistingTrackedRace(raceIdentifier); + final PolarDataService polarData = getService().getPolarDataService(); + if (trackedRace == null || polarData == null) { result = false; } else { BoatClass boatClass = trackedRace.getRace().getBoatClass(); - PolarDataService polarData = getService().getPolarDataService(); PolarDiagram polarDiagram; try { polarDiagram = new PolarDiagramGPS(boatClass, polarData); @@ -1542,7 +1508,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S result = polarDiagram != null; } return result; - } + } @Override public SimulatorResultsDTO getSimulatorResults(LegIdentifier legIdentifier) { @@ -1816,9 +1782,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S GPSFixMoving fix = fixIter.next(); if (fix.getTimePoint().before(toTimePointExcluding) || (extrapolate && fix.getTimePoint().equals(toTimePointExcluding))) { - if (logger.isLoggable(Level.FINEST)) { - logger.finest(""+competitor.getName()+": " + fix); - } + logger.finest(()->""+competitor.getName()+": " + fix); fixes.add(fix); } else { break; @@ -2086,10 +2050,10 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S final LineDetails markPositionDTOsAndLineAdvantage = trackedRace.getStartLine(dateAsTimePoint); if (markPositionDTOsAndLineAdvantage != null) { result.startLineLengthInMeters = markPositionDTOsAndLineAdvantage.getLength().getMeters(); - Bearing absoluteAngleDifferenceToTrueWind = markPositionDTOsAndLineAdvantage - .getAbsoluteAngleDifferenceToTrueWind(); - result.startLineAngleToCombinedWind = absoluteAngleDifferenceToTrueWind == null ? null - : absoluteAngleDifferenceToTrueWind.getDegrees(); + Bearing angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind = markPositionDTOsAndLineAdvantage + .getAngleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind(); + result.startLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind = angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind == null ? null + : angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind.getDegrees(); result.startLineAdvantageousSide = markPositionDTOsAndLineAdvantage .getAdvantageousSideWhileApproachingLine(); Distance advantage = markPositionDTOsAndLineAdvantage.getAdvantage(); @@ -2101,10 +2065,10 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S final LineDetails markPositionDTOsAndLineAdvantage = trackedRace.getFinishLine(dateAsTimePoint); if (markPositionDTOsAndLineAdvantage != null) { result.finishLineLengthInMeters = markPositionDTOsAndLineAdvantage.getLength().getMeters(); - Bearing absoluteAngleDifferenceToTrueWind = markPositionDTOsAndLineAdvantage - .getAbsoluteAngleDifferenceToTrueWind(); - result.finishLineAngleToCombinedWind = absoluteAngleDifferenceToTrueWind == null ? null - : absoluteAngleDifferenceToTrueWind.getDegrees(); + Bearing angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind = markPositionDTOsAndLineAdvantage + .getAngleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind(); + result.finishLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind = angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind == null ? null + : angleDifferenceFromPortToStarboardWhenApproachingLineToTrueWind.getDegrees(); result.finishLineAdvantageousSide = markPositionDTOsAndLineAdvantage .getAdvantageousSideWhileApproachingLine(); Distance advantage = markPositionDTOsAndLineAdvantage.getAdvantage(); @@ -2760,15 +2724,15 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S if (eventResult != null) { for (RegattaResultDescriptor regattaResult : eventResult.getRegattaResults()) { for(RaceResultDescriptor race: regattaResult.getRaceResults()) { - // add only the tracked races - if(race.isTracked() != null && race.isTracked() == true) { + // add only the tracked races + if (race.isTracked() != null && race.isTracked() == true) { SwissTimingRaceRecordDTO swissTimingRaceRecordDTO = new SwissTimingRaceRecordDTO(race.getId(), race.getName(), - regattaResult.getName(), race.getSeriesName(), race.getFleetName(), race.getStatus(), race.getStartTime(), - regattaResult.getXrrEntriesUrl() != null ? regattaResult.getXrrEntriesUrl().toExternalForm() : null, hasRememberedRegatta(race.getId())); + regattaResult.getName(), race.getSeriesName(), race.getFleetName(), race.getStatus(), race.getStartTime(), + regattaResult.getXrrEntriesUrl() != null ? regattaResult.getXrrEntriesUrl().toExternalForm() : null, hasRememberedRegatta(race.getId())); swissTimingRaceRecordDTO.boatClass = regattaResult.getIsafId() != null && !regattaResult.getIsafId().isEmpty() ? regattaResult.getIsafId() : regattaResult.getClassName(); swissTimingRaceRecordDTO.gender = regattaResult.getCompetitorGenderType().name(); swissTimingRaces.add(swissTimingRaceRecordDTO); - } + } } } result = new SwissTimingEventRecordDTO(eventResult.getId(), eventResult.getName(), eventResult.getTrackingDataHost(), @@ -2818,22 +2782,10 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S startList = getSwissTimingAdapter().readStartListForRace(rr.raceId, regattaResults); } // now read the entry list for the race from the result - final RaceHandle raceHandle = getSwissTimingAdapter().addSwissTimingRace(getService(), regattaToAddTo, + getSwissTimingAdapter().addSwissTimingRace(getService(), regattaToAddTo, rr.raceId, rr.getName(), raceDescription, boatClass, hostname, port, startList, getRaceLogStore(), getRegattaLogStore(), - RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS, useInternalMarkPassingAlgorithm); - if (trackWind) { - new Thread("Wind tracking starter for race " + rr.raceId + "/" + rr.getName()) { - public void run() { - try { - startTrackingWind(raceHandle, correctWindByDeclination, - RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - }.start(); - } + RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS, useInternalMarkPassingAlgorithm, trackWind, correctWindByDeclination); } } @@ -2846,7 +2798,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S List replayRaces = getSwissTimingReplayService().listReplayRaces(swissTimingUrl); List result = new ArrayList(replayRaces.size()); for (SwissTimingReplayRace replayRace : replayRaces) { - result.add(new SwissTimingReplayRaceDTO(replayRace.getFlightNumber(), replayRace.getRaceId(), replayRace.getRsc(), replayRace.getName(), replayRace.getBoatClass(), replayRace.getStartTime(), replayRace.getLink(), hasRememberedRegatta(replayRace.getRaceId()))); + result.add(new SwissTimingReplayRaceDTO(replayRace.getFlightNumber(), replayRace.getRaceId(), + replayRace.getRsc(), replayRace.getName(), replayRace.getBoatClass(), replayRace.getStartTime(), + replayRace.getLink(), hasRememberedRegatta(replayRace.getRaceId()))); } return result; } @@ -2855,35 +2809,24 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S public void replaySwissTimingRace(RegattaIdentifier regattaIdentifier, Iterable replayRaceDTOs, boolean trackWind, boolean correctWindByDeclination, boolean useInternalMarkPassingAlgorithm) { logger.info("replaySwissTimingRace for regatta "+regattaIdentifier+" for races "+replayRaceDTOs); - Regatta regatta; for (SwissTimingReplayRaceDTO replayRaceDTO : replayRaceDTOs) { try { + String boatClassName; if (regattaIdentifier == null) { - String boatClass = replayRaceDTO.boat_class; + boatClassName = replayRaceDTO.boat_class; for (String genderIndicator : new String[] { "Man", "Woman", "Men", "Women", "M", "W" }) { Pattern p = Pattern.compile("(( - )|-| )" + genderIndicator + "$"); - Matcher m = p.matcher(boatClass.trim()); + Matcher m = p.matcher(boatClassName.trim()); if (m.find()) { - boatClass = boatClass.trim().substring(0, m.start(1)); + boatClassName = boatClassName.trim().substring(0, m.start(1)); break; } } - regatta = getService().createRegatta( - RegattaImpl.getDefaultName(replayRaceDTO.rsc, boatClass.trim()), - boatClass.trim(), - /* startDate*/ null, /*endDate*/ null, - RegattaImpl.getDefaultName(replayRaceDTO.rsc, replayRaceDTO.boat_class), - 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(), getService())), false, - baseDomainFactory.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, - /* controlTrackingFromStartAndFinishTimes */ false, RankingMetricsFactory.getRankingMetricConstructor(RankingMetrics.ONE_DESIGN)); - // TODO: is course area relevant for swiss timing replay? } else { - regatta = getService().getRegatta(regattaIdentifier); + boatClassName = null; } - getSwissTimingReplayService().loadRaceData(replayRaceDTO.link, regatta, getService(), useInternalMarkPassingAlgorithm); + getSwissTimingReplayService().loadRaceData(regattaIdentifier, replayRaceDTO.link, replayRaceDTO.getName(), + replayRaceDTO.race_id, boatClassName, getService(), getService(), useInternalMarkPassingAlgorithm, getRaceLogStore(), getRegattaLogStore()); } catch (Exception e) { logger.log(Level.SEVERE, "Error trying to load SwissTimingReplay race " + replayRaceDTO, e); } @@ -3432,10 +3375,12 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S for (Entry>, Integer> e : statistics.entrySet()) { replicationCountByOperationClassName.put(e.getKey().getName(), e.getValue()); } - replicaDTOs.add(new ReplicaDTO(replicaDescriptor.getIpAddress().getHostName(), replicaDescriptor - .getRegistrationTime().asDate(), replicaDescriptor.getUuid().toString(), - replicationCountByOperationClassName, service.getAverageNumberOfOperationsPerMessage(replicaDescriptor), - service.getNumberOfMessagesSent(replicaDescriptor), service.getNumberOfBytesSent(replicaDescriptor), service.getAverageNumberOfBytesPerMessage(replicaDescriptor))); + replicaDTOs.add(new ReplicaDTO(replicaDescriptor.getIpAddress().getHostName(), + replicaDescriptor.getRegistrationTime().asDate(), replicaDescriptor.getUuid().toString(), + replicaDescriptor.getReplicableIdsAsStrings(), replicationCountByOperationClassName, + service.getAverageNumberOfOperationsPerMessage(replicaDescriptor), + service.getNumberOfMessagesSent(replicaDescriptor), service.getNumberOfBytesSent(replicaDescriptor), + service.getAverageNumberOfBytesPerMessage(replicaDescriptor))); } ReplicationMasterDTO master; ReplicationMasterDescriptor replicatingFromMaster = service.getReplicatingFromMaster(); @@ -3443,18 +3388,28 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S master = null; } else { master = new ReplicationMasterDTO(replicatingFromMaster.getHostname(), replicatingFromMaster.getServletPort(), - replicatingFromMaster.getMessagingHostname(), replicatingFromMaster.getMessagingPort(), replicatingFromMaster.getExchangeName()); + replicatingFromMaster.getMessagingHostname(), replicatingFromMaster.getMessagingPort(), replicatingFromMaster.getExchangeName(), + StreamSupport.stream(replicatingFromMaster.getReplicables().spliterator(), /* parallel */ false).map(r->r.getId()).toArray(s->new String[s])); } return new ReplicationStateDTO(master, replicaDTOs, service.getServerIdentifier().toString()); } + /** + * A warning shall be issued to the administration user if the {@link RacingEventService} is a replica. For all + * other {@link Replicable}s such as the {@link SecurityService} we don't care. + */ + @Override + public String[] getReplicableIdsAsStringThatShallLeadToWarningAboutInstanceBeingReplica() { + return new String[] { getService().getId().toString() }; + } + @Override public void startReplicatingFromMaster(String messagingHost, String masterHost, String exchangeName, int servletPort, int messagingPort) throws IOException, ClassNotFoundException, InterruptedException { - // the queue name must be always the same for this server. in order to achieve + // The queue name must always be the same for this server. In order to achieve // this we're using the unique server identifier getReplicationService().startToReplicateFrom( ReplicationFactory.INSTANCE.createReplicationMasterDescriptor(messagingHost, masterHost, exchangeName, servletPort, messagingPort, - getReplicationService().getServerIdentifier().toString())); + /* use local server identifier as queue name */ getReplicationService().getServerIdentifier().toString(), getReplicationService().getAllReplicables())); } @Override @@ -3708,10 +3663,12 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S private Map convertToLocalesAndUrls(Map sailorsInfoWebsiteURLsByLocaleName) { Map eventURLs = new HashMap<>(); for (Map.Entry entry : sailorsInfoWebsiteURLsByLocaleName.entrySet()) { - try { - eventURLs.put(toLocale(entry.getKey()), new URL(entry.getValue())); - } catch(Exception e) { - // broken URLs or Locales are not being stored + if (entry.getValue() != null) { + try { + eventURLs.put(toLocale(entry.getKey()), new URL(entry.getValue())); + } catch(Exception e) { + // broken URLs or Locales are not being stored + } } } return eventURLs; @@ -4559,7 +4516,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S try { getReplicationService().stopToReplicateFromMaster(); } catch (IOException e) { - e.printStackTrace(); + logger.log(Level.SEVERE, "Exception trying to stop replicating from master", e); throw new RuntimeException(e); } } @@ -4567,9 +4524,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S @Override public void stopAllReplicas() { try { - getReplicationService().stopAllReplica(); + getReplicationService().stopAllReplicas(); } catch (IOException e) { - e.printStackTrace(); + logger.log(Level.SEVERE, "Exception trying to stop all replicas from receiving updates from this master", e); throw new RuntimeException(e); } } @@ -4577,11 +4534,10 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S @Override public void stopSingleReplicaInstance(String identifier) { UUID uuid = UUID.fromString(identifier); - ReplicaDescriptor replicaDescriptor = new ReplicaDescriptor(null, uuid, ""); try { - getReplicationService().unregisterReplica(replicaDescriptor); + getReplicationService().unregisterReplica(uuid); } catch (IOException e) { - e.printStackTrace(); + logger.log(Level.SEVERE, "Exception trying to unregister replica with UUID "+uuid, e); throw new RuntimeException(e); } } @@ -5084,15 +5040,55 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S public void setTrackingTimes(RaceLogSetTrackingTimesDTO dto) throws NotFoundException { RaceLog raceLog = getRaceLog(dto.leaderboardName, dto.raceColumnName, dto.fleetName); // the tracking start/end time events are not revoked; updates with null as TimePoint may be added instead + final LogEventAuthorImpl author = new LogEventAuthorImpl(dto.authorName, dto.authorPriority); if (!Util.equalsWithNull(dto.newStartOfTracking, dto.currentStartOfTracking)) { - raceLog.add(new RaceLogStartOfTrackingEventImpl( - dto.newStartOfTracking, new LogEventAuthorImpl(dto.authorName, dto.authorPriority), - raceLog.getCurrentPassId())); + if (dto.newStartOfTracking != null) { + raceLog.add(new RaceLogStartOfTrackingEventImpl(dto.newStartOfTracking.getTimePoint(), + author, raceLog.getCurrentPassId())); + } else { + final Pair trackingTimesEvents = new TrackingTimesEventFinder(raceLog).analyze(); + // we assume to find a valid "set start of tracking time" event that matches the old start of tracking time; + // if the time doesn't match dto.currentStartOfTracking, the revocation is rejected with an exception; + // the result of trying to revoke is returned otherwise, and it may not be the result intended in case + // the author's priority was lower than that of the author of the event that is to be revoked. + if (trackingTimesEvents == null || trackingTimesEvents.getA() == null || + !Util.equalsWithNull(trackingTimesEvents.getA().getLogicalTimePoint(), dto.currentStartOfTracking==null?null:dto.currentStartOfTracking.getTimePoint())) { + throw new NotFoundException("Old start of tracking time in the race log ("+ + (trackingTimesEvents==null||trackingTimesEvents.getA()==null?"unset":trackingTimesEvents.getA().getLogicalTimePoint())+ + ") does not match start of tracking time at transaction start ("+dto.currentStartOfTracking==null?null:dto.currentStartOfTracking.getTimePoint()+")"); + } else { + try { + raceLog.revokeEvent(author, trackingTimesEvents.getA(), "resetting tracking start time"); + } catch (NotRevokableException e) { + logger.log(Level.WARNING, "Internal error: event "+trackingTimesEvents.getA()+" was expected to be revokable", e); + } + } + } } if (!Util.equalsWithNull(dto.newEndOfTracking, dto.currentEndOfTracking)) { - raceLog.add(new RaceLogEndOfTrackingEventImpl( - dto.newEndOfTracking, new LogEventAuthorImpl(dto.authorName, dto.authorPriority), - raceLog.getCurrentPassId())); + if (dto.newEndOfTracking != null) { + raceLog.add(new RaceLogEndOfTrackingEventImpl( + dto.newEndOfTracking.getTimePoint(), author, + raceLog.getCurrentPassId())); + } else { + final Pair trackingTimesEvents = new TrackingTimesEventFinder(raceLog).analyze(); + // we assume to find a valid "set start of tracking time" event that matches the old start of tracking time; + // if the time doesn't match dto.currentStartOfTracking, the revocation is rejected with an exception; + // the result of trying to revoke is returned otherwise, and it may not be the result intended in case + // the author's priority was lower than that of the author of the event that is to be revoked. + if (trackingTimesEvents == null || trackingTimesEvents.getB() == null || + !Util.equalsWithNull(trackingTimesEvents.getB().getLogicalTimePoint(), dto.currentEndOfTracking==null?null:dto.currentEndOfTracking.getTimePoint())) { + throw new NotFoundException("Old end of tracking time in the race log ("+ + (trackingTimesEvents==null||trackingTimesEvents.getB()==null?"unset":trackingTimesEvents.getB().getLogicalTimePoint())+ + ") does not match end of tracking time at transaction start ("+dto.currentEndOfTracking==null?null:dto.currentEndOfTracking.getTimePoint()+")"); + } else { + try { + raceLog.revokeEvent(author, trackingTimesEvents.getB(), "resetting tracking end time"); + } catch (NotRevokableException e) { + logger.log(Level.WARNING, "Internal error: event "+trackingTimesEvents.getB()+" was expected to be revokable", e); + } + } + } } } @@ -5134,8 +5130,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } @Override - public String getIgtimiAuthorizationUrl() { - return getIgtimiConnectionFactory().getAuthorizationUrl(); + public String getIgtimiAuthorizationUrl(String redirectProtocol, String redirectHostname, String redirectPort) { + return getIgtimiConnectionFactory().getAuthorizationUrl(redirectProtocol, redirectHostname, redirectPort); } @Override @@ -5743,19 +5739,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S Leaderboard leaderboard = getLeaderboardByName(leaderboardName); RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName); Fleet fleet = raceColumn.getFleetByName(fleetName); - final RaceHandle raceHandle = getRaceLogTrackingAdapter().startTracking(getService(), leaderboard, raceColumn, fleet); - if (raceHandle != null && trackWind) { - new Thread("Wind tracking starter for race " + leaderboardName + "/" + raceColumnName + "/" + fleetName) { - public void run() { - try { - startTrackingWind(raceHandle, correctWindByDeclination, - RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - }.start(); - } + getRaceLogTrackingAdapter().startTracking(getService(), leaderboard, raceColumn, fleet, trackWind, correctWindByDeclination); } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CoursePositionsDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CoursePositionsDTO.java index 88da726a389..3014ca6ad5c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CoursePositionsDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CoursePositionsDTO.java @@ -21,7 +21,7 @@ public class CoursePositionsDTO implements IsSerializable { * {@link #startLineAdvantageousSide}, {@link #startLineAdvantageInMeters} and {@link #startLineLengthInMeters} are * also both null. */ - public Double startLineAngleToCombinedWind; + public Double startLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind; public NauticalSide startLineAdvantageousSide; public Double startLineAdvantageInMeters; public Double startLineLengthInMeters; @@ -31,7 +31,7 @@ public class CoursePositionsDTO implements IsSerializable { * {@link #finishLineAdvantageousSide}, {@link #finishLineAdvantageInMeters} and * {@link #finishLineAdvantageInMeters} are also both null. */ - public Double finishLineAngleToCombinedWind; + public Double finishLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind; public NauticalSide finishLineAdvantageousSide; public Double finishLineAdvantageInMeters; public Double finishLineLengthInMeters; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/BoatMarkVectorGraphics.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/BoatMarkVectorGraphics.java index 9668f195925..842e2d1d966 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/BoatMarkVectorGraphics.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/BoatMarkVectorGraphics.java @@ -1,15 +1,15 @@ package com.sap.sailing.gwt.ui.shared.racemap; -import java.util.ArrayList; import java.util.Iterator; -import java.util.List; import com.google.gwt.canvas.dom.client.Context2d; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.Distance; +import com.sap.sailing.domain.common.LegType; import com.sap.sailing.domain.common.MarkType; import com.sap.sailing.domain.common.PassingInstruction; import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sailing.gwt.ui.shared.CoursePositionsDTO; import com.sap.sailing.gwt.ui.shared.MarkDTO; @@ -20,8 +20,8 @@ public class BoatMarkVectorGraphics extends AbstractMarkVectorGraphics { private final static Distance BOAT_MARK_HEIGHT_IN_METERS = new MeterDistance(6.2); private final static Distance BOAT_MARK_WIDTH_IN_METERS = new MeterDistance(3.5); private final static double BOAT_MARK_SELECTION_SCALE = 3.5; - private final static double BOAT_MARK_SELECTION_TRANSLATE_X = -10; - private final static double BOAT_MARK_SELECTION_TRANSLATE_Y = -30; + private final static double BOAT_MARK_SELECTION_TRANSLATE_X = 50; + private final static double BOAT_MARK_SELECTION_TRANSLATE_Y = -130; private final String markIdAsString; public BoatMarkVectorGraphics(MarkType type, String color, String shape, String pattern, String markIdAsString) { @@ -98,39 +98,129 @@ public class BoatMarkVectorGraphics extends AbstractMarkVectorGraphics { ctx.beginPath(); } + /** + * A boat that acts as a mark which is part of a line needs to be drawn in some orientation. In real life the boat + * would usually be anchoring, therefore displaying the bow to windward. Especially for reaching starts this is, + * however, a somewhat unusual, unfortunate way of displaying the start line / start boat. At least for reaching + * starts we therefore would rather like to rotate the boat such that if it is part of the start line its bow points + * generally towards the next waypoint, with the keel oriented in a right angle to the start line. But downwind + * starts are also possible, and there it may make the most sense to still generally point the bow to windward, keel + * in right angle to start line or, if available, really aligned to the wind. + *

    + * + * The {@link CoursePositionsDTO} object optionally contains wind information for + * {@link CoursePositionsDTO#startLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind start} and + * {@link CoursePositionsDTO#finishLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind finish} line. If these values are available then together + * with the line orientation that we get from its marks' positions we can infer the combined wind direction and + * rotate the boat mark accordingly for an upwind/downwind start/finish. In the absence of wind information we + * default to the algorithm for a reaching start/finish. + *

    + * + * Only for a reaching start/finish we'd like to draw the boat in a right angle to the line with the bow pointing in + * the direction in which the line is being crossed, so generally to the next waypoint for the start line, and away + * from the previous waypoint for the finish line. This can be computed using the cross-track error (XTE) off the + * bearing from the boat mark to the pin end, as follows (this example for the start line): if the next waypoint has + * a positive XTE (bearing "right" of the bearing from start boat to pin end), the start boat is on the starboard + * side of the line and needs to be rotated 90deg clockwise from the bearing to the pin end; in case of a negative + * XTE the start boat happens to be on the unusual port end of the line and needs to be rotated 90deg + * counter-clockwise from the bearing to the pin end. + *

    + */ @Override public Bearing getRotationInDegrees(CoursePositionsDTO coursePositionsDTO) { - List lineMarkPositions = new ArrayList(); MarkDTO firstMark = null; MarkDTO secondMark = null; - for (WaypointDTO currentWaypoint : coursePositionsDTO.course.waypoints) { + boolean isStart = true; + boolean isFinish; + Position previousWaypointPosition = null; + final Iterator waypointPosIter = coursePositionsDTO.waypointPositions.iterator(); + Position currentWaypointPosition = waypointPosIter.hasNext() ? waypointPosIter.next() : null; + for (Iterator waypointIter = coursePositionsDTO.course.waypoints.iterator(); waypointIter.hasNext(); ) { + WaypointDTO currentWaypoint = waypointIter.next(); + Position nextWaypointPosition = waypointPosIter.hasNext() ? waypointPosIter.next() : null; + isFinish = !waypointIter.hasNext(); if (currentWaypoint.passingInstructions == PassingInstruction.Line) { Iterator marks = currentWaypoint.controlPoint.getMarks().iterator(); firstMark = marks.next(); if (marks.hasNext()) { secondMark = marks.next(); - if (markIdAsString.equals(firstMark.getIdAsString()) - || markIdAsString.equals(secondMark.getIdAsString())) { - lineMarkPositions.add(firstMark.position); - lineMarkPositions.add(secondMark.position); - break; + if (markIdAsString.equals(firstMark.getIdAsString())) { + return getLineBearing(firstMark.position, secondMark.position, isStart, + coursePositionsDTO.startLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind, nextWaypointPosition, + isFinish, coursePositionsDTO.finishLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind, + previousWaypointPosition, /* boatIsFirstMark */ true); + } else if (markIdAsString.equals(secondMark.getIdAsString())) { + return getLineBearing(secondMark.position, firstMark.position, isStart, + coursePositionsDTO.startLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind, nextWaypointPosition, + isFinish, coursePositionsDTO.finishLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind, + previousWaypointPosition, /* boatIsFirstMark */ false); } } } + isStart = false; + previousWaypointPosition = currentWaypointPosition; + currentWaypointPosition = nextWaypointPosition; } - return getLineBearing(lineMarkPositions); + return null; } - private Bearing getLineBearing(List markPositions) { - Bearing result = null; - if (markPositions.size() > 1) { - Position firstPosition = markPositions.get(0); - Position secondPosition = markPositions.get(1); - if (firstPosition != null) { - result = firstPosition.getBearingGreatCircle(secondPosition); + private Bearing getLineBearing(Position boatMarkPosition, Position pinEndPosition, boolean isStart, + Double startLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind, Position nextWaypointPosition, boolean isFinish, + Double finishLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind, Position previousWaypointPosition, boolean boatIsFirstMark) { + // by default, assume the boat is on the starboard side of the line + final Bearing bearingFromBoatMarkToPinEnd = boatMarkPosition.getBearingGreatCircle(pinEndPosition); + final Bearing result; + if (isStart) { + if (nextWaypointPosition == null) { + result = null; + } else { + if (isReach(startLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind)) { + result = bearingOrReverseDependingOnXTEToOther(boatMarkPosition, bearingFromBoatMarkToPinEnd, nextWaypointPosition); + } else { + final boolean pinEndOnPortWhenApproachingLine = pinEndPosition.crossTrackError(boatMarkPosition, boatMarkPosition.getBearingGreatCircle(nextWaypointPosition)).compareTo(Distance.NULL) < 0; + result = getWindFrom(startLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind, + bearingFromBoatMarkToPinEnd, pinEndOnPortWhenApproachingLine).add(new DegreeBearingImpl(-90) /* bow points east with rotation 0deg */); + } + } + } else { + if (previousWaypointPosition == null) { + result = null; + } else { + if (isReach(finishLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind)) { + result = bearingOrReverseDependingOnXTEToOther(pinEndPosition, bearingFromBoatMarkToPinEnd.reverse(), previousWaypointPosition); + } else { + final boolean pinEndOnPortWhenApproachingLine = pinEndPosition.crossTrackError(previousWaypointPosition, previousWaypointPosition.getBearingGreatCircle(boatMarkPosition)).compareTo(Distance.NULL) < 0; + result = getWindFrom(finishLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind, + bearingFromBoatMarkToPinEnd, pinEndOnPortWhenApproachingLine).add(new DegreeBearingImpl(-90) /* bow points east with rotation 0deg */); + } } } return result; } + private Bearing getWindFrom(Double lineAngleFromPortToStarboardWhenApproachingLineToCombinedWind, + final Bearing bearingFromBoatMarkToPinEnd, final boolean pinEndOnPortWhenApproachingLine) { + final Bearing windFrom = pinEndOnPortWhenApproachingLine ? + // pin end on port, as usual: + bearingFromBoatMarkToPinEnd.reverse().add(new DegreeBearingImpl(lineAngleFromPortToStarboardWhenApproachingLineToCombinedWind)) : + // start boat on port; that's unusual: + bearingFromBoatMarkToPinEnd.add(new DegreeBearingImpl(lineAngleFromPortToStarboardWhenApproachingLineToCombinedWind)); + return windFrom; + } + + private Bearing bearingOrReverseDependingOnXTEToOther(Position pos, Bearing bearing, Position other) { + final Bearing result; + if (pos.crossTrackError(other, bearing).compareTo(Distance.NULL) > 0) { + result = bearing.reverse(); + } else { + result = bearing; + } + return result; + } + + private boolean isReach(Double lineAngleToCombinedWind) { + return lineAngleToCombinedWind == null || + Math.abs(90-Math.abs(lineAngleToCombinedWind)) > LegType.UPWIND_DOWNWIND_TOLERANCE_IN_DEG; + } + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/MarkVectorGraphicsFactory.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/MarkVectorGraphicsFactory.java index cfb6aa0acce..35c1a7b4e01 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/MarkVectorGraphicsFactory.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/MarkVectorGraphicsFactory.java @@ -7,20 +7,24 @@ public class MarkVectorGraphicsFactory { public MarkVectorGraphics getMarkVectorGraphics(MarkDTO markDTO) { final MarkType markType = markDTO.type; final MarkVectorGraphics result; - switch (markType) { - case BUOY: + if (markType == null) { result = createBuoyMarkVectorGraphics(markDTO); - break; - case STARTBOAT: - case FINISHBOAT: - result = new BoatMarkVectorGraphics(markDTO.type, markDTO.color, markDTO.shape, markDTO.pattern, markDTO.getIdAsString()); - break; - case LANDMARK: - result = new LandMarkVectorGraphics(markDTO.type, markDTO.color, markDTO.shape, markDTO.pattern); - break; - default: - result = createBuoyMarkVectorGraphics(markDTO); - break; + } else { + switch (markType) { + case BUOY: + result = createBuoyMarkVectorGraphics(markDTO); + break; + case STARTBOAT: + case FINISHBOAT: + result = new BoatMarkVectorGraphics(markDTO.type, markDTO.color, markDTO.shape, markDTO.pattern, markDTO.getIdAsString()); + break; + case LANDMARK: + result = new LandMarkVectorGraphics(markDTO.type, markDTO.color, markDTO.shape, markDTO.pattern); + break; + default: + result = createBuoyMarkVectorGraphics(markDTO); + break; + } } return result; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/home/solutions/RaceCommitteeAppNotes.html b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/home/solutions/RaceCommitteeAppNotes.html index 5ea4177ec7e..9bba7607dae 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/home/solutions/RaceCommitteeAppNotes.html +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/home/solutions/RaceCommitteeAppNotes.html @@ -4,6 +4,14 @@

    What's New - SAP Sailing Race Manager

    +
    January 2017
    + +
      +
    • Phone only (xhdpi and higher): If the font size (Android display settings) is larger + than default, the summary screen will be use a different layout (two lines instead of + one). This way the layout will not wrap the time or labels.
    • +
    +
    November 2016
      diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/home/solutions/SailingAnalyticsNotes.html b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/home/solutions/SailingAnalyticsNotes.html index 1f60f4c7fd4..af5ce5c59af 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/home/solutions/SailingAnalyticsNotes.html +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/home/solutions/SailingAnalyticsNotes.html @@ -4,6 +4,12 @@

      What's New - SAP Sailing Analytics

      +
      January 2017
      +
        +
      • An issue with wind-up map rotation which sometimes left the map centered over the African continent has been fixed. +
      • +
      +
      October 2016
      • An issue with live re-calculation of the maximum speed over ground (SOG) values has been fixed. diff --git a/java/com.sap.sailing.kiworesultimport.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.kiworesultimport.test/META-INF/MANIFEST.MF index c8c7a2fa269..e0e149afa64 100644 --- a/java/com.sap.sailing.kiworesultimport.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.kiworesultimport.test/META-INF/MANIFEST.MF @@ -6,5 +6,5 @@ Bundle-Version: 1.0.0.qualifier Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Fragment-Host: com.sap.sailing.kiworesultimport -Require-Bundle: org.junit4;bundle-version="4.8.2", +Require-Bundle: org.junit;bundle-version="4.8.2", com.sap.sse.common diff --git a/java/com.sap.sailing.manage2sail.resultimport.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.manage2sail.resultimport.test/META-INF/MANIFEST.MF index a39154b876f..c19f1bae884 100644 --- a/java/com.sap.sailing.manage2sail.resultimport.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.manage2sail.resultimport.test/META-INF/MANIFEST.MF @@ -13,7 +13,7 @@ Import-Package: com.sap.sailing.domain.common, com.sap.sailing.xrr.schema, org.osgi.framework;version="1.5.0" Fragment-Host: com.sap.sailing.manage2sail.resultimport -Require-Bundle: org.junit4;bundle-version="4.8.2", +Require-Bundle: org.junit;bundle-version="4.8.2", com.sap.sse.common, org.mockito.mockito-core;bundle-version="1.9.5", org.objenesis;bundle-version="1.3.0", diff --git a/java/com.sap.sailing.media.persistence.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.media.persistence.test/META-INF/MANIFEST.MF index 86266b2b867..76baa0cce22 100644 --- a/java/com.sap.sailing.media.persistence.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.media.persistence.test/META-INF/MANIFEST.MF @@ -9,7 +9,7 @@ Require-Bundle: org.mongodb.mongo-java-driver;bundle-version="2.13.0", com.sap.sse.mongodb, org.json.simple;bundle-version="1.1.0", org.hamcrest.core;bundle-version="1.1.0", - org.junit4;bundle-version="4.8.2", + org.junit;bundle-version="4.8.2", com.sap.sailing.domain.persistence;bundle-version="1.0.0", com.sap.sailing.gwt.ui;bundle-version="1.0.0", com.sap.sailing.domain.common;bundle-version="1.0.0", diff --git a/java/com.sap.sailing.mongodb.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.mongodb.test/META-INF/MANIFEST.MF index fa9864f7111..be57bdcd808 100644 --- a/java/com.sap.sailing.mongodb.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.mongodb.test/META-INF/MANIFEST.MF @@ -17,7 +17,7 @@ Require-Bundle: com.sap.sailing.domain, org.json.simple;bundle-version="1.1.0", com.sap.sailing.domain.swisstimingadapter, org.objenesis;bundle-version="1.3.0", - org.junit4;bundle-version="4.8.2", + org.junit;bundle-version="4.8.2", org.hamcrest;bundle-version="1.1.0", org.mockito.mockito-core;bundle-version="1.9.5", com.sap.sailing.polars, @@ -28,11 +28,16 @@ Require-Bundle: com.sap.sailing.domain, com.sap.sse.replication, com.sap.sse, com.sap.sse.filestorage, - com.sap.sse.shared.android + com.sap.sse.shared.android, + com.sap.sailing.domain.tractracadapter.persistence, + com.sap.sailing.domain.swisstimingadapter.persistence, + com.sap.sailing.domain.racelogtrackingadapter, + com.sap.sailing.domain.swisstimingreplayadapter Import-Package: com.sap.sailing.domain.common, com.sap.sailing.domain.common.impl, com.sap.sailing.domain.common.racelog, com.sap.sailing.domain.common.racelog.tracking, com.sap.sailing.domain.common.tracking, - com.sap.sailing.domain.common.tracking.impl + com.sap.sailing.domain.common.tracking.impl, + com.sap.sailing.domain.tractracadapter.persistence.impl diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/AbstractConnectivityParamsLoadAndStoreTest.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/AbstractConnectivityParamsLoadAndStoreTest.java new file mode 100644 index 00000000000..a36caa478ae --- /dev/null +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/AbstractConnectivityParamsLoadAndStoreTest.java @@ -0,0 +1,31 @@ +package com.sap.sailing.mongodb.test; + +import java.net.UnknownHostException; + +import org.junit.Before; + +import com.mongodb.MongoException; +import com.sap.sailing.domain.persistence.DomainObjectFactory; +import com.sap.sailing.domain.persistence.MongoObjectFactory; +import com.sap.sailing.server.RacingEventService; +import com.sap.sailing.server.impl.RacingEventServiceImpl; + +public abstract class AbstractConnectivityParamsLoadAndStoreTest extends AbstractMongoDBTest { + protected MockConnectivityParamsServiceFinderFactory serviceFinderFactory; + protected MongoObjectFactory mongoObjectFactory; + protected DomainObjectFactory domainObjectFactory; + protected RacingEventService racingEventService; + + public AbstractConnectivityParamsLoadAndStoreTest() throws UnknownHostException, MongoException { + super(); + } + + @Before + public void setUp() { + serviceFinderFactory = new MockConnectivityParamsServiceFinderFactory(); + racingEventService = new RacingEventServiceImpl(/* clearPersistentCompetitorStore */ true, serviceFinderFactory, /* restoreTrackedRaces */ false); + serviceFinderFactory.setRacingEventService(racingEventService); + mongoObjectFactory = racingEventService.getMongoObjectFactory(); + domainObjectFactory = racingEventService.getDomainObjectFactory(); + } +} diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/AbstractMongoDBTest.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/AbstractMongoDBTest.java index 75f10fdbd1a..ec9a898160d 100755 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/AbstractMongoDBTest.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/AbstractMongoDBTest.java @@ -18,13 +18,13 @@ public abstract class AbstractMongoDBTest { protected Mongo mongo; protected DB db; private final MongoDBConfiguration dbConfiguration; - private MongoDBService service; + private MongoDBService racingEventService; public AbstractMongoDBTest() throws UnknownHostException, MongoException { dbConfiguration = MongoDBConfiguration.getDefaultTestConfiguration(); - service = getDBConfiguration().getService(); + racingEventService = getDBConfiguration().getService(); mongo = newMongo(); - db = service.getDB(); + db = racingEventService.getDB(); } protected MongoDBConfiguration getDBConfiguration() { @@ -51,6 +51,6 @@ public abstract class AbstractMongoDBTest { } protected MongoDBService getMongoService() { - return service; + return racingEventService; } } diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/MockConnectivityParamsServiceFinderFactory.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/MockConnectivityParamsServiceFinderFactory.java new file mode 100644 index 00000000000..3192cfc3a80 --- /dev/null +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/MockConnectivityParamsServiceFinderFactory.java @@ -0,0 +1,131 @@ +package com.sap.sailing.mongodb.test; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.sap.sailing.domain.persistence.DomainObjectFactory; +import com.sap.sailing.domain.persistence.MongoObjectFactory; +import com.sap.sailing.domain.persistence.MongoRaceLogStoreFactory; +import com.sap.sailing.domain.persistence.MongoRegattaLogStoreFactory; +import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler; +import com.sap.sailing.domain.persistence.racelog.tracking.impl.PlaceHolderDeviceIdentifierMongoHandler; +import com.sap.sailing.domain.racelog.tracking.test.mock.AbstractTypeBasedServiceFinder; +import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier; +import com.sap.sailing.domain.racelogtracking.impl.RaceLogConnectivityParams; +import com.sap.sailing.domain.swisstimingadapter.DomainFactory; +import com.sap.sailing.domain.swisstimingadapter.impl.SwissTimingAdapterFactoryImpl; +import com.sap.sailing.domain.swisstimingadapter.impl.SwissTimingTrackingConnectivityParameters; +import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayServiceFactory; +import com.sap.sailing.domain.swisstimingreplayadapter.impl.SwissTimingReplayConnectivityParameters; +import com.sap.sailing.domain.swisstimingreplayadapter.impl.SwissTimingReplayConnectivityParamsHandler; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler; +import com.sap.sailing.domain.tractracadapter.TracTracAdapterFactory; +import com.sap.sailing.domain.tractracadapter.impl.RaceTrackingConnectivityParametersImpl; +import com.sap.sailing.domain.tractracadapter.persistence.impl.TracTracConnectivityParamsHandler; +import com.sap.sailing.server.RacingEventService; +import com.sap.sailing.server.impl.RacingEventServiceImpl; +import com.sap.sse.common.NoCorrespondingServiceRegisteredException; +import com.sap.sse.common.TypeBasedServiceFinder; +import com.sap.sse.common.TypeBasedServiceFinderFactory; + +/** + * A simplified implementation of the {@link TypeBasedServiceFinder} interface that, when the device type + * {@link SmartphoneImeiIdentifier#TYPE} is requested, returns a specific handler that was passed to this object's + * constructor. + * + * @author Fredrik Teschke + * + */ +public class MockConnectivityParamsServiceFinderFactory implements TypeBasedServiceFinderFactory { + Map, TypeBasedServiceFinder> serviceFinders = new HashMap, TypeBasedServiceFinder>(); + private RacingEventService racingEventService; + + public MockConnectivityParamsServiceFinderFactory() { + serviceFinders.put(RaceTrackingConnectivityParametersHandler.class, new AbstractTypeBasedServiceFinder() { + @Override + public RaceTrackingConnectivityParametersHandler findService(String type) + throws NoCorrespondingServiceRegisteredException { + final MongoObjectFactory mongoObjectFactory = racingEventService.getMongoObjectFactory(); + final DomainObjectFactory domainObjectFactory = racingEventService.getDomainObjectFactory(); + final DomainFactory swissTimingDomainFactory = new SwissTimingAdapterFactoryImpl().getOrCreateSwissTimingAdapter(domainObjectFactory.getBaseDomainFactory()).getSwissTimingDomainFactory(); + switch (type) { + case RaceTrackingConnectivityParametersImpl.TYPE: + return new TracTracConnectivityParamsHandler( + MongoRaceLogStoreFactory.INSTANCE.getMongoRaceLogStore(mongoObjectFactory, domainObjectFactory), + MongoRegattaLogStoreFactory.INSTANCE.getMongoRegattaLogStore(mongoObjectFactory, domainObjectFactory), + TracTracAdapterFactory.INSTANCE.getOrCreateTracTracAdapter(domainObjectFactory.getBaseDomainFactory()).getTracTracDomainFactory()); + case SwissTimingTrackingConnectivityParameters.TYPE: + return new com.sap.sailing.domain.swisstimingadapter.persistence.impl.SwissTimingConnectivityParamsHandler( + MongoRaceLogStoreFactory.INSTANCE.getMongoRaceLogStore(mongoObjectFactory, domainObjectFactory), + MongoRegattaLogStoreFactory.INSTANCE.getMongoRegattaLogStore(mongoObjectFactory, domainObjectFactory), + swissTimingDomainFactory); + case SwissTimingReplayConnectivityParameters.TYPE: + return new SwissTimingReplayConnectivityParamsHandler( + MongoRaceLogStoreFactory.INSTANCE.getMongoRaceLogStore(mongoObjectFactory, domainObjectFactory), + MongoRegattaLogStoreFactory.INSTANCE.getMongoRegattaLogStore(mongoObjectFactory, domainObjectFactory), + swissTimingDomainFactory, + SwissTimingReplayServiceFactory.INSTANCE.createSwissTimingReplayService(swissTimingDomainFactory, racingEventService)); + case RaceLogConnectivityParams.TYPE: + return new com.sap.sailing.domain.racelogtracking.impl.RaceLogConnectivityParamsHandler(racingEventService); + default: + return null; + } + } + + @Override + public Set findAllServices() { + final Set result = new HashSet<>(); + for (final String type : new String[] { + RaceTrackingConnectivityParametersImpl.TYPE, + SwissTimingTrackingConnectivityParameters.TYPE, + SwissTimingReplayConnectivityParameters.TYPE, + RaceLogConnectivityParams.TYPE }) { + result.add(findService(type)); + } + return result; + } + + @Override + public void setFallbackService(RaceTrackingConnectivityParametersHandler fallback) { + } + }); + serviceFinders.put(DeviceIdentifierMongoHandler.class, new AbstractTypeBasedServiceFinder() { + @Override + public DeviceIdentifierMongoHandler findService(String type) + throws NoCorrespondingServiceRegisteredException { + switch (type) { + case RaceTrackingConnectivityParametersImpl.TYPE: + return new PlaceHolderDeviceIdentifierMongoHandler(); + default: + return null; + } + } + + @Override + public Set findAllServices() { + return null; + } + + @Override + public void setFallbackService(DeviceIdentifierMongoHandler fallback) { + } + }); + } + + /** + * Must be called right after the constructor invocation when this object has been used to initialize a + * {@link RacingEventServiceImpl} object. + */ + public void setRacingEventService(RacingEventService racingEventService) { + this.racingEventService = racingEventService; + } + + @SuppressWarnings("unchecked") + @Override + public TypeBasedServiceFinder createServiceFinder(Class clazz) { + return (TypeBasedServiceFinder) serviceFinders.get(clazz); + } + +} diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/RaceLogConnectivityParamsLoadAndStoreTest.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/RaceLogConnectivityParamsLoadAndStoreTest.java new file mode 100644 index 00000000000..1d08976d31a --- /dev/null +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/RaceLogConnectivityParamsLoadAndStoreTest.java @@ -0,0 +1,87 @@ +package com.sap.sailing.mongodb.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +import org.junit.Test; + +import com.mongodb.MongoException; +import com.sap.sailing.domain.base.Fleet; +import com.sap.sailing.domain.base.Regatta; +import com.sap.sailing.domain.base.Series; +import com.sap.sailing.domain.base.impl.CourseAreaImpl; +import com.sap.sailing.domain.base.impl.FleetImpl; +import com.sap.sailing.domain.base.impl.SeriesImpl; +import com.sap.sailing.domain.common.racelog.tracking.NotDenotableForRaceLogTrackingException; +import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; +import com.sap.sailing.domain.leaderboard.impl.LowPoint; +import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory; +import com.sap.sailing.domain.racelogtracking.impl.RaceLogConnectivityParams; +import com.sap.sailing.domain.ranking.OneDesignRankingMetric; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; +import com.sap.sse.common.Duration; +import com.sap.sse.common.Util; +import com.sap.sse.common.impl.MillisecondsTimePoint; + +public class RaceLogConnectivityParamsLoadAndStoreTest extends AbstractConnectivityParamsLoadAndStoreTest { + public RaceLogConnectivityParamsLoadAndStoreTest() throws UnknownHostException, MongoException { + super(); + } + + @Test + public void testStoreAndLoadRaceLogTrackingParams() throws MalformedURLException, URISyntaxException, NotDenotableForRaceLogTrackingException { + // set up + final long delayToLiveInMillis = 3000; + final boolean trackWind = true; + final boolean correctWindDirectionByMagneticDeclination = true; + final FleetImpl fleet = new FleetImpl("Default"); + final SeriesImpl theSeries = new SeriesImpl("Default", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, + Arrays.asList(fleet), Collections.emptyList(), racingEventService); + final Iterable series = Arrays.asList(theSeries); + final Regatta regatta = racingEventService.createRegatta("My Regatta", "12mR", MillisecondsTimePoint.now(), MillisecondsTimePoint.now().plus(Duration.ONE_DAY), + UUID.randomUUID(), series, /* persistent */ true, new LowPoint(), new CourseAreaImpl("Default", UUID.randomUUID()), + /* buoyZoneRadiusInHullLengths */ 2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ true, + OneDesignRankingMetric::new); + theSeries.addRaceColumn("R1", racingEventService); + theSeries.addRaceColumn("R2", racingEventService); + theSeries.addRaceColumn("R3", racingEventService); + final RegattaLeaderboard leaderboard = racingEventService.addRegattaLeaderboard(regatta.getRegattaIdentifier(), /* leaderboardDisplayName */ null, new int[] { 5, 9 }); + RaceLogTrackingAdapterFactory.INSTANCE.getAdapter(domainObjectFactory.getBaseDomainFactory()).denoteAllRacesForRaceLogTracking(racingEventService, leaderboard); + final RaceLogConnectivityParams rlParams = new RaceLogConnectivityParams( + racingEventService, regatta, leaderboard.getRaceColumnByName("R2"), fleet, leaderboard, + delayToLiveInMillis, domainObjectFactory.getBaseDomainFactory(), trackWind, + correctWindDirectionByMagneticDeclination); + // store + mongoObjectFactory.addConnectivityParametersForRaceToRestore(rlParams); + // load + final Set connectivityParametersForRacesToRestore = new HashSet<>(); + domainObjectFactory.loadConnectivityParametersForRacesToRestore(params->connectivityParametersForRacesToRestore.add(params)); + // compare + assertEquals(1, Util.size(connectivityParametersForRacesToRestore)); + final RaceTrackingConnectivityParameters paramsReadFromDB = connectivityParametersForRacesToRestore.iterator().next(); + assertTrue(paramsReadFromDB instanceof RaceLogConnectivityParams); + RaceLogConnectivityParams raceLogParamsReadFromDB = (RaceLogConnectivityParams) paramsReadFromDB; + assertEquals(delayToLiveInMillis, raceLogParamsReadFromDB.getDelayToLiveInMillis()); + assertSame(leaderboard, raceLogParamsReadFromDB.getLeaderboard()); + assertSame(theSeries.getRaceColumnByName("R2"), raceLogParamsReadFromDB.getRaceColumn()); + assertSame(fleet, raceLogParamsReadFromDB.getFleet()); + assertEquals(rlParams.getTrackerID(), raceLogParamsReadFromDB.getTrackerID()); + assertEquals(rlParams.isTrackWind(), raceLogParamsReadFromDB.isTrackWind()); + assertEquals(rlParams.isCorrectWindDirectionByMagneticDeclination(), raceLogParamsReadFromDB.isCorrectWindDirectionByMagneticDeclination()); + // remove again + mongoObjectFactory.removeConnectivityParametersForRaceToRestore(rlParams); + final Set connectivityParametersForRacesToRestore2 = new HashSet<>(); + domainObjectFactory.loadConnectivityParametersForRacesToRestore(params->connectivityParametersForRacesToRestore2.add(params)); + assertTrue(connectivityParametersForRacesToRestore2.isEmpty()); + } +} diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/SwissTimingConnectivityParamsLoadAndStoreTest.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/SwissTimingConnectivityParamsLoadAndStoreTest.java new file mode 100644 index 00000000000..006786d2d0e --- /dev/null +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/SwissTimingConnectivityParamsLoadAndStoreTest.java @@ -0,0 +1,132 @@ +package com.sap.sailing.mongodb.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.junit.Test; + +import com.mongodb.MongoException; +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.swisstimingadapter.StartList; +import com.sap.sailing.domain.swisstimingadapter.SwissTimingFactory; +import com.sap.sailing.domain.swisstimingadapter.impl.CompetitorWithID; +import com.sap.sailing.domain.swisstimingadapter.impl.CompetitorWithoutID; +import com.sap.sailing.domain.swisstimingadapter.impl.CrewMemberImpl; +import com.sap.sailing.domain.swisstimingadapter.impl.StartListImpl; +import com.sap.sailing.domain.swisstimingadapter.impl.SwissTimingAdapterFactoryImpl; +import com.sap.sailing.domain.swisstimingadapter.impl.SwissTimingTrackingConnectivityParameters; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; +import com.sap.sse.common.Util; + +public class SwissTimingConnectivityParamsLoadAndStoreTest extends AbstractConnectivityParamsLoadAndStoreTest { + public SwissTimingConnectivityParamsLoadAndStoreTest() throws UnknownHostException, MongoException { + super(); + } + + @Test + public void testStoreAndLoadSwissTimingLiveParamsWithCompetitorsWithIDAndCrew() throws MalformedURLException, URISyntaxException { + final boolean trackWind = true; + final boolean correctWindDirectionByMagneticDeclination = true; + final String hostname = "a.b.com"; + final int port = 1234; + final String raceID = "Race ID 123"; + final String raceName = "The Race"; + final String raceDescription = null; + final BoatClass boatClass = domainObjectFactory.getBaseDomainFactory().getOrCreateBoatClass("49er"); + final CrewMemberImpl c1c1 = new CrewMemberImpl("c1c1", "GER", "Helm"); + final CrewMemberImpl c1c2 = new CrewMemberImpl("c1c2", "POL", "Crew"); + final CrewMemberImpl c2c1 = new CrewMemberImpl("c2c1", "AUS", "Helm"); + final CrewMemberImpl c2c2 = new CrewMemberImpl("c2c2", "NZL", "Crew"); + final CompetitorWithID c1 = new CompetitorWithID("c1", "b1", "GER", "C1", Arrays.asList(c1c1, c1c2)); + final CompetitorWithID c2 = new CompetitorWithID("c2", "b2", "AUS", "C2", Arrays.asList(c2c1, c2c2)); + final StartList startList = new StartListImpl(raceID, Arrays.asList(c1, c2)); + final boolean useInternalMarkPassingAlgorithm = false; + final long delayToLiveInMillis = 30000; + final SwissTimingTrackingConnectivityParameters stParams = new SwissTimingTrackingConnectivityParameters( + hostname, port, raceID, raceName, raceDescription, boatClass, startList, delayToLiveInMillis, + SwissTimingFactory.INSTANCE, new SwissTimingAdapterFactoryImpl().getOrCreateSwissTimingAdapter(domainObjectFactory.getBaseDomainFactory()).getSwissTimingDomainFactory(), + /* raceLogStore */ null, /* regattaLogStore */ null, useInternalMarkPassingAlgorithm, trackWind, correctWindDirectionByMagneticDeclination); + // store + mongoObjectFactory.addConnectivityParametersForRaceToRestore(stParams); + // load + final Set connectivityParametersForRacesToRestore = new HashSet<>(); + domainObjectFactory.loadConnectivityParametersForRacesToRestore(params->connectivityParametersForRacesToRestore.add(params)); + // compare + assertEquals(1, Util.size(connectivityParametersForRacesToRestore)); + final RaceTrackingConnectivityParameters paramsReadFromDB = connectivityParametersForRacesToRestore.iterator().next(); + assertTrue(paramsReadFromDB instanceof SwissTimingTrackingConnectivityParameters); + SwissTimingTrackingConnectivityParameters stParamsReadFromDB = (SwissTimingTrackingConnectivityParameters) paramsReadFromDB; + assertEquals(delayToLiveInMillis, stParamsReadFromDB.getDelayToLiveInMillis()); + assertEquals(hostname, stParamsReadFromDB.getHostname()); + assertEquals(boatClass, stParamsReadFromDB.getBoatClass()); + assertEquals(port, stParamsReadFromDB.getPort()); + assertEquals(raceDescription, stParamsReadFromDB.getRaceDescription()); + assertEquals(raceID, stParamsReadFromDB.getRaceID()); + assertEquals(raceName, stParamsReadFromDB.getRaceName()); + assertEquals(startList.getRaceID(), stParamsReadFromDB.getStartList().getRaceID()); + assertEquals(startList.getCompetitors(), stParamsReadFromDB.getStartList().getCompetitors()); + assertEquals(stParams.getTrackerID(), stParamsReadFromDB.getTrackerID()); + assertEquals(stParams.isTrackWind(), stParamsReadFromDB.isTrackWind()); + assertEquals(stParams.isCorrectWindDirectionByMagneticDeclination(), stParamsReadFromDB.isCorrectWindDirectionByMagneticDeclination()); + // remove again + mongoObjectFactory.removeConnectivityParametersForRaceToRestore(stParams); + final Set connectivityParametersForRacesToRestore2 = new HashSet<>(); + domainObjectFactory.loadConnectivityParametersForRacesToRestore(params->connectivityParametersForRacesToRestore2.add(params)); + assertTrue(connectivityParametersForRacesToRestore2.isEmpty()); + } + + @Test + public void testStoreAndLoadSwissTimingLiveParamsWithCompetitorsWithoutID() throws MalformedURLException, URISyntaxException { + final boolean trackWind = false; + final boolean correctWindDirectionByMagneticDeclination = false; + final String hostname = "a.b.com"; + final int port = 1234; + final String raceID = "Race ID 123"; + final String raceName = "The Race"; + final String raceDescription = null; + final BoatClass boatClass = domainObjectFactory.getBaseDomainFactory().getOrCreateBoatClass("49er"); + final CompetitorWithoutID c1 = new CompetitorWithoutID("b1", "GER", "C1"); + final CompetitorWithoutID c2 = new CompetitorWithoutID("b2", "GER", "21"); + final StartList startList = new StartListImpl(raceID, Arrays.asList(c1, c2)); + final boolean useInternalMarkPassingAlgorithm = false; + final long delayToLiveInMillis = 30000; + final SwissTimingTrackingConnectivityParameters stParams = new SwissTimingTrackingConnectivityParameters( + hostname, port, raceID, raceName, raceDescription, boatClass, startList, delayToLiveInMillis, + SwissTimingFactory.INSTANCE, new SwissTimingAdapterFactoryImpl().getOrCreateSwissTimingAdapter(domainObjectFactory.getBaseDomainFactory()).getSwissTimingDomainFactory(), + /* raceLogStore */ null, /* regattaLogStore */ null, useInternalMarkPassingAlgorithm, trackWind, correctWindDirectionByMagneticDeclination); + // store + mongoObjectFactory.addConnectivityParametersForRaceToRestore(stParams); + // load + final Set connectivityParametersForRacesToRestore = new HashSet<>(); + domainObjectFactory.loadConnectivityParametersForRacesToRestore(params->connectivityParametersForRacesToRestore.add(params)); + // compare + assertEquals(1, Util.size(connectivityParametersForRacesToRestore)); + final RaceTrackingConnectivityParameters paramsReadFromDB = connectivityParametersForRacesToRestore.iterator().next(); + assertTrue(paramsReadFromDB instanceof SwissTimingTrackingConnectivityParameters); + SwissTimingTrackingConnectivityParameters stParamsReadFromDB = (SwissTimingTrackingConnectivityParameters) paramsReadFromDB; + assertEquals(delayToLiveInMillis, stParamsReadFromDB.getDelayToLiveInMillis()); + assertEquals(hostname, stParamsReadFromDB.getHostname()); + assertEquals(boatClass, stParamsReadFromDB.getBoatClass()); + assertEquals(port, stParamsReadFromDB.getPort()); + assertEquals(raceDescription, stParamsReadFromDB.getRaceDescription()); + assertEquals(raceID, stParamsReadFromDB.getRaceID()); + assertEquals(raceName, stParamsReadFromDB.getRaceName()); + assertEquals(startList.getRaceID(), stParamsReadFromDB.getStartList().getRaceID()); + assertEquals(startList.getCompetitors(), stParamsReadFromDB.getStartList().getCompetitors()); + assertEquals(stParams.getTrackerID(), stParamsReadFromDB.getTrackerID()); + assertEquals(stParams.isTrackWind(), stParamsReadFromDB.isTrackWind()); + assertEquals(stParams.isCorrectWindDirectionByMagneticDeclination(), stParamsReadFromDB.isCorrectWindDirectionByMagneticDeclination()); + // remove again + mongoObjectFactory.removeConnectivityParametersForRaceToRestore(stParams); + final Set connectivityParametersForRacesToRestore2 = new HashSet<>(); + domainObjectFactory.loadConnectivityParametersForRacesToRestore(params->connectivityParametersForRacesToRestore2.add(params)); + assertTrue(connectivityParametersForRacesToRestore2.isEmpty()); + } +} diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/SwissTimingReplayConnectivityParamsLoadAndStoreTest.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/SwissTimingReplayConnectivityParamsLoadAndStoreTest.java new file mode 100644 index 00000000000..be15ba863ec --- /dev/null +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/SwissTimingReplayConnectivityParamsLoadAndStoreTest.java @@ -0,0 +1,61 @@ +package com.sap.sailing.mongodb.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.UnknownHostException; +import java.util.HashSet; +import java.util.Set; + +import org.junit.Test; + +import com.mongodb.MongoException; +import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapter; +import com.sap.sailing.domain.swisstimingadapter.impl.SwissTimingAdapterFactoryImpl; +import com.sap.sailing.domain.swisstimingreplayadapter.impl.SwissTimingReplayConnectivityParameters; +import com.sap.sailing.domain.swisstimingreplayadapter.impl.SwissTimingReplayServiceFactoryImpl; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; +import com.sap.sse.common.Util; + +public class SwissTimingReplayConnectivityParamsLoadAndStoreTest extends AbstractConnectivityParamsLoadAndStoreTest { + public SwissTimingReplayConnectivityParamsLoadAndStoreTest() throws UnknownHostException, MongoException { + super(); + } + + @Test + public void testStoreAndLoadSwissTimingReplayParams() throws MalformedURLException, URISyntaxException { + final String link = "a.b.com?race=1234&_start=0"; + final String raceID = "Race ID 123"; + final String raceName = "The Race"; + final String boatClassName = "49er"; + final boolean useInternalMarkPassingAlgorithm = false; + final SwissTimingAdapter swissTimingAdapter = new SwissTimingAdapterFactoryImpl().getOrCreateSwissTimingAdapter(domainObjectFactory.getBaseDomainFactory()); + final SwissTimingReplayConnectivityParameters stParams = new SwissTimingReplayConnectivityParameters( + link, raceName, raceID, boatClassName, useInternalMarkPassingAlgorithm, swissTimingAdapter.getSwissTimingDomainFactory(), + new SwissTimingReplayServiceFactoryImpl().createSwissTimingReplayService(swissTimingAdapter.getSwissTimingDomainFactory(), racingEventService), + /* raceLogStore */ null, /* regattaLogStore */ null); + // store + mongoObjectFactory.addConnectivityParametersForRaceToRestore(stParams); + // load + final Set connectivityParametersForRacesToRestore = new HashSet<>(); + domainObjectFactory.loadConnectivityParametersForRacesToRestore(params->connectivityParametersForRacesToRestore.add(params)); + // compare + assertEquals(1, Util.size(connectivityParametersForRacesToRestore)); + final RaceTrackingConnectivityParameters paramsReadFromDB = connectivityParametersForRacesToRestore.iterator().next(); + assertTrue(paramsReadFromDB instanceof SwissTimingReplayConnectivityParameters); + SwissTimingReplayConnectivityParameters stParamsReadFromDB = (SwissTimingReplayConnectivityParameters) paramsReadFromDB; + assertEquals(link, stParamsReadFromDB.getLink()); + assertEquals(boatClassName, stParamsReadFromDB.getBoatClassName()); + assertEquals(raceID, stParamsReadFromDB.getRaceID()); + assertEquals(raceName, stParamsReadFromDB.getRaceName()); + assertEquals(stParams.getTrackerID(), stParamsReadFromDB.getTrackerID()); + assertEquals(useInternalMarkPassingAlgorithm, stParamsReadFromDB.isUseInternalMarkPassingAlgorithm()); + // remove again + mongoObjectFactory.removeConnectivityParametersForRaceToRestore(stParams); + final Set connectivityParametersForRacesToRestore2 = new HashSet<>(); + domainObjectFactory.loadConnectivityParametersForRacesToRestore(params->connectivityParametersForRacesToRestore2.add(params)); + assertTrue(connectivityParametersForRacesToRestore2.isEmpty()); + } +} diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndLoadingEventsAndRegattas.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndLoadingEventsAndRegattas.java index 3d39b3d70b6..328f135ae8b 100755 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndLoadingEventsAndRegattas.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndLoadingEventsAndRegattas.java @@ -396,7 +396,7 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest @Test public void testLoadStoreSimpleRegattaLeaderboard() { RacingEventService res = new RacingEventServiceImpl(PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), DomainFactory.INSTANCE), PersistenceFactory.INSTANCE - .getMongoObjectFactory(getMongoService()), MediaDBFactory.INSTANCE.getMediaDB(getMongoService()), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE); + .getMongoObjectFactory(getMongoService()), MediaDBFactory.INSTANCE.getMediaDB(getMongoService()), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE, /* restoreTrackedRaces */ false); final int numberOfQualifyingRaces = 5; final int numberOfFinalRaces = 7; final String regattaBaseName = "Kieler Woche"; @@ -513,7 +513,7 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest private RacingEventServiceImpl createRacingEventServiceWithOneMockedTrackedRace(final DynamicTrackedRace q2YellowTrackedRace) { return new RacingEventServiceImpl(PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), DomainFactory.INSTANCE), PersistenceFactory.INSTANCE - .getMongoObjectFactory(getMongoService()), MediaDBFactory.INSTANCE.getMediaDB(getMongoService()), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE) { + .getMongoObjectFactory(getMongoService()), MediaDBFactory.INSTANCE.getMediaDB(getMongoService()), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE, /* restoreTrackedRaces */ false) { @Override public DynamicTrackedRace getExistingTrackedRace(RegattaAndRaceIdentifier raceIdentifier) { return q2YellowTrackedRace; @@ -853,7 +853,7 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest regatta.addRace(racedef); RacingEventServiceImpl evs = new RacingEventServiceImpl(PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), DomainFactory.INSTANCE), PersistenceFactory.INSTANCE - .getMongoObjectFactory(getMongoService()), MediaDBFactory.INSTANCE.getMediaDB(getMongoService()), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE); + .getMongoObjectFactory(getMongoService()), MediaDBFactory.INSTANCE.getMediaDB(getMongoService()), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE, /* restoreTrackedRaces */ false); assertNull(evs.getRememberedRegattaForRace(racedef.getId())); evs.raceAdded(regatta, racedef); assertNotNull(evs.getRememberedRegattaForRace(racedef.getId())); diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboardGroups.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboardGroups.java index bfa1136c2a9..136ccccbbff 100755 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboardGroups.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboardGroups.java @@ -318,7 +318,7 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe // the leaderboard named leaderboardNames[2] occurs in both groups RacingEventService racingEventService = new RacingEventServiceImpl(PersistenceFactory.INSTANCE.getDomainObjectFactory(MongoDBService.INSTANCE, DomainFactory.INSTANCE), PersistenceFactory.INSTANCE - .getMongoObjectFactory(MongoDBService.INSTANCE), MediaDBFactory.INSTANCE.getMediaDB(MongoDBService.INSTANCE), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE); // expected to load leaderboard groups + .getMongoObjectFactory(MongoDBService.INSTANCE), MediaDBFactory.INSTANCE.getMediaDB(MongoDBService.INSTANCE), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE, /* restoreTrackedRaces */ false); // expected to load leaderboard groups final LeaderboardGroup loadedLeaderboardGroup1 = racingEventService.getLeaderboardGroupByName(groupName1); final LeaderboardGroup loadedLeaderboardGroup2 = racingEventService.getLeaderboardGroupByName(groupName2); diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TracTracConnectivityParamsLoadAndStoreTest.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TracTracConnectivityParamsLoadAndStoreTest.java new file mode 100644 index 00000000000..6dd983458f5 --- /dev/null +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TracTracConnectivityParamsLoadAndStoreTest.java @@ -0,0 +1,84 @@ +package com.sap.sailing.mongodb.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.UnknownHostException; +import java.util.HashSet; +import java.util.Set; + +import org.junit.Test; + +import com.mongodb.MongoException; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; +import com.sap.sailing.domain.tractracadapter.DomainFactory; +import com.sap.sailing.domain.tractracadapter.TracTracConnectionConstants; +import com.sap.sailing.domain.tractracadapter.impl.RaceTrackingConnectivityParametersImpl; +import com.sap.sse.common.Duration; +import com.sap.sse.common.TimePoint; +import com.sap.sse.common.Util; +import com.sap.sse.common.impl.MillisecondsTimePoint; + +public class TracTracConnectivityParamsLoadAndStoreTest extends AbstractConnectivityParamsLoadAndStoreTest { + public TracTracConnectivityParamsLoadAndStoreTest() throws UnknownHostException, MongoException { + super(); + } + + @Test + public void testStoreAndLoadSimpleTracTracParams() throws MalformedURLException, URISyntaxException { + // set up + final boolean trackWind = true; + final boolean correctWindDirectionByMagneticDeclination = true; + final URL paramURL = new URL("http://tractrac.com/some/url"); + final URI storedURI = new URI("live://tractrac.com/storedURI"); + final URI courseDesignUpdateURI = new URI("https://skitrac.dk/reverse/update"); + final TimePoint startOfTracking = MillisecondsTimePoint.now(); + final TimePoint endOfTracking = startOfTracking.plus(1000); + final long delayToLiveInMillis = 3000; + final Duration offsetToStartTimeOfSimulatedRace = Duration.ONE_MINUTE; + final boolean useInternalMarkPassingAlgorithm = false; + final String tracTracUsername = "user"; + final String tracTracPassword = "pass"; + final String raceStatus = (String) TracTracConnectionConstants.REPLAY_STATUS; + final String raceVisibility = (String) TracTracConnectionConstants.REPLAY_VISIBILITY; + final RaceTrackingConnectivityParameters tracTracParams = new RaceTrackingConnectivityParametersImpl( + paramURL, /* live URI */ null, storedURI, courseDesignUpdateURI, startOfTracking, endOfTracking, + delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, + /* raceLogStore */ null, /* regattaLogStore */ null, DomainFactory.INSTANCE, tracTracUsername, tracTracPassword, + raceStatus, raceVisibility, trackWind, correctWindDirectionByMagneticDeclination, /* preferReplayIfAvailable */ false); + // store + mongoObjectFactory.addConnectivityParametersForRaceToRestore(tracTracParams); + // load + final Set connectivityParametersForRacesToRestore = new HashSet<>(); + domainObjectFactory.loadConnectivityParametersForRacesToRestore(params->connectivityParametersForRacesToRestore.add(params)); + // compare + assertEquals(1, Util.size(connectivityParametersForRacesToRestore)); + final RaceTrackingConnectivityParameters paramsReadFromDB = connectivityParametersForRacesToRestore.iterator().next(); + assertTrue(paramsReadFromDB instanceof RaceTrackingConnectivityParametersImpl); + RaceTrackingConnectivityParametersImpl tracTracParamsReadFromDB = (RaceTrackingConnectivityParametersImpl) paramsReadFromDB; + assertEquals(delayToLiveInMillis, tracTracParamsReadFromDB.getDelayToLiveInMillis()); + assertEquals(paramURL, tracTracParamsReadFromDB.getParamURL()); + assertEquals(storedURI, tracTracParamsReadFromDB.getStoredURI()); + assertEquals(courseDesignUpdateURI, tracTracParamsReadFromDB.getCourseDesignUpdateURI()); + assertEquals(startOfTracking, tracTracParamsReadFromDB.getStartOfTracking()); + assertEquals(endOfTracking, tracTracParamsReadFromDB.getEndOfTracking()); + assertEquals(offsetToStartTimeOfSimulatedRace, tracTracParamsReadFromDB.getOffsetToStartTimeOfSimulatedRace()); + assertEquals(useInternalMarkPassingAlgorithm, tracTracParamsReadFromDB.isUseInternalMarkPassingAlgorithm()); + assertEquals(tracTracUsername, tracTracParamsReadFromDB.getTracTracUsername()); + assertEquals(tracTracPassword, tracTracParamsReadFromDB.getTracTracPassword()); + assertEquals(raceStatus, tracTracParamsReadFromDB.getRaceStatus()); + assertEquals(raceVisibility, tracTracParamsReadFromDB.getRaceVisibility()); + assertEquals(tracTracParams.getTrackerID(), tracTracParamsReadFromDB.getTrackerID()); + assertEquals(tracTracParams.isTrackWind(), tracTracParamsReadFromDB.isTrackWind()); + assertEquals(tracTracParams.isCorrectWindDirectionByMagneticDeclination(), tracTracParamsReadFromDB.isCorrectWindDirectionByMagneticDeclination()); + // remove again + mongoObjectFactory.removeConnectivityParametersForRaceToRestore(tracTracParams); + final Set connectivityParametersForRacesToRestore2 = new HashSet<>(); + domainObjectFactory.loadConnectivityParametersForRacesToRestore(params->connectivityParametersForRacesToRestore2.add(params)); + assertTrue(connectivityParametersForRacesToRestore2.isEmpty()); + } +} diff --git a/java/com.sap.sailing.polars.datamining/resources/stringmessages/XLF/Polars_StringMessages_ja.properties.xlf b/java/com.sap.sailing.polars.datamining/resources/stringmessages/XLF/Polars_StringMessages_ja.properties.xlf index 47fa99e5b1a..6585b20869f 100644 --- a/java/com.sap.sailing.polars.datamining/resources/stringmessages/XLF/Polars_StringMessages_ja.properties.xlf +++ b/java/com.sap.sailing.polars.datamining/resources/stringmessages/XLF/Polars_StringMessages_ja.properties.xlf @@ -1,6 +1,6 @@ - +
        H4sIAAAAAAAAAF2PTQrCQAyF93OKXEH32TiiFBSGunAdOrEObTPQaaHHd/6s4Cbv4yW8JMaPNOs3OTmgSRyU2a0jnqgbWCzUVksdaz+uk+AP1Y37RixvGAEylYwzLVRCIWExw3fN1TwubsMoEFU9ndiWpGdMBBmz2QTt5eUsy4I7QR66M4V15ika8TQZMBVVb67b/j74ALSew+fwAAAA diff --git a/java/com.sap.sailing.polars.datamining/resources/stringmessages/XLF/Polars_StringMessages_ru.properties.xlf b/java/com.sap.sailing.polars.datamining/resources/stringmessages/XLF/Polars_StringMessages_ru.properties.xlf index 6ae3f0fcd32..3a498d58122 100644 --- a/java/com.sap.sailing.polars.datamining/resources/stringmessages/XLF/Polars_StringMessages_ru.properties.xlf +++ b/java/com.sap.sailing.polars.datamining/resources/stringmessages/XLF/Polars_StringMessages_ru.properties.xlf @@ -1,6 +1,6 @@ - +
        H4sIAAAAAAAAAF2PTQrCQAyF93OKXEH32TiiFBSGunAdOrEObTPQaaHHd/6s4Cbv4yW8JMaPNOs3OTmgSRyU2a0jnqgbWCzUVksdaz+uk+AP1Y37RixvGAEylYwzLVRCIWExw3fN1TwubsMoEFU9ndiWpGdMBBmz2QTt5eUsy4I7QR66M4V15ika8TQZMBVVb67b/j74ALSew+fwAAAA diff --git a/java/com.sap.sailing.polars.datamining/resources/stringmessages/XLF/Polars_StringMessages_zh_CN.properties.xlf b/java/com.sap.sailing.polars.datamining/resources/stringmessages/XLF/Polars_StringMessages_zh_CN.properties.xlf index 67bc6dafac7..4455aed7ee5 100644 --- a/java/com.sap.sailing.polars.datamining/resources/stringmessages/XLF/Polars_StringMessages_zh_CN.properties.xlf +++ b/java/com.sap.sailing.polars.datamining/resources/stringmessages/XLF/Polars_StringMessages_zh_CN.properties.xlf @@ -1,6 +1,6 @@ - +
        H4sIAAAAAAAAAF2PTQrCQAyF93OKXEH32TiiFBSGunAdOrEObTPQaaHHd/6s4Cbv4yW8JMaPNOs3OTmgSRyU2a0jnqgbWCzUVksdaz+uk+AP1Y37RixvGAEylYwzLVRCIWExw3fN1TwubsMoEFU9ndiWpGdMBBmz2QTt5eUsy4I7QR66M4V15ika8TQZMBVVb67b/j74ALSew+fwAAAA diff --git a/java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF index 6aef689fd45..b3f852001cf 100644 --- a/java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF @@ -9,7 +9,7 @@ Fragment-Host: com.sap.sailing.polars Require-Bundle: org.hamcrest;bundle-version="1.1.0", org.hamcrest.core;bundle-version="1.1.0", org.mockito.mockito-core;bundle-version="1.10.14", - org.junit4;bundle-version="4.8.2", + org.junit;bundle-version="4.8.2", com.sap.sailing.domain.test, com.sap.sse.common, com.sap.sailing.domain.tractracadapter, diff --git a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF index deed64883ce..dce9c6c27d8 100644 --- a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF @@ -42,9 +42,9 @@ Import-Package: javax.ws.rs;version="1.1.1", javax.ws.rs.core;version="1.1.1", org.apache.http, - org.apache.http.client, - org.apache.http.client.methods;version="4.2.6", - org.apache.http.impl.client;version="4.2.6", + org.apache.http.client;version="[4.2.6,4.3.0)", + org.apache.http.client.methods;version="[4.2.6,4.3.0)", + org.apache.http.impl.client;version="[4.2.6,4.3.0)", org.json.simple, org.json.simple.parser Web-ContextPath: /polars diff --git a/java/com.sap.sailing.polars/build.properties b/java/com.sap.sailing.polars/build.properties index f11210d3f2c..a4af5b4a1ef 100644 --- a/java/com.sap.sailing.polars/build.properties +++ b/java/com.sap.sailing.polars/build.properties @@ -2,4 +2,5 @@ source.. = src/,\ resources/ output.. = bin/ bin.includes = META-INF/,\ - . + .,\ + WEB-INF/ diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/Activator.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/Activator.java index c235c06fea5..73fc24cda67 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/Activator.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/Activator.java @@ -34,24 +34,33 @@ public class Activator implements BundleActivator { public void start(BundleContext context) throws Exception { logger.info("Registering PolarDataService"); PolarDataServiceImpl service = new PolarDataServiceImpl(); - registrations.add(context.registerService(PolarDataService.class, service, null)); + final ServiceRegistration polarDataServiceRegistration = context.registerService(PolarDataService.class, service, null); + registrations.add(polarDataServiceRegistration); final Dictionary replicableServiceProperties = new Hashtable<>(); replicableServiceProperties.put(Replicable.OSGi_Service_Registry_ID_Property_Name, service.getId().toString()); registrations.add(context.registerService(Replicable.class.getName(), service, replicableServiceProperties)); - final String polarDataSourceURL = System.getProperty(POLAR_DATA_SOURCE_URL_PROPERTY_NAME); if (polarDataSourceURL != null && !polarDataSourceURL.isEmpty()) { - waitForRacingEventServiceToObtainDomainFactory(polarDataSourceURL, service); + waitForRacingEventServiceToObtainDomainFactory(polarDataSourceURL, service, context, polarDataServiceRegistration); } } /** * Spawns a daemon thread that waits for the domain factory to be registered with the {@link PolarDataService}, then - * runs the polar data import from the given URL. The domain factory is required to resolve boat classes during - * de-serialization. + * unregisters the service from the OSGi registry because it will temporarily become unusable, runs the polar data + * import from the given URL and registers the service again, adding the service registration object to the set of + * {@link #registrations}. The domain factory is required to resolve boat classes during de-serialization. + * + * @param polarDataServiceRegistration + * used to remove the service registration temporarily while updating the service by a remote import + * @param registerPolarServiceCallback + * called when the polar data has successfully been obtained; expected to register the polar service as + * {@link Replicable} and as the {@link PolarDataService} with the OSGi registry */ - private void waitForRacingEventServiceToObtainDomainFactory(final String polarDataSourceURL, final ReplicablePolarService polarService) { - final Thread t = new Thread(()->{ + private void waitForRacingEventServiceToObtainDomainFactory(final String polarDataSourceURL, + final ReplicablePolarService polarService, final BundleContext context, + ServiceRegistration polarDataServiceRegistration) { + final Thread t = new Thread(() -> { try { Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); // ensure that classpath:... Shiro ini files are resolved properly logger.info("Waiting for domain factory to be registered with PolarService..."); @@ -59,7 +68,9 @@ public class Activator implements BundleActivator { polarService.runWithDomainFactory(domainFactory -> { PolarDataClient polarDataClient = new PolarDataClient(polarDataSourceURL, polarService); try { + polarDataServiceRegistration.unregister(); polarDataClient.updatePolarDataRegressions(); + registrations.add(context.registerService(PolarDataService.class, polarService, null)); } catch (Exception e) { logger.log(Level.SEVERE, "Exception while trying to import polar data from "+polarDataSourceURL, e); } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index dcf03018f82..081c585690b 100755 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -379,13 +379,6 @@ public class PolarDataServiceImpl implements ReplicablePolarService { consumer.accept(myDomainFactory); } - @Override - public void unregisterDomainFactory(DomainFactory domainFactory) { - if (this.domainFactory == domainFactory) { - this.domainFactory = null; - } - } - public Map getCubicRegressionsPerCourse() { return polarDataMiner.getCubicRegressionPerCourseProcessor().getRegressions(); } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/jaxrs/client/PolarDataClient.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/jaxrs/client/PolarDataClient.java index 4d1ba927a71..95aba66bf40 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/jaxrs/client/PolarDataClient.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/jaxrs/client/PolarDataClient.java @@ -29,7 +29,7 @@ public class PolarDataClient { private static final String RESOURCE = "polars/api/polar_data"; - private final ReplicablePolarService polarDataServiceImpl; + private final ReplicablePolarService polarDataService; private final String polarDataSourceURL; /** @@ -40,7 +40,7 @@ public class PolarDataClient { * {@link PolarDataServiceImpl} service to work with */ public PolarDataClient(String polarDataSourceURL, ReplicablePolarService polarDataService) { - this.polarDataServiceImpl = polarDataService; + this.polarDataService = polarDataService; this.polarDataSourceURL = polarDataSourceURL; } @@ -52,15 +52,15 @@ public class PolarDataClient { try { logger.log(Level.INFO, "Loading polar regression data from remote server " + polarDataSourceURL); final InputStream inputStream = getContentFromResponse(); - polarDataServiceImpl.clearReplicaState(); - polarDataServiceImpl.initiallyFillFrom(inputStream); + polarDataService.clearReplicaState(); + polarDataService.initiallyFillFrom(inputStream); logger.log(Level.INFO, "Loading polar regression data from remote server " + polarDataSourceURL + " succeeded"); } catch (Exception e) { // Catching ClientProtocolException to indicate problems with HTTP protocol logger.log(Level.WARNING, "Failed to load polar regression data from remote server " + polarDataSourceURL + ", " + e.getMessage()+"; resetting polar data miner"); - polarDataServiceImpl.resetState(); + polarDataService.resetState(); } } diff --git a/java/com.sap.sailing.sailwave.resultimport.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.sailwave.resultimport.test/META-INF/MANIFEST.MF index 90e311e00bb..fc3dcff993e 100644 --- a/java/com.sap.sailing.sailwave.resultimport.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.sailwave.resultimport.test/META-INF/MANIFEST.MF @@ -8,5 +8,5 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: org.osgi.framework;version="1.5.0" Bundle-ActivationPolicy: lazy Fragment-Host: com.sap.sailing.sailwave.resultimport -Require-Bundle: org.junit4;bundle-version="4.8.2", +Require-Bundle: org.junit;bundle-version="4.8.2", com.sap.sse.common diff --git a/java/com.sap.sailing.selenium.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.selenium.test/META-INF/MANIFEST.MF index 5d967ca8b4f..c3d45e5a170 100644 --- a/java/com.sap.sailing.selenium.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.selenium.test/META-INF/MANIFEST.MF @@ -6,7 +6,7 @@ Bundle-Version: 1.0.0.qualifier Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Require-Bundle: org.hamcrest;bundle-version="1.1.0", - org.junit4;bundle-version="4.8.2", + org.junit;bundle-version="4.8.2", org.openqa.selenium.osgi;bundle-version="2.40.0", com.sap.sse.mongodb;bundle-version="1.0.0", org.mongodb.mongo-java-driver;bundle-version="2.13.0", diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/TargetTimeInfoDeserializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/TargetTimeInfoDeserializer.java new file mode 100755 index 00000000000..0588510e38d --- /dev/null +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/TargetTimeInfoDeserializer.java @@ -0,0 +1,51 @@ +package com.sap.sailing.server.gateway.deserialization.impl; + +import java.util.ArrayList; +import java.util.List; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.Distance; +import com.sap.sailing.domain.common.LegType; +import com.sap.sailing.domain.common.TargetTimeInfo; +import com.sap.sailing.domain.common.TargetTimeInfo.LegTargetTimeInfo; +import com.sap.sailing.domain.common.Wind; +import com.sap.sailing.domain.common.impl.DegreeBearingImpl; +import com.sap.sailing.domain.common.impl.MeterDistance; +import com.sap.sailing.domain.common.impl.TargetTimeInfoImpl; +import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; +import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; +import com.sap.sailing.server.gateway.serialization.impl.TargetTimeInfoSerializer; +import com.sap.sse.common.Duration; +import com.sap.sse.common.TimePoint; +import com.sap.sse.common.impl.MillisecondsDurationImpl; +import com.sap.sse.common.impl.MillisecondsTimePoint; + +public class TargetTimeInfoDeserializer implements JsonDeserializer { + final JsonDeserializer windDeserializer; + + public TargetTimeInfoDeserializer(JsonDeserializer windDeserializer) { + super(); + this.windDeserializer = windDeserializer; + } + + @Override + public TargetTimeInfo deserialize(JSONObject object) throws JsonDeserializationException { + final List legInfos = new ArrayList<>(); + final JSONArray legsAsJson = (JSONArray) object.get(TargetTimeInfoSerializer.LEGS); + for (final Object legAsObject : legsAsJson) { + final JSONObject legAsJson = (JSONObject) legAsObject; + final Duration expectedDuration = new MillisecondsDurationImpl(((Number) legAsJson.get(TargetTimeInfoSerializer.LEG_DURATION_MILLIS)).longValue()); + final TimePoint expectedStartTime = new MillisecondsTimePoint(((Number) legAsJson.get(TargetTimeInfoSerializer.LEG_START_TIME_MILLIS)).longValue()); + final Bearing legBearing = new DegreeBearingImpl(((Number) legAsJson.get(TargetTimeInfoSerializer.LEG_BEARING_DEGREES)).doubleValue()); + final Distance legDistance = new MeterDistance(((Number) legAsJson.get(TargetTimeInfoSerializer.LEG_DISTANCE_METERS)).doubleValue()); + final LegType legType = LegType.valueOf((String) legAsJson.get(TargetTimeInfoSerializer.LEG_TYPE)); + final Wind legWind = windDeserializer.deserialize((JSONObject) legAsJson.get(TargetTimeInfoSerializer.LEG_WIND)); + final LegTargetTimeInfo legInfo = new TargetTimeInfoImpl.LegTargetTimeInfoImpl(legDistance, legWind, legBearing, expectedDuration, expectedStartTime, legType); + legInfos.add(legInfo); + } + return new TargetTimeInfoImpl(legInfos); + } +} diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racegroup/impl/RaceCellDeserializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racegroup/impl/RaceCellDeserializer.java index 767e41dc1f9..891a2270d19 100644 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racegroup/impl/RaceCellDeserializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racegroup/impl/RaceCellDeserializer.java @@ -5,6 +5,7 @@ import org.json.simple.JSONObject; import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.base.racegroup.RaceCell; import com.sap.sailing.domain.base.racegroup.impl.RaceCellImpl; +import com.sap.sailing.domain.common.TargetTimeInfo; import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.Helpers; @@ -12,10 +13,12 @@ import com.sap.sailing.server.gateway.serialization.racegroup.impl.RaceCellJsonS public class RaceCellDeserializer implements JsonDeserializer { - private JsonDeserializer logDeserializer; + private final JsonDeserializer logDeserializer; + private final JsonDeserializer targetTimeDeserializer; - public RaceCellDeserializer(JsonDeserializer logDeserializer) { + public RaceCellDeserializer(JsonDeserializer logDeserializer, JsonDeserializer targetTimeDeserializer) { this.logDeserializer = logDeserializer; + this.targetTimeDeserializer = targetTimeDeserializer; } public RaceCell deserialize(JSONObject object) @@ -25,9 +28,11 @@ public class RaceCellDeserializer implements JsonDeserializer { final double factor = factorJson == null ? 1 : ((Number) factorJson).doubleValue(); final Double explicitFactor = (Double) object.get(RaceCellJsonSerializer.FIELD_EXPLICIT_FACTOR); final Number zeroBasedIndexInFleet = (Number) object.get(RaceCellJsonSerializer.FIELD_ZERO_BASED_INDEX_IN_FLEET); + final JSONObject targetTimeInfoJson = (JSONObject) object.get(RaceCellJsonSerializer.FIELD_TARGET_TIME_INFO); + final TargetTimeInfo targetTime = targetTimeInfoJson == null ? null : targetTimeDeserializer.deserialize(targetTimeInfoJson); JSONObject logJson = Helpers.getNestedObjectSafe(object, RaceCellJsonSerializer.FIELD_RACE_LOG); RaceLog log = logDeserializer.deserialize(logJson); - return new RaceCellImpl(name, log, factor, explicitFactor, zeroBasedIndexInFleet == null ? -1 : zeroBasedIndexInFleet.intValue()); + return new RaceCellImpl(name, log, factor, explicitFactor, zeroBasedIndexInFleet == null ? -1 : zeroBasedIndexInFleet.intValue(), targetTime); } } diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racegroup/impl/RaceGroupDeserializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racegroup/impl/RaceGroupDeserializer.java index 05f3b44e431..20eb3745964 100644 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racegroup/impl/RaceGroupDeserializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racegroup/impl/RaceGroupDeserializer.java @@ -18,6 +18,9 @@ import com.sap.sailing.server.gateway.deserialization.impl.BoatClassJsonDeserial import com.sap.sailing.server.gateway.deserialization.impl.ColorDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.FleetDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.Helpers; +import com.sap.sailing.server.gateway.deserialization.impl.PositionJsonDeserializer; +import com.sap.sailing.server.gateway.deserialization.impl.TargetTimeInfoDeserializer; +import com.sap.sailing.server.gateway.deserialization.impl.WindJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.racelog.impl.RaceLogDeserializer; import com.sap.sailing.server.gateway.deserialization.racelog.impl.RaceLogEventDeserializer; import com.sap.sailing.server.gateway.serialization.racegroup.impl.RaceGroupJsonSerializer; @@ -33,7 +36,8 @@ public class RaceGroupDeserializer implements JsonDeserializer { JsonDeserializer proceduresDeserializer) { return new RaceGroupDeserializer(new BoatClassJsonDeserializer(domainFactory), new SeriesWithRowsDeserializer( new RaceRowDeserializer(new FleetDeserializer(new ColorDeserializer()), new RaceCellDeserializer( - new RaceLogDeserializer(RaceLogEventDeserializer.create(domainFactory))))), + new RaceLogDeserializer(RaceLogEventDeserializer.create(domainFactory)), + new TargetTimeInfoDeserializer(new WindJsonDeserializer(new PositionJsonDeserializer()))))), proceduresDeserializer); } diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/TargetTimeInfoSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/TargetTimeInfoSerializer.java new file mode 100755 index 00000000000..986760ea8d4 --- /dev/null +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/TargetTimeInfoSerializer.java @@ -0,0 +1,51 @@ +package com.sap.sailing.server.gateway.serialization.impl; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import com.sap.sailing.domain.common.TargetTimeInfo; +import com.sap.sailing.domain.common.Wind; +import com.sap.sailing.domain.common.TargetTimeInfo.LegTargetTimeInfo; +import com.sap.sailing.server.gateway.serialization.JsonSerializer; + +public class TargetTimeInfoSerializer implements JsonSerializer { + + public static final String LEGS = "legs"; + public static final String DURATION_MILLIS = "durationMillis"; + public static final String LEG_START_TIME_MILLIS = "legStartMillis"; + public static final String LEG_DURATION_MILLIS = "legDurationMillis"; + public static final String LEG_DISTANCE_METERS = "legDistanceMeters"; + public static final String LEG_BEARING_DEGREES = "legBearingDegrees"; + public static final String LEG_TRUE_WIND_ANGLE_TO_LEG_DEGREES = "legTrueWindAngleToLegDegrees"; + public static final String LEG_WIND = "legWind"; + public static final String LEG_TYPE = "legType"; + + private final JsonSerializer windSerializer; + + public TargetTimeInfoSerializer(JsonSerializer windSerializer) { + super(); + this.windSerializer = windSerializer; + } + + @Override + public JSONObject serialize(TargetTimeInfo object) { + final JSONArray legsAsJson = new JSONArray(); + final JSONObject result = new JSONObject(); + // redundant, but possibly useful to clients other than the deserializer: + result.put(DURATION_MILLIS, object.getExpectedDuration().asMillis()); + result.put(LEGS, legsAsJson); + for (final LegTargetTimeInfo legInfo : object.getLegTargetTimes()) { + final JSONObject legAsJson = new JSONObject(); + legAsJson.put(LEG_START_TIME_MILLIS, legInfo.getExpectedStartTimePoint().asMillis()); + legAsJson.put(LEG_DURATION_MILLIS, legInfo.getExpectedDuration().asMillis()); + legAsJson.put(LEG_DISTANCE_METERS, legInfo.getDistance().getMeters()); + legAsJson.put(LEG_BEARING_DEGREES, legInfo.getLegBearing().getDegrees()); + // redundant, but possibly useful to clients other than the deserializer: + legAsJson.put(LEG_TRUE_WIND_ANGLE_TO_LEG_DEGREES, legInfo.getTrueWindAngleToLeg().getDegrees()); + legAsJson.put(LEG_TYPE, legInfo.getLegType().name()); + legAsJson.put(LEG_WIND, windSerializer.serialize(legInfo.getWind())); + legsAsJson.add(legAsJson); + } + return result; + } +} diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racegroup/impl/RaceCellJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racegroup/impl/RaceCellJsonSerializer.java index 19240a6b646..6881caa0df7 100644 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racegroup/impl/RaceCellJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racegroup/impl/RaceCellJsonSerializer.java @@ -4,6 +4,7 @@ import org.json.simple.JSONObject; import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.base.racegroup.RaceCell; +import com.sap.sailing.domain.common.TargetTimeInfo; import com.sap.sailing.server.gateway.serialization.JsonSerializer; public class RaceCellJsonSerializer implements JsonSerializer { @@ -13,11 +14,14 @@ public class RaceCellJsonSerializer implements JsonSerializer { public static final String FIELD_FACTOR = "factor"; public static final String FIELD_EXPLICIT_FACTOR = "explicitfactor"; public static final String FIELD_ZERO_BASED_INDEX_IN_FLEET = "zerobasedindexinfleet"; + public static final String FIELD_TARGET_TIME_INFO = "targettimeinfo"; - private JsonSerializer logSerializer; + private final JsonSerializer logSerializer; + private final JsonSerializer targetTimeInfoSerializer; - public RaceCellJsonSerializer(JsonSerializer logSerializer) { + public RaceCellJsonSerializer(JsonSerializer logSerializer, JsonSerializer targetTimeInfoSerializer) { this.logSerializer = logSerializer; + this.targetTimeInfoSerializer = targetTimeInfoSerializer; } @Override @@ -28,6 +32,8 @@ public class RaceCellJsonSerializer implements JsonSerializer { result.put(FIELD_EXPLICIT_FACTOR, object.getExplicitFactor()); result.put(FIELD_ZERO_BASED_INDEX_IN_FLEET, object.getZeroBasedIndexInFleet()); result.put(FIELD_RACE_LOG, logSerializer.serialize(object.getRaceLog())); + final TargetTimeInfo targetTime = object.getTargetTime(); + result.put(FIELD_TARGET_TIME_INFO, targetTime==null?null:targetTimeInfoSerializer.serialize(targetTime)); return result; } diff --git a/java/com.sap.sailing.server.gateway.serialization.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.server.gateway.serialization.test/META-INF/MANIFEST.MF index 8a479508c89..25dfbf1b299 100644 --- a/java/com.sap.sailing.server.gateway.serialization.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.server.gateway.serialization.test/META-INF/MANIFEST.MF @@ -9,7 +9,7 @@ Fragment-Host: com.sap.sailing.server.gateway.serialization Require-Bundle: com.sap.sailing.domain;bundle-version="1.0.0", com.sap.sailing.domain.common;bundle-version="1.0.0", org.hamcrest;bundle-version="1.1.0", - org.junit4;bundle-version="4.8.2", + org.junit;bundle-version="4.8.2", com.sap.sailing.domain.shared.android, org.json.simple, org.mockito.mockito-core;bundle-version="1.9.5", diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/racelog/tracking/impl/GPSFixJsonServiceFinder.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/racelog/tracking/impl/GPSFixJsonServiceFinder.java deleted file mode 100644 index e755b7bcda9..00000000000 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/racelog/tracking/impl/GPSFixJsonServiceFinder.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.sap.sailing.server.gateway.serialization.racelog.tracking.impl; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -import com.sap.sailing.domain.common.tracking.GPSFix; -import com.sap.sailing.domain.common.tracking.GPSFixMoving; -import com.sap.sailing.server.gateway.deserialization.impl.GPSFixJsonDeserializer; -import com.sap.sailing.server.gateway.deserialization.impl.GPSFixMovingJsonDeserializer; -import com.sap.sailing.server.gateway.serialization.impl.GPSFixJsonSerializer; -import com.sap.sailing.server.gateway.serialization.impl.GPSFixMovingJsonSerializer; -import com.sap.sailing.server.gateway.serialization.racelog.tracking.GPSFixJsonHandler; -import com.sap.sse.common.NoCorrespondingServiceRegisteredException; -import com.sap.sse.common.TypeBasedServiceFinder; - -public class GPSFixJsonServiceFinder implements TypeBasedServiceFinder { - private final GPSFixJsonHandler gpsFixHandler = new GPSFixJsonHandlerImpl(new GPSFixJsonDeserializer(), - new GPSFixJsonSerializer()); - private final GPSFixJsonHandler gpsFixMovingHandler = new GPSFixJsonHandlerImpl( - new GPSFixMovingJsonDeserializer(), new GPSFixMovingJsonSerializer()); - - @Override - public GPSFixJsonHandler findService(String type) throws NoCorrespondingServiceRegisteredException { - if (type.equals(GPSFixJsonDeserializer.TYPE)) { - return gpsFixHandler; - } else if (type.equals(GPSFixMovingJsonDeserializer.TYPE)) { - return gpsFixMovingHandler; - } - throw new NoCorrespondingServiceRegisteredException( - "Only handlers for GPSFix, GPSFixMoving, GPSFixNmeaDTO, GPSFixMovingNmeaDTO are registered", type, - GPSFixJsonHandler.class.getSimpleName()); - } - - @Override - public void setFallbackService(GPSFixJsonHandler fallback) { - - } - - @Override - public Set findAllServices() { - return new HashSet(Arrays.asList(gpsFixHandler, gpsFixMovingHandler)); - } - -} diff --git a/java/com.sap.sailing.server.gateway.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.server.gateway.test/META-INF/MANIFEST.MF index 3b60fe6bb9a..d0d8042ccda 100644 --- a/java/com.sap.sailing.server.gateway.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.server.gateway.test/META-INF/MANIFEST.MF @@ -10,7 +10,7 @@ Require-Bundle: org.mockito.mockito-core;bundle-version="1.9.5", com.sap.sailing.domain.test, org.hamcrest;bundle-version="1.1.0", org.objenesis;bundle-version="1.3.0", - org.junit4;bundle-version="4.8.2", + org.junit;bundle-version="4.8.2", com.sap.sailing.domain.racelogtrackingadapter.testsupport, com.sap.sse.common, com.sun.jersey, diff --git a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/AbstractJaxRsApiTest.java b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/AbstractJaxRsApiTest.java index 4af70af38be..e1c15e085d3 100644 --- a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/AbstractJaxRsApiTest.java +++ b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/AbstractJaxRsApiTest.java @@ -40,8 +40,7 @@ public abstract class AbstractJaxRsApiTest { public void setUp() { service = MongoDBConfiguration.getDefaultTestConfiguration().getService(); service.getDB().dropDatabase(); - - racingEventService = new RacingEventServiceImpl(true, new MockSmartphoneUuidServiceFinderFactory()); + racingEventService = new RacingEventServiceImpl(/* clearPersistentCompetitorStore */ true, new MockSmartphoneUuidServiceFinderFactory(), /* restoreTrackedRaces */ false); } protected T spyResource(T resource) { diff --git a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/GPSFixesResourceTest.java b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/GPSFixesResourceTest.java index 40017a17f70..f2694b49954 100644 --- a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/GPSFixesResourceTest.java +++ b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/GPSFixesResourceTest.java @@ -45,7 +45,7 @@ public class GPSFixesResourceTest { @Before public void setup() { - service = new RacingEventServiceImpl(true, new MockSmartphoneUuidServiceFinderFactory()); + service = new RacingEventServiceImpl(/* clearPersistentCompetitorStore */ true, new MockSmartphoneUuidServiceFinderFactory(), /* restoreTrackedRaces */ false); service.getMongoObjectFactory().getDatabase().dropDatabase(); } diff --git a/java/com.sap.sailing.server.gateway/WEB-INF/web.xml b/java/com.sap.sailing.server.gateway/WEB-INF/web.xml index b8795dece22..9752cc0f343 100644 --- a/java/com.sap.sailing.server.gateway/WEB-INF/web.xml +++ b/java/com.sap.sailing.server.gateway/WEB-INF/web.xml @@ -139,14 +139,6 @@ WindStatusHtmlServlet /windStatus - - WindStatusJsonServlet - com.sap.sailing.server.gateway.impl.WindStatusJsonServlet - - - WindStatusJsonServlet - /windStatusJson - TrackFilesExportPostServlet com.sap.sailing.server.gateway.trackfiles.impl.TrackFilesExportPostServlet diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/WindStatusJsonServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/WindStatusJsonServlet.java deleted file mode 100644 index 856f1abb4b6..00000000000 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/WindStatusJsonServlet.java +++ /dev/null @@ -1,140 +0,0 @@ -package com.sap.sailing.server.gateway.impl; - -import java.io.IOException; -import java.io.PrintWriter; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Deque; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; - -import com.sap.sailing.domain.igtimiadapter.BulkFixReceiver; -import com.sap.sailing.domain.igtimiadapter.IgtimiWindListener; -import com.sap.sse.common.Duration; -import com.sap.sse.common.TimePoint; -import com.sap.sse.common.impl.MillisecondsTimePoint; - -/** - * Shows the state of wind receivers regardless of them being attached to a race. - * Currently Igtimi is supported. - * - * @author Simon Marcel Pamies - * - */ -public class WindStatusJsonServlet extends WindStatusServlet implements IgtimiWindListener, BulkFixReceiver { - - private static final long serialVersionUID = 6091476602985063675L; - private static final String PARAM_SHOW_DUMMY_DATA = "showDummyData"; - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - String reinitializeWindReceiverParameter = req.getParameter(PARAM_RELOAD_WIND_RECEIVER); - String showDummyDataAsString = req.getParameter(PARAM_SHOW_DUMMY_DATA); - resp.setContentType("application/json"); - PrintWriter out = resp.getWriter(); - boolean showDummyData = showDummyDataAsString != null && showDummyDataAsString.equalsIgnoreCase("true"); - if (showDummyData) { - out.print(generateDummyData().toJSONString()); - } else { - initializeWindReceiver(reinitializeWindReceiverParameter != null && reinitializeWindReceiverParameter.equalsIgnoreCase("true")); - JSONObject windInformation = new JSONObject(); - windInformation.put("windFixesReceivedSoFar", getIgtimiMessagesRawCount()+getLastExpeditionMessages().size()); - JSONArray windFixesIgtimi = new JSONArray(); - if (getLastIgtimiMessages() != null && !getLastIgtimiMessages().isEmpty()) { - for(Entry> deviceAndMessagesList: getLastIgtimiMessages().entrySet()) { - final Deque copyOfLastIgtimiMessages; - synchronized (deviceAndMessagesList.getValue()) { - copyOfLastIgtimiMessages = new ArrayDeque<>(deviceAndMessagesList.getValue()); - } - if(copyOfLastIgtimiMessages.size() > 0) { - TimePoint latestTimePoint = copyOfLastIgtimiMessages.peek().getWind().getTimePoint(); - long lastFixDiffInMs = System.currentTimeMillis() - latestTimePoint.asMillis(); - windInformation.put("lastWindFixMillisecondsAgo", lastFixDiffInMs); - } - Iterator messageIt = copyOfLastIgtimiMessages.iterator(); - while (messageIt.hasNext()){ - JSONObject windFix = new JSONObject(); - windFix.put("sensorId", deviceAndMessagesList.getKey()); - IgtimiMessageInfo message = messageIt.next(); - windFix.put("sensorPositionLatitude", message.getWind().getPosition().getLatDeg()); - windFix.put("sensorPositionLongitude", message.getWind().getPosition().getLngDeg()); - windFix.put("windFromInDegrees", message.getWind().getFrom().getDegrees()); - windFix.put("windToInDegrees", message.getWind().getFrom().reverse().getDegrees()); - windFix.put("windSpeedInMetersPerSecond", message.getWind().getMetersPerSecond()); - windFix.put("windSpeedInStatuteMilesPerHour", message.getWind().getStatuteMilesPerHour()); - windFix.put("windSpeedInKilometersPerHour", message.getWind().getKilometersPerHour()); - windFix.put("windSpeedInKnots", message.getWind().getKnots()); - windFix.put("measurementTimePointAsMillis", message.getWind().getTimePoint().asMillis()); - windFixesIgtimi.add(windFix); - } - } - } - windInformation.put("windFixes", windFixesIgtimi); - out.print(windInformation.toJSONString()); - } - out.close(); - } - - @SuppressWarnings("serial") - private JSONObject generateDummyData() { - List> dummyWindFixes = new ArrayList>(); - dummyWindFixes.add(new HashMap() {{ - put("sensorId", "DD-EE-HG"); - put("sensorPositionLatitude", 49.2511599999); - put("sensorPositionLongitude", 8.624829999); - put("windFromInDegrees", 90.0); - put("windToInDegrees", 270.0); - put("windSpeedInMetersPerSecond", 6.944444); - put("windSpeedInKilometersPerHour", 25.0); - put("windSpeedInStatuteMilesPerHour", 15.43); - put("windSpeedInKnots", 13.498920086); - put("measurementTimePointAsMillis", MillisecondsTimePoint.now().minus(Duration.ONE_SECOND.times(4)).asMillis()); - }}); - dummyWindFixes.add(new HashMap() {{ - put("sensorId", "DD-AA-EF"); - put("sensorPositionLatitude", 49.244489999); - put("sensorPositionLongitude", 8.6253399999); - put("windFromInDegrees", 100.0); - put("windToInDegrees", 280.0); - put("windSpeedInMetersPerSecond", 6.944444); - put("windSpeedInKilometersPerHour", 25.0); - put("windSpeedInStatuteMilesPerHour", 15.43); - put("windSpeedInKnots", 13.498920086); - put("measurementTimePointAsMillis", MillisecondsTimePoint.now().minus(Duration.ONE_SECOND.times(5)).asMillis()); - }}); - dummyWindFixes.add(new HashMap() {{ - put("sensorId", "DD-UU-KL"); - put("sensorPositionLatitude", 49.243539999); - put("sensorPositionLongitude", 8.6146099999); - put("windFromInDegrees", 85.0); - put("windToInDegrees", 265.0); - put("windSpeedInMetersPerSecond", 6.944444); - put("windSpeedInKilometersPerHour", 25.0); - put("windSpeedInStatuteMilesPerHour", 15.43); - put("windSpeedInKnots", 13.498920086); - put("measurementTimePointAsMillis", MillisecondsTimePoint.now().minus(Duration.ONE_SECOND.times(6)).asMillis()); - }}); - JSONObject windInformation = new JSONObject(); - windInformation.put("windFixesReceivedSoFar", 3); - JSONArray windFixesIgtimi = new JSONArray(); - windInformation.put("lastWindFixMillisecondsAgo", 1239876); - for (Map windDummyFix : dummyWindFixes) { - JSONObject windFix = new JSONObject(); - windFix.putAll(windDummyFix); - windFixesIgtimi.add(windFix); - } - windInformation.put("windFixes", windFixesIgtimi); - return windInformation; - } - -} diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupFactory.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupFactory.java index 602a876e950..71b5efa3781 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupFactory.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupFactory.java @@ -6,6 +6,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.logging.Logger; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.CourseArea; @@ -25,14 +26,21 @@ import com.sap.sailing.domain.base.racegroup.impl.RaceGroupImpl; import com.sap.sailing.domain.base.racegroup.impl.RaceRowImpl; import com.sap.sailing.domain.base.racegroup.impl.SeriesWithRowsImpl; import com.sap.sailing.domain.common.LeaderboardNameConstants; +import com.sap.sailing.domain.common.NoWindException; +import com.sap.sailing.domain.common.TargetTimeInfo; import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; +import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sse.common.TimePoint; +import com.sap.sse.common.impl.MillisecondsTimePoint; /** * Used to convert {@link Leaderboard}s to {@link RaceGroup}s. */ public class RaceGroupFactory { + private static final Logger logger = Logger.getLogger(RaceGroupFactory.class.getName()); /** * Convert given {@link RegattaLeaderboard} to a {@link RaceGroup}. @@ -98,20 +106,32 @@ public class RaceGroupFactory { } /** - * Delivers the race cell in the order of the {@link RaceColumn}s provided in {@code raceColumns}. + * Delivers the race cell in the order of the {@link RaceColumn}s provided in {@code raceColumns}. The + * {@link RaceCell#getTargetTime() target times} are based on an + * {@link TrackedRace#getEstimatedTimeToComplete(TimePoint) estimation} for the current point in time, when the + * request is being made, using this time point to determine the wind speed and direction for each leg. */ private List getCells(String fleetName, List raceColumns, boolean isFirstRaceColumnVirtual) { boolean skippedFirst = false; List cells = new ArrayList<>(); if (raceColumns != null) { int zeroBasedIndexOfRaceInFleet = 0; + final TimePoint now = MillisecondsTimePoint.now(); for (final RaceColumn raceColumn : raceColumns) { if (isFirstRaceColumnVirtual && !skippedFirst) { skippedFirst = true; } else { - Fleet fleet = raceColumn.getFleetByName(fleetName); + final Fleet fleet = raceColumn.getFleetByName(fleetName); + final TrackedRace trackedRace = raceColumn.getTrackedRace(fleet); + TargetTimeInfo targetTime; + try { + targetTime = trackedRace == null ? null : trackedRace.getEstimatedTimeToComplete(now); + } catch (NotEnoughDataHasBeenAddedException | NoWindException e) { + logger.fine(()->"Unable to determine target time for "+trackedRace); + targetTime = null; + } cells.add(new RaceCellImpl(raceColumn.getName(), raceColumn.getRaceLog(fleet), - raceColumn.getFactor(), raceColumn.getExplicitFactor(), zeroBasedIndexOfRaceInFleet++)); + raceColumn.getFactor(), raceColumn.getExplicitFactor(), zeroBasedIndexOfRaceInFleet++, targetTime)); } } } diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupJsonExportServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupJsonExportServlet.java index 91487d6bc84..74048d86294 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupJsonExportServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupJsonExportServlet.java @@ -23,7 +23,10 @@ import com.sap.sailing.server.gateway.serialization.impl.ColorJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.CompetitorJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.CourseAreaJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.FleetJsonSerializer; +import com.sap.sailing.server.gateway.serialization.impl.PositionJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.RegattaConfigurationJsonSerializer; +import com.sap.sailing.server.gateway.serialization.impl.TargetTimeInfoSerializer; +import com.sap.sailing.server.gateway.serialization.impl.WindJsonSerializer; import com.sap.sailing.server.gateway.serialization.racegroup.impl.RaceCellJsonSerializer; import com.sap.sailing.server.gateway.serialization.racegroup.impl.RaceGroupJsonSerializer; import com.sap.sailing.server.gateway.serialization.racegroup.impl.RaceRowJsonSerializer; @@ -95,7 +98,8 @@ public class RaceGroupJsonExportServlet extends AbstractJsonHttpServlet { RegattaConfigurationJsonSerializer.create(), new SeriesWithRowsOfRaceGroupSerializer(new SeriesWithRowsJsonSerializer( new RaceRowsOfSeriesWithRowsSerializer(new RaceRowJsonSerializer(new FleetJsonSerializer( - new ColorJsonSerializer()), new RaceCellJsonSerializer(createRaceLogSerializer(clientUuid))))))); + new ColorJsonSerializer()), new RaceCellJsonSerializer(createRaceLogSerializer(clientUuid), + new TargetTimeInfoSerializer(new WindJsonSerializer(new PositionJsonSerializer())))))))); } diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardGroupsResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardGroupsResource.java index 46bd91cf41c..5c1ead02012 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardGroupsResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardGroupsResource.java @@ -3,10 +3,8 @@ package com.sap.sailing.server.gateway.jaxrs.api; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import javax.ws.rs.GET; import javax.ws.rs.Path; @@ -34,6 +32,7 @@ import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource; import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util; +import com.sap.sse.common.Util.Triple; import com.sap.sse.common.impl.MillisecondsTimePoint; @Path("/v1/leaderboardgroups") @@ -93,7 +92,7 @@ public class LeaderboardGroupsResource extends AbstractSailingServerResource { jsonLeaderboard.put("lastScoringUpdate", null); } - final LinkedHashMap> fleetsBySeriesName = new LinkedHashMap<>(); + final List, Iterable>> seriesNameAndFleetsAndRaceColumnsOfSeries = new ArrayList<>(); final Map medalSeriesNames = new HashMap<>(); if (isRegattaLeaderboard) { RegattaLeaderboard regattaLeaderboard = (RegattaLeaderboard) leaderboard; @@ -103,25 +102,28 @@ public class LeaderboardGroupsResource extends AbstractSailingServerResource { for (final Series series : regatta.getSeries()) { List fleets = new ArrayList<>(); Util.addAll(series.getFleets(), fleets); - fleetsBySeriesName.put(series.getName(), fleets); + seriesNameAndFleetsAndRaceColumnsOfSeries.add(new Triple, Iterable>(series.getName(), fleets, series.getRaceColumns())); medalSeriesNames.put(series.getName(), series.isMedal()); } } else { jsonLeaderboard.put("scoringScheme", leaderboard.getScoringScheme().getType()); jsonLeaderboard.put("regattaName", null); - // write a 'default' series to be conform with our common regatta structure - fleetsBySeriesName.put(LeaderboardNameConstants.DEFAULT_SERIES_NAME, Collections.singleton(leaderboard.getFleet(LeaderboardNameConstants.DEFAULT_FLEET_NAME))); + // write a 'default' series to conform with our common regatta structure + seriesNameAndFleetsAndRaceColumnsOfSeries.add(new Triple, Iterable>( + LeaderboardNameConstants.DEFAULT_SERIES_NAME, + Collections.singleton(leaderboard.getFleet(LeaderboardNameConstants.DEFAULT_FLEET_NAME)), + leaderboard.getRaceColumns())); } JSONArray jsonSeriesEntries = new JSONArray(); jsonLeaderboard.put("series", jsonSeriesEntries); - for (final Entry> e : fleetsBySeriesName.entrySet()) { + for (final Triple, Iterable> e : seriesNameAndFleetsAndRaceColumnsOfSeries) { JSONObject jsonSeries = new JSONObject(); jsonSeriesEntries.add(jsonSeries); - jsonSeries.put("name", e.getKey()); - jsonSeries.put("isMedalSeries", medalSeriesNames.get(e.getKey())); + jsonSeries.put("name", e.getA()); + jsonSeries.put("isMedalSeries", medalSeriesNames.get(e.getA())); JSONArray jsonFleetsEntries = new JSONArray(); jsonSeries.put("fleets", jsonFleetsEntries); - for (final Fleet fleet : e.getValue()) { + for (final Fleet fleet : e.getB()) { if (fleet != null) { JSONObject jsonFleet = new JSONObject(); jsonFleet.put("name", fleet.getName()); @@ -130,7 +132,7 @@ public class LeaderboardGroupsResource extends AbstractSailingServerResource { jsonFleetsEntries.add(jsonFleet); JSONArray jsonRacesEntries = new JSONArray(); jsonFleet.put("races", jsonRacesEntries); - for (RaceColumn raceColumn: leaderboard.getRaceColumns()) { + for (RaceColumn raceColumn : e.getC()) { JSONObject jsonRaceColumn = new JSONObject(); jsonRaceColumn.put("name", raceColumn.getName()); jsonRaceColumn.put("isMedalRace" , raceColumn.isMedalRace()); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java index aa3bb3d1fa6..73b49c30cb6 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java @@ -36,6 +36,7 @@ import com.sap.sailing.domain.common.NoWindException; import com.sap.sailing.domain.common.RegattaName; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.Tack; +import com.sap.sailing.domain.common.TargetTimeInfo; import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.common.tracking.GPSFixMoving; @@ -60,14 +61,16 @@ import com.sap.sailing.server.gateway.serialization.impl.BoatJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.ColorJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.CompetitorJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.CompetitorWithChangingBoatJsonSerializer; -import com.sap.sailing.server.gateway.serialization.impl.DurationJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.FleetJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.NationalityJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.PersonJsonSerializer; +import com.sap.sailing.server.gateway.serialization.impl.PositionJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.RaceEntriesJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.RegattaJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.SeriesJsonSerializer; +import com.sap.sailing.server.gateway.serialization.impl.TargetTimeInfoSerializer; import com.sap.sailing.server.gateway.serialization.impl.TeamJsonSerializer; +import com.sap.sailing.server.gateway.serialization.impl.WindJsonSerializer; import com.sap.sse.InvalidDateException; import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; @@ -526,6 +529,7 @@ public class RegattasResource extends AbstractSailingServerResource { @Path("{regattaname}/races/{racename}/targettime") public Response getTargetTime(@PathParam("regattaname") String regattaName, @PathParam("racename") String raceName, @QueryParam("timeasmillis") Long timeasmillis) { + // TODO bug 3108: add distances upwind/downwind/reach if (timeasmillis == null) { timeasmillis = System.currentTimeMillis(); } @@ -541,12 +545,10 @@ public class RegattasResource extends AbstractSailingServerResource { } else { DynamicTrackedRace trackedRace = getService().getTrackedRace(regatta, race); if (trackedRace != null) { - Duration targetTime; + TargetTimeInfo targetTime; try { targetTime = trackedRace.getEstimatedTimeToComplete(timePoint); - - DurationJsonSerializer serializer = new DurationJsonSerializer(); - + final TargetTimeInfoSerializer serializer = new TargetTimeInfoSerializer(new WindJsonSerializer(new PositionJsonSerializer())); JSONObject jsonCourse = serializer.serialize(targetTime); String json = jsonCourse.toJSONString(); response = Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); diff --git a/java/com.sap.sailing.server.replication.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.server.replication.test/META-INF/MANIFEST.MF index 84dde5ee175..f7e5df42591 100644 --- a/java/com.sap.sailing.server.replication.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.server.replication.test/META-INF/MANIFEST.MF @@ -19,7 +19,7 @@ Require-Bundle: com.sap.sailing.server, com.sap.sailing.expeditionconnector, com.sap.sailing.domain.swisstimingadapter, org.json.simple;bundle-version="1.1.0", - org.junit4;bundle-version="4.8.2", + org.junit;bundle-version="4.8.2", org.hamcrest;bundle-version="1.1.0", com.sap.sailing.polars, com.sap.sse.common, diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/AbstractServerReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/AbstractServerReplicationTest.java index 8a729da4acf..48f8238e3e8 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/AbstractServerReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/AbstractServerReplicationTest.java @@ -55,7 +55,7 @@ public abstract class AbstractServerReplicationTest extends com.sap.sse.replicat @Override public DomainFactory getBaseDomainFactory() { return baseDomainFactory; } @Override public CompetitorStore getCompetitorStore() { return getBaseDomainFactory().getCompetitorStore(); } }; - }, MediaDBFactory.INSTANCE.getMediaDB(mongoDBService), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE, null, null, /* sailingNotificationService */ null); + }, MediaDBFactory.INSTANCE.getMediaDB(mongoDBService), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE, null, null, /* sailingNotificationService */ null, /* restoreTrackedRaces */ false); } @Override @@ -74,7 +74,7 @@ public abstract class AbstractServerReplicationTest extends com.sap.sse.replicat @Override public CompetitorStore getCompetitorStore() { return getBaseDomainFactory().getCompetitorStore(); } }; }, MediaDBFactory.INSTANCE.getMediaDB(mongoDBService), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE, - /* serviceFinderFactory */ null, null, /* sailingNotificationService */ null); + /* serviceFinderFactory */ null, null, /* sailingNotificationService */ null, /* restoreTrackedRaces */ false); } } } diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/ConnectionResetAndReconnectTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/ConnectionResetAndReconnectTest.java index 170313fa30c..aee366272ac 100644 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/ConnectionResetAndReconnectTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/ConnectionResetAndReconnectTest.java @@ -26,6 +26,7 @@ import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util; import com.sap.sse.common.Util.Pair; import com.sap.sse.common.impl.MillisecondsTimePoint; +import com.sap.sse.replication.Replicable; import com.sap.sse.replication.ReplicationMasterDescriptor; import com.sap.sse.replication.impl.ReplicationMasterDescriptorImpl; @@ -56,12 +57,12 @@ public class ConnectionResetAndReconnectTest extends AbstractServerReplicationTe static class MasterReplicationDescriptorMock extends ReplicationMasterDescriptorImpl { - public MasterReplicationDescriptorMock(String messagingHost, String hostname, String exchangeName, int servletPort, int messagingPort) { - super(messagingHost, exchangeName, messagingPort, UUID.randomUUID().toString(), hostname, servletPort); + public MasterReplicationDescriptorMock(String messagingHost, String hostname, String exchangeName, int servletPort, int messagingPort, Iterable> replicables) { + super(messagingHost, exchangeName, messagingPort, UUID.randomUUID().toString(), hostname, servletPort, replicables); } public static MasterReplicationDescriptorMock from(ReplicationMasterDescriptor obj) { - return new MasterReplicationDescriptorMock(obj.getMessagingHostname(), obj.getHostname(), obj.getExchangeName(), obj.getServletPort(), obj.getMessagingPort()); + return new MasterReplicationDescriptorMock(obj.getMessagingHostname(), obj.getHostname(), obj.getExchangeName(), obj.getServletPort(), obj.getMessagingPort(), obj.getReplicables()); } @Override diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/DelayedLeaderboardCorrectionsReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/DelayedLeaderboardCorrectionsReplicationTest.java index ed07795b3fb..a45375ef323 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/DelayedLeaderboardCorrectionsReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/DelayedLeaderboardCorrectionsReplicationTest.java @@ -164,7 +164,7 @@ public class DelayedLeaderboardCorrectionsReplicationTest extends AbstractServer private RacingEventServiceImpl createRacingEventServiceWithOneMockedTrackedRace(final DynamicTrackedRace q2YellowTrackedRace, DomainFactory domainFactory) { return new RacingEventServiceImpl(PersistenceFactory.INSTANCE.getDomainObjectFactory(MongoDBService.INSTANCE, domainFactory), PersistenceFactory.INSTANCE - .getMongoObjectFactory(MongoDBService.INSTANCE), MediaDBFactory.INSTANCE.getMediaDB(MongoDBService.INSTANCE), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE) { + .getMongoObjectFactory(MongoDBService.INSTANCE), MediaDBFactory.INSTANCE.getMediaDB(MongoDBService.INSTANCE), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE, /* restoreTrackedRaces */ false) { @Override public DynamicTrackedRace getExistingTrackedRace(RegattaAndRaceIdentifier raceIdentifier) { return q2YellowTrackedRace; diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java index 07fb1eba53d..ff35ebd6f41 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java @@ -88,9 +88,9 @@ public class InitialLoadReplicationObjectIdentityTest extends AbstractServerRepl public void setUp() throws Exception { persistenceSetUp(/* dropDB */ true); this.master = new RacingEventServiceImpl(PersistenceFactory.INSTANCE.getDomainObjectFactory(testSetUp.mongoDBService, DomainFactory.INSTANCE), PersistenceFactory.INSTANCE - .getMongoObjectFactory(testSetUp.mongoDBService), MediaDBFactory.INSTANCE.getMediaDB(testSetUp.mongoDBService), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE); + .getMongoObjectFactory(testSetUp.mongoDBService), MediaDBFactory.INSTANCE.getMediaDB(testSetUp.mongoDBService), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE, /* restoreTrackedRaces */ false); this.replica = new RacingEventServiceImpl(PersistenceFactory.INSTANCE.getDomainObjectFactory(testSetUp.mongoDBService, DomainFactory.INSTANCE), PersistenceFactory.INSTANCE - .getMongoObjectFactory(testSetUp.mongoDBService), MediaDBFactory.INSTANCE.getMediaDB(testSetUp.mongoDBService), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE); + .getMongoObjectFactory(testSetUp.mongoDBService), MediaDBFactory.INSTANCE.getMediaDB(testSetUp.mongoDBService), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE, /* restoreTrackedRaces */ false); } private void performReplicationSetup() throws Exception { diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/ReplicationInstancesManagerLoggingPerformanceTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/ReplicationInstancesManagerLoggingPerformanceTest.java index 9a2e470c9d7..9ebe9bcbdc1 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/ReplicationInstancesManagerLoggingPerformanceTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/ReplicationInstancesManagerLoggingPerformanceTest.java @@ -23,7 +23,7 @@ public class ReplicationInstancesManagerLoggingPerformanceTest { @Before public void setUp() throws UnknownHostException { replicationInstanceManager = new ReplicationInstancesManager(); - replica = new ReplicaDescriptor(InetAddress.getLocalHost(), UUID.randomUUID(), ""); + replica = new ReplicaDescriptor(InetAddress.getLocalHost(), UUID.randomUUID(), "", /* replicableIds */ new String[] { "Humba" }); replicationInstanceManager.registerReplica(replica); operation = new CreateLeaderboardGroup("Test Leaderboard Group", "Description of Test Leaderboard Group", /* displayName */ null, /* displayGroupsInReverseOrder */ false, diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceBoatCompetitorMetadataReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceBoatCompetitorMetadataReplicationTest.java index 7d8f74e6bfd..279a13d9c83 100644 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceBoatCompetitorMetadataReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceBoatCompetitorMetadataReplicationTest.java @@ -71,13 +71,14 @@ public class TrackRaceBoatCompetitorMetadataReplicationTest extends AbstractServ .createTrackingConnectivityParameters(paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking, endOfTracking, /* delayToLiveInMillis */ 0l, /* offsetToStartTimeOfSimulatedRace */null, /*ignoreTracTracMarkPassings*/ false, EmptyRaceLogStore.INSTANCE, - EmptyRegattaLogStore.INSTANCE, tracTracUsername, tracTracPassword, "", ""); + EmptyRegattaLogStore.INSTANCE, tracTracUsername, tracTracPassword, "", "", /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false, + /* preferReplayIfAvailable */ false); } private void startTracking() throws Exception, InterruptedException { startTrackingOnMaster(); waitForTrackRaceReplicationTrigger(); - raceIdentifier = racesHandle.getRaceTracker().getRaceIdentifiers().iterator().next(); + raceIdentifier = racesHandle.getRaceTracker().getRaceIdentifier(); masterTrackedRace = master.getTrackedRace(raceIdentifier); } diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java index ab0fcc60042..947ea39cbc0 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java @@ -89,13 +89,14 @@ public class TrackRaceReplicationTest extends AbstractServerReplicationTest { .createTrackingConnectivityParameters(paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking, endOfTracking, /* delayToLiveInMillis */ 0l, /* offsetToStartTimeOfSimulatedRace */null, /*ignoreTracTracMarkPassings*/ false, EmptyRaceLogStore.INSTANCE, - EmptyRegattaLogStore.INSTANCE, tracTracUsername, tracTracPassword, "", ""); + EmptyRegattaLogStore.INSTANCE, tracTracUsername, tracTracPassword, "", "", /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false, + /* preferReplayIfAvailable */ false); } private void startTracking() throws Exception, InterruptedException { startTrackingOnMaster(); waitForTrackRaceReplicationTrigger(); - raceIdentifier = racesHandle.getRaceTracker().getRaceIdentifiers().iterator().next(); + raceIdentifier = racesHandle.getRaceTracker().getRaceIdentifier(); masterTrackedRace = master.getTrackedRace(raceIdentifier); } diff --git a/java/com.sap.sailing.server.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.server.test/META-INF/MANIFEST.MF index dd44553ef6c..07ac54ed0b5 100644 --- a/java/com.sap.sailing.server.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.server.test/META-INF/MANIFEST.MF @@ -15,7 +15,7 @@ Require-Bundle: com.sap.sailing.domain, com.sap.sse.mongodb, com.tractrac.clientmodule;bundle-version="2.0.0", org.hamcrest;bundle-version="1.1.0", - org.junit4;bundle-version="4.8.2", + org.junit;bundle-version="4.8.2", com.sap.sailing.domain.test, org.mockito.mockito-core;bundle-version="1.10.14", com.sap.sse.common, diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/impl/MasterDataImporterMediaTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/impl/MasterDataImporterMediaTest.java index f531e7c4412..fa75b4e28ed 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/impl/MasterDataImporterMediaTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/impl/MasterDataImporterMediaTest.java @@ -62,7 +62,7 @@ public class MasterDataImporterMediaTest { when(mediaDb.loadAllMediaTracks()).thenReturn(Arrays.asList(existingDbMediaTracks)); racingEventService = spy(new RacingEventServiceImpl(domainObjectFactory, mongoObjectFactory, mediaDb, - windStore, sensorFixStore)); + windStore, sensorFixStore, /* restoreTrackedRaces */ false)); } @Test diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/ApplyScoresFromRaceLogTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/ApplyScoresFromRaceLogTest.java index 96e8418a984..1949cdd0517 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/ApplyScoresFromRaceLogTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/ApplyScoresFromRaceLogTest.java @@ -11,6 +11,7 @@ import java.util.Map; import java.util.UUID; import java.util.function.Function; +import org.junit.Before; import org.junit.Test; import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl; @@ -45,15 +46,20 @@ import com.sap.sse.common.TimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint; public class ApplyScoresFromRaceLogTest extends LeaderboardScoringAndRankingTestBase { - /** - * See also bug 3794: make sure that different variants of scores and max points reasons / penalties are applied to the - * leaderboard. - */ - @Test - public void testApplicationOfScoresFromRaceLog() throws NoWindException { - final RacingEventService service = new RacingEventServiceImpl(); - final List competitors = new ArrayList<>(); - for (int i=0; i<20; i++) { + private RacingEventService service; + private Regatta regatta; + private RaceColumn f1Column; + private List competitors; + private Leaderboard leaderboard; + + @Before + public void setUp() { + service = new RacingEventServiceImpl(); + } + + private void setUp(int numberOfCompetitors, TimePoint now, ScoringSchemeType scoringScheme) { + competitors = new ArrayList<>(); + for (int i=0; i scores = new HashMap<>(); final Map mprs = new HashMap<>(); int oneBasedRank = 1; @@ -120,33 +134,9 @@ public class ApplyScoresFromRaceLogTest extends LeaderboardScoringAndRankingTest */ @Test public void testScoresFromRaceLogOnlyAppliedIfExplicitOrNoMaxPointsReason() throws NoWindException { - final RacingEventService service = new RacingEventServiceImpl(); - final List competitors = new ArrayList<>(); - for (int i=0; i<8; i++) { - final String competitorName = "C"+i; - competitors.add(service.getBaseDomainFactory().getCompetitorStore().getOrCreateCompetitor(UUID.randomUUID(), - competitorName, /* displayColor */ Color.RED, /* email */ null, /* flagImageURI */ null, - new TeamImpl("STG", Collections.singleton( - new PersonImpl(competitorName, new NationalityImpl("GER"), - /* dateOfBirth */ null, "This is famous "+competitorName)), - new PersonImpl("Rigo van Maas", new NationalityImpl("NED"), - /* dateOfBirth */null, "This is Rigo, the coach")), new BoatImpl(competitorName + "'s boat", - new BoatClassImpl("505", /* typicallyStartsUpwind */ true), /* sailID */ null), - /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null)); - } - final Regatta regatta = createRegatta(/* qualifying */0, new String[] { "Default" }, /* final */1, - new String[] { "Default" }, - /* medal */false, /* medal */ 0, "testOneStartedRaceWithDifferentScores", - DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true), DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.HIGH_POINT_FIRST_GETS_TEN_OR_EIGHT)); - TimePoint now = MillisecondsTimePoint.now(); + final TimePoint now = MillisecondsTimePoint.now(); TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000); - TrackedRace f1 = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors); - RaceColumn f1Column = series.get(1).getRaceColumnByName("F1"); - f1Column.setTrackedRace(f1Column.getFleets().iterator().next(), f1); - final Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]); - - service.addLeaderboard(leaderboard); // should add a RaceLogScoringReplicator as listener to the leaderboard - service.addRegattaWithoutReplication(regatta); + setUp(8, now, ScoringSchemeType.HIGH_POINT_FIRST_GETS_TEN_OR_EIGHT); int oneBasedRank = 1; final CompetitorResults results = new CompetitorResultsImpl(); @@ -177,6 +167,45 @@ public class ApplyScoresFromRaceLogTest extends LeaderboardScoringAndRankingTest assertScoreCorrections(leaderboard, f1Column, competitors.get(6), MaxPointsReason.NONE, 4, /* score is corrected */ true, later); // score corrected based on rank because MaxPointsReason.NONE set assertScoreCorrections(leaderboard, f1Column, competitors.get(7), MaxPointsReason.NONE, 3.3, /* score is corrected */ true, later); // score corrected based on explicit score } + + /** + * See bug 4025: When a scoring race log event is received it may contain a partial competitor set. It shall depend on + * the previous state resulting from the race log, without the last event, what needs to be done with the leaderboard + * score corrections. If a competitor which which an earlier message did have a correction now no longer is part of + * the latest scoring race log event, and if the score corrections in the leaderboard match up with what the previous + * event had caused, remove that score correction again. + */ + @Test + public void testApplyingOCSThenClearingOCS() { + TimePoint now = MillisecondsTimePoint.now(); + TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000); + final int numberOfCompetitors = 2; + setUp(numberOfCompetitors, now, ScoringSchemeType.LOW_POINT); + final CompetitorResults results = new CompetitorResultsImpl(); + // set OCS + setResultForCompetitor(competitors.get(0), /* one-based rank */ 0, results, MaxPointsReason.OCS, /* explicit score */ null); + setResultForCompetitor(competitors.get(1), /* one-based rank */ 0, results, MaxPointsReason.OCS, /* explicit score */ null); + final RaceLog f1RaceLog = f1Column.getRaceLog(f1Column.getFleets().iterator().next()); + final LogEventAuthorImpl author = new LogEventAuthorImpl("Axel", 0); + final RaceState f1RaceState = new RaceStateImpl(service, f1RaceLog, author, + new RacingProcedureFactoryImpl(author, new EmptyRegattaConfiguration())); + f1RaceState.setFinishPositioningListChanged(now, results); + final List rankedCompetitorsBeforeApplying = leaderboard.getCompetitorsFromBestToWorst(later); + assertEquals(competitors, rankedCompetitorsBeforeApplying); // no effects of preliminary results list yet + f1RaceState.setFinishPositioningConfirmed(now); + // validate that it arrived in leaderboard + assertScoreCorrections(leaderboard, f1Column, competitors.get(0), MaxPointsReason.OCS, numberOfCompetitors+1, /* score is corrected */ false, later); + assertScoreCorrections(leaderboard, f1Column, competitors.get(1), MaxPointsReason.OCS, numberOfCompetitors+1, /* score is corrected */ false, later); + + // now clear OCS for first competitor again and leave second competitor OCS: + final CompetitorResults resultsWithOCSCleared = new CompetitorResultsImpl(); + setResultForCompetitor(competitors.get(1), /* one-based rank */ 0, resultsWithOCSCleared, MaxPointsReason.OCS, /* explicit score */ null); + f1RaceState.setFinishPositioningListChanged(later, resultsWithOCSCleared); + f1RaceState.setFinishPositioningConfirmed(later); + // validate that it got cleared in leaderboard + assertScoreCorrections(leaderboard, f1Column, competitors.get(1), MaxPointsReason.OCS, numberOfCompetitors+1, /* score is corrected */ false, later); + assertScoreCorrections(leaderboard, f1Column, competitors.get(0), MaxPointsReason.NONE, 1, /* score is corrected */ false, later); // ranking first, one point in low-point scheme + } private void assertScoreCorrections(Leaderboard leaderboard, RaceColumn raceColumn, Competitor competitor, MaxPointsReason expectedMaxPointsReason, double expectedScore, boolean expectedIsScoreCorrected, @@ -188,7 +217,7 @@ public class ApplyScoresFromRaceLogTest extends LeaderboardScoringAndRankingTest private void setResultForCompetitor(final Competitor competitor, int oneBasedRank, final CompetitorResults results, MaxPointsReason maxPointsReason, Double explicitScore) { - results.add(new CompetitorResultImpl(competitor.getId(), competitor.getName(), oneBasedRank++, maxPointsReason, explicitScore, /* finishingTime */ null, /* comment */ null)); + results.add(new CompetitorResultImpl(competitor.getId(), competitor.getName(), oneBasedRank, maxPointsReason, explicitScore, /* finishingTime */ null, /* comment */ null)); } diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java index 1b1deb6cc65..0bc2f6ae15a 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java @@ -296,6 +296,11 @@ public class MasterDataImportTest { new KnotSpeedWithBearingImpl(10, new DegreeBearingImpl(90))); sourceService.getSensorFixStore().storeFix(deviceIdentifier, gpsFix); + // test to check that batch-import of fixes works as intended + DeviceIdentifier deviceBatch1 = addDeviceMappingWithFixes(sourceService, regatta, competitor, logTimePoint, logTimePoint3, logTimePoint4, "x", 4999); + DeviceIdentifier deviceBatch2 = addDeviceMappingWithFixes(sourceService, regatta, competitor, logTimePoint, logTimePoint3, logTimePoint4, "y", 5000); + DeviceIdentifier deviceBatch3 = addDeviceMappingWithFixes(sourceService, regatta, competitor, logTimePoint, logTimePoint3, logTimePoint4, "z", 5001); + // Add sensor fix DeviceIdentifier deviceIdentifier2 = new SmartphoneImeiIdentifier("b"); RegattaLogDeviceMappingEvent bravoMappingEvent = new RegattaLogDeviceCompetitorBravoMappingEventImpl( @@ -449,6 +454,11 @@ public class MasterDataImportTest { Assert.assertEquals(1, sensorFixStore.getNumberOfFixes(deviceIdentifier)); verifyFix(gpsFix, sensorFixStore, logTimePoint, logTimePoint3, deviceIdentifier); + // Check that fix counts around the batch size are imported + verifyFixCount(sensorFixStore, deviceBatch1, 4999); + verifyFixCount(sensorFixStore, deviceBatch2, 5000); + verifyFixCount(sensorFixStore, deviceBatch3, 5001); + // Check for import of sensor fix Assert.assertEquals(1, sensorFixStore.getNumberOfFixes(deviceIdentifier2)); verifyFix(doubleVectorFix, sensorFixStore, logTimePoint, logTimePoint3, deviceIdentifier2); @@ -470,6 +480,22 @@ public class MasterDataImportTest { Assert.assertNotNull(registerEventOnTarget2); Assert.assertEquals(registerEvent.getId(), registerEventOnTarget2.getId()); } + + private DeviceIdentifier addDeviceMappingWithFixes(RacingEventService sourceService, Regatta regatta, CompetitorImpl competitor, + TimePoint logTimePoint, TimePoint logTimePoint3, TimePoint logTimePoint4, String imei, int numFixes) { + DeviceIdentifier deviceIdentifier = new SmartphoneImeiIdentifier(imei); + RegattaLogDeviceCompetitorMappingEvent mappingEvent = new RegattaLogDeviceCompetitorMappingEventImpl( + logTimePoint4, logTimePoint4, author, UUID.randomUUID(), competitor, deviceIdentifier, logTimePoint, + logTimePoint3); + regatta.getRegattaLog().add(mappingEvent); + List fixesToSave = new ArrayList<>(numFixes); + for(int i = 1; i <= numFixes; i++) { + fixesToSave.add(new GPSFixMovingImpl(new DegreePosition(54.333, 10.133), logTimePoint.plus(i), + new KnotSpeedWithBearingImpl(10, new DegreeBearingImpl(90)))); + } + sourceService.getSensorFixStore().storeFixes(deviceIdentifier, fixesToSave); + return deviceIdentifier; + } private void verifyFix(Timed expectedFix, SensorFixStore store, TimePoint start, TimePoint endInclusive, DeviceIdentifier deviceIdentifier) { List fixes = new ArrayList<>(1); @@ -481,6 +507,14 @@ public class MasterDataImportTest { throw new RuntimeException(e); } } + + private void verifyFixCount(SensorFixStore store, DeviceIdentifier deviceIdentifier, int numFixes) { + try { + assertEquals(numFixes, store.getNumberOfFixes(deviceIdentifier)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } @Test public void testMasterDataImportForScoreCorrections() throws MalformedURLException, IOException, @@ -808,7 +842,7 @@ public class MasterDataImportTest { RacingEventService sourceService = new RacingEventServiceImpl( PersistenceFactory.INSTANCE.getDomainObjectFactory(MongoDBService.INSTANCE, sourceDomainFactory), PersistenceFactory.INSTANCE.getMongoObjectFactory(MongoDBService.INSTANCE), - MediaDBFactory.INSTANCE.getDefaultMediaDB(), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE); + MediaDBFactory.INSTANCE.getDefaultMediaDB(), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE, /* restoreTrackedRaces */ false); Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */null, eventStartDate, eventEndDate, "testVenue", false, eventUUID); UUID courseAreaUUID = UUID.randomUUID(); diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerMock.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerMock.java index 19e3cb03b2e..ba93c62c113 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerMock.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerMock.java @@ -2,11 +2,11 @@ package com.sap.sailing.server.test; import java.io.IOException; import java.net.MalformedURLException; -import java.util.Set; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.domain.common.RegattaNameAndRaceName; import com.sap.sailing.domain.tracking.AbstractRaceTrackerBaseImpl; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; import com.sap.sailing.domain.tracking.RaceHandle; @@ -16,32 +16,31 @@ public class RaceTrackerMock extends AbstractRaceTrackerBaseImpl { private Long id; private Regatta regatta; - private Set raceDefinitions; + private RaceDefinition raceDefinition; private boolean isTracking; public RaceTrackerMock() { - // TODO check if necesary + super(null); } - - - public RaceTrackerMock(Long id, Regatta regatta, Set raceDefinitions, boolean isTracking) { - super(); + public RaceTrackerMock(Long id, Regatta regatta, RaceDefinition raceDefinition, boolean isTracking) { + super(null); this.id = id; this.regatta = regatta; - this.raceDefinitions = raceDefinitions; + this.raceDefinition = raceDefinition; this.isTracking = isTracking; } - public void setIsTracking(boolean isTracking){ + public void setIsTracking(boolean isTracking) { this.isTracking = isTracking; } - public boolean getIsTracking(){ + public boolean getIsTracking() { return isTracking; } public RaceTrackerMock(Long id) { + super(null); this.id = id; } @@ -56,12 +55,12 @@ public class RaceTrackerMock extends AbstractRaceTrackerBaseImpl { } @Override - public Set getRaces() { - return raceDefinitions; + public RaceDefinition getRace() { + return raceDefinition; } @Override - public RaceHandle getRacesHandle() { + public RaceHandle getRaceHandle() { throw new RuntimeException("No race Handle in RaceTrackerMock"); } @@ -82,8 +81,7 @@ public class RaceTrackerMock extends AbstractRaceTrackerBaseImpl { } @Override - public Set getRaceIdentifiers() { - // TODO Auto-generated method stub - return null; + public RegattaAndRaceIdentifier getRaceIdentifier() { + return new RegattaNameAndRaceName(getRegatta().getName(), getRace().getName()); } } diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java index 25ec563ca13..3153eec6445 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java @@ -11,8 +11,8 @@ import static org.mockito.Mockito.mock; import java.io.IOException; import java.net.MalformedURLException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.UUID; @@ -42,6 +42,7 @@ import com.sap.sailing.domain.tracking.TrackedRegatta; import com.sap.sailing.domain.tracking.impl.EmptyWindStore; import com.sap.sailing.server.RacingEventService; import com.sap.sailing.server.testsupport.RacingEventServiceImplMock; +import com.sap.sse.common.Util; public class RaceTrackerStartStopTest { @@ -93,21 +94,12 @@ public class RaceTrackerStartStopTest { /* windStore */ EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 0l, /* millisecondsOverWhichToAverageWind */ 0l, /* millisecondsOverWhichToAverageSpeed */ 0l, /* raceDefinitionSetToUpdate */ null, /*useMarkPassingCalculator*/ false, mock(RaceLogResolver.class)); - Set raceDefinitionSetRace1 = new HashSet(); - raceDefinitionSetRace1.add(raceDef1); - Set raceDefinitionSetRace2 = new HashSet(); - raceDefinitionSetRace2.add(raceDef1); - raceDefinitionSetRace2.add(raceDef2); - Set raceDefinitionSetRace3 = new HashSet(); - raceDefinitionSetRace3.add(raceDef1); - raceDefinitionSetRace3.add(raceDef2); - raceDefinitionSetRace3.add(raceDef3); Long trackerID1 = new Long(1); Long trackerID2 = new Long(2); Long trackerID3 = new Long(3); - raceTracker1 = new RaceTrackerMock(new Long(1), regatta, raceDefinitionSetRace1, true); - raceTracker2 = new RaceTrackerMock(new Long(2), regatta, raceDefinitionSetRace2, true); - raceTracker3 = new RaceTrackerMock(new Long(3), regatta, raceDefinitionSetRace3, true); + raceTracker1 = new RaceTrackerMock(new Long(1), regatta, raceDef1, true); + raceTracker2 = new RaceTrackerMock(new Long(2), regatta, raceDef2, true); + raceTracker3 = new RaceTrackerMock(new Long(3), regatta, raceDef3, true); raceTrackerSet.add(raceTracker1); raceTrackerSet.add(raceTracker2); raceTrackerSet.add(raceTracker3); @@ -118,7 +110,7 @@ public class RaceTrackerStartStopTest { } /** - * This test method tests, if the {@link RacingEventService#stopTracking(Regatta, RaceDefinition) stopTracking} method works correctly. + * This test method tests if the {@link RacingEventService#stopTracking(Regatta, RaceDefinition) stopTracking} method works correctly. */ @Test public void testStopTrackingRace() throws MalformedURLException, IOException, InterruptedException { @@ -142,21 +134,21 @@ public class RaceTrackerStartStopTest { trackedRegatta.unlockTrackedRacesAfterRead(); } assertTrue(foundTrackedRaceForRaceDef2); - // The raceTracker2 and raceTracker3 should currently not be in track mode. + // The raceTracker2 should no longer be in track mode: assertTrue(raceTracker1.getIsTracking()); assertFalse(raceTracker2.getIsTracking()); - assertFalse(raceTracker3.getIsTracking()); + assertTrue(raceTracker3.getIsTracking()); // The RaceTrackersByID map should not contain the trackers raceTracker2 and raceTracker3 anymore assertTrue(racingEventService.getRaceTrackersByIDMap().containsValue(raceTracker1)); assertFalse(racingEventService.getRaceTrackersByIDMap().containsValue(raceTracker2)); - assertFalse(racingEventService.getRaceTrackersByIDMap().containsValue(raceTracker3)); - // The RaceTrakcersByEvent map should contain a tracker with a set of RaceDefinitions, containing the + assertTrue(racingEventService.getRaceTrackersByIDMap().containsValue(raceTracker3)); + // The RaceTrackersByEvent map should contain a tracker with a set of RaceDefinitions, containing the // raceDefinition1 assertEquals(1, racingEventService.getRaceTrackersByRegattaMap().size()); Iterator raceTrackerIter = racingEventService.getRaceTrackersByRegattaMap().get(regatta).iterator(); while (raceTrackerIter.hasNext()) { RaceTracker currentTracker = raceTrackerIter.next(); - assertSame(raceTracker1, currentTracker); + assertTrue(Util.contains(Arrays.asList(raceTracker1, raceTracker3), currentTracker)); } } @@ -185,24 +177,19 @@ public class RaceTrackerStartStopTest { trackedRegatta.unlockTrackedRacesAfterRead(); } assertFalse(foundTrackedRaceForRaceDef2); - // The trackers map should still contain the raceTrackers + // The trackers map should still contain the raceTrackers, except for raceTracker2 which should + // have been removed because the race named RACENAME2 was removed, hopefully together with its tracker... assertTrue(racingEventService.getRaceTrackersByRegattaMap().get(regatta).contains(raceTracker1)); - assertTrue(racingEventService.getRaceTrackersByRegattaMap().get(regatta).contains(raceTracker2)); + assertFalse(racingEventService.getRaceTrackersByRegattaMap().get(regatta).contains(raceTracker2)); assertTrue(racingEventService.getRaceTrackersByRegattaMap().get(regatta).contains(raceTracker3)); - // The raceTrackerMap should still contain the raceTrackers. These raceTracker should not contain the raceDefinition raceDef2 anymore + // The raceTrackerMap should still contain the raceTrackers, except the one for race 2 which was removed. assertTrue(racingEventService.getRaceTrackersByIDMap().containsValue(raceTracker1)); - assertTrue(racingEventService.getRaceTrackersByIDMap().containsValue(raceTracker2)); + assertFalse(racingEventService.getRaceTrackersByIDMap().containsValue(raceTracker2)); assertTrue(racingEventService.getRaceTrackersByIDMap().containsValue(raceTracker3)); // The raceTracker should still exist; it shall still contain raceDef1 and raceDef2 because a tracker keeps tracking what it tracks... - assertTrue(raceTracker1.getRaces().contains(raceDef1)); - assertEquals(1, raceTracker1.getRaces().size()); - assertTrue(raceTracker2.getRaces().contains(raceDef1)); - assertTrue(raceTracker2.getRaces().contains(raceDef2)); - assertEquals(2, raceTracker2.getRaces().size()); - assertTrue(raceTracker3.getRaces().contains(raceDef1)); - assertTrue(raceTracker3.getRaces().contains(raceDef2)); - assertTrue(raceTracker3.getRaces().contains(raceDef3)); - assertEquals(3, raceTracker3.getRaces().size()); + assertSame(raceTracker1.getRace(), raceDef1); + assertSame(raceTracker2.getRace(), raceDef2); + assertSame(raceTracker3.getRace(), raceDef3); } /** @@ -222,8 +209,7 @@ public class RaceTrackerStartStopTest { assertFalse(racingEventService.getRaceTrackersByIDMap().containsValue(raceTracker2)); assertTrue(racingEventService.getRaceTrackersByIDMap().containsValue(raceTracker3)); // The raceTracker 3 should exist, and it should contain all race definitions still - assertTrue(raceTracker3.getRaces().contains(raceDef3)); - assertEquals(3, raceTracker3.getRaces().size()); + assertSame(raceTracker3.getRace(), raceDef3); } } diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerTest.java index 4855f28335d..ec41b6df4e8 100755 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerTest.java @@ -71,9 +71,9 @@ public class RaceTrackerTest { tracTracAdapterFactory = new TracTracAdapterFactoryImpl(); raceHandle = tracTracAdapterFactory.getOrCreateTracTracAdapter(service.getBaseDomainFactory()).addTracTracRace( service, paramUrl, liveUri, storedUri, courseDesignUpdateUri, EmptyRaceLogStore.INSTANCE, - EmptyRegattaLogStore.INSTANCE, - /* timeoutInMilliseconds */60000, tracTracUsername, tracTracPassword, - TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY); + EmptyRegattaLogStore.INSTANCE, /* timeoutInMilliseconds */60000, tracTracUsername, tracTracPassword, + TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY, + /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false); logger.info("Calling raceHandle.getRaces()"); RaceDefinition race = raceHandle.getRace(); // wait for RaceDefinition to be completely wired in Regatta logger.info("Obtained race: "+race); @@ -130,9 +130,10 @@ public class RaceTrackerTest { service.removeRegatta(raceHandle.getRegatta()); RaceHandle myRaceHandle = tracTracAdapterFactory.getOrCreateTracTracAdapter(service.getBaseDomainFactory()) .addTracTracRace(service, paramUrl, liveUri, storedUri, courseDesignUpdateUri, - EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, - /* timeoutInMilliseconds */60000, tracTracUsername, tracTracPassword, - TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY); + EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, /* timeoutInMilliseconds */60000, + tracTracUsername, tracTracPassword, TracTracConnectionConstants.ONLINE_STATUS, + TracTracConnectionConstants.ONLINE_VISIBILITY, /* trackWind */ false, + /* correctWindDirectionByMagneticDeclination */ false); TrackedRegatta newTrackedRegatta = myRaceHandle.getTrackedRegatta(); assertNotSame(oldTrackedRegatta, newTrackedRegatta); TrackedRace newTrackedRace = getTrackedRace(newTrackedRegatta); @@ -158,9 +159,10 @@ public class RaceTrackerTest { TrackedRace oldTrackedRace = getTrackedRace(oldTrackedRegatta); RaceHandle myRaceHandle = tracTracAdapterFactory.getOrCreateTracTracAdapter(service.getBaseDomainFactory()) .addTracTracRace(service, paramUrl, liveUri, storedUri, courseDesignUpdateUri, - EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, - /* timeoutInMilliseconds */60000, tracTracUsername, tracTracPassword, - TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY); + EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, /* timeoutInMilliseconds */60000, + tracTracUsername, tracTracPassword, TracTracConnectionConstants.ONLINE_STATUS, + TracTracConnectionConstants.ONLINE_VISIBILITY, /* trackWind */ false, + /* correctWindDirectionByMagneticDeclination */ false); TrackedRegatta newTrackedEvent = myRaceHandle.getTrackedRegatta(); TrackedRace newTrackedRace = getTrackedRace(newTrackedEvent); // expecting a new tracked race to be created when starting over with tracking diff --git a/java/com.sap.sailing.server.testsupport/src/com/sap/sailing/server/testsupport/RacingEventServiceImplMock.java b/java/com.sap.sailing.server.testsupport/src/com/sap/sailing/server/testsupport/RacingEventServiceImplMock.java index 479b0e6c5de..541f75462a7 100644 --- a/java/com.sap.sailing.server.testsupport/src/com/sap/sailing/server/testsupport/RacingEventServiceImplMock.java +++ b/java/com.sap.sailing.server.testsupport/src/com/sap/sailing/server/testsupport/RacingEventServiceImplMock.java @@ -23,7 +23,7 @@ public abstract class RacingEventServiceImplMock extends RacingEventServiceImpl private DataImportLockWithProgress lock; public RacingEventServiceImplMock() { - super(false, null); + super(/* clearPersistentCompetitorStore */ false, null, /* restoreTrackedRaces */ false); } public RacingEventServiceImplMock(DataImportProgressImpl dataImportProgressImpl, TypeBasedServiceFinderFactory serviceFinderFactory) { diff --git a/java/com.sap.sailing.server.trackfiles.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.server.trackfiles.test/META-INF/MANIFEST.MF index f890b69aafe..04c539ad640 100644 --- a/java/com.sap.sailing.server.trackfiles.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.server.trackfiles.test/META-INF/MANIFEST.MF @@ -13,7 +13,7 @@ Require-Bundle: com.tractrac.clientmodule;bundle-version="2.0.0", com.sap.sailing.domain.test;bundle-version="1.0.0", com.sap.sailing.domain;bundle-version="1.0.0", com.sap.sailing.server;bundle-version="1.0.0", - org.junit4;bundle-version="4.8.2", + org.junit;bundle-version="4.8.2", org.mockito.mockito-core;bundle-version="1.9.5", org.objenesis;bundle-version="1.3.0", com.sap.sse.common diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy OSX).launch b/java/com.sap.sailing.server/SailingServer (No Proxy OSX).launch index 41554299eb1..d6d91411db9 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy OSX).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy OSX).launch @@ -20,7 +20,7 @@ - + @@ -30,7 +30,7 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy).launch b/java/com.sap.sailing.server/SailingServer (No Proxy).launch index 250e12b1e58..d3e913fb096 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy).launch @@ -22,7 +22,7 @@ - + @@ -32,7 +32,7 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Cached MTB).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Cached MTB).launch index 18d8a97794a..8570e236fc7 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Cached MTB).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Cached MTB).launch @@ -19,7 +19,7 @@ - + @@ -29,7 +29,7 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Remote Debug SAP VM).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Remote Debug SAP VM).launch index 01e88913426..b91b50932c7 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Remote Debug SAP VM).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Remote Debug SAP VM).launch @@ -16,14 +16,14 @@ - + - + - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch index bdd1bf9f626..58fcdc60e61 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch @@ -19,7 +19,7 @@ - + @@ -29,7 +29,7 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy).launch b/java/com.sap.sailing.server/SailingServer (Proxy).launch index 2ed6d5b7755..0f547eb8db5 100644 --- a/java/com.sap.sailing.server/SailingServer (Proxy).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy).launch @@ -22,7 +22,7 @@ - + @@ -32,7 +32,7 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, winddbTest).launch b/java/com.sap.sailing.server/SailingServer (Proxy, winddbTest).launch index 7c854bbbb77..283b925a72f 100644 --- a/java/com.sap.sailing.server/SailingServer (Proxy, winddbTest).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, winddbTest).launch @@ -20,11 +20,11 @@ - + - + diff --git a/java/com.sap.sailing.server/resources/stringmessages/XLF/StringMessages_ja.properties.xlf b/java/com.sap.sailing.server/resources/stringmessages/XLF/StringMessages_ja.properties.xlf index 14ca95ed69a..6d26621c6b0 100644 --- a/java/com.sap.sailing.server/resources/stringmessages/XLF/StringMessages_ja.properties.xlf +++ b/java/com.sap.sailing.server/resources/stringmessages/XLF/StringMessages_ja.properties.xlf @@ -1,6 +1,6 @@ - +
        H4sIAAAAAAAAAKVUTW/bMAy9+1cQ2CEbkAVpdwvgQ5YuWIChDZr2sKPs0A1XWTIk2VkQ5L+PkvNhtzZarBfbEh/f4yNlGZHiQt3jk3BO3GU/KlQuNrwJ+/EBSIGpQzDYXx0GoDNAD+Hl9WEQmZA9l4iun+Tz/tvhy5tUEsUaTaKFWZ8YGlsMGrdzmCJqJq02enuPtpTO/iL1/EBOYuw3WTbshmLtXYWmIty+xPgY6GMwQC+svOiAX0AeMFVC7iy90vZNEMdYFDHczaSw1qfMSZHd4HpVJn8wdbHfg0wb8ChIPSz0byMsZEcsfAWFZ0sgKkFSJBKjTzCewJn+VuQ4hKtJ0L9BmxoqHGnVXcB3vd7Ftw3ajiJ4fNxyEAYvoqOGoVWqDc60MeyEhU6emqyt3A6NPg+NIXdaeSH9ATepzgt05LRZMhJt3aCTl9k5GjgKD1kfJwOSFHpaUZ+NYOVC1zuPbsVg4V1y7KJV93+NIW0p9dbeM4de8c5BtLXePFaPBSeQevLH9WRmWv9VHWPdkpRgnTAOrObS3vdTDOF6Ao5y7JYNNuavxXzdDUFRFEb/pVw4lDtgJN9q7MSWyVloobhmBvBn/FuXwTX3CqliKVDaUUZpCHOvjM5hNV3Cirviw+GCcZTaETxocKVRfBtm7awhFDwki1CRJQc7XRooLRrg0jLiaXNZboP+9DxDglJvR5GPL+tw/Oixx0VEeWGIb+FF/Y74WYl0Fy/rd2SFLF1t5qeQUvumDKN/18D20lAGAAA= diff --git a/java/com.sap.sailing.server/resources/stringmessages/XLF/StringMessages_ru.properties.xlf b/java/com.sap.sailing.server/resources/stringmessages/XLF/StringMessages_ru.properties.xlf index 5002ff1a35c..f3fa094f4f9 100644 --- a/java/com.sap.sailing.server/resources/stringmessages/XLF/StringMessages_ru.properties.xlf +++ b/java/com.sap.sailing.server/resources/stringmessages/XLF/StringMessages_ru.properties.xlf @@ -1,6 +1,6 @@ - +
        H4sIAAAAAAAAAKVUTW/bMAy9+1cQ2CEbkAVpdwvgQ5YuWIChDZr2sKPs0A1XWTIk2VkQ5L+PkvNhtzZarBfbEh/f4yNlGZHiQt3jk3BO3GU/KlQuNrwJ+/EBSIGpQzDYXx0GoDNAD+Hl9WEQmZA9l4iun+Tz/tvhy5tUEsUaTaKFWZ8YGlsMGrdzmCJqJq02enuPtpTO/iL1/EBOYuw3WTbshmLtXYWmIty+xPgY6GMwQC+svOiAX0AeMFVC7iy90vZNEMdYFDHczaSw1qfMSZHd4HpVJn8wdbHfg0wb8ChIPSz0byMsZEcsfAWFZ0sgKkFSJBKjTzCewJn+VuQ4hKtJ0L9BmxoqHGnVXcB3vd7Ftw3ajiJ4fNxyEAYvoqOGoVWqDc60MeyEhU6emqyt3A6NPg+NIXdaeSH9ATepzgt05LRZMhJt3aCTl9k5GjgKD1kfJwOSFHpaUZ+NYOVC1zuPbsVg4V1y7KJV93+NIW0p9dbeM4de8c5BtLXePFaPBSeQevLH9WRmWv9VHWPdkpRgnTAOrObS3vdTDOF6Ao5y7JYNNuavxXzdDUFRFEb/pVw4lDtgJN9q7MSWyVloobhmBvBn/FuXwTX3CqliKVDaUUZpCHOvjM5hNV3Cirviw+GCcZTaETxocKVRfBtm7awhFDwki1CRJQc7XRooLRrg0jLiaXNZboP+9DxDglJvR5GPL+tw/Oixx0VEeWGIb+FF/Y74WYl0Fy/rd2SFLF1t5qeQUvumDKN/18D20lAGAAA= diff --git a/java/com.sap.sailing.server/resources/stringmessages/XLF/StringMessages_zh_CN.properties.xlf b/java/com.sap.sailing.server/resources/stringmessages/XLF/StringMessages_zh_CN.properties.xlf index 812996cb168..c90179811fc 100644 --- a/java/com.sap.sailing.server/resources/stringmessages/XLF/StringMessages_zh_CN.properties.xlf +++ b/java/com.sap.sailing.server/resources/stringmessages/XLF/StringMessages_zh_CN.properties.xlf @@ -1,6 +1,6 @@ - +
        H4sIAAAAAAAAAKVUTW/bMAy9+1cQ2CEbkAVpdwvgQ5YuWIChDZr2sKPs0A1XWTIk2VkQ5L+PkvNhtzZarBfbEh/f4yNlGZHiQt3jk3BO3GU/KlQuNrwJ+/EBSIGpQzDYXx0GoDNAD+Hl9WEQmZA9l4iun+Tz/tvhy5tUEsUaTaKFWZ8YGlsMGrdzmCJqJq02enuPtpTO/iL1/EBOYuw3WTbshmLtXYWmIty+xPgY6GMwQC+svOiAX0AeMFVC7iy90vZNEMdYFDHczaSw1qfMSZHd4HpVJn8wdbHfg0wb8ChIPSz0byMsZEcsfAWFZ0sgKkFSJBKjTzCewJn+VuQ4hKtJ0L9BmxoqHGnVXcB3vd7Ftw3ajiJ4fNxyEAYvoqOGoVWqDc60MeyEhU6emqyt3A6NPg+NIXdaeSH9ATepzgt05LRZMhJt3aCTl9k5GjgKD1kfJwOSFHpaUZ+NYOVC1zuPbsVg4V1y7KJV93+NIW0p9dbeM4de8c5BtLXePFaPBSeQevLH9WRmWv9VHWPdkpRgnTAOrObS3vdTDOF6Ao5y7JYNNuavxXzdDUFRFEb/pVw4lDtgJN9q7MSWyVloobhmBvBn/FuXwTX3CqliKVDaUUZpCHOvjM5hNV3Cirviw+GCcZTaETxocKVRfBtm7awhFDwki1CRJQc7XRooLRrg0jLiaXNZboP+9DxDglJvR5GPL+tw/Oixx0VEeWGIb+FF/Y74WYl0Fy/rd2SFLF1t5qeQUvumDKN/18D20lAGAAA= diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java index 8e8a5c4965c..6596ebe725d 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java @@ -68,11 +68,13 @@ import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.RaceListener; import com.sap.sailing.domain.tracking.RaceTracker; +import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRegatta; import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; import com.sap.sailing.domain.tracking.TrackerManager; import com.sap.sailing.domain.tracking.WindStore; +import com.sap.sailing.domain.tracking.WindTracker; import com.sap.sailing.server.masterdata.DataImportLockWithProgress; import com.sap.sailing.server.simulation.SimulationService; import com.sap.sse.common.TimePoint; @@ -191,6 +193,11 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch */ void startTrackingWind(Regatta regatta, RaceDefinition race, boolean correctByDeclination); + /** + * If a {@link WindTracker} exists for {@code race}, it is stopped and the {@link RaceTrackingConnectivityParameters} + * through which the {@code race} was created are updated to not track wind when restoring that race upon a server + * restart. + */ void stopTrackingWind(Regatta regatta, RaceDefinition race) throws SocketException, IOException; /** @@ -632,4 +639,20 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch * yet, the given callback will be informed asynchronously on registration of the RaceTracker in question. */ void getRaceTrackerByRegattaAndRaceIdentifier(RegattaAndRaceIdentifier raceIdentifier, Consumer callback); + + /** + * When restoring tracked races was requested upon creation of this service and after the corresponding restore records + * were read from the persistent store, this method returns the number of races to be restored. Otherwise, it returns 0. + * + * @see #getNumberOfTrackedRacesRestored() + */ + int getNumberOfTrackedRacesToRestore(); + + /** + * When restoring tracked races was requested upon creation of this service, this method tells the number of races + * whose loading process has already been triggered. Otherwise, it returns 0. + * + * @see #getNumberOfTrackedRacesToRestore() + */ + int getNumberOfTrackedRacesRestored(); } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventServiceMXBean.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventServiceMXBean.java index 4d251c924c7..a7399bf3b8c 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventServiceMXBean.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventServiceMXBean.java @@ -9,4 +9,6 @@ package com.sap.sailing.server; */ public interface RacingEventServiceMXBean { public int getNumberOfLeaderboards(); + public int getNumberOfTrackedRacesToRestore(); + public int getNumberOfTrackedRacesRestored(); } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java index 62ba19f87c4..994d463e531 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java @@ -19,11 +19,14 @@ import org.osgi.framework.ServiceRegistration; import org.osgi.util.tracker.ServiceTracker; import org.osgi.util.tracker.ServiceTrackerCustomizer; +import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.common.tracking.impl.DoubleVectorFixImpl; import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl; import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; +import com.sap.sailing.domain.persistence.DomainObjectFactory; +import com.sap.sailing.domain.persistence.MongoObjectFactory; import com.sap.sailing.domain.persistence.racelog.tracking.FixMongoHandler; import com.sap.sailing.domain.persistence.racelog.tracking.impl.DoubleVectorFixMongoHandlerImpl; import com.sap.sailing.domain.persistence.racelog.tracking.impl.GPSFixMongoHandlerImpl; @@ -51,6 +54,8 @@ public class Activator implements BundleActivator { private static final Logger logger = Logger.getLogger(Activator.class.getName()); private static final String CLEAR_PERSISTENT_COMPETITORS_PROPERTY_NAME = "persistentcompetitors.clear"; + + private static final String RESTORE_TRACKED_RACES_PROPERTY_NAME = "restore.tracked.races"; private static ExtenderBundleTracker extenderBundleTracker; @@ -59,6 +64,8 @@ public class Activator implements BundleActivator { private RacingEventServiceImpl racingEventService; private final boolean clearPersistentCompetitors; + + private final boolean restoreTrackedRaces; private Set> registrations = new HashSet<>(); @@ -79,6 +86,8 @@ public class Activator implements BundleActivator { public Activator() { clearPersistentCompetitors = Boolean .valueOf(System.getProperty(CLEAR_PERSISTENT_COMPETITORS_PROPERTY_NAME, "" + false)); + restoreTrackedRaces = Boolean + .valueOf(System.getProperty(RESTORE_TRACKED_RACES_PROPERTY_NAME, "" + false)); logger.log(Level.INFO, "setting " + CLEAR_PERSISTENT_COMPETITORS_PROPERTY_NAME + " to " + clearPersistentCompetitors); // there is exactly one instance of the racingEventService in the whole server @@ -100,7 +109,7 @@ public class Activator implements BundleActivator { // instead. serviceFinderFactory = new CachedOsgiTypeBasedServiceFinderFactory(context); racingEventService = new RacingEventServiceImpl(clearPersistentCompetitors, serviceFinderFactory, - trackedRegattaListener, notificationService); + trackedRegattaListener, notificationService, restoreTrackedRaces); notificationService.setRacingEventService(racingEventService); masterDataImportClassLoaderServiceTracker = new ServiceTracker( @@ -114,11 +123,13 @@ public class Activator implements BundleActivator { // register the racing service in the OSGi registry racingEventService.setBundleContext(context); + context.registerService(MongoObjectFactory.class, racingEventService.getMongoObjectFactory(), /* properties */ null); + context.registerService(DomainObjectFactory.class, racingEventService.getDomainObjectFactory(), /* properties */ null); final Dictionary replicableServiceProperties = new Hashtable<>(); - replicableServiceProperties.put(Replicable.OSGi_Service_Registry_ID_Property_Name, - racingEventService.getId().toString()); + replicableServiceProperties.put(Replicable.OSGi_Service_Registry_ID_Property_Name, racingEventService.getId().toString()); context.registerService(Replicable.class.getName(), racingEventService, replicableServiceProperties); context.registerService(RacingEventService.class.getName(), racingEventService, null); + context.registerService(RaceLogResolver.class, racingEventService, null); context.registerService(ClearStateTestSupport.class.getName(), racingEventService, null); Dictionary properties = new Hashtable(); final GPSFixMongoHandlerImpl gpsFixMongoHandler = new GPSFixMongoHandlerImpl( diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/MediaLibrary.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/MediaLibrary.java index 55735076fb3..84e5e788b37 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/MediaLibrary.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/MediaLibrary.java @@ -129,7 +129,7 @@ class MediaLibrary { void deleteMediaTrack(MediaTrack mediaTrackToBeDeleted) { LockUtil.lockForWrite(lock); try { - MediaTrack deletedMediaTrack = getMediaTrackForClone(mediaTrackToBeDeleted); + MediaTrack deletedMediaTrack = resolveClone(mediaTrackToBeDeleted); mediaTracksByDbId.remove(deletedMediaTrack); updateMapByRace_Remove(deletedMediaTrack); } finally { @@ -140,7 +140,7 @@ class MediaLibrary { void titleChanged(MediaTrack changedMediaTrack) { LockUtil.lockForWrite(lock); try { - MediaTrack mediaTrack = getMediaTrackForClone(changedMediaTrack); + MediaTrack mediaTrack = resolveClone(changedMediaTrack); if (mediaTrack != null) { mediaTrack.title = changedMediaTrack.title; } @@ -152,7 +152,7 @@ class MediaLibrary { void urlChanged(MediaTrack changedMediaTrack) { LockUtil.lockForWrite(lock); try { - MediaTrack mediaTrack = getMediaTrackForClone(changedMediaTrack); + MediaTrack mediaTrack = resolveClone(changedMediaTrack); if (mediaTrack != null) { mediaTrack.url = changedMediaTrack.url; } @@ -164,7 +164,7 @@ class MediaLibrary { void mimeTypeChanged(MediaTrack changedMediaTrack) { LockUtil.lockForWrite(lock); try { - MediaTrack mediaTrack = getMediaTrackForClone(changedMediaTrack); + MediaTrack mediaTrack = resolveClone(changedMediaTrack); if (mediaTrack != null) { mediaTrack.mimeType = changedMediaTrack.mimeType; } @@ -176,7 +176,7 @@ class MediaLibrary { void startTimeChanged(MediaTrack changedMediaTrack) { LockUtil.lockForWrite(lock); try { - MediaTrack mediaTrack = getMediaTrackForClone(changedMediaTrack); + MediaTrack mediaTrack = resolveClone(changedMediaTrack); if (mediaTrack != null) { mediaTrack.startTime = changedMediaTrack.startTime; } @@ -188,7 +188,7 @@ class MediaLibrary { void durationChanged(MediaTrack changedMediaTrack) { LockUtil.lockForWrite(lock); try { - MediaTrack mediaTrack = getMediaTrackForClone(changedMediaTrack); + MediaTrack mediaTrack = resolveClone(changedMediaTrack); if (mediaTrack != null) { mediaTrack.duration = changedMediaTrack.duration; } @@ -200,7 +200,7 @@ class MediaLibrary { void assignedRacesChanged(MediaTrack changedMediaTrack) { LockUtil.lockForWrite(lock); try { - MediaTrack mediaTrack = getMediaTrackForClone(changedMediaTrack); + MediaTrack mediaTrack = resolveClone(changedMediaTrack); if (mediaTrack != null) { updateMapByRace_Remove(mediaTrack); //Cannot use updateCache_Update method, because race is changed mediaTrack.assignedRaces.clear(); @@ -214,11 +214,19 @@ class MediaLibrary { } } - private MediaTrack getMediaTrackForClone(MediaTrack mediaTrackClone) { + /** + * A {@link MediaTrack} is used on the server and in the GWT client and travels through + * GWT RPC calls. When received as a parameter it is never identical to any server-side + * {@link MediaTrack} object. This method resolves a clone such as those received as a + * GWT RPC method call parameter to a {@link MediaTrack} object known by the server, + * based on its {@link Object#hashCode()} and {@link Object#equals(Object)} method that + * is based on the {@link MediaTrack#dbId} field.

        + * + * It is also permissible to pass a server-side object to this method in which case it may + * be returned unchanged. + */ + private MediaTrack resolveClone(MediaTrack mediaTrackClone) { MediaTrack mediaTrack = mediaTracksByDbId.get(mediaTrackClone); - if (mediaTrack == mediaTrackClone) { - throw new IllegalArgumentException("Media track and clone must not be identical."); - } return mediaTrack; } @@ -292,7 +300,7 @@ class MediaLibrary { } public MediaTrack lookupMediaTrack(MediaTrack mediaTrack) { - return getMediaTrackForClone(mediaTrack); + return resolveClone(mediaTrack); } } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/OSGiBasedTrackedRegattaListener.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/OSGiBasedTrackedRegattaListener.java index dec61364235..5a430064c98 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/OSGiBasedTrackedRegattaListener.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/OSGiBasedTrackedRegattaListener.java @@ -2,6 +2,7 @@ package com.sap.sailing.server.impl; import java.util.function.Consumer; +import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.osgi.util.tracker.ServiceTracker; @@ -10,10 +11,18 @@ import com.sap.sailing.domain.tracking.TrackedRegatta; import com.sap.sailing.domain.tracking.TrackedRegattaListener; import com.sap.sse.util.ServiceTrackerFactory; +/** + * Convenient implementation of {@link TrackedRegattaListener} that delegates to {@link TrackedRegattaListener} + * instances found in the OSGi service registry. + */ public class OSGiBasedTrackedRegattaListener implements TrackedRegattaListener { - + private ServiceTracker serviceTracker; + /** + * A {@link ServiceTracker} is created using the given {@link BundleContext}. To ensure proper cleanup when the + * calling {@link BundleActivator} is stopped, call {@link #close()}. + */ public OSGiBasedTrackedRegattaListener(BundleContext context) { serviceTracker = ServiceTrackerFactory.createAndOpen(context, TrackedRegattaListener.class); serviceTracker.open(true); @@ -22,11 +31,15 @@ public class OSGiBasedTrackedRegattaListener implements TrackedRegattaListener { public void close() { serviceTracker.close(); } - + + /** + * Encapsulates the common code to iterate over all TrackedRegattaListener instances that are published to the OSGi + * service registry. + */ private void forEachListener(Consumer consumer) { - for(ServiceReference serviceReference : serviceTracker.getServiceReferences()) { + for (ServiceReference serviceReference : serviceTracker.getServiceReferences()) { TrackedRegattaListener listener = serviceTracker.getService(serviceReference); - if(listener != null) { + if (listener != null) { consumer.accept(listener); } } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RaceLogScoringReplicator.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RaceLogScoringReplicator.java index cd71012d9e0..8d5e943fecf 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RaceLogScoringReplicator.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RaceLogScoringReplicator.java @@ -1,5 +1,9 @@ package com.sap.sailing.server.impl; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + import com.sap.sailing.domain.abstractlog.race.CompetitorResult; import com.sap.sailing.domain.abstractlog.race.CompetitorResults; import com.sap.sailing.domain.abstractlog.race.RaceLog; @@ -8,6 +12,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogFinishPositioningConfirmed import com.sap.sailing.domain.abstractlog.race.RaceLogFinishPositioningListChangedEvent; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.ConfirmedFinishPositioningListFinder; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.FinishPositioningListFinder; +import com.sap.sailing.domain.abstractlog.race.impl.BaseRaceLogEventVisitor; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.RaceColumn; @@ -15,6 +20,7 @@ import com.sap.sailing.domain.base.RaceColumnListener; import com.sap.sailing.domain.common.MaxPointsReason; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.ResultDiscardingRule; +import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.racelog.RaceLogIdentifier; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.server.RacingEventService; @@ -90,9 +96,12 @@ public class RaceLogScoringReplicator implements RaceColumnListener { @Override public void raceLogEventAdded(RaceColumn raceColumn, RaceLogIdentifier raceLogIdentifier, RaceLogEvent event) { - if (event instanceof RaceLogFinishPositioningConfirmedEvent) { - handleFinishPositioningList(raceColumn, raceLogIdentifier, (RaceLogFinishPositioningConfirmedEvent) event); - } + event.accept(new BaseRaceLogEventVisitor() { + @Override + public void visit(RaceLogFinishPositioningConfirmedEvent event) { + handleFinishPositioningList(raceColumn, raceLogIdentifier, event); + } + }); } private void handleFinishPositioningList(RaceColumn raceColumn, RaceLogIdentifier raceLogIdentifier, RaceLogFinishPositioningConfirmedEvent event) { @@ -100,7 +109,7 @@ public class RaceLogScoringReplicator implements RaceColumnListener { if (leaderboard != null) { Fleet fleet = raceColumn.getFleetByName(raceLogIdentifier.getFleetName()); RaceLog raceLog = raceColumn.getRaceLog(fleet); - checkNeedForScoreCorrectionByResultsOfRaceCommittee(leaderboard, raceColumn, fleet, raceLog, event.getCreatedAt()); + checkNeedForScoreCorrectionByResultsOfRaceCommittee(leaderboard, raceColumn, fleet, raceLog, event.getCreatedAt(), event); } } @@ -125,13 +134,19 @@ public class RaceLogScoringReplicator implements RaceColumnListener { * * @param timePoint * the TimePoint at which the race committee confirmed their last rank list entered in the app. + * @param event + * the event that has announced the latest results; used with a finder to determine the effective changes to + * be applied to the leaderboard; if before {@code event} the race log had a result for a competitor that + * is still in the {@code leaderboard}'s score corrections and after applying {@code event} there is no + * result for that competitor anymore, remove those score corrections. */ - private void checkNeedForScoreCorrectionByResultsOfRaceCommittee(Leaderboard leaderboard, RaceColumn raceColumn, Fleet fleet, RaceLog raceLog, TimePoint timePoint) { + private void checkNeedForScoreCorrectionByResultsOfRaceCommittee(Leaderboard leaderboard, RaceColumn raceColumn, + Fleet fleet, RaceLog raceLog, TimePoint timePoint, RaceLogFinishPositioningConfirmedEvent event) { int numberOfCompetitorsInLeaderboard = Util.size(leaderboard.getCompetitors()); int numberOfCompetitorsInRace; CompetitorResults positioningList; numberOfCompetitorsInRace = getNumberOfCompetitorsInRace(raceColumn, fleet, numberOfCompetitorsInLeaderboard); - ConfirmedFinishPositioningListFinder confirmedPositioningListFinder = new ConfirmedFinishPositioningListFinder(raceLog); + final ConfirmedFinishPositioningListFinder confirmedPositioningListFinder = new ConfirmedFinishPositioningListFinder(raceLog); positioningList = confirmedPositioningListFinder.analyze(); if (positioningList == null) { // we expect this case for old sailing events such as ESS Singapore, Quingdao, where the confirmation event did not contain the finish @@ -141,27 +156,52 @@ public class RaceLogScoringReplicator implements RaceColumnListener { // RaceLogFinishPositioningConfirmedEvent event was found in the race log } if (positioningList != null) { + final Map newResultsByCompetitorId = new HashMap<>(); for (CompetitorResult positionedCompetitor : positioningList) { - Competitor competitor = service.getBaseDomainFactory().getExistingCompetitorById(positionedCompetitor.getCompetitorId()); + newResultsByCompetitorId.put(positionedCompetitor.getCompetitorId(), positionedCompetitor); + final Competitor competitor = service.getBaseDomainFactory().getExistingCompetitorById(positionedCompetitor.getCompetitorId()); // The score is updated when explicitly provided or when no penalty was set; // in turn, this means that when a penalty is set and no score is explicitly provided, // it is up to the scoring scheme to infer a penalty score for the MaxPointsReason. // See also bug 3955. - if (positionedCompetitor.getScore() != null - || positionedCompetitor.getMaxPointsReason() == null - || positionedCompetitor.getMaxPointsReason().equals(MaxPointsReason.NONE)) { + if (isNeedToCorrectScore(positionedCompetitor)) { int rankByRaceCommittee = getRankInPositioningListByRaceCommittee(positionedCompetitor); correctScoreInLeaderboard(leaderboard, raceColumn, timePoint, numberOfCompetitorsInRace, competitor, rankByRaceCommittee, positionedCompetitor.getScore()); } setMaxPointsReasonInLeaderboardIfNecessary(leaderboard, raceColumn, timePoint, positionedCompetitor.getMaxPointsReason(), competitor); } + final CompetitorResults oldResults = confirmedPositioningListFinder.analyzeIgnoring(event); + if (oldResults != null) { + // check if any of the old results need to be canceled from the leaderboard's score corrections, such + // as a cleared OCS; see also bug 4025 + for (final CompetitorResult oldResult : oldResults) { + if (!newResultsByCompetitorId.containsKey(oldResult.getCompetitorId())) { + final Competitor competitor = service.getBaseDomainFactory().getExistingCompetitorById(oldResult.getCompetitorId()); + // check what the leaderboard score correction looks like; if it matches the old result, remove the correction: + if (Util.equalsWithNull(leaderboard.getScoreCorrection().getMaxPointsReason(competitor, raceColumn, TimePoint.EndOfTime), oldResult.getMaxPointsReason())) { + service.apply(new UpdateLeaderboardMaxPointsReason(leaderboard.getName(), raceColumn.getName(), competitor.getId().toString(), /* reason */ null, timePoint)); + } + if (isNeedToCorrectScore(oldResult) && + Util.equalsWithNull(leaderboard.getScoreCorrection().getExplicitScoreCorrection(competitor, raceColumn), + getScoreFromRaceCommittee(leaderboard, raceColumn, timePoint, numberOfCompetitorsInRace, competitor, oldResult.getOneBasedRank(), oldResult.getScore()))) { + service.apply(new UpdateLeaderboardScoreCorrection(leaderboard.getName(), raceColumn.getName(), competitor.getId().toString(), /* correctedScore */ null, timePoint)); + } + } + } + } // Since the metadata update is used by the Sailing suite to determine the final state of a race, it has to // be triggered, even though no score correction may have been performed applyMetadataUpdate(leaderboard, timePoint, COMMENT_TEXT_ON_SCORE_CORRECTION); } } + private boolean isNeedToCorrectScore(CompetitorResult positionedCompetitor) { + return positionedCompetitor.getScore() != null + || positionedCompetitor.getMaxPointsReason() == null + || positionedCompetitor.getMaxPointsReason().equals(MaxPointsReason.NONE); + } + private boolean setMaxPointsReasonInLeaderboardIfNecessary(Leaderboard leaderboard, RaceColumn raceColumn, TimePoint timePoint, MaxPointsReason maxPointsReason, Competitor competitor) { boolean scoreHasBeenCorrected = false; @@ -177,6 +217,22 @@ public class RaceLogScoringReplicator implements RaceColumnListener { private void correctScoreInLeaderboard(Leaderboard leaderboard, RaceColumn raceColumn, TimePoint timePoint, final int numberOfCompetitorsInRace, Competitor competitor, int rankByRaceCommittee, Double optionalExplicitScore) { + final Double scoreByRaceCommittee = getScoreFromRaceCommittee(leaderboard, raceColumn, timePoint, + numberOfCompetitorsInRace, competitor, rankByRaceCommittee, optionalExplicitScore); + // Do ALWAYS apply score corrections from race committee + applyScoreCorrectionOperation(leaderboard, raceColumn, competitor, scoreByRaceCommittee, timePoint); + } + + /** + * If a non-{@code null} {@code optionalExplicitScore} is provided, it it returned. Otherwise, an implicit score is + * determined from the {@code rankByRaceCommittee} using the leaderboard scoring scheme's + * {@link ScoringScheme#getScoreForRank(Leaderboard, RaceColumn, Competitor, int, java.util.concurrent.Callable, com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher, TimePoint) + * getScoreForRank(...)} method. Usually, scoring schemes will return {@code null} as score for {@code 0} as + * (one-based) rank. + */ + private Double getScoreFromRaceCommittee(Leaderboard leaderboard, RaceColumn raceColumn, TimePoint timePoint, + final int numberOfCompetitorsInRace, Competitor competitor, int rankByRaceCommittee, + Double optionalExplicitScore) { final Double scoreByRaceCommittee; if (optionalExplicitScore == null) { scoreByRaceCommittee = leaderboard.getScoringScheme().getScoreForRank(leaderboard, raceColumn, competitor, @@ -184,8 +240,7 @@ public class RaceLogScoringReplicator implements RaceColumnListener { } else { scoreByRaceCommittee = optionalExplicitScore; } - // Do ALWAYS apply score corrections from race committee - applyScoreCorrectionOperation(leaderboard, raceColumn, competitor, scoreByRaceCommittee, timePoint); + return scoreByRaceCommittee; } private void applyScoreCorrectionOperation(Leaderboard leaderboard, RaceColumn raceColumn, Competitor competitor, Double correctedScore, TimePoint timePoint) { diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index 06c93aeca4a..5b386c00f69 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -9,6 +9,7 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.net.MalformedURLException; import java.net.SocketException; +import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; @@ -31,6 +32,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.Function; import java.util.logging.Level; @@ -93,6 +95,7 @@ import com.sap.sailing.domain.common.DataImportSubProgress; import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.MaxPointsReason; import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.RaceIdentifier; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaIdentifier; import com.sap.sailing.domain.common.RegattaName; @@ -437,7 +440,13 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes private final TrackedRegattaListener trackedRegattaListener; - private transient ConcurrentHashMap scoreCorrectionListenersByLeaderboard; + private int numberOfTrackedRacesToRestore; + + private final AtomicInteger numberOfTrackedRacesRestored; + + private transient final ConcurrentHashMap scoreCorrectionListenersByLeaderboard; + + private transient final ConcurrentHashMap connectivityParametersByRace; /** * Providing the constructor parameters for a new {@link RacingEventServiceImpl} instance is a bit tricky @@ -464,20 +473,21 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes * compatibility with prior releases that did not support a persistent competitor collection. */ public RacingEventServiceImpl() { - this(true, null); + this(/* clearPersistentCompetitorStore */ true, /* serviceFinderFactory */ null, /* restoreTrackedRaces */ false); } public RacingEventServiceImpl(WindStore windStore, SensorFixStore sensorFixStore, TypeBasedServiceFinderFactory serviceFinderFactory) { - this(true, windStore, sensorFixStore, serviceFinderFactory, /* sailingNotificationService */ null); + this(/* clearPersistentCompetitorStore */ true, windStore, sensorFixStore, serviceFinderFactory, + /* sailingNotificationService */ null, /* restoreTrackedRaces */ false); } void setBundleContext(BundleContext bundleContext) { this.bundleContext = bundleContext; } - public RacingEventServiceImpl(boolean clearPersistentCompetitorStore, final TypeBasedServiceFinderFactory serviceFinderFactory) { - this(clearPersistentCompetitorStore, serviceFinderFactory, null, /* sailingNotificationService */ null); + public RacingEventServiceImpl(boolean clearPersistentCompetitorStore, final TypeBasedServiceFinderFactory serviceFinderFactory, boolean restoreTrackedRaces) { + this(clearPersistentCompetitorStore, serviceFinderFactory, null, /* sailingNotificationService */ null, restoreTrackedRaces); } /** @@ -493,7 +503,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes * notification service is available, e.g., in test set-ups */ public RacingEventServiceImpl(boolean clearPersistentCompetitorStore, final TypeBasedServiceFinderFactory serviceFinderFactory, - TrackedRegattaListener trackedRegattaListener, SailingNotificationService sailingNotificationService) { + TrackedRegattaListener trackedRegattaListener, SailingNotificationService sailingNotificationService, boolean restoreTrackedRaces) { this((final RaceLogResolver raceLogResolver)-> { return new ConstructorParameters() { private final MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(serviceFinderFactory); @@ -506,11 +516,12 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes @Override public DomainFactory getBaseDomainFactory() { return competitorStore.getBaseDomainFactory(); } @Override public CompetitorStore getCompetitorStore() { return competitorStore; } }; - }, MediaDBFactory.INSTANCE.getDefaultMediaDB(), null, null, serviceFinderFactory ,trackedRegattaListener, sailingNotificationService); + }, MediaDBFactory.INSTANCE.getDefaultMediaDB(), null, null, serviceFinderFactory ,trackedRegattaListener, sailingNotificationService, restoreTrackedRaces); } private RacingEventServiceImpl(final boolean clearPersistentCompetitorStore, WindStore windStore, - SensorFixStore sensorFixStore, final TypeBasedServiceFinderFactory serviceFinderFactory, SailingNotificationService sailingNotificationService) { + SensorFixStore sensorFixStore, final TypeBasedServiceFinderFactory serviceFinderFactory, + SailingNotificationService sailingNotificationService, boolean restoreTrackedRaces) { this((final RaceLogResolver raceLogResolver)-> { return new ConstructorParameters() { private final MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(serviceFinderFactory); @@ -523,11 +534,11 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes @Override public DomainFactory getBaseDomainFactory() { return competitorStore.getBaseDomainFactory(); } @Override public CompetitorStore getCompetitorStore() { return competitorStore; } }; - }, MediaDBFactory.INSTANCE.getDefaultMediaDB(), windStore, sensorFixStore, serviceFinderFactory, null, sailingNotificationService); + }, MediaDBFactory.INSTANCE.getDefaultMediaDB(), windStore, sensorFixStore, serviceFinderFactory, null, sailingNotificationService, restoreTrackedRaces); } public RacingEventServiceImpl(final DomainObjectFactory domainObjectFactory, MongoObjectFactory mongoObjectFactory, - MediaDB mediaDB, WindStore windStore, SensorFixStore sensorFixStore) { + MediaDB mediaDB, WindStore windStore, SensorFixStore sensorFixStore, boolean restoreTrackedRaces) { this((final RaceLogResolver raceLogResolver)-> { return new ConstructorParameters() { @Override public DomainObjectFactory getDomainObjectFactory() { return domainObjectFactory; } @@ -535,7 +546,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes @Override public DomainFactory getBaseDomainFactory() { return domainObjectFactory.getBaseDomainFactory(); } @Override public CompetitorStore getCompetitorStore() { return getBaseDomainFactory().getCompetitorStore(); } }; - }, mediaDB, windStore, sensorFixStore, null, null, /* sailingNotificationService */ null); + }, mediaDB, windStore, sensorFixStore, null, null, /* sailingNotificationService */ null, restoreTrackedRaces); } /** @@ -549,12 +560,23 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes * @param sailingNotificationService * a notification service to call upon events worth notifying users about, or {@code null} if no * notification service is available, e.g., in test set-ups + * @param restoreTrackedRaces + * if {@code true}, the tracking connectivity parameters for the races last loaded in the server are + * {@link DomainObjectFactory#loadConnectivityParametersForRacesToRestore(Consumer) obtained} from the database, + * and {@link RaceTracker}s are + * {@link #addRace(RegattaIdentifier, RaceTrackingConnectivityParameters, long) created} for those, + * effectively restoring the server state to what it was last according to the database. If {@code false}, + * all restore information is + * {@link MongoObjectFactory#removeAllConnectivityParametersForRacesToRestore() cleared} from the + * database, and the server starts out with an empty list of tracked races. */ public RacingEventServiceImpl(Function constructorParametersProvider, MediaDB mediaDb, final WindStore windStore, final SensorFixStore sensorFixStore, TypeBasedServiceFinderFactory serviceFinderFactory, - TrackedRegattaListener trackedRegattaListener, SailingNotificationService sailingNotificationService) { + TrackedRegattaListener trackedRegattaListener, SailingNotificationService sailingNotificationService, boolean restoreTrackedRaces) { logger.info("Created " + this); + this.numberOfTrackedRacesRestored = new AtomicInteger(); this.scoreCorrectionListenersByLeaderboard = new ConcurrentHashMap<>(); + this.connectivityParametersByRace = new ConcurrentHashMap<>(); this.notificationService = sailingNotificationService; final ConstructorParameters constructorParameters = constructorParametersProvider.apply(this); this.domainObjectFactory = constructorParameters.getDomainObjectFactory(); @@ -590,15 +612,13 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes regattaTrackingCache = new ConcurrentHashMap<>(); regattaTrackingCacheLock = new NamedReentrantReadWriteLock("regattaTrackingCache for " + this, /* fair */false); raceTrackersByRegatta = new ConcurrentHashMap<>(); - raceTrackersByRegattaLock = new NamedReentrantReadWriteLock("raceTrackersByRegatta for " + this, /* fair */ - false); + raceTrackersByRegattaLock = new NamedReentrantReadWriteLock("raceTrackersByRegatta for " + this, /* fair */false); raceTrackersByID = new ConcurrentHashMap<>(); raceTrackersByIDLocks = new ConcurrentHashMap<>(); raceTrackerCallbacks = new ConcurrentHashMap<>(); leaderboardGroupsByName = new ConcurrentHashMap<>(); leaderboardGroupsByID = new ConcurrentHashMap<>(); - leaderboardGroupsByNameLock = new NamedReentrantReadWriteLock("leaderboardGroupsByName for " + this, /* fair */ - false); + leaderboardGroupsByNameLock = new NamedReentrantReadWriteLock("leaderboardGroupsByName for " + this, /* fair */false); leaderboardsByName = new ConcurrentHashMap(); leaderboardsByNameLock = new NamedReentrantReadWriteLock("leaderboardsByName for " + this, /* fair */false); operationExecutionListeners = new ConcurrentHashMap<>(); @@ -615,15 +635,12 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes this.sensorFixStore = sensorFixStore == null ? MongoSensorFixStoreFactory.INSTANCE.getMongoGPSFixStore( mongoObjectFactory, domainObjectFactory, serviceFinderFactory) : sensorFixStore; } catch (Exception e) { - e.printStackTrace(); + logger.log(Level.SEVERE, "Exception trying to obtain MongoDB sensor fix store", e); throw new RuntimeException(e); } - this.configurationMap = new DeviceConfigurationMapImpl(); this.serviceFinderFactory = serviceFinderFactory; - this.trackedRegattaListener = trackedRegattaListener == null ? EmptyTrackedRegattaListener.INSTANCE : trackedRegattaListener; - sailingServerConfiguration = domainObjectFactory.loadServerConfiguration(); final Iterable> loadedEventsWithRequireStoreFlag = loadStoredEvents(); loadStoredRegattas(); @@ -633,7 +650,6 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes loadMediaLibary(); loadStoredDeviceConfigurations(); loadAllRemoteSailingServersAndSchedulePeriodicEventCacheRefresh(); - // Stores all events which run through a data migration // Remark: must be called after loadLinksFromEventsToLeaderboardGroups(), otherwise would loose the Event -> LeaderboardGroup relation for (Pair eventAndRequireStoreFlag : loadedEventsWithRequireStoreFlag) { @@ -641,6 +657,29 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes mongoObjectFactory.storeEvent(eventAndRequireStoreFlag.getA()); } } + if (restoreTrackedRaces) { + restoreTrackedRaces(); + } else { + getMongoObjectFactory().removeAllConnectivityParametersForRacesToRestore(); + } + } + + private void restoreTrackedRaces() { + final ScheduledExecutorService backgroundExecutor = ThreadPoolUtil.INSTANCE.getDefaultBackgroundTaskThreadPoolExecutor(); + try { + numberOfTrackedRacesToRestore = getDomainObjectFactory().loadConnectivityParametersForRacesToRestore(params -> { + backgroundExecutor.execute(()->{ + try { + addRace(/* addToRegatta==null means "default regatta" */ null, params, RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS); + numberOfTrackedRacesRestored.incrementAndGet(); + } catch (Exception e) { + logger.log(Level.SEVERE, "Exception trying to restore race"+params, e); + } + }); + }); + } catch (MalformedURLException | URISyntaxException e) { + logger.log(Level.SEVERE, "Exception trying to obtain connectivity parameters for restoring tracked races", e); + } } @Override @@ -1198,8 +1237,8 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes Set trackers = raceTrackersByRegatta.get(regattaContext); if (trackers != null) { for (RaceTracker tracker : trackers) { - final Set races = tracker.getRaces(); - if (races != null && races.contains(r)) { + final RaceDefinition race = tracker.getRace(); + if (race == r) { return true; } } @@ -1429,6 +1468,15 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes } else { cacheAndReplicateDefaultRegatta(tracker.getRegatta()); } + getMongoObjectFactory().addConnectivityParametersForRaceToRestore(params); + // ensure that as soon as the RaceDefinition becomes available, the connectivity params are linked to it in connectivityParametersByRace + tracker.add((RaceTracker t) -> rememberConnectivityParametersForRace(t)); + if (params.isTrackWind()) { + // start wind tracking if requested, as soon as the RaceDefinition becomes available + tracker.add((RaceTracker t) -> + new Thread(()->startTrackingWind(regattaWithName, t.getRace(), params.isCorrectWindDirectionByMagneticDeclination()), + "Starting wind trackers for race "+t.getRace()).start()); + } } else { logger.warning("Race tracker with ID "+trackerID+" already found; not tracking twice to avoid race duplication"); WindStore existingTrackersWindStore = tracker.getWindStore(); @@ -1440,12 +1488,32 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes if (timeoutInMilliseconds != -1) { scheduleAbortTrackerAfterInitialTimeout(tracker, timeoutInMilliseconds); } - return tracker.getRacesHandle(); + return tracker.getRaceHandle(); } finally { unlockRaceTrackersById(trackerID, raceTrackersByIdLock); } } + /** + * Remembers the link between the {@link RaceDefinition} that the {@code tracker} just produced and its + * {@link RaceTracker#getConnectivityParams() connectivity parameters}. This is important for later removing those + * connectivity parameters from the + * {@link MongoObjectFactory#removeConnectivityParametersForRaceToRestore(RaceTrackingConnectivityParameters) DB} + * when the {@link #removeRace(Regatta, RaceDefinition) race is removed}. + * + * @param tracker + * must have produced a {@link RaceDefinition} which can be guaranteed by waiting for the callback on a + * {@link RaceTracker.RaceCreationListener} + * {@link RaceTracker#add(com.sap.sailing.domain.tracking.RaceTracker.RaceCreationListener) registered} + * on that tracker and not calling this method before that listener has fired. + */ + private void rememberConnectivityParametersForRace(RaceTracker tracker) { + final RaceDefinition race = tracker.getRace(); // guaranteed to be != null by callback + assert race != null; + final RaceTrackingConnectivityParameters connectivityParams = tracker.getConnectivityParams(); + connectivityParametersByRace.put(race, connectivityParams); + } + /** * The regatta and all its contained {@link Regatta#getAllRaces() races} are replicated to all replicas. * @@ -1966,11 +2034,9 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes final Set trackersForRegatta = raceTrackersByRegatta.get(regatta); if (trackersForRegatta != null) { for (RaceTracker raceTracker : trackersForRegatta) { - final Set races = raceTracker.getRaces(); - if (races != null) { - for (RaceDefinition race : races) { - stopTrackingWind(regatta, race); - } + final RaceDefinition race = raceTracker.getRace(); + if (race != null) { + stopTrackingWind(regatta, race); } raceTracker.stop(/* preemptive */false); final Object trackerId = raceTracker.getID(); @@ -2033,7 +2099,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes ScheduledFuture task = getScheduler().schedule(new Runnable() { @Override public void run() { - if (tracker.getRaces() == null || tracker.getRaces().isEmpty()) { + if (tracker.getRace() == null) { try { Regatta regatta = tracker.getRegatta(); logger.log(Level.SEVERE, "RaceDefinition for a race in regatta " + regatta.getName() @@ -2073,8 +2139,8 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes Iterator trackerIter = trackerSet.iterator(); while (trackerIter.hasNext()) { RaceTracker raceTracker = trackerIter.next(); - if (raceTracker.getRaces() != null && raceTracker.getRaces().contains(race)) { - logger.info("Found tracker to stop for races " + raceTracker.getRaces()); + if (raceTracker.getRace() == race) { + logger.info("Found tracker to stop for races " + raceTracker.getRace()); raceTracker.stop(/* preemptive */false); trackerIter.remove(); final Object trackerId = raceTracker.getID(); @@ -2090,6 +2156,14 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes logger.warning("Didn't find any trackers for regatta " + regatta); } stopTrackingWind(regatta, race); + final RaceTrackingConnectivityParameters connectivityParams = connectivityParametersByRace.get(race); + // update the "restore" handle for race in DB such that when restoring, no wind tracker will be requested for race + if (connectivityParams != null) { + connectivityParams.setTrackWind(false); + getMongoObjectFactory().addConnectivityParametersForRaceToRestore(connectivityParams); + } else { + logger.warning("Would have expected to find connectivity params for race "+race+" but didn't"); + } // if the last tracked race was removed, confirm that tracking for the entire regatta has stopped if (trackerSet == null || trackerSet.isEmpty()) { stopTracking(regatta); @@ -2189,6 +2263,10 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes public void removeRace(Regatta regatta, RaceDefinition race) throws MalformedURLException, IOException, InterruptedException { logger.info("Removing the race " + race + "..."); + final RaceTrackingConnectivityParameters connectivityParams = connectivityParametersByRace.remove(race); + if (connectivityParams != null) { + getMongoObjectFactory().removeConnectivityParametersForRaceToRestore(connectivityParams); + } stopAllTrackersForWhichRaceIsLastReachable(regatta, race); stopTrackingWind(regatta, race); TrackedRace trackedRace = getExistingTrackedRace(regatta, race); @@ -2258,40 +2336,27 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes Iterator trackerIter = raceTrackersByRegatta.get(regatta).iterator(); while (trackerIter.hasNext()) { RaceTracker raceTracker = trackerIter.next(); - if (raceTracker.getRaces() != null && raceTracker.getRaces().contains(race)) { - boolean foundReachableRace = false; - for (RaceDefinition raceTrackedByTracker : raceTracker.getRaces()) { - if (raceTrackedByTracker != race && isReachable(regatta, raceTrackedByTracker)) { - foundReachableRace = true; - break; - } + if (raceTracker.getRace() == race) { + // firstly stop the tracker + raceTracker.stop(/* preemptive */true); + // remove it from the raceTrackers by Regatta + trackerIter.remove(); + final Object trackerId = raceTracker.getID(); + final NamedReentrantReadWriteLock lock = lockRaceTrackersById(trackerId); + try { + raceTrackersByID.remove(trackerId); + } finally { + unlockRaceTrackersById(trackerId, lock); } - if (!foundReachableRace) { - // firstly stop the tracker - raceTracker.stop(/* preemptive */true); - // remove it from the raceTrackers by Regatta - trackerIter.remove(); - final Object trackerId = raceTracker.getID(); - final NamedReentrantReadWriteLock lock = lockRaceTrackersById(trackerId); - try { - raceTrackersByID.remove(trackerId); - } finally { - unlockRaceTrackersById(trackerId, lock); - } - // if the last tracked race was removed, remove the entire regatta - if (raceTrackersByRegatta.get(regatta).isEmpty()) { - stopTracking(regatta); - } + // if the last tracked race was removed, remove the entire regatta + if (raceTrackersByRegatta.get(regatta).isEmpty()) { + stopTracking(regatta); } } } } } - private boolean isReachable(Regatta regatta, RaceDefinition race) { - return Util.contains(regatta.getAllRaces(), race); - } - @Override public void startTrackingWind(Regatta regatta, RaceDefinition race, boolean correctByDeclination) { for (WindTrackerFactory windTrackerFactory : getWindTrackerFactories()) { @@ -2857,6 +2922,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes } finally { LockUtil.unlockAfterWrite(leaderboardsByNameLock); } + connectivityParametersByRace.clear(); eventsById.clear(); mediaLibrary.clear(); competitorStore.clear(); @@ -3261,7 +3327,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes result = Collections.singleton((WindTrackerFactory) ExpeditionWindTrackerFactory.getInstance()); } else { ServiceTracker tracker = new ServiceTracker( - bundleContext, WindTrackerFactory.class.getName(), null); + bundleContext, WindTrackerFactory.class, null); tracker.open(); result = new HashSet<>(); for (WindTrackerFactory factory : tracker.getServices(new WindTrackerFactory[0])) { @@ -3482,7 +3548,9 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes Iterable trackedRaces = trackedRegatta.getTrackedRaces(); for (TrackedRace trackedRace : trackedRaces) { trackedRace.setPolarDataService(service); - service.insertExistingFixes(trackedRace); + if (service != null) { + service.insertExistingFixes(trackedRace); + } } } catch (Throwable e) { logger.log(Level.SEVERE, "Error reconstructing the polars for tracked races", e); @@ -3495,9 +3563,8 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes public void unsetPolarDataService(PolarDataService service) { if (polarDataService == service) { - polarDataService.unregisterDomainFactory(baseDomainFactory); polarDataService = null; - setPolarDataService(null); + setPolarDataServiceOnAllTrackedRaces(null); } } @@ -3562,16 +3629,20 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes @Override public void getRaceTrackerByRegattaAndRaceIdentifier(RegattaAndRaceIdentifier raceIdentifier, Consumer callback) { + final Regatta regatta; LockUtil.lockForRead(regattasByNameLock); - Regatta regatta = regattasByName.get(raceIdentifier.getRegattaName()); - LockUtil.unlockAfterRead(regattasByNameLock); + try { + regatta = regattasByName.get(raceIdentifier.getRegattaName()); + } finally { + LockUtil.unlockAfterRead(regattasByNameLock); + } if (regatta != null) { LockUtil.lockForRead(raceTrackersByRegattaLock); try { Set raceTrackersForRegatta = raceTrackersByRegatta.get(regatta); - if(raceTrackersForRegatta != null) { + if (raceTrackersForRegatta != null) { for (RaceTracker raceTracker : raceTrackersForRegatta) { - if (raceTracker.getRaceIdentifiers().contains(raceIdentifier)) { + if (Util.equalsWithNull(raceTracker.getRaceIdentifier(), raceIdentifier)) { callback.accept(raceTracker); return; } @@ -3598,15 +3669,26 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes private void notifyListenersForNewRaceTracker(RaceTracker tracker) { LockUtil.lockForRead(raceTrackersByRegattaLock); try { - tracker.getRaceIdentifiers().forEach((raceIdentifier) -> { + final RaceIdentifier raceIdentifier = tracker.getRaceIdentifier(); + if (raceIdentifier != null) { Set> callbacks = getRaceTrackerCallbacks().remove(raceIdentifier); if (callbacks != null) { callbacks.forEach((callback) -> callback.accept(tracker)); } - }); + }; } finally { LockUtil.unlockAfterRead(raceTrackersByRegattaLock); } } + + @Override + public int getNumberOfTrackedRacesToRestore() { + return numberOfTrackedRacesToRestore; + } + + @Override + public int getNumberOfTrackedRacesRestored() { + return numberOfTrackedRacesRestored.get(); + } } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceMXBeanImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceMXBeanImpl.java index ccfff914913..39baae70ae6 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceMXBeanImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceMXBeanImpl.java @@ -19,4 +19,14 @@ public class RacingEventServiceMXBeanImpl implements RacingEventServiceMXBean { public int getNumberOfLeaderboards() { return getRacingEventService().getLeaderboards().size(); } + + @Override + public int getNumberOfTrackedRacesToRestore() { + return getRacingEventService().getNumberOfTrackedRacesToRestore(); + } + + @Override + public int getNumberOfTrackedRacesRestored() { + return getRacingEventService().getNumberOfTrackedRacesRestored(); + } } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java index 4bed05fb47a..2b3736f605a 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java @@ -27,6 +27,7 @@ import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.Tack; +import com.sap.sailing.domain.common.TargetTimeInfo; import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.tracking.GPSFix; @@ -58,7 +59,6 @@ import com.sap.sailing.domain.tracking.WindStore; import com.sap.sailing.domain.tracking.WindWithConfidence; import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl; import com.sap.sailing.domain.tracking.impl.EmptyWindStore; -import com.sap.sse.common.Duration; import com.sap.sse.common.IsManagedByCache; import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util; @@ -604,7 +604,7 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials { } @Override - public Duration getEstimatedTimeToComplete(TimePoint timepoint) + public TargetTimeInfo getEstimatedTimeToComplete(TimePoint timepoint) throws NotEnoughDataHasBeenAddedException, NoWindException { return null; } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/ImportMasterDataOperation.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/ImportMasterDataOperation.java index 4fc6033d2ad..d861dd49adc 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/ImportMasterDataOperation.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/ImportMasterDataOperation.java @@ -72,6 +72,8 @@ public class ImportMasterDataOperation extends private static final long serialVersionUID = 3131715325307370303L; private static final Logger logger = Logger.getLogger(ImportMasterDataOperation.class.getName()); + + private static final int BATCH_SIZE_FOR_IMPORTING_FIXES = 5000; private final TopLevelMasterData masterData; @@ -123,6 +125,9 @@ public class ImportMasterDataOperation extends progress.setOverAllProgressPct(0.5); progress.setCurrentSubProgressPct(0); createWindTracks(toState); + progress.setCurrentSubProgress(DataImportSubProgress.IMPORT_SENSOR_FIXES); + progress.setOverAllProgressPct(0.8); + progress.setCurrentSubProgressPct(0); importRaceLogTrackingGPSFixes(toState); if (masterData.getDeviceConfigurations() != null) { importDeviceConfigurations(toState); @@ -394,7 +399,7 @@ public class ImportMasterDataOperation extends } i++; progress.setCurrentSubProgressPct((double) i / numOfWindTracks); - progress.setOverAllProgressPct(0.5 + (0.5) * ((double) i / numOfWindTracks)); + progress.setOverAllProgressPct(0.5 + (0.3) * ((double) i / numOfWindTracks)); } } @@ -404,28 +409,44 @@ public class ImportMasterDataOperation extends Map> raceLogTrackingFixes = masterData.getRaceLogTrackingFixes(); if (raceLogTrackingFixes != null) { SensorFixStore store = toState.getSensorFixStore(); + int i = 0; + final int numberOfDevices = raceLogTrackingFixes.size(); for (Entry> entry : raceLogTrackingFixes.entrySet()) { DeviceIdentifier device = entry.getKey(); + final Collection fixesToAddAsBatch = new ArrayList<>(BATCH_SIZE_FOR_IMPORTING_FIXES); for (Timed fixToAdd : entry.getValue()) { - try { - if (fixToAdd instanceof CompactGPSFixMovingImpl) { - CompactGPSFixMovingImpl gpsFix = (CompactGPSFixMovingImpl) fixToAdd; - fixToAdd = new GPSFixMovingImpl(gpsFix.getPosition(), fixToAdd.getTimePoint(), - ((CompactGPSFixMovingImpl) fixToAdd).getSpeed()); - } else if (fixToAdd instanceof CompactGPSFixImpl) { - CompactGPSFixImpl gpsFix = (CompactGPSFixImpl) fixToAdd; - fixToAdd = new GPSFixImpl(gpsFix.getPosition(), fixToAdd.getTimePoint()); - } - store.storeFix(device, fixToAdd); - } catch (NoCorrespondingServiceRegisteredException e) { - logger.severe("Failed to store race log tracking fix while importing."); - e.printStackTrace(); + if (fixToAdd instanceof CompactGPSFixMovingImpl) { + CompactGPSFixMovingImpl gpsFix = (CompactGPSFixMovingImpl) fixToAdd; + fixToAdd = new GPSFixMovingImpl(gpsFix.getPosition(), fixToAdd.getTimePoint(), + ((CompactGPSFixMovingImpl) fixToAdd).getSpeed()); + } else if (fixToAdd instanceof CompactGPSFixImpl) { + CompactGPSFixImpl gpsFix = (CompactGPSFixImpl) fixToAdd; + fixToAdd = new GPSFixImpl(gpsFix.getPosition(), fixToAdd.getTimePoint()); + } + fixesToAddAsBatch.add(fixToAdd); + if (fixesToAddAsBatch.size() == BATCH_SIZE_FOR_IMPORTING_FIXES) { + storeFixes(store, device, fixesToAddAsBatch); } } + if (!fixesToAddAsBatch.isEmpty()) { + storeFixes(store, device, fixesToAddAsBatch); + } + i++; + progress.setCurrentSubProgressPct((double) i / numberOfDevices); } } } + private void storeFixes(SensorFixStore store, DeviceIdentifier device, final Collection fixesToAddAsBatch) { + try { + store.storeFixes(device, fixesToAddAsBatch); + fixesToAddAsBatch.clear(); + } catch (NoCorrespondingServiceRegisteredException e) { + logger.severe("Failed to store race log tracking fixes while importing."); + e.printStackTrace(); + } + } + private void createRegattas(RacingEventService toState, LeaderboardGroup leaderboardGroup) { Iterable leaderboards = leaderboardGroup.getLeaderboards(); for (Leaderboard leaderboard : leaderboards) { diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/StopTrackingRegatta.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/StopTrackingRegatta.java deleted file mode 100755 index 359ce916824..00000000000 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/StopTrackingRegatta.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.sap.sailing.server.operationaltransformation; - -import java.io.IOException; -import java.net.MalformedURLException; - -import com.sap.sailing.domain.base.Regatta; -import com.sap.sailing.domain.common.RegattaIdentifier; -import com.sap.sailing.server.RacingEventService; -import com.sap.sailing.server.RacingEventServiceOperation; - -public class StopTrackingRegatta extends AbstractRacingEventServiceOperation { - private static final long serialVersionUID = -651066062098923320L; - private final RegattaIdentifier regattaIdentifier; - - public StopTrackingRegatta(RegattaIdentifier regattaIdentifier) { - super(); - this.regattaIdentifier = regattaIdentifier; - } - - @Override - public Void internalApplyTo(RacingEventService toState) throws MalformedURLException, IOException, InterruptedException { - Regatta regatta = toState.getRegatta(regattaIdentifier); - if (regatta != null) { - toState.stopTracking(regatta); - } - return null; - } - - @Override - public RacingEventServiceOperation transformClientOp(RacingEventServiceOperation serverOp) { - // TODO Auto-generated method stub - return null; - } - - @Override - public RacingEventServiceOperation transformServerOp(RacingEventServiceOperation clientOp) { - // TODO Auto-generated method stub - return null; - } - -} diff --git a/java/com.sap.sailing.simulator.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.simulator.test/META-INF/MANIFEST.MF index 98fb40d647d..4affde18434 100644 --- a/java/com.sap.sailing.simulator.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.simulator.test/META-INF/MANIFEST.MF @@ -6,7 +6,7 @@ Bundle-Version: 1.0.0.qualifier Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Require-Bundle: com.sap.sailing.simulator;bundle-version="1.0.0", - org.junit4;bundle-version="4.8.2", + org.junit;bundle-version="4.8.2", com.sap.sailing.domain;bundle-version="1.0.0", com.sap.sailing.domain.common;bundle-version="1.0.0", com.sap.sailing.server;bundle-version="1.0.0", diff --git a/java/com.sap.sailing.simulator/META-INF/MANIFEST.MF b/java/com.sap.sailing.simulator/META-INF/MANIFEST.MF index 6a6c653f9fe..925a1d3f660 100644 --- a/java/com.sap.sailing.simulator/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.simulator/META-INF/MANIFEST.MF @@ -10,7 +10,7 @@ Require-Bundle: com.sap.sailing.domain.common;bundle-version="1.0.0", com.sap.sailing.domain.tractracadapter, com.sap.sse, com.sap.sse.common, - org.apache.httpcomponents.httpclient;bundle-version="4.2.6", + org.apache.httpcomponents.httpclient;bundle-version="[4.2.6,4.3.0)", org.json.simple;bundle-version="1.1.0" Bundle-ActivationPolicy: lazy Import-Package: org.osgi.framework;version="1.6.0" diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java index 21e724d6612..6da70b8ec64 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java @@ -23,7 +23,7 @@ public class PolarDiagramGPS extends PolarDiagramBase { private static final long serialVersionUID = -9219705955440602679L; private final PolarDataService polarData; - private double avgSpeed; + private double avgSpeedInKnots; public PolarDiagramGPS(BoatClass boatClass, PolarDataService polarData) throws SparseSimulationDataException { this.boatClass = boatClass; @@ -53,7 +53,7 @@ public class PolarDiagramGPS extends PolarDiagramBase { SpeedWithBearing beatPort; SpeedWithBearing beatStar; int avgCount = 0; - avgSpeed = 0; + avgSpeedInKnots = 0; for (int i = 0; i < windSpeeds.size(); i++) { try { beatPort = this.polarData.getAverageSpeedWithBearing(this.boatClass, windSpeeds.get(i), LegType.UPWIND, @@ -71,14 +71,14 @@ public class PolarDiagramGPS extends PolarDiagramBase { beatAngles.add(avgBeatAngle); Speed avgBeatSpeed = new KnotSpeedImpl((beatStar.getKnots() + beatPort.getKnots()) / 2.0); beatSpeed.add(avgBeatSpeed); - avgSpeed += avgBeatSpeed.getKnots(); + avgSpeedInKnots += avgBeatSpeed.getKnots(); avgCount++; } else { beatAngles.add(null); beatSpeed.add(null); } } - avgSpeed /= avgCount; + avgSpeedInKnots /= avgCount; // initialize jibe-angles and -speeds SpeedWithBearing jibePort; @@ -175,7 +175,7 @@ public class PolarDiagramGPS extends PolarDiagramBase { } public double getAvgSpeed() { - return this.avgSpeed; + return this.avgSpeedInKnots; } } diff --git a/java/com.sap.sailing.targetplatform.base/features/target-base/feature.xml b/java/com.sap.sailing.targetplatform.base/features/target-base/feature.xml index 7b1553c431c..b2ab1335057 100755 --- a/java/com.sap.sailing.targetplatform.base/features/target-base/feature.xml +++ b/java/com.sap.sailing.targetplatform.base/features/target-base/feature.xml @@ -19,13 +19,6 @@ version="1.3.0" unpack="false"/> - - - - - - - - - - - - Jackson libraries - - - - NA. - - - - - - - - - - - - - - - - - - - - - + + + + + Jackson libraries + + + + NA. + + + + + + + + + + + diff --git a/java/com.sap.sailing.targetplatform.base/features/target-test/feature.xml b/java/com.sap.sailing.targetplatform.base/features/target-test/feature.xml index d406f0b67c9..9abddac5a66 100644 --- a/java/com.sap.sailing.targetplatform.base/features/target-test/feature.xml +++ b/java/com.sap.sailing.targetplatform.base/features/target-test/feature.xml @@ -12,48 +12,6 @@ NA. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/com.sap.sailing.velum.resultimport.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.velum.resultimport.test/META-INF/MANIFEST.MF index 37750879a18..78d95d14d7c 100644 --- a/java/com.sap.sailing.velum.resultimport.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.velum.resultimport.test/META-INF/MANIFEST.MF @@ -7,7 +7,7 @@ Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Import-Package: org.osgi.framework;version="1.5.0" Bundle-ActivationPolicy: lazy -Require-Bundle: org.junit4;bundle-version="4.8.2", +Require-Bundle: org.junit;bundle-version="4.8.2", com.sap.sailing.velum.resultimport, com.sap.sailing.domain.common, com.sap.sailing.domain, diff --git a/java/com.sap.sailing.www/images/JConsoleNumberOfTrackedRacesRestored.png b/java/com.sap.sailing.www/images/JConsoleNumberOfTrackedRacesRestored.png new file mode 100644 index 00000000000..b8dee7f697f Binary files /dev/null and b/java/com.sap.sailing.www/images/JConsoleNumberOfTrackedRacesRestored.png differ diff --git a/java/com.sap.sailing.www/images/JConsoleNumberOfTrackedRacesToRestore.png b/java/com.sap.sailing.www/images/JConsoleNumberOfTrackedRacesToRestore.png new file mode 100644 index 00000000000..0b380e80aff Binary files /dev/null and b/java/com.sap.sailing.www/images/JConsoleNumberOfTrackedRacesToRestore.png differ diff --git a/java/com.sap.sailing.www/release_notes_admin.html b/java/com.sap.sailing.www/release_notes_admin.html index 9ae68cc6c15..20a2e97afb9 100644 --- a/java/com.sap.sailing.www/release_notes_admin.html +++ b/java/com.sap.sailing.www/release_notes_admin.html @@ -23,6 +23,104 @@

        Release Notes - Administration Console

        +

        January 2017

        +
          +
        • Filter boxes now support quoting phrases, such as "Race 2" to make them a single search term. + Previously, when entering Race 2 this would match all lines containing Race as well + as all lines containing the digit 2, such as it occurs in 2015 or 2016. + Enclosing the phrase with double quotes as in "Race 2" will consider the full string in quotes + as a search term. In the unlikely case of searching for a double quote you can do that by prefixing + the double quote with a backslash character, as in \". +
        • +
        • When changing the start/end of tracking times in the "Smartphone Tracking" tab, it is now possible + to "unset" one or both of these time stamps. Next to the date/time entry field these is now a + "Set" checkbox. When the date/time field is modified, the box is automatically checked because + it seems reasonable to assume that the user wants this change to be committed. When committing + an empty date/time field with the "Set" checkbox ticked, the null time stamp which + makes this an open tracking interval is committed and will override any inference that may be + made based on the race's start and finish time. When unchecking the "Set" checkbox, the previously + committed timestamp is revoked (regardless of the contents of the date/time field), allowing for + inference to take place if the regatta has been configured for tracking times inference. +
        • +
        • There is a new permission, DATA_MINING, which is now required in order to use the + /gwt/DataMining.html entry point and the related back-end service. +
        • +
        • Quoted filtering keywords may now contain leading and/or trailing space characters that are + considered when applying the filter. For example, if you have regatta names "Regatta I - 2017" and + "Regatta II - 2017" and you would like to filter for "Regatta I" then you now may enter the + search term "Regatta I " (with a trailing blank) which then will no longer match "Regatta II - 2017". +
        • +
        +

        December 2016

        +
          +
        • Partial replication is now possible. If the system property replicate.on.start + that controls automatic replication upon start-up provides only a partial comma-separated + list of replicables, the connection to the master server (configured by the other + replication-related system properties such as replicate.master.servlet.host etc.) + will be used to replicate only those replicables whose IDs are specified in + replicate.on.start. All other replicables will operate locally. + Usually, the replicate.on.start system property is controlled + by the environment variable REPLICATE_ON_START that is set in the env.sh + configuration file used during server startup and influenced by the server environments + from http://releases.sapsailing.com/environments + and overridden by the user details provided to the instance through the AWS EC2 machinery.

          + The Administration Console (/gwt/AdminConsole.html entry point) will now + show which replicables a replica replicates, both, on the replica's administration + console's "Replication" panel, as well as on the master's "Replication" panel, there + for each replica.

          + The master will restrict broadcasting its operations to those replicables for which + at least one replica has subscribed. While the initial load is requested individually + for each replica and can therefore be tailored to the replicas requested, the operation + broadcast channel is not specific to any replica. It therefore contains the operations + of all replicables for which at least one replica has subscribed. Replicas receiving + operations for replicables for which they did not subscribe will simply drop those + operations (note, however, that the replica will still have to read the operations from the + channel before dropping; this way, registering a replica may have bandwidth effects for + other replicas). Similarly, "backward replication" from a replica to a master will only + take place for those replicables for which the replica has requested replication from (and + therefore also "to") the master.

          + With this it is now possible to replicate only the Security Service, using ID + com.sap.sse.security.impl.SecurityServiceImpl, running all other replicables + in "master mode." This will let the instance share user, permission, role and session management + details with the instance from which the Security Service is being replicated while + having its own master objects for all other replicables, such as the sailing application + (RacingEventService), mail service or the polar data service. Note that with this feature + the server replicating another instance's Security Service will grant the roles and permissions + to subjects authenticated against that replicated Security Service. For example, if a user + has been granted the admin role by that service then the user has that role also + in all other server instances replicating that Security Service. Therefore, use this feature + with extreme caution. Keep in mind that in the near future we will extend the permissions and + role concept by the possibility to restrict permissions and roles to "scopes" such as an + event or a regatta or an account. See also + https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=3504. +

        • +
        • The server can now optionally restore all races it had tracked the last time. This is + similar to a web browser's capability to restore the tabs the user had open when the browser + was terminated or crashed. To enable this feature, use the system property + restore.tracked.races, using the command line option -Drestore.tracked.races=true. + So far, the default for this system property is false, so by default, when the property + is not provided at all on the command line then the tracked races loaded last won't be restored, + and the server will start out with an empty set of tracked races.

          + Note that the wind tracking properties, as set when initially tracking the race, are also restored + to their last state. If a live race has last been tracked with live wind tracking, with wind directions + being corrected by the local magnetic declination, then upon server restore the same live tracking will + be started again, including live wind tracking with the same declination correction settings.

          + When the "Stop Tracking" feature is used, implicitly tracking wind will be stopped in case live wind + tracking had previously been requested for that race. This change is also considered during the restore + process where such a race will no longer receive live wind tracking upon restoring it.

          + When a tracked race is removed from the server then it will not be restored anymore upon the next + server restart with the restore option enabled. When an entire regatta with a number of tracked + races linked into it is removed then the tracked races are removed as well and will not be + restored upon server re-start.

          + The restore progress can be monitored using the JConsole or any other JMX monitoring tool. In the + com.sap.sailing category you will find the RacingEventService object. Among its + attributes there are now the two new ones: NumberOfTrackedRacesToRestore and + NumberOfTrackedRacesRestored. See the following two screen shots:

          +

          + +

        • +
        +

        November 2016

        • When launching a server, a system property polardata.source.url can diff --git a/java/com.sap.sailing.xmlexport.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.xmlexport.test/META-INF/MANIFEST.MF index 928da6af4d1..2568e3beedd 100644 --- a/java/com.sap.sailing.xmlexport.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.xmlexport.test/META-INF/MANIFEST.MF @@ -6,7 +6,7 @@ Bundle-Version: 1.0.0.qualifier Bundle-Vendor: SAP Fragment-Host: com.sap.sailing.xmlexport Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Require-Bundle: org.junit4;bundle-version="4.8.2", +Require-Bundle: org.junit;bundle-version="4.8.2", com.sap.sse.common, com.sap.sailing.domain.test;bundle-version="1.0.0", com.tractrac.clientmodule;bundle-version="3.0.7" diff --git a/java/com.sap.sailing.xrr.resultimport.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.xrr.resultimport.test/META-INF/MANIFEST.MF index 1d11bdf45a4..d8ca9e79cdf 100644 --- a/java/com.sap.sailing.xrr.resultimport.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.xrr.resultimport.test/META-INF/MANIFEST.MF @@ -6,7 +6,7 @@ Bundle-Version: 1.0.0.qualifier Bundle-Vendor: SAP Fragment-Host: com.sap.sailing.xrr.resultimport Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Require-Bundle: org.junit4;bundle-version="4.8.2", +Require-Bundle: org.junit;bundle-version="4.8.2", com.sap.sse.common Export-Package: com.sap.sailing.xrr.resultimport.test Import-Package: com.sap.sailing.xrr.schema diff --git a/java/com.sap.sailing.yachtscoring.resultimport.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.yachtscoring.resultimport.test/META-INF/MANIFEST.MF index d3021c8f440..d09158cd622 100644 --- a/java/com.sap.sailing.yachtscoring.resultimport.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.yachtscoring.resultimport.test/META-INF/MANIFEST.MF @@ -13,7 +13,7 @@ Import-Package: com.sap.sailing.domain.common, com.sap.sailing.xrr.schema, org.osgi.framework;version="1.5.0" Fragment-Host: com.sap.sailing.yachtscoring.resultimport -Require-Bundle: org.junit4;bundle-version="4.8.2", +Require-Bundle: org.junit;bundle-version="4.8.2", com.sap.sse.common, org.mockito.mockito-core;bundle-version="1.9.5", org.objenesis;bundle-version="1.3.0", diff --git a/java/com.sap.sse.common.test/META-INF/MANIFEST.MF b/java/com.sap.sse.common.test/META-INF/MANIFEST.MF index cc8d376e601..ee329444eca 100644 --- a/java/com.sap.sse.common.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.common.test/META-INF/MANIFEST.MF @@ -9,5 +9,5 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Require-Bundle: org.mockito.mockito-core;bundle-version="1.9.5", org.hamcrest;bundle-version="1.1.0", org.objenesis;bundle-version="1.3.0", - org.junit4;bundle-version="4.8.2" + org.junit;bundle-version="4.8.2" Export-Package: com.sap.sse.common.test diff --git a/java/com.sap.sse.common.test/src/com/sap/sse/common/test/SplittingStringsTest.java b/java/com.sap.sse.common.test/src/com/sap/sse/common/test/SplittingStringsTest.java new file mode 100755 index 00000000000..bc249c98173 --- /dev/null +++ b/java/com.sap.sse.common.test/src/com/sap/sse/common/test/SplittingStringsTest.java @@ -0,0 +1,39 @@ +package com.sap.sse.common.test; + +import static com.sap.sse.common.Util.isEmpty; +import static com.sap.sse.common.Util.splitAlongWhitespaceRespectingDoubleQuotedPhrases; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; + +import org.junit.Test; + +import com.sap.sse.common.Util; + +/** + * Tests {@link Util#splitAlongWhitespaceRespectingDoubleQuotedPhrases(String)} + * + * @author Axel Uhl (d043530) + * + */ +public class SplittingStringsTest { + @Test + public void testSimpleSplit() { + assertNull(splitAlongWhitespaceRespectingDoubleQuotedPhrases(null)); + assertEquals(Arrays.asList("a", "b", "c"), splitAlongWhitespaceRespectingDoubleQuotedPhrases("a b c")); + assertEquals(Arrays.asList("a", "b c"), splitAlongWhitespaceRespectingDoubleQuotedPhrases("a \"b c\"")); + assertEquals(Arrays.asList("a", "b \" c"), splitAlongWhitespaceRespectingDoubleQuotedPhrases("a \"b \\\" c\"")); + assertEquals(Arrays.asList("a", "bc", "de"), splitAlongWhitespaceRespectingDoubleQuotedPhrases("a \"bc\"de")); + assertEquals(Arrays.asList("a", "bc", "de"), splitAlongWhitespaceRespectingDoubleQuotedPhrases("a\"bc\"de ")); + assertEquals(Arrays.asList("a", "b \" c"), splitAlongWhitespaceRespectingDoubleQuotedPhrases("a \"b \\\" c\"")); + assertEquals(Arrays.asList(" "), splitAlongWhitespaceRespectingDoubleQuotedPhrases("\\ ")); + assertEquals(Arrays.asList("\""), splitAlongWhitespaceRespectingDoubleQuotedPhrases(" \\\"")); + assertEquals(Arrays.asList(" \\"), splitAlongWhitespaceRespectingDoubleQuotedPhrases(" \\ \\\\ ")); + assertTrue(isEmpty(splitAlongWhitespaceRespectingDoubleQuotedPhrases(" \n\t "))); + assertEquals(Arrays.asList("abc"), splitAlongWhitespaceRespectingDoubleQuotedPhrases(" \"abc")); // ignore unterminated quote + assertEquals(Arrays.asList("abc "), splitAlongWhitespaceRespectingDoubleQuotedPhrases(" \"abc ")); // ignore unterminated quote + assertEquals(Arrays.asList("afd", "fsd", "fd\\"), splitAlongWhitespaceRespectingDoubleQuotedPhrases("afd fsd fd\\")); // trailing escape character stands for itself + } +} diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/TypeBasedServiceFinder.java b/java/com.sap.sse.common/src/com/sap/sse/common/TypeBasedServiceFinder.java index 0edde83f64a..94fd620cd0b 100644 --- a/java/com.sap.sse.common/src/com/sap/sse/common/TypeBasedServiceFinder.java +++ b/java/com.sap.sse.common/src/com/sap/sse/common/TypeBasedServiceFinder.java @@ -5,10 +5,10 @@ import java.util.Set; /** * Manages a set of services implementing {@code ServiceType}. Each of these services provides similar functionality * (e.g. a storage service), but for a different {@code type} of input/output (e.g. storage for competitors or GPS - * fixes). + * fixes).

          * * Methods exist for {@link #findService(String) finding a service for a certain type}, or {@link #findAllServices() - * finding all services} that implement {@code ServiceType}. + * finding all services} that implement {@code ServiceType}.

          * * An implementation of this interface might e.g. be backed by the OSGi service registry. For that case, this * OSGi-agnostic interface allows such a {@code TypeBasedServiceFinder} object to be introduced in contexts that are not @@ -21,6 +21,10 @@ import java.util.Set; */ public interface TypeBasedServiceFinder { public static final String TYPE = "type"; + + public interface Callback { + void withService(ServiceType service); + } /** * Find a service that implements the interface for the specified {@code type} of object (e.g. type of @@ -32,6 +36,17 @@ public interface TypeBasedServiceFinder { * Thrown, if no service is registered for this type. */ ServiceType findService(String type) throws NoCorrespondingServiceRegisteredException; + + /** + * Similar to {@link #findService(String)}; when the service for {@code type} is already available, + * the {@code callback} is immediately {@link Callback#withService(Object) invoked} with the service + * found. If the service is not found, instead of throwing a {@link NoCorrespondingServiceRegisteredException}, + * the {@code callback} is recorded by this finder; when at a later point in time a service with {@code type} + * becomes available, all callbacks recorded this way will be {@link Callback#withService(Object) invoked} + * with the service that became available. The method returns immediately after recording the callback in + * this case. + */ + void applyServiceWhenAvailable(String type, Callback callback); Set findAllServices(); diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/TypeBasedServiceFinderFactory.java b/java/com.sap.sse.common/src/com/sap/sse/common/TypeBasedServiceFinderFactory.java index bb63c63b446..bc26d8bee1a 100644 --- a/java/com.sap.sse.common/src/com/sap/sse/common/TypeBasedServiceFinderFactory.java +++ b/java/com.sap.sse.common/src/com/sap/sse/common/TypeBasedServiceFinderFactory.java @@ -1,6 +1,5 @@ package com.sap.sse.common; - /** * A factory for creating {@link TypeBasedServiceFinder}s, for a specific type of service. * The desired service type is specified by the {@link Class} object passed to the diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/Util.java b/java/com.sap.sse.common/src/com/sap/sse/common/Util.java old mode 100644 new mode 100755 index 0c4b4de0a66..49330b92752 --- a/java/com.sap.sse.common/src/com/sap/sse/common/Util.java +++ b/java/com.sap.sse.common/src/com/sap/sse/common/Util.java @@ -450,6 +450,99 @@ public class Util { return join(separator, strings); } + /** + * Splits {@code s} along whitespace (blank, tab, line feed, carriage return, form feed) characters that are not + * within a phrase. Phrases are enclosed by double quotes ({@code "}). To make a double quote or any other + * character part of a {@link String} in the result, a backslash ({@code \}) must precede the double quote as an escape character. With this, + * a {@code \} character or a whitespace character can become part of the split result by escaping it with a {@code \} character. If {@code s}'s last character + * happens to be the (unescaped) escape character it stands for itself. + * + * A double quote {@code "} in the middle of an unquoted phrase marks the beginning of a new quoted phrase. When occurring unescaped in + * a quoted phrase, it marks the end of that quoted phrase, and a new unquoted phrase starts. + *

          + * + * The following example expressions all evaluate to {@code true}: + * + *

          +     * {@link #splitAlongWhitespaceRespectingDoubleQuotedPhrases(String) splitAlongWhitespaceRespectingDoubleQuotedPhrases("a b c")}.equals(Arrays.asList("a", "b", "c"))
          +     * {@link #splitAlongWhitespaceRespectingDoubleQuotedPhrases(String) splitAlongWhitespaceRespectingDoubleQuotedPhrases("a \"b c\"")}.equals(Arrays.asList("a", "b c"))
          +     * {@link #splitAlongWhitespaceRespectingDoubleQuotedPhrases(String) splitAlongWhitespaceRespectingDoubleQuotedPhrases("a \"b \\\" c\"")}.equals(Arrays.asList("a", "b \" c"))
          +     * {@link #splitAlongWhitespaceRespectingDoubleQuotedPhrases(String) splitAlongWhitespaceRespectingDoubleQuotedPhrases("a \"bc\"de")}.equals(Arrays.asList("a", "bc", "de"))
          +     * {@link #splitAlongWhitespaceRespectingDoubleQuotedPhrases(String) splitAlongWhitespaceRespectingDoubleQuotedPhrases("a\"bc\" de")}.equals(Arrays.asList("a", "bc", "de"))
          +     * {@link #splitAlongWhitespaceRespectingDoubleQuotedPhrases(String) splitAlongWhitespaceRespectingDoubleQuotedPhrases("\\ ")}.equals(Arrays.asList(" "))
          +     * {@link #splitAlongWhitespaceRespectingDoubleQuotedPhrases(String) splitAlongWhitespaceRespectingDoubleQuotedPhrases("  \\ \\\\ ")}.equals(Arrays.asList(" \\"))
          +     * {@link #isEmpty(Iterable) isEmpty(splitAlongWhitespaceRespectingDoubleQuotedPhrases(" \n\t  "))
          +     * 
          + * + * @return if {@code s==null}, then {@code null}, else a non-{@code null} but possibly empty sequence of {@link Strings} whose iteration order corresponds with + * the occurrence of the split results, left to right, in {@code s} + */ + public static Iterable splitAlongWhitespaceRespectingDoubleQuotedPhrases(String s) { + final char ESCAPE_CHARACTER = '\\'; + final List result; + if (s == null) { + result = null; + } else { + result = new ArrayList<>(); + boolean escaped = false; + StringBuilder phrase = null; + boolean inQuotedPhrase = false; + for (final char c : s.toCharArray()) { + if (escaped) { + if (phrase == null) { + phrase = new StringBuilder(); + } + phrase.append(c); + escaped = false; + } else if (c == ESCAPE_CHARACTER) { + escaped = true; // don't append but mark for next character to be appended + } else if (c == '"') { + if (inQuotedPhrase) { + result.add(phrase.toString()); + inQuotedPhrase = false; + phrase = null; + } else { + inQuotedPhrase = true; + if (phrase != null) { // starts a quoted phrase in the middle of a running phrase + result.add(phrase.toString()); + } + phrase = new StringBuilder(); + } + } else { + if (inQuotedPhrase) { + phrase.append(c); + } else { + if (new String(new char[] {c}).matches("\\s")) { // whitespace + if (phrase != null) { + // phrase is terminated by this whitespace + result.add(phrase.toString()); + phrase = null; + } // else skip whitespace outside of phrases + } else { + if (phrase == null) { + phrase = new StringBuilder(); + } + phrase.append(c); + } + } + } + } + if (escaped) { + // escape character as last character stands for itself + if (phrase == null) { + phrase = new StringBuilder(); + } + phrase.append(ESCAPE_CHARACTER); + } + if (phrase != null) { + // a phrase is also terminated by the end of the string, also (lenient mode) if + // within an (unterminated) quoted phrase + result.add(phrase.toString()); + } + } + return result; + } + /** * Returns the first non-null object in objects or null * if no such object exists. diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/filter/impl/KeywordMatcher.java b/java/com.sap.sse.common/src/com/sap/sse/common/filter/impl/KeywordMatcher.java index a17781eb2f8..8016a81b085 100644 --- a/java/com.sap.sse.common/src/com/sap/sse/common/filter/impl/KeywordMatcher.java +++ b/java/com.sap.sse.common/src/com/sap/sse/common/filter/impl/KeywordMatcher.java @@ -39,7 +39,7 @@ public abstract class KeywordMatcher { private boolean containsText(T obj, Iterable keywords) { boolean failed = false; for (String word : keywords) { - String textAsUppercase = word.toUpperCase().trim(); + String textAsUppercase = word.toUpperCase(); failed = true; for (String s : getStrings(obj)) { if (s != null && s.toUpperCase().contains(textAsUppercase)) { diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/impl/SingleTypeBasedServiceFinderImpl.java b/java/com.sap.sse.common/src/com/sap/sse/common/impl/SingleTypeBasedServiceFinderImpl.java index 619800444b2..868890533e5 100644 --- a/java/com.sap.sse.common/src/com/sap/sse/common/impl/SingleTypeBasedServiceFinderImpl.java +++ b/java/com.sap.sse.common/src/com/sap/sse/common/impl/SingleTypeBasedServiceFinderImpl.java @@ -7,8 +7,9 @@ import com.sap.sse.common.NoCorrespondingServiceRegisteredException; import com.sap.sse.common.TypeBasedServiceFinder; /** - * Only can provide the service for a single {@code type}, e.g. on smartphones with no OSGi context. - * Optionally, a {@link #setFallbackService(Object) fallback} can be set. + * Only can provide the service for a single {@code type}, e.g. on smartphones with no OSGi context. Optionally, a + * {@link #setFallbackService(Object) fallback} can be set. + * * @author Fredrik Teschke */ public class SingleTypeBasedServiceFinderImpl implements @@ -30,11 +31,23 @@ TypeBasedServiceFinder { @Override public ServiceType findService(String type) throws NoCorrespondingServiceRegisteredException { - if (this.type.equals(type)) return service; - if (fallback != null) return fallback; + if (this.type.equals(type)) { + return service; + } + if (fallback != null) { + return fallback; + } throw new NoCorrespondingServiceRegisteredException("Only one service registered", type, service.getClass().getSimpleName()); } + @Override + public void applyServiceWhenAvailable(String type, Callback callback) { + final ServiceType service = findService(type); + if (service != null) { + callback.withService(service); + } + } + @Override public void setFallbackService(ServiceType fallback) { this.fallback = fallback; diff --git a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/functions/TestCompoundFunction.java b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/functions/TestCompoundFunction.java index 35942421c61..27ab7bf1347 100644 --- a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/functions/TestCompoundFunction.java +++ b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/functions/TestCompoundFunction.java @@ -22,18 +22,28 @@ public class TestCompoundFunction { private Function getContainerElementFunction; private Function getNameFunction; + private Function getStringFromIntFunction; private Function compoundFunction; + private Function compoundFunctionWithSecondTakingSingleIntParameter; @Before public void initializeCompoundFunction() throws ClassCastException, NoSuchMethodException, SecurityException { + { List> functions = new ArrayList<>(); getContainerElementFunction = FunctionTestsUtil.getFunctionFactory().createMethodWrappingFunction(MarkedContainer.class.getMethod("getContainerElement", new Class[0])); functions.add(getContainerElementFunction); getNameFunction = FunctionTestsUtil.getFunctionFactory().createMethodWrappingFunction(ContainerElement.class.getMethod("getName", new Class[0])); functions.add(getNameFunction); - compoundFunction = FunctionTestsUtil.getFunctionFactory().createCompoundFunction(functions); + } + { + List> functions = new ArrayList<>(); + functions.add(getContainerElementFunction); + getStringFromIntFunction = FunctionTestsUtil.getFunctionFactory().createMethodWrappingFunction(ContainerElement.class.getMethod("getStringFromInt", new Class[] { int.class })); + functions.add(getStringFromIntFunction); + compoundFunctionWithSecondTakingSingleIntParameter = FunctionTestsUtil.getFunctionFactory().createCompoundFunction(functions); + } } @Test(expected=IllegalArgumentException.class) @@ -64,4 +74,13 @@ public class TestCompoundFunction { assertThat(result, is(containerElementName)); } + @Test + public void testInvocationWithParametersForSecondFunction() { + String one = "1"; + ContainerElement element = new ContainerElementImpl(one); + MarkedContainer container = new MarkedContainerImpl(element); + String result = compoundFunctionWithSecondTakingSingleIntParameter.tryToInvoke(container, ()->new Object[] { 1 }); + assertThat(result, is(one)); + } + } diff --git a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/functions/TestMethodWrappingFunctionInvocation.java b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/functions/TestMethodWrappingFunctionInvocation.java index 067648c2ffd..8a1e654293e 100644 --- a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/functions/TestMethodWrappingFunctionInvocation.java +++ b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/functions/TestMethodWrappingFunctionInvocation.java @@ -40,9 +40,11 @@ public class TestMethodWrappingFunctionInvocation { @Test public void testInvocationWithWrongParameters() { DataTypeWithContext dataEntry = new DataTypeWithContextImpl("Regatta Name", "Race Name", 7); - assertThat(getRegattaName.tryToInvoke(dataEntry, () -> {return new Object[] {"Wrong Parameter"};} ), is(nullValue())); - + // the one parameter is ignored; see bug 4034; in case of concatenated functions, excess parameters are ignored + // and would only be used for those methods that need it; therefore, we receive a valid result here + assertThat(getRegattaName.tryToInvoke(dataEntry, () -> {return new Object[0];} ), is("Regatta Name")); SimpleClassWithMarkedMethods instance = new SimpleClassWithMarkedMethods(); + // invoking a method that expects an int parameter with no ParameterProvider is expected to return null ("error") assertThat(increment.tryToInvoke(instance), is(nullValue())); } diff --git a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/data/impl/ContainerElement.java b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/data/impl/ContainerElement.java index 2d21e951a2c..8edd1dcb612 100644 --- a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/data/impl/ContainerElement.java +++ b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/data/impl/ContainerElement.java @@ -7,4 +7,6 @@ public interface ContainerElement { @Dimension(messageKey="Name") public String getName(); + @Dimension(messageKey="StringFromInt") + public String getStringFromInt(int i); } diff --git a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/data/impl/ContainerElementImpl.java b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/data/impl/ContainerElementImpl.java index 2e0bfd6a577..53083df5fd9 100644 --- a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/data/impl/ContainerElementImpl.java +++ b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/data/impl/ContainerElementImpl.java @@ -13,4 +13,9 @@ public class ContainerElementImpl implements ContainerElement { return name; } + @Override + public String getStringFromInt(int i) { + return ""+i; + } + } diff --git a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/data/impl/DataTypeWithContext.java b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/data/impl/DataTypeWithContext.java index 1e8d18b0620..ac301dbe57d 100644 --- a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/data/impl/DataTypeWithContext.java +++ b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/data/impl/DataTypeWithContext.java @@ -17,5 +17,4 @@ public interface DataTypeWithContext extends DataTypeInterface { @Dimension(messageKey="legNumber") public int getLegNumber(); - } diff --git a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/data/impl/DataTypeWithContextImpl.java b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/data/impl/DataTypeWithContextImpl.java index bd51122c90f..758f4e11a1a 100644 --- a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/data/impl/DataTypeWithContextImpl.java +++ b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/data/impl/DataTypeWithContextImpl.java @@ -37,5 +37,4 @@ public class DataTypeWithContextImpl extends SuperType implements DataTypeWithCo public int getSpeedInKnots() { return 0; } - } diff --git a/java/com.sap.sse.datamining/resources/stringmessages/StringMessages.properties b/java/com.sap.sse.datamining/resources/stringmessages/StringMessages.properties index 6fce8be037d..3f09729ac71 100644 --- a/java/com.sap.sse.datamining/resources/stringmessages/StringMessages.properties +++ b/java/com.sap.sse.datamining/resources/stringmessages/StringMessages.properties @@ -4,6 +4,7 @@ Sum=Sum Maximum=Maximum Minimum=Minimum Count=Count +CountDistinct=Count Distinct Meters=m Knots=kn Collecting=Collection diff --git a/java/com.sap.sse.datamining/resources/stringmessages/StringMessages_de.properties b/java/com.sap.sse.datamining/resources/stringmessages/StringMessages_de.properties index fc22da1ea48..81e0c2120ab 100644 --- a/java/com.sap.sse.datamining/resources/stringmessages/StringMessages_de.properties +++ b/java/com.sap.sse.datamining/resources/stringmessages/StringMessages_de.properties @@ -4,6 +4,7 @@ Sum=Summe Maximum=Maximum Minimum=Minimum Count=Anzahl +CountDistinct=Anzahl Verschiedener Meters=m Knots=kn Collecting=Sammlung diff --git a/java/com.sap.sse.datamining/resources/stringmessages/XLF/StringMessages_ja.properties.xlf b/java/com.sap.sse.datamining/resources/stringmessages/XLF/StringMessages_ja.properties.xlf index d6aa3a6d09d..4eae11dd4fd 100644 --- a/java/com.sap.sse.datamining/resources/stringmessages/XLF/StringMessages_ja.properties.xlf +++ b/java/com.sap.sse.datamining/resources/stringmessages/XLF/StringMessages_ja.properties.xlf @@ -1,6 +1,6 @@ - +
          H4sIAAAAAAAAAD2MQQoCMQxF9znFLHWnB8hicCXSjYMHKBpLsE2hTQdhmLsbpuoieT958MeZig+EYyc4erAX7ICpJbQB59+cLH8JjqXfnXDKTRS3bQ1KpWKCi2St+BKzMdJdWQL+Yha4Um1RJw7CT6aCy2Eddstx3cP/d5absG6GZTAHH6Y6WeOvAAAA diff --git a/java/com.sap.sse.datamining/resources/stringmessages/XLF/StringMessages_ru.properties.xlf b/java/com.sap.sse.datamining/resources/stringmessages/XLF/StringMessages_ru.properties.xlf index 8560071614a..f71af745a65 100644 --- a/java/com.sap.sse.datamining/resources/stringmessages/XLF/StringMessages_ru.properties.xlf +++ b/java/com.sap.sse.datamining/resources/stringmessages/XLF/StringMessages_ru.properties.xlf @@ -1,6 +1,6 @@ - +
          H4sIAAAAAAAAAD2MQQoCMQxF9znFLHWnB8hicCXSjYMHKBpLsE2hTQdhmLsbpuoieT958MeZig+EYyc4erAX7ICpJbQB59+cLH8JjqXfnXDKTRS3bQ1KpWKCi2St+BKzMdJdWQL+Yha4Um1RJw7CT6aCy2Eddstx3cP/d5absG6GZTAHH6Y6WeOvAAAA diff --git a/java/com.sap.sse.datamining/resources/stringmessages/XLF/StringMessages_zh_CN.properties.xlf b/java/com.sap.sse.datamining/resources/stringmessages/XLF/StringMessages_zh_CN.properties.xlf index 5d4af88ca94..b6d3c91f7c3 100644 --- a/java/com.sap.sse.datamining/resources/stringmessages/XLF/StringMessages_zh_CN.properties.xlf +++ b/java/com.sap.sse.datamining/resources/stringmessages/XLF/StringMessages_zh_CN.properties.xlf @@ -1,6 +1,6 @@ - +
          H4sIAAAAAAAAAD2MQQoCMQxF9znFLHWnB8hicCXSjYMHKBpLsE2hTQdhmLsbpuoieT958MeZig+EYyc4erAX7ICpJbQB59+cLH8JjqXfnXDKTRS3bQ1KpWKCi2St+BKzMdJdWQL+Yha4Um1RJw7CT6aCy2Eddstx3cP/d5absG6GZTAHH6Y6WeOvAAAA diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/factories/QueryFactory.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/factories/QueryFactory.java index fb393e49a8f..04d179bcd2a 100644 --- a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/factories/QueryFactory.java +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/factories/QueryFactory.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; @@ -88,20 +87,7 @@ public class QueryFactory { } private boolean functionNeedsLocalizationParameters(Function function) { - Iterator> parameterTypesIterator = function.getParameters().iterator(); - int parameterCount = 0; - while (parameterTypesIterator.hasNext()) { - parameterCount++; - parameterTypesIterator.next(); - } - if (parameterCount != 2) { - return false; - } - - parameterTypesIterator = function.getParameters().iterator(); - Class firstParameter = parameterTypesIterator.next(); - Class secondParameter = parameterTypesIterator.next(); - return firstParameter.isAssignableFrom(Locale.class) && secondParameter.isAssignableFrom(ResourceBundleStringMessages.class); + return function.needsLocalizationParameters(); } @SuppressWarnings("unchecked") diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/functions/Function.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/functions/Function.java index cc78829b24c..de6b1af29c0 100644 --- a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/functions/Function.java +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/functions/Function.java @@ -9,6 +9,7 @@ public interface Function { public Class getDeclaringType(); public Iterable> getParameters(); + public boolean needsLocalizationParameters(); public Class getReturnType(); public boolean isDimension(); diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/DataMiningFrameworkActivator.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/DataMiningFrameworkActivator.java index 4b7643a1175..af07400a2c2 100644 --- a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/DataMiningFrameworkActivator.java +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/DataMiningFrameworkActivator.java @@ -26,6 +26,7 @@ import com.sap.sse.datamining.components.management.DataSourceProviderRegistry; import com.sap.sse.datamining.components.management.FunctionRegistry; import com.sap.sse.datamining.components.management.QueryDefinitionDTORegistry; import com.sap.sse.datamining.impl.components.aggregators.ParallelGroupedDataCountAggregationProcessor; +import com.sap.sse.datamining.impl.components.aggregators.ParallelGroupedDataCountDistinctAggregationProcessor; import com.sap.sse.datamining.impl.components.aggregators.ParallelGroupedNumberDataAverageAggregationProcessor; import com.sap.sse.datamining.impl.components.aggregators.ParallelGroupedNumberDataMaxAggregationProcessor; import com.sap.sse.datamining.impl.components.aggregators.ParallelGroupedNumberDataMedianAggregationProcessor; @@ -80,6 +81,7 @@ public class DataMiningFrameworkActivator implements BundleActivator { private Iterable> getDefaultAggregationProcessors() { Collection> defaultAggregationProcessors = new HashSet<>(); + defaultAggregationProcessors.add(ParallelGroupedDataCountDistinctAggregationProcessor.getDefinition()); defaultAggregationProcessors.add(ParallelGroupedDataCountAggregationProcessor.getDefinition()); defaultAggregationProcessors.add(ParallelGroupedNumberDataAverageAggregationProcessor.getDefinition()); defaultAggregationProcessors.add(ParallelGroupedNumberDataMaxAggregationProcessor.getDefinition()); diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/aggregators/ParallelGroupedDataCountDistinctAggregationProcessor.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/aggregators/ParallelGroupedDataCountDistinctAggregationProcessor.java new file mode 100755 index 00000000000..bf8d7ce3d51 --- /dev/null +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/aggregators/ParallelGroupedDataCountDistinctAggregationProcessor.java @@ -0,0 +1,65 @@ +package com.sap.sse.datamining.impl.components.aggregators; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.ExecutorService; + +import com.sap.sse.common.Util; +import com.sap.sse.datamining.components.AdditionalResultDataBuilder; +import com.sap.sse.datamining.components.AggregationProcessorDefinition; +import com.sap.sse.datamining.components.Processor; +import com.sap.sse.datamining.impl.components.GroupedDataEntry; +import com.sap.sse.datamining.impl.components.SimpleAggregationProcessorDefinition; +import com.sap.sse.datamining.shared.GroupKey; + +/** + * Counts distinct instances; for this, the aggregator needs to keep in memory a {@link Set} of the + * group-keyed objects to match new objects with already existing ones based on their {@link Object#equals(Object)} + * and {@link Object#hashCode()} definitions. + * + * @author Axel Uhl (d043530) + * + */ +public class ParallelGroupedDataCountDistinctAggregationProcessor + extends AbstractParallelGroupedDataAggregationProcessor { + + private static final AggregationProcessorDefinition DEFINITION = + new SimpleAggregationProcessorDefinition<>(Object.class, Number.class, "CountDistinct", ParallelGroupedDataCountDistinctAggregationProcessor.class); + + public static AggregationProcessorDefinition getDefinition() { + return DEFINITION; + } + + private Map> countMap; + + public ParallelGroupedDataCountDistinctAggregationProcessor(ExecutorService executor, + Collection, ?>> resultReceivers) { + super(executor, resultReceivers, "Count"); + countMap = new HashMap<>(); + } + + @Override + protected void handleElement(GroupedDataEntry element) { + GroupKey key = element.getKey(); + Util.addToValueSet(countMap, key, element.getDataEntry()); + } + + @Override + protected Map getResult() { + final Map result = new HashMap<>(); + for (final Entry> e : countMap.entrySet()) { + result.put(e.getKey(), e.getValue().size()); + } + return result; + } + + @Override + protected void setAdditionalData(AdditionalResultDataBuilder additionalDataBuilder) { + super.setAdditionalData(additionalDataBuilder); + additionalDataBuilder.setResultDecimals(0); + } + +} diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/functions/ConcatenatingCompoundFunction.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/functions/ConcatenatingCompoundFunction.java index 0ed9e6c763b..e27ef4a87a4 100644 --- a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/functions/ConcatenatingCompoundFunction.java +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/functions/ConcatenatingCompoundFunction.java @@ -87,6 +87,16 @@ public class ConcatenatingCompoundFunction extends AbstractFunction< return getFirstFunction().getParameters(); } + @Override + public boolean needsLocalizationParameters() { + for (final Function function : getFunctions()) { + if (function.needsLocalizationParameters()) { + return true; + } + } + return false; + } + /** * @return the return type of the last function. */ @@ -159,7 +169,7 @@ public class ConcatenatingCompoundFunction extends AbstractFunction< Object result = functionsIterator.next().tryToInvoke(instance, parameterProvider); while (functionsIterator.hasNext()) { Function function = functionsIterator.next(); - result = function.tryToInvoke(result); + result = function.tryToInvoke(result, parameterProvider); if (result == null) { return null; } diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/functions/MethodWrappingFunction.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/functions/MethodWrappingFunction.java index 7b34364e908..25252dac5d5 100644 --- a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/functions/MethodWrappingFunction.java +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/functions/MethodWrappingFunction.java @@ -3,6 +3,7 @@ package com.sap.sse.datamining.impl.functions; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; +import java.util.Iterator; import java.util.Locale; import java.util.logging.Level; @@ -79,6 +80,23 @@ public class MethodWrappingFunction extends AbstractFunction> parameterTypesIterator = getParameters().iterator(); + int parameterCount = 0; + while (parameterTypesIterator.hasNext()) { + parameterCount++; + parameterTypesIterator.next(); + } + if (parameterCount != 2) { + return false; + } + parameterTypesIterator = getParameters().iterator(); + Class firstParameter = parameterTypesIterator.next(); + Class secondParameter = parameterTypesIterator.next(); + return firstParameter.isAssignableFrom(Locale.class) && secondParameter.isAssignableFrom(ResourceBundleStringMessages.class); + } + @Override public ReturnType tryToInvoke(Object instance) { return tryToInvoke(instance, new Object[0]); @@ -86,7 +104,14 @@ public class MethodWrappingFunction extends AbstractFunction + + com.sap.sse.feature.runtime + + + + + + org.eclipse.pde.FeatureBuilder + + + + + + org.eclipse.pde.FeatureNature + + diff --git a/java/com.sap.sse.feature.runtime/build.properties b/java/com.sap.sse.feature.runtime/build.properties new file mode 100644 index 00000000000..82ab19c62d1 --- /dev/null +++ b/java/com.sap.sse.feature.runtime/build.properties @@ -0,0 +1 @@ +bin.includes = feature.xml diff --git a/java/com.sap.sse.feature.runtime/feature.xml b/java/com.sap.sse.feature.runtime/feature.xml new file mode 100644 index 00000000000..3dee603586e --- /dev/null +++ b/java/com.sap.sse.feature.runtime/feature.xml @@ -0,0 +1,406 @@ + + + + + [Enter Feature Description here.] + + + + [Enter Copyright Description here.] + + + + [Enter License Description here.] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/com.sap.sse.feature.runtime/pom.xml b/java/com.sap.sse.feature.runtime/pom.xml new file mode 100644 index 00000000000..27cb27a002c --- /dev/null +++ b/java/com.sap.sse.feature.runtime/pom.xml @@ -0,0 +1,15 @@ + + + + 4.0.0 + + + com.sap.sailing + root + 1.0.0-SNAPSHOT + + + com.sap.sse.feature.runtime + eclipse-feature + diff --git a/java/com.sap.sse.feature/.gitignore b/java/com.sap.sse.feature/.gitignore new file mode 100644 index 00000000000..3e2fcc71712 --- /dev/null +++ b/java/com.sap.sse.feature/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/java/com.sap.sse.feature/.project b/java/com.sap.sse.feature/.project new file mode 100644 index 00000000000..55ac4855a33 --- /dev/null +++ b/java/com.sap.sse.feature/.project @@ -0,0 +1,11 @@ + + + com.sap.sse.feature + + + + + + + + diff --git a/java/com.sap.sse.feature/build.properties b/java/com.sap.sse.feature/build.properties new file mode 100644 index 00000000000..b73c56eb863 --- /dev/null +++ b/java/com.sap.sse.feature/build.properties @@ -0,0 +1 @@ +bin.includes = diff --git a/java/com.sap.sse.feature/feature.xml b/java/com.sap.sse.feature/feature.xml new file mode 100644 index 00000000000..32e93aa1aa9 --- /dev/null +++ b/java/com.sap.sse.feature/feature.xml @@ -0,0 +1,167 @@ + + + + + The SAP Sailing Server. + +Provides connectivity to a TracTrac live GPS feed as well as a live feed from an Expedition system. +Computes leaderboard information for sailing races and offers RESTful APIs to all of this. + + + + [Enter Copyright Description here.] + + + + [Enter License Description here.] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/com.sap.sse.feature/pom.xml b/java/com.sap.sse.feature/pom.xml new file mode 100644 index 00000000000..54a33e86098 --- /dev/null +++ b/java/com.sap.sse.feature/pom.xml @@ -0,0 +1,15 @@ + + + + 4.0.0 + + + com.sap.sailing + root + 1.0.0-SNAPSHOT + + + com.sap.sse.feature + eclipse-feature + diff --git a/java/com.sap.sse.filestorage.test/META-INF/MANIFEST.MF b/java/com.sap.sse.filestorage.test/META-INF/MANIFEST.MF index efe4d4b1cc6..d1509cfd315 100644 --- a/java/com.sap.sse.filestorage.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.filestorage.test/META-INF/MANIFEST.MF @@ -7,6 +7,8 @@ Bundle-Vendor: SAP Fragment-Host: com.sap.sse.filestorage Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Require-Bundle: org.hamcrest;bundle-version="1.1.0", - org.junit4;bundle-version="4.8.2", + org.junit;bundle-version="4.8.2", org.apache.commons.io;bundle-version="2.2.0", com.sap.sse.replication.testsupport +Import-Package: org.apache.commons.logging, + org.slf4j;version="1.6.4" diff --git a/java/com.sap.sse.filestorage.test/pom.xml b/java/com.sap.sse.filestorage.test/pom.xml index 871455584ed..d87194b0d23 100644 --- a/java/com.sap.sse.filestorage.test/pom.xml +++ b/java/com.sap.sse.filestorage.test/pom.xml @@ -9,4 +9,24 @@ com.sap.sse.filestorage.test eclipse-test-plugin + + + + + org.eclipse.tycho + target-platform-configuration + + + + + eclipse-plugin + jcl.over.slf4j + 1.6.4 + + + + + + + diff --git a/java/com.sap.sse.filestorage/META-INF/MANIFEST.MF b/java/com.sap.sse.filestorage/META-INF/MANIFEST.MF index 4df238efad2..1dd0213f6cf 100644 --- a/java/com.sap.sse.filestorage/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.filestorage/META-INF/MANIFEST.MF @@ -8,14 +8,14 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Export-Package: com.sap.sse.filestorage, com.sap.sse.filestorage.impl, com.sap.sse.filestorage.testsupport -Import-Package: org.osgi.framework;version="1.8.0", +Import-Package: org.apache.commons.logging, + org.osgi.framework;version="1.8.0", org.osgi.util.tracker;version="1.5.1" Bundle-Activator: com.sap.sse.filestorage.impl.Activator Require-Bundle: org.apache.servicemix.bundles.aws-java-sdk;bundle-version="1.9.8", com.fasterxml.jackson.core.jackson-databind, com.fasterxml.jackson.core.jackson-core, com.fasterxml.jackson.core.jackson-annotations, - org.apache.commons.logging;bundle-version="1.1.3", com.sap.sse.common, com.sap.sse.mongodb, com.sap.sse.replication, diff --git a/java/com.sap.sse.filestorage/resources/stringmessages/XLF/FileStorageStringMessages_ja.properties.xlf b/java/com.sap.sse.filestorage/resources/stringmessages/XLF/FileStorageStringMessages_ja.properties.xlf index 14952fa3fe4..93be3d54df8 100644 --- a/java/com.sap.sse.filestorage/resources/stringmessages/XLF/FileStorageStringMessages_ja.properties.xlf +++ b/java/com.sap.sse.filestorage/resources/stringmessages/XLF/FileStorageStringMessages_ja.properties.xlf @@ -1,6 +1,6 @@ - +
          H4sIAAAAAAAAAJ1SwW7bMAy95yt4bNHBPeTcQ4tiQLGiGNrmAxiZTojSkiHKSb3Dvn2klGTddSdL1OPj4/PT9SNpuHsrKRMMLKSQItyP+Ms+b+sO3vcEmXSWwnFXERBxJGAFhIyxTyNsNk+PMMmsUAydMu84ojQwxd4au5Wu70Mg1ae+DmwXsL4rITwQbAXjB5QEsxL8vu3wqLchU0+xMIoCRy2E/fWF6ActTToZrMCJ0Kr/wfgwhw8qL7ZXpfQDpAFa+UxxtUf1M0q2tgXok7V8A7PAnzNEol5B52HgwDYEJsojq3KKer2SFFBOgvOBg7mdMqj5fvbVFVUDK7S5p4tpHDv4Xt93JsY7qHdohf3Esr9xaNXsWgJG2Nqx+mFILLBFpU2Wv7iXVMhG2ZPPCykW1yuLSzHmEx7GWSsi0+DxuLDyVjwUo7HI4hGxMPhY34g+cZyEKnbPdDAF2wXGNMcaIDz1fV3vax8M2QKlBQuHTnFSZPH8BKumZk+mSTi0f3HxoGsGPzTl1Wc/w+b1GW7gsvhdLRx9JZgnSdibvirlyCL/btgYn88TKme9gV/PqXDtbRf7Mbij1R9TuhdHUgMAAA== diff --git a/java/com.sap.sse.filestorage/resources/stringmessages/XLF/FileStorageStringMessages_ru.properties.xlf b/java/com.sap.sse.filestorage/resources/stringmessages/XLF/FileStorageStringMessages_ru.properties.xlf index 06d3cca4f1e..f3a5d73e2b5 100644 --- a/java/com.sap.sse.filestorage/resources/stringmessages/XLF/FileStorageStringMessages_ru.properties.xlf +++ b/java/com.sap.sse.filestorage/resources/stringmessages/XLF/FileStorageStringMessages_ru.properties.xlf @@ -1,6 +1,6 @@ - +
          H4sIAAAAAAAAAJ1SwW7bMAy95yt4bNHBPeTcQ4tiQLGiGNrmAxiZTojSkiHKSb3Dvn2klGTddSdL1OPj4/PT9SNpuHsrKRMMLKSQItyP+Ms+b+sO3vcEmXSWwnFXERBxJGAFhIyxTyNsNk+PMMmsUAydMu84ojQwxd4au5Wu70Mg1ae+DmwXsL4rITwQbAXjB5QEsxL8vu3wqLchU0+xMIoCRy2E/fWF6ActTToZrMCJ0Kr/wfgwhw8qL7ZXpfQDpAFa+UxxtUf1M0q2tgXok7V8A7PAnzNEol5B52HgwDYEJsojq3KKer2SFFBOgvOBg7mdMqj5fvbVFVUDK7S5p4tpHDv4Xt93JsY7qHdohf3Esr9xaNXsWgJG2Nqx+mFILLBFpU2Wv7iXVMhG2ZPPCykW1yuLSzHmEx7GWSsi0+DxuLDyVjwUo7HI4hGxMPhY34g+cZyEKnbPdDAF2wXGNMcaIDz1fV3vax8M2QKlBQuHTnFSZPH8BKumZk+mSTi0f3HxoGsGPzTl1Wc/w+b1GW7gsvhdLRx9JZgnSdibvirlyCL/btgYn88TKme9gV/PqXDtbRf7Mbij1R9TuhdHUgMAAA== diff --git a/java/com.sap.sse.filestorage/resources/stringmessages/XLF/FileStorageStringMessages_zh_CN.properties.xlf b/java/com.sap.sse.filestorage/resources/stringmessages/XLF/FileStorageStringMessages_zh_CN.properties.xlf index 8599337a144..a057d7c44bb 100644 --- a/java/com.sap.sse.filestorage/resources/stringmessages/XLF/FileStorageStringMessages_zh_CN.properties.xlf +++ b/java/com.sap.sse.filestorage/resources/stringmessages/XLF/FileStorageStringMessages_zh_CN.properties.xlf @@ -1,6 +1,6 @@ - +
          H4sIAAAAAAAAAJ1SwW7bMAy95yt4bNHBPeTcQ4tiQLGiGNrmAxiZTojSkiHKSb3Dvn2klGTddSdL1OPj4/PT9SNpuHsrKRMMLKSQItyP+Ms+b+sO3vcEmXSWwnFXERBxJGAFhIyxTyNsNk+PMMmsUAydMu84ojQwxd4au5Wu70Mg1ae+DmwXsL4rITwQbAXjB5QEsxL8vu3wqLchU0+xMIoCRy2E/fWF6ActTToZrMCJ0Kr/wfgwhw8qL7ZXpfQDpAFa+UxxtUf1M0q2tgXok7V8A7PAnzNEol5B52HgwDYEJsojq3KKer2SFFBOgvOBg7mdMqj5fvbVFVUDK7S5p4tpHDv4Xt93JsY7qHdohf3Esr9xaNXsWgJG2Nqx+mFILLBFpU2Wv7iXVMhG2ZPPCykW1yuLSzHmEx7GWSsi0+DxuLDyVjwUo7HI4hGxMPhY34g+cZyEKnbPdDAF2wXGNMcaIDz1fV3vax8M2QKlBQuHTnFSZPH8BKumZk+mSTi0f3HxoGsGPzTl1Wc/w+b1GW7gsvhdLRx9JZgnSdibvirlyCL/btgYn88TKme9gV/PqXDtbRf7Mbij1R9TuhdHUgMAAA== diff --git a/java/com.sap.sse.gwt.adminconsole/src/com/sap/sse/gwt/AdminConsole.gwt.xml b/java/com.sap.sse.gwt.adminconsole/src/com/sap/sse/gwt/AdminConsole.gwt.xml index f7eead5536b..6eff7218c8b 100755 --- a/java/com.sap.sse.gwt.adminconsole/src/com/sap/sse/gwt/AdminConsole.gwt.xml +++ b/java/com.sap.sse.gwt.adminconsole/src/com/sap/sse/gwt/AdminConsole.gwt.xml @@ -1,5 +1,6 @@ + diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ReplicationPanel.java b/java/com.sap.sse.gwt.adminconsole/src/com/sap/sse/gwt/adminconsole/ReplicationPanel.java similarity index 78% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ReplicationPanel.java rename to java/com.sap.sse.gwt.adminconsole/src/com/sap/sse/gwt/adminconsole/ReplicationPanel.java index 024720136d9..6bf51cef680 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ReplicationPanel.java +++ b/java/com.sap.sse.gwt.adminconsole/src/com/sap/sse/gwt/adminconsole/ReplicationPanel.java @@ -1,30 +1,36 @@ -package com.sap.sailing.gwt.ui.adminconsole; +package com.sap.sse.gwt.adminconsole; +import java.util.Arrays; +import java.util.HashSet; import java.util.Map; +import java.util.Set; +import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.safehtml.shared.SafeHtmlBuilder; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.CaptionPanel; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.FocusWidget; import com.google.gwt.user.client.ui.Grid; +import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; -import com.sap.sailing.gwt.ui.client.SailingServiceAsync; -import com.sap.sailing.gwt.ui.client.StringMessages; -import com.sap.sailing.gwt.ui.shared.ReplicaDTO; -import com.sap.sailing.gwt.ui.shared.ReplicationMasterDTO; -import com.sap.sailing.gwt.ui.shared.ReplicationStateDTO; import com.sap.sse.common.Util; import com.sap.sse.gwt.client.ErrorReporter; +import com.sap.sse.gwt.client.async.MarkedAsyncCallback; import com.sap.sse.gwt.client.controls.IntegerBox; import com.sap.sse.gwt.client.dialog.DataEntryDialog; import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback; +import com.sap.sse.gwt.client.replication.RemoteReplicationServiceAsync; +import com.sap.sse.gwt.shared.replication.ReplicaDTO; +import com.sap.sse.gwt.shared.replication.ReplicationMasterDTO; +import com.sap.sse.gwt.shared.replication.ReplicationStateDTO; /** * Allows administrators to manage all aspects of server instance replication such as showing whether the instance @@ -37,7 +43,7 @@ public class ReplicationPanel extends FlowPanel { private final Grid registeredReplicas; private final Grid registeredMasters; - private final SailingServiceAsync sailingService; + private final RemoteReplicationServiceAsync replicationServiceAsync; private final ErrorReporter errorReporter; private final StringMessages stringMessages; @@ -45,10 +51,35 @@ public class ReplicationPanel extends FlowPanel { private final Button stopReplicationButton; private final Button removeAllReplicas; - public ReplicationPanel(SailingServiceAsync sailingService, ErrorReporter errorReporter, StringMessages stringMessages) { - this.sailingService = sailingService; + /** + * For administrators it is important to understand whether an instance is a replica. A + * {@link ErrorReporter#reportPersistentInformation(String) persistent error message} shall be shown + * in this case; however, some replicables may not be considered part of the "domain-based" replication. + * For example, if only a security service for user management is replicated in order to implement + * central user and session management then this shall not lead to the warning being displayed. + * This set contains the stringified IDs of those replicables for which being a replica shall + * lead to a warnings. + */ + private final Set replicableIdsAsStringThatShallLeadToWarningAboutInstanceBeingReplica; + + public ReplicationPanel(RemoteReplicationServiceAsync replicationService, ErrorReporter errorReporter, final StringMessages stringMessages) { + this.replicationServiceAsync = replicationService; this.stringMessages = stringMessages; this.errorReporter = errorReporter; + this.replicableIdsAsStringThatShallLeadToWarningAboutInstanceBeingReplica = new HashSet<>(); + replicationService.getReplicableIdsAsStringThatShallLeadToWarningAboutInstanceBeingReplica(new MarkedAsyncCallback<>(new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + GWT.log(stringMessages.errorFetchingReplicaData(caught.getMessage())); + } + + @Override + public void onSuccess(String[] result) { + for (final String replicableIdAsStringThatShallLeadToWarningAboutInstanceBeingReplica : result) { + replicableIdsAsStringThatShallLeadToWarningAboutInstanceBeingReplica.add(replicableIdAsStringThatShallLeadToWarningAboutInstanceBeingReplica); + } + } + })); Button refreshButton = new Button(stringMessages.refresh()); refreshButton.addClickHandler(new ClickHandler() { @@ -113,7 +144,7 @@ public class ReplicationPanel extends FlowPanel { } protected void stopAllReplicas() { - sailingService.stopAllReplicas(new AsyncCallback() { + replicationServiceAsync.stopAllReplicas(new AsyncCallback() { @Override public void onFailure(Throwable caught) { errorReporter.reportError(caught.getMessage()); @@ -129,7 +160,7 @@ public class ReplicationPanel extends FlowPanel { private void stopReplication() { stopReplicationButton.setEnabled(false); - sailingService.stopReplicatingFromMaster(new AsyncCallback() { + replicationServiceAsync.stopReplicatingFromMaster(new AsyncCallback() { @Override public void onFailure(Throwable caught) { errorReporter.reportError(caught.getMessage()); @@ -158,7 +189,7 @@ public class ReplicationPanel extends FlowPanel { registeredMasters.setWidget(0, 0, new Label(stringMessages.loading())); addButton.setEnabled(false); stopReplicationButton.setEnabled(false); - sailingService.startReplicatingFromMaster(masterNameAndExchangeNameAndMessagingPortNumberAndServletPortNumber.getA().getA(), + replicationServiceAsync.startReplicatingFromMaster(masterNameAndExchangeNameAndMessagingPortNumberAndServletPortNumber.getA().getA(), masterNameAndExchangeNameAndMessagingPortNumberAndServletPortNumber.getA().getB(), masterNameAndExchangeNameAndMessagingPortNumberAndServletPortNumber.getA().getC(), masterNameAndExchangeNameAndMessagingPortNumberAndServletPortNumber.getC(), @@ -190,7 +221,7 @@ public class ReplicationPanel extends FlowPanel { } public void updateReplicaList() { - sailingService.getReplicaInfo(new AsyncCallback() { + replicationServiceAsync.getReplicaInfo(new AsyncCallback() { @Override public void onSuccess(ReplicationStateDTO replicas) { int i=0; @@ -206,7 +237,7 @@ public class ReplicationPanel extends FlowPanel { removeReplicaButton.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { - sailingService.stopSingleReplicaInstance(replica.getIdentifier(), new AsyncCallback() { + replicationServiceAsync.stopSingleReplicaInstance(replica.getIdentifier(), new AsyncCallback() { @Override public void onFailure(Throwable caught) { errorReporter.reportError(caught.getMessage()); @@ -222,6 +253,11 @@ public class ReplicationPanel extends FlowPanel { registeredReplicas.setWidget(i, 2, removeReplicaButton); i++; registeredReplicas.insertRow(i); + registeredReplicas.setWidget(i, 1, new Label(stringMessages.replicables())); + HTML replicablesLabel = new HTML(new SafeHtmlBuilder().appendEscapedLines(Arrays.toString(replica.getReplicableIdsAsStrings()).replaceAll(",", "\n")).toSafeHtml()); + registeredReplicas.setWidget(i, 2, replicablesLabel); + i++; + registeredReplicas.insertRow(i); registeredReplicas.setWidget(i, 1, new Label(stringMessages.averageNumberOfOperationsPerMessage())); registeredReplicas.setWidget(i, 2, new Label(""+replica.getAverageNumberOfOperationsPerMessage())); i++; @@ -268,14 +304,19 @@ public class ReplicationPanel extends FlowPanel { registeredMasters.insertRow(i); registeredMasters.setWidget(i, 0, new Label("Client UUID: " + replicas.getServerIdentifier())); i++; - final ReplicationMasterDTO replicatingFromMaster = replicas.getReplicatingFromMaster(); - if (replicatingFromMaster != null) { - errorReporter.reportPersistentInformation(stringMessages.warningServerIsReplica()); + if (replicatingFromMaster != null) { // TODO bug 2465: replicating only the user service from a "domain controller master" shouldn't lead to a warning here... + for (final String replicableIdAsStringOfReplicableThatWeAreReplicatingCurrently : replicatingFromMaster.getReplicableIdsAsString()) { + if (Util.contains(replicableIdsAsStringThatShallLeadToWarningAboutInstanceBeingReplica, replicableIdAsStringOfReplicableThatWeAreReplicatingCurrently)) { + errorReporter.reportPersistentInformation(stringMessages.warningServerIsReplica()); + break; + } + } registeredMasters.insertRow(i); registeredMasters.setWidget(i, 0, new Label(stringMessages.replicatingFromMaster(replicatingFromMaster.getHostname(), replicatingFromMaster.getMessagingPort(), replicatingFromMaster.getServletPort(), - replicatingFromMaster.getMessagingHostname(), replicatingFromMaster.getExchangeName()))); + replicatingFromMaster.getMessagingHostname(), replicatingFromMaster.getExchangeName(), + Arrays.toString(replicatingFromMaster.getReplicableIdsAsString())))); i++; addButton.setEnabled(false); stopReplicationButton.setEnabled(true); diff --git a/java/com.sap.sse.gwt.adminconsole/src/com/sap/sse/gwt/adminconsole/StringMessages.java b/java/com.sap.sse.gwt.adminconsole/src/com/sap/sse/gwt/adminconsole/StringMessages.java index ada97f4b194..5d6def16543 100755 --- a/java/com.sap.sse.gwt.adminconsole/src/com/sap/sse/gwt/adminconsole/StringMessages.java +++ b/java/com.sap.sse.gwt.adminconsole/src/com/sap/sse/gwt/adminconsole/StringMessages.java @@ -22,4 +22,75 @@ public interface StringMessages extends Messages { String version(String buildVersion); String unknown(); + + String refresh(); + + String explainReplicasRegistered(); + + String explainConnectionsToMaster(); + + String stopAllReplicas(); + + String connectToMaster(); + + String stopConnectionToMaster(); + + String errorStartingReplication(String b, String c, String message); + + String loading(); + + String registeredAt(String string); + + String dropReplicaConnection(); + + String replicables(); + + String averageNumberOfOperationsPerMessage(); + + String numberOfQueueMessagesSent(); + + String averageMessageSize(); + + String totalSize(); + + String totalNumberOfOperations(); + + String explainNoConnectionsFromReplicas(); + + String warningServerIsReplica(); + + String replicatingFromMaster(String hostname, int messagingPort, int servletPort, String messagingHostname, + String exchangeName, String string); + + String explainNoConnectionsToMaster(); + + String errorFetchingReplicaData(String message); + + String connect(); + + String enterMaster(); + + String cancel(); + + String hostname(); + + String explainReplicationHostname(); + + String exchangeHost(); + + String explainExchangeHostName(); + + String exchangeName(); + + String explainReplicationExchangeName(); + + String messagingPortNumber(); + + String explainReplicationExchangePort(); + + String servletPortNumber(); + + String explainReplicationServletPort(); + + String ok(); } diff --git a/java/com.sap.sse.gwt.test/META-INF/MANIFEST.MF b/java/com.sap.sse.gwt.test/META-INF/MANIFEST.MF index 8c1509e8fcc..c111f201413 100644 --- a/java/com.sap.sse.gwt.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.gwt.test/META-INF/MANIFEST.MF @@ -7,6 +7,6 @@ Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Fragment-Host: com.sap.sse.gwt Require-Bundle: com.sap.sse.common;bundle-version="1.0.0", - org.junit4;bundle-version="4.8.2", + org.junit;bundle-version="4.8.2", com.google.gwt.user;bundle-version="2.8.0";resolution:=optional, com.google.gwt.servlet;bundle-version="2.8.0" diff --git a/java/com.sap.sse.gwt/META-INF/MANIFEST.MF b/java/com.sap.sse.gwt/META-INF/MANIFEST.MF index 20dbb08af24..132d9d93150 100644 --- a/java/com.sap.sse.gwt/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.gwt/META-INF/MANIFEST.MF @@ -39,6 +39,7 @@ Export-Package: com.google.gwt.user.client.rpc.core.java.util, com.sap.sse.gwt.client.mvp, com.sap.sse.gwt.client.panels, com.sap.sse.gwt.client.player, + com.sap.sse.gwt.client.replication, com.sap.sse.gwt.client.sapheader, com.sap.sse.gwt.client.shared.components, com.sap.sse.gwt.client.shared.perspective, @@ -57,6 +58,8 @@ Export-Package: com.google.gwt.user.client.rpc.core.java.util, com.sap.sse.gwt.resources, com.sap.sse.gwt.server, com.sap.sse.gwt.server.filestorage, + com.sap.sse.gwt.server.replication, com.sap.sse.gwt.settings, com.sap.sse.gwt.shared, - com.sap.sse.gwt.shared.filestorage + com.sap.sse.gwt.shared.filestorage, + com.sap.sse.gwt.shared.replication diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/filestorage/XLF/StringMessages_ja.properties.xlf b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/filestorage/XLF/StringMessages_ja.properties.xlf index 1c5c5a7aae1..956b382f5f0 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/filestorage/XLF/StringMessages_ja.properties.xlf +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/filestorage/XLF/StringMessages_ja.properties.xlf @@ -1,6 +1,6 @@ - +
          H4sIAAAAAAAAAH1SzU4jMQy+5yn8BIgXyKEa2MuuAFEEZ5O4naiZZOR4WiG0746TtLNod+Hi/2/8eb4UPJLdqjFMU9b4sTmTD/b+p3GYHEU7NGfQe7vx3pAPYm/VmEJ8JL5lzmw3CagGkJ1bmMnDaQyRAEVomiWkPUgGl5OgE5CRoKOv4CESFgI3kjvAW14YEskp86FOJ3IScgJMHoTfAPcY0pVxMReyQ7WNudADZ0d+YRowxnaGEKxFqNUz30eaY6Bity0D7qlp7Ic8TUsKDivhlyBjH7LtRuXzqQsnbZ+PMHpT0L+3aU73yKb0WLcIYIE+YFxeor/L8kRFlNxMLJXKUMuQsv4YbcC8dlaAfua/87rqi/G+v/IPjv5CdDaNvDZX1K+M/itY1N63uCOGiK/xAi3/YC8DF3hR5XZMZVS1mjcJJ7J3aswR40L2uVrjqTgOc30G9uZP3AXryiiTtAv7hbFNDZ+zqkbVqwreg7Xyowk+50RJ1i7sVOn3699mbu9yS1GfoD0/0tIy8wFPN7vhNwMAAA== diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/filestorage/XLF/StringMessages_ru.properties.xlf b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/filestorage/XLF/StringMessages_ru.properties.xlf index 2ee2e51325e..a4d472945a9 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/filestorage/XLF/StringMessages_ru.properties.xlf +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/filestorage/XLF/StringMessages_ru.properties.xlf @@ -1,6 +1,6 @@ - +
          H4sIAAAAAAAAAH1SzU4jMQy+5yn8BIgXyKEa2MuuAFEEZ5O4naiZZOR4WiG0746TtLNod+Hi/2/8eb4UPJLdqjFMU9b4sTmTD/b+p3GYHEU7NGfQe7vx3pAPYm/VmEJ8JL5lzmw3CagGkJ1bmMnDaQyRAEVomiWkPUgGl5OgE5CRoKOv4CESFgI3kjvAW14YEskp86FOJ3IScgJMHoTfAPcY0pVxMReyQ7WNudADZ0d+YRowxnaGEKxFqNUz30eaY6Bity0D7qlp7Ic8TUsKDivhlyBjH7LtRuXzqQsnbZ+PMHpT0L+3aU73yKb0WLcIYIE+YFxeor/L8kRFlNxMLJXKUMuQsv4YbcC8dlaAfua/87rqi/G+v/IPjv5CdDaNvDZX1K+M/itY1N63uCOGiK/xAi3/YC8DF3hR5XZMZVS1mjcJJ7J3aswR40L2uVrjqTgOc30G9uZP3AXryiiTtAv7hbFNDZ+zqkbVqwreg7Xyowk+50RJ1i7sVOn3699mbu9yS1GfoD0/0tIy8wFPN7vhNwMAAA== diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/filestorage/XLF/StringMessages_zh_CN.properties.xlf b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/filestorage/XLF/StringMessages_zh_CN.properties.xlf index 4d48de6efa0..0d8f099fc49 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/filestorage/XLF/StringMessages_zh_CN.properties.xlf +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/filestorage/XLF/StringMessages_zh_CN.properties.xlf @@ -1,6 +1,6 @@ - +
          H4sIAAAAAAAAAH1SzU4jMQy+5yn8BIgXyKEa2MuuAFEEZ5O4naiZZOR4WiG0746TtLNod+Hi/2/8eb4UPJLdqjFMU9b4sTmTD/b+p3GYHEU7NGfQe7vx3pAPYm/VmEJ8JL5lzmw3CagGkJ1bmMnDaQyRAEVomiWkPUgGl5OgE5CRoKOv4CESFgI3kjvAW14YEskp86FOJ3IScgJMHoTfAPcY0pVxMReyQ7WNudADZ0d+YRowxnaGEKxFqNUz30eaY6Bity0D7qlp7Ic8TUsKDivhlyBjH7LtRuXzqQsnbZ+PMHpT0L+3aU73yKb0WLcIYIE+YFxeor/L8kRFlNxMLJXKUMuQsv4YbcC8dlaAfua/87rqi/G+v/IPjv5CdDaNvDZX1K+M/itY1N63uCOGiK/xAi3/YC8DF3hR5XZMZVS1mjcJJ7J3aswR40L2uVrjqTgOc30G9uZP3AXryiiTtAv7hbFNDZ+zqkbVqwreg7Xyowk+50RJ1i7sVOn3699mbu9yS1GfoD0/0tIy8wFPN7vhNwMAAA== diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/AbstractFilterablePanel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/AbstractFilterablePanel.java index 48c9da24ca4..f1ee5267bb8 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/AbstractFilterablePanel.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/AbstractFilterablePanel.java @@ -1,7 +1,6 @@ package com.sap.sse.gwt.client.panels; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -13,6 +12,7 @@ import com.google.gwt.user.cellview.client.CellTable; import com.google.gwt.user.cellview.client.ColumnSortEvent; import com.google.gwt.user.client.ui.HasVerticalAlignment; import com.google.gwt.user.client.ui.HorizontalPanel; +import com.google.gwt.user.client.ui.Panel; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.view.client.ListDataProvider; import com.sap.sse.common.Util; @@ -177,7 +177,7 @@ public abstract class AbstractFilterablePanel extends HorizontalPanel { public void filter() { filtered.getList().clear(); retainElementsInFilteredThatPassFilter(); - filtered.refresh(); + filtered.flush(); sort(); } @@ -211,7 +211,7 @@ public abstract class AbstractFilterablePanel extends HorizontalPanel { getTextBox().addKeyUpHandler(new KeyUpHandler() { @Override public void onKeyUp(KeyUpEvent event) { - filterer.setKeywords(Arrays.asList(getTextBox().getText().split(" "))); + filterer.setKeywords(Util.splitAlongWhitespaceRespectingDoubleQuotedPhrases(getTextBox().getText())); filter(); } }); diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/replication/RemoteReplicationService.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/replication/RemoteReplicationService.java new file mode 100644 index 00000000000..287b497acc2 --- /dev/null +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/replication/RemoteReplicationService.java @@ -0,0 +1,17 @@ +package com.sap.sse.gwt.client.replication; + +import com.sap.sse.gwt.shared.replication.ReplicationStateDTO; + +public interface RemoteReplicationService { + ReplicationStateDTO getReplicaInfo(); + + void startReplicatingFromMaster(String messagingHost, String masterName, String exchangeName, int servletPort, int messagingPort) throws Exception; + + void stopAllReplicas(); + + void stopReplicatingFromMaster(); + + void stopSingleReplicaInstance(String identifier); + + String[] getReplicableIdsAsStringThatShallLeadToWarningAboutInstanceBeingReplica(); +} diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/replication/RemoteReplicationServiceAsync.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/replication/RemoteReplicationServiceAsync.java new file mode 100644 index 00000000000..f8a6347d429 --- /dev/null +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/replication/RemoteReplicationServiceAsync.java @@ -0,0 +1,20 @@ +package com.sap.sse.gwt.client.replication; + +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.sap.sse.gwt.shared.replication.ReplicationStateDTO; + +public interface RemoteReplicationServiceAsync { + void getReplicaInfo(AsyncCallback callback); + + void startReplicatingFromMaster(String messagingHost, String masterName, String exchangeName, int servletPort, + int messagingPort, AsyncCallback callback); + + void stopAllReplicas(AsyncCallback asyncCallback); + + void stopReplicatingFromMaster(AsyncCallback asyncCallback); + + void stopSingleReplicaInstance(String identifier, AsyncCallback asyncCallback); + + void getReplicableIdsAsStringThatShallLeadToWarningAboutInstanceBeingReplica(AsyncCallback markedAsyncCallback); + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ReplicaDTO.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/shared/replication/ReplicaDTO.java similarity index 75% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ReplicaDTO.java rename to java/com.sap.sse.gwt/src/com/sap/sse/gwt/shared/replication/ReplicaDTO.java index 21019522bf6..6476d9fdcf2 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ReplicaDTO.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/shared/replication/ReplicaDTO.java @@ -1,4 +1,4 @@ -package com.sap.sailing.gwt.ui.shared; +package com.sap.sse.gwt.shared.replication; import java.util.Date; import java.util.Map; @@ -14,12 +14,14 @@ public class ReplicaDTO implements IsSerializable { private long numberOfMessagesSent; private double averageMessageSizeInBytes; private long totalNumberOfBytesSent; + private String[] replicableIdsAsStrings; ReplicaDTO() {} public ReplicaDTO(String hostname, Date registrationTime, - String identifier, Map operationCountByOperationClassName, double averageNumberOfOperationsPerMessage, - long numberOfMessagesSent, long totalNumberOfBytesSent, double averageMessageSizeInBytes) { + String identifier, String[] replicableIdsAsStrings, Map operationCountByOperationClassName, + double averageNumberOfOperationsPerMessage, long numberOfMessagesSent, long totalNumberOfBytesSent, double averageMessageSizeInBytes) { this.hostname = hostname; this.identifier = identifier; + this.replicableIdsAsStrings = replicableIdsAsStrings; this.registrationTime = registrationTime; this.operationCountByOperationClassName = operationCountByOperationClassName; this.averageNumberOfOperationsPerMessage = averageNumberOfOperationsPerMessage; @@ -51,4 +53,7 @@ public class ReplicaDTO implements IsSerializable { public String getIdentifier() { return identifier; } + public String[] getReplicableIdsAsStrings() { + return replicableIdsAsStrings; + } } \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ReplicationMasterDTO.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/shared/replication/ReplicationMasterDTO.java similarity index 65% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ReplicationMasterDTO.java rename to java/com.sap.sse.gwt/src/com/sap/sse/gwt/shared/replication/ReplicationMasterDTO.java index f9a4591133d..f10bf04b713 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ReplicationMasterDTO.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/shared/replication/ReplicationMasterDTO.java @@ -1,4 +1,4 @@ -package com.sap.sailing.gwt.ui.shared; +package com.sap.sse.gwt.shared.replication; import com.google.gwt.user.client.rpc.IsSerializable; @@ -8,28 +8,45 @@ public class ReplicationMasterDTO implements IsSerializable { private int servletPort; private String messagingHostname; private String exchangeName; - ReplicationMasterDTO() {} - public ReplicationMasterDTO(String hostname, int servletPort, String messagingHostname, int messagingPort, String exchangeName) { + private String[] replicableIdsAsString; + + // TODO bug 2465: capture the set of replicables that are replicated from this master; this may be a subset of the + // replica's / master's Replicables + + ReplicationMasterDTO() { + } + + public ReplicationMasterDTO(String hostname, int servletPort, String messagingHostname, int messagingPort, String exchangeName, String[] replicableIdsAsString) { super(); this.hostname = hostname; this.servletPort = servletPort; this.messagingHostname = messagingHostname; this.messagingPort = messagingPort; this.exchangeName = exchangeName; + this.replicableIdsAsString = replicableIdsAsString; } + public String getHostname() { return hostname; } + public int getMessagingPort() { return messagingPort; } + public int getServletPort() { return servletPort; } + public String getMessagingHostname() { return messagingHostname; } + public String getExchangeName() { return exchangeName; } + + public String[] getReplicableIdsAsString() { + return replicableIdsAsString; + } } \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ReplicationStateDTO.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/shared/replication/ReplicationStateDTO.java similarity index 93% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ReplicationStateDTO.java rename to java/com.sap.sse.gwt/src/com/sap/sse/gwt/shared/replication/ReplicationStateDTO.java index fc4d3a3fa02..04004a5a175 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ReplicationStateDTO.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/shared/replication/ReplicationStateDTO.java @@ -1,4 +1,4 @@ -package com.sap.sailing.gwt.ui.shared; +package com.sap.sse.gwt.shared.replication; import java.util.HashMap; import java.util.Map; diff --git a/java/com.sap.sse.mail.replication.test/META-INF/MANIFEST.MF b/java/com.sap.sse.mail.replication.test/META-INF/MANIFEST.MF index 2757ce82521..375a06837e7 100644 --- a/java/com.sap.sse.mail.replication.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.mail.replication.test/META-INF/MANIFEST.MF @@ -7,7 +7,7 @@ Bundle-Vendor: SAP Fragment-Host: com.sap.sse.mail Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Require-Bundle: org.hamcrest;bundle-version="1.1.0", - org.junit4;bundle-version="4.8.2", + org.junit;bundle-version="4.8.2", com.rabbitmq.client, com.sap.sse.replication.testsupport, com.sap.sse.mail.replication.testsupport diff --git a/java/com.sap.sse.mail.replication.testsupport/META-INF/MANIFEST.MF b/java/com.sap.sse.mail.replication.testsupport/META-INF/MANIFEST.MF index 84cf686c18a..1416b29bad6 100644 --- a/java/com.sap.sse.mail.replication.testsupport/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.mail.replication.testsupport/META-INF/MANIFEST.MF @@ -11,7 +11,7 @@ Require-Bundle: com.sap.sse.replication, com.sap.sse, com.sap.sse.common, org.hamcrest;bundle-version="1.1.0", - org.junit4;bundle-version="4.8.2", + org.junit;bundle-version="4.8.2", org.mockito.mockito-core;bundle-version="1.10.14", com.sun.mail.javax.mail;bundle-version="1.4.5" Export-Package: com.sap.sse.mail.replication.testsupport diff --git a/java/com.sap.sse.operationaltransformation.test/META-INF/MANIFEST.MF b/java/com.sap.sse.operationaltransformation.test/META-INF/MANIFEST.MF index 75cb42f3baf..e39da7f26ea 100644 --- a/java/com.sap.sse.operationaltransformation.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.operationaltransformation.test/META-INF/MANIFEST.MF @@ -5,6 +5,6 @@ Bundle-SymbolicName: com.sap.sse.operationaltransformation.test Bundle-Version: 1.0.0.qualifier Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Require-Bundle: org.junit4;bundle-version="4.8.2", +Require-Bundle: org.junit;bundle-version="4.8.2", com.sap.sse.replication Fragment-Host: com.sap.sse.operationaltransformation diff --git a/java/com.sap.sse.replication.testsupport/META-INF/MANIFEST.MF b/java/com.sap.sse.replication.testsupport/META-INF/MANIFEST.MF index ff140e7a262..3ce5281ec10 100644 --- a/java/com.sap.sse.replication.testsupport/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.replication.testsupport/META-INF/MANIFEST.MF @@ -5,7 +5,7 @@ Bundle-SymbolicName: com.sap.sse.replication.testsupport Bundle-Version: 1.0.0.qualifier Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Require-Bundle: org.junit4;bundle-version="4.8.2", +Require-Bundle: org.junit;bundle-version="4.8.2", com.sap.sse.replication, com.sap.sse.common, com.rabbitmq.client;bundle-version="2.8.4", diff --git a/java/com.sap.sse.replication.testsupport/src/com/sap/sse/replication/testsupport/AbstractServerReplicationTestSetUp.java b/java/com.sap.sse.replication.testsupport/src/com/sap/sse/replication/testsupport/AbstractServerReplicationTestSetUp.java index df30b57c3dd..95adc3baedc 100644 --- a/java/com.sap.sse.replication.testsupport/src/com/sap/sse/replication/testsupport/AbstractServerReplicationTestSetUp.java +++ b/java/com.sap.sse.replication.testsupport/src/com/sap/sse/replication/testsupport/AbstractServerReplicationTestSetUp.java @@ -17,12 +17,12 @@ import java.net.ServerSocket; import java.net.Socket; import java.net.URL; import java.net.URLConnection; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; -import net.jpountz.lz4.LZ4BlockOutputStream; - import org.junit.Rule; import org.junit.rules.Timeout; @@ -41,6 +41,8 @@ import com.sap.sse.replication.impl.ReplicationReceiver; import com.sap.sse.replication.impl.ReplicationServiceImpl; import com.sap.sse.replication.impl.SingletonReplicablesProvider; +import net.jpountz.lz4.LZ4BlockOutputStream; + public abstract class AbstractServerReplicationTestSetUp, ReplicableImpl extends ReplicableInterface> { private static final Logger logger = Logger.getLogger(AbstractServerReplicationTestSetUp.class.getName()); @@ -147,12 +149,12 @@ public abstract class AbstractServerReplicationTestSetUp replicaReplicator = new ReplicationServiceTestImpl(exchangeName, exchangeHost, rim, replicaDescriptor, - this.replica, this.master, masterReplicator, masterDescriptor); + this.replica, this.master, masterReplicator); masterDescriptor = replicaReplicator.getMasterDescriptor(); servletPort = masterDescriptor.getServletPort(); Pair, ReplicationMasterDescriptor> result = new Pair<>(replicaReplicator, masterDescriptor); @@ -236,15 +238,19 @@ public abstract class AbstractServerReplicationTestSetUp> replicablesToReplicate = new ArrayList<>(); + for (final String replicableIdAsString : replicaDescriptor.getReplicableIdsAsStrings()) { + replicablesToReplicate.add(getReplicablesProvider().getReplicable(replicableIdAsString, /* wait */ false)); + } this.masterDescriptor = new ReplicationMasterDescriptorImpl(exchangeHost, exchangeName, /* messagingPort */ 0, - UUID.randomUUID().toString(), "localhost", ss.getLocalPort()); + UUID.randomUUID().toString(), "localhost", ss.getLocalPort(), replicablesToReplicate); } ReplicationMasterDescriptor getMasterDescriptor() { diff --git a/java/com.sap.sse.replication/META-INF/MANIFEST.MF b/java/com.sap.sse.replication/META-INF/MANIFEST.MF index ded07e7ce34..9448e8b758b 100644 --- a/java/com.sap.sse.replication/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.replication/META-INF/MANIFEST.MF @@ -15,6 +15,7 @@ Require-Bundle: com.sap.sse.common, lz4-java;bundle-version="1.3.0", org.apache.commons.lang Bundle-ClassPath: . +Comment: The Web-ContextPath is referred to by ReplicationMasterDescriptorImpl.REPLICATION_SERVLET Web-ContextPath: /replication Import-Package: com.rabbitmq.client;version="2.8.4", javax.servlet;version="2.6.0", diff --git a/java/com.sap.sse.replication/WEB-INF/web.xml b/java/com.sap.sse.replication/WEB-INF/web.xml index 1c632f3388e..0f3b19a152c 100755 --- a/java/com.sap.sse.replication/WEB-INF/web.xml +++ b/java/com.sap.sse.replication/WEB-INF/web.xml @@ -22,6 +22,7 @@ Replication + /replication diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/ReplicationFactory.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/ReplicationFactory.java index bbf04436a26..f6e786f11bb 100755 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/ReplicationFactory.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/ReplicationFactory.java @@ -5,5 +5,5 @@ import com.sap.sse.replication.impl.ReplicationFactoryImpl; public interface ReplicationFactory { static ReplicationFactory INSTANCE = new ReplicationFactoryImpl(); - ReplicationMasterDescriptor createReplicationMasterDescriptor(String messagingHostname, String hostname, String exchangeName, int servletPort, int jmsPort, String jmsQueueName); + ReplicationMasterDescriptor createReplicationMasterDescriptor(String messagingHostname, String hostname, String exchangeName, int servletPort, int jmsPort, String jmsQueueName, Iterable> replicables); } diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/ReplicationMasterDescriptor.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/ReplicationMasterDescriptor.java index 7b038be9ffc..614bde58bc1 100644 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/ReplicationMasterDescriptor.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/ReplicationMasterDescriptor.java @@ -14,6 +14,10 @@ import com.sap.sse.replication.impl.ReplicationServlet; /** * Identifies a master server instance from which a replica can obtain an initial load and continuous updates. * + * TODO bug 2465: add the set of {@link Replicable}s that are replicated from the master represented by this descriptor, + * considering that this may be a subset only of the replicables running on this instance or the master server. Example: + * replicating only the SecurityService from some other server but being a master regarding all other Replicables. + * * @author Frank Mittag, Axel Uhl (d043530) * */ @@ -64,4 +68,6 @@ public interface ReplicationMasterDescriptor { */ Channel createChannel() throws IOException; + Iterable> getReplicables(); + } diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/ReplicationService.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/ReplicationService.java index 1aa878672e8..f3626ed1372 100644 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/ReplicationService.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/ReplicationService.java @@ -31,22 +31,20 @@ public interface ReplicationService { ReplicationMasterDescriptor getReplicatingFromMaster(); /** - * Same as {@link #startToReplicateFrom(ReplicationMasterDescriptor, Iterable)}, where the list of replicables - * is determined by asking this service's {@link ReplicablesProvider} which replicables are currently available. - * Note that this is not a good idea during the OSGi start-up phase, particularly if there is a bundle that - * starts after this bundle because then that {@link Replicable} will not yet be registered and won't become part - * of replication. - */ - void startToReplicateFrom(ReplicationMasterDescriptor master) throws IOException, ClassNotFoundException, InterruptedException; - - /** - * Performs a servlet request to the remote master server's {@link ReplicationServlet}, first registering this - * replica, ensuring the message queue for replicating operations is created, then subscribing to the master's - * replication message queue and asking the servlet for the stream containing the initial load which will then be - * used to {@link Replicable#initiallyFillFrom(java.io.ObjectInputStream) replace} the {@link Replicable replicables}' + * Starts to replicate the {@link Replicables} that the {@code master} descriptor + * {@link ReplicationMasterDescriptor#getReplicables() specifies}. Performs a servlet request to the remote master + * server's {@link ReplicationServlet}, first registering this replica, ensuring the message queue for replicating + * operations is created, then subscribing to the master's replication message queue and asking the servlet for the + * stream containing the initial load for the specific {@link Replicables} which will then be used to + * {@link Replicable#initiallyFillFrom(java.io.ObjectInputStream) replace} those {@link Replicable replicables}' * state. + *

          + * + * Note that calling this method is not a good idea during the OSGi start-up phase, particularly if there is a + * bundle that starts after this bundle because then that {@link Replicable} will not yet be registered and won't + * become part of replication. */ - void startToReplicateFrom(ReplicationMasterDescriptor master, Iterable> replicables) throws IOException, + void startToReplicateFrom(ReplicationMasterDescriptor master) throws IOException, ClassNotFoundException, InterruptedException; /** @@ -62,6 +60,11 @@ public interface ReplicationService { * {@link #registerReplica(ReplicaDescriptor) registered} again. */ void unregisterReplica(ReplicaDescriptor replica) throws IOException; + + /** + * Same as {@link #unregisterReplica(ReplicaDescriptor)}, identifying the replica by its {@link ReplicaDescriptor#getUuid() ID}. + */ + ReplicaDescriptor unregisterReplica(UUID replicaId) throws IOException; /** * For a replica replicating off this master, provides statistics in the form of number of operations sent to that @@ -80,7 +83,7 @@ public interface ReplicationService { /** * Stops all replica currently registered with this server. */ - void stopAllReplica() throws IOException; + void stopAllReplicas() throws IOException; /** * Returns an unique server identifier @@ -94,4 +97,6 @@ public interface ReplicationService { long getNumberOfBytesSent(ReplicaDescriptor replica); double getAverageNumberOfBytesPerMessage(ReplicaDescriptor replica); + + Iterable> getAllReplicables(); } diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/Activator.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/Activator.java index bc969eb1664..d143937243e 100644 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/Activator.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/Activator.java @@ -155,9 +155,9 @@ public class Activator implements BundleActivator { Integer.valueOf(System.getProperty(PROPERTY_NAME_REPLICATE_MASTER_QUEUE_PORT).trim()), serverReplicationMasterService.getServerIdentifier().toString(), System.getProperty(PROPERTY_NAME_REPLICATE_MASTER_SERVLET_HOST), - Integer.valueOf(System.getProperty(PROPERTY_NAME_REPLICATE_MASTER_SERVLET_PORT).trim())); + Integer.valueOf(System.getProperty(PROPERTY_NAME_REPLICATE_MASTER_SERVLET_PORT).trim()), replicables); try { - serverReplicationMasterService.startToReplicateFrom(master, replicables); + serverReplicationMasterService.startToReplicateFrom(master); logger.info("Automatic replication has been started."); } catch (ClassNotFoundException | IOException | InterruptedException e) { logger.log(Level.SEVERE, "Error with automatic replication from "+master, e); @@ -173,7 +173,7 @@ public class Activator implements BundleActivator { serverReplicationMasterService.stopToReplicateFromMaster(); } // stop sending stuff to the exchange for other replicas (if this is a master) - serverReplicationMasterService.stopAllReplica(); + serverReplicationMasterService.stopAllReplicas(); } public static BundleContext getDefaultContext() { diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicaDescriptor.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicaDescriptor.java index db2a3d3562b..3051ffb5c8b 100644 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicaDescriptor.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicaDescriptor.java @@ -6,6 +6,7 @@ import java.util.UUID; import com.sap.sse.common.TimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint; +import com.sap.sse.replication.Replicable; /** * Describes a replica by remembering its IP address as well as the replication time and a UUID. Hash code and equality @@ -21,15 +22,18 @@ public class ReplicaDescriptor implements Serializable { private final InetAddress ipAddress; private final TimePoint registrationTime; private final String additionalInformation; + private final String[] replicableIdsAsStrings; /** * Sets the registration time to now. */ - public ReplicaDescriptor(InetAddress ipAddress, UUID serverUuid, String additionalInformation) { + public ReplicaDescriptor(InetAddress ipAddress, UUID serverUuid, String additionalInformation, String[] replicableIdsAsStrings) { + assert replicableIdsAsStrings != null && replicableIdsAsStrings.length > 0; this.uuid = serverUuid; this.registrationTime = MillisecondsTimePoint.now(); this.ipAddress = ipAddress; this.additionalInformation = additionalInformation; + this.replicableIdsAsStrings = replicableIdsAsStrings; } public UUID getUuid() { @@ -48,6 +52,17 @@ public class ReplicaDescriptor implements Serializable { return additionalInformation; } + /** + * The {@link Replicable#getId() IDs} of the replicables that the replica represented by this descriptor + * has requested from the master for replication. The master may send operations for a superset of those + * replicables in case other replicas have requested replication for other replicables. Therefore, the + * replica must filter the operations received for those replicable IDs it has been requesting replication + * for. + */ + public String[] getReplicableIdsAsStrings() { + return replicableIdsAsStrings; + } + @Override public int hashCode() { final int prime = 31; @@ -74,7 +89,7 @@ public class ReplicaDescriptor implements Serializable { } public String toString() { - return ""+uuid+": "+ipAddress+" ("+additionalInformation+")"; + return ""+uuid+": "+ipAddress+" ("+additionalInformation+") for replicables "+String.join(", ", getReplicableIdsAsStrings()); } } diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicableWithObjectInputStream.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicableWithObjectInputStream.java index 1c2837b30ea..ae92c9a057e 100755 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicableWithObjectInputStream.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicableWithObjectInputStream.java @@ -226,6 +226,10 @@ public interface ReplicableWithObjectInputStream operationWithResultWithIdWrapper); + /** + * @return the descriptor of the master from which this replica is replicating this {@link Replicable}, or + * {@code null} if this {@link Replicable} is currently running as a master. + */ ReplicationMasterDescriptor getMasterDescriptor(); /** diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationFactoryImpl.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationFactoryImpl.java index 50bd889f690..90db95a2d38 100755 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationFactoryImpl.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationFactoryImpl.java @@ -1,12 +1,15 @@ package com.sap.sse.replication.impl; +import com.sap.sse.replication.Replicable; import com.sap.sse.replication.ReplicationFactory; import com.sap.sse.replication.ReplicationMasterDescriptor; public class ReplicationFactoryImpl implements ReplicationFactory { @Override - public ReplicationMasterDescriptor createReplicationMasterDescriptor(String messagingHostname, String hostname, String exchangeName, int servletPort, int messagingPort, String queueName) { - return new ReplicationMasterDescriptorImpl(messagingHostname, exchangeName, messagingPort, queueName, hostname, servletPort); + public ReplicationMasterDescriptor createReplicationMasterDescriptor(String messagingHostname, String hostname, + String exchangeName, int servletPort, int messagingPort, String queueName, + Iterable> replicables) { + return new ReplicationMasterDescriptorImpl(messagingHostname, exchangeName, messagingPort, queueName, hostname, servletPort, replicables); } } diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationInstancesManager.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationInstancesManager.java index f9b2517cc32..423194c3c58 100644 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationInstancesManager.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationInstancesManager.java @@ -1,11 +1,12 @@ package com.sap.sse.replication.impl; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; +import java.util.UUID; import com.sap.sse.common.Util; import com.sap.sse.replication.OperationWithResult; @@ -22,9 +23,10 @@ public class ReplicationInstancesManager { /** * The set of descriptors of all registered slaves. All broadcast operations will send the messages to all - * registered slaves, assuming the slaves will have subscribed for the replication topic. + * registered slaves, assuming the slaves will have subscribed for the replication topic. Keys are the + * {@link ReplicaDescriptor#getUuid() IDs of the corresponding values}. */ - private Set replicaDescriptors; + private Map replicaDescriptors; private Map>, Integer>> replicationCounts; @@ -53,7 +55,7 @@ public class ReplicationInstancesManager { private ReplicationMasterDescriptor replicationMasterDescriptor; public ReplicationInstancesManager() { - replicaDescriptors = new HashSet(); + replicaDescriptors = new HashMap<>(); replicationCounts = new HashMap>,Integer>>(); totalMessageCount = new HashMap<>(); totalNumberOfOperations = new HashMap<>(); @@ -71,21 +73,31 @@ public class ReplicationInstancesManager { } public Iterable getReplicaDescriptors() { - return Collections.unmodifiableCollection(replicaDescriptors); + return Collections.unmodifiableCollection(replicaDescriptors.values()); } public ReplicationMasterDescriptor getReplicationMasterDescriptor() { return replicationMasterDescriptor; } + public Iterable getAllReplicableIdsAtLeastOneReplicaIsReplicating() { + return replicaDescriptors.values().stream().map(replicableDescriptor->Arrays.asList(replicableDescriptor.getReplicableIdsAsStrings())).reduce(new HashSet(), + (result, ids)->{result.addAll(ids); return result;}, (r1, r2)->{r1.addAll(r2); return r1;}); + } + public void registerReplica(ReplicaDescriptor replica) { - replicaDescriptors.add(replica); - replicationCounts.put(replica, new HashMap>, Integer>()); + replicaDescriptors.put(replica.getUuid(), replica); + replicationCounts.put(replica, new HashMap<>()); } public void unregisterReplica(ReplicaDescriptor replica) { - replicaDescriptors.remove(replica); + unregisterReplica(replica.getUuid()); + } + + public ReplicaDescriptor unregisterReplica(UUID replicaUuid) { + final ReplicaDescriptor replica = replicaDescriptors.remove(replicaUuid); replicationCounts.remove(replica); + return replica; } /** diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationMasterDescriptorImpl.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationMasterDescriptorImpl.java index 34dc073b159..10655c9db87 100755 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationMasterDescriptorImpl.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationMasterDescriptorImpl.java @@ -41,6 +41,7 @@ public class ReplicationMasterDescriptorImpl implements ReplicationMasterDescrip private final String messagingHostname; private final int messagingPort; private final String queueName; + private final Iterable> replicables; private QueueingConsumer consumer; @@ -61,9 +62,15 @@ public class ReplicationMasterDescriptorImpl implements ReplicationMasterDescrip * replica with the master * @param servletPort * port for HTTP requests to the master + * @param replicables + * the {@link Replicable} objects to replicate from the master described by this object; the master will + * send operations only for those replicables that at least one replica is registered for; this, however, + * may mean that the master also sends operations for replicables that a particular replica hasn't + * registered for. Replicas shall silently drop operations for such replicables that they haven't + * requested replication for. */ public ReplicationMasterDescriptorImpl(String messagingHostname, String exchangeName, int messagingPort, - String queueName, String masterServletHostname, int servletPort) { + String queueName, String masterServletHostname, int servletPort, Iterable> replicables) { this.masterServletHostname = masterServletHostname; this.messagingHostname = messagingHostname; this.servletPort = servletPort; @@ -71,16 +78,20 @@ public class ReplicationMasterDescriptorImpl implements ReplicationMasterDescrip this.exchangeName = exchangeName; this.queueName = queueName; this.consumer = null; + this.replicables = replicables; } @Override public URL getReplicationRegistrationRequestURL(UUID uuid, String additional) throws MalformedURLException, UnsupportedEncodingException { + final String[] replicableIdsAsString = StreamSupport.stream(replicables.spliterator(), /* parallel */ false).map(r->r.getId()).toArray(i->new String[i]); return new URL("http", getHostname(), servletPort, REPLICATION_SERVLET + "?" + ReplicationServlet.ACTION + "=" + ReplicationServlet.Action.REGISTER.name() + "&" + ReplicationServlet.SERVER_UUID + "=" + java.net.URLEncoder.encode(uuid.toString(), "UTF-8") + "&" + ReplicationServlet.ADDITIONAL_INFORMATION + "=" - + java.net.URLEncoder.encode(ServerInfo.getBuildVersion(), "UTF-8")); + + java.net.URLEncoder.encode(ServerInfo.getBuildVersion(), "UTF-8") + "&" + + ReplicationServlet.REPLICABLES_IDS_AS_STRINGS_COMMA_SEPARATED + "=" + + java.net.URLEncoder.encode(String.join(",", replicableIdsAsString), "UTF-8")); } @Override @@ -90,6 +101,11 @@ public class ReplicationMasterDescriptorImpl implements ReplicationMasterDescrip + uuid.toString()); } + @Override + public Iterable> getReplicables() { + return replicables; + } + @Override public int hashCode() { final int prime = 31; diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationReceiver.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationReceiver.java index a114d3783cf..1702fe792ac 100755 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationReceiver.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationReceiver.java @@ -66,7 +66,11 @@ public class ReplicationReceiver implements Runnable { private static final long CHECK_INTERVAL_MILLIS = 2000; // how long (milliseconds) to pause before checking connection again private static final int CHECK_COUNT = 150; // how long to check, value is CHECK_INTERVAL second steps + /** + * descriptor of the master server from which this replicator receives messages + */ private final ReplicationMasterDescriptor master; + private final ReplicablesProvider replicableProvider; /** @@ -138,12 +142,14 @@ public class ReplicationReceiver implements Runnable { } /** - * Starts fetching messages from the {@link #consumer}. After receiving a single message, assumes it's an - * {@link Iterable} of serialized {@link RacingEventServiceOperation} objects, and applies it to the - * {@link RacingEventService} which is obtained from the service tracker passed to this replicator at construction - * time. + * Starts fetching messages from the {@link #consumer}. After receiving a single message, assumes it's a + * {@link ReplicationServiceImpl#createUncompressingInputStream(InputStream) compressed} stream that first + * {@link DataInputStream#readUTF() encodes a UTF string} representing the {@link Replicable#getId() replicable ID}, + * followed by a sequence of serialized {@code byte[]} objects which each can be {@link Replicable#readOperation(InputStream) de-serialized} + * by the receiving {@link Replicable} identified by the ID received as a prefix. This method then applies these operations to the + * {@link Replicable} identified by the ID, retrieved through the {@link #replicableProvider}. * - * @see ReplicationServiceImpl#executed(RacingEventServiceOperation) + * @see ReplicationServiceExecutionListener#executed(OperationWithResult) */ @Override public void run() { @@ -170,12 +176,13 @@ public class ReplicationReceiver implements Runnable { } } } - byte[] bytesFromMessage = delivery.getBody(); + final byte[] bytesFromMessage = delivery.getBody(); checksPerformed = 0; // Set the replicable's class's class loader as context for deserialization so that all exported classes // of all required bundles/packages can be deserialized at least final InputStream uncompressingInputStream = ReplicationServiceImpl.createUncompressingInputStream(new ByteArrayInputStream(bytesFromMessage)); - String replicableIdAsString = new DataInputStream(uncompressingInputStream).readUTF(); + final String replicableIdAsString = new DataInputStream(uncompressingInputStream).readUTF(); + // TODO bug 2465: decide based on the master descriptor whether we want to process this message; if it's for a Replicable we're not replicating from that master, drop the message Replicable replicable = replicableProvider.getReplicable(replicableIdAsString, /* wait */ false); if (replicable != null) { ObjectInputStream ois = new ObjectInputStream(uncompressingInputStream); // no special stream required; only reading a generic byte[] @@ -183,11 +190,15 @@ public class ReplicationReceiver implements Runnable { try { while (true) { byte[] serializedOperation = (byte[]) ois.readObject(); - readOperationAndApplyOrQueueIt(replicable, serializedOperation); - operationCount++; - operationsInMessage++; - if (operationCount % 10000l == 0) { - logger.info("Received " + operationCount + " operations so far"); + if (Util.contains(master.getReplicables(), replicable)) { + readOperationAndApplyOrQueueIt(replicable, serializedOperation); + operationCount++; + operationsInMessage++; + if (operationCount % 10000l == 0) { + logger.info("Received " + operationCount + " operations so far"); + } + } else { + logger.fine("Dropping operation for non-replicated replicable "+replicable.getId()); } } } catch (EOFException eof) { diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java index 8fe224ca762..3b8ba7f1c56 100755 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java @@ -13,6 +13,7 @@ import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Timer; @@ -22,9 +23,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; -import net.jpountz.lz4.LZ4BlockInputStream; -import net.jpountz.lz4.LZ4BlockOutputStream; - import org.osgi.util.tracker.ServiceTracker; import com.rabbitmq.client.AMQP.Exchange; @@ -43,6 +41,9 @@ import com.sap.sse.replication.ReplicationMasterDescriptor; import com.sap.sse.replication.ReplicationService; import com.sap.sse.util.HttpUrlConnectionHelper; +import net.jpountz.lz4.LZ4BlockInputStream; +import net.jpountz.lz4.LZ4BlockOutputStream; + /** * Manages a set of observers of {@link Replicable}, receiving notifications for the operations they perform that * require replication. This service provides the connectivity and central management including central keeping of @@ -79,7 +80,8 @@ public class ReplicationServiceImpl implements ReplicationService { /** * null, if this instance is not currently replicating from some master; the master's descriptor - * otherwise + * otherwise; note that partial replication is supported, meaning that only operations for a subset of the + * {@link Replicable}s running on this server will be considered when received from the master. */ private ReplicationMasterDescriptor replicatingFromMaster; @@ -241,10 +243,11 @@ public class ReplicationServiceImpl implements ReplicationService { private class LifeCycleListener implements ReplicableLifeCycleListener { @Override public void replicableAdded(Replicable replicable) { - // add a replication listener to the new replicable only if there are replicas currently registered + // add a replication listener to the new replicable only if there are replicas currently registered... synchronized (replicationInstancesManager) { - if (replicationInstancesManager.hasReplicas()) { - addNewOperationExecutionListener(replicable); + // .. and at least one of them wants to replicate the replicable with that ID + if (Util.contains(replicationInstancesManager.getAllReplicableIdsAtLeastOneReplicaIsReplicating(), replicable.getId().toString())) { + ensureOperationExecutionListener(replicable); } } } @@ -340,12 +343,16 @@ public class ReplicationServiceImpl implements ReplicationService { private Iterable> getReplicables() { return replicablesProvider.getReplicables(); } - + + private Replicable getReplicable(String replicableIdAsString, boolean wait) { + return replicablesProvider.getReplicable(replicableIdAsString, wait); + } + @Override public void registerReplica(ReplicaDescriptor replica) throws IOException { synchronized (replicationInstancesManager) { if (!replicationInstancesManager.hasReplicas()) { - addAsListenerToReplicables(); + addAsListenerToReplicables(replica.getReplicableIdsAsStrings()); synchronized (this) { if (masterChannel == null) { masterChannel = createMasterChannelAndDeclareFanoutExchange(); @@ -357,25 +364,34 @@ public class ReplicationServiceImpl implements ReplicationService { logger.info("Registered replica " + replica); } - private void addAsListenerToReplicables() { - for (Replicable replicable : getReplicables()) { - addNewOperationExecutionListener(replicable); + private void addAsListenerToReplicables(String[] replicableIdsAsStringForReplicablesToReplicate) { + for (final String replicableIdAsStringForReplicableToReplicate : replicableIdsAsStringForReplicablesToReplicate) { + Replicable replicable = getReplicable(replicableIdAsStringForReplicableToReplicate, /* wait */ true); + ensureOperationExecutionListener(replicable); } } - private void addNewOperationExecutionListener(Replicable replicable) { - final ReplicationServiceExecutionListener listener = new ReplicationServiceExecutionListener(this, - replicable); - executionListenersByReplicableIdAsString.put(replicable.getId().toString(), listener); + /** + * If no listener exists yet for {@code replicable}, a new one is created, registered as listener on + * {@code replicable} and remembered in {@link #executionListenersByReplicableIdAsString}. Otherwise, this method is + * a no-op. + */ + private void ensureOperationExecutionListener(Replicable replicable) { + if (!executionListenersByReplicableIdAsString.containsKey(replicable.getId().toString())) { + final ReplicationServiceExecutionListener listener = new ReplicationServiceExecutionListener(this, replicable); + executionListenersByReplicableIdAsString.put(replicable.getId().toString(), listener); + } } @Override - public void unregisterReplica(ReplicaDescriptor replica) throws IOException { - logger.info("Unregistering replica " + replica); + public ReplicaDescriptor unregisterReplica(UUID replicaUuid) throws IOException { + logger.info("Unregistering replica with ID " + replicaUuid); synchronized (replicationInstancesManager) { - replicationInstancesManager.unregisterReplica(replica); - if (!replicationInstancesManager.hasReplicas()) { - removeAsListenerFromReplicables(); + final Iterable oldReplicablesInReplication = replicationInstancesManager.getAllReplicableIdsAtLeastOneReplicaIsReplicating(); + final ReplicaDescriptor unregisteredReplica = replicationInstancesManager.unregisterReplica(replicaUuid); + final Iterable newReplicablesInReplication = replicationInstancesManager.getAllReplicableIdsAtLeastOneReplicaIsReplicating(); + for (final String idAsStringOfReplicableNoReplicaIsInterestedInAnymore : Util.removeAll(newReplicablesInReplication, Util.addAll(oldReplicablesInReplication, new HashSet<>()))) { + removeAsListenerFromReplicable(idAsStringOfReplicableNoReplicaIsInterestedInAnymore); synchronized (this) { if (masterChannel != null) { masterChannel.getConnection().close(); @@ -383,6 +399,23 @@ public class ReplicationServiceImpl implements ReplicationService { } } } + return unregisteredReplica; + } + } + + @Override + public void unregisterReplica(ReplicaDescriptor replica) throws IOException { + logger.info("Unregistering replica " + replica); + unregisterReplica(replica.getUuid()); + } + + private void removeAsListenerFromReplicable(String idAsStringOfReplicableNoReplicaIsInterestedInAnymore) { + ReplicationServiceExecutionListener listener = executionListenersByReplicableIdAsString.remove(idAsStringOfReplicableNoReplicaIsInterestedInAnymore); + if (listener != null) { + logger.info("Unsubscribed replication listener from replicable with ID "+idAsStringOfReplicableNoReplicaIsInterestedInAnymore); + listener.unsubscribe(); + } else { + logger.warning("Couldn't find a replication listener on replicable with ID "+idAsStringOfReplicableNoReplicaIsInterestedInAnymore); } } @@ -540,22 +573,16 @@ public class ReplicationServiceImpl implements ReplicationService { * which implements the initial load sending process. */ @Override - public void startToReplicateFrom(ReplicationMasterDescriptor master) throws IOException, ClassNotFoundException, - InterruptedException { - final Iterable> replicables = getReplicables(); - startToReplicateFrom(master, replicables); - } - - @Override - public void startToReplicateFrom(final ReplicationMasterDescriptor master, final Iterable> replicables) + public void startToReplicateFrom(final ReplicationMasterDescriptor master) throws IOException, ClassNotFoundException, InterruptedException { if (initialLoadChannels.containsKey(master)) { logger.warning("An initial load from "+master+" is already running, replicating the following replicables: "+ initialLoadChannels.get(master).getReplicables()+". Not starting a second time."); } else { + final Iterable> replicables = master.getReplicables(); logger.info("Starting to replicate from " + master); try { - registerReplicaWithMaster(master); + registerReplicaWithMaster(master, replicables); } catch (Exception ex) { logger.log(Level.SEVERE, "ERROR", ex); throw ex; @@ -627,9 +654,14 @@ public class ReplicationServiceImpl implements ReplicationService { } /** + * @param replicables + * the replica is registered for these {@link Replicable}s. The master sends operations only for + * replicables that at least one replica has registered for. This may mean that operations are received + * for replicables for which no replicable was requested. Replicas shall drop such operations silently. + * * @return the UUID that the master generated for this client which is also entered into {@link #replicaUUIDs} */ - private String registerReplicaWithMaster(ReplicationMasterDescriptor master) throws IOException, + private String registerReplicaWithMaster(ReplicationMasterDescriptor master, Iterable> replicables) throws IOException, ClassNotFoundException { URL replicationRegistrationRequestURL = master.getReplicationRegistrationRequestURL(getServerIdentifier(), ServerInfo.getBuildVersion()); @@ -702,6 +734,11 @@ public class ReplicationServiceImpl implements ReplicationService { return replicationInstancesManager.getAverageNumberOfBytesPerMessage(replica); } + @Override + public Iterable> getAllReplicables() { + return getReplicablesProvider().getReplicables(); + } + @Override public void stopToReplicateFromMaster() throws IOException { ReplicationMasterDescriptor descriptor = getReplicatingFromMaster(); @@ -740,7 +777,7 @@ public class ReplicationServiceImpl implements ReplicationService { } @Override - public void stopAllReplica() throws IOException { + public void stopAllReplicas() throws IOException { if (replicationInstancesManager.hasReplicas()) { replicationInstancesManager.removeAll(); removeAsListenerFromReplicables(); diff --git a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServlet.java b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServlet.java index 0621d2faa94..b11ff33bbd9 100755 --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServlet.java +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServlet.java @@ -17,6 +17,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import net.jpountz.lz4.LZ4BlockInputStream; import net.jpountz.lz4.LZ4BlockOutputStream; import org.apache.commons.lang.StringEscapeUtils; @@ -77,15 +78,23 @@ public class ReplicationServlet extends AbstractHttpServlet { /** * The client identifies itself in the request. Two servlet operations are supported currently: registering the - * client with the replication service (if not already created, the JMS replication topic will be created by this - * registration); and obtaining an initial load stream that the replica can use to initialize its - * {@link RacingEventService}. The operation performed is selected by passing one of the {@link Action} enumeration - * values for the URL parameter named {@link #ACTION}. + * client with the replication service (if not already created, the message exchange will be created by this + * registration); and triggering sending an initial load stream through RabbitMQ that the replica can use to + * initialize its {@link Replicable} s. The IDs of the replicables of which the initial load is to be sent is + * expected as the {@link #REPLICABLES_IDS_AS_STRINGS_COMMA_SEPARATED} parameter value. The servlet response + * consists of the name of the RabbitMQ queue name that the client can connect to and through which to receive + * the LZ4-compressed initial load stream per replicable, using a {@link RabbitInputStreamProvider} and + * an {@link LZ4BlockInputStream}.. + *

          + * + * The operation performed is selected by passing one of the {@link Action} enumeration values for the URL parameter + * named {@link #ACTION}. */ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String action = req.getParameter(ACTION); logger.info("Received replication request, action is "+action); + String[] replicableIdsAsStrings; switch (Action.valueOf(action)) { case REGISTER: registerClientWithReplicationService(req, resp); @@ -94,7 +103,7 @@ public class ReplicationServlet extends AbstractHttpServlet { deregisterClientWithReplicationService(req, resp); break; case INITIAL_LOAD: - String[] replicableIdsAsStrings = req.getParameter(REPLICABLES_IDS_AS_STRINGS_COMMA_SEPARATED).split(","); + replicableIdsAsStrings = req.getParameter(REPLICABLES_IDS_AS_STRINGS_COMMA_SEPARATED).split(","); Channel channel = getReplicationService().createMasterChannel(); try { RabbitOutputStream ros = new RabbitOutputStream(INITIAL_LOAD_PACKAGE_SIZE, channel, @@ -174,17 +183,22 @@ public class ReplicationServlet extends AbstractHttpServlet { } private void deregisterClientWithReplicationService(HttpServletRequest req, HttpServletResponse resp) throws IOException { - ReplicaDescriptor replica = getReplicaDescriptor(req); - getReplicationService().unregisterReplica(replica); - logger.info("Deregistered replication client with this server " + replica.getIpAddress()); - resp.setContentType("text/plain"); - resp.getWriter().print(replica.getUuid()); + final UUID replicaUuid = UUID.fromString(req.getParameter(SERVER_UUID)); + final ReplicaDescriptor replica = getReplicationService().unregisterReplica(replicaUuid); + if (replica != null) { + logger.info("Deregistered replication client with this server " + replica.getIpAddress()); + resp.setContentType("text/plain"); + resp.getWriter().print(replica.getUuid()); + } else { + logger.warning("Couldn't find replica to de-register with ID "+replicaUuid); + } } private void registerClientWithReplicationService(HttpServletRequest req, HttpServletResponse resp) throws IOException { - ReplicaDescriptor replica = getReplicaDescriptor(req); + final ReplicaDescriptor replica = getReplicaDescriptor(req); getReplicationService().registerReplica(replica); + logger.info("Registered new replica " + replica); resp.setContentType("text/plain"); resp.getWriter().print(replica.getUuid()); } @@ -193,7 +207,7 @@ public class ReplicationServlet extends AbstractHttpServlet { InetAddress ipAddress = InetAddress.getByName(req.getRemoteAddr()); UUID uuid = UUID.fromString(req.getParameter(SERVER_UUID)); String additional = req.getParameter(ADDITIONAL_INFORMATION); - logger.info("Registered new replica " + ipAddress + " " + uuid.toString() + " " + additional); - return new ReplicaDescriptor(ipAddress, uuid, additional); + final String[] replicableIdsAsStrings = req.getParameter(REPLICABLES_IDS_AS_STRINGS_COMMA_SEPARATED).split(","); + return new ReplicaDescriptor(ipAddress, uuid, additional, replicableIdsAsStrings); } } diff --git a/java/com.sap.sse.security.replication.test/META-INF/MANIFEST.MF b/java/com.sap.sse.security.replication.test/META-INF/MANIFEST.MF index 69111890757..53ee9028cfb 100644 --- a/java/com.sap.sse.security.replication.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.security.replication.test/META-INF/MANIFEST.MF @@ -7,7 +7,7 @@ Bundle-Vendor: SAP Fragment-Host: com.sap.sse.security Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Require-Bundle: org.hamcrest;bundle-version="1.1.0", - org.junit4;bundle-version="4.8.2", + org.junit;bundle-version="4.8.2", com.sap.sse.security.userstore.mongodb, com.sap.sse.mongodb, com.rabbitmq.client, diff --git a/java/com.sap.sse.security.test/META-INF/MANIFEST.MF b/java/com.sap.sse.security.test/META-INF/MANIFEST.MF index c6c6aa543bc..98d004b01d1 100755 --- a/java/com.sap.sse.security.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.security.test/META-INF/MANIFEST.MF @@ -6,7 +6,7 @@ Bundle-Version: 1.0.0.qualifier Bundle-Vendor: SAP Fragment-Host: com.sap.sse.security Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Require-Bundle: org.junit4;bundle-version="4.8.2", +Require-Bundle: org.junit;bundle-version="4.8.2", com.sap.sse.security.userstore.mongodb, org.mongodb.mongo-java-driver;bundle-version="2.13.0", com.sap.sse.mongodb, diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/XLF/StringMessages_ja.properties.xlf b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/XLF/StringMessages_ja.properties.xlf index 6e129ed41ee..13291e0934a 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/XLF/StringMessages_ja.properties.xlf +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/XLF/StringMessages_ja.properties.xlf @@ -1,6 +1,6 @@ - +

          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 diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/XLF/StringMessages_ru.properties.xlf b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/XLF/StringMessages_ru.properties.xlf index 52245d238ab..06038d7fc62 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/XLF/StringMessages_ru.properties.xlf +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/XLF/StringMessages_ru.properties.xlf @@ -1,6 +1,6 @@ - +
          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 diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/XLF/StringMessages_zh_CN.properties.xlf b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/XLF/StringMessages_zh_CN.properties.xlf index 68818b0e333..50cc7d36195 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/XLF/StringMessages_zh_CN.properties.xlf +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/i18n/XLF/StringMessages_zh_CN.properties.xlf @@ -1,6 +1,6 @@ - +
          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 diff --git a/java/com.sap.sse.security/META-INF/MANIFEST.MF b/java/com.sap.sse.security/META-INF/MANIFEST.MF index dde6a66b53b..797ca5b03e5 100644 --- a/java/com.sap.sse.security/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.security/META-INF/MANIFEST.MF @@ -18,10 +18,8 @@ Import-Package: com.sap.sse.util, Bundle-ActivationPolicy: lazy Bundle-ClassPath: . Require-Bundle: javax.servlet;bundle-version="3.1.0", - slf4j.api;bundle-version="1.6.4", org.json.simple, com.sun.jersey, - org.apache.commons.logging;bundle-version="1.1.1", org.apache.commons.beanutils;bundle-version="1.8.0", org.apache.shiro.core;bundle-version="1.2.2", org.apache.shiro.ehcache;bundle-version="1.2.3", diff --git a/java/com.sap.sse.shared.android.test/META-INF/MANIFEST.MF b/java/com.sap.sse.shared.android.test/META-INF/MANIFEST.MF index 40e6dcd56b5..53d4cbe93cb 100644 --- a/java/com.sap.sse.shared.android.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.shared.android.test/META-INF/MANIFEST.MF @@ -9,5 +9,5 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Require-Bundle: org.mockito.mockito-core;bundle-version="1.9.5", org.hamcrest;bundle-version="1.1.0", org.objenesis;bundle-version="1.3.0", - org.junit4;bundle-version="4.8.2" + org.junit;bundle-version="4.8.2" Export-Package: com.sap.sse.shared.android.test diff --git a/java/com.sap.sse.test/META-INF/MANIFEST.MF b/java/com.sap.sse.test/META-INF/MANIFEST.MF index 09b9c3c8b12..7f6967449cc 100644 --- a/java/com.sap.sse.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.test/META-INF/MANIFEST.MF @@ -6,4 +6,4 @@ Bundle-Version: 1.0.0.qualifier Bundle-Vendor: SAP Fragment-Host: com.sap.sse Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Require-Bundle: org.junit4;bundle-version="4.8.2" +Require-Bundle: org.junit;bundle-version="4.8.2" diff --git a/java/com.sap.sse/src/com/sap/sse/osgi/CachedOsgiTypeBasedServiceFinder.java b/java/com.sap.sse/src/com/sap/sse/osgi/CachedOsgiTypeBasedServiceFinder.java index 4d4d4c6e95f..3d2b944d5c4 100644 --- a/java/com.sap.sse/src/com/sap/sse/osgi/CachedOsgiTypeBasedServiceFinder.java +++ b/java/com.sap.sse/src/com/sap/sse/osgi/CachedOsgiTypeBasedServiceFinder.java @@ -4,6 +4,8 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; @@ -12,10 +14,17 @@ import org.osgi.util.tracker.ServiceTrackerCustomizer; import com.sap.sse.common.NoCorrespondingServiceRegisteredException; import com.sap.sse.common.TypeBasedServiceFinder; +import com.sap.sse.common.Util; /** - * Caches OSGI services of a certain type, based on their type property. Has to be registered as the - * {@link ServiceTrackerCustomizer} of a {@link ServiceTracker}. + * Caches OSGI services of a certain type, based on their {@link TypeBasedServiceFinder#TYPE} + * {@link BundleContext#registerService(Class, Object, java.util.Dictionary) property}. Has to be registered as the + * {@link ServiceTrackerCustomizer} of a {@link ServiceTracker}. The {@link #findService(String)} method looks + * up the requested type string against the {@link TypeBasedServiceFinder#TYPE} property that OSGi-based + * service implementations must have provided as a property during service registration with the OSGi service + * registry. Don't confuse this "type" string with the classes of the service implementations which all + * have to implement {@code ServiceT} but whose name has nothing to do with the type string used here for + * service lookup. * * @author Fredrik Teschke * @@ -24,34 +33,65 @@ import com.sap.sse.common.TypeBasedServiceFinder; */ public class CachedOsgiTypeBasedServiceFinder implements ServiceTrackerCustomizer, TypeBasedServiceFinder { + private static final Logger logger = Logger.getLogger(CachedOsgiTypeBasedServiceFinder.class.getName()); private final Map services = new HashMap<>(); private final BundleContext context; - private Class serviceType; + private final Class serviceType; private ServiceT fallback; + private final Map>> callbacks; public CachedOsgiTypeBasedServiceFinder(Class serviceType, BundleContext context) { this.serviceType = serviceType; this.context = context; + this.callbacks = new HashMap<>(); } @Override public ServiceT findService(String type) { - ServiceT service = services.get(type); - if (service == null) { - if (fallback != null) { - return fallback; + synchronized (services) { + ServiceT service = services.get(type); + if (service == null) { + if (fallback != null) { + return fallback; + } else { + throw new NoCorrespondingServiceRegisteredException("Could not find service", type, serviceType.getSimpleName()); + } + } + return service; + } + } + + @Override + public void applyServiceWhenAvailable(String type, Callback callback) { + synchronized (services) { + ServiceT service = services.get(type); + if (service != null) { + callback.withService(service); } else { - throw new NoCorrespondingServiceRegisteredException("Could not find service", type, serviceType.getSimpleName()); + Util.addToValueSet(callbacks, type, callback); } } - return service; } @Override public ServiceT addingService(ServiceReference reference) { String type = (String) reference.getProperty(TypeBasedServiceFinder.TYPE); ServiceT service = context.getService(reference); - services.put(type, service); + Set> callbacksToNotify; + synchronized (services) { + services.put(type, service); + callbacksToNotify = callbacks.remove(type); + } + if (callbacksToNotify != null) { + callbacksToNotify.forEach(c -> { + try { + c.withService(service); + } catch (Exception e) { + logger.log(Level.SEVERE, + "Exception trying to inform " + c + " about the availability of service " + service,e); + } + }); + } return service; } @@ -63,7 +103,9 @@ public class CachedOsgiTypeBasedServiceFinder implements ServiceTracke @Override public void removedService(ServiceReference reference, ServiceT service) { String type = (String) reference.getProperty(TypeBasedServiceFinder.TYPE); - services.remove(type); + synchronized (services) { + services.remove(type); + } } @Override @@ -73,6 +115,8 @@ public class CachedOsgiTypeBasedServiceFinder implements ServiceTracke @Override public Set findAllServices() { - return new HashSet(services.values()); + synchronized (services) { + return new HashSet(services.values()); + } } } diff --git a/java/com.sap.sse/src/com/sap/sse/osgi/CachedOsgiTypeBasedServiceFinderFactory.java b/java/com.sap.sse/src/com/sap/sse/osgi/CachedOsgiTypeBasedServiceFinderFactory.java index d2aac7d0a42..686e71df714 100644 --- a/java/com.sap.sse/src/com/sap/sse/osgi/CachedOsgiTypeBasedServiceFinderFactory.java +++ b/java/com.sap.sse/src/com/sap/sse/osgi/CachedOsgiTypeBasedServiceFinderFactory.java @@ -13,13 +13,16 @@ import com.sap.sse.common.TypeBasedServiceFinderFactory; /** * Use the OSGi service registry to find the desired services. + *

          * - * Using this indirection it is possible to integrate new service types into the system without - * the need to change any existing code.

          + * Using this indirection it is possible to integrate new service types into the system without the need to change any + * existing code. + *

          + * + * Note that it is not easily possible to use the {@link CachedOsgiTypeBasedServiceFinderFactory} in test case scenarios + * because there is usually no OSGi context available during test execution. Consider using the + * {@link MockServiceFinderFactory} instead. * - * Note that it is not easily possible to use the {@link CachedOsgiTypeBasedServiceFinderFactory} in test - * case scenarios because there is usually no OSGi context available during test execution. - * Consider using the {@link MockServiceFinderFactory} instead. * @author Fredrik Teschke * */ @@ -35,18 +38,14 @@ public class CachedOsgiTypeBasedServiceFinderFactory implements TypeBasedService @Override public TypeBasedServiceFinder createServiceFinder(Class clazz) { @SuppressWarnings("unchecked") - CachedOsgiTypeBasedServiceFinder finder = (CachedOsgiTypeBasedServiceFinder) serviceFinders - .get(clazz); - + CachedOsgiTypeBasedServiceFinder finder = (CachedOsgiTypeBasedServiceFinder) serviceFinders.get(clazz); if (finder == null) { finder = new CachedOsgiTypeBasedServiceFinder<>(clazz, context); ServiceTracker tracker = new ServiceTracker(context, clazz, finder); - serviceFinders.put(clazz, finder); serviceTrackers.add(tracker); tracker.open(); } - return finder; } diff --git a/java/com.sap.sse/src/com/sap/sse/util/SmartFutureCache.java b/java/com.sap.sse/src/com/sap/sse/util/SmartFutureCache.java index 7988f3f3fbb..c9fd773c4a4 100755 --- a/java/com.sap.sse/src/com/sap/sse/util/SmartFutureCache.java +++ b/java/com.sap.sse/src/com/sap/sse/util/SmartFutureCache.java @@ -421,9 +421,7 @@ public class SmartFutureCache> { logger.finest("resuming cache "+nameForLocks); for (Iterator>>>>> i=triggeredAndNotYetScheduled.entrySet().iterator(); i.hasNext(); ) { Entry>>>> e = i.next(); - if (logger.isLoggable(Level.FINEST)) { - logger.finest("while resuming "+nameForLocks+", triggering update for key "+e.getKey()+" with update interval "+e.getValue()); - } + logger.finest(()->"while resuming "+nameForLocks+", triggering update for key "+e.getKey()+" with update interval "+e.getValue()); triggerUpdate(e.getKey(), e.getValue().getA()); i.remove(); } diff --git a/java/com.tractrac.clientmodule/META-INF/MANIFEST.MF b/java/com.tractrac.clientmodule/META-INF/MANIFEST.MF index 73b357e4426..7a44b192f75 100644 --- a/java/com.tractrac.clientmodule/META-INF/MANIFEST.MF +++ b/java/com.tractrac.clientmodule/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: TracTrac Client module Bundle-SymbolicName: com.tractrac.clientmodule -Bundle-Version: 3.2.1 +Bundle-Version: 3.3.1 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ClassPath: ., lib/TracAPI.jar @@ -23,17 +23,24 @@ Export-Package: com.tractrac.asio.lib.api, com.tractrac.dataflow.lib.api.live, com.tractrac.dataflow.lib.api.live.server, com.tractrac.dataflow.lib.api.protocol, + com.tractrac.dataflow.lib.api.statistics, com.tractrac.dataflow.lib.api.stored, com.tractrac.dataflow.lib.api.ws, + com.tractrac.dataflow.lib.impl, + com.tractrac.dataflow.lib.impl.statistics, com.tractrac.dataflow.lib.prov.live.ascii, com.tractrac.dataflow.lib.prov.live.provider, com.tractrac.dataflow.lib.prov.live.server, com.tractrac.dataflow.lib.prov.live.tcp, com.tractrac.dataflow.lib.prov.live.tcp.provider, com.tractrac.dataflow.lib.prov.live.tcp.server, - com.tractrac.dataflow.lib.prov.stored, com.tractrac.dataflow.lib.prov.stored.jdbc, + com.tractrac.dataflow.lib.prov.stored.mtb, + com.tractrac.dataflow.lib.prov.stored.mtb.stream, + com.tractrac.dataflow.lib.prov.stored.mtb.writer, com.tractrac.dataflow.lib.prov.stored.tcp, + com.tractrac.dataflow.lib.prov.stored.tcp.get, + com.tractrac.dataflow.lib.prov.stored.tcp.put, com.tractrac.dataflow.lib.spi, com.tractrac.dataflow.lib.spi.authentication, com.tractrac.model.lib.api, @@ -43,6 +50,14 @@ Export-Package: com.tractrac.asio.lib.api, com.tractrac.model.lib.api.metadata, com.tractrac.model.lib.api.route, com.tractrac.model.lib.api.spatial, + com.tractrac.model.lib.impl, + com.tractrac.model.lib.impl.attachment, + com.tractrac.model.lib.impl.data, + com.tractrac.model.lib.impl.event, + com.tractrac.model.lib.impl.event.json, + com.tractrac.model.lib.impl.metadata, + com.tractrac.model.lib.impl.route, + com.tractrac.model.lib.impl.spatial, com.tractrac.osgi, com.tractrac.subscription.app.tracapi, com.tractrac.subscription.lib.api, @@ -51,6 +66,16 @@ Export-Package: com.tractrac.asio.lib.api, com.tractrac.subscription.lib.api.event, com.tractrac.subscription.lib.api.race, com.tractrac.subscription.lib.api.route, + com.tractrac.subscription.lib.impl, + com.tractrac.subscription.lib.impl.competitor, + com.tractrac.subscription.lib.impl.control, + com.tractrac.subscription.lib.impl.event, + com.tractrac.subscription.lib.impl.gms, + com.tractrac.subscription.lib.impl.io, + com.tractrac.subscription.lib.impl.io.controllers, + com.tractrac.subscription.lib.impl.io.receivers, + com.tractrac.subscription.lib.impl.race, + com.tractrac.subscription.lib.impl.route, com.tractrac.util.lib.api, com.tractrac.util.lib.api.autolog, com.tractrac.util.lib.api.classloader, @@ -63,7 +88,15 @@ Export-Package: com.tractrac.asio.lib.api, com.tractrac.util.lib.api.programparameters, com.tractrac.util.lib.api.search, com.tractrac.util.lib.api.selector, - com.tractrac.util.lib.api.serialize + com.tractrac.util.lib.api.serialize, + com.tractrac.util.lib.impl.autolog, + com.tractrac.util.lib.impl.autolog.file, + com.tractrac.util.lib.impl.autolog.log4j, + com.tractrac.util.lib.impl.autolog.multiple, + com.tractrac.util.lib.impl.autolog.println, + com.tractrac.util.lib.impl.classloader, + com.tractrac.util.lib.impl.http, + com.tractrac.util.lib.impl.programparameters Bundle-ActivationPolicy: lazy Bundle-Vendor: TracTrac Import-Package: org.osgi.framework diff --git a/java/com.tractrac.clientmodule/README.txt b/java/com.tractrac.clientmodule/README.txt index 5c4dec1f1d2..2a8e2e3e233 100755 --- a/java/com.tractrac.clientmodule/README.txt +++ b/java/com.tractrac.clientmodule/README.txt @@ -16,6 +16,32 @@ It contains also some files: - test.sh -> script that compiles the code in the src folder, creates the test.jar library and execute the code of the example. - Manifest.txt -> manifest used to create the test.jar file +******************************************** +************* TracAPI 3.3.1 **************** +******************************************** +This is a final version. It fixes bugs in the implementation and it adds some new features. +These features add methods to the API, but they keep the backward compatibility. +This version provides a new JavaDoc version. + +Release date: 17/01/2017 +Build number: 13262 + +1) Features + + - When a race is in live, the library starts to download stored data from the time when the library connects + allowing to the user show data as fast as the first data stream arrives. Then, the library starts to download + the rest of the old data. (Requested by Jakob Odum, 25/09/2016) + - Adding the method IEventFactory.deleteCache that removes all the previous cached objects (Requested by Andre + Borud, 14/12/2016) + - Changing the synchronization of the UtilLocator.createParamerSet method. It was synchronized at method level + but it is only necessary to synchronize the initialization of the factory and not the usage (Requested by Axel Uhl, + 17/01/2017) + +2) Bugs + + - The ICompetitorClass implementation can contain duplicate entries of different implementations. (Reported by + Juan Salvador Pérez, 16/01/2017) + ******************************************** ************* TracAPI 3.3.0 **************** ******************************************** @@ -23,12 +49,12 @@ This is a final version. It fixes bugs in the implementation and it adds some ne These features add methods to the API breaking the backward compatibility. These changes are: Release date: 21/09/2016 -Build number: 12933 +Build number: 12935 1) Features - - Adding the method IRaceCompetitor.updateRaceCompetitor that is thrown when a race - competitor is updated + - Adding the method IRaceCompetitorListener.updateRaceCompetitor that is thrown when a race + competitor is updated (Requested by Jerome Soussens, 21/09/2016) ******************************************** ************* TracAPI 3.2.2 **************** diff --git a/java/com.tractrac.clientmodule/javadoc/allclasses-frame.html b/java/com.tractrac.clientmodule/javadoc/allclasses-frame.html index d853c5570f3..8df91bfc193 100755 --- a/java/com.tractrac.clientmodule/javadoc/allclasses-frame.html +++ b/java/com.tractrac.clientmodule/javadoc/allclasses-frame.html @@ -1,10 +1,10 @@ - + - + All Classes - + diff --git a/java/com.tractrac.clientmodule/javadoc/allclasses-noframe.html b/java/com.tractrac.clientmodule/javadoc/allclasses-noframe.html index 66a43074021..5141cbb0cce 100755 --- a/java/com.tractrac.clientmodule/javadoc/allclasses-noframe.html +++ b/java/com.tractrac.clientmodule/javadoc/allclasses-noframe.html @@ -1,10 +1,10 @@ - + - + All Classes - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/AbstractModelTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/AbstractModelTest.html deleted file mode 100644 index df065359a98..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/AbstractModelTest.html +++ /dev/null @@ -1,777 +0,0 @@ - - - - - -AbstractModelTest - - - - - -

          JavaScript is disabled on your browser.
          - - - - - - -
          -
          com.tractrac.model.lib.api
          -

          Class AbstractModelTest

          -
          -
          -
            -
          • java.lang.Object
          • -
          • -
              -
            • junit.framework.Assert
            • -
            • -
                -
              • junit.framework.TestCase
              • -
              • -
                  -
                • com.tractrac.model.lib.api.AbstractModelTest
                • -
                -
              • -
              -
            • -
            -
          • -
          -
          -
            -
          • -
            -
            All Implemented Interfaces:
            -
            junit.framework.Test
            -
            -
            -
            Direct Known Subclasses:
            -
            AbstractSubscriptionTest
            -
            -
            -
            -
            public abstract class AbstractModelTest
            -extends junit.framework.TestCase
            -

            - Abstract class that can be inherited for the test classes. It has utility - methods to create mock objects that are used by the test classes in order to - create a model of objects for testing -

            -
            Author:
            -
            Jorge Piera Llodrá
            -
            See Also:
            Mock Object
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Summary

              - - - - - - - - -
              Constructors 
              Constructor and Description
              AbstractModelTest() 
              -
            • -
            - - -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                AbstractModelTest

                -
                public AbstractModelTest()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                createEvent

                -
                public IEvent createEvent(java.lang.String name)
                -
                Creates an event from a name. This event contains an empty list of races - and an empty list of controls
                -
                Parameters:
                name - the name of the event
                -
                Returns:
                an event
                -
              • -
              - - - -
                -
              • -

                createEvent

                -
                public IEvent createEvent(java.lang.String name,
                -                 java.util.UUID uuid)
                -
                Creates an event from a name. This event contains an empty list of races - and an empty list of controls.
                -
                Parameters:
                name - the name of the event
                uuid - the uuid of the event
                -
                Returns:
                an event
                -
              • -
              - - - -
                -
              • -

                addAttachment

                -
                public void addAttachment(IAttachable attachable,
                -                 IAttachmentKey attachmentKey,
                -                 java.lang.Object object)
                -
                Add an attachment to an attachable object
                -
                Parameters:
                attachable - the attachable object
                attachmentKey - the attachment key
                object - the object to attach
                -
              • -
              - - - -
                -
              • -

                addRacetoEvent

                -
                public IRace addRacetoEvent(IEvent event,
                -                   java.lang.String name)
                -
                Adds a race to an event. The race will contains an empty list of - competitors.
                -
                Parameters:
                event - the event
                name - the name of the race
                -
                Returns:
                the created race
                -
              • -
              - - - -
                -
              • -

                addRacetoEvent

                -
                public IRace addRacetoEvent(IEvent event,
                -                   java.lang.String name,
                -                   java.util.UUID raceId)
                -
                Adds a race to an event. The race will contains an empty list of - competitors.
                -
                Parameters:
                event - the event
                name - the name of the race
                UUID - the uuid of the race
                -
                Returns:
                the created race
                -
              • -
              - - - -
                -
              • -

                addRacetoEvent

                -
                public IRace addRacetoEvent(IEvent event,
                -                   java.lang.String name,
                -                   java.util.UUID raceId,
                -                   long trackingStartTime,
                -                   long trackingEndTime,
                -                   long raceStartTime)
                -
                Adds a race to an event. The race will contains an empty list of - competitors.
                -
                Parameters:
                event - the event
                name - the name of the race
                UUID - the uuid of the race
                trackingStartTime - the tracking start time
                trackingEndTime - the tracking end time
                raceStartTime - the race start time
                -
                Returns:
                the created race
                -
              • -
              - - - -
                -
              • -

                addCompetitorToRace

                -
                public IRaceCompetitor addCompetitorToRace(IRace race,
                -                                  ICompetitor competitor)
                -
                Add a competitor to a race. The competitor will be attached to the first - route
                -
                Parameters:
                race - the race
                competitor - the competitor to add
                -
                Returns:
                a race competitor
                -
              • -
              - - - -
                -
              • -

                addCompetitorToEvent

                -
                public ICompetitor addCompetitorToEvent(IEvent event,
                -                               java.lang.String competitorName)
                -
                Add the competitor the the event
                -
                Parameters:
                event - the event
                competitorName - the competitor name
                -
                Returns:
                the competitor
                -
              • -
              - - - -
                -
              • -

                addCompetitorToEvent

                -
                public ICompetitor addCompetitorToEvent(IEvent event,
                -                               java.lang.String competitorName,
                -                               java.util.UUID competitorId)
                -
                Add the competitor the the event
                -
                Parameters:
                event - the event
                competitorName - the competitor name
                competitorId - the competitor id
                -
                Returns:
                the competitor
                -
              • -
              - - - -
                -
              • -

                createCompetitor

                -
                public ICompetitor createCompetitor(java.lang.String competitorName)
                -
                Create a competitor
                -
                Parameters:
                name - the competitor name
                -
                Returns:
                the competitor
                -
              • -
              - - - -
                -
              • -

                createCompetitor

                -
                public ICompetitor createCompetitor(java.util.UUID uuid,
                -                           java.lang.String competitorName)
                -
                Create a competitor
                -
                Parameters:
                uuid - the identifier
                name - the competitor name
                -
                Returns:
                the competitor
                -
              • -
              - - - -
                -
              • -

                addRaceCompetitorToRace

                -
                public IRaceCompetitor addRaceCompetitorToRace(IRace race,
                -                                      java.lang.String name)
                -
                Adds a competitor to a race
                -
                Parameters:
                race - the race
                name - the name of the competitor
                -
                Returns:
                the created competitor
                -
              • -
              - - - -
                -
              • -

                addControlToEvent

                -
                public IControl addControlToEvent(IEvent event,
                -                         java.lang.String name)
                -
                Adds a control to an event
                -
                Parameters:
                event - the event
                name - the name of the control
                -
                Returns:
                the created control
                -
              • -
              - - - -
                -
              • -

                addControlPointToControl

                -
                public IControlPoint addControlPointToControl(IControl control,
                -                                     double lat,
                -                                     double lon,
                -                                     long timestamp)
                -
                Adds a controlpoint to a control
                -
                Parameters:
                lat - the latitude value of the control point
                lon - the longitude value of the control point
                timestamp - the timestamp of the control point
                -
                Returns:
                the created control point
                -
              • -
              - - - -
                -
              • -

                addControlToEvent

                -
                public IControl addControlToEvent(IEvent event,
                -                         java.lang.String name,
                -                         double lat,
                -                         double lon,
                -                         long timestamp)
                -
                Adds a control to an event with one control point
                -
                Parameters:
                event - the event
                name - the name of the control
                lat - the latitude value of the first control point
                lon - the longitude value of the first control point
                timestamp - the timestamp of the first control point
                -
                Returns:
                the created control
                -
              • -
              - - - -
                -
              • -

                addControlToEvent

                -
                public IControl addControlToEvent(IEvent event,
                -                         java.lang.String name,
                -                         double lat1,
                -                         double lon1,
                -                         long timestamp1,
                -                         double lat2,
                -                         double lon2,
                -                         long timestamp2)
                -
                Adds a control to an event with two control points
                -
                Parameters:
                event - the event
                name - the name of the control
                lat1 - the latitude value of the first control point
                lon1 - the longitude value of the first control point
                timestamp1 - the timestamp of the second first point
                lat2 - the latitude value of the second control point
                lon2 - the longitude value of the second control point
                timestamp2 - the timestamp of the first second point
                -
                Returns:
                the created control
                -
              • -
              - - - -
                -
              • -

                createControl

                -
                public IControl createControl(java.lang.String name)
                -
                Create a control
                -
                Parameters:
                name - the name of the control
                -
                Returns:
                the control
                -
              • -
              - - - -
                -
              • -

                createControlPoint

                -
                public IControlPoint createControlPoint(double lat,
                -                               double lon,
                -                               long timestamp)
                -
                Create a control point
                -
                Parameters:
                lat - the latitude value of the control point
                lon - the longitude value of the control point
                timestamp - the timestamp of the control point
                -
                Returns:
                the control point
                -
              • -
              - - - -
                -
              • -

                addControlToRace

                -
                public IControl addControlToRace(IRace race,
                -                        java.lang.String name)
                -
                Adds a control to the race. This method also adds the control in the event
                -
                Parameters:
                race - the race
                name - the name of the control
                -
                Returns:
                the created control
                -
              • -
              - - - -
                -
              • -

                addControlToRace

                -
                public IControl addControlToRace(IRace race,
                -                        IControl control)
                -
                Adds a control to the race.
                -
                Parameters:
                race - the race
                control - the control
                -
                Returns:
                the created control
                -
              • -
              - - - -
                -
              • -

                addControlToRace

                -
                public IControl addControlToRace(IRace race,
                -                        java.lang.String name,
                -                        double lat,
                -                        double lon,
                -                        long timestamp)
                -
                Adds a control to the race. This method also adds the control in the event
                -
                Parameters:
                race - the race
                name - the name of the control
                lat - the latitude value of the control
                lon - the longitude value of the control
                timestamp - the timestamp of the control
                -
                Returns:
                the created control
                -
              • -
              - - - -
                -
              • -

                addControlRouteToRace

                -
                public IControlRoute addControlRouteToRace(IRace race)
                -
                Add a route to the race
                -
                Parameters:
                race - the race
                -
                Returns:
                the control route
                -
              • -
              - - - -
                -
              • -

                addControlRouteToRace

                -
                public IControlRoute addControlRouteToRace(IRace race,
                -                                  java.lang.String metadata)
                -
                Add a route to the race
                -
                Parameters:
                race - the race
                metadata - the metadata
                -
                Returns:
                the control route
                -
              • -
              -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/IIdentifiable.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/IIdentifiable.html index 36ddf6a5ed1..9492d492d83 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/IIdentifiable.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/IIdentifiable.html @@ -1,10 +1,10 @@ - + - + IIdentifiable - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/INamed.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/INamed.html index b832cfd6702..6468c81199e 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/INamed.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/INamed.html @@ -1,10 +1,10 @@ - + - + INamed - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/ModelLocator.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/ModelLocator.html index a60a6373f19..ae65c92b7f7 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/ModelLocator.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/ModelLocator.html @@ -1,10 +1,10 @@ - + - + ModelLocator - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/ModelLocatorTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/ModelLocatorTest.html deleted file mode 100644 index 64410d4670a..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/ModelLocatorTest.html +++ /dev/null @@ -1,430 +0,0 @@ - - - - - -ModelLocatorTest - - - - - - - - - - - -
          -
          com.tractrac.model.lib.api
          -

          Class ModelLocatorTest

          -
          -
          -
            -
          • java.lang.Object
          • -
          • -
              -
            • junit.framework.Assert
            • -
            • -
                -
              • junit.framework.TestCase
              • -
              • -
                  -
                • com.tractrac.model.lib.api.ModelLocatorTest
                • -
                -
              • -
              -
            • -
            -
          • -
          -
          -
            -
          • -
            -
            All Implemented Interfaces:
            -
            junit.framework.Test
            -
            -
            -
            -
            public abstract class ModelLocatorTest
            -extends junit.framework.TestCase
            -

            - This test checks if an implementation of the model library implements all the - mandatory objects -

            -
            Author:
            -
            Jorge Piera Llodrá
            -
          • -
          -
          -
          - -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                ModelLocatorTest

                -
                public ModelLocatorTest()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                testRegisteredEventFactory

                -
                public void testRegisteredEventFactory()
                -
                Test if there is a IEventFactory registered.
                -
              • -
              - - - -
                -
              • -

                testEventFactorySingleton

                -
                public void testEventFactorySingleton()
                -
                Test if the IEventFactory is a singleton
                -
              • -
              - - - -
                -
              • -

                testRegisteredPathRouteFactory

                -
                public void testRegisteredPathRouteFactory()
                -
                Test if there is a IPathRouteFactory registered.
                -
              • -
              - - - -
                -
              • -

                testPathRouteFactorySingleton

                -
                public void testPathRouteFactorySingleton()
                -
                Test if the IPathRouteFactory is a singleton
                -
              • -
              - - - -
                -
              • -

                testRegisteredAttachmentManager

                -
                public void testRegisteredAttachmentManager()
                -
                Test if there is a IAttachmentManager registered.
                -
              • -
              - - - -
                -
              • -

                testAttachmentManagerSingleton

                -
                public void testAttachmentManagerSingleton()
                -
                Test if the IAttachmentManager is a singleton
                -
              • -
              - - - -
                -
              • -

                testRegisteredPositionFactory

                -
                public void testRegisteredPositionFactory()
                -
                Test if there is a IPositionFactory registered.
                -
              • -
              - - - -
                -
              • -

                testPositionFactorySingleton

                -
                public void testPositionFactorySingleton()
                -
                Test if the IPositionFactory is a singleton
                -
              • -
              - - - -
                -
              • -

                testRegisteredMetadataFactory

                -
                public void testRegisteredMetadataFactory()
                -
                Test if there is a IMetadataFactory registered.
                -
              • -
              - - - -
                -
              • -

                testMetadataFactorySingleton

                -
                public void testMetadataFactorySingleton()
                -
                Test if the IPositionFactory is a singleton
                -
              • -
              -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/ReadParameterFileTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/ReadParameterFileTest.html deleted file mode 100644 index 6b608620f20..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/ReadParameterFileTest.html +++ /dev/null @@ -1,406 +0,0 @@ - - - - - -ReadParameterFileTest - - - - - - - - - - - -
          -
          com.tractrac.model.lib.api
          -

          Class ReadParameterFileTest

          -
          -
          -
            -
          • java.lang.Object
          • -
          • -
              -
            • com.tractrac.model.lib.api.ReadParameterFileTest
            • -
            -
          • -
          -
          -
            -
          • -
            -
            -
            public abstract class ReadParameterFileTest
            -extends java.lang.Object
            -
            Class that tests reading in a Text parameter file and setting up an event
            -
            Author:
            -
            Jesper Grooss
            -
          • -
          -
          -
          - -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                ReadParameterFileTest

                -
                public ReadParameterFileTest()
                -
              • -
              -
            • -
            - - -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/AbstractAttachable.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/AbstractAttachable.html index 0960b5a3182..38aad0e3192 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/AbstractAttachable.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/AbstractAttachable.html @@ -1,10 +1,10 @@ - + - + AbstractAttachable - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/AttachmentsTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/AttachmentsTest.html deleted file mode 100644 index b90a81c2860..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/AttachmentsTest.html +++ /dev/null @@ -1,316 +0,0 @@ - - - - - -AttachmentsTest - - - - - - - - - - - -
          -
          com.tractrac.model.lib.api.attachment
          -

          Class AttachmentsTest

          -
          -
          -
            -
          • java.lang.Object
          • -
          • -
              -
            • junit.framework.Assert
            • -
            • -
                -
              • junit.framework.TestCase
              • -
              • -
                  -
                • com.tractrac.model.lib.api.attachment.AttachmentsTest
                • -
                -
              • -
              -
            • -
            -
          • -
          -
          -
            -
          • -
            -
            All Implemented Interfaces:
            -
            junit.framework.Test
            -
            -
            -
            -
            public abstract class AttachmentsTest
            -extends junit.framework.TestCase
            -

            - This test checks the attachments. -

            -
            Author:
            -
            Jorge Piera Llodrá
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Summary

              - - - - - - - - -
              Constructors 
              Constructor and Description
              AttachmentsTest() 
              -
            • -
            - -
              -
            • - - -

              Method Summary

              - - - - - - - - - - - - - - - - - - -
              Methods 
              Modifier and TypeMethod and Description
              voidsetUp() 
              voidtestCreateAttachment() 
              voidtestCreateSeveralAttachments() 
              -
                -
              • - - -

                Methods inherited from class junit.framework.TestCase

                -assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertFalse, assertFalse, assertNotNull, assertNotNull, assertNotSame, assertNotSame, assertNull, assertNull, assertSame, assertSame, assertTrue, assertTrue, countTestCases, fail, fail, failNotEquals, failNotSame, failSame, format, getName, run, run, runBare, setName, toString
              • -
              -
                -
              • - - -

                Methods inherited from class java.lang.Object

                -equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
              • -
              -
            • -
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                AttachmentsTest

                -
                public AttachmentsTest()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                setUp

                -
                public void setUp()
                -           throws java.net.URISyntaxException,
                -                  CreateModelException
                -
                -
                Overrides:
                -
                setUp in class junit.framework.TestCase
                -
                Throws:
                -
                java.net.URISyntaxException
                -
                CreateModelException
                -
              • -
              - - - -
                -
              • -

                testCreateAttachment

                -
                public void testCreateAttachment()
                -
              • -
              - - - -
                -
              • -

                testCreateSeveralAttachments

                -
                public void testCreateSeveralAttachments()
                -
              • -
              -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/IAttachable.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/IAttachable.html index c31d903f4a5..1ec28d7b4b4 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/IAttachable.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/IAttachable.html @@ -1,10 +1,10 @@ - + - + IAttachable - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/IAttachmentKey.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/IAttachmentKey.html index 856b46f2907..8ad21a3ab94 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/IAttachmentKey.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/IAttachmentKey.html @@ -1,10 +1,10 @@ - + - + IAttachmentKey - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/IAttachmentManager.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/IAttachmentManager.html index d9e8c1923c7..316b10ffab0 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/IAttachmentManager.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/IAttachmentManager.html @@ -1,10 +1,10 @@ - + - + IAttachmentManager - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/class-use/AbstractAttachable.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/class-use/AbstractAttachable.html deleted file mode 100644 index 12fd20590f4..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/class-use/AbstractAttachable.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.model.lib.api.attachment.AbstractAttachable - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.model.lib.api.attachment.AbstractAttachable

          -
          -
          No usage of com.tractrac.model.lib.api.attachment.AbstractAttachable
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/class-use/AttachmentsTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/class-use/AttachmentsTest.html deleted file mode 100644 index d71cbd4f3d1..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/class-use/AttachmentsTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.model.lib.api.attachment.AttachmentsTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.model.lib.api.attachment.AttachmentsTest

          -
          -
          No usage of com.tractrac.model.lib.api.attachment.AttachmentsTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/class-use/IAttachable.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/class-use/IAttachable.html deleted file mode 100644 index a6f95831bc9..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/class-use/IAttachable.html +++ /dev/null @@ -1,371 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.attachment.IAttachable - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.attachment.IAttachable

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/class-use/IAttachmentKey.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/class-use/IAttachmentKey.html deleted file mode 100644 index 658d90d9f22..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/class-use/IAttachmentKey.html +++ /dev/null @@ -1,209 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.attachment.IAttachmentKey - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.attachment.IAttachmentKey

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/class-use/IAttachmentManager.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/class-use/IAttachmentManager.html deleted file mode 100644 index f4d9a449eef..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/class-use/IAttachmentManager.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.attachment.IAttachmentManager - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.attachment.IAttachmentManager

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/package-frame.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/package-frame.html index 5763f1fc700..1e18edcce7e 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/package-frame.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/package-frame.html @@ -1,10 +1,10 @@ - + - + com.tractrac.model.lib.api.attachment - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/package-summary.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/package-summary.html index b9860985805..f0daf6f5027 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/package-summary.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/package-summary.html @@ -1,10 +1,10 @@ - + - + com.tractrac.model.lib.api.attachment - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/package-tree.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/package-tree.html index fd99391795f..f34990ed89e 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/package-tree.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/package-tree.html @@ -1,10 +1,10 @@ - + - + com.tractrac.model.lib.api.attachment Class Hierarchy - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/package-use.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/package-use.html deleted file mode 100644 index bf8a5a69be6..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/attachment/package-use.html +++ /dev/null @@ -1,266 +0,0 @@ - - - - - -Uses of Package com.tractrac.model.lib.api.attachment - - - - - - - - - - -
          -

          Uses of Package
          com.tractrac.model.lib.api.attachment

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/class-use/AbstractModelTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/class-use/AbstractModelTest.html deleted file mode 100644 index 939153d59b7..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/class-use/AbstractModelTest.html +++ /dev/null @@ -1,260 +0,0 @@ - - - - - -Uses of Class com.tractrac.model.lib.api.AbstractModelTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.model.lib.api.AbstractModelTest

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/class-use/IIdentifiable.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/class-use/IIdentifiable.html deleted file mode 100644 index 1890a43bcea..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/class-use/IIdentifiable.html +++ /dev/null @@ -1,279 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.IIdentifiable - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.IIdentifiable

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/class-use/INamed.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/class-use/INamed.html deleted file mode 100644 index 69669d431db..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/class-use/INamed.html +++ /dev/null @@ -1,252 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.INamed - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.INamed

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/class-use/ModelLocator.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/class-use/ModelLocator.html deleted file mode 100644 index 82c755681f6..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/class-use/ModelLocator.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.model.lib.api.ModelLocator - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.model.lib.api.ModelLocator

          -
          -
          No usage of com.tractrac.model.lib.api.ModelLocator
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/class-use/ModelLocatorTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/class-use/ModelLocatorTest.html deleted file mode 100644 index 778d0b53d51..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/class-use/ModelLocatorTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.model.lib.api.ModelLocatorTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.model.lib.api.ModelLocatorTest

          -
          -
          No usage of com.tractrac.model.lib.api.ModelLocatorTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/class-use/ReadParameterFileTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/class-use/ReadParameterFileTest.html deleted file mode 100644 index 4050ea6b25f..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/class-use/ReadParameterFileTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.model.lib.api.ReadParameterFileTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.model.lib.api.ReadParameterFileTest

          -
          -
          No usage of com.tractrac.model.lib.api.ReadParameterFileTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IControlPassing.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IControlPassing.html index 080e979c5c7..8545c8b9b44 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IControlPassing.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IControlPassing.html @@ -1,10 +1,10 @@ - + - + IControlPassing - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IControlPassings.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IControlPassings.html index 46813ddf38c..18ffb2870fa 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IControlPassings.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IControlPassings.html @@ -1,10 +1,10 @@ - + - + IControlPassings - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IMessageData.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IMessageData.html index 2e2e80a1b06..f018c64e374 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IMessageData.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IMessageData.html @@ -1,10 +1,10 @@ - + - + IMessageData - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IPosition.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IPosition.html index 2aca18426b9..7900eecc0c1 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IPosition.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IPosition.html @@ -1,10 +1,10 @@ - + - + IPosition - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IPositionFactory.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IPositionFactory.html index 72aefcecf3c..71e893e4ed1 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IPositionFactory.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IPositionFactory.html @@ -1,10 +1,10 @@ - + - + IPositionFactory - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IPositionOffset.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IPositionOffset.html index 869bc4a2c09..672add36389 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IPositionOffset.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IPositionOffset.html @@ -1,10 +1,10 @@ - + - + IPositionOffset - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IPositionSnapped.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IPositionSnapped.html index f9da21ab974..b84155cfeb4 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IPositionSnapped.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IPositionSnapped.html @@ -1,10 +1,10 @@ - + - + IPositionSnapped - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IStartStopData.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IStartStopData.html index ab5d41d2520..f4fbf377e26 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IStartStopData.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/IStartStopData.html @@ -1,10 +1,10 @@ - + - + IStartStopData - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/ITimeData.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/ITimeData.html index ea0baf78535..11e80755e12 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/ITimeData.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/ITimeData.html @@ -1,10 +1,10 @@ - + - + ITimeData - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IControlPassing.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IControlPassing.html deleted file mode 100644 index 20e8def2a9b..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IControlPassing.html +++ /dev/null @@ -1,161 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.data.IControlPassing - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.data.IControlPassing

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IControlPassings.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IControlPassings.html deleted file mode 100644 index 5a972d34f13..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IControlPassings.html +++ /dev/null @@ -1,161 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.data.IControlPassings - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.data.IControlPassings

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IMessageData.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IMessageData.html deleted file mode 100644 index 9d0cd8ef12c..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IMessageData.html +++ /dev/null @@ -1,204 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.data.IMessageData - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.data.IMessageData

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IPosition.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IPosition.html deleted file mode 100644 index 40bac6fc98b..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IPosition.html +++ /dev/null @@ -1,312 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.data.IPosition - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.data.IPosition

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IPositionFactory.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IPositionFactory.html deleted file mode 100644 index eebdd0df54f..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IPositionFactory.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.data.IPositionFactory - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.data.IPositionFactory

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IPositionOffset.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IPositionOffset.html deleted file mode 100644 index 2e64e56d257..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IPositionOffset.html +++ /dev/null @@ -1,188 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.data.IPositionOffset - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.data.IPositionOffset

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IPositionSnapped.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IPositionSnapped.html deleted file mode 100644 index 65bfa51ac9f..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IPositionSnapped.html +++ /dev/null @@ -1,161 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.data.IPositionSnapped - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.data.IPositionSnapped

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IStartStopData.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IStartStopData.html deleted file mode 100644 index 4faba160b34..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/IStartStopData.html +++ /dev/null @@ -1,174 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.data.IStartStopData - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.data.IStartStopData

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/ITimeData.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/ITimeData.html deleted file mode 100644 index ad50af36d03..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/class-use/ITimeData.html +++ /dev/null @@ -1,190 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.data.ITimeData - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.data.ITimeData

          -
          -
          -
            -
          • - - - - - - - - - - - - -
            Packages that use ITimeData 
            PackageDescription
            com.tractrac.model.lib.api.data -
            - Contains positions and control passings
            -
            -
          • -
          • -
              -
            • - - -

              Uses of ITimeData in com.tractrac.model.lib.api.data

              - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
              Subinterfaces of ITimeData in com.tractrac.model.lib.api.data 
              Modifier and TypeInterface and Description
              interface IControlPassing -
              A control passing is a combination of a time and a control, usually the time where - a competitor passed a control of an control route.
              -
              interface IMessageData -
              - A generic message object that can transfer messages either as text or as - binary large object (blob).
              -
              interface IPosition -
              - A geographical position in latitude-longitude coordinates, recorded at a - certain time.
              -
              interface IPositionOffset -
              A position along the route in the form of an offset, recorded at a certain - time.
              -
              interface IPositionSnapped -
              A snapped position is a position along a line or a route.
              -
              -
            • -
            -
          • -
          -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/package-frame.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/package-frame.html index e45a053a981..eb6a26e0012 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/package-frame.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/package-frame.html @@ -1,10 +1,10 @@ - + - + com.tractrac.model.lib.api.data - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/package-summary.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/package-summary.html index a228a4e8884..e1840cb3bcc 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/package-summary.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/package-summary.html @@ -1,10 +1,10 @@ - + - + com.tractrac.model.lib.api.data - + @@ -72,8 +72,7 @@

          Package com.tractrac.model.lib.api.data

          - Contains positions and control passings -
          + Contains positions and control passings

          See: Description

          diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/package-tree.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/package-tree.html index af754771797..e4bd9d9035f 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/package-tree.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/package-tree.html @@ -1,10 +1,10 @@ - + - + com.tractrac.model.lib.api.data Class Hierarchy - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/package-use.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/package-use.html deleted file mode 100644 index 01524c8412e..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/data/package-use.html +++ /dev/null @@ -1,348 +0,0 @@ - - - - - -Uses of Package com.tractrac.model.lib.api.data - - - - - - - - - - -
          -

          Uses of Package
          com.tractrac.model.lib.api.data

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/AbstractJSONTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/AbstractJSONTest.html deleted file mode 100644 index d69a4fb448f..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/AbstractJSONTest.html +++ /dev/null @@ -1,291 +0,0 @@ - - - - - -AbstractJSONTest - - - - - - - - - - - -
          -
          com.tractrac.model.lib.api.event
          -

          Class AbstractJSONTest

          -
          -
          -
            -
          • java.lang.Object
          • -
          • -
              -
            • junit.framework.Assert
            • -
            • -
                -
              • junit.framework.TestCase
              • -
              • -
                  -
                • com.tractrac.model.lib.api.event.AbstractJSONTest
                • -
                -
              • -
              -
            • -
            -
          • -
          -
          - -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Summary

              - - - - - - - - -
              Constructors 
              Constructor and Description
              AbstractJSONTest() 
              -
            • -
            - -
              -
            • - - -

              Method Summary

              - - - - - - - - - - -
              Methods 
              Modifier and TypeMethod and Description
              voidsetUp() 
              -
                -
              • - - -

                Methods inherited from class junit.framework.TestCase

                -assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertFalse, assertFalse, assertNotNull, assertNotNull, assertNotSame, assertNotSame, assertNull, assertNull, assertSame, assertSame, assertTrue, assertTrue, countTestCases, fail, fail, failNotEquals, failNotSame, failSame, format, getName, run, run, runBare, setName, toString
              • -
              -
                -
              • - - -

                Methods inherited from class java.lang.Object

                -equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
              • -
              -
            • -
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                AbstractJSONTest

                -
                public AbstractJSONTest()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                setUp

                -
                public void setUp()
                -           throws java.io.IOException,
                -                  java.net.URISyntaxException
                -
                -
                Overrides:
                -
                setUp in class junit.framework.TestCase
                -
                Throws:
                -
                java.io.IOException
                -
                java.net.URISyntaxException
                -
              • -
              -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/CreateModelException.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/CreateModelException.html index cb62a7cff43..018b2a199b9 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/CreateModelException.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/CreateModelException.html @@ -1,10 +1,10 @@ - + - + CreateModelException - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/EventType.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/EventType.html index d431aeca51e..148f6e9baaf 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/EventType.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/EventType.html @@ -1,10 +1,10 @@ - + - + EventType - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/EventsFromClubsJSONTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/EventsFromClubsJSONTest.html deleted file mode 100644 index f0de1bf3c5e..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/EventsFromClubsJSONTest.html +++ /dev/null @@ -1,296 +0,0 @@ - - - - - -EventsFromClubsJSONTest - - - - - - - - - - - -
          -
          com.tractrac.model.lib.api.event
          -

          Class EventsFromClubsJSONTest

          -
          -
          - -
          - -
          -
          -
            -
          • - - - -
              -
            • - - -

              Method Summary

              - - - - - - - - - - -
              Methods 
              Modifier and TypeMethod and Description
              voidtestParseEventsFile() 
              - -
                -
              • - - -

                Methods inherited from class junit.framework.TestCase

                -assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertFalse, assertFalse, assertNotNull, assertNotNull, assertNotSame, assertNotSame, assertNull, assertNull, assertSame, assertSame, assertTrue, assertTrue, countTestCases, fail, fail, failNotEquals, failNotSame, failSame, format, getName, run, run, runBare, setName, toString
              • -
              -
                -
              • - - -

                Methods inherited from class java.lang.Object

                -equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
              • -
              -
            • -
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                EventsFromClubsJSONTest

                -
                public EventsFromClubsJSONTest()
                -
              • -
              -
            • -
            - - -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/EventsFromJSONTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/EventsFromJSONTest.html deleted file mode 100644 index c08f85f269b..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/EventsFromJSONTest.html +++ /dev/null @@ -1,296 +0,0 @@ - - - - - -EventsFromJSONTest - - - - - - - - - - - -
          -
          com.tractrac.model.lib.api.event
          -

          Class EventsFromJSONTest

          -
          -
          - -
          - -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Summary

              - - - - - - - - -
              Constructors 
              Constructor and Description
              EventsFromJSONTest() 
              -
            • -
            - -
              -
            • - - -

              Method Summary

              - - - - - - - - - - -
              Methods 
              Modifier and TypeMethod and Description
              voidtestParseEventsFile() 
              - -
                -
              • - - -

                Methods inherited from class junit.framework.TestCase

                -assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertFalse, assertFalse, assertNotNull, assertNotNull, assertNotSame, assertNotSame, assertNull, assertNull, assertSame, assertSame, assertTrue, assertTrue, countTestCases, fail, fail, failNotEquals, failNotSame, failSame, format, getName, run, run, runBare, setName, toString
              • -
              -
                -
              • - - -

                Methods inherited from class java.lang.Object

                -equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
              • -
              -
            • -
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                EventsFromJSONTest

                -
                public EventsFromJSONTest()
                -
              • -
              -
            • -
            - - -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/ICompetitor.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/ICompetitor.html index 9f59efab2ef..ed50af16641 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/ICompetitor.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/ICompetitor.html @@ -1,10 +1,10 @@ - + - + ICompetitor - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/ICompetitorClass.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/ICompetitorClass.html index 097070eb8c7..16df0eb6e49 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/ICompetitorClass.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/ICompetitorClass.html @@ -1,10 +1,10 @@ - + - + ICompetitorClass - + @@ -144,10 +144,7 @@ extends java.util.List<ICompetitor> getCompetitors()
          - Get a list of competitors in the class - - - This method is thread-safety: it returns a copy of the list.
          + Get a list of competitors in the class diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/IEvent.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/IEvent.html index 2d68ffce5e0..e625fcae100 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/IEvent.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/IEvent.html @@ -1,10 +1,10 @@ - + - + IEvent - + @@ -242,10 +242,7 @@ extends java.util.List<IRaceSerie> getRaceSeries()
          - Gets the list of race series of this event - - - This method is thread-safety: it returns a copy of the list.
          + Gets the list of race series of this event diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/IEventFactory.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/IEventFactory.html index c5cb6494d91..73057c2537a 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/IEventFactory.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/IEventFactory.html @@ -1,10 +1,10 @@ - + - + IEventFactory - + @@ -18,7 +18,7 @@ catch(err) { } //--> -var methods = {"i0":6,"i1":6,"i2":6,"i3":6,"i4":6,"i5":6,"i6":6}; +var methods = {"i0":6,"i1":6,"i2":6,"i3":6,"i4":6,"i5":6,"i6":6,"i7":6}; var tabs = {65535:["t0","All Methods"],2:["t2","Instance Methods"],4:["t3","Abstract Methods"]}; var altColor = "altColor"; var rowColor = "rowColor"; @@ -179,6 +179,12 @@ extends com.tractrac.common.lib.api.service.IServiceProvider
          Creates an IRace from a parameters file
          + +void +deleteCache() +
          Deletes all the cached objects that has been created
          + + @@ -315,7 +321,7 @@ extends com.tractrac.common.lib.api.service.IServiceProvider -
            +
            • createRace

              IRace createRace(com.tractrac.util.lib.api.programparameters.IParameterSet parameterSet,
              @@ -335,6 +341,16 @@ extends com.tractrac.common.lib.api.service.IServiceProvider
            + + + +
              +
            • +

              deleteCache

              +
              void deleteCache()
              +
              Deletes all the cached objects that has been created
              +
            • +
          diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/IRace.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/IRace.html index 2e7fb41583e..3b4255a0047 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/IRace.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/IRace.html @@ -1,10 +1,10 @@ - + - + IRace - + @@ -225,10 +225,7 @@ extends java.util.List<IRoute> getRoutes()
          - Gets the routes of this race - - - This method is thread-safety: it returns a copy of the list.
          + Gets the routes of this race @@ -246,7 +243,17 @@ extends + START_PHASE: Race start likely at the given race start time. + RUNNING: Post 10 minute gun (Start time therefore confirmed) + FINISHING: racing + FINISHED: race completed + POSTPONED: race postponed, restart unknown + CAN_RESTART: Race abandoned, a restart is possible. + ABANDONED: Race abandoned, no restart today. + NO_RACE: No racing today + CANCELLED: No racing today. + NO_DATA: Due to technical problems, no data available for this race. + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/IRaceCompetitor.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/IRaceCompetitor.html index 0cdb1012177..fddbffab4bb 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/IRaceCompetitor.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/IRaceCompetitor.html @@ -1,10 +1,10 @@ - + - + IRaceCompetitor - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/IRaceSerie.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/IRaceSerie.html index 3deea8d467e..9d97f67f453 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/IRaceSerie.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/IRaceSerie.html @@ -1,10 +1,10 @@ - + - + IRaceSerie - + @@ -131,10 +131,7 @@ extends java.util.List<IRace> getRaces()
          - Gets the list of races that compose the serie - - - This method is thread-safety: it returns a copy of the list.
          + Gets the list of races that compose the serie diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/ITeam.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/ITeam.html index 7073b362b5b..3a38b58f20e 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/ITeam.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/ITeam.html @@ -1,10 +1,10 @@ - + - + ITeam - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RaceCompetitorStatusType.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RaceCompetitorStatusType.html index e906c147cae..3a760eef207 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RaceCompetitorStatusType.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RaceCompetitorStatusType.html @@ -1,10 +1,10 @@ - + - + RaceCompetitorStatusType - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RaceCompetitorTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RaceCompetitorTest.html deleted file mode 100644 index 7444dd2adad..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RaceCompetitorTest.html +++ /dev/null @@ -1,302 +0,0 @@ - - - - - -RaceCompetitorTest - - - - - - - - - - - -
          -
          com.tractrac.model.lib.api.event
          -

          Class RaceCompetitorTest

          -
          -
          -
            -
          • java.lang.Object
          • -
          • -
              -
            • junit.framework.Assert
            • -
            • -
                -
              • junit.framework.TestCase
              • -
              • -
                  -
                • com.tractrac.model.lib.api.event.RaceCompetitorTest
                • -
                -
              • -
              -
            • -
            -
          • -
          -
          -
            -
          • -
            -
            All Implemented Interfaces:
            -
            junit.framework.Test
            -
            -
            -
            -
            public abstract class RaceCompetitorTest
            -extends junit.framework.TestCase
            -
            Author:
            -
            Jorge Piera Llodrá
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Summary

              - - - - - - - - -
              Constructors 
              Constructor and Description
              RaceCompetitorTest() 
              -
            • -
            - -
              -
            • - - -

              Method Summary

              - - - - - - - - - - - - - - -
              Methods 
              Modifier and TypeMethod and Description
              voidtestMetadata() 
              voidtestRouteNotNull() 
              -
                -
              • - - -

                Methods inherited from class junit.framework.TestCase

                -assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertFalse, assertFalse, assertNotNull, assertNotNull, assertNotSame, assertNotSame, assertNull, assertNull, assertSame, assertSame, assertTrue, assertTrue, countTestCases, fail, fail, failNotEquals, failNotSame, failSame, format, getName, run, run, runBare, setName, toString
              • -
              -
                -
              • - - -

                Methods inherited from class java.lang.Object

                -equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
              • -
              -
            • -
            -
          • -
          -
          -
          - -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RaceLoadingException.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RaceLoadingException.html index 4dff0bbc50e..2c0f3bcc518 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RaceLoadingException.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RaceLoadingException.html @@ -1,10 +1,10 @@ - + - + RaceLoadingException - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RaceSerieTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RaceSerieTest.html deleted file mode 100644 index 1162c09b27e..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RaceSerieTest.html +++ /dev/null @@ -1,284 +0,0 @@ - - - - - -RaceSerieTest - - - - - - - - - - - -
          -
          com.tractrac.model.lib.api.event
          -

          Class RaceSerieTest

          -
          -
          -
            -
          • java.lang.Object
          • -
          • -
              -
            • junit.framework.Assert
            • -
            • -
                -
              • junit.framework.TestCase
              • -
              • -
                  -
                • com.tractrac.model.lib.api.event.RaceSerieTest
                • -
                -
              • -
              -
            • -
            -
          • -
          -
          -
            -
          • -
            -
            All Implemented Interfaces:
            -
            junit.framework.Test
            -
            -
            -
            -
            public abstract class RaceSerieTest
            -extends junit.framework.TestCase
            -
            Author:
            -
            Jorge Piera Llodrá
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Summary

              - - - - - - - - -
              Constructors 
              Constructor and Description
              RaceSerieTest() 
              -
            • -
            - -
              -
            • - - -

              Method Summary

              - - - - - - - - - - -
              Methods 
              Modifier and TypeMethod and Description
              voidtestReadRaceSerie() 
              -
                -
              • - - -

                Methods inherited from class junit.framework.TestCase

                -assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertFalse, assertFalse, assertNotNull, assertNotNull, assertNotSame, assertNotSame, assertNull, assertNull, assertSame, assertSame, assertTrue, assertTrue, countTestCases, fail, fail, failNotEquals, failNotSame, failSame, format, getName, run, run, runBare, setName, toString
              • -
              -
                -
              • - - -

                Methods inherited from class java.lang.Object

                -equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
              • -
              -
            • -
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                RaceSerieTest

                -
                public RaceSerieTest()
                -
              • -
              -
            • -
            - - -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RaceStatusType.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RaceStatusType.html index 1b5a9141763..e352d88b66b 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RaceStatusType.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RaceStatusType.html @@ -1,10 +1,10 @@ - + - + RaceStatusType - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RaceVisibilityType.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RaceVisibilityType.html index c6a263652b9..2e041b9dcfa 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RaceVisibilityType.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RaceVisibilityType.html @@ -1,10 +1,10 @@ - + - + RaceVisibilityType - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RacesFromJSONTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RacesFromJSONTest.html deleted file mode 100644 index c5ec2e1b938..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/RacesFromJSONTest.html +++ /dev/null @@ -1,300 +0,0 @@ - - - - - -RacesFromJSONTest - - - - - - - - - - - -
          -
          com.tractrac.model.lib.api.event
          -

          Class RacesFromJSONTest

          -
          -
          - -
          - -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Summary

              - - - - - - - - -
              Constructors 
              Constructor and Description
              RacesFromJSONTest() 
              -
            • -
            - -
              -
            • - - -

              Method Summary

              - - - - - - - - - - -
              Methods 
              Modifier and TypeMethod and Description
              voidtestLoadRace() 
              - -
                -
              • - - -

                Methods inherited from class junit.framework.TestCase

                -assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertFalse, assertFalse, assertNotNull, assertNotNull, assertNotSame, assertNotSame, assertNull, assertNull, assertSame, assertSame, assertTrue, assertTrue, countTestCases, fail, fail, failNotEquals, failNotSame, failSame, format, getName, run, run, runBare, setName, toString
              • -
              -
                -
              • - - -

                Methods inherited from class java.lang.Object

                -equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
              • -
              -
            • -
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                RacesFromJSONTest

                -
                public RacesFromJSONTest()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                testLoadRace

                -
                public void testLoadRace()
                -                  throws java.net.URISyntaxException,
                -                         CreateModelException,
                -                         java.net.MalformedURLException,
                -                         java.text.ParseException
                -
                Throws:
                -
                java.net.URISyntaxException
                -
                CreateModelException
                -
                java.net.MalformedURLException
                -
                java.text.ParseException
                -
              • -
              -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/SameEventTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/SameEventTest.html deleted file mode 100644 index 8f086f17607..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/SameEventTest.html +++ /dev/null @@ -1,294 +0,0 @@ - - - - - -SameEventTest - - - - - - - - - - - -
          -
          com.tractrac.model.lib.api.event
          -

          Class SameEventTest

          -
          -
          - -
          - -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Summary

              - - - - - - - - -
              Constructors 
              Constructor and Description
              SameEventTest() 
              -
            • -
            - -
              -
            • - - -

              Method Summary

              - - - - - - - - - - -
              Methods 
              Modifier and TypeMethod and Description
              voidtestSameEvent() 
              - -
                -
              • - - -

                Methods inherited from class junit.framework.TestCase

                -assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertFalse, assertFalse, assertNotNull, assertNotNull, assertNotSame, assertNotSame, assertNull, assertNull, assertSame, assertSame, assertTrue, assertTrue, countTestCases, fail, fail, failNotEquals, failNotSame, failSame, format, getName, run, run, runBare, setName, toString
              • -
              -
                -
              • - - -

                Methods inherited from class java.lang.Object

                -equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
              • -
              -
            • -
            -
          • -
          -
          -
          - -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/StartTimeType.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/StartTimeType.html index a09b365ba13..52fbdf78617 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/StartTimeType.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/StartTimeType.html @@ -1,10 +1,10 @@ - + - + StartTimeType - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/StartTimeTypeTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/StartTimeTypeTest.html deleted file mode 100644 index d1c77b0a186..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/StartTimeTypeTest.html +++ /dev/null @@ -1,524 +0,0 @@ - - - - - -StartTimeTypeTest - - - - - - - - - - - -
          -
          com.tractrac.model.lib.api.event
          -

          Class StartTimeTypeTest

          -
          -
          -
            -
          • java.lang.Object
          • -
          • -
              -
            • junit.framework.Assert
            • -
            • -
                -
              • junit.framework.TestCase
              • -
              • -
                  -
                • com.tractrac.model.lib.api.event.StartTimeTypeTest
                • -
                -
              • -
              -
            • -
            -
          • -
          -
          -
            -
          • -
            -
            All Implemented Interfaces:
            -
            junit.framework.Test
            -
            -
            -
            -
            public abstract class StartTimeTypeTest
            -extends junit.framework.TestCase
            -
            Class used to test the StartTimeType enumerate
            -
            Author:
            -
            Jorge Piera Llodrá
            -
          • -
          -
          -
          - -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                StartTimeTypeTest

                -
                public StartTimeTypeTest()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                setUp

                -
                public void setUp()
                -
                -
                Overrides:
                -
                setUp in class junit.framework.TestCase
                -
                -
              • -
              - - - -
                -
              • -

                testStartTypeNullAndStartTimeTypeNull

                -
                public void testStartTypeNullAndStartTimeTypeNull()
                -                                           throws java.net.URISyntaxException,
                -                                                  CreateModelException,
                -                                                  java.io.IOException
                -
                Throws:
                -
                java.net.URISyntaxException
                -
                CreateModelException
                -
                java.io.IOException
                -
              • -
              - - - -
                -
              • -

                testStartTypeMassStartAndStartTimeTypeNull

                -
                public void testStartTypeMassStartAndStartTimeTypeNull()
                -                                                throws java.net.URISyntaxException,
                -                                                       CreateModelException,
                -                                                       java.io.IOException
                -
                Throws:
                -
                java.net.URISyntaxException
                -
                CreateModelException
                -
                java.io.IOException
                -
              • -
              - - - -
                -
              • -

                testStartTypeIdividualAndStartTimeTypeNull

                -
                public void testStartTypeIdividualAndStartTimeTypeNull()
                -                                                throws java.net.URISyntaxException,
                -                                                       CreateModelException,
                -                                                       java.io.IOException
                -
                Throws:
                -
                java.net.URISyntaxException
                -
                CreateModelException
                -
                java.io.IOException
                -
              • -
              - - - -
                -
              • -

                testStartTypeNullAndStartTimeTypeIndividual

                -
                public void testStartTypeNullAndStartTimeTypeIndividual()
                -                                                 throws java.net.URISyntaxException,
                -                                                        CreateModelException,
                -                                                        java.io.IOException
                -
                Throws:
                -
                java.net.URISyntaxException
                -
                CreateModelException
                -
                java.io.IOException
                -
              • -
              - - - -
                -
              • -

                testStartTypeNullAndStartTimeTypeFirstMark

                -
                public void testStartTypeNullAndStartTimeTypeFirstMark()
                -                                                throws java.net.URISyntaxException,
                -                                                       CreateModelException,
                -                                                       java.io.IOException
                -
                Throws:
                -
                java.net.URISyntaxException
                -
                CreateModelException
                -
                java.io.IOException
                -
              • -
              - - - -
                -
              • -

                testStartTypeNullAndStartTimeTypeRace

                -
                public void testStartTypeNullAndStartTimeTypeRace()
                -                                           throws java.net.URISyntaxException,
                -                                                  CreateModelException,
                -                                                  java.io.IOException
                -
                Throws:
                -
                java.net.URISyntaxException
                -
                CreateModelException
                -
                java.io.IOException
                -
              • -
              - - - -
                -
              • -

                testStartTypeMassStartAndStartTimeTypeIndividual

                -
                public void testStartTypeMassStartAndStartTimeTypeIndividual()
                -                                                      throws java.net.URISyntaxException,
                -                                                             CreateModelException,
                -                                                             java.io.IOException
                -
                Throws:
                -
                java.net.URISyntaxException
                -
                CreateModelException
                -
                java.io.IOException
                -
              • -
              - - - -
                -
              • -

                testStartTypeMassStartAndStartTimeTypeFirstMark

                -
                public void testStartTypeMassStartAndStartTimeTypeFirstMark()
                -                                                     throws java.net.URISyntaxException,
                -                                                            CreateModelException,
                -                                                            java.io.IOException
                -
                Throws:
                -
                java.net.URISyntaxException
                -
                CreateModelException
                -
                java.io.IOException
                -
              • -
              - - - -
                -
              • -

                testStartTypeMassStartAndStartTimeTypeRace

                -
                public void testStartTypeMassStartAndStartTimeTypeRace()
                -                                                throws java.net.URISyntaxException,
                -                                                       CreateModelException,
                -                                                       java.io.IOException
                -
                Throws:
                -
                java.net.URISyntaxException
                -
                CreateModelException
                -
                java.io.IOException
                -
              • -
              - - - -
                -
              • -

                testStartTypeIndividualAndStartTimeTypeIndividual

                -
                public void testStartTypeIndividualAndStartTimeTypeIndividual()
                -                                                       throws java.net.URISyntaxException,
                -                                                              CreateModelException,
                -                                                              java.io.IOException
                -
                Throws:
                -
                java.net.URISyntaxException
                -
                CreateModelException
                -
                java.io.IOException
                -
              • -
              - - - -
                -
              • -

                testStartTypeIndividualAndStartTimeTypeFirstMark

                -
                public void testStartTypeIndividualAndStartTimeTypeFirstMark()
                -                                                      throws java.net.URISyntaxException,
                -                                                             CreateModelException,
                -                                                             java.io.IOException
                -
                Throws:
                -
                java.net.URISyntaxException
                -
                CreateModelException
                -
                java.io.IOException
                -
              • -
              - - - -
                -
              • -

                testStartTypeIndividualAndStartTimeTypeRace

                -
                public void testStartTypeIndividualAndStartTimeTypeRace()
                -                                                 throws java.net.URISyntaxException,
                -                                                        CreateModelException,
                -                                                        java.io.IOException
                -
                Throws:
                -
                java.net.URISyntaxException
                -
                CreateModelException
                -
                java.io.IOException
                -
              • -
              -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/AbstractJSONTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/AbstractJSONTest.html deleted file mode 100644 index df6bbc2f751..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/AbstractJSONTest.html +++ /dev/null @@ -1,170 +0,0 @@ - - - - - -Uses of Class com.tractrac.model.lib.api.event.AbstractJSONTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.model.lib.api.event.AbstractJSONTest

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/CreateModelException.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/CreateModelException.html deleted file mode 100644 index d03bffa20d3..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/CreateModelException.html +++ /dev/null @@ -1,206 +0,0 @@ - - - - - -Uses of Class com.tractrac.model.lib.api.event.CreateModelException - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.model.lib.api.event.CreateModelException

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/EventType.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/EventType.html deleted file mode 100644 index d102cdab568..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/EventType.html +++ /dev/null @@ -1,185 +0,0 @@ - - - - - -Uses of Class com.tractrac.model.lib.api.event.EventType - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.model.lib.api.event.EventType

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/EventsFromClubsJSONTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/EventsFromClubsJSONTest.html deleted file mode 100644 index a74b832d869..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/EventsFromClubsJSONTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.model.lib.api.event.EventsFromClubsJSONTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.model.lib.api.event.EventsFromClubsJSONTest

          -
          -
          No usage of com.tractrac.model.lib.api.event.EventsFromClubsJSONTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/EventsFromJSONTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/EventsFromJSONTest.html deleted file mode 100644 index bc57f9e3ef0..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/EventsFromJSONTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.model.lib.api.event.EventsFromJSONTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.model.lib.api.event.EventsFromJSONTest

          -
          -
          No usage of com.tractrac.model.lib.api.event.EventsFromJSONTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/ICompetitor.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/ICompetitor.html deleted file mode 100644 index 667ea200314..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/ICompetitor.html +++ /dev/null @@ -1,249 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.event.ICompetitor - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.event.ICompetitor

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/ICompetitorClass.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/ICompetitorClass.html deleted file mode 100644 index ed3720b3874..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/ICompetitorClass.html +++ /dev/null @@ -1,176 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.event.ICompetitorClass - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.event.ICompetitorClass

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/IEvent.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/IEvent.html deleted file mode 100644 index 5e5c8fe5553..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/IEvent.html +++ /dev/null @@ -1,252 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.event.IEvent - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.event.IEvent

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/IEventFactory.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/IEventFactory.html deleted file mode 100644 index b33d403a9bc..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/IEventFactory.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.event.IEventFactory - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.event.IEventFactory

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/IRace.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/IRace.html deleted file mode 100644 index 893f542bc94..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/IRace.html +++ /dev/null @@ -1,335 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.event.IRace - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.event.IRace

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/IRaceCompetitor.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/IRaceCompetitor.html deleted file mode 100644 index 6a39ff960c1..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/IRaceCompetitor.html +++ /dev/null @@ -1,288 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.event.IRaceCompetitor - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.event.IRaceCompetitor

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/IRaceSerie.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/IRaceSerie.html deleted file mode 100644 index 446e911c7ec..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/IRaceSerie.html +++ /dev/null @@ -1,176 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.event.IRaceSerie - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.event.IRaceSerie

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/ITeam.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/ITeam.html deleted file mode 100644 index d3a2ac6da6e..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/ITeam.html +++ /dev/null @@ -1,176 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.event.ITeam - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.event.ITeam

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/RaceCompetitorTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/RaceCompetitorTest.html deleted file mode 100644 index 78d8f864d67..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/RaceCompetitorTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.model.lib.api.event.RaceCompetitorTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.model.lib.api.event.RaceCompetitorTest

          -
          -
          No usage of com.tractrac.model.lib.api.event.RaceCompetitorTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/RaceLoadingException.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/RaceLoadingException.html deleted file mode 100644 index 75d271a4466..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/RaceLoadingException.html +++ /dev/null @@ -1,161 +0,0 @@ - - - - - -Uses of Class com.tractrac.model.lib.api.event.RaceLoadingException - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.model.lib.api.event.RaceLoadingException

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/RaceSerieTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/RaceSerieTest.html deleted file mode 100644 index b54611a4ab3..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/RaceSerieTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.model.lib.api.event.RaceSerieTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.model.lib.api.event.RaceSerieTest

          -
          -
          No usage of com.tractrac.model.lib.api.event.RaceSerieTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/RaceStatusType.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/RaceStatusType.html deleted file mode 100644 index 8693b5dccd1..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/RaceStatusType.html +++ /dev/null @@ -1,191 +0,0 @@ - - - - - -Uses of Class com.tractrac.model.lib.api.event.RaceStatusType - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.model.lib.api.event.RaceStatusType

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/RacesFromJSONTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/RacesFromJSONTest.html deleted file mode 100644 index 15d50868dd2..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/RacesFromJSONTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.model.lib.api.event.RacesFromJSONTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.model.lib.api.event.RacesFromJSONTest

          -
          -
          No usage of com.tractrac.model.lib.api.event.RacesFromJSONTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/SameEventTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/SameEventTest.html deleted file mode 100644 index 72aa32477b2..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/SameEventTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.model.lib.api.event.SameEventTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.model.lib.api.event.SameEventTest

          -
          -
          No usage of com.tractrac.model.lib.api.event.SameEventTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/StartTimeType.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/StartTimeType.html deleted file mode 100644 index 98367661e83..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/StartTimeType.html +++ /dev/null @@ -1,180 +0,0 @@ - - - - - -Uses of Class com.tractrac.model.lib.api.event.StartTimeType - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.model.lib.api.event.StartTimeType

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/StartTimeTypeTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/StartTimeTypeTest.html deleted file mode 100644 index 2dcff6710db..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/class-use/StartTimeTypeTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.model.lib.api.event.StartTimeTypeTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.model.lib.api.event.StartTimeTypeTest

          -
          -
          No usage of com.tractrac.model.lib.api.event.StartTimeTypeTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/package-frame.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/package-frame.html index 445fd7fa337..fe541bc1b3c 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/package-frame.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/package-frame.html @@ -1,10 +1,10 @@ - + - + com.tractrac.model.lib.api.event - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/package-summary.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/package-summary.html index c798bce2316..705061a553b 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/package-summary.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/package-summary.html @@ -1,10 +1,10 @@ - + - + com.tractrac.model.lib.api.event - + @@ -72,8 +72,7 @@

          Package com.tractrac.model.lib.api.event

          - Events, races and competitors -
          + Events, races and competitors

          See: Description

          @@ -158,8 +157,7 @@ EventType
          - The type of the event, according with the EventManager -
          + The type of the event, according with the EventManager @@ -205,8 +203,7 @@ RaceLoadingException
          - This exception is thrown when there is an error loading the race -
          + This exception is thrown when there is an error loading the race diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/package-tree.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/package-tree.html index 71d19fa2f75..d6fd978a7df 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/package-tree.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/package-tree.html @@ -1,10 +1,10 @@ - + - + com.tractrac.model.lib.api.event Class Hierarchy - + @@ -150,11 +150,11 @@ diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/package-use.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/package-use.html deleted file mode 100644 index 55a7ac7ecda..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/event/package-use.html +++ /dev/null @@ -1,381 +0,0 @@ - - - - - -Uses of Package com.tractrac.model.lib.api.event - - - - - - - - - - -
          -

          Uses of Package
          com.tractrac.model.lib.api.event

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/IMetadata.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/IMetadata.html index cd12a212dd2..834ff2455a8 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/IMetadata.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/IMetadata.html @@ -1,10 +1,10 @@ - + - + IMetadata - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/IMetadataContainer.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/IMetadataContainer.html index e166e238962..8ba84079035 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/IMetadataContainer.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/IMetadataContainer.html @@ -1,10 +1,10 @@ - + - + IMetadataContainer - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/IMetadataFactory.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/IMetadataFactory.html index 9a66e9391f6..3c99bf4449a 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/IMetadataFactory.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/IMetadataFactory.html @@ -1,10 +1,10 @@ - + - + IMetadataFactory - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/IPropertiesContainer.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/IPropertiesContainer.html index 4068fc24951..4d5296f5c4e 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/IPropertiesContainer.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/IPropertiesContainer.html @@ -1,10 +1,10 @@ - + - + IPropertiesContainer - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/MetadataTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/MetadataTest.html deleted file mode 100644 index 8600be4881e..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/MetadataTest.html +++ /dev/null @@ -1,404 +0,0 @@ - - - - - -MetadataTest - - - - - - - - - - - -
          -
          com.tractrac.model.lib.api.metadata
          -

          Class MetadataTest

          -
          -
          -
            -
          • java.lang.Object
          • -
          • -
              -
            • junit.framework.Assert
            • -
            • -
                -
              • junit.framework.TestCase
              • -
              • -
                  -
                • com.tractrac.model.lib.api.metadata.MetadataTest
                • -
                -
              • -
              -
            • -
            -
          • -
          -
          -
            -
          • -
            -
            All Implemented Interfaces:
            -
            junit.framework.Test
            -
            -
            -
            -
            public abstract class MetadataTest
            -extends junit.framework.TestCase
            -

            - Test the metadata -

            -
            Author:
            -
            Jorge Piera Llodrá
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Summary

              - - - - - - - - -
              Constructors 
              Constructor and Description
              MetadataTest() 
              -
            • -
            - -
              -
            • - - -

              Method Summary

              - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
              Methods 
              Modifier and TypeMethod and Description
              voidsetUp() 
              voidtestMetadataForCompetitor() 
              voidtestMetadataForControlPoint() 
              voidtestMetadataForRace() 
              voidtestMetadataForRoute() 
              voidtestMetadataIsEmpty() 
              voidtestMetadataisNull() 
              voidtestMetadataWithBreakLine() 
              -
                -
              • - - -

                Methods inherited from class junit.framework.TestCase

                -assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertFalse, assertFalse, assertNotNull, assertNotNull, assertNotSame, assertNotSame, assertNull, assertNull, assertSame, assertSame, assertTrue, assertTrue, countTestCases, fail, fail, failNotEquals, failNotSame, failSame, format, getName, run, run, runBare, setName, toString
              • -
              -
                -
              • - - -

                Methods inherited from class java.lang.Object

                -equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
              • -
              -
            • -
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                MetadataTest

                -
                public MetadataTest()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                setUp

                -
                public void setUp()
                -           throws java.net.MalformedURLException,
                -                  java.net.URISyntaxException,
                -                  CreateModelException
                -
                -
                Overrides:
                -
                setUp in class junit.framework.TestCase
                -
                Throws:
                -
                java.net.MalformedURLException
                -
                java.net.URISyntaxException
                -
                CreateModelException
                -
              • -
              - - - -
                -
              • -

                testMetadataForRace

                -
                public void testMetadataForRace()
                -                         throws java.net.MalformedURLException
                -
                Throws:
                -
                java.net.MalformedURLException
                -
              • -
              - - - -
                -
              • -

                testMetadataForCompetitor

                -
                public void testMetadataForCompetitor()
                -                               throws java.net.MalformedURLException
                -
                Throws:
                -
                java.net.MalformedURLException
                -
              • -
              - - - -
                -
              • -

                testMetadataForRoute

                -
                public void testMetadataForRoute()
                -                          throws java.net.MalformedURLException
                -
                Throws:
                -
                java.net.MalformedURLException
                -
              • -
              - - - -
                -
              • -

                testMetadataForControlPoint

                -
                public void testMetadataForControlPoint()
                -                                 throws java.net.MalformedURLException
                -
                Throws:
                -
                java.net.MalformedURLException
                -
              • -
              - - - -
                -
              • -

                testMetadataWithBreakLine

                -
                public void testMetadataWithBreakLine()
                -                               throws java.net.MalformedURLException
                -
                Throws:
                -
                java.net.MalformedURLException
                -
              • -
              - - - -
                -
              • -

                testMetadataIsEmpty

                -
                public void testMetadataIsEmpty()
                -                         throws java.net.MalformedURLException
                -
                Throws:
                -
                java.net.MalformedURLException
                -
              • -
              - - - -
                -
              • -

                testMetadataisNull

                -
                public void testMetadataisNull()
                -                        throws java.net.MalformedURLException
                -
                Throws:
                -
                java.net.MalformedURLException
                -
              • -
              -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/class-use/IMetadata.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/class-use/IMetadata.html deleted file mode 100644 index c240a4c4c99..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/class-use/IMetadata.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.metadata.IMetadata - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.metadata.IMetadata

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/class-use/IMetadataContainer.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/class-use/IMetadataContainer.html deleted file mode 100644 index 41cad553cca..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/class-use/IMetadataContainer.html +++ /dev/null @@ -1,224 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.metadata.IMetadataContainer - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.metadata.IMetadataContainer

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/class-use/IMetadataFactory.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/class-use/IMetadataFactory.html deleted file mode 100644 index e5cb7dcd662..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/class-use/IMetadataFactory.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.metadata.IMetadataFactory - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.metadata.IMetadataFactory

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/class-use/IPropertiesContainer.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/class-use/IPropertiesContainer.html deleted file mode 100644 index dd6ef559974..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/class-use/IPropertiesContainer.html +++ /dev/null @@ -1,259 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.metadata.IPropertiesContainer - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.metadata.IPropertiesContainer

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/class-use/MetadataTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/class-use/MetadataTest.html deleted file mode 100644 index 4397e5a73ce..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/class-use/MetadataTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.model.lib.api.metadata.MetadataTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.model.lib.api.metadata.MetadataTest

          -
          -
          No usage of com.tractrac.model.lib.api.metadata.MetadataTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/package-frame.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/package-frame.html index d21f2488af6..6cb897f6b81 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/package-frame.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/package-frame.html @@ -1,10 +1,10 @@ - + - + com.tractrac.model.lib.api.metadata - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/package-summary.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/package-summary.html index 6b92f8e4265..df5d00e3416 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/package-summary.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/package-summary.html @@ -1,10 +1,10 @@ - + - + com.tractrac.model.lib.api.metadata - + @@ -72,8 +72,7 @@

          Package com.tractrac.model.lib.api.metadata

          - The metadata functionality -
          + The metadata functionality

          See: Description

          diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/package-tree.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/package-tree.html index cade2697e58..f0442a55101 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/package-tree.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/package-tree.html @@ -1,10 +1,10 @@ - + - + com.tractrac.model.lib.api.metadata Class Hierarchy - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/package-use.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/package-use.html deleted file mode 100644 index 9639d5c5803..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/metadata/package-use.html +++ /dev/null @@ -1,236 +0,0 @@ - - - - - -Uses of Package com.tractrac.model.lib.api.metadata - - - - - - - - - - -
          -

          Uses of Package
          com.tractrac.model.lib.api.metadata

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/package-frame.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/package-frame.html index 65a41681494..719bd795d36 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/package-frame.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/package-frame.html @@ -1,10 +1,10 @@ - + - + com.tractrac.model.lib.api - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/package-summary.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/package-summary.html index c6d281e260a..7ba2e754d9f 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/package-summary.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/package-summary.html @@ -1,10 +1,10 @@ - + - + com.tractrac.model.lib.api - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/package-tree.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/package-tree.html index 21482c0d2c1..c5132f18a9d 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/package-tree.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/package-tree.html @@ -1,10 +1,10 @@ - + - + com.tractrac.model.lib.api Class Hierarchy - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/package-use.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/package-use.html deleted file mode 100644 index 717868ade5d..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/package-use.html +++ /dev/null @@ -1,211 +0,0 @@ - - - - - -Uses of Package com.tractrac.model.lib.api - - - - - - - - - - -
          -

          Uses of Package
          com.tractrac.model.lib.api

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/ControlRouteTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/ControlRouteTest.html deleted file mode 100644 index 5ba597c4ae0..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/ControlRouteTest.html +++ /dev/null @@ -1,303 +0,0 @@ - - - - - -ControlRouteTest - - - - - - - - - - - -
          -
          com.tractrac.model.lib.api.route
          -

          Class ControlRouteTest

          -
          -
          -
            -
          • java.lang.Object
          • -
          • -
              -
            • junit.framework.Assert
            • -
            • -
                -
              • junit.framework.TestCase
              • -
              • -
                  -
                • com.tractrac.model.lib.api.route.ControlRouteTest
                • -
                -
              • -
              -
            • -
            -
          • -
          -
          -
            -
          • -
            -
            All Implemented Interfaces:
            -
            junit.framework.Test
            -
            -
            -
            -
            public abstract class ControlRouteTest
            -extends junit.framework.TestCase
            -

            - Reads a route with marks from the parameters file -

            -
            Author:
            -
            Jorge Piera Llodrá
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Summary

              - - - - - - - - -
              Constructors 
              Constructor and Description
              ControlRouteTest() 
              -
            • -
            - -
              -
            • - - -

              Method Summary

              - - - - - - - - - - - - - - -
              Methods 
              Modifier and TypeMethod and Description
              voidsetUp() 
              voidtestCreateAttachment() 
              -
                -
              • - - -

                Methods inherited from class junit.framework.TestCase

                -assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertFalse, assertFalse, assertNotNull, assertNotNull, assertNotSame, assertNotSame, assertNull, assertNull, assertSame, assertSame, assertTrue, assertTrue, countTestCases, fail, fail, failNotEquals, failNotSame, failSame, format, getName, run, run, runBare, setName, toString
              • -
              -
                -
              • - - -

                Methods inherited from class java.lang.Object

                -equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
              • -
              -
            • -
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                ControlRouteTest

                -
                public ControlRouteTest()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                setUp

                -
                public void setUp()
                -           throws java.net.URISyntaxException,
                -                  CreateModelException
                -
                -
                Overrides:
                -
                setUp in class junit.framework.TestCase
                -
                Throws:
                -
                java.net.URISyntaxException
                -
                CreateModelException
                -
              • -
              - - - -
                -
              • -

                testCreateAttachment

                -
                public void testCreateAttachment()
                -
              • -
              -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IControl.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IControl.html index 938aa0569f4..a49c7d91837 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IControl.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IControl.html @@ -1,10 +1,10 @@ - + - + IControl - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IControlPoint.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IControlPoint.html index 8c2db02628c..26208c5fb29 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IControlPoint.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IControlPoint.html @@ -1,10 +1,10 @@ - + - + IControlPoint - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IControlRoute.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IControlRoute.html index f0debcbe64a..008f085d385 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IControlRoute.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IControlRoute.html @@ -1,10 +1,10 @@ - + - + IControlRoute - + @@ -149,10 +149,7 @@ extends java.util.List<IControl> getControls()
          - The list of controls of a route - - - This method is thread-safety: it returns a copy of the list.
          + The list of controls of a route diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IPathRoute.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IPathRoute.html index 862ad58a4f3..03443c48ed6 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IPathRoute.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IPathRoute.html @@ -1,10 +1,10 @@ - + - + IPathRoute - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IPathRouteFactory.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IPathRouteFactory.html index 3fc8d18956d..fb278a18d87 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IPathRouteFactory.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IPathRouteFactory.html @@ -1,10 +1,10 @@ - + - + IPathRouteFactory - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IPathSegment.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IPathSegment.html index 69e383dae82..ef558c6d6bb 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IPathSegment.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IPathSegment.html @@ -1,10 +1,10 @@ - + - + IPathSegment - + @@ -147,7 +147,7 @@ var activeTableTab = "activeTableTab"; boolean isReversed()
          Flag specifying whether the segment is part of the route in reverse order, - i.e. travelled from end of segment to start of segment.
          + i.e. diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IRoute.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IRoute.html index eb886aa900a..fe9de82bb9d 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IRoute.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/IRoute.html @@ -1,10 +1,10 @@ - + - + IRoute - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/ISegment.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/ISegment.html index 1e8f8024eea..b4a441d2e62 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/ISegment.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/ISegment.html @@ -1,10 +1,10 @@ - + - + ISegment - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/ReadRouteTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/ReadRouteTest.html deleted file mode 100644 index cd12b16db39..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/ReadRouteTest.html +++ /dev/null @@ -1,284 +0,0 @@ - - - - - -ReadRouteTest - - - - - - - - - - - -
          -
          com.tractrac.model.lib.api.route
          -

          Class ReadRouteTest

          -
          -
          -
            -
          • java.lang.Object
          • -
          • -
              -
            • junit.framework.Assert
            • -
            • -
                -
              • junit.framework.TestCase
              • -
              • -
                  -
                • com.tractrac.model.lib.api.route.ReadRouteTest
                • -
                -
              • -
              -
            • -
            -
          • -
          -
          -
            -
          • -
            -
            All Implemented Interfaces:
            -
            junit.framework.Test
            -
            -
            -
            -
            public abstract class ReadRouteTest
            -extends junit.framework.TestCase
            -
            Author:
            -
            Jorge Piera Llodrá
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Summary

              - - - - - - - - -
              Constructors 
              Constructor and Description
              ReadRouteTest() 
              -
            • -
            - -
              -
            • - - -

              Method Summary

              - - - - - - - - - - -
              Methods 
              Modifier and TypeMethod and Description
              voidtestRouteName() 
              -
                -
              • - - -

                Methods inherited from class junit.framework.TestCase

                -assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertFalse, assertFalse, assertNotNull, assertNotNull, assertNotSame, assertNotSame, assertNull, assertNull, assertSame, assertSame, assertTrue, assertTrue, countTestCases, fail, fail, failNotEquals, failNotSame, failSame, format, getName, run, run, runBare, setName, toString
              • -
              -
                -
              • - - -

                Methods inherited from class java.lang.Object

                -equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
              • -
              -
            • -
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                ReadRouteTest

                -
                public ReadRouteTest()
                -
              • -
              -
            • -
            - - -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/ReadTextPathRouteTests.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/ReadTextPathRouteTests.html deleted file mode 100644 index aa06aabb547..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/ReadTextPathRouteTests.html +++ /dev/null @@ -1,282 +0,0 @@ - - - - - -ReadTextPathRouteTests - - - - - - - - - - - -
          -
          com.tractrac.model.lib.api.route
          -

          Class ReadTextPathRouteTests

          -
          -
          -
            -
          • java.lang.Object
          • -
          • -
              -
            • junit.framework.Assert
            • -
            • -
                -
              • junit.framework.TestCase
              • -
              • -
                  -
                • com.tractrac.model.lib.api.route.ReadTextPathRouteTests
                • -
                -
              • -
              -
            • -
            -
          • -
          -
          -
            -
          • -
            -
            All Implemented Interfaces:
            -
            junit.framework.Test
            -
            -
            -
            -
            public abstract class ReadTextPathRouteTests
            -extends junit.framework.TestCase
            -

            - Checks the reading of a route path -

            -
            Author:
            -
            Jesper Grooss
            -
          • -
          -
          -
          -
            -
          • - - - -
              -
            • - - -

              Method Summary

              - - - - - - - - - - -
              Methods 
              Modifier and TypeMethod and Description
              voidtestRouteData() 
              -
                -
              • - - -

                Methods inherited from class junit.framework.TestCase

                -assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertFalse, assertFalse, assertNotNull, assertNotNull, assertNotSame, assertNotSame, assertNull, assertNull, assertSame, assertSame, assertTrue, assertTrue, countTestCases, fail, fail, failNotEquals, failNotSame, failSame, format, getName, run, run, runBare, setName, toString
              • -
              -
                -
              • - - -

                Methods inherited from class java.lang.Object

                -equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
              • -
              -
            • -
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                ReadTextPathRouteTests

                -
                public ReadTextPathRouteTests()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                testRouteData

                -
                public void testRouteData()
                -
              • -
              -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/ControlRouteTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/ControlRouteTest.html deleted file mode 100644 index 3e34ccf071a..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/ControlRouteTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.model.lib.api.route.ControlRouteTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.model.lib.api.route.ControlRouteTest

          -
          -
          No usage of com.tractrac.model.lib.api.route.ControlRouteTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/IControl.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/IControl.html deleted file mode 100644 index 82d1c90575e..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/IControl.html +++ /dev/null @@ -1,299 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.route.IControl - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.route.IControl

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/IControlPoint.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/IControlPoint.html deleted file mode 100644 index 5199a2736e0..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/IControlPoint.html +++ /dev/null @@ -1,161 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.route.IControlPoint - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.route.IControlPoint

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/IControlRoute.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/IControlRoute.html deleted file mode 100644 index 8504e3fb0f6..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/IControlRoute.html +++ /dev/null @@ -1,161 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.route.IControlRoute - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.route.IControlRoute

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/IPathRoute.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/IPathRoute.html deleted file mode 100644 index 0c05e00b374..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/IPathRoute.html +++ /dev/null @@ -1,160 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.route.IPathRoute - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.route.IPathRoute

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/IPathRouteFactory.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/IPathRouteFactory.html deleted file mode 100644 index 26413a79688..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/IPathRouteFactory.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.route.IPathRouteFactory - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.route.IPathRouteFactory

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/IPathSegment.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/IPathSegment.html deleted file mode 100644 index fba9ec54e5f..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/IPathSegment.html +++ /dev/null @@ -1,161 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.route.IPathSegment - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.route.IPathSegment

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/IRoute.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/IRoute.html deleted file mode 100644 index 7d927656f24..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/IRoute.html +++ /dev/null @@ -1,254 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.route.IRoute - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.route.IRoute

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/ISegment.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/ISegment.html deleted file mode 100644 index cd44390e8b0..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/ISegment.html +++ /dev/null @@ -1,160 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.route.ISegment - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.route.ISegment

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/ReadRouteTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/ReadRouteTest.html deleted file mode 100644 index f6f14ebed46..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/ReadRouteTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.model.lib.api.route.ReadRouteTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.model.lib.api.route.ReadRouteTest

          -
          -
          No usage of com.tractrac.model.lib.api.route.ReadRouteTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/ReadTextPathRouteTests.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/ReadTextPathRouteTests.html deleted file mode 100644 index b3e0aec875a..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/class-use/ReadTextPathRouteTests.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.model.lib.api.route.ReadTextPathRouteTests - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.model.lib.api.route.ReadTextPathRouteTests

          -
          -
          No usage of com.tractrac.model.lib.api.route.ReadTextPathRouteTests
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/package-frame.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/package-frame.html index 9ae41eb40f1..7daa3ba8858 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/package-frame.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/package-frame.html @@ -1,10 +1,10 @@ - + - + com.tractrac.model.lib.api.route - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/package-summary.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/package-summary.html index acfa4778f0c..1774f325c9e 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/package-summary.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/package-summary.html @@ -1,10 +1,10 @@ - + - + com.tractrac.model.lib.api.route - + @@ -72,8 +72,7 @@

          Package com.tractrac.model.lib.api.route

          - The route configuration: controls, paths and routes -
          + The route configuration: controls, paths and routes

          See: Description

          @@ -134,8 +133,7 @@ IRoute
          - A route can be one of: IControlRoute or IPathRoute. see those - for details.
          + A route can be one of: IControlRoute or IPathRoute. diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/package-tree.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/package-tree.html index b8cf17f5583..3045d7048ec 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/package-tree.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/package-tree.html @@ -1,10 +1,10 @@ - + - + com.tractrac.model.lib.api.route Class Hierarchy - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/package-use.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/package-use.html deleted file mode 100644 index 2c578303a02..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/route/package-use.html +++ /dev/null @@ -1,319 +0,0 @@ - - - - - -Uses of Package com.tractrac.model.lib.api.route - - - - - - - - - - -
          -

          Uses of Package
          com.tractrac.model.lib.api.route

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/ICoordinate.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/ICoordinate.html index 79f4c934f87..8da36066ec6 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/ICoordinate.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/ICoordinate.html @@ -1,10 +1,10 @@ - + - + ICoordinate - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/ICoordinateSequence.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/ICoordinateSequence.html index 7d0dbc5e534..fc756958d37 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/ICoordinateSequence.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/ICoordinateSequence.html @@ -1,10 +1,10 @@ - + - + ICoordinateSequence - + @@ -131,12 +131,7 @@ var activeTableTab = "activeTableTab"; ICoordinate locateAlong(double mOffset) -
          Returns the coordinate at the specified m ordinate value - - - It is required that the coordinates has an m ordiante value and is sorted - in order of strictly increasing m ordinate value -
          +
          Returns the coordinate at the specified m ordinate value
          diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/IGeoCoordinate.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/IGeoCoordinate.html index 9bcd44115e6..57a63180d86 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/IGeoCoordinate.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/IGeoCoordinate.html @@ -1,10 +1,10 @@ - + - + IGeoCoordinate - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/class-use/ICoordinate.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/class-use/ICoordinate.html deleted file mode 100644 index db63a5cfb2b..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/class-use/ICoordinate.html +++ /dev/null @@ -1,264 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.spatial.ICoordinate - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.spatial.ICoordinate

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/class-use/ICoordinateSequence.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/class-use/ICoordinateSequence.html deleted file mode 100644 index f56cebdba87..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/class-use/ICoordinateSequence.html +++ /dev/null @@ -1,160 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.spatial.ICoordinateSequence - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.spatial.ICoordinateSequence

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/class-use/IGeoCoordinate.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/class-use/IGeoCoordinate.html deleted file mode 100644 index 9f3802bf52b..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/class-use/IGeoCoordinate.html +++ /dev/null @@ -1,168 +0,0 @@ - - - - - -Uses of Interface com.tractrac.model.lib.api.spatial.IGeoCoordinate - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.model.lib.api.spatial.IGeoCoordinate

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/package-frame.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/package-frame.html index af3d4c321cd..57e42f10368 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/package-frame.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/package-frame.html @@ -1,10 +1,10 @@ - + - + com.tractrac.model.lib.api.spatial - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/package-summary.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/package-summary.html index 090d277eeb0..f006ca855ed 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/package-summary.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/package-summary.html @@ -1,10 +1,10 @@ - + - + com.tractrac.model.lib.api.spatial - + @@ -72,8 +72,7 @@

          Package com.tractrac.model.lib.api.spatial

          - The spatial data -
          + The spatial data

          See: Description

          @@ -98,8 +97,7 @@ ICoordinateSequence
          - A sequence of points connected by straight lines (line segments) -
          + A sequence of points connected by straight lines (line segments) diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/package-tree.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/package-tree.html index 3204615e860..3ef90b1897e 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/package-tree.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/package-tree.html @@ -1,10 +1,10 @@ - + - + com.tractrac.model.lib.api.spatial Class Hierarchy - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/package-use.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/package-use.html deleted file mode 100644 index 8dce6802f25..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/model/lib/api/spatial/package-use.html +++ /dev/null @@ -1,216 +0,0 @@ - - - - - -Uses of Package com.tractrac.model.lib.api.spatial - - - - - - - - - - -
          -

          Uses of Package
          com.tractrac.model.lib.api.spatial

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/AbstractSubscriptionTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/AbstractSubscriptionTest.html deleted file mode 100644 index 71b09e561ff..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/AbstractSubscriptionTest.html +++ /dev/null @@ -1,343 +0,0 @@ - - - - - -AbstractSubscriptionTest - - - - - - - - - - - -
          -
          com.tractrac.subscription.lib.api
          -

          Class AbstractSubscriptionTest

          -
          -
          - -
          - -
          -
          - -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                AbstractSubscriptionTest

                -
                public AbstractSubscriptionTest()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                addDataserverToEvent

                -
                public ILocalDataserver addDataserverToEvent(IEvent event)
                -                                      throws java.io.IOException,
                -                                             java.net.URISyntaxException
                -
                Create a local dataserver that can be used to establish a communication - with the subscription library. It uses both the live and the stored - connections.
                -
                Parameters:
                event - the event
                -
                Returns:
                a local dataserver
                -
                Throws:
                -
                java.io.IOException - if there is an error creating the sockets
                -
                java.net.URISyntaxException - if there is an error creating the URIs
                -
              • -
              - - - -
                -
              • -

                addDataserverToEvent

                -
                public ILocalDataserver addDataserverToEvent(IEvent event,
                -                                    boolean isLive,
                -                                    boolean isStored)
                -                                      throws java.io.IOException,
                -                                             java.net.URISyntaxException
                -
                Create a local dataserver that can be used to establish a communication - with the subscription library.
                -
                Parameters:
                event - the event
                isLive - if the subscription uses the live connection
                isStored - if the subscription uses the stored connection
                -
                Returns:
                a local dataserver
                -
                Throws:
                -
                java.io.IOException - if there is an error creating the sockets
                -
                java.net.URISyntaxException - if there is an error creating the URIs
                -
              • -
              -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/IEventSubscriber.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/IEventSubscriber.html index 8fb1c703b53..018f5e1419e 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/IEventSubscriber.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/IEventSubscriber.html @@ -1,10 +1,10 @@ - + - + IEventSubscriber - + @@ -176,16 +176,14 @@ extends void unsubscribeCompetitors(ICompetitorsListener listener)
          - Unsubscribes the competitors subscribers -
          + Unsubscribes the competitors subscribers void unsubscribeControls(IControlsListener listener)
          - Unsubscribes the controls subscribers -
          + Unsubscribes the controls subscribers @@ -204,8 +202,7 @@ extends void unsubscribeRaces(IRacesListener listener)
          - Unsubscribes the races subscribers -
          + Unsubscribes the races subscribers diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/ILocalDataServerFactory.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/ILocalDataServerFactory.html deleted file mode 100644 index 7230494a345..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/ILocalDataServerFactory.html +++ /dev/null @@ -1,232 +0,0 @@ - - - - - -ILocalDataServerFactory - - - - - - - - - - - -
          -
          com.tractrac.subscription.lib.api
          -

          Interface ILocalDataServerFactory

          -
          -
          -
          -
            -
          • -
            -
            -
            public interface ILocalDataServerFactory
            -

            - Factory used to create ILocalDataserver instances. The implementation - of this class has to be registered using the Service Loader pattern. If you want to add a new implementation, you - have to create a new component, create a file - META-INF/services/com.tractrac.subscription.lib.api.ILocalDataServerFactory - and add a line with the name of the ILocalDataServerFactory class. -

            -
            Author:
            -
            Jorge Piera Llodrá
            -
            See Also:
            Service - Loader
            -
          • -
          -
          -
          - -
          -
          -
            -
          • - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                createLocalDataServer

                -
                ILocalDataserver createLocalDataServer(IEvent event,
                -                                     boolean isLive,
                -                                     boolean isStored)
                -                                       throws java.io.IOException
                -
                Creates and instance of a ILocalDataserver
                -
                Parameters:
                event - the event
                isLive - if the subscription uses the live connection
                isStored - if the subscription uses the stored connection
                -
                Returns:
                a local dataserver
                -
                Throws:
                -
                java.io.IOException - if there is an error creating the dataserver
                -
              • -
              -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/ILocalDataserver.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/ILocalDataserver.html deleted file mode 100644 index ff77d0c7838..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/ILocalDataserver.html +++ /dev/null @@ -1,635 +0,0 @@ - - - - - -ILocalDataserver - - - - - - - - - - - -
          -
          com.tractrac.subscription.lib.api
          -

          Interface ILocalDataserver

          -
          -
          -
          -
            -
          • -
            -
            -
            public interface ILocalDataserver
            -

            - This class is a local dataserver. It is possible to add events and connect to - it order to test the implementation of the subscription library. -

            -
            Author:
            -
            Jorge Piera Llodrá
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Method Summary

              - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
              Methods 
              Modifier and TypeMethod and Description
              voidabandonRace(java.util.UUID raceId) -
              Abandon a race from the event
              -
              voidaddCompetitor(java.lang.String name, - java.lang.String shortName, - java.lang.String team, - java.lang.String competitorClass) -
              Add a new competitor to the event
              -
              voidaddLivePosition(ICompetitor competitor, - double lon, - double lat, - long timestamp) -
              Adds a live position to the dataserver.
              -
              voidaddMark(java.lang.String name, - java.lang.String shortName, - double lat, - double lon, - long timestamp) -
              Add a new mark to the event
              -
              voidaddRace(java.lang.String raceName, - long trackingStartTime, - long trackingEndTime, - long raceStartTime, - RaceStatusType status, - java.net.URI parametersFile) -
              Add a new race to the event
              -
              voidaddRaceCompetitor(IRace race, - IRoute route, - ICompetitor competitor) -
              Add a competitor to a race
              -
              voidaddStoredPosition(ICompetitor competitor, - double lon, - double lat, - long timestamp) -
              Adds a stored position to the dataserver.
              -
              voidclose() -
              Closes the connections
              -
              voiddeleteCompetitor(java.util.UUID competitorId) -
              Delete a competitor from the event
              -
              voiddeleteMark(java.util.UUID markId) -
              Delete a mark from the event
              -
              voiddeleteRace(java.util.UUID raceId) -
              Delete a race from the event
              -
              intgetLivePort() -
              Gets the port for the live connection
              -
              intgetStoredPort() -
              Gets the port for the stored connection
              -
              voidreloadRace(java.util.UUID raceId) -
              Reload a race from the event
              -
              voidremoveRaceCompetitor(IRace race, - java.util.UUID competitorId) -
              Delete a race from the race
              -
              voidstart() -
              Create the threads that listen both the stored and live connections
              -
              voidupdateCompetitor(java.util.UUID competitorId, - java.lang.String name, - java.lang.String shortName, - java.lang.String team, - java.lang.String competitorClass) -
              Update a competitor
              -
              voidupdateLiveDelay(int seconds) -
              Update the live delay property
              -
              voidupdateLiveDelay(java.util.UUID raceId, - int seconds) -
              Update the live delay property for a race
              -
              voidupdateMark(java.util.UUID markId, - java.lang.String name, - java.lang.String shortName, - double lat, - double lon, - long timestamp) -
              Add a new mark to the event
              -
              voidupdateRace(java.util.UUID raceId, - java.lang.String raceName, - long trackingStartTime, - long trackingEndTime, - long raceStartTime, - RaceStatusType status, - java.net.URI parametersFile) -
              Update a race
              -
              -
            • -
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                getStoredPort

                -
                int getStoredPort()
                -
                Gets the port for the stored connection
                -
                Returns:
                the stored port
                -
              • -
              - - - -
                -
              • -

                getLivePort

                -
                int getLivePort()
                -
                Gets the port for the live connection
                -
                Returns:
                the live port
                -
              • -
              - - - -
                -
              • -

                start

                -
                void start()
                -
                Create the threads that listen both the stored and live connections
                -
              • -
              - - - -
                -
              • -

                close

                -
                void close()
                -           throws java.io.IOException
                -
                Closes the connections
                -
                Throws:
                -
                java.io.IOException - if there is an error closing the sockets
                -
              • -
              - - - -
                -
              • -

                addLivePosition

                -
                void addLivePosition(ICompetitor competitor,
                -                   double lon,
                -                   double lat,
                -                   long timestamp)
                -
                Adds a live position to the dataserver. This position will be sent if the - application is subscribed to the competitor positions event using the live - connection.
                -
                Parameters:
                competitor - the competitor
                lon - the longitude
                lat - the latitude
                timestamp - the time stamp when this position is valid
                -
              • -
              - - - -
                -
              • -

                addStoredPosition

                -
                void addStoredPosition(ICompetitor competitor,
                -                     double lon,
                -                     double lat,
                -                     long timestamp)
                -
                Adds a stored position to the dataserver. This position will be sent if the - application is subscribed to the competitor positions event using the - stored connection.
                -
                Parameters:
                competitor - the competitor
                lon - the longitude
                lat - the latitude
                timestamp - the time stamp when this position is valid
                -
              • -
              - - - -
                -
              • -

                addCompetitor

                -
                void addCompetitor(java.lang.String name,
                -                 java.lang.String shortName,
                -                 java.lang.String team,
                -                 java.lang.String competitorClass)
                -
                Add a new competitor to the event
                -
                Parameters:
                name - the name of the competitor
                shortName - the short name of the competitor
                team - the team name
                competitorClass - the competitor class
                -
              • -
              - - - -
                -
              • -

                deleteCompetitor

                -
                void deleteCompetitor(java.util.UUID competitorId)
                -
                Delete a competitor from the event
                -
                Parameters:
                competitorId - the competitor identifier
                -
              • -
              - - - -
                -
              • -

                updateCompetitor

                -
                void updateCompetitor(java.util.UUID competitorId,
                -                    java.lang.String name,
                -                    java.lang.String shortName,
                -                    java.lang.String team,
                -                    java.lang.String competitorClass)
                -
                Update a competitor
                -
                Parameters:
                competitorId - the competitor identifier
                name - the name of the competitor
                shortName - the short name of the competitor
                team - the team name
                competitorClass - the competitor class
                -
              • -
              - - - -
                -
              • -

                addRace

                -
                void addRace(java.lang.String raceName,
                -           long trackingStartTime,
                -           long trackingEndTime,
                -           long raceStartTime,
                -           RaceStatusType status,
                -           java.net.URI parametersFile)
                -
                Add a new race to the event
                -
                Parameters:
                raceName - the race name
                trackingStartTime - the tracking start time
                trackingEndTime - the tracking end time
                raceStartTime - the race start time
                status - the status of the race
                parametersFile - the parameters file
                -
              • -
              - - - -
                -
              • -

                updateRace

                -
                void updateRace(java.util.UUID raceId,
                -              java.lang.String raceName,
                -              long trackingStartTime,
                -              long trackingEndTime,
                -              long raceStartTime,
                -              RaceStatusType status,
                -              java.net.URI parametersFile)
                -
                Update a race
                -
                Parameters:
                raceId - the race identifier
                raceName - the race name
                trackingStartTime - the tracking start time
                trackingEndTime - the tracking end time
                raceStartTime - the race start time
                status - the status of the race
                parametersFile - the parameters file
                -
              • -
              - - - -
                -
              • -

                deleteRace

                -
                void deleteRace(java.util.UUID raceId)
                -
                Delete a race from the event
                -
                Parameters:
                raceId - the race identifier
                -
              • -
              - - - -
                -
              • -

                reloadRace

                -
                void reloadRace(java.util.UUID raceId)
                -
                Reload a race from the event
                -
                Parameters:
                raceId - the race identifier
                -
              • -
              - - - -
                -
              • -

                abandonRace

                -
                void abandonRace(java.util.UUID raceId)
                -
                Abandon a race from the event
                -
                Parameters:
                raceId - the race identifier
                -
              • -
              - - - -
                -
              • -

                addMark

                -
                void addMark(java.lang.String name,
                -           java.lang.String shortName,
                -           double lat,
                -           double lon,
                -           long timestamp)
                -
                Add a new mark to the event
                -
                Parameters:
                name - the name of the mark
                name - the name of the mark
                lat - the latitude value of the mark
                lon - the longitude value of the mark
                timestamp - the timestamp of the mark
                -
              • -
              - - - -
                -
              • -

                updateMark

                -
                void updateMark(java.util.UUID markId,
                -              java.lang.String name,
                -              java.lang.String shortName,
                -              double lat,
                -              double lon,
                -              long timestamp)
                -
                Add a new mark to the event
                -
                Parameters:
                markId - the mark identifier
                name - the name of the mark
                shortName - the short name
                lat - the latitude value of the mark
                lon - the longitude value of the mark
                timestamp - the timestamp of the mark
                -
              • -
              - - - -
                -
              • -

                deleteMark

                -
                void deleteMark(java.util.UUID markId)
                -
                Delete a mark from the event
                -
                Parameters:
                markId - the mark identifier
                -
              • -
              - - - -
                -
              • -

                addRaceCompetitor

                -
                void addRaceCompetitor(IRace race,
                -                     IRoute route,
                -                     ICompetitor competitor)
                -
                Add a competitor to a race
                -
                Parameters:
                race - the race
                route - the route
                competitor - the competitor to add
                -
              • -
              - - - -
                -
              • -

                removeRaceCompetitor

                -
                void removeRaceCompetitor(IRace race,
                -                        java.util.UUID competitorId)
                -
                Delete a race from the race
                -
                Parameters:
                race - the race
                competitorId - the competitor identifier
                -
              • -
              - - - -
                -
              • -

                updateLiveDelay

                -
                void updateLiveDelay(int seconds)
                -
                Update the live delay property
                -
                Parameters:
                the - number of seconds for the new live delay
                -
              • -
              - - - -
                -
              • -

                updateLiveDelay

                -
                void updateLiveDelay(java.util.UUID raceId,
                -                   int seconds)
                -
                Update the live delay property for a race
                -
                Parameters:
                raceId - the race identifier
                the - number of seconds for the new live delay
                -
              • -
              -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/IRaceSubscriber.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/IRaceSubscriber.html index cf9f8bcfcc7..5adacf31c3b 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/IRaceSubscriber.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/IRaceSubscriber.html @@ -1,10 +1,10 @@ - + - + IRaceSubscriber - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/ISubscriber.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/ISubscriber.html index 9abb09d62ef..8044ac3210a 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/ISubscriber.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/ISubscriber.html @@ -1,10 +1,10 @@ - + - + ISubscriber - + @@ -148,11 +148,7 @@ var activeTableTab = "activeTableTab"; void subscribeConnectionStatus(IConnectionStatusListener listener) -
          Subscribes for connection status - - - The method returns immediately and the positions are subsequently sent to - the given listener, as they are received.
          +
          Subscribes for connection status
          diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/ISubscriberFactory.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/ISubscriberFactory.html index fad6342183f..21f25b99907 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/ISubscriberFactory.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/ISubscriberFactory.html @@ -1,10 +1,10 @@ - + - + ISubscriberFactory - + @@ -143,11 +143,7 @@ extends com.tractrac.common.lib.api.service.IServiceProvider java.net.URI liveUri, java.net.URI storedUri)
          - Returns a subscriber for the event - - - It retrieves data using the liveUri and the storedUri parameters - discarding the internal URIs of the event.
          + Returns a subscriber for the event @@ -171,11 +167,7 @@ extends com.tractrac.common.lib.api.service.IServiceProvider createRaceSubscriber(java.net.URI parametersURI)
          Returns a subscriber for the race contained in the provided parameters - file - - - It connects with the liveURI (if exists) and with the storedURI contained - in this parameters file.
          + file diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/ISubscriberListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/ISubscriberListener.html index 22ae36d555a..fa0594cd13f 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/ISubscriberListener.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/ISubscriberListener.html @@ -1,10 +1,10 @@ - + - + ISubscriberListener - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/ReadHDRaceTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/ReadHDRaceTest.html deleted file mode 100644 index c8218c60b78..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/ReadHDRaceTest.html +++ /dev/null @@ -1,309 +0,0 @@ - - - - - -ReadHDRaceTest - - - - - - - - - - - -
          -
          com.tractrac.subscription.lib.api
          -

          Class ReadHDRaceTest

          -
          -
          -
            -
          • java.lang.Object
          • -
          • -
              -
            • junit.framework.Assert
            • -
            • -
                -
              • junit.framework.TestCase
              • -
              • -
                  -
                • com.tractrac.subscription.lib.api.ReadHDRaceTest
                • -
                -
              • -
              -
            • -
            -
          • -
          -
          -
            -
          • -
            -
            All Implemented Interfaces:
            -
            junit.framework.Test
            -
            -
            -
            -
            public abstract class ReadHDRaceTest
            -extends junit.framework.TestCase
            -
            Author:
            -
            Jorge Piera Llodrá
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Summary

              - - - - - - - - -
              Constructors 
              Constructor and Description
              ReadHDRaceTest() 
              -
            • -
            - -
              -
            • - - -

              Method Summary

              - - - - - - - - - - - - - - -
              Methods 
              Modifier and TypeMethod and Description
              voidsetUp() 
              voidtestEventData() 
              -
                -
              • - - -

                Methods inherited from class junit.framework.TestCase

                -assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertFalse, assertFalse, assertNotNull, assertNotNull, assertNotSame, assertNotSame, assertNull, assertNull, assertSame, assertSame, assertTrue, assertTrue, countTestCases, fail, fail, failNotEquals, failNotSame, failSame, format, getName, run, run, runBare, setName, toString
              • -
              -
                -
              • - - -

                Methods inherited from class java.lang.Object

                -equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
              • -
              -
            • -
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                ReadHDRaceTest

                -
                public ReadHDRaceTest()
                -
              • -
              -
            • -
            - - -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/SubscriberInitializationException.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/SubscriberInitializationException.html index fe767f18608..ce514fc0ae5 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/SubscriberInitializationException.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/SubscriberInitializationException.html @@ -1,10 +1,10 @@ - + - + SubscriberInitializationException - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/SubscriptionLocator.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/SubscriptionLocator.html index adf55ddaf6b..0746b17e731 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/SubscriptionLocator.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/SubscriptionLocator.html @@ -1,10 +1,10 @@ - + - + SubscriptionLocator - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/SubscriptionLocatorTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/SubscriptionLocatorTest.html deleted file mode 100644 index 240bc94659a..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/SubscriptionLocatorTest.html +++ /dev/null @@ -1,302 +0,0 @@ - - - - - -SubscriptionLocatorTest - - - - - - - - - - - -
          -
          com.tractrac.subscription.lib.api
          -

          Class SubscriptionLocatorTest

          -
          -
          -
            -
          • java.lang.Object
          • -
          • -
              -
            • junit.framework.Assert
            • -
            • -
                -
              • junit.framework.TestCase
              • -
              • -
                  -
                • com.tractrac.subscription.lib.api.SubscriptionLocatorTest
                • -
                -
              • -
              -
            • -
            -
          • -
          -
          -
            -
          • -
            -
            All Implemented Interfaces:
            -
            junit.framework.Test
            -
            -
            -
            -
            public abstract class SubscriptionLocatorTest
            -extends junit.framework.TestCase
            -

            - This test checks if an implementation of the model library implements all the - mandatory objects -

            -
            Author:
            -
            Jorge Piera Llodrá
            -
          • -
          -
          -
          -
            -
          • - - - -
              -
            • - - -

              Method Summary

              - - - - - - - - - - - - - - -
              Methods 
              Modifier and TypeMethod and Description
              voidtestRegisteredSubscriberFactory() -
              Test if there is a ISubscriberFactory registered.
              -
              voidtestSubscriberFactorySingleton() -
              Test if the ISubscriberFactory is a singleton
              -
              -
                -
              • - - -

                Methods inherited from class junit.framework.TestCase

                -assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertFalse, assertFalse, assertNotNull, assertNotNull, assertNotSame, assertNotSame, assertNull, assertNull, assertSame, assertSame, assertTrue, assertTrue, countTestCases, fail, fail, failNotEquals, failNotSame, failSame, format, getName, run, run, runBare, setName, toString
              • -
              -
                -
              • - - -

                Methods inherited from class java.lang.Object

                -equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
              • -
              -
            • -
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                SubscriptionLocatorTest

                -
                public SubscriptionLocatorTest()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                testRegisteredSubscriberFactory

                -
                public void testRegisteredSubscriberFactory()
                -
                Test if there is a ISubscriberFactory registered.
                -
              • -
              - - - -
                -
              • -

                testSubscriberFactorySingleton

                -
                public void testSubscriberFactorySingleton()
                -
                Test if the ISubscriberFactory is a singleton
                -
              • -
              -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/SubscriptionTestLocator.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/SubscriptionTestLocator.html deleted file mode 100644 index 83a48894396..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/SubscriptionTestLocator.html +++ /dev/null @@ -1,282 +0,0 @@ - - - - - -SubscriptionTestLocator - - - - - - - - - - - -
          -
          com.tractrac.subscription.lib.api
          -

          Class SubscriptionTestLocator

          -
          -
          -
            -
          • java.lang.Object
          • -
          • -
              -
            • com.tractrac.subscription.lib.api.SubscriptionTestLocator
            • -
            -
          • -
          -
          -
            -
          • -
            -
            -
            public class SubscriptionTestLocator
            -extends java.lang.Object
            -

            - This class is a locator used to get the instance of some of the factories - that are used for the creation of the objects. Is the entry point of the - system, and the unique class with statics methods. -

            -

            - The principal object is the ILocalDataserver, that can be created - using the ILocalDataServerFactory singleton. The implementation of - this factory, has to be registered using the Service Loader pattern. -

            -
            Author:
            -
            Jorge Piera Llodrá
            -
            See Also:
            Locator, -Service - Loader
            -
          • -
          -
          -
          - -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                SubscriptionTestLocator

                -
                public SubscriptionTestLocator()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                getLocalDataserverFactory

                -
                public static ILocalDataServerFactory getLocalDataserverFactory()
                -
                Used to get the instance of the ILocalDataServerFactory class. If - there are more registered instances it returns the first one.
                -
                Returns:
                the instance of the ILocalDataServerFactory object
                -
                Throws:
                -
                a - runtime exception if there are not registered instances.
                -
              • -
              -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/AbstractSubscriptionTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/AbstractSubscriptionTest.html deleted file mode 100644 index 6dd15f8589a..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/AbstractSubscriptionTest.html +++ /dev/null @@ -1,232 +0,0 @@ - - - - - -Uses of Class com.tractrac.subscription.lib.api.AbstractSubscriptionTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.subscription.lib.api.AbstractSubscriptionTest

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/IEventSubscriber.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/IEventSubscriber.html deleted file mode 100644 index fe2ef0b8cf3..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/IEventSubscriber.html +++ /dev/null @@ -1,170 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.IEventSubscriber - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.IEventSubscriber

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/ILocalDataServerFactory.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/ILocalDataServerFactory.html deleted file mode 100644 index a4aa73e28d9..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/ILocalDataServerFactory.html +++ /dev/null @@ -1,160 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.ILocalDataServerFactory - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.ILocalDataServerFactory

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/ILocalDataserver.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/ILocalDataserver.html deleted file mode 100644 index 129a4f07aec..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/ILocalDataserver.html +++ /dev/null @@ -1,178 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.ILocalDataserver - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.ILocalDataserver

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/IRaceSubscriber.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/IRaceSubscriber.html deleted file mode 100644 index 6de54b3a796..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/IRaceSubscriber.html +++ /dev/null @@ -1,188 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.IRaceSubscriber - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.IRaceSubscriber

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/ISubscriber.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/ISubscriber.html deleted file mode 100644 index 0c39210b54d..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/ISubscriber.html +++ /dev/null @@ -1,168 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.ISubscriber - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.ISubscriber

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/ISubscriberFactory.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/ISubscriberFactory.html deleted file mode 100644 index 03cd1a1c95f..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/ISubscriberFactory.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.ISubscriberFactory - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.ISubscriberFactory

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/ISubscriberListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/ISubscriberListener.html deleted file mode 100644 index f087b01aaf9..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/ISubscriberListener.html +++ /dev/null @@ -1,341 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.ISubscriberListener - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.ISubscriberListener

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/ReadHDRaceTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/ReadHDRaceTest.html deleted file mode 100644 index c65d03c2188..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/ReadHDRaceTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.subscription.lib.api.ReadHDRaceTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.subscription.lib.api.ReadHDRaceTest

          -
          -
          No usage of com.tractrac.subscription.lib.api.ReadHDRaceTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/SubscriberInitializationException.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/SubscriberInitializationException.html deleted file mode 100644 index e8b925f2bed..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/SubscriberInitializationException.html +++ /dev/null @@ -1,204 +0,0 @@ - - - - - -Uses of Class com.tractrac.subscription.lib.api.SubscriberInitializationException - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.subscription.lib.api.SubscriberInitializationException

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/SubscriptionLocator.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/SubscriptionLocator.html deleted file mode 100644 index bcd06e7d34c..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/SubscriptionLocator.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.subscription.lib.api.SubscriptionLocator - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.subscription.lib.api.SubscriptionLocator

          -
          -
          No usage of com.tractrac.subscription.lib.api.SubscriptionLocator
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/SubscriptionLocatorTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/SubscriptionLocatorTest.html deleted file mode 100644 index 1029ca01dec..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/SubscriptionLocatorTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.subscription.lib.api.SubscriptionLocatorTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.subscription.lib.api.SubscriptionLocatorTest

          -
          -
          No usage of com.tractrac.subscription.lib.api.SubscriptionLocatorTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/SubscriptionTestLocator.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/SubscriptionTestLocator.html deleted file mode 100644 index 2c9caa22a4d..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/class-use/SubscriptionTestLocator.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.subscription.lib.api.SubscriptionTestLocator - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.subscription.lib.api.SubscriptionTestLocator

          -
          -
          No usage of com.tractrac.subscription.lib.api.SubscriptionTestLocator
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/CompetitorsEventTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/CompetitorsEventTest.html deleted file mode 100644 index b31f32ef753..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/CompetitorsEventTest.html +++ /dev/null @@ -1,367 +0,0 @@ - - - - - -CompetitorsEventTest - - - - - - - - - - - -
          -
          com.tractrac.subscription.lib.api.competitor
          -

          Class CompetitorsEventTest

          -
          -
          - -
          - -
          -
          - -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                CompetitorsEventTest

                -
                public CompetitorsEventTest()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                setUp

                -
                public void setUp()
                -
                -
                Overrides:
                -
                setUp in class junit.framework.TestCase
                -
                -
              • -
              - - - -
                -
              • -

                testAddCompetitor

                -
                public void testAddCompetitor()
                -                       throws java.io.IOException,
                -                              java.net.URISyntaxException,
                -                              java.lang.InterruptedException
                -
                Throws:
                -
                java.io.IOException
                -
                java.net.URISyntaxException
                -
                java.lang.InterruptedException
                -
              • -
              - - - -
                -
              • -

                testRemoveCompetitor

                -
                public void testRemoveCompetitor()
                -                          throws java.io.IOException,
                -                                 java.net.URISyntaxException,
                -                                 java.lang.InterruptedException
                -
                Throws:
                -
                java.io.IOException
                -
                java.net.URISyntaxException
                -
                java.lang.InterruptedException
                -
              • -
              - - - -
                -
              • -

                testUpdateCompetitor

                -
                public void testUpdateCompetitor()
                -                          throws java.io.IOException,
                -                                 java.net.URISyntaxException,
                -                                 java.lang.InterruptedException
                -
                Throws:
                -
                java.io.IOException
                -
                java.net.URISyntaxException
                -
                java.lang.InterruptedException
                -
              • -
              -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/ICompetitorsListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/ICompetitorsListener.html index 0bebc51561e..ee1f878cf27 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/ICompetitorsListener.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/ICompetitorsListener.html @@ -1,10 +1,10 @@ - + - + ICompetitorsListener - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/IPositionListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/IPositionListener.html index 5a9f996f95a..eed71f3eee3 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/IPositionListener.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/IPositionListener.html @@ -1,10 +1,10 @@ - + - + IPositionListener - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/IPositionOffsetListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/IPositionOffsetListener.html index 017b7ec8437..669274acf3f 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/IPositionOffsetListener.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/IPositionOffsetListener.html @@ -1,10 +1,10 @@ - + - + IPositionOffsetListener - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/IPositionSnappedListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/IPositionSnappedListener.html index 78c590205cb..597841f58ef 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/IPositionSnappedListener.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/IPositionSnappedListener.html @@ -1,10 +1,10 @@ - + - + IPositionSnappedListener - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/PositionEventTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/PositionEventTest.html deleted file mode 100644 index 883c1809bfb..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/PositionEventTest.html +++ /dev/null @@ -1,340 +0,0 @@ - - - - - -PositionEventTest - - - - - - - - - - - -
          -
          com.tractrac.subscription.lib.api.competitor
          -

          Class PositionEventTest

          -
          -
          - -
          - -
          -
          - -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                PositionEventTest

                -
                public PositionEventTest()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                setUp

                -
                public void setUp()
                -
                -
                Overrides:
                -
                setUp in class junit.framework.TestCase
                -
                -
              • -
              - - - -
                -
              • -

                testModel

                -
                public void testModel()
                -
              • -
              - - - -
                -
              • -

                testStoredPositions

                -
                public void testStoredPositions()
                -                         throws java.io.IOException,
                -                                java.net.URISyntaxException,
                -                                java.lang.InterruptedException
                -
                Throws:
                -
                java.io.IOException
                -
                java.net.URISyntaxException
                -
                java.lang.InterruptedException
                -
              • -
              -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/ReconnectAddingEntriesTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/ReconnectAddingEntriesTest.html deleted file mode 100644 index fa63dbd294f..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/ReconnectAddingEntriesTest.html +++ /dev/null @@ -1,311 +0,0 @@ - - - - - -ReconnectAddingEntriesTest - - - - - - - - - - - -
          -
          com.tractrac.subscription.lib.api.competitor
          -

          Class ReconnectAddingEntriesTest

          -
          -
          -
            -
          • java.lang.Object
          • -
          • -
              -
            • junit.framework.Assert
            • -
            • -
                -
              • junit.framework.TestCase
              • -
              • -
                  -
                • com.tractrac.subscription.lib.api.competitor.ReconnectAddingEntriesTest
                • -
                -
              • -
              -
            • -
            -
          • -
          -
          -
            -
          • -
            -
            All Implemented Interfaces:
            -
            junit.framework.Test
            -
            -
            -
            -
            public abstract class ReconnectAddingEntriesTest
            -extends junit.framework.TestCase
            -
            Author:
            -
            Jorge Piera Llodrá
            -
          • -
          -
          -
          -
            -
          • - - - -
              -
            • - - -

              Method Summary

              - - - - - - - - - - - - - - -
              Methods 
              Modifier and TypeMethod and Description
              voidsetUp() 
              voidtestRemoveAndAddCompetitor() 
              -
                -
              • - - -

                Methods inherited from class junit.framework.TestCase

                -assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertFalse, assertFalse, assertNotNull, assertNotNull, assertNotSame, assertNotSame, assertNull, assertNull, assertSame, assertSame, assertTrue, assertTrue, countTestCases, fail, fail, failNotEquals, failNotSame, failSame, format, getName, run, run, runBare, setName, toString
              • -
              -
                -
              • - - -

                Methods inherited from class java.lang.Object

                -equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
              • -
              -
            • -
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                ReconnectAddingEntriesTest

                -
                public ReconnectAddingEntriesTest()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                setUp

                -
                public void setUp()
                -           throws java.net.MalformedURLException,
                -                  java.io.IOException,
                -                  java.net.URISyntaxException
                -
                -
                Overrides:
                -
                setUp in class junit.framework.TestCase
                -
                Throws:
                -
                java.net.MalformedURLException
                -
                java.io.IOException
                -
                java.net.URISyntaxException
                -
              • -
              - - - - -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/class-use/CompetitorsEventTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/class-use/CompetitorsEventTest.html deleted file mode 100644 index 6a48bde0e80..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/class-use/CompetitorsEventTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.subscription.lib.api.competitor.CompetitorsEventTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.subscription.lib.api.competitor.CompetitorsEventTest

          -
          -
          No usage of com.tractrac.subscription.lib.api.competitor.CompetitorsEventTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/class-use/ICompetitorsListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/class-use/ICompetitorsListener.html deleted file mode 100644 index 77635a0cebc..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/class-use/ICompetitorsListener.html +++ /dev/null @@ -1,168 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.competitor.ICompetitorsListener - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.competitor.ICompetitorsListener

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/class-use/IPositionListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/class-use/IPositionListener.html deleted file mode 100644 index 19a4007734d..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/class-use/IPositionListener.html +++ /dev/null @@ -1,174 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.competitor.IPositionListener - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.competitor.IPositionListener

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/class-use/IPositionOffsetListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/class-use/IPositionOffsetListener.html deleted file mode 100644 index d5dbec552de..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/class-use/IPositionOffsetListener.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.competitor.IPositionOffsetListener - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.competitor.IPositionOffsetListener

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/class-use/IPositionSnappedListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/class-use/IPositionSnappedListener.html deleted file mode 100644 index a30684e1f0a..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/class-use/IPositionSnappedListener.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.competitor.IPositionSnappedListener - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.competitor.IPositionSnappedListener

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/class-use/PositionEventTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/class-use/PositionEventTest.html deleted file mode 100644 index 5007627c951..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/class-use/PositionEventTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.subscription.lib.api.competitor.PositionEventTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.subscription.lib.api.competitor.PositionEventTest

          -
          -
          No usage of com.tractrac.subscription.lib.api.competitor.PositionEventTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/class-use/ReconnectAddingEntriesTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/class-use/ReconnectAddingEntriesTest.html deleted file mode 100644 index 4e9835feed5..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/class-use/ReconnectAddingEntriesTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.subscription.lib.api.competitor.ReconnectAddingEntriesTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.subscription.lib.api.competitor.ReconnectAddingEntriesTest

          -
          -
          No usage of com.tractrac.subscription.lib.api.competitor.ReconnectAddingEntriesTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/package-frame.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/package-frame.html index e44246f097a..167593bf289 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/package-frame.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/package-frame.html @@ -1,10 +1,10 @@ - + - + com.tractrac.subscription.lib.api.competitor - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/package-summary.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/package-summary.html index a8458a71635..54d0e00f21c 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/package-summary.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/package-summary.html @@ -1,10 +1,10 @@ - + - + com.tractrac.subscription.lib.api.competitor - + @@ -72,8 +72,7 @@

          Package com.tractrac.subscription.lib.api.competitor

          - Subscriptions related with competitors and positions -
          + Subscriptions related with competitors and positions

          See: Description

          diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/package-tree.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/package-tree.html index cd8b678e8c5..5f26cdd010b 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/package-tree.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/package-tree.html @@ -1,10 +1,10 @@ - + - + com.tractrac.subscription.lib.api.competitor Class Hierarchy - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/package-use.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/package-use.html deleted file mode 100644 index ebc26dcc191..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/competitor/package-use.html +++ /dev/null @@ -1,172 +0,0 @@ - - - - - -Uses of Package com.tractrac.subscription.lib.api.competitor - - - - - - - - - - -
          -

          Uses of Package
          com.tractrac.subscription.lib.api.competitor

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/ControlsEventTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/ControlsEventTest.html deleted file mode 100644 index a4365dba0cc..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/ControlsEventTest.html +++ /dev/null @@ -1,367 +0,0 @@ - - - - - -ControlsEventTest - - - - - - - - - - - -
          -
          com.tractrac.subscription.lib.api.control
          -

          Class ControlsEventTest

          -
          -
          - -
          - -
          -
          - -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                ControlsEventTest

                -
                public ControlsEventTest()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                setUp

                -
                public void setUp()
                -
                -
                Overrides:
                -
                setUp in class junit.framework.TestCase
                -
                -
              • -
              - - - -
                -
              • -

                testAddMark

                -
                public void testAddMark()
                -                 throws java.io.IOException,
                -                        java.net.URISyntaxException,
                -                        java.lang.InterruptedException
                -
                Throws:
                -
                java.io.IOException
                -
                java.net.URISyntaxException
                -
                java.lang.InterruptedException
                -
              • -
              - - - -
                -
              • -

                testRemoveMark

                -
                public void testRemoveMark()
                -                    throws java.io.IOException,
                -                           java.net.URISyntaxException,
                -                           java.lang.InterruptedException
                -
                Throws:
                -
                java.io.IOException
                -
                java.net.URISyntaxException
                -
                java.lang.InterruptedException
                -
              • -
              - - - -
                -
              • -

                testUpdateMark

                -
                public void testUpdateMark()
                -                    throws java.io.IOException,
                -                           java.net.URISyntaxException,
                -                           java.lang.InterruptedException
                -
                Throws:
                -
                java.io.IOException
                -
                java.net.URISyntaxException
                -
                java.lang.InterruptedException
                -
              • -
              -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/IControlPassingsListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/IControlPassingsListener.html index 6dda17af95e..57e83f85585 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/IControlPassingsListener.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/IControlPassingsListener.html @@ -1,10 +1,10 @@ - + - + IControlPassingsListener - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/IControlPointPositionListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/IControlPointPositionListener.html index c3c5be5af92..069a9f35849 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/IControlPointPositionListener.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/IControlPointPositionListener.html @@ -1,10 +1,10 @@ - + - + IControlPointPositionListener - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/IControlRouteChangeListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/IControlRouteChangeListener.html index b75dd5e083c..cbae5c8c37c 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/IControlRouteChangeListener.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/IControlRouteChangeListener.html @@ -1,10 +1,10 @@ - + - + IControlRouteChangeListener - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/IControlsListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/IControlsListener.html index ab1e94609e7..1fe06f0f40c 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/IControlsListener.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/IControlsListener.html @@ -1,10 +1,10 @@ - + - + IControlsListener - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/ReconnectAddingControlsTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/ReconnectAddingControlsTest.html deleted file mode 100644 index ed4cb76528f..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/ReconnectAddingControlsTest.html +++ /dev/null @@ -1,311 +0,0 @@ - - - - - -ReconnectAddingControlsTest - - - - - - - - - - - -
          -
          com.tractrac.subscription.lib.api.control
          -

          Class ReconnectAddingControlsTest

          -
          -
          -
            -
          • java.lang.Object
          • -
          • -
              -
            • junit.framework.Assert
            • -
            • -
                -
              • junit.framework.TestCase
              • -
              • -
                  -
                • com.tractrac.subscription.lib.api.control.ReconnectAddingControlsTest
                • -
                -
              • -
              -
            • -
            -
          • -
          -
          -
            -
          • -
            -
            All Implemented Interfaces:
            -
            junit.framework.Test
            -
            -
            -
            -
            public abstract class ReconnectAddingControlsTest
            -extends junit.framework.TestCase
            -
            Author:
            -
            Jorge Piera Llodrá
            -
          • -
          -
          -
          -
            -
          • - - - -
              -
            • - - -

              Method Summary

              - - - - - - - - - - - - - - -
              Methods 
              Modifier and TypeMethod and Description
              voidsetUp() 
              voidtestChangeControlCompetitor() 
              -
                -
              • - - -

                Methods inherited from class junit.framework.TestCase

                -assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertFalse, assertFalse, assertNotNull, assertNotNull, assertNotSame, assertNotSame, assertNull, assertNull, assertSame, assertSame, assertTrue, assertTrue, countTestCases, fail, fail, failNotEquals, failNotSame, failSame, format, getName, run, run, runBare, setName, toString
              • -
              -
                -
              • - - -

                Methods inherited from class java.lang.Object

                -equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
              • -
              -
            • -
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                ReconnectAddingControlsTest

                -
                public ReconnectAddingControlsTest()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                setUp

                -
                public void setUp()
                -           throws java.net.MalformedURLException,
                -                  java.io.IOException,
                -                  java.net.URISyntaxException
                -
                -
                Overrides:
                -
                setUp in class junit.framework.TestCase
                -
                Throws:
                -
                java.net.MalformedURLException
                -
                java.io.IOException
                -
                java.net.URISyntaxException
                -
              • -
              - - - - -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/ReconnectWithStaticControlsTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/ReconnectWithStaticControlsTest.html deleted file mode 100644 index 5a856373f81..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/ReconnectWithStaticControlsTest.html +++ /dev/null @@ -1,311 +0,0 @@ - - - - - -ReconnectWithStaticControlsTest - - - - - - - - - - - -
          -
          com.tractrac.subscription.lib.api.control
          -

          Class ReconnectWithStaticControlsTest

          -
          -
          -
            -
          • java.lang.Object
          • -
          • -
              -
            • junit.framework.Assert
            • -
            • -
                -
              • junit.framework.TestCase
              • -
              • -
                  -
                • com.tractrac.subscription.lib.api.control.ReconnectWithStaticControlsTest
                • -
                -
              • -
              -
            • -
            -
          • -
          -
          -
            -
          • -
            -
            All Implemented Interfaces:
            -
            junit.framework.Test
            -
            -
            -
            -
            public abstract class ReconnectWithStaticControlsTest
            -extends junit.framework.TestCase
            -
            Author:
            -
            Jorge Piera Llodrá
            -
          • -
          -
          -
          -
            -
          • - - - -
              -
            • - - -

              Method Summary

              - - - - - - - - - - - - - - -
              Methods 
              Modifier and TypeMethod and Description
              voidsetUp() 
              voidtestStaticPointChanged() 
              -
                -
              • - - -

                Methods inherited from class junit.framework.TestCase

                -assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertFalse, assertFalse, assertNotNull, assertNotNull, assertNotSame, assertNotSame, assertNull, assertNull, assertSame, assertSame, assertTrue, assertTrue, countTestCases, fail, fail, failNotEquals, failNotSame, failSame, format, getName, run, run, runBare, setName, toString
              • -
              -
                -
              • - - -

                Methods inherited from class java.lang.Object

                -equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
              • -
              -
            • -
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                ReconnectWithStaticControlsTest

                -
                public ReconnectWithStaticControlsTest()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                setUp

                -
                public void setUp()
                -           throws java.net.MalformedURLException,
                -                  java.io.IOException,
                -                  java.net.URISyntaxException
                -
                -
                Overrides:
                -
                setUp in class junit.framework.TestCase
                -
                Throws:
                -
                java.net.MalformedURLException
                -
                java.io.IOException
                -
                java.net.URISyntaxException
                -
              • -
              - - - - -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/StaticControlInSecondRace.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/StaticControlInSecondRace.html deleted file mode 100644 index 1f135ce42e3..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/StaticControlInSecondRace.html +++ /dev/null @@ -1,313 +0,0 @@ - - - - - -StaticControlInSecondRace - - - - - - - - - - - -
          -
          com.tractrac.subscription.lib.api.control
          -

          Class StaticControlInSecondRace

          -
          -
          -
            -
          • java.lang.Object
          • -
          • -
              -
            • junit.framework.Assert
            • -
            • -
                -
              • junit.framework.TestCase
              • -
              • -
                  -
                • com.tractrac.subscription.lib.api.control.StaticControlInSecondRace
                • -
                -
              • -
              -
            • -
            -
          • -
          -
          -
            -
          • -
            -
            All Implemented Interfaces:
            -
            junit.framework.Test
            -
            -
            -
            -
            public abstract class StaticControlInSecondRace
            -extends junit.framework.TestCase
            -
            This test check the values for the static control points when a recond race - is loaded
            -
            Author:
            -
            Jorge Piera Llodrá
            -
          • -
          -
          -
          -
            -
          • - - - -
              -
            • - - -

              Method Summary

              - - - - - - - - - - - - - - -
              Methods 
              Modifier and TypeMethod and Description
              voidsetUp() 
              voidtestStaticControlInSecondRace() 
              -
                -
              • - - -

                Methods inherited from class junit.framework.TestCase

                -assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertEquals, assertFalse, assertFalse, assertNotNull, assertNotNull, assertNotSame, assertNotSame, assertNull, assertNull, assertSame, assertSame, assertTrue, assertTrue, countTestCases, fail, fail, failNotEquals, failNotSame, failSame, format, getName, run, run, runBare, setName, toString
              • -
              -
                -
              • - - -

                Methods inherited from class java.lang.Object

                -equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
              • -
              -
            • -
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                StaticControlInSecondRace

                -
                public StaticControlInSecondRace()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                setUp

                -
                public void setUp()
                -           throws java.net.MalformedURLException,
                -                  java.io.IOException,
                -                  java.net.URISyntaxException
                -
                -
                Overrides:
                -
                setUp in class junit.framework.TestCase
                -
                Throws:
                -
                java.net.MalformedURLException
                -
                java.io.IOException
                -
                java.net.URISyntaxException
                -
              • -
              - - - - -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/StaticControlTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/StaticControlTest.html deleted file mode 100644 index 3df9ad902a0..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/StaticControlTest.html +++ /dev/null @@ -1,329 +0,0 @@ - - - - - -StaticControlTest - - - - - - - - - - - -
          -
          com.tractrac.subscription.lib.api.control
          -

          Class StaticControlTest

          -
          -
          - -
          -
            -
          • -
            -
            All Implemented Interfaces:
            -
            junit.framework.Test
            -
            -
            -
            -
            public abstract class StaticControlTest
            -extends AbstractSubscriptionTest
            -

            - This test checks that the subscription library sends events with the - positions of the static marks -

            -
            Author:
            -
            Jorge Piera Llodrá
            -
          • -
          -
          -
          - -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                StaticControlTest

                -
                public StaticControlTest()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                setUp

                -
                public void setUp()
                -
                -
                Overrides:
                -
                setUp in class junit.framework.TestCase
                -
                -
              • -
              - - - -
                -
              • -

                testStaticMark

                -
                public void testStaticMark()
                -                    throws java.io.IOException,
                -                           java.net.URISyntaxException
                -
                Throws:
                -
                java.io.IOException
                -
                java.net.URISyntaxException
                -
              • -
              -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/StaticRoutesChangesTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/StaticRoutesChangesTest.html deleted file mode 100644 index fd0a90ae092..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/StaticRoutesChangesTest.html +++ /dev/null @@ -1,325 +0,0 @@ - - - - - -StaticRoutesChangesTest - - - - - - - - - - - -
          -
          com.tractrac.subscription.lib.api.control
          -

          Class StaticRoutesChangesTest

          -
          -
          - -
          - -
          -
          - -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                StaticRoutesChangesTest

                -
                public StaticRoutesChangesTest()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                setUp

                -
                public void setUp()
                -
                -
                Overrides:
                -
                setUp in class junit.framework.TestCase
                -
                -
              • -
              - - - -
                -
              • -

                testStaticRoute

                -
                public void testStaticRoute()
                -                     throws java.io.IOException,
                -                            java.net.URISyntaxException
                -
                Throws:
                -
                java.io.IOException
                -
                java.net.URISyntaxException
                -
              • -
              -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/ControlsEventTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/ControlsEventTest.html deleted file mode 100644 index 6ace47e1550..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/ControlsEventTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.subscription.lib.api.control.ControlsEventTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.subscription.lib.api.control.ControlsEventTest

          -
          -
          No usage of com.tractrac.subscription.lib.api.control.ControlsEventTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/IControlPassingsListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/IControlPassingsListener.html deleted file mode 100644 index 054ff2a43a7..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/IControlPassingsListener.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.control.IControlPassingsListener - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.control.IControlPassingsListener

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/IControlPointPositionListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/IControlPointPositionListener.html deleted file mode 100644 index b1f1b317ec9..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/IControlPointPositionListener.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.control.IControlPointPositionListener - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.control.IControlPointPositionListener

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/IControlRouteChangeListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/IControlRouteChangeListener.html deleted file mode 100644 index ab603a24f71..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/IControlRouteChangeListener.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.control.IControlRouteChangeListener - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.control.IControlRouteChangeListener

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/IControlsListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/IControlsListener.html deleted file mode 100644 index 6f6ef36f89b..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/IControlsListener.html +++ /dev/null @@ -1,168 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.control.IControlsListener - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.control.IControlsListener

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/ReconnectAddingControlsTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/ReconnectAddingControlsTest.html deleted file mode 100644 index d094b2d89a8..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/ReconnectAddingControlsTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.subscription.lib.api.control.ReconnectAddingControlsTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.subscription.lib.api.control.ReconnectAddingControlsTest

          -
          -
          No usage of com.tractrac.subscription.lib.api.control.ReconnectAddingControlsTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/ReconnectWithStaticControlsTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/ReconnectWithStaticControlsTest.html deleted file mode 100644 index 8e4ac426920..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/ReconnectWithStaticControlsTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.subscription.lib.api.control.ReconnectWithStaticControlsTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.subscription.lib.api.control.ReconnectWithStaticControlsTest

          -
          -
          No usage of com.tractrac.subscription.lib.api.control.ReconnectWithStaticControlsTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/StaticControlInSecondRace.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/StaticControlInSecondRace.html deleted file mode 100644 index 5f77851381d..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/StaticControlInSecondRace.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.subscription.lib.api.control.StaticControlInSecondRace - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.subscription.lib.api.control.StaticControlInSecondRace

          -
          -
          No usage of com.tractrac.subscription.lib.api.control.StaticControlInSecondRace
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/StaticControlTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/StaticControlTest.html deleted file mode 100644 index e7c1745b590..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/StaticControlTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.subscription.lib.api.control.StaticControlTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.subscription.lib.api.control.StaticControlTest

          -
          -
          No usage of com.tractrac.subscription.lib.api.control.StaticControlTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/StaticRoutesChangesTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/StaticRoutesChangesTest.html deleted file mode 100644 index dfe3726a2bc..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/class-use/StaticRoutesChangesTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.subscription.lib.api.control.StaticRoutesChangesTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.subscription.lib.api.control.StaticRoutesChangesTest

          -
          -
          No usage of com.tractrac.subscription.lib.api.control.StaticRoutesChangesTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/package-frame.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/package-frame.html index e940acbc5ce..749fbc70473 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/package-frame.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/package-frame.html @@ -1,10 +1,10 @@ - + - + com.tractrac.subscription.lib.api.control - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/package-summary.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/package-summary.html index 30337decb43..da1dfb16334 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/package-summary.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/package-summary.html @@ -1,10 +1,10 @@ - + - + com.tractrac.subscription.lib.api.control - + @@ -72,8 +72,7 @@

          Package com.tractrac.subscription.lib.api.control

          - Subscriptions related with the controls and control passings -
          + Subscriptions related with the controls and control passings

          See: Description

          diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/package-tree.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/package-tree.html index 390546e2420..ea2a323efc3 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/package-tree.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/package-tree.html @@ -1,10 +1,10 @@ - + - + com.tractrac.subscription.lib.api.control Class Hierarchy - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/package-use.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/package-use.html deleted file mode 100644 index 9cdb9010eaf..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/control/package-use.html +++ /dev/null @@ -1,171 +0,0 @@ - - - - - -Uses of Package com.tractrac.subscription.lib.api.control - - - - - - - - - - -
          -

          Uses of Package
          com.tractrac.subscription.lib.api.control

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IConnectionStatusListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IConnectionStatusListener.html index c2944291d3a..3ceeebd1458 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IConnectionStatusListener.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IConnectionStatusListener.html @@ -1,10 +1,10 @@ - + - + IConnectionStatusListener - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IEventMessageListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IEventMessageListener.html index 1de57ff1e5c..85d7e1f0a26 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IEventMessageListener.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IEventMessageListener.html @@ -1,10 +1,10 @@ - + - + IEventMessageListener - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/ILiveDataEvent.StatusType.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/ILiveDataEvent.StatusType.html index 75712f3101a..c34bb834c25 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/ILiveDataEvent.StatusType.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/ILiveDataEvent.StatusType.html @@ -1,10 +1,10 @@ - + - + ILiveDataEvent.StatusType - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/ILiveDataEvent.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/ILiveDataEvent.html index 7df8f6a27bc..5bd6b7b5cbb 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/ILiveDataEvent.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/ILiveDataEvent.html @@ -1,10 +1,10 @@ - + - + ILiveDataEvent - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IMessage.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IMessage.html index 750f0400bbf..3c987828bc5 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IMessage.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IMessage.html @@ -1,10 +1,10 @@ - + - + IMessage - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IServerTimeListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IServerTimeListener.html index e840a8bad1f..12f1696a7d7 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IServerTimeListener.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IServerTimeListener.html @@ -1,10 +1,10 @@ - + - + IServerTimeListener - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IStoredDataEvent.Type.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IStoredDataEvent.Type.html index 0d142e5b151..bb19e39f599 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IStoredDataEvent.Type.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IStoredDataEvent.Type.html @@ -1,10 +1,10 @@ - + - + IStoredDataEvent.Type - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IStoredDataEvent.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IStoredDataEvent.html index 0b836f58c84..04424564b3d 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IStoredDataEvent.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/IStoredDataEvent.html @@ -1,10 +1,10 @@ - + - + IStoredDataEvent - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/IConnectionStatusListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/IConnectionStatusListener.html deleted file mode 100644 index 7bb3ab7475d..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/IConnectionStatusListener.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.event.IConnectionStatusListener - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.event.IConnectionStatusListener

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/IEventMessageListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/IEventMessageListener.html deleted file mode 100644 index be886a4e749..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/IEventMessageListener.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.event.IEventMessageListener - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.event.IEventMessageListener

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/ILiveDataEvent.StatusType.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/ILiveDataEvent.StatusType.html deleted file mode 100644 index e01ece077ea..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/ILiveDataEvent.StatusType.html +++ /dev/null @@ -1,173 +0,0 @@ - - - - - -Uses of Class com.tractrac.subscription.lib.api.event.ILiveDataEvent.StatusType - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.subscription.lib.api.event.ILiveDataEvent.StatusType

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/ILiveDataEvent.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/ILiveDataEvent.html deleted file mode 100644 index 167deec9402..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/ILiveDataEvent.html +++ /dev/null @@ -1,160 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.event.ILiveDataEvent - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.event.ILiveDataEvent

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/IMessage.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/IMessage.html deleted file mode 100644 index 535eda043da..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/IMessage.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.event.IMessage - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.event.IMessage

          -
          -
          No usage of com.tractrac.subscription.lib.api.event.IMessage
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/IServerTimeListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/IServerTimeListener.html deleted file mode 100644 index 89501cc6100..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/IServerTimeListener.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.event.IServerTimeListener - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.event.IServerTimeListener

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/IStoredDataEvent.Type.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/IStoredDataEvent.Type.html deleted file mode 100644 index 252e207cd2e..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/IStoredDataEvent.Type.html +++ /dev/null @@ -1,173 +0,0 @@ - - - - - -Uses of Class com.tractrac.subscription.lib.api.event.IStoredDataEvent.Type - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.subscription.lib.api.event.IStoredDataEvent.Type

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/IStoredDataEvent.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/IStoredDataEvent.html deleted file mode 100644 index f4650340efb..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/class-use/IStoredDataEvent.html +++ /dev/null @@ -1,160 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.event.IStoredDataEvent - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.event.IStoredDataEvent

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/package-frame.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/package-frame.html index 3be3da25770..a91ed9e5ed3 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/package-frame.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/package-frame.html @@ -1,10 +1,10 @@ - + - + com.tractrac.subscription.lib.api.event - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/package-summary.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/package-summary.html index fcb652fa803..d93e85a3452 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/package-summary.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/package-summary.html @@ -1,10 +1,10 @@ - + - + com.tractrac.subscription.lib.api.event - + @@ -72,8 +72,7 @@

          Package com.tractrac.subscription.lib.api.event

          - Subscriptions related with the event and/or the connection -
          + Subscriptions related with the event and/or the connection

          See: Description

          @@ -106,32 +105,28 @@ ILiveDataEvent
          - This event is used to monitorize the status of the live connection -
          + This event is used to monitorize the status of the live connection IMessage
          - General message associated to an event -
          + General message associated to an event IServerTimeListener
          - Listener used to listen the serever time -
          + Listener used to listen the serever time IStoredDataEvent
          - This event is used to monitorize the status of the stored connection -
          + This event is used to monitorize the status of the stored connection diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/package-tree.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/package-tree.html index 142dd7da8fc..449113cd9c9 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/package-tree.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/package-tree.html @@ -1,10 +1,10 @@ - + - + com.tractrac.subscription.lib.api.event Class Hierarchy - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/package-use.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/package-use.html deleted file mode 100644 index 759e88613c2..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/event/package-use.html +++ /dev/null @@ -1,208 +0,0 @@ - - - - - -Uses of Package com.tractrac.subscription.lib.api.event - - - - - - - - - - -
          -

          Uses of Package
          com.tractrac.subscription.lib.api.event

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/package-frame.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/package-frame.html index 9913545bfb1..e497de420cd 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/package-frame.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/package-frame.html @@ -1,10 +1,10 @@ - + - + com.tractrac.subscription.lib.api - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/package-summary.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/package-summary.html index fc645a0ddc2..1a5ffc90063 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/package-summary.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/package-summary.html @@ -1,10 +1,10 @@ - + - + com.tractrac.subscription.lib.api - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/package-tree.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/package-tree.html index fcafa02fd67..2d789fc5b6c 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/package-tree.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/package-tree.html @@ -1,10 +1,10 @@ - + - + com.tractrac.subscription.lib.api Class Hierarchy - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/package-use.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/package-use.html deleted file mode 100644 index a98dd0d8c3e..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/package-use.html +++ /dev/null @@ -1,304 +0,0 @@ - - - - - -Uses of Package com.tractrac.subscription.lib.api - - - - - - - - - - -
          -

          Uses of Package
          com.tractrac.subscription.lib.api

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/IRaceCompetitorListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/IRaceCompetitorListener.html index 22f10e573eb..d434ac786ca 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/IRaceCompetitorListener.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/IRaceCompetitorListener.html @@ -1,10 +1,10 @@ - + - + IRaceCompetitorListener - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/IRaceMessageListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/IRaceMessageListener.html index 9e41953059a..5c37d63ac20 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/IRaceMessageListener.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/IRaceMessageListener.html @@ -1,10 +1,10 @@ - + - + IRaceMessageListener - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/IRaceStartStopTimesChangeListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/IRaceStartStopTimesChangeListener.html index fbdcfcab4b8..4fbc1dad2e6 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/IRaceStartStopTimesChangeListener.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/IRaceStartStopTimesChangeListener.html @@ -1,10 +1,10 @@ - + - + IRaceStartStopTimesChangeListener - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/IRacesListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/IRacesListener.html index de71b4749b8..29e60653ad1 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/IRacesListener.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/IRacesListener.html @@ -1,10 +1,10 @@ - + - + IRacesListener - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/IStartStopTimesChangeListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/IStartStopTimesChangeListener.html index ebfdc859bf2..722a7016e8f 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/IStartStopTimesChangeListener.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/IStartStopTimesChangeListener.html @@ -1,10 +1,10 @@ - + - + IStartStopTimesChangeListener - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/RaceCompetitorTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/RaceCompetitorTest.html deleted file mode 100644 index 0b1d8d2b90d..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/RaceCompetitorTest.html +++ /dev/null @@ -1,330 +0,0 @@ - - - - - -RaceCompetitorTest - - - - - - - - - - - -
          -
          com.tractrac.subscription.lib.api.race
          -

          Class RaceCompetitorTest

          -
          -
          - -
          - -
          -
          - -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                RaceCompetitorTest

                -
                public RaceCompetitorTest()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                testAddCompetitor

                -
                public void testAddCompetitor()
                -                       throws java.io.IOException,
                -                              java.net.URISyntaxException,
                -                              java.lang.InterruptedException
                -
                Throws:
                -
                java.io.IOException
                -
                java.net.URISyntaxException
                -
                java.lang.InterruptedException
                -
              • -
              - - - -
                -
              • -

                testRemoveCompetitor

                -
                public void testRemoveCompetitor()
                -                          throws java.io.IOException,
                -                                 java.net.URISyntaxException,
                -                                 java.lang.InterruptedException
                -
                Throws:
                -
                java.io.IOException
                -
                java.net.URISyntaxException
                -
                java.lang.InterruptedException
                -
              • -
              -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/RacesEventTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/RacesEventTest.html deleted file mode 100644 index 6d96caaee3a..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/RacesEventTest.html +++ /dev/null @@ -1,407 +0,0 @@ - - - - - -RacesEventTest - - - - - - - - - - - -
          -
          com.tractrac.subscription.lib.api.race
          -

          Class RacesEventTest

          -
          -
          - -
          - -
          -
          - -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                RacesEventTest

                -
                public RacesEventTest()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                setUp

                -
                public void setUp()
                -
                -
                Overrides:
                -
                setUp in class junit.framework.TestCase
                -
                -
              • -
              - - - -
                -
              • -

                testAddRace

                -
                public void testAddRace()
                -                 throws java.io.IOException,
                -                        java.net.URISyntaxException,
                -                        java.lang.InterruptedException
                -
                Throws:
                -
                java.io.IOException
                -
                java.net.URISyntaxException
                -
                java.lang.InterruptedException
                -
              • -
              - - - -
                -
              • -

                testRemoveRace

                -
                public void testRemoveRace()
                -                    throws java.io.IOException,
                -                           java.net.URISyntaxException,
                -                           java.lang.InterruptedException
                -
                Throws:
                -
                java.io.IOException
                -
                java.net.URISyntaxException
                -
                java.lang.InterruptedException
                -
              • -
              - - - -
                -
              • -

                testUpdateRace

                -
                public void testUpdateRace()
                -                    throws java.io.IOException,
                -                           java.net.URISyntaxException,
                -                           java.lang.InterruptedException
                -
                Throws:
                -
                java.io.IOException
                -
                java.net.URISyntaxException
                -
                java.lang.InterruptedException
                -
              • -
              - - - -
                -
              • -

                testReloadRace

                -
                public void testReloadRace()
                -                    throws java.io.IOException,
                -                           java.net.URISyntaxException,
                -                           java.lang.InterruptedException
                -
                Throws:
                -
                java.io.IOException
                -
                java.net.URISyntaxException
                -
                java.lang.InterruptedException
                -
              • -
              - - - -
                -
              • -

                testAbandonRace

                -
                public void testAbandonRace()
                -                     throws java.io.IOException,
                -                            java.net.URISyntaxException,
                -                            java.lang.InterruptedException
                -
                Throws:
                -
                java.io.IOException
                -
                java.net.URISyntaxException
                -
                java.lang.InterruptedException
                -
              • -
              -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/RacesListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/RacesListener.html deleted file mode 100644 index 542cf74c35f..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/RacesListener.html +++ /dev/null @@ -1,441 +0,0 @@ - - - - - -RacesListener - - - - - - - - - - - -
          -
          com.tractrac.subscription.lib.api.race
          -

          Class RacesListener

          -
          -
          -
            -
          • java.lang.Object
          • -
          • -
              -
            • com.tractrac.subscription.lib.api.race.RacesListener
            • -
            -
          • -
          -
          - -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Summary

              - - - - - - - - -
              Constructors 
              Constructor and Description
              RacesListener(java.util.List<IRace> races) 
              -
            • -
            - -
              -
            • - - -

              Method Summary

              - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
              Methods 
              Modifier and TypeMethod and Description
              voidabandonRace(java.util.UUID raceId) -
              It is thrown when the race has been abandoned.
              -
              voidaddRace(IRace race) -
              A new race has been added to the event.
              -
              voiddeleteRace(java.util.UUID raceId) -
              A race has been removed from the event
              -
              IRacegetAbandonedRace() 
              IRacegetInitializedRace() 
              IRacegetReloadedRace() 
              booleanisUpdateReceived() 
              voidreloadRace(java.util.UUID raceId) -
              - The set of positions or control passings have changed and this race needs - to be reloaded.
              -
              voidstartTracking(java.util.UUID raceId) -
              It is thrown when tracking of a race has started.
              -
              voidupdateRace(IRace race) -
              This event is thrown when a race is updated.
              -
              -
                -
              • - - -

                Methods inherited from class java.lang.Object

                -equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
              • -
              -
            • -
            -
          • -
          -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                RacesListener

                -
                public RacesListener(java.util.List<IRace> races)
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                updateRace

                -
                public void updateRace(IRace race)
                -
                Description copied from interface: IRacesListener
                -
                This event is thrown when a race is updated. I means that any of its static - attributes have changed.
                -
                -
                Specified by:
                -
                updateRace in interface IRacesListener
                -
                Parameters:
                race - the race that has been updated.
                -
              • -
              - - - -
                -
              • -

                addRace

                -
                public void addRace(IRace race)
                -
                Description copied from interface: IRacesListener
                -
                A new race has been added to the event.
                -
                -
                Specified by:
                -
                addRace in interface IRacesListener
                -
                Parameters:
                race - the added race
                -
              • -
              - - - -
                -
              • -

                deleteRace

                -
                public void deleteRace(java.util.UUID raceId)
                -
                Description copied from interface: IRacesListener
                -
                A race has been removed from the event
                -
                -
                Specified by:
                -
                deleteRace in interface IRacesListener
                -
                Parameters:
                raceId - the deleted race identifier
                -
              • -
              - - - -
                -
              • -

                reloadRace

                -
                public void reloadRace(java.util.UUID raceId)
                -
                Description copied from interface: IRacesListener
                -

                - The set of positions or control passings have changed and this race needs - to be reloaded. -

                -

                - This event will be always sent after a recalculation. -

                -
                -
                Specified by:
                -
                reloadRace in interface IRacesListener
                -
                Parameters:
                raceId - the identifier of the race that needs to be reloaded
                -
              • -
              - - - -
                -
              • -

                abandonRace

                -
                public void abandonRace(java.util.UUID raceId)
                -
                Description copied from interface: IRacesListener
                -
                It is thrown when the race has been abandoned. It is necessary to abort the - current subscriptions.
                -
                -
                Specified by:
                -
                abandonRace in interface IRacesListener
                -
                Parameters:
                raceId - the identifier of the race that needs to be abandoned
                -
              • -
              - - - -
                -
              • -

                startTracking

                -
                public void startTracking(java.util.UUID raceId)
                -
                Description copied from interface: IRacesListener
                -
                It is thrown when tracking of a race has started. In practice, it means that - the start tracking time and the end tracking time are valid and the - system will attach positions to this race if they arrive between the - tracking interval
                -
                -
                Specified by:
                -
                startTracking in interface IRacesListener
                -
                Parameters:
                raceId - the identifier of the race which tracking has been initialized
                -
              • -
              - - - -
                -
              • -

                isUpdateReceived

                -
                public boolean isUpdateReceived()
                -
              • -
              - - - -
                -
              • -

                getReloadedRace

                -
                public IRace getReloadedRace()
                -
              • -
              - - - -
                -
              • -

                getAbandonedRace

                -
                public IRace getAbandonedRace()
                -
              • -
              - - - -
                -
              • -

                getInitializedRace

                -
                public IRace getInitializedRace()
                -
              • -
              -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/StaticRaceStartStopTimesChangeTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/StaticRaceStartStopTimesChangeTest.html deleted file mode 100644 index 218cae82226..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/StaticRaceStartStopTimesChangeTest.html +++ /dev/null @@ -1,325 +0,0 @@ - - - - - -StaticRaceStartStopTimesChangeTest - - - - - - - - - - - -
          -
          com.tractrac.subscription.lib.api.race
          -

          Class StaticRaceStartStopTimesChangeTest

          -
          -
          - -
          - -
          -
          - -
          -
          -
            -
          • - -
              -
            • - - -

              Constructor Detail

              - - - -
                -
              • -

                StaticRaceStartStopTimesChangeTest

                -
                public StaticRaceStartStopTimesChangeTest()
                -
              • -
              -
            • -
            - -
              -
            • - - -

              Method Detail

              - - - -
                -
              • -

                setUp

                -
                public void setUp()
                -
                -
                Overrides:
                -
                setUp in class junit.framework.TestCase
                -
                -
              • -
              - - - -
                -
              • -

                testTrackingTimesChange

                -
                public void testTrackingTimesChange()
                -                             throws java.io.IOException,
                -                                    java.net.URISyntaxException
                -
                Throws:
                -
                java.io.IOException
                -
                java.net.URISyntaxException
                -
              • -
              -
            • -
            -
          • -
          -
          -
          - - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/IRaceCompetitorListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/IRaceCompetitorListener.html deleted file mode 100644 index 4cc962bef24..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/IRaceCompetitorListener.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.race.IRaceCompetitorListener - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.race.IRaceCompetitorListener

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/IRaceMessageListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/IRaceMessageListener.html deleted file mode 100644 index 7dadcb94c57..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/IRaceMessageListener.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.race.IRaceMessageListener - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.race.IRaceMessageListener

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/IRaceStartStopTimesChangeListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/IRaceStartStopTimesChangeListener.html deleted file mode 100644 index 655bd110d41..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/IRaceStartStopTimesChangeListener.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.race.IRaceStartStopTimesChangeListener - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.race.IRaceStartStopTimesChangeListener

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/IRacesListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/IRacesListener.html deleted file mode 100644 index 94e39c70430..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/IRacesListener.html +++ /dev/null @@ -1,168 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.race.IRacesListener - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.race.IRacesListener

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/IStartStopTimesChangeListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/IStartStopTimesChangeListener.html deleted file mode 100644 index a874475419c..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/IStartStopTimesChangeListener.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.race.IStartStopTimesChangeListener - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.race.IStartStopTimesChangeListener

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/RaceCompetitorTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/RaceCompetitorTest.html deleted file mode 100644 index 9b7951ba9bd..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/RaceCompetitorTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.subscription.lib.api.race.RaceCompetitorTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.subscription.lib.api.race.RaceCompetitorTest

          -
          -
          No usage of com.tractrac.subscription.lib.api.race.RaceCompetitorTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/RacesEventTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/RacesEventTest.html deleted file mode 100644 index 32c402045cb..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/RacesEventTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.subscription.lib.api.race.RacesEventTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.subscription.lib.api.race.RacesEventTest

          -
          -
          No usage of com.tractrac.subscription.lib.api.race.RacesEventTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/RacesListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/RacesListener.html deleted file mode 100644 index c38aaf7a30f..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/RacesListener.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.subscription.lib.api.race.RacesListener - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.subscription.lib.api.race.RacesListener

          -
          -
          No usage of com.tractrac.subscription.lib.api.race.RacesListener
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/StaticRaceStartStopTimesChangeTest.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/StaticRaceStartStopTimesChangeTest.html deleted file mode 100644 index 27171aee158..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/class-use/StaticRaceStartStopTimesChangeTest.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - -Uses of Class com.tractrac.subscription.lib.api.race.StaticRaceStartStopTimesChangeTest - - - - - - - - - - -
          -

          Uses of Class
          com.tractrac.subscription.lib.api.race.StaticRaceStartStopTimesChangeTest

          -
          -
          No usage of com.tractrac.subscription.lib.api.race.StaticRaceStartStopTimesChangeTest
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/package-frame.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/package-frame.html index f2d2353300e..b4f06f1c7ab 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/package-frame.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/package-frame.html @@ -1,10 +1,10 @@ - + - + com.tractrac.subscription.lib.api.race - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/package-summary.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/package-summary.html index 7a957abe013..338247d0a12 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/package-summary.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/package-summary.html @@ -1,10 +1,10 @@ - + - + com.tractrac.subscription.lib.api.race - + @@ -72,8 +72,7 @@

          Package com.tractrac.subscription.lib.api.race

          - Subscriptions related with the races -
          + Subscriptions related with the races

          See: Description

          @@ -91,17 +90,14 @@ IRaceCompetitorListener
          - This listener is manages the relationship between competitor and race. used - at a IRaceSubscriber level: it means that it is necessary to be - subscribed to an individual race to receive its events.
          + This listener is manages the relationship between competitor and race. IRaceMessageListener
          - General message associated to a race -
          + General message associated to a race diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/package-tree.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/package-tree.html index 27de5104554..d0ce1d03e50 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/package-tree.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/package-tree.html @@ -1,10 +1,10 @@ - + - + com.tractrac.subscription.lib.api.race Class Hierarchy - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/package-use.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/package-use.html deleted file mode 100644 index 85be3216b7c..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/race/package-use.html +++ /dev/null @@ -1,179 +0,0 @@ - - - - - -Uses of Package com.tractrac.subscription.lib.api.race - - - - - - - - - - -
          -

          Uses of Package
          com.tractrac.subscription.lib.api.race

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/route/IRoutesListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/route/IRoutesListener.html index 9ca109dff7d..4808c97a365 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/route/IRoutesListener.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/route/IRoutesListener.html @@ -1,10 +1,10 @@ - + - + IRoutesListener - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/route/class-use/IRoutesListener.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/route/class-use/IRoutesListener.html deleted file mode 100644 index aab2b4ac9bc..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/route/class-use/IRoutesListener.html +++ /dev/null @@ -1,168 +0,0 @@ - - - - - -Uses of Interface com.tractrac.subscription.lib.api.route.IRoutesListener - - - - - - - - - - -
          -

          Uses of Interface
          com.tractrac.subscription.lib.api.route.IRoutesListener

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/route/package-frame.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/route/package-frame.html index 754935dbc93..2371b9fc9a0 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/route/package-frame.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/route/package-frame.html @@ -1,10 +1,10 @@ - + - + com.tractrac.subscription.lib.api.route - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/route/package-summary.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/route/package-summary.html index cea15e6801c..7780900e1f4 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/route/package-summary.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/route/package-summary.html @@ -1,10 +1,10 @@ - + - + com.tractrac.subscription.lib.api.route - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/route/package-tree.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/route/package-tree.html index 9710d3b9dd6..f7464552b65 100755 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/route/package-tree.html +++ b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/route/package-tree.html @@ -1,10 +1,10 @@ - + - + com.tractrac.subscription.lib.api.route Class Hierarchy - + diff --git a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/route/package-use.html b/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/route/package-use.html deleted file mode 100644 index 35565016368..00000000000 --- a/java/com.tractrac.clientmodule/javadoc/com/tractrac/subscription/lib/api/route/package-use.html +++ /dev/null @@ -1,154 +0,0 @@ - - - - - -Uses of Package com.tractrac.subscription.lib.api.route - - - - - - - - - - -
          -

          Uses of Package
          com.tractrac.subscription.lib.api.route

          -
          -
          - -
          - - - - - - diff --git a/java/com.tractrac.clientmodule/javadoc/constant-values.html b/java/com.tractrac.clientmodule/javadoc/constant-values.html index b9da47f6e0b..46752f470ce 100755 --- a/java/com.tractrac.clientmodule/javadoc/constant-values.html +++ b/java/com.tractrac.clientmodule/javadoc/constant-values.html @@ -1,10 +1,10 @@ - + - + Constant Field Values - + diff --git a/java/com.tractrac.clientmodule/javadoc/deprecated-list.html b/java/com.tractrac.clientmodule/javadoc/deprecated-list.html index 8bd7171dbf1..c2519792309 100755 --- a/java/com.tractrac.clientmodule/javadoc/deprecated-list.html +++ b/java/com.tractrac.clientmodule/javadoc/deprecated-list.html @@ -1,10 +1,10 @@ - + - + Deprecated List - + diff --git a/java/com.tractrac.clientmodule/javadoc/help-doc.html b/java/com.tractrac.clientmodule/javadoc/help-doc.html index 91a4812ff24..942bb535ecf 100755 --- a/java/com.tractrac.clientmodule/javadoc/help-doc.html +++ b/java/com.tractrac.clientmodule/javadoc/help-doc.html @@ -1,10 +1,10 @@ - + - + API Help - + diff --git a/java/com.tractrac.clientmodule/javadoc/index-files/index-1.html b/java/com.tractrac.clientmodule/javadoc/index-files/index-1.html index 69c1d62dfb9..411e94a398f 100755 --- a/java/com.tractrac.clientmodule/javadoc/index-files/index-1.html +++ b/java/com.tractrac.clientmodule/javadoc/index-files/index-1.html @@ -1,10 +1,10 @@ - + - + A-Index - + diff --git a/java/com.tractrac.clientmodule/javadoc/index-files/index-10.html b/java/com.tractrac.clientmodule/javadoc/index-files/index-10.html index 167bdb32e1b..e98eeb511a4 100755 --- a/java/com.tractrac.clientmodule/javadoc/index-files/index-10.html +++ b/java/com.tractrac.clientmodule/javadoc/index-files/index-10.html @@ -1,10 +1,10 @@ - + - + L-Index - + @@ -83,12 +83,7 @@
          locateAlong(double) - Method in interface com.tractrac.model.lib.api.spatial.ICoordinateSequence
          -
          Returns the coordinate at the specified m ordinate value - - - It is required that the coordinates has an m ordiante value and is sorted - in order of strictly increasing m ordinate value -
          +
          Returns the coordinate at the specified m ordinate value
          A B C D E F G H I L M N O R S T U V  diff --git a/java/com.tractrac.clientmodule/javadoc/index-files/index-11.html b/java/com.tractrac.clientmodule/javadoc/index-files/index-11.html index 8179882576a..6cc7ac618f8 100755 --- a/java/com.tractrac.clientmodule/javadoc/index-files/index-11.html +++ b/java/com.tractrac.clientmodule/javadoc/index-files/index-11.html @@ -1,10 +1,10 @@ - + - + M-Index - + diff --git a/java/com.tractrac.clientmodule/javadoc/index-files/index-12.html b/java/com.tractrac.clientmodule/javadoc/index-files/index-12.html index 1a0561d7242..3d919678410 100755 --- a/java/com.tractrac.clientmodule/javadoc/index-files/index-12.html +++ b/java/com.tractrac.clientmodule/javadoc/index-files/index-12.html @@ -1,10 +1,10 @@ - + - + N-Index - + diff --git a/java/com.tractrac.clientmodule/javadoc/index-files/index-13.html b/java/com.tractrac.clientmodule/javadoc/index-files/index-13.html index 8b81b0e38b1..5cc1fa54ae7 100755 --- a/java/com.tractrac.clientmodule/javadoc/index-files/index-13.html +++ b/java/com.tractrac.clientmodule/javadoc/index-files/index-13.html @@ -1,10 +1,10 @@ - + - + O-Index - + diff --git a/java/com.tractrac.clientmodule/javadoc/index-files/index-14.html b/java/com.tractrac.clientmodule/javadoc/index-files/index-14.html index 839c4708b28..1509009f265 100755 --- a/java/com.tractrac.clientmodule/javadoc/index-files/index-14.html +++ b/java/com.tractrac.clientmodule/javadoc/index-files/index-14.html @@ -1,10 +1,10 @@ - + - + R-Index - + @@ -80,8 +80,7 @@
          RaceLoadingException - Exception in com.tractrac.model.lib.api.event
          - This exception is thrown when there is an error loading the race -
          + This exception is thrown when there is an error loading the race
          RaceLoadingException(String) - Constructor for exception com.tractrac.model.lib.api.event.RaceLoadingException
           
          diff --git a/java/com.tractrac.clientmodule/javadoc/index-files/index-15.html b/java/com.tractrac.clientmodule/javadoc/index-files/index-15.html index 01b6e0a7472..6c055422c47 100755 --- a/java/com.tractrac.clientmodule/javadoc/index-files/index-15.html +++ b/java/com.tractrac.clientmodule/javadoc/index-files/index-15.html @@ -1,10 +1,10 @@ - + - + S-Index - + @@ -143,11 +143,7 @@
          subscribeConnectionStatus(IConnectionStatusListener) - Method in interface com.tractrac.subscription.lib.api.ISubscriber
          -
          Subscribes for connection status - - - The method returns immediately and the positions are subsequently sent to - the given listener, as they are received.
          +
          Subscribes for connection status
          subscribeControlPassings(IControlPassingsListener) - Method in interface com.tractrac.subscription.lib.api.IRaceSubscriber
          diff --git a/java/com.tractrac.clientmodule/javadoc/index-files/index-16.html b/java/com.tractrac.clientmodule/javadoc/index-files/index-16.html index d937ae846fa..db588164a1e 100755 --- a/java/com.tractrac.clientmodule/javadoc/index-files/index-16.html +++ b/java/com.tractrac.clientmodule/javadoc/index-files/index-16.html @@ -1,10 +1,10 @@ - + - + T-Index - + diff --git a/java/com.tractrac.clientmodule/javadoc/index-files/index-17.html b/java/com.tractrac.clientmodule/javadoc/index-files/index-17.html index d312d292644..a2d1e5021e3 100755 --- a/java/com.tractrac.clientmodule/javadoc/index-files/index-17.html +++ b/java/com.tractrac.clientmodule/javadoc/index-files/index-17.html @@ -1,10 +1,10 @@ - + - + U-Index - + @@ -76,8 +76,7 @@
          unsubscribeCompetitors(ICompetitorsListener) - Method in interface com.tractrac.subscription.lib.api.IEventSubscriber
          - Unsubscribes the competitors subscribers -
          + Unsubscribes the competitors subscribers
          unsubscribeConnectionStatus(IConnectionStatusListener) - Method in interface com.tractrac.subscription.lib.api.ISubscriber
          @@ -94,8 +93,7 @@
          unsubscribeControls(IControlsListener) - Method in interface com.tractrac.subscription.lib.api.IEventSubscriber
          - Unsubscribes the controls subscribers -
          + Unsubscribes the controls subscribers
          unsubscribeEventMessages(IEventMessageListener) - Method in interface com.tractrac.subscription.lib.api.IEventSubscriber
          @@ -128,8 +126,7 @@
          unsubscribeRaces(IRacesListener) - Method in interface com.tractrac.subscription.lib.api.IEventSubscriber
          - Unsubscribes the races subscribers -
          + Unsubscribes the races subscribers
          unsubscribeRaceTimesChanges(IRaceStartStopTimesChangeListener) - Method in interface com.tractrac.subscription.lib.api.IRaceSubscriber
          diff --git a/java/com.tractrac.clientmodule/javadoc/index-files/index-18.html b/java/com.tractrac.clientmodule/javadoc/index-files/index-18.html index ecf8f983fbd..5cac1989c21 100755 --- a/java/com.tractrac.clientmodule/javadoc/index-files/index-18.html +++ b/java/com.tractrac.clientmodule/javadoc/index-files/index-18.html @@ -1,10 +1,10 @@ - + - + V-Index - + diff --git a/java/com.tractrac.clientmodule/javadoc/index-files/index-2.html b/java/com.tractrac.clientmodule/javadoc/index-files/index-2.html index b73dd04c042..0c0464b4de4 100755 --- a/java/com.tractrac.clientmodule/javadoc/index-files/index-2.html +++ b/java/com.tractrac.clientmodule/javadoc/index-files/index-2.html @@ -1,10 +1,10 @@ - + - + B-Index - + diff --git a/java/com.tractrac.clientmodule/javadoc/index-files/index-3.html b/java/com.tractrac.clientmodule/javadoc/index-files/index-3.html index 4ea0250f520..20d49031073 100755 --- a/java/com.tractrac.clientmodule/javadoc/index-files/index-3.html +++ b/java/com.tractrac.clientmodule/javadoc/index-files/index-3.html @@ -1,10 +1,10 @@ - + - + C-Index - + @@ -86,32 +86,27 @@
          com.tractrac.model.lib.api.data - package com.tractrac.model.lib.api.data
          - Contains positions and control passings -
          + Contains positions and control passings
          com.tractrac.model.lib.api.event - package com.tractrac.model.lib.api.event
          - Events, races and competitors -
          + Events, races and competitors
          com.tractrac.model.lib.api.metadata - package com.tractrac.model.lib.api.metadata
          - The metadata functionality -
          + The metadata functionality
          com.tractrac.model.lib.api.route - package com.tractrac.model.lib.api.route
          - The route configuration: controls, paths and routes -
          + The route configuration: controls, paths and routes
          com.tractrac.model.lib.api.spatial - package com.tractrac.model.lib.api.spatial
          - The spatial data -
          + The spatial data
          com.tractrac.subscription.lib.api - package com.tractrac.subscription.lib.api
          @@ -121,26 +116,22 @@
          com.tractrac.subscription.lib.api.competitor - package com.tractrac.subscription.lib.api.competitor
          - Subscriptions related with competitors and positions -
          + Subscriptions related with competitors and positions
          com.tractrac.subscription.lib.api.control - package com.tractrac.subscription.lib.api.control
          - Subscriptions related with the controls and control passings -
          + Subscriptions related with the controls and control passings
          com.tractrac.subscription.lib.api.event - package com.tractrac.subscription.lib.api.event
          - Subscriptions related with the event and/or the connection -
          + Subscriptions related with the event and/or the connection
          com.tractrac.subscription.lib.api.race - package com.tractrac.subscription.lib.api.race
          - Subscriptions related with the races -
          + Subscriptions related with the races
          com.tractrac.subscription.lib.api.route - package com.tractrac.subscription.lib.api.route
           
          @@ -181,11 +172,7 @@
          createEventSubscriber(IEvent, URI, URI) - Method in interface com.tractrac.subscription.lib.api.ISubscriberFactory
          - Returns a subscriber for the event - - - It retrieves data using the liveUri and the storedUri parameters - discarding the internal URIs of the event.
          + Returns a subscriber for the event
          createMetadata(String) - Method in interface com.tractrac.model.lib.api.metadata.IMetadataFactory
          @@ -232,11 +219,7 @@
          Returns a subscriber for the race contained in the provided parameters - file - - - It connects with the liveURI (if exists) and with the storedURI contained - in this parameters file.
          + file
          createRaceSubscriber(IRace, URI, URI) - Method in interface com.tractrac.subscription.lib.api.ISubscriberFactory
          diff --git a/java/com.tractrac.clientmodule/javadoc/index-files/index-4.html b/java/com.tractrac.clientmodule/javadoc/index-files/index-4.html index 5e5e7c442de..271b8131226 100755 --- a/java/com.tractrac.clientmodule/javadoc/index-files/index-4.html +++ b/java/com.tractrac.clientmodule/javadoc/index-files/index-4.html @@ -1,10 +1,10 @@ - + - + D-Index - + @@ -73,6 +73,10 @@

          D

          +
          deleteCache() - Method in interface com.tractrac.model.lib.api.event.IEventFactory
          +
          +
          Deletes all the cached objects that has been created
          +
          deleteCompetitor(UUID) - Method in interface com.tractrac.model.lib.api.event.IEvent
          Delete a competitor from the event
          diff --git a/java/com.tractrac.clientmodule/javadoc/index-files/index-5.html b/java/com.tractrac.clientmodule/javadoc/index-files/index-5.html index da5d985b1b5..8e0480e209f 100755 --- a/java/com.tractrac.clientmodule/javadoc/index-files/index-5.html +++ b/java/com.tractrac.clientmodule/javadoc/index-files/index-5.html @@ -1,10 +1,10 @@ - + - + E-Index - + @@ -76,8 +76,7 @@
          EventType - Enum in com.tractrac.model.lib.api.event
          - The type of the event, according with the EventManager -
          + The type of the event, according with the EventManager
          A B C D E F G H I L M N O R S T U V  diff --git a/java/com.tractrac.clientmodule/javadoc/index-files/index-6.html b/java/com.tractrac.clientmodule/javadoc/index-files/index-6.html index 02013841f53..28ac25d11b6 100755 --- a/java/com.tractrac.clientmodule/javadoc/index-files/index-6.html +++ b/java/com.tractrac.clientmodule/javadoc/index-files/index-6.html @@ -1,10 +1,10 @@ - + - + F-Index - + diff --git a/java/com.tractrac.clientmodule/javadoc/index-files/index-7.html b/java/com.tractrac.clientmodule/javadoc/index-files/index-7.html index eb0673e4e1e..880a1e66321 100755 --- a/java/com.tractrac.clientmodule/javadoc/index-files/index-7.html +++ b/java/com.tractrac.clientmodule/javadoc/index-files/index-7.html @@ -1,10 +1,10 @@ - + - + G-Index - + @@ -111,10 +111,7 @@
          getCompetitors() - Method in interface com.tractrac.model.lib.api.event.ICompetitorClass
          - Get a list of competitors in the class - - - This method is thread-safety: it returns a copy of the list.
          + Get a list of competitors in the class
          getCompetitors() - Method in interface com.tractrac.model.lib.api.event.IEvent
          @@ -142,10 +139,7 @@
          getControls() - Method in interface com.tractrac.model.lib.api.route.IControlRoute
          - The list of controls of a route - - - This method is thread-safety: it returns a copy of the list.
          + The list of controls of a route
          getCoordinate(int) - Method in interface com.tractrac.model.lib.api.spatial.ICoordinateSequence
          @@ -421,10 +415,7 @@
          getRaces() - Method in interface com.tractrac.model.lib.api.event.IRaceSerie
          - Gets the list of races that compose the serie - - - This method is thread-safety: it returns a copy of the list.
          + Gets the list of races that compose the serie
          getRaceSerie() - Method in interface com.tractrac.model.lib.api.event.IRace
          @@ -433,10 +424,7 @@
          getRaceSeries() - Method in interface com.tractrac.model.lib.api.event.IEvent
          - Gets the list of race series of this event - - - This method is thread-safety: it returns a copy of the list.
          + Gets the list of race series of this event
          getRaceStartTime() - Method in interface com.tractrac.model.lib.api.event.IRace
          @@ -459,10 +447,7 @@
          getRoutes() - Method in interface com.tractrac.model.lib.api.event.IRace
          - Gets the routes of this race - - - This method is thread-safety: it returns a copy of the list.
          + Gets the routes of this race
          getSegment() - Method in interface com.tractrac.model.lib.api.route.IPathSegment
          @@ -517,7 +502,17 @@ NO_INFOS: No information available for this race yet ESTIMATED: Race start time is estimated SCHEDULED: Race scheduled to start at the given race start time - START_PHASE: Race start likely at the given race start time. + START_PHASE: Race start likely at the given race start time. + RUNNING: Post 10 minute gun (Start time therefore confirmed) + FINISHING: racing + FINISHED: race completed + POSTPONED: race postponed, restart unknown + CAN_RESTART: Race abandoned, a restart is possible. + ABANDONED: Race abandoned, no restart today. + NO_RACE: No racing today + CANCELLED: No racing today. + NO_DATA: Due to technical problems, no data available for this race. +
          getStatus() - Method in interface com.tractrac.model.lib.api.event.IRaceCompetitor
          diff --git a/java/com.tractrac.clientmodule/javadoc/index-files/index-8.html b/java/com.tractrac.clientmodule/javadoc/index-files/index-8.html index 544f8c5235a..9d9c818eced 100755 --- a/java/com.tractrac.clientmodule/javadoc/index-files/index-8.html +++ b/java/com.tractrac.clientmodule/javadoc/index-files/index-8.html @@ -1,10 +1,10 @@ - + - + H-Index - + diff --git a/java/com.tractrac.clientmodule/javadoc/index-files/index-9.html b/java/com.tractrac.clientmodule/javadoc/index-files/index-9.html index 84a857a9f66..bafdb028614 100755 --- a/java/com.tractrac.clientmodule/javadoc/index-files/index-9.html +++ b/java/com.tractrac.clientmodule/javadoc/index-files/index-9.html @@ -1,10 +1,10 @@ - + - + I-Index - + @@ -161,8 +161,7 @@
          ICoordinateSequence - Interface in com.tractrac.model.lib.api.spatial
          - A sequence of points connected by straight lines (line segments) -
          + A sequence of points connected by straight lines (line segments)
          IEvent - Interface in com.tractrac.model.lib.api.event
          @@ -196,8 +195,7 @@
          ILiveDataEvent - Interface in com.tractrac.subscription.lib.api.event
          - This event is used to monitorize the status of the live connection -
          + This event is used to monitorize the status of the live connection
          ILiveDataEvent.StatusType - Enum in com.tractrac.subscription.lib.api.event
          @@ -206,8 +204,7 @@
          IMessage - Interface in com.tractrac.subscription.lib.api.event
          - General message associated to an event -
          + General message associated to an event
          IMessageData - Interface in com.tractrac.model.lib.api.data
          @@ -298,15 +295,12 @@
          IRaceCompetitorListener - Interface in com.tractrac.subscription.lib.api.race
          - This listener is manages the relationship between competitor and race. used - at a IRaceSubscriber level: it means that it is necessary to be - subscribed to an individual race to receive its events.
          + This listener is manages the relationship between competitor and race.
          IRaceMessageListener - Interface in com.tractrac.subscription.lib.api.race
          - General message associated to a race -
          + General message associated to a race
          IRaceSerie - Interface in com.tractrac.model.lib.api.event
          @@ -332,8 +326,7 @@
          IRoute - Interface in com.tractrac.model.lib.api.route
          - A route can be one of: IControlRoute or IPathRoute. see those - for details.
          + A route can be one of: IControlRoute or IPathRoute.
          IRoutesListener - Interface in com.tractrac.subscription.lib.api.route
          @@ -352,8 +345,7 @@
          IServerTimeListener - Interface in com.tractrac.subscription.lib.api.event
          - Listener used to listen the serever time -
          + Listener used to listen the serever time
          isFavourite() - Method in interface com.tractrac.model.lib.api.event.ICompetitor
          @@ -376,7 +368,7 @@
          isReversed() - Method in interface com.tractrac.model.lib.api.route.IPathSegment
          Flag specifying whether the segment is part of the route in reverse order, - i.e. travelled from end of segment to start of segment.
          + i.e.
          isRunning() - Method in interface com.tractrac.subscription.lib.api.ISubscriber
          @@ -398,8 +390,7 @@
          IStoredDataEvent - Interface in com.tractrac.subscription.lib.api.event
          - This event is used to monitorize the status of the stored connection -
          + This event is used to monitorize the status of the stored connection
          IStoredDataEvent.Type - Enum in com.tractrac.subscription.lib.api.event
          diff --git a/java/com.tractrac.clientmodule/javadoc/index.html b/java/com.tractrac.clientmodule/javadoc/index.html index fba26b23575..3b7d8daa7b6 100755 --- a/java/com.tractrac.clientmodule/javadoc/index.html +++ b/java/com.tractrac.clientmodule/javadoc/index.html @@ -1,8 +1,8 @@ - + - + Generated Documentation (Untitled) diff --git a/java/com.tractrac.clientmodule/javadoc/overview-summary.html b/java/com.tractrac.clientmodule/javadoc/overview-summary.html index b514db6fb10..03ee2d20972 100755 --- a/java/com.tractrac.clientmodule/javadoc/overview-summary.html +++ b/java/com.tractrac.clientmodule/javadoc/overview-summary.html @@ -1,10 +1,10 @@ - + - + Overview - + @@ -94,40 +94,35 @@ com.tractrac.model.lib.api.data
          - Contains positions and control passings -
          + Contains positions and control passings com.tractrac.model.lib.api.event
          - Events, races and competitors -
          + Events, races and competitors com.tractrac.model.lib.api.metadata
          - The metadata functionality -
          + The metadata functionality com.tractrac.model.lib.api.route
          - The route configuration: controls, paths and routes -
          + The route configuration: controls, paths and routes com.tractrac.model.lib.api.spatial
          - The spatial data -
          + The spatial data @@ -141,32 +136,28 @@ com.tractrac.subscription.lib.api.competitor
          - Subscriptions related with competitors and positions -
          + Subscriptions related with competitors and positions com.tractrac.subscription.lib.api.control
          - Subscriptions related with the controls and control passings -
          + Subscriptions related with the controls and control passings com.tractrac.subscription.lib.api.event
          - Subscriptions related with the event and/or the connection -
          + Subscriptions related with the event and/or the connection com.tractrac.subscription.lib.api.race
          - Subscriptions related with the races -
          + Subscriptions related with the races diff --git a/java/com.tractrac.clientmodule/javadoc/overview-tree.html b/java/com.tractrac.clientmodule/javadoc/overview-tree.html index 90d5a38aebf..cf649ed17e6 100755 --- a/java/com.tractrac.clientmodule/javadoc/overview-tree.html +++ b/java/com.tractrac.clientmodule/javadoc/overview-tree.html @@ -1,10 +1,10 @@ - + - + Class Hierarchy - + @@ -332,13 +332,13 @@ diff --git a/java/com.tractrac.clientmodule/javadoc/resources/background.gif b/java/com.tractrac.clientmodule/javadoc/resources/background.gif deleted file mode 100644 index f471940fde2..00000000000 Binary files a/java/com.tractrac.clientmodule/javadoc/resources/background.gif and /dev/null differ diff --git a/java/com.tractrac.clientmodule/javadoc/resources/tab.gif b/java/com.tractrac.clientmodule/javadoc/resources/tab.gif deleted file mode 100644 index 1a73a83be11..00000000000 Binary files a/java/com.tractrac.clientmodule/javadoc/resources/tab.gif and /dev/null differ diff --git a/java/com.tractrac.clientmodule/javadoc/resources/titlebar.gif b/java/com.tractrac.clientmodule/javadoc/resources/titlebar.gif deleted file mode 100644 index 17443b3e169..00000000000 Binary files a/java/com.tractrac.clientmodule/javadoc/resources/titlebar.gif and /dev/null differ diff --git a/java/com.tractrac.clientmodule/javadoc/resources/titlebar_end.gif b/java/com.tractrac.clientmodule/javadoc/resources/titlebar_end.gif deleted file mode 100644 index 3ad78d461b2..00000000000 Binary files a/java/com.tractrac.clientmodule/javadoc/resources/titlebar_end.gif and /dev/null differ diff --git a/java/com.tractrac.clientmodule/javadoc/serialized-form.html b/java/com.tractrac.clientmodule/javadoc/serialized-form.html index 3a47f66744e..475c59885f8 100755 --- a/java/com.tractrac.clientmodule/javadoc/serialized-form.html +++ b/java/com.tractrac.clientmodule/javadoc/serialized-form.html @@ -1,10 +1,10 @@ - + - + Serialized Form - + diff --git a/java/com.tractrac.clientmodule/lib/TracAPI-src.jar b/java/com.tractrac.clientmodule/lib/TracAPI-src.jar index ffaed9cb982..363a0461d57 100755 Binary files a/java/com.tractrac.clientmodule/lib/TracAPI-src.jar and b/java/com.tractrac.clientmodule/lib/TracAPI-src.jar differ diff --git a/java/com.tractrac.clientmodule/lib/TracAPI.jar b/java/com.tractrac.clientmodule/lib/TracAPI.jar index 7775a36cde6..6dc7a5362c1 100755 Binary files a/java/com.tractrac.clientmodule/lib/TracAPI.jar and b/java/com.tractrac.clientmodule/lib/TracAPI.jar differ diff --git a/java/com.tractrac.clientmodule/pom.xml b/java/com.tractrac.clientmodule/pom.xml index 065ca9396b9..008df388f71 100755 --- a/java/com.tractrac.clientmodule/pom.xml +++ b/java/com.tractrac.clientmodule/pom.xml @@ -8,6 +8,6 @@ 1.0.0-SNAPSHOT com.tractrac.clientmodule - 3.2.1 + 3.3.1 eclipse-plugin diff --git a/java/pom.xml b/java/pom.xml old mode 100755 new mode 100644 index 5908d91ca5e..c5397e72e3d --- a/java/pom.xml +++ b/java/pom.xml @@ -92,6 +92,8 @@ com.sap.sse.replication com.sap.sse.threadmanager com.sap.sse.filestorage + com.sap.sse.feature + com.sap.sse.feature.runtime com.sap.sailing.competitorimport com.sap.sailing.domain com.sap.sailing.geocoding @@ -189,7 +191,6 @@ com.sap.sailing.simulator.test com.sap.sailing.server.trackfiles.test com.sap.sailing.selenium.test - com.sap.sailing.autoload com.sap.sailing.feature.p2build com.sap.sailing.polars com.sap.sailing.polars.datamining diff --git a/mobile/com.sap.sailing.racecommittee.app/res/layout/race_summary_large_font.xml b/mobile/com.sap.sailing.racecommittee.app/res/layout/race_summary_large_font.xml new file mode 100644 index 00000000000..0b7533a425a --- /dev/null +++ b/mobile/com.sap.sailing.racecommittee.app/res/layout/race_summary_large_font.xml @@ -0,0 +1,293 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mobile/com.sap.sailing.racecommittee.app/res/layout/race_summary.xml b/mobile/com.sap.sailing.racecommittee.app/res/layout/race_summary_normal.xml similarity index 100% rename from mobile/com.sap.sailing.racecommittee.app/res/layout/race_summary.xml rename to mobile/com.sap.sailing.racecommittee.app/res/layout/race_summary_normal.xml diff --git a/mobile/com.sap.sailing.racecommittee.app/res/values-de/strings.xml b/mobile/com.sap.sailing.racecommittee.app/res/values-de/strings.xml index 29c9e302ede..398f1bd11ea 100644 --- a/mobile/com.sap.sailing.racecommittee.app/res/values-de/strings.xml +++ b/mobile/com.sap.sailing.racecommittee.app/res/values-de/strings.xml @@ -475,7 +475,7 @@ Manuelle Eingabe Gruppenwettfahrt Wettfahrt auswählen - \'%1$s\' zur Ergebnisliste hinzugefügt an Position %2$s + \'%1$s\' zur Ergebnisliste hinzugefügt an Position %2$d Anmelden Position Server erfolgreich geändert auf %1$s. diff --git a/mobile/com.sap.sailing.racecommittee.app/res/values/strings.xml b/mobile/com.sap.sailing.racecommittee.app/res/values/strings.xml index d31aec67500..083a364f50b 100644 --- a/mobile/com.sap.sailing.racecommittee.app/res/values/strings.xml +++ b/mobile/com.sap.sailing.racecommittee.app/res/values/strings.xml @@ -503,7 +503,7 @@ Manual Input Group Racing Select Race - Added \'%1$s\' to result list at position %2$s + Added \'%1$s\' to result list at position %2$d Getting Started Position Server successfully changed to %1$s. diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/adapters/FinishListAdapter.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/adapters/FinishListAdapter.java index 09625c7da3b..451726b5bbc 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/adapters/FinishListAdapter.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/adapters/FinishListAdapter.java @@ -159,9 +159,10 @@ public class FinishListAdapter extends BaseDraggableSwipeAdapter 1 && !appUtils.isHDPI()) { + resId = R.layout.race_summary_large_font; + } else { + resId = R.layout.race_summary_normal; + } + View layout = inflater.inflate(resId, container, false); mRaceStateListener = new RaceStateListener(); mDateFormat = new SimpleDateFormat("HH:mm:ss", getResources().getConfiguration().locale); diff --git a/wiki/howto/development/fix-tracking.md b/wiki/howto/development/fix-tracking.md new file mode 100644 index 00000000000..fcc12bb04b0 --- /dev/null +++ b/wiki/howto/development/fix-tracking.md @@ -0,0 +1,97 @@ +# Fix Tracking + +[[_TOC_]] + +## Introduction + +The current fixes and tracks implementation provides an extensible micro-framework. The framework implements all the moving parts required for persisting and loading fixes and tracks. By defining interfaces and sticking to the visitor pattern, the framework also does help to implement new fix types. + +The framework also provides a generic double fix vector implementation that can be used to store fixes that consist of double values. + +### Fixes and Tracks + +In the domain model, `Tracks` hold and provide access to the `Fix` instances and provide some useful operations. `Tracks` themselves associated to a `TrackedRace`. `TrackedRace` instances hold and provide access to associated `Track` instances. + +There are abstract `Track` implementations that e.g. provide `Timepoint` based access. Typically there are specific implementations that provide calculations based on the concrete `Fix` type. + +### Mappings + +In the persistence, fixes are saved for a given `DeviceIdentifier`. In the RegattaLog, specific `RegattaLogDeviceMappingEvent` subtype events map domain objects (e.g. `Competitor` or `Mark`) to `DeviceIdentifiers`. + +The concrete mapping type event is used when loading fixes: this allows us to handle specific mappings according to the underlying mapping type. + +### Persisting Fixes + +The persistence of fixes is implemented by a `SensorFixStore` instance that supports saving and loading fixes. To be able to work with fixes on the persistence, a `DeviceIdentifier` needs to be mapped as described in "Mappings" above. + +Fixes can be saved one by one or in batches. Saving single fixes is meant to be used in live mode when a device submits single fixes to the server. When importing archived fixes, this should always be done in batches to reduce the load on the DB as well as the time for the save operation. + +The concrete MongoDB-based implementation of `SensorFixStore` is `MongoSensorFixStoreImpl`. + +## Fixes on the persistence and in the domain model + +The persistence and domain model fixes build upon on different type of Fixes: +while we persist a certain sensor data in the persistence layer, the domain model can define a typed view on top of the underlying sensor datatype. + +There are different ways to implement extensions to handle new types of fixes. + +The persistence defined by `SensorFixStore` implementations can be extended to store custom fix implementations. + +In addition there is a special type of fix called `DoubleVectorFix` that can be used as a generic persistence that is based on a specific amount of double values. Based on this persistent fix type you can also define a mapping to be processed when loading fixes. Doing this, you do not need to extend the persistence but map such generic fixes to concrete ones. + +The further chapters will guide through the process of extending the system for a new fix type or the usage of already existing fix types. + +### Implementing new persistent fix type + +Any fix type to be persisted by `SensorFixStore` implementations needs to implement the `Timed` interface. + +To enable `MongoSensorFixStoreImpl` to save and load specific data of your concrete fix type, you need to provide an implementation of `FixMongoHandler`. This implementation is responsible to construct MongoDB objects from the fix when saving and needs to map those MongoDB objects back to the fix objects when loading from the DB. + +To make `MongoSensorFixStoreImpl` find your implementation, it needs to be registered as OSGi service and the `FixMongoHandler` interface needs to specifically be exported. In addition the qualified classname of your fix implementation must be given to the registered OSGi service using the key `TypeBasedServiceFinder.TYPE`. + + Dictionary properties = new Hashtable(); + properties.put(TypeBasedServiceFinder.TYPE, MyFixImpl.class.getName()); + ServiceRegistration registration = context.registerService(FixMongoHandler.class, new MyFixMongoHandlerImpl(), properties); + +To be able to load the new fix type, you need to define a mapping event that implements `RegattaLogDeviceMappingEvent`. There's an abstract implementation `RegattaLogDeviceMappingEventImpl` that implements most of the needed common logic. + +For your concrete mapping event implementation you need to provide a specific sub-interface of `RegattaLogDeviceMappingEvent`. This interface is used to extend the visitor interface `MappingEventVisitor`, so that you can call the correct visitor method in your mapping event implementation's `accept(MappingEventVisitor)` method. + +In consequence of extending the `MappingEventVisitor` interface, you need to fix two implementations in `FixLoaderAndTracker` that now need a new method for your mapping event type. Here you need to implement the logic to load fixes into the specific `Track` of the `TrackedRace`: + +* An implementation for loading fixes of a timerange +* An implementation to handle newly added fixes in live tracking + +### Implementing SensorFix type + +In contrast of handling the persistence and loading of the fixes by ourself, you can also provide an implementation based on `DoubleVectorFix`, that supports the storage of arbitrary amounts of double values and accessing those by index. + +Similar to the above case, you need to provide a mapping event implementation. In this case there's no need to define a custom interface. For mapping `Competitors`, implement `RegattaLogDeviceCompetitorSensorDataMappingEvent`. + +To load `DoubleVectorFixes` for your new mapping event type into a `Track`, you need to provide an implementation of `SensorFixMapper`. This implementation needs to be published as OSGi service: + + ServiceRegistration registration = context.registerService(SensorFixMapper.class, new MyFixMapper(), null); + +The `SensorFixMapper` is responsible to obtain and/or create the correct `Track` given the `TrackedRace` and adding fixes to the specific `Track`. + +### More specifics on Tracks + +There are different ways to associate `Tracks` with a `TrackedRace`. e.g. `GPSFixes` are managed in distinct `GPSFixTrack` instances that are associated to `Competitor` or `Mark` instances known by the `TrackedRace`. This can also be implemented for a specific new Track/Fix type. + +In addition there's a generic association of `SensorFixTracks` that are not only associated to the specific domain object but also to a key so that you can easily have multiple additional `Tracks` for a domain object of a `TrackedRace`. + +## Import of Fixes + +Importing Fixes uses the the defined persistence to store fix data uploaded through the admin console. The upload is a simple http form based upload that delivers a file to be parsed. The servlet than delivers the imported devide uuid, so the frontend can properly load the persisted data. + +The `SensorDataImportServlet` does process the uploaded file by extending `AbstractFileUploadServlet` and retrieving available importers through the OSGi registry. The `AbstractFileUploadServlet` does process the incoming http file upload. The OSGi registry delivers instance references to previously registered importers. + +It is possible to register different `DoubleVectorFixImporter` implementations. The `SensorDataImportServlet` matches the `getType` information provided by the importers found in the registry against the selected importer information in the upload form in the admin console. The first importer to match the requested import type is used to process/ import the uploaded file. + + +### Adding importer + +Further `DoubleVectorFixImporter` implementations can be registered to the OSGi registry through a bundle activator, e.g.: `com.sap.sailing.server.trackfiles.impl.Activator`. + +The importer to be used to process the file is defined by the user by choosing the appropriate importer in the admin console upload formular. +The list available importer types is retrieved dynamically in the administration console. Therefore, simply registering an importer as a service in the OSGi registry makes it available in the admin ui. diff --git a/wiki/howto/onboarding.md b/wiki/howto/onboarding.md index c4e32cec374..d29634b9923 100644 --- a/wiki/howto/onboarding.md +++ b/wiki/howto/onboarding.md @@ -87,7 +87,7 @@ Beside the installation script for Eclipse Mars, there is also one for the new E * Wait until the target platform has been resolved completely * Rebuild all projects 4. Run the Race Analysis Suite - * Start the MongoDB + * Start the MongoDB (cd /somePathto MongoDB/mongodb/bin; rm c:/data/SAP/sailing/mongodb/mongod.lock; ./mongod --dbpath c:/data/SAP/sailing/mongodb) * Start the appropriate Eclipse launch configuration (e.g. 'Sailing Server (Proxy)') You´ll find this in the debug dropdown * Run "Security UI sdm" in the debug dropdown * Run "SailingGWT" in the debug dropdown