67285 Commits
Author SHA1 Message Date
Axel Uhl 6016c85740 Add Yngling keelboat class
Register the Yngling one-design keelboat in the BoatClassMasterdata enum
with its LOA (6.35 m), beam (1.73 m), monohull hull type and downwind-sail
flag (symmetric spinnaker). Wire the class to a 140x140 transparent PNG
derived from the International Yngling Association class insignia via the
GWT BoatClassImageResources ClientBundle and the BoatClassImageResolver
display-name map, and add it to the KeelBoatWithGennaker vector graphics in
BoatClassVectorGraphicsResolver so it renders as a keelboat on the race map
rather than falling back to the default spinnaker-dinghy shape.

Also document the addition in the "What's New" (SailingAnalyticsNotes.html)
and Admin Console release notes (release_notes_admin.html) under September
2026.

Assisted-By: Claude Opus 4.8 (claude-opus-5-5)
2026-09-23 00:50:05 +02:00
Axel Uhl 596b19e877 reduced heap size for launch configs slighly 2026-09-23 00:45:16 +02:00
Axel Uhl 1a07ce63b6 bug6227: stream wind-track count as bare int and share MDI progress-step constants
Switch the wind-track section count on both MDI wire-format ends from
writeObject(Integer)/readObject to writeInt/readInt. The count is now a
bare 4-byte inline int carrying no class descriptor and occupying no
handle-table entry; the null sentinel remains the authority on section
end, the count only drives progress. Javadoc/comments on both ends
updated accordingly.

Introduce shared named constants for the MDI progress schedule
(PROGRESS_TRANSFER_STARTED, PROGRESS_TRANSFER_COMPLETED,
PROGRESS_GRAPH_APPLIED, PROGRESS_WIND_TRACKS_DONE, PROGRESS_COMPLETE)
and derive the two 0.2 band widths from the milestones
(PROGRESS_WIND_TRACKS_BAND_WIDTH, PROGRESS_SENSOR_FIXES_BAND_WIDTH) so
the bands can never drift from the milestones. Replaces the redundant
0.03/0.3/0.6/0.8/1.0/0.2 float literals scattered across the import
progress call sites with a single source of truth.

Assisted-By: Claude Opus 4.8 (claude-opus-5)
2026-09-23 00:31:10 +02:00
Axel Uhl e4baf5e416 bug6227: add TimePoint.plusResolution()/minusResolution() and convert resolution-tick call sites
Introduces two convenience methods on TimePoint that step by one resolution
unit rather than a hard-coded millisecond:

  TimePoint plusResolution()  == plus(getResolution())
  TimePoint minusResolution() == minus(getResolution())

They are implemented once in AbstractTimePoint and saturate at EndOfTime /
BeginningOfTime just like plus/minus do. Expressing "the next/previous
representable instant" through getResolution() keeps these sites correct
should a finer-resolution TimePoint ever be introduced, instead of assuming
one-millisecond granularity.

All plus(1)/minus(1) call sites whose receiver is a TimePoint and whose intent
is a single resolution tick were converted to plusResolution()/minusResolution()
across production and tests. Deliberately left unchanged:
  - plus(1)/minus(1) on Duration receivers (not TimePoints)
  - TimePointTest overflow/saturation tests that exercise plus(long)/minus(long)
    with the literal 1 on purpose

Assisted-By: Claude Opus 4.8 (claude-opus-5)
2026-09-22 23:18:13 +02:00
Axel Uhl 62f95da4bd bug6227: add queryable TimePoint.getResolution() and use it for inclusive->exclusive range end
Introduce TimePoint.getResolution() returning the smallest positive Duration by which two
distinct instants of an implementation can differ, implemented in AbstractTimePoint as
Duration.ONE_MILLISECOND (all current impls are millisecond-backed). TopLevelMasterData's
inclusive-to-exclusive conversion for device mapping ranges now uses
toInclusive.plus(toInclusive.getResolution()) instead of a hard-coded +1ms, so a
single-instant PING mapping stays a non-empty range and the code remains correct should a
finer-resolution TimePoint ever be introduced.

