Class CaldavMirrorAnswerService

java.lang.Object
org.exoplatform.caldav.service.CaldavMirrorAnswerService

@Service public class CaldavMirrorAnswerService extends Object

Reads the owner's answers off the copies in a collection nothing else reads (EXO-89814).

The defect this exists for. Where the copies go to eXo's own exo-meetings collection — DEDICATED_CALENDAR, which is the default — nothing ever reads that collection back in. CaldavSyncService excludes it from materialisation (isDedicatedMirror) and excludes the MIRROR pair from the inbound loop, both deliberately and both for the same real reason: the collection is eXo's own projection, and importing it would show the user a second, personal copy of every space meeting they already have. The consequence nobody chose is that EXO-89807 — which reads an answer off one of eXo's own copies at the moment the collection's sync report names it as changed — has no pass to sit on there, and EXO-89810's daily full read cannot reach those copies either. The only reader left is the mirror verification's ETag gate, and on a server that records an answer without moving the ETag (BlueMind, measured) that gate never opens. On such a server, with the default destination, an answer given in the copy reaches eXo by no route at all.

What this does, and the line it does not cross. It reads answers, never events. The collection is asked what changed, the named objects are fetched, and the one field the mirror allows back — the owner's own PARTSTAT — is handed to CaldavAnswerAdoptionService, the same narrow mapping EXO-89681 defined and the other two readers use. Nothing here creates a calendar, imports an object, or writes to the server. The duplicate-calendar failure the exclusion prevents is untouched, because the exclusion is untouched: this pass never asks for the collection to be materialised and never calls the import.

Why a sync report and not a wider look. The report is the only evidence available on a server whose ETag does not move, and it is the one EXO-89807 already rests on. It is also what bounds the cost: a collection where nothing has happened answers with an empty list, so a converged account pays one REPORT per sweep and fetches nothing. What it does cost is one fetch per copy eXo itself writes, once, on the sweep after the write — eXo's own push is a change the report reports, and no signal tells it from a client's. That is honest and it is bounded by how much eXo writes, never by how large the collection is; maxObjectsPerPass is the ceiling for the one case that is not — a backfill burst — and it is spent on objects that carry NEEDS-ACTION and adopt to nothing.

The token is established without reading anything. A blank or refused token means the next report is an initial sync, which names every object in the collection. Those bodies are deliberately not fetched: the burst of reads that shape produces is exactly what EXO-89810 refused to build, at the one moment a deployment can least absorb it. So the first pass takes the token and reads nothing, and every pass after it reads only what changed since. An answer given before this ships is therefore not healed by it — the same forward-looking limit EXO-89807 has, and for the same reason.

Only where nothing else reads the collection. Where the copies land in the account's own calendar (MAIN_CALENDAR), that calendar is materialised and read through its own binding, and EXO-89807 already adopts every answer on that path. Running here as well would double a REPORT and a fetch per sweep to reach a conclusion already reached. The condition is therefore stated as what it actually is — no active binding reads this collection — rather than as the destination setting, so a mirror pointed at a collection whose binding is paused or tombstoned is covered too.

NEEDS-ACTION is refused, and that is load-bearing here more than anywhere. Every copy eXo pushes carries NEEDS-ACTION until somebody answers, and this pass meets those copies on the sweep after each push. A pass that adopted NEEDS-ACTION would walk an account erasing the answers its owner had given in eXo. The refusal lives in the adoption service and is pinned through this pass against the real one, never a mock.

  • Constructor Details

    • CaldavMirrorAnswerService

      public CaldavMirrorAnswerService()
  • Method Details

    • readAnswers

      public int readAnswers(long userIdentityId, String username, org.exoplatform.caldav.model.CaldavUserSetting settings, org.exoplatform.caldav.model.CalendarSync mirror)
      Reads the owner's answers off the copies of a mirror collection that no binding reads.

      Never throws at its caller. This runs inside the mirror pass, whose other halves — the relocation and the verification — must not lose a round because a report could not be made.

      Parameters:
      userIdentityId - identity of the account's owner
      username - their eXo login, which the credentials provider maps to their account on the server
      settings - the connected account
      mirror - the binding recording where the copies are written
      Returns:
      how many answers were recorded in agenda