Class BlueMindRestSession

java.lang.Object
org.exoplatform.caldav.client.bluemind.BlueMindRestSession

@Component public class BlueMindRestSession extends Object
The one way eXo talks to BlueMind's own REST API: a session opened with the account's stored login and password, kept per account for a short while (see call), every call carrying the key it answered.

Extracted from BlueMindAclClient (EXO-90253) when a second conversation needed it (EXO-90307, the ICS import channel), so that both share one transport, one authentication path and one set of rules:

  • Same server, never another. The REST root is the scheme, host and port of the endpoint minted from the server registry for the DAV calls, with the /api root BlueMind serves its REST API under (RestServiceApiDescriptionParser.java prefixes every path with /api; bluemind-vhosts.conf proxies location /api/). Nothing a caller passes can name a host; redirects are never followed.
  • The account's own credentials, from storage. The login and password are the ones the configured credentials provider produces for the account's DAV requests, taken from its Basic header; a provider producing anything else is not a login this API accepts.
  • A session, reused for a short while. POST /api/auth/login?login=… with the password as a JSON string under exactly Content-Type: application/json — what BlueMind's own client proxy sends (ClientProxyGenerator.java, ByMimeTypeCodec.encode); BlueMind picks the body codec by the exact Content-Type value (DefaultBodyParameterCodecs.java), so a text/plain carrying a charset parameter would be read as JSON, refused with a 500 and logged by BlueMind with the password in it — answers a LoginResponse whose authKey travels in the X-BM-ApiKey header (parent/core/net.bluemind.core.rest/.../base/RestRootHandler.java reads it, BasicClientProxy.java sends it). Since EXO-90397 that key is kept for a few minutes, in this node's own memory and nowhere else (BlueMindSessionCache), so the next read costs one request rather than three; the rules that follow from it are on call(org.exoplatform.caldav.client.CalDavEndpoint, java.util.function.Function<org.exoplatform.caldav.client.bluemind.BlueMindRestSession.Session, T>). The key and the password are never logged, never written to the database, never handed to a caller — BlueMindRestSession.Session exposes who the session is, not what authenticates it — and never part of an exception message.
  • Bounded answers. Every body is read up to a fixed size and refused beyond it: nothing this add-on reads from this API is large.
  • Field Details

    • API_KEY_HEADER

      public static final String API_KEY_HEADER
      The header BlueMind's REST API reads a session key from.
      See Also:
    • JSON_MEDIA_TYPE

      public static final String JSON_MEDIA_TYPE
      The media type BlueMind's REST API reads and answers.
      See Also:
  • Constructor Details

    • BlueMindRestSession

      @Autowired public BlueMindRestSession(CaldavCredentialsResolver caldavCredentialsResolver, BlueMindSessionCache sessions)
      The transport Spring builds: no redirects, bounded timeouts.
      Parameters:
      caldavCredentialsResolver - the seam producing the account's credentials
      sessions - the short-lived sessions kept per account
  • Method Details

    • acceptsCredentials

      public boolean acceptsCredentials(CalDavEndpoint endpoint)
      Whether the account's configured credentials are a login and password this API can take, without calling the server.
      Parameters:
      endpoint - the account's endpoint, minted from the registry
      Returns:
      true when the provider produces a Basic login and password
    • call

      public <T> T call(CalDavEndpoint endpoint, Function<BlueMindRestSession.Session,T> job)
      Runs one job inside a REST session opened as the account: the session kept for that account when there is one, a fresh login otherwise.

      What is reused, and for whom. The session is looked up by the account it acts as — the declared server, the eXo login the credentials are produced for, and the account those credentials address on that server (BlueMindSessionCache.Key) — so no session ever serves an account other than the one it was minted for. An endpoint minted from a DAV account name rather than for an eXo user carries no login to key on, and neither does one whose provider cannot name the account it would address: such a call opens its own session and closes it, as every call did before EXO-90397.

      Never to another address. The kept session records the REST root it was opened at; an endpoint that now resolves elsewhere — an administrator changed the declared server's URL — does not reuse it. The key is a credential of one host and is never sent to another.

      An expired or refused session re-logs in once. BlueMind expires a session on its own clock, and this add-on does not measure it (EXO-89647 is where that measurement lands), so the kept entry's lifetime is set well below any plausible server-side one and the refusal is handled rather than predicted: a 401 on a reused session drops the entry, opens another and sends the request again — once per call, tracked on the session itself, so a server refusing everything costs one extra login and never a loop.

      Logout. A session nothing keeps is logged out when the job ends, as before — and nothing keeps it covers three cases, not one: the endpoint that cannot be keyed at all, the store that had no room for another account (maxAccounts), and the session another thread's took the place of. A kept one is not closed: it is closed when eXo drops it on purpose (forget(long, java.lang.String)), and otherwise expires on BlueMind's own clock when the entry expires here first — the same fallback a failed logout has always relied on. A session refused by BlueMind is already closed on its side and is dropped without one.

      Type Parameters:
      T - what the job produces
      Parameters:
      endpoint - the account's DAV endpoint, minted from the registry
      job - the calls to make with the session
      Returns:
      what the job produced
      Throws:
      UnsupportedOperationException - when the configured credentials are not a login and password
      CalDavAuthenticationException - when BlueMind refuses the login
      CalDavUnreachableException - when the server cannot be reached
      CalDavException - when the login answers anything else
    • open

      Opens a REST session as the account, for as many calls as one service call needs, kept or reused as call(CalDavEndpoint, Function) describes; BlueMindRestSession.Session.close() logs out a session nothing keeps, and leaves a kept one in the store. The exceptions are those of call(CalDavEndpoint, Function) for the login half.
      Parameters:
      endpoint - the account's DAV endpoint, minted from the registry
      Returns:
      the session, to close
    • forget

      public void forget(long serverId, String exoLogin)
      Drops the session kept for one account and closes it, because eXo itself changed what that account is: new credentials, a reconnection, a disconnection.

      The logout is best-effort and never fails the caller: a session that could not be closed expires on BlueMind's own clock.

      Parameters:
      serverId - the declared server registration, zero for the legacy deployment property
      exoLogin - the eXo login whose session goes
    • forgetAll

      public void forgetAll()
      Drops every kept session, because the declared servers changed under them. The sessions are not closed — the entries carrying them are gone — so they expire on BlueMind's own clock.
    • parse

      public tools.jackson.databind.JsonNode parse(String body, URI uri)
      Parses a JSON answer.
      Parameters:
      body - the body
      uri - the request, for the message
      Returns:
      the tree
      Throws:
      CalDavException - when the body is not JSON
    • textOf

      public static String textOf(tools.jackson.databind.JsonNode node, String name)
      One text member of a JSON object.
      Parameters:
      node - the object
      name - the member
      Returns:
      its text, or null when absent, null or not a value
    • encodeSegment

      public static String encodeSegment(String segment)
      One path segment, percent-encoded outside the unreserved set.
      Parameters:
      segment - the decoded segment
      Returns:
      the encoded segment