Assisted-By: Claude Opus 4.8 (claude-opus-5)
2026-09-22 23:02:46 +02:00
Axel Uhl 43d0e89336 bug6227: fix single-instant PING mappings being dropped during MDI
RegattaLogDeviceMappingEvent.getToInclusive() is an inclusive end, but
TimeRange.to() is exclusive. TopLevelMasterData.addRangeIfMappingEvent
built TimeRange.create(from, toInclusive); for a single-instant PING
mapping (from == toInclusive, e.g. a pinged mark) this is an empty range,
which MultiTimeRange silently discards. The device is then never streamed
and its single fix never reaches the importing instance.

Convert the inclusive end to an exclusive one by adding one TimePoint
resolution unit (currently 1ms), so [t, t] becomes the non-empty range
[t, t+1ms), and load the corresponding fixes with toIsInclusive == false
in MasterDataResource.writeRaceLogTrackingFixes. Update the related
Javadoc on both sides accordingly.

Adds MasterDataImportTest.testMasterDataImportPingedMarkSingleInstantFix,
which reproduces the drop (single-instant inclusive mapping with one
stored fix) and now passes.

Assisted-By: Claude Opus 4.8 (claude-opus-5)
2026-09-22 22:58:49 +02:00
Axel Uhl 7273a75e12 bug6227: bound serialization handle table with per-1000-fix reset
While streaming a single device's race-log tracking GPS fixes, reset() was
called only once per device. After the bug6227 per-device MultiTimeRange
merge, each device's range can span an entire season, so the
ObjectOutputStream$HandleTable accumulated a whole season of fixes for a
device before being cleared, causing the exporting server to be OOM-killed
(a Memory Analyzer report attributed ~616 MB / 73% of the heap to a single
HandleTable rooted at writeRaceLogTrackingFixes).

Keep the existing per-device reset() and additionally reset() after every
FIXES_PER_HANDLE_TABLE_RESET (1000) fixes, bounding the handle table to at
most 1000 retained fixes at any time. Class descriptors are re-emitted after
each reset() but amortize cheaply and gzip-compress well; the null-sentinel
framing is unaffected by TC_RESET position.

Assisted-By: Claude Opus (claude-opus-5)
2026-09-22 22:33:31 +02:00
Axel Uhl 2b33134758 bug6227: minor changes, debug logging reduced 2026-09-22 21:58:09 +02:00
Axel Uhl dc24eb05f5 bug6227: publish MDI result only at true completion, after sensor-fix import
The AdminConsole client reported import success before the sensor-fix import
had even started. The client's completion signal is a non-null
DataImportProgress result, but ImportMasterDataOperation.internalApplyTo set
the result and overall progress 1.0 at the end of the object-graph apply() --
which returns to MasterDataImporter.importFromStream, where the wind-track and
sensor-fix imports still run afterward. So "Done"/1.0/result fired prematurely.

Move ownership of terminal completion to MasterDataImporter.importFromStream,
the true end of the whole import (object graph + wind + fixes):

- ImportMasterDataOperation.internalApplyTo no longer sets the result, overall
  1.0, or logs "Done"; it ends at overall 0.6 and returns creationCount.
- MasterDataImporter publishes the single terminal completion after the wind
  band (0.6->0.8) and sensor-fix band (0.8->1.0):
  createOrUpdateDataImportProgressWithReplication(id, 1.0, IMPORT_SENSOR_FIXES,
  1.0, creationCount). The result is set only at true 1.0, with replication,
  so replica-polling clients observe completion correctly.
- Plumb a nullable MasterDataImportObjectCreationCount through a new 5-arg
  createOrUpdateDataImportProgressWithReplication overload and the
  CreateOrUpdateDataImportProgress operation, which sets the result on replicas
  via progress.setResult(result) when non-null.
- createOrUpdateDataImportProgressWithoutReplication now uses getDataImportLock()
  instead of the raw field so subclass overrides (the test mock) are honored;
  a no-op in production where the getter returns the field.

serialVersionUIDs left unchanged: adding a nullable field is serialization-
compatible and master/replicas upgrade together.

