Interface CalDavClient
- All Known Implementing Classes:
HttpCalDavClient
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 onlyendpoint(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
MkCalendarResultwhose one trustworthy confirmation is the caller's own fresh listing.
-
Method Summary
Modifier and TypeMethodDescriptioncalendarQuery(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.intdeleteCollection(CalDavEndpoint endpoint, org.exoplatform.caldav.model.CalendarSync pair, String username, String password) Removes a whole collection, and everything in it.intdeleteObject(CalDavEndpoint endpoint, String href, String ifMatch, String username, String password) Deletes one object, conditionally when a version is given.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).discoverDefaultCalendar(CalDavEndpoint endpoint, String username, String password) Asks the account which of its calendars it treats as the default one — the collection a scheduling client files an invitation into when nobody says otherwise.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.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.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.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.listResourceEtags(CalDavEndpoint endpoint, String collectionHref, String username, String password) Lists every object in the collection with its version — thelistResourceEtagspattern the plan reuses verbatim from email-connector, and what tells the engine who is new, who changed and who is gone.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.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.overwriteObject(CalDavEndpoint endpoint, String href, String icsData, String username, String password) Writes one object over whatever is there, with no precondition at all.probeCapabilities(CalDavEndpoint endpoint, String collectionHref, String username, String password) Probes which report tiers a collection's server can serve, in one Depth:0 PROPFIND.Creates one object — always underIf-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.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.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.updateObject(CalDavEndpoint endpoint, String href, String icsData, String ifMatch, String username, String password)
-
Method Details
-
endpoint
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 resolutiondavUsername- 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
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 onusername- the account to authenticate aspassword- that account's password- Returns:
- the calendar home's server-absolute raw path
- Throws:
CalDavAuthenticationException- when the credentials are refusedCalDavException- when the server cannot be reached or answers no principal or no home
-
discoverDefaultCalendar
Asks the account which of its calendars it treats as the default one — the collection a scheduling client files an invitation into when nobody says otherwise.Two hops, both of them questions rather than guesses: the principal names its scheduling inbox (
schedule-inbox-URL), and the inbox names the default calendar (schedule-default-calendar-URL) — the RFC 6638 property that exists precisely so a client does not have to invent one. Guessing from the home listing instead (the first collection, the one called "Calendar", the one whose path ends in the username) is what this method exists not to do: each of those is right on one server and wrong on the next, and being wrong means writing somebody's meetings into a calendar nobody was looking at.A server naming none is an answer, not a fault. Scheduling is an extension, and plenty of CalDAV servers implement none of it; that is a null here rather than an exception, and the caller decides what to do with a destination it cannot establish. Credentials being refused, or the server being unreachable, still raise — those are not "there is no default calendar".
The path it answers is a claim, in the sense
mkCalendar(org.exoplatform.caldav.client.CalDavEndpoint, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)documents: the collection is only real once the caller has seen it in a listing of the account's own home.- Parameters:
endpoint- the declared serverusername- the account to authenticate aspassword- that account's password- Returns:
- the default calendar's server-absolute raw path, or null when the account names none
- Throws:
CalDavAuthenticationException- when the credentials are refusedCalDavException- when the server cannot be reached or answers no principal at all
-
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 serverhomeHref- the calendar home's server-absolute pathusername- the account to authenticate aspassword- 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 refusedCalDavException- 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 serverhref- the collection's server-absolute pathusername- the account to authenticate aspassword- 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 refusedCalDavException- when the server cannot be reached or errors
-
getCtag
Reads the collection's current ctag, the cheap tier-2 question asked first on every run.- Parameters:
endpoint- the declared serverhref- the collection's server-absolute pathusername- the account to authenticate aspassword- that account's password- Returns:
- the current ctag, or null when the server does not implement it
- Throws:
CalDavAuthenticationException- when the credentials are refusedCalDavException- 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 — thelistResourceEtagspattern 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 servercollectionHref- the collection's server-absolute pathusername- the account to authenticate aspassword- 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 refusedCalDavException- 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 servercollectionHref- the collection's server-absolute pathstart- window start, inclusive, or null for an unbounded startend- window end, exclusive, or null for an unbounded endusername- the account to authenticate aspassword- that account's password- Returns:
- the matching objects with their calendar data, never null
- Throws:
CalDavAuthenticationException- when the credentials are refusedCalDavException- 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 servercollectionHref- the collection's server-absolute pathhrefs- the object paths to fetchusername- the account to authenticate aspassword- 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 refusedCalDavException- 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 servercollectionHref- the collection's server-absolute pathsyncToken- the stored token, or null/blank for the initial sync that establishes oneusername- the account to authenticate aspassword- 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 refusedCalDavException- 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 servercollectionHref- the collection's server-absolute pathusername- the account to authenticate aspassword- that account's password- Returns:
- the capability answer, never null
- Throws:
CalDavAuthenticationException- when the credentials are refusedCalDavException- when the server cannot be reached or errors
-
fetchObject
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 serverhref- the object's server-absolute pathusername- the account to authenticate aspassword- 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 refusedCalDavException- 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 underIf-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 serverhref- the object's server-absolute path to create aticsData- the iCalendar text to storeusername- the account to authenticate aspassword- 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 refusedCalDavException- when the server cannot be reached or answers a status that is neither a write nor a precondition refusal
-
overwriteObject
PutResult overwriteObject(CalDavEndpoint endpoint, String href, String icsData, String username, String password) Writes one object over whatever is there, with no precondition at all.The deliberate exception to the conditional-write doctrine every other method here follows, and the only one.
putObject(org.exoplatform.caldav.client.CalDavEndpoint, java.lang.String, java.lang.String, java.lang.String, java.lang.String)sendsIf-None-Match: *and so refuses an object that exists;updateObject(org.exoplatform.caldav.client.CalDavEndpoint, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)sendsIf-Matchand so refuses an object that has moved on. A repair needs to write in exactly the case both of them refuse — the copy is there and it has drifted — so neither can carry it, and the guard has to come off.What makes that safe is not this method but its caller: a repair is only reached after both copies have been read and compared and the eXo one judged authoritative. Called anywhere that comparison has not happened, this silently destroys somebody's change. There is no way for the client to check that, which is why the name says overwrite.
- Parameters:
endpoint- the account's endpointhref- the object's pathicsData- the iCalendar object to writeusername- the account's loginpassword- the account's password- Returns:
- the result, which cannot be a precondition failure
-
updateObject
-
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 serverhref- the object's server-absolute pathifMatch- theIf-Matchvalue to guard the delete with, or null to delete unconditionally — tolerated here (unlikeupdateObject(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 versionusername- the account to authenticate aspassword- 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 refusedCalDavException- 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 declaressupported-calendar-component-setwithVEVENT: 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 — seeMkCalendarResultfor why the one statement of success a caller may trust is the collection's presence in its own fresh listing.- Parameters:
endpoint- the declared serverhref- the collection's server-absolute path to create atdisplayName- the display name to create withcolor- the Apple calendar-color to set, or null to set noneusername- the account to authenticate aspassword- 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
-
deleteCollection
int deleteCollection(CalDavEndpoint endpoint, org.exoplatform.caldav.model.CalendarSync pair, String username, String password) Removes a whole collection, and everything in it.The only irreversible outward-facing call in this client, and the one that deletes data eXo may not have authored — a user's own events, added from their phone, live in the collection too and go with it.
It takes the pair rather than an href on purpose. An href parameter would let any caller name any collection, and the guard would then live in whoever remembered to write it. Taking the binding means the invariant is checked where the request is built: the collection must belong to an
SyncOrigin.EXOpair, and its last path segment must be the one eXo derives from that pair's own calendar anchor. A collection eXo did not create cannot be addressed by this method at all.- Parameters:
endpoint- the declared serverpair- the binding whose collection is to be removedusername- the account's usernamepassword- the account's password- Returns:
- the HTTP status; 404 and 410 mean it was already gone
- Throws:
IllegalArgumentException- when the pair does not authorise the deletion — never a runtime refusal to be caught and worked around, but a programming error surfaced at the call siteCalDavAuthenticationException- when the credentials are refusedCalDavException- when the server refuses or cannot be reached
-