I have a strange issue. Given this controller code:
return CompletableFuture
.supplyAsync(() -> this.acknowledgementTemplatingService.prepareHtmlViewForDocument(offer))
.thenApply(htmlContent -> documentService.generatePdfDocumentFromHtml(htmlContent, ASSETS))
Given this templating code from this.acknowledgementTemplatingService.prepareHtmlViewForDocument(offer)
Using the templating engine from thymeleaf:
ITemplateEngine
Context ctx = new Context();
ctx.setVariable(
ThymeleafEvaluationContext.THYMELEAF_EVALUATION_CONTEXT_CONTEXT_VARIABLE_NAME,
new ThymeleafEvaluationContext(applicationContext, null));
ctx.setVariable("offer", offerDto);
return templateEngine.process("/documents/offer/pdf", ctx);
When this code runs, the template /documents/offer/pdf
cannot be found by the templating engine.
When i refactor this code to the following - calling the template rendering AND the pdf generation in one step:
return CompletableFuture
.supplyAsync(() -> {
String htmlContent = this.serviceDescriptionTemplatingService.prepareHtmlViewForDocument(offerDto);
byte[] pdfContent = documentService.generatePdfDocumentFromHtml(htmlContent, ASSETS);
return pdfContent;
}
The view will be found and will be rendered properly.
What am i doing wrong?
I found a solution for this:
supplyAsync()
offers as additional parameter an Executor
to be used. I have a dedicated Executor
in my configuration like this:
@Bean
public Executor executor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(6);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("DmpkApplication-");
executor.initialize();
// the following is necessary because of:
// https://stackoverflow.com/a/57434013/7320372
return new DelegatingSecurityContextAsyncTaskExecutor(executor);
}
I can autowire
this bean and use it like so:
return CompletableFuture
.supplyAsync(() -> this.serviceDescriptionTemplatingService.prepareHtmlViewForDocument(offer), executor)
.thenApply(html -> documentService.generatePdfDocumentFromHtml(html, ASSETS))
.thenApply(bytes -> this.handleDownload(bytes, offer.getIssueKey()));
So the trick seems to use my dedicated executor in the first supplyAsync
statement.