Verified: MasterDataImportTest 13/13 green; both projects compile clean.

Assisted-By: Claude Opus (claude-opus-5)
2026-09-22 21:50:49 +02:00
Axel Uhl 3395b1592e bug6227: bound MDI memory for wind tracks; merge per-device fix ranges; monotonic progress
Follow-up to fd6739d9a6 (stream GPS fixes as top-level objects), addressing
four defects on the master data import path:

- Merge DeviceMappingDescriptor objects per DeviceIdentifier into a single
  MultiTimeRange so a device mapped in several regattas (or via several
  open-ended mapping events) has its fixes streamed once over coalesced,
  non-overlapping sub-ranges rather than once per mapping. Obsoletes and
  removes the nested DeviceMappingDescriptor class; the field becomes
  Map<DeviceIdentifier, MultiTimeRange> raceLogTrackingDeviceRanges. The
  inclusive mapping end is carried through and streamed with
  toIsInclusive=true, at worst streaming a harmless superset (store dedups).

- Apply the top-level-streaming + per-object reset() design to
  WindTrackMasterData: strip the wind tracks out of the serialized graph,
  stream one WindTrackMasterData at a time on export (held only transiently),
  and read+store them incrementally on import (master only). Bounds retained
  memory to a single wind track at a time instead of the whole season.

- Remove the premature "Done importing master data into ..." log and the
  overall 1.0 from ImportMasterDataOperation.internalApplyTo; that fired
  before the sensor-fix import ran. The single terminal "Done" and 1.0 now
  live in MasterDataImporter.importFromStream, the true end of the import.

- Make progress rise monotonically and reserve the two phases that usually
  dominate the import: the object graph now ends at 0.6, the wind-track
  import occupies 0.6 -> 0.8 (20%), and the sensor-fix import occupies
  0.8 -> 1.0 (20%), instead of reaching 1.0 in the operation and then
  dropping back in the importer.

Assisted-By: Claude Opus 4.8 (claude-opus-5)
2026-09-22 21:05:45 +02:00
Axel Uhl fd6739d9a6 bug6227: stream MDI GPS fixes as top-level objects to bound memory
Master data import/export (MDI) previously materialized every mapped
device's GPS fixes in memory before serialization: the fixes lived inside
the single TopLevelMasterData object graph written with one writeObject, so
ObjectOutputStream/ObjectInputStream had to retain a handle for every fix to
resolve potential back-references. For events with many fixes this dominated
heap on both the exporting and importing server.

Change the wire format so fixes are streamed as top-level stream objects
after the (now fix-free) TopLevelMasterData:

- TopLevelMasterData no longer carries the fixes themselves. It records
  lightweight, fully Serializable DeviceFixMappingDescriptor entries
  (device, from, toInclusive) plus a transient SensorFixStore used only on
  the export side. Default serialization now does the right thing; the
  stripped replica copy carries no descriptors.
- MasterDataResource.writeRaceLogTrackingFixes streams each device section
  straight from the SensorFixStore: device header, each fix as a top-level
  object, a null fix terminator, then ObjectOutputStream.reset() once per
  device so no fix handle is retained across devices while the fix class
  descriptor stays shared within a device. A trailing null device ends the
  section. Framing uses null sentinels, never fix counts (a concurrent write
  could invalidate a count).
- MasterDataImporter.importRaceLogTrackingGPSFixes reads that section
  incrementally and stores fixes in batches of BATCH_SIZE_FOR_IMPORTING_FIXES
  directly into the master's SensorFixStore, holding at most one batch at a
  time. This stream-based import only ever runs on the primary/master:
  such requests must always be routed there, and a replica receives only the
  stripped ImportMasterDataOperation (no fixes), obtaining tracking data by
  the master loading tracked races whose transactions are replicated. A
  non-null master descriptor here is therefore a broken routing state and is
  reported by throwing IllegalStateException rather than silently draining.
  IMPORT_SENSOR_FIXES sub-progress is advanced once per device section,
  driven by the descriptor count, restoring the per-device granularity that
  existed before.
