Commit Graph
100 Commits
Author SHA1 Message Date
Axel Uhl df7c212d07 Always report lastScoringUpdate in UTC
Use an explicit UTC formatter with an English locale while preserving the
existing timestamp format, preventing timezone-only server comparison diffs.

Add an endpoint regression test that switches the JVM default time zone
back and forth and verifies stable UTC output and epoch milliseconds.

Assisted-By: GPT-6 Astra
2026-10-30 18:42:20 +01:00
Axel Uhl 1da818504c adding opencode support for the local CLAUDE.md file 2026-10-30 17:36:00 +01:00
Axel Uhl b9ccbce1b8 bug6321: stream wind-track fixes as top-level objects in MDI to keep the socket draining
Make WindTrackMasterData.windTrack transient and transmit each wind track's
fixes as individual top-level writeObject(...) calls terminated by a null
sentinel (mirroring the bug6227 GPS-fix streaming), resetting the
ObjectOutputStream handle table every FIXES_PER_HANDLE_TABLE_RESET fixes.
This restores reader/writer interleaving so the importer keeps draining the
HTTP socket during large Mongo inserts, avoiding the Jetty connector
idle-timeout EofException / Premature EOF on multi-day wind tracks.

The deduping against already-present fixes is preserved on the import side.

Assisted-By: Claude Opus 4.8
2026-10-29 18:47:19 +01:00
Axel Uhl 3912f1309e bug6321: improved logging for MasterDataResource and MasterDataImporter 2026-10-29 18:03:46 +01:00
Axel Uhl 23b901d4ad bug6320: use ArrayList instead of Collections.singleton for replica set serialization in live check 2026-10-28 11:19:00 +01:00
Axel Uhl 0db5918113 added Oliver Harnack's quote 2026-10-27 15:59:48 +01:00
Axel Uhl f7d121e78e more elaborate listing of sailing analytics servers in AWS region 2026-10-27 15:59:32 +01:00
Axel Uhl ec4054626c Merge remote-tracking branch 'eclipse/translation' into eclipse-main 2026-10-18 15:23:05 +02:00
Axel Uhl 61e6aaa2c8 Merge remote-tracking branch 'eclipse/main' into eclipse-main 2026-10-18 10:18:37 +02:00
Axel Uhl 102cf7d5ef Merge branch 'eclipse-main' into translation 2026-10-18 09:00:49 +02:00
Axel Uhl 85fc612fbb bug6319: merge eclipse/main into bug6319
Merge the latest eclipse/main. The only content eclipse/main carried
past the branch point was the bug6318 WPub.dll JSON-action stopgap for
the ORC public-certificate adapter. bug6319 is the intended replacement
for that stopgap: it migrates the same adapter onto ORC's new key-based
JSON API at https://sas.orc.org/api/{key}/<endpoint> and restores the
pre-bug6318 semantics (country-less search, year search, real overview
counts and VPP year).

The two reworks touch the same files incompatibly, so all five ORC
adapter/test conflicts are resolved to the bug6319 version, discarding
the superseded bug6318 hunks:
  ORCPublicCertificateDatabase.java
  impl/ORCPublicCertificateDatabaseImpl.java
  test .../orc/FailIfNoValidOrcCertificateRule.java
  test .../orc/TestORCPerformanceCurve.java
  test .../orc/TestORCPublicCertificateDatabase.java
MANIFEST.MF auto-merged identically on both sides (the x-friends
dedup). The resulting tree is byte-identical to the bug6319 tip.

Assisted-By: Claude (Opus 4.8)
2026-10-18 07:16:05 +02:00
Axel Uhl 423a492bd3 bug6319: document ORC_API_KEY requirement for self-hosting in admin release notes
Add an October 2026 Admin Console release-notes entry announcing the migration
of ORC certificate retrieval to the key-based sas.orc.org web API (the previous
public endpoints were decommissioned by ORC). Self-hosting administrators must
now configure the ORC_API_KEY environment variable for the server process; the
SAP-hosted landscape already has it provisioned. Links issue #6319.

Assisted-By: Claude Opus 4.8 (claude-opus-5-5)
2026-10-17 22:05:25 +02:00
Axel Uhl 5f3a8c0097 bug6319: document country-fleet → dxtName → DownBoatRMS inactive-cert discovery path
Record in the adapter's class Javadoc that activecerts (and thus search())
only ever returns active certificates, that the sole wired route to an
inactive certificate is DownBoatRMS_byrefno by a known RefNo, and the
two-hop country-fleet → dxtName → DownBoatRMS path that would let one
discover inactive certificates by boat identity (not wired today; would
also need a dxtName-keyed handle representation).

