Spring Cloud sleuth breaking DelegatingSecurityContextExecutor - spring-boot

I've a spring boot app version 1.3.3.RELEASE and I've added Spring Cloud Sleuth get those tracing goodies. After adding spring-cloud-sleuth I see some of my async API's are breaking as passed in org.springframework.security.concurrent.DelegatingSecurityContextExecutor. is null. Short story, I'm using org.springframework.security.concurrent.DelegatingSecurityContextExecutor from spring security to pass security context around different executors. For that I'm just wrapping those executors into org.springframework.security.concurrent.DelegatingSecurityContextExecutor instances during application start up. What I'm not sure is why Spring Sleuth is becoming a problem here. The way we're using async processing is, we have created a custom annotation and then created a point cut's around that to inject async behavior.
Example:
public class Async implements AsyncConfigurer {
public Executor getCustomAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//all configuration for thread pool
return new DelegatingSecurityContextAsyncTaskExecutor(executor);
}
public Executor getProcessingExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//all configuration for thread pool
return new DelegatingSecurityContextAsyncTaskExecutor(executor);
}
}
public class MyControllerClass {
#Autowired
private Executor myProcessingExecutor;
#MyCustomAsyncAnnotation
public ResponseObject getResponse() {
//As soon as below code get's executed I get NPE as
//`org.springframework.security.concurrent.DelegatingSecurityContextExecutor.executor` is null. What I'm not sure is why it's null??
// As you see above in my Async class I've registered executor in DelegatingSecurityContextAsyncTaskExecutor, then why it's null here?
//If I remove spring-cloud-sleuth code works fine once again.
CompletableFuture.supplyAsync(() -> ..., myProcessingExecutor);
}
}
class MyCustomAspect {
private Executor myCustomAsyncExecutor;
//create point cut here for those method which have
// #MyCustomAsyncAnnotionation and process it async via above executor.
}
I'm using spring boot 1.3.3.RELEASE, rest easy, spring-security 4.0.3.RELEASE, spring-cloud-sleuth 1.0.12.RELEASE

Related

Spring Batch/Data JPA application not persisting/saving data to Postgres database when calling JPA repository (save, saveAll) methods

