Multi Tenancy with the Thread Context

What is a Thread Context?#

The SAP Cloud SDK for Java provides a so called ThreadContext. It serves as thread-safe storage for potentially sensitive information. Specifically the following three objects are stored:

  • The current Tenant
  • The current Principal (User)
  • The JSON Web Token (JWT)

This information is used throughout the SDK to provide features like tenant and principal isolation, JWT verification and authorization against other systems and services. To ensure different tenants and users are properly isolated in an application this information is always limited to the Thread it was created on, unless it is explicitly passed on by the application (see Propagating the Thread Context).

How is a Thread Context created?#

The SDK provides a RequestFilter that will listen on incoming HTTP requests. If the Authorization header contains a JWT from the AppRouter the filter will:

  • Verify this token
  • Store it in the ThreadContext and
  • Pull the Tenant and Principal information from it

How can the Thread Context be used?#

Accessing Information#

The Thread context can be accessed via the static ThreadContextAccessor.

For the frequently needed Tenant, Principal and JWT there are also dedicated accessors: TenantAccessor, PrincipalAccessor, AuthTokenAccessor.

Storing Information#

The ThreadContext allows for some manipulation by the application. However, oftentimes it is more convenient to leverage the executeWith...() functionality offered by the dedicated accessors.

Consider a scenario where some part of the code should run on behalf of a specific tenant. In that case you can override the current tenant explicitly:

TenantAccessor.executeWithTenant(customTenant, () -> doStuff());

Running asynchronous operations#

As the name suggests the ThreadContext is bound to a Thread, more specifically to the one it was created. If asynchronous operations need to access the information it has to be propagated to the new Threads.

The following code achieves this:

ThreadContextExecutor executor = new ThreadContextExecutor();
Callable operationWithContext = () -> executor.execute(() -> operation());
invokeAsynchronously(operationWithContext);

Take note that the ThreadContextExecutor is created before performing the asynchronous operation. This is important because only at that time the context is available and will be propagated.

A similar approach can be applied with the Tenant, Principal and AuthToken accessors. This code runs an asynchronous operation with a dedicated tenant:

Callable operationWithTenant = TenantAccessor.executeWithTenant(customTenant, () -> operation());
invokeAsynchronously(operationWithContext);
note

Be cautious with long running, asynchronous operations. A propagated Thread context will only persist as long as the Thread lives that it was created on. So when the parent Thread dies the context will seize to exist and no longer be available in any of the Threads.

Last updated on by Frank Essenberger