Also fix a stale {@link #search(..., boolean)} reference to the current
6-arg signature.

Assisted-By: Claude Opus 4.8 (claude-opus-4-8)
2026-10-17 21:43:11 +02:00
Axel Uhl 85ccfc4e5e bug6319: remove dead includeInvalid search parameter; export ORC_API_KEY
The key-based sas.orc.org API routes all active-certificate search through the
activecerts endpoint, which returns active certificates only. The includeInvalid
flag on ORCPublicCertificateDatabase.search(...) could therefore never change the
result and was dead. Remove it from the interface and impl, the default
getCertificateHandle, the two fuzzy-search call sites, SailingServiceImpl, and all
ORC test call sites. The sole route to an inactive certificate in this adapter is to
download it by reference number via DownBoatRMS_byrefno (getCertificate /
getCertificateHandle); the search Javadoc now states this explicitly.

Also export ORC_API_KEY from java/target/env-default-rules.sh so the Java VM can read
it via System.getenv(...). configuration/secrets assigns it as a plain (non-exported)
shell variable that the java child process would not otherwise inherit; it is passed
as an environment variable rather than a -D system property so it is not exposed on
the process command line.

Assisted-By: Claude Opus 4.8 (claude-opus-5-5)
2026-10-17 21:27:46 +02:00
Axel Uhl fd97a6b8a2 bug6319: document ORC_API_KEY onboarding and how to obtain an ORC API key
Add an entry under "Tuning the Eclipse Installation" explaining that
ORC_API_KEY must be defined as an Eclipse String Substitution variable and
referenced as an environment variable in the test launch configuration, but
only if one wants to run the ORC public-certificate tests
(TestORCPublicCertificateDatabase, TestORCPerformanceCurve) locally with
success. Without the key these tests fail on purpose (rather than being
skipped) so the misconfiguration is noticed; offline tests still run.

Also describe how to obtain a key: ask the team lead, or request your own via
ORC Sailor Services -> Developers page (request API access, then create keys
once granted), noting that a key is shown only once at creation.

Assisted-By: Claude (claude-4.8-opus)
2026-10-17 18:33:38 +02:00
Axel Uhl 326cdc1acb bug6319: relax testCertificateUpdate to assert same RefNo instead of issue-date ordering
Under the new DownBoatRMS_byrefno endpoint, searchForUpdate keys on the
reference number and returns the newest certificate for that RefNo. The
activecerts seed row's dxtDate can therefore be newer than the certificate
returned by searchForUpdate, so the previous invariant "an update is never
older than the original" no longer holds and made the test flaky across the
randomly shuffled seed from FailIfNoValidOrcCertificateRule.

Replace the issue-date ordering assertion with a reference-number round-trip,
which is the invariant the byrefno endpoint actually guarantees. The issuing
country assertion is kept.

Assisted-By: Claude (claude-4.8-opus)
2026-10-17 18:22:06 +02:00
Axel Uhl f18b36556e bug6319: migrate ORC public-certificate adapter to key-based sas.orc.org JSON API
Replace the decommissioned data.orc.org/public/WPub.dll XML endpoints with the
new key-based JSON API at https://sas.orc.org/api/{key}/<endpoint> (docs:
https://sas.orc.org/api-docs.php), restoring the pre-bug6318 semantics that the
bug6318 stopgap had regressed:

  - country-less and year-based search via the 'activecerts' endpoint
    (all criteria optional and AND-combined server-side; no criteria -> every
    active certificate), removing the forced-country / current-year-only branches;
  - real per-country/family overview counts and VPP year via the 'RMS' endpoint,
    replacing the stubbed certCount=0 / vppYear=null;
  - full-certificate download via 'DownBoatRMS_byrefno' (top-level rms[], consumed
    by the unchanged ORCCertificatesJsonImporter);
  - TestORCPerformanceCurve bulk online fetch via 'DownRMS'.

The API key is read from the ORC_API_KEY environment variable and placed into the
request path; it is never logged. When the key is absent the adapter throws, so
the live tests FAIL loudly rather than skip. The two pure-offline date-parse tests
run independently of the key. release.yml passes ORC_API_KEY to the test runtime,
gated so compile-only (skip_tests) runs never see the real secret.

Assisted-By: Claude Opus 4.8 (claude-opus-4-8)
2026-10-17 12:40:54 +02:00
Axel Uhl 6b9460355b bug6318: route TestORCPerformanceCurve online fetch through the impl via x-friends
Drop the hardcoded ORC bulk-download URL from TestORCPerformanceCurve.initialize()
and instead obtain the authenticated per-country bulk JSON stream from
ORCPublicCertificateDatabaseImpl.openBulkCertificatesJsonStream(CountryCode). All
endpoint knowledge (host, path, action, ext) now lives in the impl, the online fetch
gains the Authorization header it was previously missing, and no method has to be
added to the public ORCPublicCertificateDatabase interface. The .impl package is
reachable from the test because it is exported with an x-friends restriction naming
com.sap.sailing.domain.test; clean up the duplicated friend entry in the manifest.

Assisted-By: Claude Opus 4.8 (claude-opus-5-5)
2026-10-17 11:15:50 +02:00
Axel Uhl 182e7a00dd bug6318: remove now-unused logger field from TestORCPerformanceCurve
The logger field became unused once the country-overview-based online fetch
was replaced with a fixed GER bulk fetch in setUp (its only use was the
"Trying to read country certificates for ..." info line). Remove the field
and the now-unused java.util.logging.Logger import so the workspace stays
warning-free.

Assisted-By: Claude Opus 4.8 (claude-opus-5-5)
2026-10-17 10:27:55 +02:00
Axel Uhl 933111da2e bug6318: rewrite ORC public certificate search onto live WPub.dll JSON actions
The legacy data.orc.org WPub.dll XML entry points the adapter depended on
were decommissioned: search() POSTed action=ListCert and
getCountriesWithValidCertificates() GET /RMS, both parsed as XML. They now
302-redirect to HTML / return empty bodies, so parsing as XML tripped the
DOCTYPE / NPE path and failed every ORC test.

Rework the adapter onto the two surviving JSON actions:
  - ?action=DownBoatRMS&RefNo=<ref>&ext=json  (single certificate)
  - ?action=DownRMS&CountryId=<IOC>&ext=json  (per-country bulk)
The global DownRMS (no CountryId) still yields the Countries list used by
getCountriesWithValidCertificates().

ORCPublicCertificateDatabaseImpl:
  - new httpGetJson() (HttpGet + lax redirects + Basic auth; inline
    BOMInputStream charset detection; JSONParser), wrapping org.json.simple
    ParseException in IOException so no org.json type leaks into signatures;
  - parseHandleFromJson() mapping the rich rms-row schema (NatAuth->country,
    BIN->fileId, GPH, RefNo, Family, Age_Year->yearBuilt, IssueDate) with
    tolerant asString/asDouble/asInteger helpers and familyFromString();
  - search(): RefNo-direct via DownBoatRMS, else per-country bulk via DownRMS
    with client-side matchesClientFilters() (name/sail/boatclass/year); the
    JSON actions return only valid certs, so includeInvalid is retained for
    API compatibility but no longer branches;
  - getCountriesWithValidCertificates() reads the top-level Countries array,
    skips the ORC pseudo-entry; throws clause narrowed to IOException;
  - searchForUpdate() now queries by RefNo (dxtName no longer exists);
  - fuzzy search derives the issuing country from the sail-number prefix
    (countryFromSailNumber) so bulk lookups have a CountryId;
  - dead XML parseHandle(Node)/parseCountryOverview(Node) removed.

ORCPublicCertificateDatabase: narrow getCountriesWithValidCertificates()
throws to IOException; Javadoc updated to the JSON actions; https.

Tests: FailIfNoValidOrcCertificateRule probes a fixed strong country (GER)
instead of the now-unavailable cert-count ranking. TestORCPublicCertificate-
Database replaces the dead FRA00013881/SOULMATE fixture with a live
self-seeding pickSeedHandle(); all live tests gated by
@FailIfNoValidOrcCertificates. TestORCPerformanceCurve.setUp fetches the GER
bulk JSON directly. Full com.sap.sailing.domain.orc package: 28/28 green.

Assisted-By: Claude Opus 4.8 (claude-opus-5-5)
2026-10-17 09:44:42 +02:00
Axel Uhl 44e9eedfbc bu6202: release notes 2026-10-12 22:17:59 +02:00
Axel Uhl bc79a79b0c Merge branch 'eclipse-main' into bug6202 2026-10-12 21:55:15 +02:00
Axel Uhl fbff5aedf4 Merge branch 'eclipse-main' into translation 2026-10-08 18:45:17 +02:00
Axel Uhl 4007d30be1 adjusting config for maptile server "baking" 2026-10-08 00:11:00 +02:00
Axel UhlandCopilot b334fac7f5 bug6316: activate Java-11 fragment profile for all JDKs >= 9
The with-java11-non-android-relevant profile was bounded by an upper JDK
version, so with Maven running on JDK 25 the routeconverterjava11extension
fragment was missing from the test runtime. On docker-* branches (tests
on JDK 8 with JavaSE-11.profile) routeconverter then loaded javax.xml.bind
from rt.jar via boot delegation, while Tycho 5's new test TCCL exposes
jaxb-impl's META-INF/services entry, so the bundled impl (wired to the
jaxb-api bundle) got picked, causing JAXB ClassCastExceptions and
IllegalAnnotationsExceptions in com.sap.sailing.server.trackfiles.test.