- ImportMasterDataOperation no longer imports fixes; that logic moved to
  MasterDataImporter, which has the target store in scope.

Exporter and importer are upgraded together; mixed-version transfer is not
required.

Verified: MasterDataImportTest green (full class 13/13 repeatedly;
testMasterDataImportWithoutHttpStack repeatedly), covering the 4999/5000/5001
batch-boundary fix counts and the DoubleVectorFix round trip.

Assisted-By: Claude Opus 4.8 (claude-opus-5)
2026-09-22 17:36:22 +02:00
Axel Uhl 652f1a39c4 publish /sailingserver/api/v1/remoteserverreference for read access 2026-09-22 13:22:24 +02:00
Axel Uhl 69f110a252 Merge remote-tracking branch 'eclipse/bug6305' into eclipse-main main-202609191010 main-202609191017 2026-09-19 09:37:21 +02:00
Axel Uhl b4e0d844b4 fixed typo in release_notes_admin.html 2026-09-19 09:29:45 +02:00
Axel Uhl 62bcf11147 Merge branch 'eclipse-main' into bug6305 2026-09-19 00:21:11 +02:00
Axel Uhl 5207dd7132 Merge branch 'translation' into eclipse-main main-202609190004 main-202609190115 2026-09-19 00:17:43 +02:00
Axel Uhl eda617b1b8 bug6305: remove stale commented-out dead code in TestStartAndStopTrackingForTracTracEvents
Drop the leftover TODO and commented-out NO_REGATTA / BMW_CUP_RACE_8
constants and the surrounding blank lines; no behavioral change.

Assisted-By: Claude (claude-4.8-opus)
2026-09-19 00:14:53 +02:00
Axel Uhl efc00c3fe4 bug6305: fix Selenium timing flake in start/stop tracking cycle test
TestStartStopTrackingCycleForRaceLog failed intermittently with
NoSuchElementException on ACTION_START_TRACKING: at click time the race
rows still showed the un-denoted action set, i.e. the leaderboard
denotation had not applied client-side yet.

Root cause: the leaderboard-level denoteForRaceLogTracking RPC in
SmartphoneTrackingEventManagementPanel used a plain AsyncCallback, so it
was invisible to the PENDING_AJAX_CALLS counter that only
MarkedAsyncCallback touches. waitForAjaxRequests() therefore returned
before the denote->refresh chain settled, and the test clicked against
the stale DOM. This was exposed by the Selenium 3.141 -> 4.13 upgrade,
not by a bug6305 functional change.

Two fixes:
1. Production (root): wrap the denote RPC callback in MarkedAsyncCallback,
   matching the sibling RPCs in the same panel, so the pending-call
   counter stays non-zero until the whole denote->refresh chain settles.
   Makes waitForAjaxRequests() honest for this RPC for any test.
2. Test robustness: startTrackingForRace now takes the race name and
   waits state-based for the ACTION_START_TRACKING image to appear
   (re-fetching the row each poll to survive GWT re-renders) before
   clicking, via a new DataEntryPO.hasActionImage helper, instead of
   assuming row presence implies the action is available.

Verified: test passes on two independent local runs (98.9s and 275.5s),
both denote->start->stop->undenote cycles green.

Assisted-By: Claude (claude-4.8-opus)
2026-09-19 00:14:05 +02:00
Axel Uhl 1cfb69e401 Merge branch 'eclipse-main' into bug6305 2026-09-18 22:59:13 +02:00
Axel Uhl cd5def0111 Merge branch 'eclipse-main' into translation main-202609182143 2026-09-18 20:49:53 +02:00
Axel Uhl aec2cb681c Assign Fareast 28R to keelboat vector graphics on the race map
The Fareast 28R boat class (added in e2a2c4d) was never assigned a vector
graphics in BoatClassVectorGraphicsResolver, so it fell back to the default
spinnaker-dinghy shape on the race map. Add it to the KeelBoatWithGennaker
group so this 9.07 m monohull renders as a keelboat, matching its actual
type.

