What this is about
When CRMS runs as a separate service, the application needs a way to reach it. That is crms-client. It offers the same ports as the embedded CRMS. A port is a Java interface through which your application calls CRMS. Behind the port there is no renderer here, but an HTTP call to the service.
When the client applies
The operating mode is set in codamai.crms.placement (CODAMAI_CRMS_PLACEMENT). The hub derives it from the runtime the CRMS module is placed in.
placement | In the build | The start fails if |
|---|---|---|
local | the embedded CRMS and the format modules | the embedded CRMS is missing |
remote | crms-client | crms-client is missing, or the CRMS renderer or the CRMS HTTP interface are present |
More in Embedded or standalone.
What the client offers
| Port and method | Route on the service |
|---|---|
ReportRenderPort.render | POST /crms/render |
ReportRenderPort.renderTo | POST /crms/render, the response flows straight into your OutputStream as it arrives |
ReportRenderPort.validate | POST /crms/render/validate |
CapabilitiesPort.capabilities | GET /crms/capabilities |
DocumentArchivePort.read | GET /crms/documents/{revision} |
DocumentArchivePort.revisions | GET /crms/documents?template=…&scope=…&reference=… |
DocumentArchivePort.verify | POST /crms/documents/{revision}/verify |
DocumentArchivePort.setLegalHold / releaseLegalHold | POST /crms/documents/{revision}/legal-hold / …/release |
DocumentArchivePort.erase | POST /crms/documents/{revision}/erase |
Editing templates is not a port. A template editor calls the service’s template routes directly, see The endpoints.
The person’s token, not a service token
On every call, crms-client sends along the token of the person whose request the application is answering right now. Unchanged. The CRMS service then exchanges it for one for its own client. This way every revision and every document names the person who caused it, and not “the service”.
sequenceDiagram
participant B as User
participant A as Application
participant C as CRMS service
participant I as CIAS in the service
B->>A: Request with bearer token
A->>C: POST /crms/render, the same bearer token
C->>I: Check and exchange the token
I-->>C: Person, tenant, roles of the CRMS client
C-->>A: Document or error answer
A-->>B: Response
When: the normal case, the application is answering an HTTP request right now
The client takes the token from the header Authorization: Bearer … of this request.
Result: the call is made on behalf of this person
When: a background job, a timer, a thread of its own
There is no person, so there is no token. The client sends nothing.
Result: CrmsException with crms-authentication-required
When: the application provides its own bean of type CallerToken
The client asks this bean for the token instead of the running request.
Result: the call is made with the token the bean returns
How the exchange works in the service is described in Token exchange. Which roles the service then sees, in The roles.
Settings
| Property | Variable | Default | Meaning |
|---|---|---|---|
codamai.crms.client.url | CODAMAI_CRMS_CLIENT_URL | none, required | http or https address of the service. The routes sit below it: https://crms.example.com becomes https://crms.example.com/crms/render |
codamai.crms.client.connect-timeout | CODAMAI_CRMS_CLIENT_CONNECT_TIMEOUT | PT5S | how long the client waits for a connection |
codamai.crms.client.timeout | CODAMAI_CRMS_CLIENT_TIMEOUT | PT2M | how long one whole call may take |
The values are ISO-8601 durations: PT5S means five seconds, PT2M two minutes. If the address is missing or is not an http or https address, the application does not start. Neither does it with a duration of zero or less.
The default of timeout is longer than the service’s render timeout (default PT30S). This way a slow document comes back as the service’s render-timeout, instead of the client giving up first.
How responses come back
| Status | Error answer with messageKey? | What your code gets |
|---|---|---|
| 2xx | – | the object, as embedded: RenderedDocument, Capabilities, ArchivedDocument … |
| error | yes, payload-rejected | PayloadRejectedException with the same violations |
| error | yes | CrmsException with the same key and the same parameters |
| 401 | no | crms-authentication-required |
| 403 | no | crms-access-denied |
| other | no | crms-unavailable |
A 401 or 403 without an error answer comes from the service’s security chain, before CRMS saw the request. crms-unavailable means: the service could not be reached, did not answer in time, or the response did not come from CRMS, such as a proxy’s page or a missing route. A successful response that lacks something is also crms-unavailable. The client never returns half an object.
Values on the way
- Data travels as JSON carries it. Dates and times as ISO-8601 text, decimals with their decimal places:
12.50stays12.50. Otherwise a template would print something different than when embedded. - What JSON cannot carry is already refused by the client, as
payload-rejectedwith the same rule as the embedded check (value-type-unsupported,key-invalid). - With attachments, the body is compressed. If a render request has pictures as attachments, the client sends it with
Content-Encoding: gzip. Base64 makes pictures a third larger, gzip takes back almost all of it. - No redirects. If the address answers with a redirect, the client does not follow it. The result is then
crms-unavailable.