Assisted-By: Claude Opus 5.5

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-07 21:56:18 +02:00
Axel Uhl 9092744d4a bug6241 aftermath: dispatch regatta raceAdded catch-up outside computeIfAbsent to fix replication initial-load deadlock
The runWhenDoneLoading rewrite in 15dbbc03a1 (companion bug6241
listener-leak fix) made runWhenDoneLoading register a race-removal
listener on the race's regatta via TrackedRegatta.addRaceListener. When
a replica performs its initial load from a master
(RacingEventServiceImpl.initiallyFillFromInternal), it registers a
synchronous RaceAdditionListener on each deserialized regatta. Those
regattas already contain their races, so addRaceListener's catch-up loop
fires raceAdded for every existing race -- and it did so from INSIDE the
ConcurrentHashMap.computeIfAbsent mapping function, because the
SynchronousRunnableExecutor runs the dispatch inline. raceAdded then
called runWhenDoneLoading, which re-entered addRaceListener and nested a
second computeIfAbsent on the same raceListeners map while the outer one
still held the key's reservation node. ConcurrentHashMap forbids the
mapping function from modifying the map, so the nested update spun/blocked
forever on the reservation node -- a single-threaded self-deadlock, not a
lock-ordering deadlock.

This affected production: a replica would hang indefinitely during
initial load from a non-empty master (both automatic replica startup via
the replication Activator and operator-initiated replication). It also
wedged the CI build, which runs the replication initial-load tests;
TrackedRaceWithSensorFixStoreContentsReplicationTest hung until the 6h
GitHub Actions cap cancelled the job. The normal getOrCreateTrackedRegatta
path was unaffected because the listener there is registered on a
freshly-created empty regatta (zero-race catch-up, no re-entry).

Fix: TrackedRegattaImpl.addRaceListener now captures the catch-up work in
a holder inside the mapping function and runs it AFTER computeIfAbsent
returns, with the entry fully installed and no reservation node held. The
holder is populated only when a new entry was actually created, preserving
the duplicate-registration collapse, and the catch-up dispatch still runs
under the trackedRacesLock read lock, so ordering relative to enqueEvent
is unchanged. A nested addRaceListener for a different listener key now
proceeds normally.

TrackedRaceWithSensorFixStoreContentsReplicationTest now passes in ~7s.

Assisted-By: Claude (Anthropic claude-4.8-opus)
2026-10-07 14:07:58 +02:00
Axel Uhl 6cb7d291e5 bug6316: add junit-platform-suite bundles to target platform for Tycho 5
Tycho 4.0.x implicitly contributed the JUnit Platform "suite" bundles to the
tycho-surefire test runtime, so our target platform never had to provide
org.junit.platform.suite.api itself. Tycho 5.0.4 no longer injects them, which
broke test resolution for every *.test bundle with:

  Cannot resolve project dependencies:
    You requested to install 'java.package; org.junit.platform.suite.api
    [1.0.0,2.0.0)' but it could not be found

The 2024-12 release repository we already reference for JUnit 5 (Platform
1.11.3 / Jupiter 5.11.3) also provides the split suite artifacts, so add them
from the same repository and include them as bundles:

  junit-platform-suite-api / -commons / -engine (+ .source) 1.11.3

suite-engine is included as well, at effectively no cost, to also support
running JUnit Platform @Suite classes via the suite engine.

Assisted-By: Claude Opus 4.8
2026-10-06 19:46:29 +02:00
Axel Uhl 3cda4269cd Merge branch 'eclipse-main' into bug6316 2026-10-06 18:10:51 +02:00
Axel Uhl b7f5edf70e Bump pinned Apache Maven from 3.9.11 to 3.9.16
3.9.16 is the newest release on the Maven 3.9.x line (which Tycho 4.0.x
requires) and is the version the GitHub runner image shipped as default
before image 20261004.327.1 bumped the default to the Tycho-incompatible
3.10.0. Pinning the image's former default avoids a per-run download.

Assisted-By: Claude Opus 4.8
2026-10-06 18:08:33 +02:00
Axel Uhl defa035a70 bug6316: upgrade Eclipse Tycho from 4.0.12 to 5.0.4
Tycho 4.0.x is end-of-life (4.0.13 was the final release); upstream
recommends migrating to the 5.x main line. Tycho 5.x requires Java 21+ as
its runtime VM, which release.yml already satisfies (Maven runs on JDK
25), and still targets the Maven 3.9.x line, so the existing Maven 3.9.11
pin remains valid.

Only java/pom.xml is bumped. The standalone java/com.amazon.aws.aws-java-api
module is deliberately left at Tycho 1.7.0: it builds its local p2 repo
under JDK 8, and Tycho 5.x cannot run on Java 8.