Assisted-By: Claude Opus 4.8 (claude-opus-5)
main-202609181942
2026-09-18 20:34:51 +02:00
Axel Uhl e62ef53e71 Add Swiss keelboat classes Onyx, Lacustre and 15m2 SNS
Register three Swiss one-design keelboat classes in the BoatClassMasterdata
enum with their LOA, beam, hull type and downwind-sail flag. Wire each class
to a 140x140 transparent PNG class-association logo via the GWT
BoatClassImageResources ClientBundle and the BoatClassImageResolver
display-name map, and assign all three to the KeelBoatWithGennaker vector
graphics in BoatClassVectorGraphicsResolver so they render as keelboats on
the race map rather than falling back to the default spinnaker-dinghy shape.

Also document the addition in the "What's New" (SailingAnalyticsNotes.html)
and Admin Console release notes (release_notes_admin.html) under September
2026.

Assisted-By: Claude Opus 4.8 (claude-opus-5)
2026-09-18 20:34:27 +02:00
Masha Kashirina 2c2f00605e bug6305: fixes for repl panel removal multi _ polishing other deletions 2026-09-18 19:41:09 +02:00
Axel Uhl 01d2fc7128 Make TestNegativeAcls robust against user-creation lock and dialog races
The user-creation rate limit and animated ACL dialogs caused flaky
failures in TestNegativeAcls:

- The per-IP user-creation lock (server side) previously was only worked
  around by a client-side Thread.sleep for the full locking duration.
  Instead, when a UserManagementPanelPO has an AdminConsolePage, it now
  clears the server-side lock through the "User Creation Abuse" blocklist
  UI before creating a user. The legacy sleep-based throttle is retained
  as a fallback when no AdminConsolePage is available.

- IpBlocklistPanelPO.unblockIP now polls a freshly refreshed table until
  the unblocked row is gone, tying completion to a server round-trip. The
  unlock and data-load GWT-RPCs in IPBlocklistTableWrapper are now wrapped
  in MarkedAsyncCallback so waitForAjaxRequests() can observe them instead
  of being a silent no-op.

- AclActionInputPO.addAction waits for the suggest box to be displayed and
  enabled before typing, fixing an ElementNotInteractableException when the
  denied-actions input is still settling after adding a user group.

Assisted-By: Claude Opus 4.8 (claude-opus-5)
main-202609181831
2026-09-18 18:59:13 +02:00
Axel Uhl bfa1f9baa1 Fix spurious TestNegativeAcls failures from GWT CellTable async redraws
The negative-ACL Selenium tests intermittently failed with
StaleElementReferenceException (masked as a generic RuntimeException) and
a NullPointerException when reading a freshly-created role/permission row
that had not yet rendered.

Root causes and fixes:

- Several user-management RPCs were fired through a plain AsyncCallback, so
  they were invisible to the pending-Ajax counter and waitForAjaxRequests()
  returned before their deferred CellTable repaint ran. Wrap them in
  MarkedAsyncCallback: createRoleDefinition, getRolesAndPermissionsForUser,
  getUserList, addPermissionForUser, removePermissionFromUser and
  removeRoleFromUser.

- Pair those marks with waitForAjaxRequests() barriers on the test side after
  add/delete clicks (WildcardPermissionPanelPO, UserRoleDefinitionPanelPO).
  The ...AndExpectPermissionError() variants deliberately click directly and
  wait for the alert instead, since the RPC fails rather than completing.

- Add RoleDefinitionsPanelPO.findRoleWhenPresent(): wait for Ajax, then poll
  until the row is present before returning it, and use it from
  TestNegativeAcls so a not-yet-rendered row no longer NPEs.

- Stop CellTablePO/GenericCellTablePO from masking staleness: narrow
  createEntry's catch to the exceptions the reflective construction can
  actually throw, unwrap InvocationTargetException(StaleElementReferenceException)
  and rethrow it, and retry getEntries() a bounded number of times so a
  redraw-during-read is tolerated rather than surfaced as an opaque failure.

- PageObject.waitUntil now ignores StaleElementReferenceException.

