Class CaldavServerService
-
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final StringThe name the Bluemind seed is declared under.static final StringLegacy configuration property telling whether the single CalDAV connector is enabled; read once, to give the seed row its initial activation.static final StringProvider name of the seed registration — the name the kernel plugin has always registered on the agenda side — and the prefix every derived provider name starts with.static final StringLegacy configuration property holding the single CalDAV server URL.static final StringAddress the Bluemind seed row is declared with: a placeholder an administrator is expected to replace with the DAV endpoint of their own BlueMind, whose shape it mirrors (BlueMind serves DAV under/dav/and answers there with 401 Basic realm="bm.basic.auth.v2"; the bare host only redirects).static final StringAddress the Stalwart seed row falls back to when the deployment named none throughCALDAV_SERVER_URL_PROPERTY.static final StringThe name the Stalwart seed is declared under.static final StringThe refusal of a write channel the server behind the registration cannot speak (EXO-90307):BLUEMIND_IMPORTcalls BlueMind's own REST endpoints and is offered to a BlueMind registration only. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionbooleanWhether a user may edit the registry: platform administrators only.Whether each declared provider asks its user for anything, keyed by provider name.longcountServerReferences(long serverId) Counts the connected accounts referencing a registration: the user contexts holding a CaldavServerId setting whose value is this row's id.org.exoplatform.caldav.model.CaldavServercreateServer(org.exoplatform.caldav.model.CaldavServer server, String username) Declares a new CalDAV server.voiddeleteServer(long serverId, String username) Deletes a declared server.List<org.exoplatform.caldav.model.ForeignWriter> getForeignWriters(long serverId, String username) Which other eXo deployments have been seen writing meeting copies into this server's accounts (EXO-89824) — what the administration drawer shows under the behaviours this server has been seen having.getProviderConfig(long serverId, String username) The provider configuration of a registration, as an administration screen may see it: every field except the secret ones, which are never read back on this path.org.exoplatform.caldav.model.CaldavServergetServerById(long serverId) One registration by its technical identifier.getServerImageInputStream(long serverId) The stored image of a registration, as a stream the REST layer serves — email-connector's illustration mechanics.List<org.exoplatform.caldav.model.CaldavServer> getUserIdentitiesOfServer(long serverId) The users connected to a server, by their identity: what a change of its provider disconnects.booleanisConnected(org.exoplatform.caldav.model.CaldavUserSetting settings) Whether a stored account is a connection at all - the addon's single definition, which push, inbound, outbound, read, sync, the relay and the connector service all ask rather than restate.voidrecordForeignWriter(long serverId, String authority) Records that another eXo deployment was seen writing a meeting copy into an account on this server.org.exoplatform.caldav.model.CaldavServerresolveServer(Long serverId) Resolves the registration a user's account reads and pushes through: the row the account references when it references one and that row still exists, else the seed row, else null.resolveServerUrl(Long serverId) Resolves the CalDAV base URL a user's account reads and pushes through: the URL of the resolved registration, else null — the caller then falls back to the legacy configuration property, which is today's behaviour for deployments that never touched the registry.protected voidSeeds the two default servers into an EMPTY registry — never over an administrator's rows: Stalwart, under the fixed legacy provider name so accounts connected before the registry existed keep resolving; its URL comes from the legacy property when set, else the literal default, and its activation from the legacy enabled property (historically enabled). Bluemind, a normally-named row whose agenda remote provider is upserted here, since no kernel plugin declares it — and which arrives excused for what BlueMind is known to do to a copy, seeBLUEMIND_SEED_QUIRKS, and pointed at the account's main calendar, which is where the browser preset points it too.org.exoplatform.caldav.model.CaldavServersetServerActive(long serverId, boolean active, String username) Activates or deactivates a declared server, propagating the switch to the agenda remote provider so the connector appears in — or leaves — every user's connectors list.voidstart()Defers the seeding of the registry to the portal container's post-create phase — the same deferral agenda's own provider plugin uses — because the Bluemind default needs its agenda remote provider written, and agenda's kernel services are only safely callable once the portal container is up.org.exoplatform.caldav.model.CaldavServerupdateServer(org.exoplatform.caldav.model.CaldavServer server, String username) Updates a declared server: name, description, URL, activation, whether the copies pushed to it carry answer links, and the two lists of behaviours this server is excused for.
-
Field Details
-
CALDAV_PROVIDER_NAME
Provider name of the seed registration — the name the kernel plugin has always registered on the agenda side — and the prefix every derived provider name starts with.- See Also:
-
CALDAV_SERVER_URL_PROPERTY
Legacy configuration property holding the single CalDAV server URL. Becomes the seed row, then stays forever as a deprecated fallback for deployments whose registry is empty. Never dropped.- See Also:
-
CALDAV_ENABLED_PROPERTY
Legacy configuration property telling whether the single CalDAV connector is enabled; read once, to give the seed row its initial activation.- See Also:
-
STALWART_SERVER_NAME
The name the Stalwart seed is declared under.- See Also:
-
BLUEMIND_SERVER_NAME
The name the Bluemind seed is declared under.- See Also:
-
DEFAULT_STALWART_URL
Address the Stalwart seed row falls back to when the deployment named none throughCALDAV_SERVER_URL_PROPERTY.It is a placeholder, and it is one on purpose (EXO-89794). This constant used to read
http://localhost:8888/dav/cal/{username}/— the local development rig — so a fresh install shipped an active registration pointing at its own loopback interface, through which a connected user could drive the relay's verbs at a row nobody had typed. A product default must not name a host that exists only on a developer's machine, and it must certainly not arrive switched on.The replacement is an RFC 2606
.invalidname, which is guaranteed never to resolve: it cannot be a live target under any deployment's resolver, it reads unmistakably as "replace me", and it fails the address check — which is what makes the row arrive inactive, seeseedDefaultServers(). The rig keeps working the documented way, by settingCALDAV_SERVER_URL_PROPERTY(plus the opt-ins the address check reads); it no longer works by being the shipped default.- See Also:
-
DEFAULT_BLUEMIND_URL
Address the Bluemind seed row is declared with: a placeholder an administrator is expected to replace with the DAV endpoint of their own BlueMind, whose shape it mirrors (BlueMind serves DAV under/dav/and answers there with 401 Basic realm="bm.basic.auth.v2"; the bare host only redirects).An RFC 2606
.invalidname for the same reason asDEFAULT_STALWART_URL: it can never resolve, so the row can never be a live target, and it fails the address check — so the row is seeded inactive rather than offered to users as a connector that goes nowhere.Note also that BlueMind sends no CORS headers, so connecting from the browser needs the portal to front it on its own origin.
- See Also:
-
WRITE_CHANNEL_NOT_SUPPORTED_MESSAGE
The refusal of a write channel the server behind the registration cannot speak (EXO-90307):BLUEMIND_IMPORTcalls BlueMind's own REST endpoints and is offered to a BlueMind registration only.- See Also:
-
-
Constructor Details
-
CaldavServerService
public CaldavServerService()
-
-
Method Details
-
start
@PostConstruct public void start()Defers the seeding of the registry to the portal container's post-create phase — the same deferral agenda's own provider plugin uses — because the Bluemind default needs its agenda remote provider written, and agenda's kernel services are only safely callable once the portal container is up. -
seedDefaultServers
protected void seedDefaultServers()Seeds the two default servers into an EMPTY registry — never over an administrator's rows:- Stalwart, under the fixed legacy provider name so accounts connected before the registry existed keep resolving; its URL comes from the legacy property when set, else the literal default, and its activation from the legacy enabled property (historically enabled).
- Bluemind, a normally-named row whose agenda remote provider is
upserted here, since no kernel plugin declares it — and which arrives
excused for what BlueMind is known to do to a copy, see
BLUEMIND_SEED_QUIRKS, and pointed at the account's main calendar, which is where the browser preset points it too.The destination is seeded rather than left at the model's default because on THIS server the default has an established cost: BlueMind's dedicated calendar is excluded from the account's free/busy — colleagues booking around the user see eXo meeting times as free — and it carries no answer buttons. The general caution on the option ("only once copies synchronise cleanly") is right and stays where it is; it simply does not weigh what is already known about this one server, which is the same judgement
serverPresets.jsmakes for the drawer. A seed that disagreed with the preset an administrator is about to apply to the very same row would be teaching two answers to one question.answerLinksInCopyis stated rather than defaulted — the model is built positionally through its all-arguments constructor, so the field initialiser is overwritten whatever the seed passes, and the seed passestrue. It happens to be the model's own default and the preset's value too, so the three agree; it is simply stated twice rather than once.
A seeded row is switched ON only when its address passes the same check an administrator's would (EXO-89794). Seeding used to be exempt from the address check of EXO-89774 altogether, which left the loophole that check was written to close: a fresh install carried an active registration at
http://localhost:8888/dav/cal/{username}/, so a connected user could drive the relay's allowed verbs at loopback through a row nobody had typed. Neither the check nor the seeds are the thing to drop; what had to go is the row arriving switched on without ever meeting the check.So the seeds are still written — they are the pre-filled form an administrator edits, and skipping them would leave a fresh install with an empty administration screen and no hint of what belongs in it — but their activation is the address check's answer, not a constant:
- An address the deployment actually named and vouched for — through
CALDAV_SERVER_URL_PROPERTY, plus whichever of the fourexo.agenda.caldav.server.*opt-ins that address needs — passes, and the row is seeded active exactly as before. This is how the local development rig keeps working: through its documented properties, not through a default that walks past them. - Anything else — both shipped placeholders included — is seeded
inactive, and said out loud in the log (see
isDeclarableSeedAddress(String, String)). Inactive is the honest state for an address nobody has vouched for: no user is offered the connector, nothing can be driven at it, and switching it on later is a write thatsetServerActive(long, boolean, String)already puts through the full check. The administrator edits the row, which is checked too, and turns it on — the check is never bypassed, only deferred to the person who knows the address.
This governs a fresh registry and nothing else. The guard below is the whole upgrade story: a deployment that already holds rows returns before any of this runs, so an install that has been serving its users for months does not find its servers deactivated — or judged at all — by taking this version.
-
isConnected
public boolean isConnected(org.exoplatform.caldav.model.CaldavUserSetting settings) Whether a stored account is a connection at all - the addon's single definition, which push, inbound, outbound, read, sync, the relay and the connector service all ask rather than restate.It used to be written out in each of them as "a username and a password", and that reading is wrong for a connection whose material the platform produces: there is no password to store, so all seven called such an account disconnected while the screen showed it connected, and nothing ever synchronised. The rule is: an account is named, and either it carries a password or its provider produces the material on its behalf.
A typed account settles on the spot, without reading the registry: this runs on every sweep.
- Parameters:
settings- the stored account, possibly null- Returns:
- true when the account can actually be used
-
connectionRequirements
Whether each declared provider asks its user for anything, keyed by provider name.This is what a browser needs before showing a connect button: a provider that needs nothing must connect in a click rather than open a form the user cannot fill. Only the providers the registry actually names are answered - a user is entitled to know about the connectors offered to them, not about how the instance configures its providers, whose registry is administrators-only.
Silence means ask. A registration naming no provider contributes nothing, and a seam that is absent answers true: a connector list that cannot tell must send the user to a form, never connect on its own.
- Returns:
- one entry per declared provider name, true when the user must supply something
-
getServers
-
getServerById
public org.exoplatform.caldav.model.CaldavServer getServerById(long serverId) throws org.exoplatform.commons.exception.ObjectNotFoundException One registration by its technical identifier.- Parameters:
serverId- technical identifier of the registration- Returns:
- the registration
- Throws:
org.exoplatform.commons.exception.ObjectNotFoundException- when no registration carries that id
-
getForeignWriters
public List<org.exoplatform.caldav.model.ForeignWriter> getForeignWriters(long serverId, String username) throws IllegalAccessException Which other eXo deployments have been seen writing meeting copies into this server's accounts (EXO-89824) — what the administration drawer shows under the behaviours this server has been seen having.- Parameters:
serverId- technical identifier of the registrationusername- the user asking- Returns:
- the deployments seen writing here, most recently seen first, empty when none has been
- Throws:
IllegalAccessException- when the user may not administer servers
-
recordForeignWriter
Records that another eXo deployment was seen writing a meeting copy into an account on this server.Called from the inbound pass, and never allowed to matter to it. Nothing is imported, skipped, removed or repaired differently on the strength of this record: what to do with a foreign copy is a product decision nobody has taken, and the wrong one destroys real calendar entries. It is written so that an administrator opening the drawer can see what until now existed only as a line in
platform.log.- Parameters:
serverId- technical identifier of the registration the copy was read fromauthority- how the other deployment names itself in the copies it writes — host and port, asCaldavInboundServiceread it off the copy's event link
-
getProviderConfig
public Map<String,String> getProviderConfig(long serverId, String username) throws IllegalAccessException, org.exoplatform.commons.exception.ObjectNotFoundException The provider configuration of a registration, as an administration screen may see it: every field except the secret ones, which are never read back on this path.- Parameters:
serverId- the registration whose configuration is readusername- the user asking, checked against the administration ACL - reading a technical account is an administration act- Returns:
- the stored values without any secret, empty when nothing is stored or when the storage is not deployed
- Throws:
IllegalAccessException- when the user is not a platform administratororg.exoplatform.commons.exception.ObjectNotFoundException- when no registration carries that id
-
createServer
public org.exoplatform.caldav.model.CaldavServer createServer(org.exoplatform.caldav.model.CaldavServer server, String username) throws IllegalAccessException Declares a new CalDAV server. The provider name is derived from the row id (agenda.caldavCalendar.<id>), then the matching agenda remote provider is upserted under that name — which is all agenda needs: its existing enabled-check and connected-provider binding key on the name.- Parameters:
server- registration to create (id and provider name ignored)username- user declaring the server- Returns:
- the created registration
- Throws:
IllegalAccessException- when the user is not a platform administratorIllegalArgumentException- when the registration, its name or its URL is missing
-
updateServer
public org.exoplatform.caldav.model.CaldavServer updateServer(org.exoplatform.caldav.model.CaldavServer server, String username) throws IllegalAccessException, org.exoplatform.commons.exception.ObjectNotFoundException Updates a declared server: name, description, URL, activation, whether the copies pushed to it carry answer links, and the two lists of behaviours this server is excused for. The provider name never changes, and the activation is propagated to the agenda remote provider — the switch users' connector lists actually read.- Parameters:
server- registration carrying the id to update and the new valuesusername- user updating the server- Returns:
- the updated registration
- Throws:
IllegalAccessException- when the user is not a platform administratorIllegalArgumentException- when the registration, its name or its URL is missingorg.exoplatform.commons.exception.ObjectNotFoundException- when no registration carries that id
-
setServerActive
public org.exoplatform.caldav.model.CaldavServer setServerActive(long serverId, boolean active, String username) throws IllegalAccessException, org.exoplatform.commons.exception.ObjectNotFoundException Activates or deactivates a declared server, propagating the switch to the agenda remote provider so the connector appears in — or leaves — every user's connectors list.- Parameters:
serverId- technical identifier of the registrationactive- whether users may connect to this serverusername- user flipping the switch- Returns:
- the updated registration
- Throws:
IllegalAccessException- when the user is not a platform administratororg.exoplatform.commons.exception.ObjectNotFoundException- when no registration carries that idIllegalArgumentException- carrying a message code when the row is being ACTIVATED and its stored address is one the platform must not be made to connect to, orcaldav.managed.serverInUsewhen the row is the one managed mode synchronises everybody with
-
deleteServer
public void deleteServer(long serverId, String username) throws IllegalAccessException, org.exoplatform.commons.exception.ObjectNotFoundException Deletes a declared server. Refused — with the number of blocked accounts — while ANY user's connection references the row: deleting under them would silently re-point their stored credentials at whatever the resolution falls back to, which is a different server. The administrator deactivates instead, users disconnect, then the delete goes through. Legacy accounts (no stored reference) never block a delete: they resolve through the seed then the property, exactly as before the registry. Before the row goes, its agenda remote provider is disabled — agenda has no provider-delete API, so disabling is what actually removes the connector from every user's list; the orphaned provider row stays, disabled and invisible.- Parameters:
serverId- technical identifier of the registrationusername- user deleting the server- Throws:
IllegalAccessException- when the user is not a platform administratororg.exoplatform.commons.exception.ObjectNotFoundException- when no registration carries that idIllegalStateException- carrying the message codecaldav.server.referenced:<count>when connected accounts still reference the row (the REST layer answers 409)IllegalArgumentException- carryingcaldav.managed.serverInUsewhen the row is the one managed mode synchronises everybody with (the REST layer answers 400)
-
getUserIdentitiesOfServer
The users connected to a server, by their identity: what a change of its provider disconnects. The accounts whose stored server id names the row - and, for the row that carriesCALDAV_PROVIDER_NAME, also the accounts that store no server id: connected before the server list existed, every use resolves them to that row (resolveServer(Long)with null), so they are its users too.- Parameters:
serverId- the server- Returns:
- the technical identity identifiers of its users
-
countServerReferences
public long countServerReferences(long serverId) Counts the connected accounts referencing a registration: the user contexts holding a CaldavServerId setting whose value is this row's id. The enumeration is by setting NAME (the settings API offers no by-value filter), each candidate's value then compared — the same walk email-connector does to find a connector's users.- Parameters:
serverId- technical identifier of the registration- Returns:
- number of accounts whose stored connection references the row
-
getServerImageInputStream
public InputStream getServerImageInputStream(long serverId) throws org.exoplatform.commons.exception.ObjectNotFoundException The stored image of a registration, as a stream the REST layer serves — email-connector's illustration mechanics.- Parameters:
serverId- technical identifier of the registration- Returns:
- the image stream, or null when the row holds no image
- Throws:
org.exoplatform.commons.exception.ObjectNotFoundException- when no registration carries that id
-
resolveServer
Resolves the registration a user's account reads and pushes through: the row the account references when it references one and that row still exists, else the seed row, else null. This single resolution rule is shared by the URL resolution below and by the relay's authorization check — the relay refuses any target that is not the row this method answers for the user.- Parameters:
serverId- registration the user's account references, or null- Returns:
- the resolved registration, or null when the registry answers nothing
-
resolveServerUrl
Resolves the CalDAV base URL a user's account reads and pushes through: the URL of the resolved registration, else null — the caller then falls back to the legacy configuration property, which is today's behaviour for deployments that never touched the registry.- Parameters:
serverId- registration the user's account references, or null- Returns:
- the resolved base URL, or null when the registry answers nothing
-
canEdit
Whether a user may edit the registry: platform administrators only. This is the same gate email-connector's admin registry uses (EmailConnectorService.canEdit), minus its blank-username-passes quirk — an anonymous caller is refused here.- Parameters:
username- user asking to write- Returns:
- true when the user is a platform administrator
-