This is an initial probe to see how the release build reacts; serialization/
replication validation (see Bugzilla 3859 note in java/pom.xml) still pending.

Assisted-By: Claude Opus 4.8
2026-10-06 17:40:15 +02:00
Axel Uhl 2550cecea3 Pin Apache Maven to 3.9.11 for Tycho 4.0.x compatibility
The GitHub-hosted ubuntu runner image bumped its default Apache Maven
from 3.9.x to 3.10.0 (image ubuntu24/20261004.327). Tycho 4.0.x requires
the Maven 3.9.x line; under Maven 3.10.0 Tycho's Sisu/Guice component
wiring fails at extension bootstrap with "No implementation for
MavenTargetLocationFactory was bound" while provisioning
DefaultTargetPlatformFactory, breaking the build.

Pin Maven to 3.9.11 via setup-maven in every workflow that runs Maven
(release, build-aws-sdk-p2, build-sailing-base-p2, build-jetty-p2) so the
build no longer depends on whatever Maven the runner image ships. The
action is SHA-pinned consistently (stCarolas/setup-maven v5.1).

Assisted-By: Claude Opus 4.8
2026-10-06 17:33:04 +02:00
Axel Uhl b516c7957e fixed typo in wiki tutorials home.md 2026-10-06 13:13:21 +02:00
Axel Uhl 179a3ee25c Merge remote-tracking branch 'eclipse/main' into eclipse-main 2026-10-06 13:05:21 +02:00
Axel Uhl ab8a04f8e7 Merge branch 'eclipse-main' into hyperspace 2026-10-06 13:04:14 +02:00
Axel Uhl 024d1d65be hyperspace: raise Gradle Java source/target from 8 to 17
JDK 21 flagged source/target 8 as obsolete. The Gradle app build already
runs under JDK 17 (buildAndUpdateProduct.sh / release.yml), and AGP 8.7
permits Java 17, so bump sourceCompatibility/targetCompatibility and the
ext compat vars to 17. This covers the 4 mobile modules and the 8 java/
library subprojects compiled by Gradle; the separate Tycho/Maven OSGi
build is unaffected (its BREE pins it to Java 8 independently).

Assisted-By: Claude (claude-opus-5-5)
2026-10-06 11:34:23 +02:00
Axel Uhl 51f9c0ee6b sorted links in Wiki/tutorial support.sapsailing.com section 2026-10-06 00:09:06 +02:00
Axel Uhl baa80a0126 fixed link from planning.md to smartphone-tracking-general 2026-10-05 21:01:47 +02:00
Axel Uhl 98a09bd4e7 Merge remote-tracking branch 'eclipse/main' into eclipse-main 2026-10-05 21:01:42 +02:00
Axel Uhl 22e789d921 Bind httpprinter debug socket to loopback only
Fixes CodeQL py/bind-socket-all-network-interfaces by binding the
local debug HTTP printer to 127.0.0.1 instead of all interfaces
(host = ''). The script is a small local debugging utility, so
restricting it to loopback does not affect its intended use.

Assisted-By: Claude Opus 4.8
2026-10-05 18:37:26 +02:00
Axel Uhl 42bdfb2cb4 checking MDI URL for trusted domains also from admin console UI 2026-10-05 18:18:24 +02:00
Axel Uhl b3ab8d8d62 Merge pull request #36 from SAP-Sailing-Analytics/renovate/lock-file-maintenance
Lock file maintenance
2026-10-05 12:12:42 +02:00
Axel Uhl b70837f803 Merge pull request #35 from SAP-Sailing-Analytics/renovate/rubygems-sinatra-vulnerability
Update dependency sinatra to v4 [SECURITY]
2026-10-05 12:12:00 +02:00
Axel Uhl 88a8587f91 Merge pull request #34 from SAP-Sailing-Analytics/renovate/rubygems-webrick-vulnerability
Update dependency webrick to v1.8.2 [SECURITY]
2026-10-05 12:11:32 +02:00
Axel Uhl 4bcf7cabad Merge pull request #33 from SAP-Sailing-Analytics/renovate/rubygems-rack-vulnerability
Update dependency rack to v2.2.23 [SECURITY]
2026-10-05 12:11:15 +02:00
Axel Uhl 05f8a658c9 hyperspace: fix Play Services version skew crashing QR scanner (Buoy Pinger)
After the Android 16 / AGP 8 / JDK 21 upgrades, opening the QR code scanner
in the Buoy Pinger app crashed with NoClassDefFoundError for the internal
GMS classes zzdkv / zzbgl when constructing BarcodeDetector.Builder.

Root cause: play-services-maps:17.0.1 forced play-services-basement/base up to
17.0.0 in the Buoy Pinger graph, while play-services-vision stayed at 12.0.1.
Vision 12.0.1 references internal classes that existed in basement 12.x but
were removed in 17.0.0, so they fail to resolve at runtime. Race Manager was
unaffected only because it pulls no maps dependency, keeping its whole GMS
stack at 12.x.

Fix (preserving minSdk 16 for old tracking/pinging hardware):
- Bump play-services-vision 12.0.1 -> 19.0.0. Vision 19.0.0 depends on
  basement/base 17.0.0 (the versions maps 17.0.1 already pulls) and keeps
  minSdk 14; the BarcodeDetector API is unchanged, so no barcode code changes.
- Bump play-services-location 12.0.1 -> 21.0.1 so Race Manager's fused-location
  calls do not hit the same basement mismatch once vision raises basement; it
  resolves to basement 18.1.0 (minSdk 14), consistent across all three apps.
- Modernize WindFragment's deprecated LocationRequest API (create()/setPriority/
  setInterval/setFastestInterval and LocationRequest.PRIORITY_HIGH_ACCURACY) to
  LocationRequest.Builder with Priority.PRIORITY_HIGH_ACCURACY.

Verified: all three app modules (Buoy Pinger, Tracking, Race Manager) compile
cleanly against the bumped versions, with no location deprecation warnings.
Runtime QR scan not yet smoke-tested on a device.

Assisted-By: Claude Opus 4.8 (claude-opus-5-5)
2026-10-05 11:40:26 +02:00
Axel UhlandCopilot bd7ce48e65 hyperspace: target Android 16 (API level 36) as required by Google Play
Since August 31, 2026 Google Play rejects app updates not targeting API
level 36 ("Target SDK of artifact is too low: <versionCode>").