Assisted-By: Claude Opus 4.8 (Claude Code)
main-202609181620
2026-09-18 17:29:27 +02:00
Axel Uhl 37b43fbc9f bug fix: CORS configuration is now applied after receiving it through replication initial load 2026-09-18 15:10:43 +02:00
Axel Uhl 283915c05c Merge branch 'eclipse-main' into translation 2026-09-17 17:28:42 +02:00
service-tip-git 9ceb91b869 [INTERNAL] Translation delivery: commit by LX Lab
Change-Id: Iebb421f04e802880d6d84e7458b8c9da81aa8f91
2026-09-17 15:19:11 +00:00
Axel Uhl 04013c2d59 updated CONTRIBUTING.md for upstream specifics main-202609171624 main-202609171608 2026-09-17 15:38:44 +02:00
Axel Uhl 6430b85a7d new pluginsForEclipse2026-09.p2f for Eclipse 2026-09 2026-09-17 15:25:48 +02:00
Axel Uhl cd70c8d761 Fix typo "loose" -> "lose" in confirmNonRenewingSubscriptionText
Correct the English source message for the stop-auto-renewal
confirmation dialog. Other locales are produced by the downstream
translation process from the English source.

Assisted-By: Claude Opus 4.8 (claude-opus-5)
2026-09-17 11:59:36 +02:00
Axel Uhl 3cf859993b Render newlines in ConfirmationDialog message text
The ConfirmationDialog message is shown in a GWT Label whose default
white-space handling collapses newlines (and runs of whitespace) into
single spaces, so multi-line messages appeared on a single line. Add
white-space: pre-line to the .infoText style so intentional line
separators in the message are rendered as line breaks while long lines
still wrap.

Assisted-By: Claude Opus 4.8 (claude-opus-5)
2026-09-17 11:59:21 +02:00
Axel Uhl 20d8b4e285 bug6288 aftermath: fixed more serialization problems main-202609170120 2026-09-17 00:20:47 +02:00
Axel Uhl de16b68fcc Merge branch 'bug6304' into eclipse-main main-202609160000 2026-09-15 22:40:33 +02:00
Axel Uhl 4c9616faf4 Surface Google Maps authentication failures in the simulator map too
SimulatorMap uses the same GoogleMapsLoader as RaceMap, so a rejected
Google Maps key left its map blank with no diagnostic in the same way.
Register the same gm_authFailure listener here, reporting through the
existing errorReporter with the shared errorGoogleMapsAuthenticationFailed
message. Mirrors the RaceMap change.

Assisted-By: Claude Opus 4.8 (claude-opus-5)
main-202609152003
2026-09-15 18:53:16 +02:00
Axel Uhl 34a693e970 Surface Google Maps authentication failures instead of a blank map
When the Google Maps JavaScript API rejects the request (invalid or
missing key, disallowed referrer, API not enabled, or a quota / billing
problem), the map area stayed blank with no controls and no diagnostic.
The RaceBoard's map controls, including the "moreOptionsButton", are
created only inside the loader's onLoad callback, so a rejected key left
that button absent -- which surfaced downstream only as a generic 60s
Selenium timeout (NoSuchElementException: moreOptionsButton) with an
otherwise fully rendered page.

GoogleMapsLoader now installs a window.gm_authFailure hook that the Maps
API invokes exactly on such an authentication failure, and RaceMap
registers a listener that reports it through the existing errorReporter
with a dedicated, translatable message. This is event-driven rather than
timeout-based, so it never misfires on a slow but valid load (relevant
for slow connections and low-end devices).

