With the release of version 4.0.0, the
This blog post will explain the changes we made that simplify running tasks on different threads with the
The SAP Cloud SDK for Java provides a so-called
It serves as thread-safe storage for potentially sensitive information.
A few of the most important objects stored are:
This information is used throughout the SAP Cloud SDK to provide features like tenant and principal isolation, JWT verification and authorization against other systems and services.
Every servlet that is received in its own thread, will receive a
This ensures different tenants and users are properly isolated.
Let's say we need to run a lengthy operation upon receiving a request.
The simple solution would be to block the servlet for as long as the operation takes:
Because the Spring servlet has its own thread we automatically get access to the
But our users now experience long loading times because the servlet takes a long time to answer.
We need to offload the long-running operation to another thread.
We can call an
The
However, this breaks the Multi Tenancy of the SAP Cloud SDK.
We cannot access the current tenant in the new thread.
We can try to use the
But the access is unreliable because the
We can also see that the resulting code has a lot of boilerplate code.
Let's take a look at how this use case would be done with SAP Cloud SDK 4.
With version 4 you can simplify your code for running asynchronous tasks with the newly introduced
This functionality is conveniently integrated to work with Springs
This is thanks to this
This already comes out of the box on newly generated applications such as
You can read more about the
The
You can create a custom
When using CAP the tenant, principal, and headers are derived from the
Use the
Please refer to the documentation on how to override existing values in the CAP context.
The new
It can be integrated with Spring and CAP, and can also be configured.
For more details, please refer to the documentation or the upgrade guide.
Do you have any questions on the new features?
Or are you struggling with the upgrade?
Don’t hesitate to share your feedback in the comments below, ask a question in the Q&A or to create a dedicated issue on our GitHub.
ThreadContext has seen major changes in the SAP Cloud SDK.This blog post will explain the changes we made that simplify running tasks on different threads with the
ThreadContext attached.What is a ThreadContext?
The SAP Cloud SDK for Java provides a so-called
ThreadContext.It serves as thread-safe storage for potentially sensitive information.
A few of the most important objects stored are:
- The current Tenant
- The current Principal (User)
- The JSON Web Token (JWT)
This information is used throughout the SAP Cloud SDK to provide features like tenant and principal isolation, JWT verification and authorization against other systems and services.
Every servlet that is received in its own thread, will receive a
ThreadContext.This ensures different tenants and users are properly isolated.
A Typical Use Case
Let's say we need to run a lengthy operation upon receiving a request.
The simple solution would be to block the servlet for as long as the operation takes:
@RestController
public class HelloWorldController
{
@GetMapping( "/hello" )
public String getHello()
{
final ThreadContext currentContext = ThreadContextAccessor.getCurrentContext();
database.store(currentTenant);// long-running operation
return "Hello";
}
}
Because the Spring servlet has its own thread we automatically get access to the
ThreadContext.But our users now experience long loading times because the servlet takes a long time to answer.
We need to offload the long-running operation to another thread.
Asynchronous Operations with SAP Cloud SDK Version 3
We can call an
@Async annotated method:@RestController
public class HelloWorldController
{
@Autowired
private DatabaseAccess databaseAccess;
@GetMapping( "/hello" )
public String getHello()
{
databaseAccess.store();
return "Hello";
}
}
The
@Async method must be public in another class to be executed in a separate thread:@EnableAsync
@Configuration
public class DatabaseAccess
{
@Async
public String store()
{
final ThreadContext currentContext = ThreadContextAccessor.getCurrentContext();// doesn't work
database.store(currentTenant);// long-running operation
return "success";
}
}
However, this breaks the Multi Tenancy of the SAP Cloud SDK.
We cannot access the current tenant in the new thread.
Propagate the ThreadContext
We can try to use the
ThreadContextExecutor to propagate the ThreadContext to the new thread:@RestController
public class HelloWorldController
{
@Autowired
private DatabaseAccess databaseAccess;
@GetMapping( "/hello" )
public String getHello()
{
final ThreadContextExecutor executor = new ThreadContextExecutor();
databaseAccess.store(executor);
return "Hello";
}
}
@EnableAsync
@Configuration
public class DatabaseAccess
{
@Async
public String store( ThreadContextExecutor executor )
{
return executor.execute(() -> {
final ThreadContext currentContext = ThreadContextAccessor.getCurrentContext();// unreliable
database.store(currentTenant);// long-running operation
return "success";
});
}
}
But the access is unreliable because the
ThreadContext will get removed when the servlet is finished.We can also see that the resulting code has a lot of boilerplate code.
Let's take a look at how this use case would be done with SAP Cloud SDK 4.
Asynchronous Operations with SAP Cloud SDK Version 4
With version 4 you can simplify your code for running asynchronous tasks with the newly introduced
ThreadContextExecutors:Future runningTask = ThreadContextExecutors.submit(() -> operation());
This functionality is conveniently integrated to work with Springs
@Async annotation like so:@RestController
public class HelloWorldController
{
@Autowired
private DatabaseAccess databaseAccess;
@GetMapping( "/hello" )
public String getHello()
{
databaseAccess.store();
return "Hello";
}
}
@Configuration
public class DatabaseAccess
{
@Async
public void store()
{
database.store(TenantAccessor.getCurrentTenant());// long-running operation
}
}
This is thanks to this
@Configuration all methods annotated with @Async will have the ThreadContext available:@EnableAsync
@Configuration
public class AsynchronousConfiguration implements AsyncConfigurer
{
@Override
public Executor getAsyncExecutor()
{
return ThreadContextExecutors.getExecutor();
}
}
This already comes out of the box on newly generated applications such as
scp-cf-spring.You can read more about the
@Async functionality here.Configuring the Executor
The
ThreadContextExecutors class leverages a single ThreadContextExecutorService instance that can be configured.You can create a custom
ThreadContextExecutorService, for example to use a different thread pool, via:ThreadContextExecutorService executor = DefaultThreadContextExecutorService
.of(Executors.newFixedThreadPool(3));
// use it directly:
executor.submit(myTask);
// or set it to be used by the static ThreadContextExecutors API:
ThreadContextExecutors.setExecutor(executor);
ThreadContextExecutors.submit(myTask);
CAP Integration
When using CAP the tenant, principal, and headers are derived from the
RequestContext.Use the
cds-integration-cloud-sdk dependency to propagate the context out of the box when using our APIs.Please refer to the documentation on how to override existing values in the CAP context.
Summary
The new
ThreadContextExecutors allows you to run tasks on different threads with the ThreadContext attached.It can be integrated with Spring and CAP, and can also be configured.
For more details, please refer to the documentation or the upgrade guide.
Share Your Feedback
Do you have any questions on the new features?
Or are you struggling with the upgrade?
Don’t hesitate to share your feedback in the comments below, ask a question in the Q&A or to create a dedicated issue on our GitHub.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.