I am near wits-end. I read/googled endlessly so far and tried the solutions on all the google/stackoverflow posts that have this similiar issue (there a quite a few). Some seemed promising, but nothing has worked for me yet; though I have made some progress and I am on the right track I believe (I'm believing at this point its something with the Transaction manager and some possible conflict with Spring Batch vs. Spring Data JPA).
References:
Spring boot repository does not save to the DB if called from scheduled job
JpaItemWriter: no transaction is in progress
Similar to the aforementioned posts, I have a Spring Boot application that is using Spring Batch and Spring Data JPA. It reads comma delimited data from a .csv file, then does some processing/transformation, and attempts to persist/save to database using the JPA Repository methods, specifically here .saveAll() (I also tried .save() method and this did the same thing), since I'm saving a List<MyUserDefinedDataType> of a user-defined data type (batch insert).
Now, my code was working fine on Spring Boot starter 1.5.9.RELEASE, but I recently attempted to upgrade to 2.X.X, which I found, after countless hours of debugging, only version 2.2.0.RELEASE would persist/save data to database. So an upgrade to >= 2.2.1.RELEASE breaks persistence. Everything is read fine from the .csv, its just when the first time the code flow hits a JPA repository method like .save() .saveAll(), the application keeps running but nothing gets persisted. I also noticed the Hikari pool logs "active=1 idle=4", but when I looked at the same log when on version 1.5.9.RELEASE, it says active=0 idle=5 immediately after persisting the data, so the application is definitely hanging. I went into the debugger and even saw after jumping into the Repository calls, it goes into almost an infinite cycle through the Spring AOP libraries and such (all third party) and I don't believe ever comes back to the real application/business logic that I wrote.
3c22fb53ed64 2021-05-20 23:53:43.909 DEBUG
[HikariPool-1 housekeeper] com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Pool stats (total=5, active=1, idle=4, waiting=0)
Anyway, I tried the most common solutions that worked for other people which were:
Defining a JpaTransactionManager #Bean and injecting it into the Step function, while keeping the JobRepository using the PlatformTransactionManager. This did not work. Then I also I tried using the JpaTransactionManager also in the JobRepository #Bean, this also did not work.
Defining a #RestController endpoint in my application to manually trigger this Job, instead of doing it manually from my main Application.java class. (I talk about this more below). And per one of the posts I posted above, the data persisted correctly to the database even on spring >= 2.2.1, which further I suspect now something with the Spring Batch persistence/entity/transaction managers is messed up.
The code is basically this:
BatchConfiguration.java
#Configuration
#EnableBatchProcessing
#Import({DatabaseConfiguration.class})
public class BatchConfiguration {
// Datasource is a Postgres DB defined in separate IntelliJ project that I add to my pom.xml
DataSource dataSource;
#Autowired
public BatchConfiguration(#Qualifier("dataSource") DataSource dataSource) {
this.dataSource = dataSource;
}
#Bean
#Primary
public JpaTransactionManager jpaTransactionManager() {
final JpaTransactionManager tm = new JpaTransactionManager();
tm.setDataSource(dataSource);
return tm;
}
#Bean
public JobRepository jobRepository(PlatformTransactionManager transactionManager) throws Exception {
JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean();
jobRepositoryFactoryBean.setDataSource(dataSource);
jobRepositoryFactoryBean.setTransactionManager(transactionManager);
jobRepositoryFactoryBean.setDatabaseType("POSTGRES");
return jobRepositoryFactoryBean.getObject();
}
#Bean
public JobLauncher jobLauncher(JobRepository jobRepository) {
SimpleJobLauncher simpleJobLauncher = new SimpleJobLauncher();
simpleJobLauncher.setJobRepository(jobRepository);
return simpleJobLauncher;
}
#Bean(name = "jobToLoadTheData")
public Job jobToLoadTheData() {
return jobBuilderFactory.get("jobToLoadTheData")
.start(stepToLoadData())
.listener(new CustomJobListener())
.build();
}
#Bean
#StepScope
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(maxThreads);
threadPoolTaskExecutor.setThreadGroupName("taskExecutor-batch");
return threadPoolTaskExecutor;
}
#Bean(name = "stepToLoadData")
public Step stepToLoadData() {
TaskletStep step = stepBuilderFactory.get("stepToLoadData")
.transactionManager(jpaTransactionManager())
.<List<FieldSet>, List<myCustomPayloadRecord>>chunk(chunkSize)
.reader(myCustomFileItemReader(OVERRIDDEN_BY_EXPRESSION))
.processor(myCustomPayloadRecordItemProcessor())
.writer(myCustomerWriter())
.faultTolerant()
.skipPolicy(new AlwaysSkipItemSkipPolicy())
.skip(DataValidationException.class)
.listener(new CustomReaderListener())
.listener(new CustomProcessListener())
.listener(new CustomWriteListener())
.listener(new CustomSkipListener())
.taskExecutor(taskExecutor())
.throttleLimit(maxThreads)
.build();
step.registerStepExecutionListener(stepExecutionListener());
step.registerChunkListener(new CustomChunkListener());
return step;
}
My main method:
Application.java
#Autowired
#Qualifier("jobToLoadTheData")
private Job loadTheData;
#Autowired
private JobLauncher jobLauncher;
#PostConstruct
public void launchJob () throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException
{
JobParameters parameters = (new JobParametersBuilder()).addDate("random", new Date()).toJobParameters();
jobLauncher.run(loadTheData, parameters);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
Now, normally I'm reading this .csv from Amazon S3 bucket, but since I'm testing locally, I am just placing the .csv in the project directory and reading it directly by triggering the job in the Application.java main class (as you can see above). Also, I do have some other beans defined in this BatchConfiguration class but I don't want to over-complicate this post more than it already is and from the googling I've done, the problem possibly is with the methods I posted (hopefully).
Also, I would like to point out, similar to one of the other posts on Google/stackoverflow with a user having a similar problem, I created a #RestController endpoint that simply calls the .run() method the JobLauncher and I pass in the JobToLoadTheData Bean, and it triggers the batch insert. Guess what? Data persists to the database just fine, even on spring >= 2.2.1.
What is going on here? is this a clue? is something funky going wrong with some type of entity or transaction manager? I'll take any advice tips! I can provide any more information that you guys may need , so please just ask.
You are defining a bean of type JobRepository and expecting it to be picked up by Spring Batch. This is not correct. You need to provide a BatchConfigurer and override getJobRepository. This is explained in the reference documentation:
You can customize any of these beans by creating a custom implementation of the
BatchConfigurer interface. Typically, extending the DefaultBatchConfigurer
(which is provided if a BatchConfigurer is not found) and overriding the required
getter is sufficient.
This is also documented in the Javadoc of #EnableBatchProcessing. So in your case, you need to define a bean of type Batchconfigurer and override getJobRepository and getTransactionManager, something like:
#Bean
public BatchConfigurer batchConfigurer(EntityManagerFactory entityManagerFactory, DataSource dataSource) {
return new DefaultBatchConfigurer(dataSource) {
#Override
public PlatformTransactionManager getTransactionManager() {
return new JpaTransactionManager(entityManagerFactory);
}
#Override
public JobRepository getJobRepository() {
JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean();
jobRepositoryFactoryBean.setDataSource(dataSource);
jobRepositoryFactoryBean.setTransactionManager(getTransactionManager());
// set other properties
return jobRepositoryFactoryBean.getObject();
}
};
}
In a Spring Boot context, you could also override the createTransactionManager and createJobRepository methods of org.springframework.boot.autoconfigure.batch.JpaBatchConfigurer if needed.

Spring Async get current Executor

I am using Spring Async by implementing AsyncConfigurer and overriding the getAsyncExecutor to define my Executor.
Now I would like to expose an endpoint, to return the current queue size, number of threads ...etc, of the Executor that is used by Async.
But I could not find a way to find or autowire the current executor that is used by Async.
I was thinking I can define a bean, that will be both used by the getAsyncExecutor method, and my reporting service.
But I was wondering if there is an easier/more appropriate way I can interact with async to get the current Executor.
My current config:
#Configuration
#EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
final ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setThreadNamePrefix("async-thread-");
threadPoolTaskExecutor.setCorePoolSize(2);
threadPoolTaskExecutor.setMaxPoolSize(2);
threadPoolTaskExecutor.setQueueCapacity(100);
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
}
You haven't registered a bean for the ThreadPoolTaskExector.
#Configuration
#EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {
#Override
public Executor getAsyncExecutor() {
return taskExecutor();
}
#Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setThreadNamePrefix("async-thread-");
threadPoolTaskExecutor.setCorePoolSize(2);
threadPoolTaskExecutor.setMaxPoolSize(2);
threadPoolTaskExecutor.setQueueCapacity(100);
return threadPoolTaskExecutor;
}
}
However Spring Boot 2.1 already pre-configures a TaskExecutor for you which you can configure through properties. You can then remove all config and only use #EnableAsync in your configuration.
spring.task.execution.pool.core-size=2
spring.task.execution.pool.max-size=2
spring.task.execution.pool.queue-capacity=100
spring.task.exection.thread-name-prefix=async-thread-
This configuration, together with a single #EnableAsync will achieve the same, without additional configuration.
With either configuration, you can now use #Autowired to get the instance in your service.