Assisted-By: Claude Opus 4.8 (claude-opus-5)
2026-09-15 18:51:33 +02:00
Axel Uhl b4b990bec5 bug6288: admin release notes 2026-09-15 14:48:11 +02:00
Axel Uhl bd9d852ea7 Merge branch 'bug6288' into eclipse-main 2026-09-15 14:46:24 +02:00
Axel Uhl 78475da80a bug6304: fixed by using regular time point again in CandidateImpl;
but keeping more efficient comparison in CandidateChooserImpl
2026-09-15 14:39:49 +02:00
Axel Uhl ffdf65b603 added force (-f) option to merge-upstream-to-downstream.sh;
this allows users to produce PRs with no file changes, yet updating,
e.g., merge paths
2026-09-15 14:19:44 +02:00
Axel Uhl a915f10f61 Merge branch 'eclipse-main' into bug6288 2026-09-15 00:24:39 +02:00
Axel Uhl 40cc60ca8f Fix Android SDK setup after runner image dropped legacy 'tools' package
The GitHub-hosted runner image rolled out on ~2026-09-14 ships
cmdline-tools 12.0, whose sdkmanager no longer resolves the obsolete
'tools' (legacy Android SDK Tools) package. The android-actions/setup-android
step inherits the action default packages 'tools platform-tools' and thus
failed with "Failed to find package 'tools'" (exit code 1) before the build
script even ran. The same request in buildAndUpdateProduct.sh would fail
the same way once reached.

Override the action's packages input to 'platform-tools' and drop the
trailing 'tools' argument from the sdkmanager call in
buildAndUpdateProduct.sh. build-tools, platform-tools and the platform
are still installed by the script. The cmdline-tools-version pin is left
unchanged.

Assisted-By: Claude (claude-4.8-opus)
main-202609150119
2026-09-15 00:15:54 +02:00
Axel Uhl b12bd7f49d bug6288: use Iterable in live check DTOs 2026-09-14 23:30:20 +02:00
Axel Uhl 8b26a148af bug6288: handle pre-bug6288 replica sets in live-content pre-flight check
A replica set whose server predates bug6288 does not expose
GET /sailingserver/api/v1/livecontent; the master answers HTTP 400 with a
text/plain body. SailingServerImpl.getLiveContent then deserialized a null
JSON body and threw an NPE that surfaced in the UI as an unreadable error.
A secondary bug aborted the whole batch on the first such server.

Detect the non-success response cleanly and model the outcome as a
first-class per-replica-set UNDETERMINED state, treated fail-safe
(block-unless-forced):

- New checked exception LiveContentCheckUnsupportedException in
  landscape.common (the only bundle both server.gateway and landscape see).
- SailingServerImpl.getLiveContent inspects the response status and throws
  it instead of deserializing a null body.
- LandscapeServiceImpl.checkForLiveContent catches per replica set, records
  the name as undetermined, and continues the loop (fixes batch-abort);
  checkForLiveContentUnlessForced now also blocks on undetermined sets.
- LiveContentCheckResult carries a parallel undeterminedReplicaSetNames
  list (new 3-arg constructor; 2-arg preserved; serialVersionUID
  regenerated). JSON and GWT wire formats extended in lockstep; JSON
  deserializer tolerates the field's absence for backward compatibility.
- UI shows a single combined dialog with a live-content section and a
  'could not verify (server not upgraded)' section; forcing proceeds past
  both. English and German messages added.

Also fixes a missing result.add(processJson) in SailingLandscapeResource
that left the returned JSON array empty.

Assisted-By: Claude (claude-4.8-opus)
2026-09-14 23:30:20 +02:00
Axel Uhl e8e7e1f53a bug6288: document force parameter and previously undocumented landscape endpoints
Describe the new per-operation "force" and per-replica-set
"forceReplicaSet" parameters in the affected webservices/api docs
(movetoarchiveserver, removeapplicationreplicaset, movemastertootherinstance,
upgradeapplicationreplicaset, stopreplicatingandremovemasterfromtargetgroups,
moveallapplicationprocessesawayfrom), explaining the live-content pre-check and
the resulting HTTP 409 when live content is present, and how force bypasses it.

Add docs for endpoints that had none and link them from index.html:
livecontentcheck, deployreplicatoexistinghost,
gethostseligibleforreplicasetprocessdeployment,
changeautoscalingreplicasinstancetype. The livecontentcheck doc explains its
relationship to the single-server GET /v1/livecontent counterpart.

Audit index.html for link completeness: all landscape-gateway endpoints are now
linked except createmfasessioncredentials (covered by createsessioncredentials.html);
no dangling links or orphaned docs remain.

