Interface CalDavClient

All Known Implementing Classes:
HttpCalDavClient

public interface CalDavClient
The CalDAV protocol, and nothing else: it fetches, it does not decide. No business rule, no storage, no notion of what an event means locally — the sync engine owns all of that. It exists as an interface so the engine can be tested against canned answers, and so a different transport could be dropped in without the engine noticing — the exact seam email-connector's CardDavClient cut for CardDAV.

Two disciplines hold on every method:

  • Targets come from the registry, never from a caller. Every method addresses a CalDavEndpoint, which only endpoint(Long, String) can mint, and only from the administrator-managed server registry; hrefs are server-absolute paths resolved against that endpoint's authority, and an absolute href naming any other host is refused, not followed.
  • A 207 is never read as success by itself. Every property is read through its propstat status — BlueMind answers 207 with failing propstats where a naive client reads 2xx as success — and MKCALENDAR answers a raw MkCalendarResult whose one trustworthy confirmation is the caller's own fresh listing.
  • Method Details

    • endpoint

      CalDavEndpoint endpoint(Long serverId, String davUsername)
      Mints the one kind of target this client will talk to: the account base of a declared server, resolved exclusively from the server registry — the row the account references, else the seed row, else the legacy deployment property — with the {username} placeholder replaced by the account's DAV username, percent-encoded for its path position.
      Parameters:
      serverId - registry row the user's account references, or null for the seed-row / legacy-property resolution
      davUsername - the account's username on the CalDAV server
      Returns:
      the endpoint every other method addresses
      Throws:
      CalDavException - when no server is declared anywhere, the resolved URL is unusable, or the username cannot be part of a URL path
    • discoverCalendarHome

      String discoverCalendarHome(CalDavEndpoint endpoint, String username, String password)
      Walks discovery from the endpoint base: asks who the authenticated user is (current-user-principal), then where that principal's calendars live (calendar-home-set). Started at the registered base path directly — never at /.well-known/caldav, whose contract is a redirect and this client follows none.
      Parameters:
      endpoint - the declared server to discover on
      username - the account to authenticate as
      password - that account's password
      Returns:
      the calendar home's server-absolute raw path
      Throws:
      CalDavAuthenticationException - when the credentials are refused
      CalDavException - when the server cannot be reached or answers no principal or no home
    • listCalendars

      List<CalendarCollection> listCalendars(CalDavEndpoint endpoint, String homeHref, String username, String password)
      Lists the calendar collections of a home with every property the sync engine binds on: display name, resource type, ctag, sync token, supported reports, the user's privileges, and the calendar colour.
      Parameters:
      endpoint - the declared server
      homeHref - the calendar home's server-absolute path
      username - the account to authenticate as
      password - that account's password
      Returns:
      the calendar collections, never null — non-calendar members of the home (the home itself included) are filtered out here by resource type, never guessed from the path
      Throws:
      CalDavAuthenticationException - when the credentials are refused
      CalDavException - when the server cannot be reached or errors
    • readCalendar

      CalendarCollection readCalendar(CalDavEndpoint endpoint, String href, String username, String password)
      Reads one collection's properties at Depth:0 — what a bound pair re-reads on each run without paying a full home listing.
      Parameters:
      endpoint - the declared server
      href - the collection's server-absolute path
      username - the account to authenticate as
      password - that account's password
      Returns:
      the collection, or null when the resource exists but is not a calendar collection
      Throws:
      CalDavAuthenticationException - when the credentials are refused
      CalDavException - when the server cannot be reached or errors
    • getCtag

      String getCtag(CalDavEndpoint endpoint, String href, String username, String password)
      Reads the collection's current ctag, the cheap tier-2 question asked first on every run.
      Parameters:
      endpoint - the declared server
      href - the collection's server-absolute path
      username - the account to authenticate as
      password - that account's password
      Returns:
      the current ctag, or null when the server does not implement it
      Throws:
      CalDavAuthenticationException - when the credentials are refused
      CalDavException - when the server cannot be reached or errors
    • listResourceEtags

      Map<String,String> listResourceEtags(CalDavEndpoint endpoint, String collectionHref, String username, String password)
      Lists every object in the collection with its version — the listResourceEtags pattern the plan reuses verbatim from email-connector, and what tells the engine who is new, who changed and who is gone. Also the mirror-verification listing of the metadata design.
      Parameters:
      endpoint - the declared server
      collectionHref - the collection's server-absolute path
      username - the account to authenticate as
      password - that account's password
      Returns:
      object path to object version, insertion-ordered, never null — the collection itself and any member without an ETag are skipped by that one rule, never by guessing from the path
      Throws:
      CalDavAuthenticationException - when the credentials are refused
      CalDavException - when the server cannot be reached or errors
    • calendarQuery

      List<CalendarObject> calendarQuery(CalDavEndpoint endpoint, String collectionHref, Instant start, Instant end, String username, String password)
      Runs a calendar-query REPORT for the VEVENTs of a time window — the tier-3 floor for servers whose listings carry no usable ETags, and the ranged read the events endpoint will serve from.
      Parameters:
      endpoint - the declared server
      collectionHref - the collection's server-absolute path
      start - window start, inclusive, or null for an unbounded start
      end - window end, exclusive, or null for an unbounded end
      username - the account to authenticate as
      password - that account's password
      Returns:
      the matching objects with their calendar data, never null
      Throws:
      CalDavAuthenticationException - when the credentials are refused
      CalDavException - when the server cannot be reached or errors
    • multiget

      List<CalendarObject> multiget(CalDavEndpoint endpoint, String collectionHref, List<String> hrefs, String username, String password)
      Fetches the calendar data of the given objects in one calendar-multiget REPORT — why a changed collection costs one round trip per batch rather than one per event.
      Parameters:
      endpoint - the declared server
      collectionHref - the collection's server-absolute path
      hrefs - the object paths to fetch
      username - the account to authenticate as
      password - that account's password
      Returns:
      the objects the server returned, never null; an href it did not return is simply absent, which the caller must tolerate
      Throws:
      CalDavAuthenticationException - when the credentials are refused
      CalDavException - when the server cannot be reached or errors
    • syncCollection

      SyncCollectionResult syncCollection(CalDavEndpoint endpoint, String collectionHref, String syncToken, String username, String password)
      Runs an RFC 6578 sync-collection REPORT with a stored token — tier 1, one round trip for everything changed or deleted since that token.
      Parameters:
      endpoint - the declared server
      collectionHref - the collection's server-absolute path
      syncToken - the stored token, or null/blank for the initial sync that establishes one
      username - the account to authenticate as
      password - that account's password
      Returns:
      the changes and the token to store — or the invalid-token result when the server rejected the presented token, which the caller answers by falling through to the listing tier for this run
      Throws:
      CalDavAuthenticationException - when the credentials are refused
      CalDavException - when the server cannot be reached or errors
    • probeCapabilities

      ServerCapabilities probeCapabilities(CalDavEndpoint endpoint, String collectionHref, String username, String password)
      Probes which report tiers a collection's server can serve, in one Depth:0 PROPFIND. Stateless: the answer is bind-time information whose caching belongs to the caller's pair state, not to this client.
      Parameters:
      endpoint - the declared server
      collectionHref - the collection's server-absolute path
      username - the account to authenticate as
      password - that account's password
      Returns:
      the capability answer, never null
      Throws:
      CalDavAuthenticationException - when the credentials are refused
      CalDavException - when the server cannot be reached or errors
    • fetchObject

      CalendarObject fetchObject(CalDavEndpoint endpoint, String href, String username, String password)
      Reads one object's current data and version in a single GET — what a conflict re-read asks, so the three-way runs against the object as the server holds it NOW rather than as the last sync remembers it.
      Parameters:
      endpoint - the declared server
      href - the object's server-absolute path
      username - the account to authenticate as
      password - that account's password
      Returns:
      the object as the server returned it, or null when the server says there is no such object — including BlueMind's way of saying it, a 500 for an .ics that is simply not there; reporting "absent" on a server error is safe rather than optimistic because every creating write keeps its If-None-Match: *, so the worst case is a refused write, never a silently overwritten one
      Throws:
      CalDavAuthenticationException - when the credentials are refused
      CalDavException - when the server cannot be reached or errors
    • putObject

      PutResult putObject(CalDavEndpoint endpoint, String href, String icsData, String username, String password)
      Creates one object — always under If-None-Match: *, with no unconditional variant on purpose: the server itself then guarantees the PUT can only CREATE, and an object already at that path answers 412 instead of being overwritten, whatever race led there.
      Parameters:
      endpoint - the declared server
      href - the object's server-absolute path to create at
      icsData - the iCalendar text to store
      username - the account to authenticate as
      password - that account's password
      Returns:
      the status and, when the server sent them, the stored version and the object's real location; a 412 is answered, not thrown
      Throws:
      CalDavAuthenticationException - when the credentials are refused
      CalDavException - when the server cannot be reached or answers a status that is neither a write nor a precondition refusal
    • updateObject

      PutResult updateObject(CalDavEndpoint endpoint, String href, String icsData, String ifMatch, String username, String password)
      Replaces one existing object — guarded the opposite way from putObject(org.exoplatform.caldav.client.CalDavEndpoint, java.lang.String, java.lang.String, java.lang.String, java.lang.String): If-Match with the version the caller just read means the server only accepts the write when nobody changed the object since that read. A blank precondition is refused here rather than sent as an unconditional overwrite: "every conditional write stays conditional" is the connector doctrine this client enforces structurally.
      Parameters:
      endpoint - the declared server
      href - the object's server-absolute path
      icsData - the iCalendar text to store
      ifMatch - the If-Match value — the ETag the caller's own read just answered, exactly as sent, quotes and all
      username - the account to authenticate as
      password - that account's password
      Returns:
      the status and the stored version when the server sent one; a 412 is answered, not thrown
      Throws:
      CalDavAuthenticationException - when the credentials are refused
      CalDavException - when the server cannot be reached or answers a status that is neither a write nor a precondition refusal
      IllegalArgumentException - when the precondition is blank
    • deleteObject

      int deleteObject(CalDavEndpoint endpoint, String href, String ifMatch, String username, String password)
      Deletes one object, conditionally when a version is given.
      Parameters:
      endpoint - the declared server
      href - the object's server-absolute path
      ifMatch - the If-Match value to guard the delete with, or null to delete unconditionally — tolerated here (unlike updateObject(org.exoplatform.caldav.client.CalDavEndpoint, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)) because a delete of an already-diverged object is recoverable where an overwrite is not, and some flows legitimately hold no version
      username - the account to authenticate as
      password - that account's password
      Returns:
      the status the server answered: 200/204 deleted, 404/410 already gone — a fact, not a fault — and 412 a refused precondition
      Throws:
      CalDavAuthenticationException - when the credentials are refused
      CalDavException - when the server cannot be reached or answers any other status
    • mkCalendar

      MkCalendarResult mkCalendar(CalDavEndpoint endpoint, String href, String displayName, String color, String username, String password)
      Asks the server to create a calendar collection, display name and optional colour set atomically. The request always declares supported-calendar-component-set with VEVENT: BlueMind derives the created collection's kind from it, and without it answers 201 while creating nothing (proven live 2026-08-20). The answer is a claim, never a fact — see MkCalendarResult for why the one statement of success a caller may trust is the collection's presence in its own fresh listing.
      Parameters:
      endpoint - the declared server
      href - the collection's server-absolute path to create at
      displayName - the display name to create with
      color - the Apple calendar-color to set, or null to set none
      username - the account to authenticate as
      password - that account's password
      Returns:
      the raw outcome, refusals included — a server declining MKCALENDAR gives an answer the caller maps to its degraded states, not an error
      Throws:
      CalDavAuthenticationException - when the credentials are refused (401/407 — a 403 here is the refusal, not an auth failure)
      CalDavException - when the server cannot be reached