Build:
- AGP 8.7.3 -> 8.13.2 (8.7 supports at most API 35; 8.13 supports 36.1
  and works with our Gradle 8.14.3 / JDK 21)
- compileSdk 33 -> 36, targetSdk 35 -> 36, buildTools 34.0.0 -> 36.0.0
- azure-pipelines/main.yml: androidVersion 35 -> 36

Apps (all via the shared library, so all apps get it):
- Predictive back: opt out with android:enableOnBackInvokedCallback="false"
  so that the existing onBackPressed() overrides and KEYCODE_BACK handling
  keep being invoked on Android 16.
- Large screens: temporarily opt out of ignored orientation/resizability
  restrictions on sw600dp displays with
  PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY (ignored from API 37 on).
- Edge-to-edge, enforced without opt-out for targetSdk 36: new
  SystemBarInsetsHandler, registered by LoggableApplication on Android 15+
  devices, pads each activity's content view by the system bar, display
  cutout and (unless adjustPan/adjustNothing) keyboard insets so content
  no longer hides behind the status and navigation/button bar, and fills
  the status bar area with the theme's colorPrimaryDark as before.

Document the androidVersion / Play target API requirement in
building-and-deploying.md.

Assisted-By: Claude Opus 5.5

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-05 01:35:05 +02:00
Axel Uhl c37c5f5502 bug6312: allow operators to set the initial admin password via SAILING_ADMIN_INITIAL_PASSWORD
When a fresh instance starts with an empty security store (one that does
not replicate its security service from a master), initEmptyStore() creates
the default "admin" user. Until now this always used the well-known password
"admin", created the account without enforcing a strong password, and logged
the password in cleartext.

Operators can now set the SAILING_ADMIN_INITIAL_PASSWORD environment variable
to supply the initial admin password. When set to a non-blank value, that
value is used and a strong password is enforced; the password value is never
logged. When unset, the previous "admin" default is kept for backward
compatibility (test / docker-deploy / CI) but a security warning is logged
recommending the variable be set, and noting that future versions may require
it. The cleartext password is no longer logged in either case.

This is opt-in and non-breaking; forcing the variable is left to a later
change after a deprecation period.

Assisted-By: Claude (Opus)
2026-10-04 23:00:56 +02:00
Axel Uhl b2632c3882 Stop logging OAuth tokens, secrets, and request tokens
OAuthRealm and SecurityServiceImpl logged OAuth credentials in cleartext:
the access token, its token value, secret, and raw provider response, as
well as the request token and verifier. These are sensitive values that
should never reach log files, where they widen the blast radius if logs
are leaked or aggregated.

Replace them with non-sensitive success/failure messages that identify
the provider only. The request-token/verifier lines are also dropped to
FINE since they carry no diagnostic value beyond the provider name.