How to fix - Executor is required to handle java.util.concurrent.Callable return values

I have a controller in Spring Boot/Spring Data Rest where my handler downloads a file like this
#RequestMapping(method = GET, value = "/orderAttachments/{id}/download")
#ResponseStatus(HttpStatus.OK)
public ResponseEntity<StreamingResponseBody> downloadAttachment(#PathVariable("id") Long attachmentID, HttpServletRequest request)
throws IOException {
InputStream inputStream = fileManager.getInputStream(orderAttachment);
StreamingResponseBody responseBody = outputStream -> {
int numberOfBytesToWrite;
byte[] data = new byte[1024];
while ((numberOfBytesToWrite = inputStream.read(data, 0, data.length)) != -1) {
outputStream.write(data, 0, numberOfBytesToWrite);
}
inputStream.close();
};
return ResponseEntity
.ok()
.contentLength(orderAttachment.getFileSize())
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + orderAttachment.getFileName()+ "\"")
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(responseBody);
}
I got this error in the console
!!!
An Executor is required to handle java.util.concurrent.Callable return values.
Please, configure a TaskExecutor in the MVC config under "async support".
The SimpleAsyncTaskExecutor currently in use is not suitable under load.
-------------------------------
Request URI: '/api/v1/orderAttachments/163/download'
!!!
But everything works, I can download the file from calling the API
For starters it is a warning and not an error. If it was an error your application wouldn't start or work. It is a warning telling you that the default used SimpleAsyncTaskExecutor shouldn't be used in production.
The SimpleAsyncTaskExecutor creates a new Thread when something needs to be processed asynchronously. Each thread takes up 1MB of memory by default (and some processing power). Now imagine someone issuing 100000 calls to this API. This would also mean 100000 threads (times the memory and each a little cpu power). It would cripple your application and maybe even kill it.
NOTE: If you are on Spring Boot 2.1.x or higher a default TaskExecutor will be configured and used for you. However if you have something in your configuration that disables the auto-configuration (like an #EnableWebMvc on an #Configuration class for instance). To configure that executor (with threads etc) check the Spring Boot Reference Guide.
You can also manually create a TaskExecutor and associate that with Spring MVC.
#Configuration
public class WebMvcConfig implements WebMvcConfigurer {
#Bean
public ThreadPoolTaskExecutor mvcTaskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(10);
taskExecutor.setMaxPoolSize(10);
return taskExecutor;
}
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setTaskExecutor(mvcTaskExecutor());
}
}
Now this dedicated TaskExecutor will be used for the async handling in Spring MVC. However as mentioned this should already be pre-configured by Spring Boot if you haven't disabled auto-configuration (maybe even by accident!).
It is just a warning (that is why you still are able to download requested file) and Spring's suggestion to define your own TaskExecutor rather than using the SimpleAsyncTaskExecutor which, as the message states, is not suitable under load.
SimpleAsyncTaskExecutor fires up a new Thread for each task and does not reuse them. If you haven't configured limit on concurrent threads, it is by default unlimited.
#Configuration
class AsyncConfiguration implements AsyncConfigurer {
#Bean
protected WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setTaskExecutor([your task executor]);
}
};
}
}
Use the above configuration to define your own TaskExecutor that suits your needs and get rid of the warning.
Spring boot in 2.1.0 provides auto configuration for task executors and uses for #EnableAsync and Spring MVC Async support.
There is no task executor bean / webMvcConfigurer configuration is needed from application. If you have one please remove it and you should be good.
If you like to control the values you can adjust using applicaion properties/yml file with spring.task.execution.*. Full listing can be found here
More details here and here
For me, the following did the trick:
#Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
#Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setMaxPoolSize(10);
executor.setThreadNamePrefix("mvc-task-executor-");
executor.initialize();
configurer.setTaskExecutor(executor);
}
}
By this means a ThreadPoolTaskExecutor is specified for the MVC context.
Make sure your application picks up the #Configuration class! If your main class is annotated with #SpringBootApplication, this should happen automatically.
Also, make sure to configure your ThreadPoolTaskExecutor appropriately to your needs.

