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)
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)
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>
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)
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
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
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
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
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)
Updated instructions for smartphone tracking, including clarifications on server requirements, competitor registration, and tracking management. Enhanced details on using the Race Manager app and handling competitor invitations.
New page wiki/howto/eventmanagers/smartphone-tracking.md walks event hosts
through setting up smartphone tracking in the order they actually do it:
event structure, denoting races (incl. the race name prefix dialog),
competitor registration, course layout, the three ways to bring phones in
(device mapping QR code, e-mail invitation, open-regatta registration link),
starting tracking and typical problems. Button and dialog names were taken from the
current Admin Console string messages where available.
Linked from Home.md; the two older pages
info/mobile/smartphone-tracking-steps and -general now point to it.
Assisted-By: Claude Code with Claude Sonnet 5.5
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
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)
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>
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)
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)
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/*