From 86fc0c6dfd486cbfce2a72c3fc6477dbf8f7cfb2 Mon Sep 17 00:00:00 2001 From: Felipe Desiderati Date: Wed, 29 Nov 2023 15:50:24 -0300 Subject: [PATCH] Some improvements on GraphQL asynchronous configuration. --- README.md | 5 +- common-parent-info/pom.xml | 2 +- .../GraphQLWebSecurityConfiguration.java | 48 +++++++++++++++++++ .../WebSecurityAutoConfiguration.java | 23 ++++++++- .../common/web/security/jwt/JwtService.java | 12 +++-- .../configuration/AsyncWebConfiguration.java | 46 ++++++++++-------- ...guration.java => JpaWebConfiguration.java} | 2 +- .../configuration/WebAutoConfiguration.java | 2 +- .../ExceptionHandlingController.java | 2 +- .../exception/GraphQLExceptionHandler.kt | 8 ++-- ...itional-spring-configuration-metadata.json | 6 +++ .../application-common-web.properties | 15 ++++++ .../configuration/AsyncConfiguration.java | 25 ---------- .../common/configuration/AsyncProperties.java | 47 ------------------ ...itional-spring-configuration-metadata.json | 35 +------------- .../resources/application-common.properties | 21 ++++---- 16 files changed, 147 insertions(+), 152 deletions(-) create mode 100644 common-web-security/src/main/java/io/herd/common/web/security/configuration/GraphQLWebSecurityConfiguration.java rename common-web/src/main/java/io/herd/common/web/configuration/{JpaWebAutoConfiguration.java => JpaWebConfiguration.java} (98%) delete mode 100644 common/src/main/java/io/herd/common/configuration/AsyncProperties.java diff --git a/README.md b/README.md index af6c990..ae6f370 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Commons Herd.io --------------- [![Build Status](https://github.com/desiderati/commons/workflows/Build/badge.svg)](https://github.com/desiderati/commons/actions) -[![Version](https://img.shields.io/badge/Version-3.1.2.RELEASE-red.svg)](https://github.com/desiderati/commons/releases) +[![Version](https://img.shields.io/badge/Version-3.1.3.RELEASE-red.svg)](https://github.com/desiderati/commons/releases) [![GitHub Stars](https://img.shields.io/github/stars/desiderati/commons.svg?label=GitHub%20Stars)](https://github.com/desiderati/commons/) [![LICENSE](https://img.shields.io/badge/License-MIT-lightgrey.svg)](https://github.com/desiderati/commons/blob/master/LICENSE) @@ -24,6 +24,9 @@ Changelog All project changes will be documented in this file. +#### [3.1.3.RELEASE] - 2023-11-27 +- Some improvements on GraphQL asynchronous configuration. + #### [3.1.2.RELEASE] - 2023-11-27 - Some improvements on GraphQL ErrorHandling. diff --git a/common-parent-info/pom.xml b/common-parent-info/pom.xml index f89defc..0a7066e 100644 --- a/common-parent-info/pom.xml +++ b/common-parent-info/pom.xml @@ -82,7 +82,7 @@ - 3.1.2.RELEASE + 3.1.3.RELEASE ${revision} diff --git a/common-web-security/src/main/java/io/herd/common/web/security/configuration/GraphQLWebSecurityConfiguration.java b/common-web-security/src/main/java/io/herd/common/web/security/configuration/GraphQLWebSecurityConfiguration.java new file mode 100644 index 0000000..2861bdd --- /dev/null +++ b/common-web-security/src/main/java/io/herd/common/web/security/configuration/GraphQLWebSecurityConfiguration.java @@ -0,0 +1,48 @@ +package io.herd.common.web.security.configuration; + +import graphql.kickstart.autoconfigure.web.OnSchemaOrSchemaProviderBean; +import graphql.kickstart.autoconfigure.web.servlet.AsyncServletProperties; +import graphql.kickstart.autoconfigure.web.servlet.GraphQLServletProperties; +import graphql.kickstart.autoconfigure.web.servlet.GraphQLWebSecurityAutoConfiguration; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; +import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.AsyncTaskExecutor; +import org.springframework.security.authentication.DefaultAuthenticationEventPublisher; +import org.springframework.security.task.DelegatingSecurityContextAsyncTaskExecutor; +import org.springframework.web.servlet.DispatcherServlet; + +import java.util.concurrent.Executor; + +@Configuration +@RequiredArgsConstructor +@ConditionalOnWebApplication(type = Type.SERVLET) +@Conditional(OnSchemaOrSchemaProviderBean.class) +@ConditionalOnProperty(value = "graphql.servlet.enabled", havingValue = "true", matchIfMissing = true) +@AutoConfigureBefore(GraphQLWebSecurityAutoConfiguration.class) +@ConditionalOnClass({DispatcherServlet.class, DefaultAuthenticationEventPublisher.class}) +@EnableConfigurationProperties({GraphQLServletProperties.class, AsyncServletProperties.class}) +public class GraphQLWebSecurityConfiguration { + + private final AsyncServletProperties asyncServletProperties; + + @Bean("graphqlAsyncTaskExecutor") + @ConditionalOnProperty(prefix = "spring.mvc.async", name = "thread-context-inheritable", havingValue = "true") + public Executor simpleGraphQLAsyncTaskExecutor( + @Qualifier(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME) AsyncTaskExecutor taskExecutor + ) { + if (asyncServletProperties.isDelegateSecurityContext()) { + return new DelegatingSecurityContextAsyncTaskExecutor(taskExecutor); + } + return null; + } +} diff --git a/common-web-security/src/main/java/io/herd/common/web/security/configuration/WebSecurityAutoConfiguration.java b/common-web-security/src/main/java/io/herd/common/web/security/configuration/WebSecurityAutoConfiguration.java index 7271b1c..2e72fe3 100644 --- a/common-web-security/src/main/java/io/herd/common/web/security/configuration/WebSecurityAutoConfiguration.java +++ b/common-web-security/src/main/java/io/herd/common/web/security/configuration/WebSecurityAutoConfiguration.java @@ -18,6 +18,7 @@ */ package io.herd.common.web.security.configuration; +import graphql.kickstart.autoconfigure.web.servlet.GraphQLWebSecurityAutoConfiguration; import io.herd.common.web.UrlUtils; import io.herd.common.web.configuration.CorsProperties; import io.herd.common.web.configuration.WebAutoConfiguration; @@ -31,6 +32,7 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -48,6 +50,7 @@ import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.AuthenticationConverter; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @@ -61,16 +64,22 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.handler.HandlerMappingIntrospector; +import static org.springframework.security.core.context.SecurityContextHolder.MODE_INHERITABLETHREADLOCAL; + @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) @EnableWebSecurity @EnableMethodSecurity(securedEnabled = true) @PropertySource("classpath:application-common-web-security.properties") +@AutoConfigureBefore(GraphQLWebSecurityAutoConfiguration.class) @ComponentScan(basePackages = "io.herd.common.web.security", // Do not add the auto-configured classes, otherwise the auto-configuration will not work as expected. excludeFilters = @ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) ) -@Import(WebAutoConfiguration.class) // To be used with @WebMvcTest +@Import({ + WebAutoConfiguration.class, + GraphQLWebSecurityConfiguration.class +}) // To be used with @WebMvcTest public class WebSecurityAutoConfiguration implements WebMvcConfigurer { @Value("${spring.web.security.default.authentication.enabled:false}") @@ -118,6 +127,18 @@ public class WebSecurityAutoConfiguration implements WebMvcConfigurer { @Value("${springdoc.api-docs.path:/api-docs}") private String springDocOpenApiPath; + public WebSecurityAutoConfiguration( + @Value("${graphql.servlet.async.enabled:false}") + boolean graphqlServletAsyncEnabled, + + @Value("${graphql.servlet.async.delegate-security-context:true}") + boolean graphqlServletAsyncDelegateSecurityContext + ) { + if (graphqlServletAsyncEnabled && graphqlServletAsyncDelegateSecurityContext) { + SecurityContextHolder.setStrategyName(MODE_INHERITABLETHREADLOCAL); + } + } + private JwtAuthenticationFilter jwtAuthenticationFilter; private JwtAuthorizationFilter jwtAuthorizationFilter; private SignRequestAuthorizationFilter signRequestAuthorizationFilter; diff --git a/common-web-security/src/main/java/io/herd/common/web/security/jwt/JwtService.java b/common-web-security/src/main/java/io/herd/common/web/security/jwt/JwtService.java index 09c23c0..ad8acec 100644 --- a/common-web-security/src/main/java/io/herd/common/web/security/jwt/JwtService.java +++ b/common-web-security/src/main/java/io/herd/common/web/security/jwt/JwtService.java @@ -127,11 +127,13 @@ private SecretKey loadSecretKey() { public String generateToken(JwtTokenConfigurer configurer) { Claims tokenPayload = Jwts.claims(); configurer.configure(tokenPayload); - if (expirationPeriod > 0) { - tokenPayload.setExpiration( - Date.from(LocalDateTime.now().plusHours(expirationPeriod).toInstant(ZoneOffset.UTC)) - ); - } + tokenPayload.setExpiration( + Date.from( + LocalDateTime.now().plusHours( + expirationPeriod > 0 ? expirationPeriod : Integer.MAX_VALUE + ).toInstant(ZoneOffset.UTC) + ) + ); JwtBuilder builder = Jwts.builder().setClaims(tokenPayload); if (jwtEncryptionMethod == JwtEncryptionMethod.ASYMMETRIC) { diff --git a/common-web/src/main/java/io/herd/common/web/configuration/AsyncWebConfiguration.java b/common-web/src/main/java/io/herd/common/web/configuration/AsyncWebConfiguration.java index 786c190..466c3e3 100644 --- a/common-web/src/main/java/io/herd/common/web/configuration/AsyncWebConfiguration.java +++ b/common-web/src/main/java/io/herd/common/web/configuration/AsyncWebConfiguration.java @@ -18,20 +18,20 @@ */ package io.herd.common.web.configuration; -import io.herd.common.configuration.AsyncProperties; import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; +import org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.task.AsyncTaskExecutor; +import org.springframework.core.task.SimpleAsyncTaskExecutor; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.async.CallableProcessingInterceptor; import org.springframework.web.context.request.async.TimeoutCallableProcessingInterceptor; +import org.springframework.web.filter.RequestContextFilter; import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -39,30 +39,19 @@ @Slf4j @Configuration -@EnableConfigurationProperties(AsyncProperties.class) @ConditionalOnProperty(name = "spring.async.enabled", havingValue = "true") public class AsyncWebConfiguration { - private final AsyncProperties asyncProperties; - - @Autowired - public AsyncWebConfiguration(AsyncProperties asyncProperties) { - this.asyncProperties = asyncProperties; - } - - /** Configure async support for Spring MVC. */ + /** + * Configure async support for Spring MVC. + */ @Bean - public WebMvcConfigurer webMvcConfigurerConfigurer( - @Qualifier("defaultAsyncTaskExecutor") AsyncTaskExecutor taskExecutor, - CallableProcessingInterceptor callableProcessingInterceptor - ) { + public WebMvcConfigurer webMvcConfigurerConfigurer(CallableProcessingInterceptor callableProcessingInterceptor) { return new WebMvcConfigurer() { @Override public void configureAsyncSupport(@NotNull AsyncSupportConfigurer configurer) { - log.info("Configuring Spring MVC with asynchronous task executor..."); - configurer.setDefaultTimeout(asyncProperties.getTaskTimeout()).setTaskExecutor(taskExecutor); + log.info("Configuring Spring MVC with custom CallableProcessingInterceptor..."); configurer.registerCallableInterceptors(callableProcessingInterceptor); - WebMvcConfigurer.super.configureAsyncSupport(configurer); } }; } @@ -80,4 +69,21 @@ public Object handleTimeout(NativeWebRequest request, Callable task) thro } }; } + + @Bean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME) + @ConditionalOnProperty(prefix = "spring.mvc.async", name = "thread-context-inheritable", havingValue = "true") + public AsyncTaskExecutor simpleAsyncTaskExecutor() { + log.info("Creating simple asynchronous task executor..."); + SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor(); + executor.setThreadNamePrefix("simple-task-"); + return executor; + } + + @Bean + @ConditionalOnProperty(prefix = "spring.mvc.async", name = "thread-context-inheritable", havingValue = "true") + public RequestContextFilter requestContextFilter() { + RequestContextFilter requestContextFilter = new OrderedRequestContextFilter(); + requestContextFilter.setThreadContextInheritable(true); + return requestContextFilter; + } } diff --git a/common-web/src/main/java/io/herd/common/web/configuration/JpaWebAutoConfiguration.java b/common-web/src/main/java/io/herd/common/web/configuration/JpaWebConfiguration.java similarity index 98% rename from common-web/src/main/java/io/herd/common/web/configuration/JpaWebAutoConfiguration.java rename to common-web/src/main/java/io/herd/common/web/configuration/JpaWebConfiguration.java index ddcf8ac..7c0fb13 100644 --- a/common-web/src/main/java/io/herd/common/web/configuration/JpaWebAutoConfiguration.java +++ b/common-web/src/main/java/io/herd/common/web/configuration/JpaWebConfiguration.java @@ -44,7 +44,7 @@ @ConditionalOnJpa @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) @ConditionalOnProperty(prefix = "spring.jpa", name = "open-in-view", havingValue = "true") -public class JpaWebAutoConfiguration { +public class JpaWebConfiguration { @Bean public Filter openEntityManagerInViewFilter() { diff --git a/common-web/src/main/java/io/herd/common/web/configuration/WebAutoConfiguration.java b/common-web/src/main/java/io/herd/common/web/configuration/WebAutoConfiguration.java index 9741efc..71c2045 100644 --- a/common-web/src/main/java/io/herd/common/web/configuration/WebAutoConfiguration.java +++ b/common-web/src/main/java/io/herd/common/web/configuration/WebAutoConfiguration.java @@ -85,7 +85,7 @@ // Need to be auto-loaded too. AsyncWebConfiguration.class, JpaAutoConfiguration.class, - JpaWebAutoConfiguration.class, + JpaWebConfiguration.class, OpenApiConfiguration.class }) public class WebAutoConfiguration implements WebMvcRegistrations, WebMvcConfigurer, RepositoryRestConfigurer { diff --git a/common-web/src/main/java/io/herd/common/web/exception/ExceptionHandlingController.java b/common-web/src/main/java/io/herd/common/web/exception/ExceptionHandlingController.java index 034834e..c0e646a 100644 --- a/common-web/src/main/java/io/herd/common/web/exception/ExceptionHandlingController.java +++ b/common-web/src/main/java/io/herd/common/web/exception/ExceptionHandlingController.java @@ -586,7 +586,7 @@ private boolean shouldLogAsWarning(Throwable throwable, String errorMessage) { if (isSameClass) { List msgsList = entry.getValue(); - if (msgsList.size() == 0) { + if (msgsList.isEmpty()) { return true; } else { for (String msg : msgsList) { diff --git a/common-web/src/main/java/io/herd/common/web/graphql/exception/GraphQLExceptionHandler.kt b/common-web/src/main/java/io/herd/common/web/graphql/exception/GraphQLExceptionHandler.kt index b4adcb5..181b781 100644 --- a/common-web/src/main/java/io/herd/common/web/graphql/exception/GraphQLExceptionHandler.kt +++ b/common-web/src/main/java/io/herd/common/web/graphql/exception/GraphQLExceptionHandler.kt @@ -27,6 +27,7 @@ import graphql.execution.DataFetcherExceptionHandlerResult import graphql.kickstart.spring.error.ErrorContext import io.herd.common.exception.ApplicationException import org.springframework.context.MessageSource +import org.springframework.context.i18n.LocaleContextHolder import org.springframework.web.bind.annotation.ExceptionHandler import java.io.Serializable import java.lang.reflect.UndeclaredThrowableException @@ -41,9 +42,6 @@ open class GraphQLExceptionHandler( private const val DEFAULT_ERROR_CODE = "internal_server_error" private const val DEFAULT_ERROR_MESSAGE = "The server encountered an unexpected condition that prevented it from fulfilling the request!" - - // TODO Felipe Desiderati: Extrair a localização a partir da requisição. - private val DEFAULT_LOCALE = Locale("pt", "BR") } override fun handleException( @@ -103,11 +101,11 @@ open class GraphQLExceptionHandler( errorContext: ErrorContext, extensions: Map = mapOf() ): GraphQLError { - val internalServerError = getMessage(DEFAULT_LOCALE, DEFAULT_ERROR_CODE, DEFAULT_ERROR_MESSAGE) + val internalServerError = getMessage(LocaleContextHolder.getLocale(), DEFAULT_ERROR_CODE, DEFAULT_ERROR_MESSAGE) return GraphqlErrorBuilder.newError() .message( getMessage( - DEFAULT_LOCALE, + LocaleContextHolder.getLocale(), message ?: DEFAULT_ERROR_CODE, internalServerError ) diff --git a/common-web/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/common-web/src/main/resources/META-INF/additional-spring-configuration-metadata.json index d9ded13..c88b32f 100644 --- a/common-web/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/common-web/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -30,6 +30,12 @@ "description": "Only the paths defined here will be exposed on Swagger UI.", "defaultValue": "/v.*,/public/v.*" }, + { + "name": "spring.mvc.async.thread-context-inheritable", + "type": "java.lang.Boolean", + "description": "Set whether to expose the LocaleContext and RequestAttributes as inheritable for child threads (using an InheritableThreadLocal). If true, a SimpleAsyncTaskExecutor will be used (instead of a ThreadPoolTaskExecutor) since it will never re-use threads. WARNING: Do not use inheritance for child threads if you are accessing a thread pool which is configured to potentially add new threads on demand, since this will expose the inherited context to such a pooled thread.", + "defaultValue": false + }, { "name": "spring.web.cors.allowed-methods", "type": "java.lang.String", diff --git a/common-web/src/main/resources/application-common-web.properties b/common-web/src/main/resources/application-common-web.properties index 7686548..78034d6 100644 --- a/common-web/src/main/resources/application-common-web.properties +++ b/common-web/src/main/resources/application-common-web.properties @@ -73,6 +73,21 @@ springdoc.package-to-scan= # Only the paths defined here will be exposed on Swagger UI. springdoc.paths-to-expose=/v.*,/public/v.* +# +# Async Support +# +# Set whether to expose the LocaleContext and RequestAttributes as inheritable for child threads, using +# an InheritableThreadLocal. If configured as true, a SimpleAsyncTaskExecutor will be used (instead of a +# ThreadPoolTaskExecutor), since it will never re-use threads. +# +# WARNING: Do not use inheritance for child threads if you are accessing a thread pool which is configured +# to potentially add new threads on demand, since this will expose the inherited context to such a pooled thread. +spring.mvc.async.thread-context-inheritable=false + +# Amount of time before asynchronous request handling times out. If this value is not set, the default timeout +# of the underlying implementation is used. +spring.mvc.async.request-timeout=30000 + # # GraphQL Support # diff --git a/common/src/main/java/io/herd/common/configuration/AsyncConfiguration.java b/common/src/main/java/io/herd/common/configuration/AsyncConfiguration.java index e69970a..0b332f0 100644 --- a/common/src/main/java/io/herd/common/configuration/AsyncConfiguration.java +++ b/common/src/main/java/io/herd/common/configuration/AsyncConfiguration.java @@ -21,44 +21,19 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @Slf4j @Configuration @EnableAsync @EnableScheduling -@EnableConfigurationProperties(AsyncProperties.class) @ConditionalOnProperty(name = "spring.async.enabled", havingValue = "true") public class AsyncConfiguration implements AsyncConfigurer { - private final AsyncProperties asyncProperties; - - @Autowired - public AsyncConfiguration(AsyncProperties asyncProperties) { - this.asyncProperties = asyncProperties; - } - - @Bean(name = "defaultAsyncTaskExecutor") - public AsyncTaskExecutor defaultAsyncTaskExecutor() { - log.info("Creating default asynchronous task executor..."); - ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); - executor.setThreadNamePrefix("Async-"); - executor.setCorePoolSize(asyncProperties.getInitialPoolSize()); - executor.setMaxPoolSize(asyncProperties.getMaxPoolSize()); - executor.setQueueCapacity(asyncProperties.getQueueCapacity()); - executor.initialize(); - return executor; - } - @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new SimpleAsyncUncaughtExceptionHandler(); diff --git a/common/src/main/java/io/herd/common/configuration/AsyncProperties.java b/common/src/main/java/io/herd/common/configuration/AsyncProperties.java deleted file mode 100644 index 17425e7..0000000 --- a/common/src/main/java/io/herd/common/configuration/AsyncProperties.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2023 - Felipe Desiderati - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT - * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package io.herd.common.configuration; - -import jakarta.validation.constraints.NotNull; -import lombok.Getter; -import lombok.Setter; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.stereotype.Component; -import org.springframework.validation.annotation.Validated; - -@Getter -@Setter -@Validated -@Component -@ConfigurationProperties(prefix = "spring.async") -public class AsyncProperties { - - @NotNull - private Integer initialPoolSize = 5; - - @NotNull - private Integer maxPoolSize = 10; - - @NotNull - private Integer queueCapacity = 25; - - @NotNull - private Integer taskTimeout = 30000; - -} diff --git a/common/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/common/src/main/resources/META-INF/additional-spring-configuration-metadata.json index d57e9f8..c28da80 100644 --- a/common/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/common/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,10 +1,4 @@ { - "groups": [ - { - "name": "spring.async", - "type": "io.herd.common.configuration.AsyncProperties" - } - ], "properties": [ { "name": "app.system-id", @@ -14,33 +8,8 @@ { "name": "spring.async.enabled", "type": "java.lang.Boolean", - "description": "It defines if Async support will be enabled or not.", - "defaultValue": false - }, - { - "name": "spring.async.initial-pool-size", - "type": "java.lang.Integer", - "description": "Set the ThreadPoolExecutor's initial pool size. Default is 5.", - "defaultValue": 5 - }, - { - "name": "spring.async.max-pool-size", - "type": "java.lang.Integer", - "description": "Set the ThreadPoolExecutor's maximum pool size. Default is 10.", - "defaultValue": 10 - }, - { - "name": "spring.async.queue-pool-size", - "type": "java.lang.Integer", - "description": "Set the capacity for the ThreadPoolExecutor's BlockingQueue. Default is 25.", - "defaultValue": 25 - }, - { - "name": "spring.async.task-timeout", - "type": "java.lang.Integer", - "description": "Specify the amount of time, in milliseconds, before asynchronous request handling times out. Default is 30000.", - "defaultValue": 30000 + "description": "It defines if custom async support will be enabled or not.", + "defaultValue": true } ] } - diff --git a/common/src/main/resources/application-common.properties b/common/src/main/resources/application-common.properties index 826a2c5..3a967d8 100644 --- a/common/src/main/resources/application-common.properties +++ b/common/src/main/resources/application-common.properties @@ -13,17 +13,16 @@ logging.level.com.amazonaws.xray.strategy.sampling.pollers.RulePoller=ERROR # # Async Support # -# It defines if Async support will be enabled or not. -spring.async.enabled=false +# It defines if custom async support will be enabled or not. If false, the default ThreadPoolTaskExecutor +# will be created anyway, with the configuration below. +spring.async.enabled=true -# Set the ThreadPoolExecutor's initial pool size. Default is 5. -#spring.async.initial-pool-size=5 +# Core number of threads. Default is 8. +spring.task.execution.pool.core-size=8 -# Set the ThreadPoolExecutor's maximum pool size. Default is 10. -#spring.async.max-pool-size=10 +# Maximum allowed number of threads. If tasks are filling up the queue, the pool can expand up to that size +# to accommodate the load. Ignored if the queue is unbounded. +spring.task.execution.pool.max-size=128 -# Set the capacity for the ThreadPoolExecutor's BlockingQueue. Default is 25. -#spring.async.queue-pool-size=25 - -# Specify the amount of time, in milliseconds, before asynchronous request handling times out. Default is 30000. -#spring.async.task-timeout=30000 +# Queue capacity. An unbounded capacity does not increase the pool and therefore ignores the "max-size" property. +spring.task.execution.pool.queue-capacity=128