Assisted-By: Claude Opus 4.8 (claude-opus-5)
2026-09-14 23:30:20 +02:00
Axel Uhl a7200b0da2 bug6288: pre-flight live-content check before landscape operations
Add an up-front live-content pre-flight to the five landscape operations
(move-master, move-away, remove, archive, upgrade) in
LandscapeManagementPanel. Before mutating anything, each operation now
checks its whole selection for live content in a single round-trip and
warns the user once, up front, rather than surfacing a warning mid-walk
(possibly minutes in, unattended, after client timeouts).

Force is scoped strictly to the replica sets that the pre-flight flagged
and the user confirmed: per-set operations force a set only when
confirmedConflictingReplicaSetNames.contains(set.getName()), and the
batch move-away passes only the confirmed conflicting names. Every other
set runs force=false and keeps its reactive back-end guard as a fallback
for surprise races that begin after the pre-flight check.

Assisted-By: Claude Opus 4.8 (claude-opus-5)
2026-09-14 23:30:20 +02:00
Axel Uhl ac146266d6 bug6288: replace Consumer[] holder with generic Holder<T>
The confirm-and-force retry in LandscapeManagementPanel needs each
issueRequest lambda to reference itself, which previously forced a
one-element Consumer[] array holder plus @SuppressWarnings("unchecked").
Introduce com.sap.sse.common.util.Holder<T> (mirroring IntHolder/
DoubleHolder, GWT/Android/OSGi-safe) and use it at all five sites,
removing the array idiom and the unchecked-cast suppressions.

No behavioral change: each AsyncCallback is still constructed inside the
lambda body, so a fresh callback is created per (initial and forced)
invocation.

Also carry over the CLAUDE.md note about keeping auto-formatting scoped
to the actual change.

Assisted-By: Claude Opus 4.8 (claude-opus-5)
2026-09-14 23:30:20 +02:00
Axel Uhl eb30965b32 bug6288: comment the live-content conflict branches in LandscapeManagementPanel
Each branch that handles a refused landscape operation now explains that
the refusal is a live-content conflict (one or more affected replica sets
are currently serving live content) and that the branch offers the user
the chance to proceed and force the operation anyway.

Assisted-By: Claude Opus 4.8 (claude-opus-5)
2026-09-14 23:30:20 +02:00
Axel Uhl fec26daad6 bug6288: let the user force landscape operations past a live-content warning
The live-content warning shown by LandscapeManagementPanel was a dead end for
five operations (upgrade, move-master-to-other-instance, move-all-away,
archive, remove): on a live-content conflict the panel only displayed an
informational alert and left the operation aborted, with no way to override it
even though the backend already accepts a "force" flag.

Wire the confirm-and-force path (already used by "stop replicating") into all
five: on a conflict, show the warning via a ConfirmationDialog whose callback,
when the user chooses to proceed, re-issues the same request with force enabled
(boolean force=true for upgrade/move-master/archive/remove; the conflicting
replica-set names as forceMasterReplicaSetNames for move-all-away). For the
batched upgrade, the busy-countdown is re-incremented before the forced retry
so the busy indicator stays balanced.

With every operation now offering a "proceed anyway" path, the no-callback
showLiveContentWarning overload has no remaining callers and is removed.

Assisted-By: Claude Opus 4.8 (claude-opus-5)
2026-09-14 23:30:20 +02:00
Axel Uhl e3409dece3 bug6288: replace MoveAllApplicationProcessesResultDTO with plain String
The result of moveAllApplicationProcessesAwayFrom was wrapped in a
MoveAllApplicationProcessesResultDTO that carried a single String, the ID of
the new host. That wrapper forced a hand-written GWT-RPC
MoveAllApplicationProcessesResultDTO_CustomFieldSerializer to exist purely to
serialize that one string, and the client never even read getNewHostId(). The
service now returns LiveContentAwareOperationResult<String> directly; GWT
serializes String natively. The DTO class, its custom field serializer and the
corresponding Export-Package entry are removed, and the new-host-ID result is
documented on the Async interface's callback parameter.

Assisted-By: Claude Opus 4.8 (claude-opus-5)
2026-09-14 23:30:20 +02:00