Class AccountCalendarOwners

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

public final class AccountCalendarOwners extends Object
What the server itself says about who owns each calendar an account sees — the fourth witness CaldavOutboundService#ownershipOf hears (EXO-90347), asked at most once per account per pass.

The three other witnesses cannot tell a colleague's calendar the account subscribed to from the account's own when neither was minted by this deployment: on BlueMind the DAV listing puts the subscribed calendar under the account's own home and names the account as its owner, the slug names an anchor nobody here holds, and the container uid is eXo's shape rather than one that carries an owner. So a calendar a colleague had imported from elsewhere and then shared was adopted as the sharee's own — observed live as eric's "Perso" coming back as root's calendar 24. BlueMind's subscription listing carries the owner of every calendar in the account's view; this is that listing, reduced to the one question the classifier asks of it.

Four words, and what each costs the classifier. AccountCalendarOwners.Word.SILENT is a server that was not asked — one that is not BlueMind's, where the current behaviour is kept unchanged. AccountCalendarOwners.Word.ACCOUNTS_OWN and AccountCalendarOwners.Word.ANOTHERS are answers. AccountCalendarOwners.Word.UNKNOWN is a question that could not be answered: the listing could not be fetched, the session was not the account's, or the calendar is simply not in it. The classifier fails closed on it for adoption alone — adopting on missing evidence is exactly the defect — and leaves the calendar for the next pass.

Deferred, and memoised. The listing costs a REST login, a GET and a logout, while most passes never need it: a collection outside eXo's naming never reaches this witness, and one inside it is settled by the user's own pairs or the deployment's pairs before this witness is heard. The sweep's skip line and its retirement of a bound subscription never fetch either: the one reads what was already heard, the other hears the silent witness. So the listing is fetched the first time a question reaches it and kept for the rest of the pass — one fetch per account per pass at most, none when nothing asks — which is the bound the sweep is held to. Across passes the fetch is served from CaldavServerOwnerStorage's cache, so most first questions cost no request either.

One refresh per pass, for a calendar the listing does not name. A cached listing predates every share made since it was written, and a new share met as "not in the listing" would sit unclassified — neither adopted nor listed as shared — until the entry expired. So a deferred witness may carry a second supplier: heard once per pass, the first time a question about a calendar the present listing does not name comes in, and never for a listing that could not be had at all (a failed fetch is one attempt per pass, as before the cache). A refresh that yields nothing keeps the listing already in hand.

Services-local like CollectionOwnership: an answer held for the length of one pass or one listing, persisted nowhere.

  • Method Details

    • silent

      public static AccountCalendarOwners silent()
      A server that is not asked: every calendar is AccountCalendarOwners.Word.SILENT, and the classifier behaves as it did before this witness existed.
      Returns:
      the silent witness
    • unavailable

      public static AccountCalendarOwners unavailable()
      A server that was asked and could not answer: every calendar is AccountCalendarOwners.Word.UNKNOWN.
      Returns:
      the unavailable witness
    • of

      public static AccountCalendarOwners of(String accountUid, Map<String,String> ownerByContainerUid)
      A listing in hand.
      Parameters:
      accountUid - the directory entry uid the listing was read as; a blank uid makes every calendar AccountCalendarOwners.Word.UNKNOWN, since an owner cannot be compared to nobody
      ownerByContainerUid - the owner uid of each listed calendar, keyed by container uid
      Returns:
      the witness
    • deferred

      public static AccountCalendarOwners deferred(Supplier<AccountCalendarOwners> fetch)
      A listing fetched on the first question and kept for every later one.

      The supplier runs at most once, however many calendars are asked about and whatever it answers — an unavailable listing is not fetched again within the pass either. A supplier that throws is read as unavailable(); the caller that built it is expected to have caught and said what it wanted to say.

      Parameters:
      fetch - how to get the listing
      Returns:
      the deferred witness, with no refresh
    • deferred

      A listing fetched on the first question, kept for every later one, and read again at most once for a calendar it does not name.

      The refresh runs the first time a question about an absent calendar reaches a listing that is present — never when the fetch failed, since a failure is one attempt per pass — and whatever it answers it is not run again in this witness's life. A refresh that answers null, or throws, leaves the listing in hand as it was.

      Parameters:
      fetch - how to get the listing
      refresh - how to read it again, fresh from the server; null for no refresh
      Returns:
      the deferred witness
    • ownerOf

      public AccountCalendarOwners.Verdict ownerOf(String containerUid)
      The server's word on one calendar.
      Parameters:
      containerUid - the calendar's container uid, the last segment of its collection path
      Returns:
      the verdict, never null
    • ownerAlreadyHeard

      public AccountCalendarOwners.Verdict ownerAlreadyHeard(String containerUid)
      The word already heard on one calendar, fetching and refreshing nothing: what a line written after the classification may cite without costing the account a request.

      A deferred witness whose fetch has not run answers AccountCalendarOwners.Word.UNKNOWN for every calendar — the listing was never needed, so nothing is known of it — and one that has run answers from the listing it holds, the fresh one when the refresh replaced it. The sweep's skip line asks this for a share the deployment's own pairs settled: the listing may never have been read for it, and the line must not be the reason it is.

      Parameters:
      containerUid - the calendar's container uid
      Returns:
      the verdict, never null