Assisted-By: Claude (Opus)
2026-10-04 22:47:17 +02:00
Axel Uhl 46222d2d65 added configuration/listSailingAnalyticsServers script;
with valid AWS credentials in the env vars (see awsmfa) this script
will use findSailingAnalyticsServers and show the process configurations
found on them under /home/sailing/servers/*
2026-10-04 21:00:49 +02:00
Axel UhlandCopilot 34e3c45de8 hyperspace: rename release APKs to NAAS allow-listed names in Fastlane
Central Signing failed with HTTP 406 ("The requested list doesn't exist in
the library files") after 9e17c0b453 switched files2sign.json to the AGP 8
default <module>-release-unsigned.apk names: Non-ABAP Assembly allow-lists
the APK file name together with group/artifact ID per MoMa assembly, and
the allow-listed names are <module>-unsigned.apk.

Restore the allow-listed names in files2sign.json and let the releaseBuild
lane rename the APKs reported by the gradle action from
*-release-unsigned.apk to *-unsigned.apk, failing the lane if none is
found. This replaces the applicationVariants outputFileName rename dropped
in the AGP 8 migration without relying on the deprecated Variant API that
AGP 9 removes. Update building-and-deploying.md accordingly.

Assisted-By: Claude Opus 5.5

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-04 20:48:26 +02:00
Axel UhlandCopilot 9e17c0b453 hyperspace: match files2sign.json APK names to AGP 8 default output names
The AGP 8 migration (5a78d3e8e3) dropped the applicationVariants
outputFileName rename that stripped "-release" from the APK name, so the
build now produces <module>-release-unsigned.apk. The central signing stage
of the sap-mobile-pipeline template signs Binaries/Android/<file> literally
as listed in files2sign.json, which still named <module>-unsigned.apk and
failed with "File does not exist". Point files2sign.json at the AGP default
names instead of reintroducing the deprecated Variant API rename, which
AGP 9 removes. Document the naming requirement and update the JDK version
in building-and-deploying.md.

Assisted-By: Claude Opus 5.5

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-04 20:15:34 +02:00
Axel Uhl 9bc3a67c12 Bump sapGradleVersion to 8.14.3 to match the Gradle wrapper
Assisted-By: Claude Opus 4.8 (claude-opus-5-5)
2026-10-02 21:17:37 +02:00
Axel Uhl ffec282970 Merge branch 'eclipse-main' into hyperspace 2026-10-02 21:11:13 +02:00
Axel Uhl c8e7076053 Fix fatal lintDebug errors so Android apps stay Play-Store publishable under AGP 8
Under AGP 8 the CI 'build' task runs lintDebug as fatal. After the JDK 21 /
Gradle 8.14.3 / AGP 8.7.3 migration this surfaced several blockers that the
old Fastlane assembleRelease path never ran lint against.

Google Play SDK Index (RiskyLibrary):
- play-services-maps 12.0.1 is flagged as outdated and blocks publishing.
  Split the shared play_services version variable into per-artifact versions
  (maps/location/vision) so modules can diverge, and bump maps to 17.0.1 (its
  minSdk floor is still 14, so no app needs a higher minSdk).
- Move play-services out of the shared library: it now exposes vision only
  (the shared QR/barcode scanner). maps goes to Buoy Pinger (the only app with
  a real map UI); location goes to Race Manager (WindFragment); the dead
  play-services-gcm declaration is dropped from the tracking app.
- Race Manager no longer depends on maps: AppPreferences stored a wind
  position as com.google.android.gms.maps.model.LatLng purely as a
  (double,double) holder. Replace it with the existing domain type
  com.sap.sse.common.Position / com.sap.sse.common.impl.DegreePosition.

targetSdk 35 runtime-correctness (local lint checks, not Play Console gates):
- TrackingFragment: register the GPS-disabled receiver with
  RECEIVER_NOT_EXPORTED on API 33+ (guarded native flag; androidx.core 1.0.0
  lacks the ContextCompat overload), suppressing UnspecifiedRegisterReceiverFlag.
- TrackingService: scope the gpsDisabled broadcast to this app's package so it
  reaches the now non-exported receiver (fixes UnsafeImplicitIntentLaunch).
- RaceStateService / RaceLogPollingService: guard setExact with
  canScheduleExactAlarms() on API 31+, falling back to inexact set().
- RaceStateService: declare POST_NOTIFICATIONS and guard the notify() update
  with a runtime permission check on API 33+.

MissingInflatedId false positives: the referenced ids live in qualified layout
variants (e.g. race_schedule_start_time_normal.xml) that lint cannot resolve
from R.layout.race_schedule_start_time; suppress MissingInflatedId on the three
affected onCreateView methods.

Verified: ./gradlew lintDebug assembleRelease (JDK 21) and the CI gate
./gradlew build (JDK 17) both BUILD SUCCESSFUL with 0 fatal lint errors; all
three apps' apk/.version artifacts regenerate.

Assisted-By: Claude Opus 4.8
2026-10-02 19:07:29 +02:00
Axel Uhl 084ce18ee2 Revert "Upgrade Gradle wrapper from 7.6 to 8.14.3 for JDK 21 support"
This reverts commit 700bbcb1d2.
2026-10-02 17:56:58 +02:00
Axel Uhl 5a78d3e8e3 Migrate Android build to AGP 8.7.3 for Gradle 8.14.3 / JDK 21
CI dropped JDK 17 for JDK 21; Gradle 7.6 cannot run on JDK 21, so the
wrapper was bumped to 8.14.3 (700bbcb1d2). That alone is insufficient:
AGP 7.2.2 references IncrementalTaskInputs (removed in Gradle 8.0), so
assembleRelease failed. Gradle 8.x and AGP must move together.

Changes:
- Root build.gradle: AGP 7.2.2 -> 8.7.3; grgit 2.3.0 -> 5.3.0 (classpath
  and plugins block); ext.buildTools 30.0.3 -> 34.0.0 (AGP 8.7 floor).
- gradle.properties: enable AndroidX via the Jetifier shim
  (android.useAndroidX=true, android.enableJetifier=true) and set
  android.nonTransitiveRClass=false to restore the merged R so appcompat
  attrs (e.g. R.attr.colorAccent) resolve as before.
- All 4 mobile/ modules: package= in AndroidManifest.xml -> namespace in
  build.gradle; packagingOptions -> packaging; re-enable BuildConfig
  generation (buildFeatures { buildConfig true }), off by default in AGP 8.
- 3 app modules: port the removed applicationVariants APK-copy to the AGP 8
  androidComponents Artifacts API (CopyApkToWww consumes SingleArtifact.APK
  via BuiltArtifactsLoader), reproducing the <packageName>.apk and
  <packageName>.version output in java/com.sap.sailing.www/apps/.
- Jetifier rewrites dependency bytecode but not our own sources, so migrate
  all android.support.* imports/FQNs/XML tags to androidx.* across mobile/.
- AGP 8 makes R fields non-final; convert every switch(R.id...) to if/else
  (18 files) since case labels require constant expressions.

assembleRelease is BUILD SUCCESSFUL under JDK 21 / Gradle 8.14.3 and
produces the same APK and .version artifacts as before. Remaining warnings
are Gradle-10 space-assignment deprecations only; we are intentionally on 8.x.

Assisted-By: Claude Opus 4.8 (claude-opus-5-5)
2026-10-02 17:54:22 +02:00
Axel Uhl 700bbcb1d2 Upgrade Gradle wrapper from 7.6 to 8.14.3 for JDK 21 support
The CI JDK was upgraded from 17 to 21, but Gradle 7.6 only supports
running on JDK up to 19. Running it on JDK 21 made the Groovy script
compiler reject the JDK-21 (class file major version 65) settings.gradle
with 'Unsupported class file major version 65' / 'Could not open settings
generic class cache'. Gradle 8.5 is the first release to support JDK 21;
8.14.3 is the latest 8.x (supports JDK 21-24) and avoids the breaking
changes of the 9.x line. Verified locally: 'gradlew help' configures the
:mobile Android project BUILD SUCCESSFUL under JDK 21.

Assisted-By: Claude Opus 4.8
2026-10-02 16:39:35 +02:00
Axel Uhl 15dbbc03a1 Make TrackedRace.runWhenDoneLoading fire at most once per registration
On ARCHIVE (replay-heavy) startup the restore counters could drift:
numberofracesrestoreddoneloading short and numberofracesstillloading
negative (observed -1). The negative count traced to a single race whose
stillLoading-- callback fired twice against one matching raceAdded +1.

Every restored race arms runWhenDoneLoading twice, from two independent
sites with two distinct runnables (restore path -> doneLoading++, and
RaceAdditionListener.raceAdded -> stillLoading--), each meant to fire
once. The old runWhenDoneLoading removed its listener inside
statusChanged with no one-shot guard. Because notifyListeners snapshots
the listener set under lock and then iterates the snapshot outside the
lock, a removeListener happening during dispatch does not shorten an
already-in-flight iteration. When a race crossed past-LOADING twice
back-to-back under concurrent status pushes (loader finish plus the
stopTracking teardown), a second dispatch snapshotted the listener array
before the first dispatch's removeListener landed, so the callback fired
on both transitions -> two decrements vs one increment -> net -1 (and a
symmetric +1 over-count on doneLoading).

Rewrite runWhenDoneLoading to mirror runWhenPastLoading: a settled
AtomicBoolean CAS makes firing strictly one-shot per registration no
matter how many times or along how many concurrent paths the transition
is delivered, plus a cooperating regatta raceRemoved listener that tears
down and settles-without-firing if the race is removed while still
PREPARED/LOADING/ERROR (closing the companion bug6241 listener-leak
class).

Add TrackedRaceRunWhenDoneLoadingTest as contract tests for the
single-fire / already-done / removed-before-done / fire-then-remove
behaviours. Note these do not reproduce the production concurrency
interleaving: setStatus dispatches statusChanged synchronously and
sequentially, so single-threaded JUnit cannot deterministically recreate
the snapshot/remove race; the fix rests on the settled CAS.

Assisted-By: Claude (Anthropic claude-4.8-opus)
2026-10-02 15:40:14 +02:00
Axel Uhl d1faacfb5b Reapply "trying JDK 21 for Azure mobile app builds"
This reverts commit 35482cad75.
2026-10-02 15:20:31 +02:00
Axel Uhl 35482cad75 Revert "trying JDK 21 for Azure mobile app builds"
This reverts commit 4d924ef358.
2026-10-02 15:18:08 +02:00
Axel Uhl 4d924ef358 trying JDK 21 for Azure mobile app builds 2026-10-02 14:27:28 +02:00
Axel Uhl f5aea106a9 release notes for MPC candidate time comparison fix 2026-10-01 17:23:01 +02:00
Axel Uhl 9de68eb70d bug6279: prove candidate time comparator overflow with a regression test
The StartAndEndAwareTimeBasedCandidateComparator compared candidate time
points with (int) (o1.getTimePoint().asMillis() - o2.getTimePoint().asMillis()).
Epoch milliseconds are ~1.7e12, so for candidates more than Integer.MAX_VALUE
ms (~24.8 days) apart the 64-bit difference is truncated to 32 bits and its
sign can flip, turning the comparator into a non-total order. On the "my"
server this pinned all background-executor threads in TreeSet/NavigableSet
navigation and starved the maneuver-calculation queue (live candidate set
span was ~355.9 days, 14.3x the int limit).

CandidateComparatorOverflowTest reproduces the mechanism in isolation against
plain long epoch millis, independent of any domain type:
- truncatingCastReportsACyclicOrder: a single >2^31 ms step overflows and
  flips the sign, producing the cycle T_LOW > T_MID > T_HIGH > T_LOW.
- treeSetNavigationIsConsistentOnlyWithFixedComparator: the broken order makes
  TreeSet first()/last() disagree with the true chronological extremes.
- convergenceLoopTerminatesOnlyWithFixedComparator: an updateCandidates-shaped
  loop over a realistically sized multi-cluster set never drains under the
  buggy comparator (hits a 1,000,000-iteration cap) but drains in ~50 passes
  with Long.compare.

Assisted-By: Claude Opus 4.8 (Claude Code)
2026-10-01 17:09:08 +02:00
Axel Uhl 770bad9fca properly compare candidate time points in mark passing calculator;
the old code subtracted the millisecond representation of the time
points from one another and cast the result to an int which may have
silently led to an overflow in case the difference between the time
points was greater than 49 days. For races with open-ended tracking time
ranges and sporadic fixes along that full time range may have caused
problems here.
2026-10-01 15:59:43 +02:00
Axel Uhl bf333bf5bd worked on onboarding.md improvements 2026-10-01 10:22:32 +02:00
Axel Uhl d980f1744f Wiki improvements 2026-10-01 10:04:39 +02:00
Axel Uhl 0cb1df2686 bug2626: make testMaxSpeedCacheRaceCondition deterministic
The barrier-based synchronization introduced earlier let the second query's
re-entry into computeMaxSpeed(...) await the auto-reset CyclicBarrier a second
time. Only the three fix-adding threads plus the first query's stale
computation ever reach the barrier, so on the second generation the number of
arriving parties depended on how many times computeMaxSpeed(...) got re-entered
via cacheLookup(...)'s recursive getMaxSpeed(entryTo, to) stitching after the
cache was invalidated. That count varies with thread scheduling: locally it
happened to reach the barrier's party count and returned fast, while in CI it
did not, so await(20s) timed out and the write lock was reported held for more
than 10s, failing the run consistently.

Gate the barrier await with an AtomicBoolean so that exactly the first
computeMaxSpeed(...) invocation waits on the barrier. Every later invocation
(the second query and all its recursive sub-range computations) skips it,
removing the scheduling-dependent second-generation rendezvous entirely.

Assisted-By: Claude Opus 4.8 (claude-opus-4-8)
2026-10-01 08:30:23 +02:00
Axel Uhl e4360b299e make testMaxSpeedCacheRaceCondition deterministic
The test reproduced a MaxSpeedCache race by letting a stale
computeMaxSpeed(...) result get cached after invalidating GPS fixes had
been added. It relied on a fixed Thread.sleep(1000) inside computeMaxSpeed
to "hope" the three fix-adding threads had reached the point of contending
for the cache's write lock. Under CI load this timing assumption broke and
the test failed with a CyclicBarrier TimeoutException.

Replace the sleep with an explicit CyclicBarrier (fixThreadsReadyToContend,
parties=4): the first query's stale computation and all three fix-adding
threads rendezvous on it, so the stale result only completes once the
invalidations are guaranteed to be about to contend. Hoist the threads to
named locals and join all of them, establishing happens-before between the
invalidations and the second query before asserting the recomputed maximum
speed. This removes the timing dependency and makes the ordering
deterministic.

Assisted-By: Claude Opus 4.8 (claude-opus-5-5)
2026-10-01 00:52:27 +02:00
Axel Uhl ee6bef722d bug6308: removed extra package import from com.sap.sailing.gwt.ui 2026-09-30 20:54:35 +02:00
Axel Uhl 3af2d7cfcc Merge branch 'eclipse-main' into bug6308 2026-09-30 20:49:06 +02:00
Axel Uhl 27653b7e6e further details for MoMa handling for Android app builds 2026-09-30 20:42:45 +02:00
Axel Uhl fe19925dbd Upgraded Android apps from version 1.4.125 to 1.4.126 2026-09-30 17:04:23 +02:00
Axel Uhl 62adb85016 Merge branch 'eclipse-main' into hyperspace 2026-09-30 17:04:09 +02:00
Axel Uhl 0ea45039cb adjusted MoMa URLs for building Android mobile apps 2026-09-30 17:03:50 +02:00
Axel Uhl cd4a4885e2 Merge remote-tracking branch 'githubsapsailing/hyperspace' into hyperspace 2026-09-30 15:06:52 +02:00
Axel Uhl b0bd857569 drop HttpRequest connection close in Android apps;
this may have dated back to very old Android versions <2.3
and is no longer needed; dropping it should raise chances a socket / TLS
connection can be re-used across multiple HTTPS requests, improving
performance specifically on links with high latency
2026-09-30 14:36:26 +02:00
Axel Uhl 9d1b63a066 Merge remote-tracking branch 'eclipse/main' into eclipse-main 2026-09-30 14:26:33 +02:00
Axel Uhl 9c192c0465 upgraded gradle to 7.2.2 2026-09-30 14:25:56 +02:00
Axel Uhl 2abd5d3ad1 fixed incorrect link in tutorial home.md 2026-09-30 14:21:28 +02:00
Axel Uhl e32e2da979 Merge branch 'eclipse-main' into translation 2026-09-29 12:59:26 +02:00
Axel Uhl a9b2cf834e (re-)added missing sailinsight i18n messages;
those had accidentally gotten deleted by commit
31003728e9dd5915a4c1c3d4c79d2ee24cb34128, crossing a correponding change
in the original repo, followed by a somewhat careless copy/overwrite
from here.
2026-09-29 10:46:51 +02:00
Axel Uhl ebbc42cbc8 Merge branch 'eclipse-main' into bug6308 2026-09-28 23:12:02 +02:00
Axel Uhl a08b307758 accept as available also when numberofracesstillloading<=0 2026-09-28 23:10:48 +02:00
Axel Uhl c96bdae53c Merge branch 'eclipse-main' into bug6308 2026-09-28 22:58:01 +02:00
Axel Uhl 83cb23c78a Add one-shot migration to strip/remove legacy random= nonce docs from restore queue
Legacy documents in CONNECTIVITY_PARAMS_FOR_RACES_TO_BE_RESTORED stored the raw
paramURL including a random=<nonce> query parameter. Current keying (via
TracTracRaceTrackerImpl.getParamURLStrippedOfRandomParam) writes and deletes
using the paramURL stripped of that nonce, so the raw legacy copies can never be
matched by replaceOne/deleteOne. They linger indefinitely, inflate the restore
queue, and are re-attempted on every server start.

This mongosh migration auto-discovers every database in a replica set, skips
those without the collection, and for each nonce-bearing document either deletes
it (when a stripped twin already exists) or strips the random= parameter in
place (when it is the only copy). Dry-run by default; APPLY=1 executes and first
copies each affected document into a per-DB _NONCE_BACKUP collection for
rollback.

Assisted-By: Claude Opus 4.8 (claude-opus-5-5)
2026-09-28 22:52:42 +02:00
Axel Uhl b41b1e1e40 improving logs for race restore records, quoting regatta name 2026-09-28 21:59:02 +02:00
Axel Uhl 27898cfc7c Merge branch 'eclipse-main' into bug6308 2026-09-28 17:22:09 +02:00
Axel Uhl edc2c0dffa sap issue 81: fixed use of wrong environment name in imageupgrade 2026-09-28 17:13:09 +02:00
Axel Uhl aca9abe6c5 bug6308: admin release notes update 2026-09-28 17:12:49 +02:00
Axel Uhl e0eda7f660 bug6308: using waitUntilRacesLoaded=true for primary health check 2026-09-28 17:11:45 +02:00
Axel Uhl 4ae62a1d6f bug6308: moved WAIT_UNTIL_RACES_LOADED constant to new common LandscapeConstants 2026-09-28 16:35:16 +02:00
Axel Uhl bbe59e19a6 bug6308: start out with >0 races to restore, if restoring races is requested 2026-09-28 15:00:58 +02:00
Axel Uhl ead3b3c400 sap issue #81: launch T4_G_MEDIUM for ARM64 images for upgrade 2026-09-28 15:00:04 +02:00
Axel Uhl cc9548dc0a moved ai-agent-sailing-knowledge.md to wiki/projects and linked from Home.md 2026-09-28 14:59:19 +02:00
Axel Uhl 415bc45c24 Merge remote-tracking branch 'sap/maptiles-imageupgrade' into maptiles-imageupgrade 2026-09-24 15:46:32 +02:00
Axel Uhl ca26fcf06a maptiles: no mongo clearing for maptiles server image 2026-09-24 15:42:03 +02:00
Axel Uhl bc71893d6e maptiles: probe for uv before running deploy_linux_host.py
The local branch of setup-maptiles-instance.sh runs
./linux_host/deploy_linux_host.py, which requires the "uv" tool. When uv
is not installed locally, the deployment failed. Probe for uv before the
pushd/deploy step and, if it is missing, prompt the user (defaulting to
No) before installing it via the official astral.sh installer. After a
confirmed install, source ~/.local/bin/env so uv is on PATH in the
current shell, then re-verify and abort with a clear message if it is
still missing or the user declines.

Assisted-By: Claude (claude-4.8-opus)
2026-09-24 15:38:10 +02:00
Axel Uhl 7d88d3b2cc Merge branch 'eclipse-main' into maptiles-imageupgrade 2026-09-24 14:56:21 +02:00
Axel Uhl 00f2f59508 Merge remote-tracking branch 'eclipse/main' into eclipse-main 2026-09-24 14:52:51 +02:00
Axel Uhl c63fc22189 De-flake leaderboard Selenium tests: wait for regatta leaderboard row before reading details
createRegattaLeaderboard() returned as soon as the create dialog's OK was
pressed, without waiting for the newly created regatta leaderboard row to be
rendered into the leaderboards cell table. A subsequent getLeaderboardDetails()
call could then race the AJAX round-trip, find no matching row yet, and return
null, causing NullPointerExceptions downstream (e.g. in linkRace()).

Wait until the new row (named after the regatta, see RegattaDescriptor.toString())
has been rendered before returning, mirroring createFlexibleLeaderboard(). This
fixes the intermittent failures of TestColumnsInLeaderboard.testLeaderboardPanelRefresh
and TestSwitchingOffStartOfRaceInference; the other callers of
createRegattaLeaderboard() were verified to still pass.

Assisted-By: Claude Opus 4.8 (Claude Code)
2026-09-24 14:50:37 +02:00