Server Sent Event with SQL Database connection using Spring Boot

I want to implement Server Sent Events in Spring Boot. The data is there in SQL database, It is resulting in blocking connection. Web Flux is an option but it is supported only for some NoSQL database.
Yes, you right WebFlux framework doesn't SQL databases in the non blocking mode because reactive drivers does not exist.
But WebFlux provides some instruments to avoid blocking our main threads while we are making blocking long queries to a database.
1) Create configuration with Scheduler where count of threads equals to pool size:
#Configuration
public class SchedulerConfiguration {
#Value("${spring.datasource.maximum-pool-size}
private final Integer connectionPoolSize;
#Bean
#Qualifier("jdbcScheduler")
public Scheduler jdbcScheduler() {
return Schedulers.fromExecutor(Executors.newFixedThreadPool(connectionPoolSize));
}
}
2) Inject your "jdbcScheduler" to the service class:
#Service
public class DataService {
#Autowired
private final DataRepository jdbcRepository;
#Autowired #Qualifier("jdbcScheduler")
private final Scheduler scheduler;
public Mono<String> findById(long id) {
return async(() -> repository.findById(id));
}
private <T> Mono<T> async(Callable<T> callable) {
return Mono.fromCallable(callable).publishOn(scheduler);
}
}
Wrap your blocking method by Mono.fromCallable and delegate from main thread to your "scheduler" via Mono.publishOn
More about schedulers you can read here: Threading and Schedulers
Yes you can achieve asynchronous processing in spring without flux by using their inbuilt #Async processing, here how you can achieve it.
Step1: Enable Aysnc and define a bean for Executor. You can define separate configuration or directly under Main application class.
#SpringBootApplication
#EnableAsync
public class Application {
public static void main(String[] args) {
// close the application context to shut down the custom ExecutorService
SpringApplication.run(Application.class, args).close();
}
#Bean
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("GithubLookup-");
executor.initialize();
return executor;
}
STEP2:
Following is the simple way to configure a method with void return type to run asynchronously, You can also invoke method by retrieve the result of the asynchronous process using the Future object.
#Async
public void asyncMethodWithVoidReturnType() {
System.out.println("Execute method asynchronously. "
+ Thread.currentThread().getName());
}
For more information, You can visit Spring official guide Spring Async

Can spring provide concept like job queue

Example like I have a 25 job and i want to execute 3 job concurrent and after one of the three job complete then next one pick-up from queue.
You can do this with classes in the standard Java library - you don't need Spring for this. Use an ExecutorService, for example:
class MyJob implements Runnable {
private final String message;
MyJob(String message) {
this.message = message;
}
#Override
public void run() {
System.out.println(message);
}
}
public class Example {
public static void main(String[] args) {
// Executor service with 3 threads
ExecutorService executorService = Executors.newFixedThreadPool(3);
// Submit jobs to be executed
executorService.execute(new MyJob("testing"));
executorService.execute(new MyJob("one"));
executorService.execute(new MyJob("two"));
executorService.execute(new MyJob("three"));
// ...
}
}
Yes Spring provides support for Job scheduling through Quartz Scheduler. For more information about how Spring is using Quartz, you can go through the official spring documentation.
Apart from this, if you want some ready made example, you can go through, Spring 3 + Quartz Scheduler and Spring 4 + Quartz Scheduler.
I suggest you to use the Spring boot:
Here is a good start with scheduling using Annotation with Spring
https://spring.io/guides/gs/scheduling-tasks/
Here is a nice Introduction for Spring Boot with Quartz: http://de.slideshare.net/davidkiss/spring-boot-with-quartz
Good Luck!

Resources