From 6a0a3799bbee427ad9338cb30e34ae10de47173a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johnny=20Miller=20=28=E9=94=BA=E4=BF=8A=29?= Date: Sun, 13 Dec 2020 20:14:41 +0800 Subject: [PATCH] perf($Project): abstract common configuration into custom starter Added 2 new modules: - muscle-and-fitness-server-spring-boot-starter - muscle-and-fitness-server-reactive-spring-boot-starter BREAKING CHANGE: abstract common configuration into custom starter --- api-portal/pom.xml | 5 +- .../maf/apiportal/ApiPortalApplication.java | 14 +- .../remoteapi/AuthCenterRemoteApi.java | 8 +- .../aspect/ExceptionControllerAdvice.java | 154 ----------------- .../MethodArgumentValidationAspect.java | 160 ------------------ .../universal/aspect/ValidateArgument.java | 19 --- .../universal/aspect/WebRequestLogAspect.java | 130 -------------- .../SecurityHandlerConfiguration.java | 2 +- .../controller/RedirectController.java | 55 ------ .../filter/JwtAuthenticationFilter.java | 4 +- .../universal/service/CommonService.java | 5 +- .../universal/service/SftpService.java | 5 +- .../service/impl/CommonServiceImpl.java | 2 - .../service/impl/SftpServiceImpl.java | 18 +- auth-center/pom.xml | 5 +- .../permission/service/PermissionService.java | 7 +- .../service/impl/PermissionServiceImpl.java | 3 - .../aspect/ExceptionControllerAdvice.java | 155 ----------------- .../MethodArgumentValidationAspect.java | 160 ------------------ .../universal/aspect/ValidateArgument.java | 19 --- .../universal/aspect/WebRequestLogAspect.java | 129 -------------- .../configuration/WebMvcConfiguration.java | 45 ----- .../controller/CommonController.java | 2 +- .../controller/RedirectController.java | 55 ------ .../universal/filter/RequestFilter.java | 32 ---- .../universal/service/CommonService.java | 7 +- .../service/impl/CommonServiceImpl.java | 4 +- .../authcenter/user/service/UserService.java | 5 +- .../user/service/impl/UserServiceImpl.java | 2 - common/pom.xml | 3 - .../common}/domain/ValidationTestPayload.java | 2 +- exercise-mis/pom.xml | 5 +- .../aspect/ExceptionControllerAdvice.java | 136 --------------- .../MethodArgumentValidationAspect.java | 160 ------------------ .../universal/aspect/ValidateArgument.java | 19 --- .../universal/aspect/WebRequestLogAspect.java | 129 -------------- .../configuration/WebMvcConfiguration.java | 45 ----- .../controller/CommonController.java | 2 +- .../domain/ValidationTestPayload.java | 24 --- .../universal/filter/RequestFilter.java | 32 ---- .../universal/service/CommonService.java | 7 +- .../service/impl/CommonServiceImpl.java | 4 +- .../README.md | 10 ++ .../pom.xml | 29 ++++ .../package-info.java | 1 + .../src/main/resources/application.yml | 1 + ...rverSpringBootStarterApplicationTests.java | 13 ++ .../README.md | 10 ++ .../pom.xml | 37 ++++ .../aspect/ExceptionControllerAdvice.java | 15 +- .../aspect/WebRequestLogAspect.java | 6 +- ...scleAndFitnessServerAutoConfiguration.java | 74 ++++++++ .../configuration/WebMvcConfiguration.java | 2 +- .../controller/RedirectController.java | 11 +- .../filter/RequestFilter.java | 4 +- .../helper/IpHelper.java | 36 ++-- .../package-info.java | 1 + .../util/CaseConversionUtil.java | 2 +- .../util/FileUtil.java | 2 +- .../util/RequestUtil.java | 2 +- .../util/ResponseUtil.java | 2 +- .../main/resources/META-INF/spring.factories | 3 + muscle-mis/pom.xml | 5 +- .../maf/musclemis/MuscleMisApplication.java | 14 +- .../aspect/ExceptionControllerAdvice.java | 136 --------------- .../MethodArgumentValidationAspect.java | 160 ------------------ .../universal/aspect/ValidateArgument.java | 19 --- .../configuration/ServerConfiguration.java | 118 ------------- .../configuration/WebMvcConfiguration.java | 45 ----- .../universal/filter/RequestFilter.java | 32 ---- .../universal/service/CommonService.java | 5 +- .../service/impl/CommonServiceImpl.java | 2 - pom.xml | 36 +++- service-registry/pom.xml | 5 +- .../ServiceRegistryApplication.java | 14 +- .../aspect/ExceptionControllerAdvice.java | 136 --------------- .../MethodArgumentValidationAspect.java | 160 ------------------ .../universal/aspect/ValidateArgument.java | 19 --- .../universal/aspect/WebRequestLogAspect.java | 129 -------------- .../configuration/ServerConfiguration.java | 118 ------------- .../configuration/WebMvcConfiguration.java | 45 ----- .../controller/RedirectController.java | 55 ------ .../universal/service/CommonService.java | 5 +- .../service/impl/CommonServiceImpl.java | 2 - spring-boot-admin/pom.xml | 5 +- .../MethodArgumentValidationAspect.java | 160 ------------------ .../universal/aspect/ValidateArgument.java | 19 --- .../universal/aspect/WebRequestLogAspect.java | 129 -------------- .../controller/RedirectController.java | 55 ------ .../universal/filter/RequestFilter.java | 32 ---- .../universal/service/CommonService.java | 5 +- .../service/impl/CommonServiceImpl.java | 2 - 92 files changed, 343 insertions(+), 3364 deletions(-) delete mode 100644 api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/aspect/ExceptionControllerAdvice.java delete mode 100644 api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/aspect/MethodArgumentValidationAspect.java delete mode 100644 api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/aspect/ValidateArgument.java delete mode 100644 api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/aspect/WebRequestLogAspect.java delete mode 100644 api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/controller/RedirectController.java delete mode 100644 auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/aspect/ExceptionControllerAdvice.java delete mode 100644 auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/aspect/MethodArgumentValidationAspect.java delete mode 100644 auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/aspect/ValidateArgument.java delete mode 100644 auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/aspect/WebRequestLogAspect.java delete mode 100644 auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/configuration/WebMvcConfiguration.java delete mode 100644 auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/controller/RedirectController.java delete mode 100644 auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/filter/RequestFilter.java rename {auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal => common/src/main/java/com/jmsoftware/maf/common}/domain/ValidationTestPayload.java (89%) delete mode 100644 exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/aspect/ExceptionControllerAdvice.java delete mode 100644 exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/aspect/MethodArgumentValidationAspect.java delete mode 100644 exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/aspect/ValidateArgument.java delete mode 100644 exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/aspect/WebRequestLogAspect.java delete mode 100644 exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/configuration/WebMvcConfiguration.java delete mode 100644 exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/domain/ValidationTestPayload.java delete mode 100644 exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/filter/RequestFilter.java create mode 100644 muscle-and-fitness-server-reactive-spring-boot-starter/README.md create mode 100644 muscle-and-fitness-server-reactive-spring-boot-starter/pom.xml create mode 100644 muscle-and-fitness-server-reactive-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverreactivespringbootstarter/package-info.java create mode 100644 muscle-and-fitness-server-reactive-spring-boot-starter/src/main/resources/application.yml create mode 100644 muscle-and-fitness-server-reactive-spring-boot-starter/src/test/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/MuscleAndFitnessServerSpringBootStarterApplicationTests.java create mode 100644 muscle-and-fitness-server-spring-boot-starter/README.md create mode 100644 muscle-and-fitness-server-spring-boot-starter/pom.xml rename {spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal => muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter}/aspect/ExceptionControllerAdvice.java (92%) rename {muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal => muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter}/aspect/WebRequestLogAspect.java (97%) create mode 100644 muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/configuration/MuscleAndFitnessServerAutoConfiguration.java rename {api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal => muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter}/configuration/WebMvcConfiguration.java (94%) rename {muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal => muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter}/controller/RedirectController.java (76%) rename {service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal => muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter}/filter/RequestFilter.java (86%) rename api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/configuration/ServerConfiguration.java => muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/helper/IpHelper.java (73%) create mode 100644 muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/package-info.java rename {common/src/main/java/com/jmsoftware/maf/common => muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter}/util/CaseConversionUtil.java (99%) rename {common/src/main/java/com/jmsoftware/maf/common => muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter}/util/FileUtil.java (97%) rename {common/src/main/java/com/jmsoftware/maf/common => muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter}/util/RequestUtil.java (94%) rename {common/src/main/java/com/jmsoftware/maf/common => muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter}/util/ResponseUtil.java (97%) create mode 100644 muscle-and-fitness-server-spring-boot-starter/src/main/resources/META-INF/spring.factories delete mode 100644 muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/aspect/ExceptionControllerAdvice.java delete mode 100644 muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/aspect/MethodArgumentValidationAspect.java delete mode 100644 muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/aspect/ValidateArgument.java delete mode 100644 muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/configuration/ServerConfiguration.java delete mode 100644 muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/configuration/WebMvcConfiguration.java delete mode 100644 muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/filter/RequestFilter.java delete mode 100644 service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/aspect/ExceptionControllerAdvice.java delete mode 100644 service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/aspect/MethodArgumentValidationAspect.java delete mode 100644 service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/aspect/ValidateArgument.java delete mode 100644 service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/aspect/WebRequestLogAspect.java delete mode 100644 service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/configuration/ServerConfiguration.java delete mode 100644 service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/configuration/WebMvcConfiguration.java delete mode 100644 service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/controller/RedirectController.java delete mode 100644 spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/aspect/MethodArgumentValidationAspect.java delete mode 100644 spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/aspect/ValidateArgument.java delete mode 100644 spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/aspect/WebRequestLogAspect.java delete mode 100644 spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/controller/RedirectController.java delete mode 100644 spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/filter/RequestFilter.java diff --git a/api-portal/pom.xml b/api-portal/pom.xml index 3afa45f7..e7107ca2 100644 --- a/api-portal/pom.xml +++ b/api-portal/pom.xml @@ -108,7 +108,10 @@ com.jmsoftware.maf common - 0.0.1-SNAPSHOT + + + com.jmsoftware.maf + muscle-and-fitness-server-spring-boot-starter diff --git a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/ApiPortalApplication.java b/api-portal/src/main/java/com/jmsoftware/maf/apiportal/ApiPortalApplication.java index fe497a62..2a3cb9e3 100644 --- a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/ApiPortalApplication.java +++ b/api-portal/src/main/java/com/jmsoftware/maf/apiportal/ApiPortalApplication.java @@ -1,7 +1,7 @@ package com.jmsoftware.maf.apiportal; import com.jmsoftware.maf.apiportal.universal.configuration.ProjectProperty; -import com.jmsoftware.maf.apiportal.universal.configuration.ServerConfiguration; +import com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.helper.IpHelper; import lombok.extern.slf4j.Slf4j; import lombok.val; import org.springframework.boot.SpringApplication; @@ -28,11 +28,11 @@ public class ApiPortalApplication { private static final String LINE_SEPARATOR = System.lineSeparator(); private static ProjectProperty projectProperty; - private static ServerConfiguration serverConfiguration; + private static IpHelper ipHelper; - public ApiPortalApplication(ProjectProperty projectProperty, ServerConfiguration serverConfiguration) { + public ApiPortalApplication(ProjectProperty projectProperty, IpHelper ipHelper) { ApiPortalApplication.projectProperty = projectProperty; - ApiPortalApplication.serverConfiguration = serverConfiguration; + ApiPortalApplication.ipHelper = ipHelper; } public static void main(String[] args) { @@ -45,8 +45,8 @@ public static void main(String[] args) { log.info("⚙️ Environment: {}", projectProperty.getEnvironment()); log.info("⏳ Deployment duration: {} seconds ({} ms)", duration.getSeconds(), duration.toMillis()); log.info("⏰ App started at {} (timezone - {})", endInstant, TimeZone.getDefault().getDisplayName()); - log.info("{} App running at{} - Local: http://localhost:{}{}/{} - Network: {}/", - LINE_SEPARATOR, LINE_SEPARATOR, serverConfiguration.getServerPort(), projectProperty.getContextPath(), - LINE_SEPARATOR, serverConfiguration.getBaseUrl()); + log.info("{} App running at{} - Local: http://localhost:{}{}/{} - Network: {}/{}", + LINE_SEPARATOR, LINE_SEPARATOR, ipHelper.getServerPort(), projectProperty.getContextPath(), + LINE_SEPARATOR, ipHelper.getPublicIp(),projectProperty.getContextPath()); } } diff --git a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/remoteapi/AuthCenterRemoteApi.java b/api-portal/src/main/java/com/jmsoftware/maf/apiportal/remoteapi/AuthCenterRemoteApi.java index 4705880a..407edc12 100644 --- a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/remoteapi/AuthCenterRemoteApi.java +++ b/api-portal/src/main/java/com/jmsoftware/maf/apiportal/remoteapi/AuthCenterRemoteApi.java @@ -1,6 +1,5 @@ package com.jmsoftware.maf.apiportal.remoteapi; -import com.jmsoftware.maf.apiportal.universal.aspect.ValidateArgument; import com.jmsoftware.maf.common.bean.ResponseBodyBean; import com.jmsoftware.maf.common.domain.authcenter.permission.GetPermissionListByRoleIdListPayload; import com.jmsoftware.maf.common.domain.authcenter.permission.GetPermissionListByRoleIdListResponse; @@ -13,6 +12,7 @@ import com.jmsoftware.maf.common.domain.authcenter.user.SaveUserForRegisteringPayload; import com.jmsoftware.maf.common.domain.authcenter.user.SaveUserForRegisteringResponse; import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -26,6 +26,7 @@ * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com * @date 5/10/20 4:50 PM */ +@Validated @FeignClient(name = "auth-center") public interface AuthCenterRemoteApi { /** @@ -34,7 +35,6 @@ public interface AuthCenterRemoteApi { * @param payload the payload * @return the user by login token */ - @ValidateArgument @PostMapping("/user-remote-api/get-user-by-login-token") ResponseBodyBean getUserByLoginToken(@Valid @RequestBody GetUserByLoginTokenPayload payload); @@ -44,7 +44,6 @@ public interface AuthCenterRemoteApi { * @param payload the payload * @return the role list by user id */ - @ValidateArgument @PostMapping("/role-remote-api/get-role-list-by-user-id") ResponseBodyBean getRoleListByUserId(@Valid @RequestBody GetRoleListByUserIdPayload payload); @@ -54,7 +53,6 @@ public interface AuthCenterRemoteApi { * @param payload the payload * @return the response body bean */ - @ValidateArgument @PostMapping("/user-remote-api/save-user-for-registering") ResponseBodyBean saveUserForRegistering(@Valid @RequestBody SaveUserForRegisteringPayload payload); @@ -64,7 +62,6 @@ public interface AuthCenterRemoteApi { * @param payload the payload * @return the permission list by role id list */ - @ValidateArgument @PostMapping("/permission-remote-api/get-permission-list-by-role-id-list") ResponseBodyBean getPermissionListByRoleIdList(@Valid @RequestBody GetPermissionListByRoleIdListPayload payload); @@ -74,7 +71,6 @@ public interface AuthCenterRemoteApi { * @param payload the payload * @return the response body bean */ - @ValidateArgument @PostMapping("/permission-remote-api/get-permission-list-by-user-id") ResponseBodyBean getPermissionListByUserId(@Valid @RequestBody GetPermissionListByUserIdPayload payload); } diff --git a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/aspect/ExceptionControllerAdvice.java b/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/aspect/ExceptionControllerAdvice.java deleted file mode 100644 index d6b88c87..00000000 --- a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/aspect/ExceptionControllerAdvice.java +++ /dev/null @@ -1,154 +0,0 @@ -package com.jmsoftware.maf.apiportal.universal.aspect; - -import cn.hutool.core.collection.CollUtil; -import com.jmsoftware.maf.common.bean.ResponseBodyBean; -import com.jmsoftware.maf.common.constant.HttpStatus; -import com.jmsoftware.maf.common.exception.BaseException; -import com.jmsoftware.maf.common.util.RequestUtil; -import lombok.extern.slf4j.Slf4j; -import lombok.val; -import org.springframework.context.support.DefaultMessageSourceResolvable; -import org.springframework.http.converter.HttpMessageNotReadableException; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.InternalAuthenticationServiceException; -import org.springframework.validation.BindException; -import org.springframework.web.HttpRequestMethodNotSupportedException; -import org.springframework.web.bind.MethodArgumentNotValidException; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; -import org.springframework.web.servlet.NoHandlerFoundException; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.validation.ConstraintViolationException; -import java.util.Objects; - -/** - *

ExceptionControllerAdvice

- *

- * Exception advice for global controllers. - * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-03-02 17:39 - **/ -@Slf4j -@ControllerAdvice -public class ExceptionControllerAdvice { - /** - *

Exception handler.

- *

ATTENTION: In this method, cannot throw any exception.

- * - * @param request HTTP request - * @param exception any kinds of exception occurred in controller - * @return custom exception info - */ - @ResponseBody - @ExceptionHandler(value = Exception.class) - public ResponseBodyBean handleException(HttpServletRequest request, - HttpServletResponse response, - Exception exception) { - log.error("Exception occurred when [{}] requested access. URL: {}", RequestUtil.getRequestIpAndPort(request), - request.getServletPath()); - - // FIXME: THIS IS NOT A PROBLEM - // ATTENTION: Use only ResponseBodyBean.ofStatus() in handleException() method and DON'T throw any exception - if (exception instanceof NoHandlerFoundException) { - log.error("NoHandlerFoundException: Request URL = {}, HTTP method = {}", - ((NoHandlerFoundException) exception).getRequestURL(), - ((NoHandlerFoundException) exception).getHttpMethod()); - response.setStatus(HttpStatus.NOT_FOUND.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.NOT_FOUND); - } else if (exception instanceof HttpRequestMethodNotSupportedException) { - log.error("Exception occurred when the request handler does not support a specific request method. " + - "Current method is {}, Support HTTP method = {}", - ((HttpRequestMethodNotSupportedException) exception).getMethod(), - ((HttpRequestMethodNotSupportedException) exception).getSupportedHttpMethods()); - response.setStatus(HttpStatus.METHOD_NOT_ALLOWED.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.METHOD_NOT_ALLOWED); - } else if (exception instanceof MethodArgumentNotValidException) { - log.error("Exception occurred when validation on an argument annotated with fails. Exception message: {}", - exception.getMessage()); - response.setStatus(HttpStatus.BAD_REQUEST.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.BAD_REQUEST.getCode(), - getFieldErrorMessageFromException((MethodArgumentNotValidException) exception), - null); - } else if (exception instanceof ConstraintViolationException) { - log.error("Constraint violations exception occurred. Exception message: {}", exception.getMessage()); - response.setStatus(HttpStatus.BAD_REQUEST.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.BAD_REQUEST.getCode(), - CollUtil.getFirst(((ConstraintViolationException) exception).getConstraintViolations()).getMessage(), - null); - } else if (exception instanceof MethodArgumentTypeMismatchException) { - log.error("MethodArgumentTypeMismatchException: Parameter name = {}, Exception message: {}", - ((MethodArgumentTypeMismatchException) exception).getName(), - ((MethodArgumentTypeMismatchException) exception).getMessage()); - response.setStatus(HttpStatus.PARAM_NOT_MATCH.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.PARAM_NOT_MATCH); - } else if (exception instanceof HttpMessageNotReadableException) { - log.error("HttpMessageNotReadableException: {}", - ((HttpMessageNotReadableException) exception).getMessage()); - response.setStatus(HttpStatus.PARAM_NOT_NULL.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.PARAM_NOT_NULL); - } else if (exception instanceof BaseException) { - log.error("BaseException: Status code: {}, message: {}, data: {}", ((BaseException) exception).getCode(), - exception.getMessage(), ((BaseException) exception).getData()); - response.setStatus(((BaseException) exception).getCode()); - return ResponseBodyBean.ofStatus(((BaseException) exception).getCode(), exception.getMessage(), - ((BaseException) exception).getData()); - } else if (exception instanceof BindException) { - log.error("Exception message: {} ", exception.getMessage()); - response.setStatus(HttpStatus.INVALID_PARAM.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.INVALID_PARAM); - } else if (exception instanceof IllegalArgumentException) { - log.error("Exception message: {} ", exception.getMessage()); - response.setStatus(HttpStatus.BAD_REQUEST.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.BAD_REQUEST.getCode(), exception.getMessage(), null); - } else if (exception instanceof BadCredentialsException) { - // IMPORTANT: org.springframework.security.authentication.BadCredentialsException only exists in the project - // that depends on org.springframework.boot.spring-boot-starter-security - log.error("Exception message: {} ", exception.getMessage()); - response.setStatus(HttpStatus.BAD_CREDENTIALS.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.BAD_CREDENTIALS.getCode(), exception.getMessage(), null); - } else if (exception instanceof InternalAuthenticationServiceException) { - log.error("An authentication request could not be processed due to a system problem that occurred " + - "internally. Exception message: {} ", exception.getMessage()); - if (exception.getCause() instanceof BaseException) { - val exceptionCause = (BaseException) exception.getCause(); - val code = exceptionCause.getCode(); - response.setStatus(code); - return ResponseBodyBean.ofStatus(HttpStatus.fromCode(code)); - } - response.setStatus(HttpStatus.BAD_CREDENTIALS.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.BAD_CREDENTIALS.getCode(), exception.getMessage(), null); - } - log.error("Internal system exception occurred! Exception message: {} ", exception.getMessage(), exception); - response.setStatus(HttpStatus.ERROR.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.ERROR.getCode(), HttpStatus.ERROR.getMessage(), null); - } - - /** - * Get field error message from exception. If two or more fields do not pass Spring Validation check, then will - * return the 1st error message of the error field. - * - * @param exception MethodArgumentNotValidException - * @return field error message - */ - private String getFieldErrorMessageFromException(MethodArgumentNotValidException exception) { - try { - val firstErrorField = - (DefaultMessageSourceResolvable) Objects.requireNonNull(exception.getBindingResult() - .getAllErrors() - .get(0) - .getArguments())[0]; - val firstErrorFieldName = firstErrorField.getDefaultMessage(); - val firstErrorFieldMessage = exception.getBindingResult().getAllErrors().get(0).getDefaultMessage(); - return String.format("%s %s", firstErrorFieldName, firstErrorFieldMessage); - } catch (Exception e) { - log.error("Exception occurred when get field error message from exception. Exception message: {}", - e.getMessage(), e); - return HttpStatus.INVALID_PARAM.getMessage(); - } - } -} diff --git a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/aspect/MethodArgumentValidationAspect.java b/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/aspect/MethodArgumentValidationAspect.java deleted file mode 100644 index cb4b9909..00000000 --- a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/aspect/MethodArgumentValidationAspect.java +++ /dev/null @@ -1,160 +0,0 @@ -package com.jmsoftware.maf.apiportal.universal.aspect; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.StrUtil; -import lombok.extern.slf4j.Slf4j; -import lombok.val; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.*; -import org.aspectj.lang.reflect.MethodSignature; -import org.springframework.stereotype.Component; - -import javax.validation.ConstraintViolation; -import javax.validation.Valid; -import javax.validation.Validation; -import javax.validation.Validator; -import java.util.LinkedList; -import java.util.Set; - -/** - *

MethodArgumentValidationAspect

- *

This class is an aspect class that validates method's argument(s).

- *

USAGE (Must Do's)

- *
    - *
  1. Annotate the method which argument we need to validate by @ValidateArgument
  2. - *
  3. Annotate the argument(s) that we need to validate by @javax.validation.Valid
  4. - *
  5. The field(s) of the argument(s) could be annotated by the constraint annotation provided by Spring - * Security
  6. - *
- *

ATTENTION

- *

If the argument doesn't pass validation, an IllegalArgumentException will be thrown, and not proceed - * the target method.

- * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-07-06 12:17 - **/ -@Slf4j -@Aspect -@Component -public class MethodArgumentValidationAspect { - private final Validator validator; - - public MethodArgumentValidationAspect() { - val validatorFactory = Validation.buildDefaultValidatorFactory(); - this.validator = validatorFactory.getValidator(); - log.info("The validator for {} has been initiated.", this.getClass().getSimpleName()); - } - - /** - * Define pointcut. Pointcut is a predicate or expression that matches join points. In - * ValidateMethodArgumentAspect, we need to cut any method annotated with `@ValidateArgument` only. - *

- * More detail at: Spring aop aspectJ - * pointcut expression examples - */ - @Pointcut("@annotation(com.jmsoftware.maf.apiportal.universal.aspect.ValidateArgument)") - public void validateMethodArgumentPointcut() { - } - - /** - * Before handle method's argument. This method will be executed after called `proceedingJoinPoint.proceed()`. In - * this phrase, we're going to take some logs. - *

- * `@Before` annotated methods run exactly before the all methods matching with pointcut expression. - * - * @param joinPoint a point of execution of the program - */ - @Before("validateMethodArgumentPointcut()") - public void beforeMethodHandleArgument(JoinPoint joinPoint) { - log.info("Method : {}#{}", - joinPoint.getSignature().getDeclaringTypeName(), - joinPoint.getSignature().getName()); - log.info("Argument : {}", joinPoint.getArgs()); - } - - /** - * Around annotated method processes argument. Around advice can perform custom behavior before and after the - * method invocation. - * - * @param proceedingJoinPoint the object can perform method invocation - * @return any value (may be void) that annotated method returned - */ - @Around("validateMethodArgumentPointcut()") - public Object aroundMethodHandleArgument(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { - log.info("======= METHOD'S ARGUMENT VALIDATION START ======="); - val args = proceedingJoinPoint.getArgs(); - val signature = (MethodSignature) proceedingJoinPoint.getSignature(); - val parameterAnnotations = signature.getMethod().getParameterAnnotations(); - // argumentIndexes is the array list that stores the index of argument we need to validate (the argument - // annotated by `@Valid`) - val argumentIndexListThatNeedsToBeValidated = new LinkedList(); - for (val parameterAnnotation : parameterAnnotations) { - val paramIndex = ArrayUtil.indexOf(parameterAnnotations, parameterAnnotation); - for (val annotation : parameterAnnotation) { - if (annotation instanceof Valid) { - argumentIndexListThatNeedsToBeValidated.add(paramIndex); - } - } - } - val errorMessageList = new LinkedList(); - for (val index : argumentIndexListThatNeedsToBeValidated) { - val constraintViolationSet = validator.validate(args[index]); - if (CollUtil.isNotEmpty(constraintViolationSet)) { - val errorMessage = String.format("Argument validation failed! %s", - getAllFieldErrorMessage(constraintViolationSet)); - errorMessageList.add(errorMessage); - } - } - if (CollUtil.isNotEmpty(errorMessageList)) { - val joinedErrorMessage = StrUtil.join(", ", errorMessageList); - log.info("Method : {}#{}", proceedingJoinPoint.getSignature().getDeclaringTypeName(), - proceedingJoinPoint.getSignature().getName()); - log.info("Argument : {}", args); - log.error("Validation result: {}", joinedErrorMessage); - // If the argument doesn't pass validation, an IllegalArgumentException will be thrown, and not - // proceed the target method - throw new IllegalArgumentException(joinedErrorMessage); - } - log.info("Validation result: Validation passed"); - return proceedingJoinPoint.proceed(); - } - - /** - * `@After` annotated methods run exactly after the all methods matching with pointcut expression. - */ - @After("validateMethodArgumentPointcut()") - public void afterMethodHandleArgument() { - log.info("======== METHOD'S ARGUMENT VALIDATION END ========"); - } - - /** - * `@AfterThrowing` annotated methods run after the method (matching with pointcut expression) exits by throwing an - * exception. - * - * @param joinPoint a point of execution of the program - * @param e exception that controller's method throws - */ - @AfterThrowing(pointcut = "validateMethodArgumentPointcut()", throwing = "e") - public void afterThrowingException(JoinPoint joinPoint, Exception e) { - log.info("Signature : {}", joinPoint.getSignature().toShortString()); - log.error("Exception message: {}", e.toString()); - log.error("== METHOD'S ARGUMENT VALIDATION END WITH EXCEPTION =="); - } - - /** - * Gets all field error message. - * - * @param constraintViolationSet the constraint violation set - * @return the all field error message - */ - private String getAllFieldErrorMessage(Set> constraintViolationSet) { - val allErrorMessageList = new LinkedList(); - for (val constraintViolation : constraintViolationSet) { - allErrorMessageList.add(String.format("invalid field: %s, %s", constraintViolation.getPropertyPath(), - constraintViolation.getMessage())); - } - return StrUtil.join("; ", allErrorMessageList); - } -} diff --git a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/aspect/ValidateArgument.java b/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/aspect/ValidateArgument.java deleted file mode 100644 index 3f84e4c7..00000000 --- a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/aspect/ValidateArgument.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.jmsoftware.maf.apiportal.universal.aspect; - -import java.lang.annotation.*; - -/** - *

ValidateArgument

- *

Annotation for validating method's argument.

- *

ATTENTION

- *

If the argument doesn't pass validation, an IllegalArgumentException will be thrown, and not proceed - * the target method.

- * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-07-06 12:08 - **/ -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) -@Documented -public @interface ValidateArgument { -} diff --git a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/aspect/WebRequestLogAspect.java b/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/aspect/WebRequestLogAspect.java deleted file mode 100644 index 650caa45..00000000 --- a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/aspect/WebRequestLogAspect.java +++ /dev/null @@ -1,130 +0,0 @@ -package com.jmsoftware.maf.apiportal.universal.aspect; - -import cn.hutool.json.JSONUtil; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.jmsoftware.maf.common.util.RequestUtil; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import lombok.val; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.*; -import org.springframework.stereotype.Component; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; - -import java.time.Duration; -import java.time.Instant; - -/** - *

RequestLogAspect

- *

Description:

- *

RequestLogAspect is an AOP for logging URL, HTTP method, client IP and other information when web resource was - * accessed.

- *

Feature:

- *

No methods in controller need to be decorated with annotation. This aspect would automatically cut the method - * decorated with `@GetMapping` or `@PostMapping`.

- * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-05-05 19:55 - **/ -@Slf4j -@Aspect -@Component -@RequiredArgsConstructor -public class WebRequestLogAspect { - private static final String LINE_SEPARATOR = System.lineSeparator(); - private static final int MAX_LENGTH_OF_JSON_STRING = 500; - private final ObjectMapper mapper = new ObjectMapper(); - - /** - * Define pointcut. Pointcut is a predicate or expression that matches join points. In WebRequestLogAspect, we need - * to cut any method annotated with `@GetMapping`, `@PostMapping`, `@PutMapping`, `@DeleteMapping`, - * `@PatchMapping`, `@RequestMapping`. - *

- * More detail at: Spring aop aspectJ - * pointcut expression examples - */ - @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)" + - " || @annotation(org.springframework.web.bind.annotation.PostMapping)" + - " || @annotation(org.springframework.web.bind.annotation.PutMapping)" + - " || @annotation(org.springframework.web.bind.annotation.DeleteMapping)" + - " || @annotation(org.springframework.web.bind.annotation.PatchMapping)" + - " || @annotation(org.springframework.web.bind.annotation.RequestMapping)") - public void requestLogPointcut() { - } - - /** - * Before controller handle client request (on client sent a request). - *

- * `@Before` annotated methods run exactly before the all methods matching with pointcut expression. - * - * @param joinPoint a point of execution of the program - */ - @Before("requestLogPointcut()") - public void beforeHandleRequest(JoinPoint joinPoint) { - val attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); - assert attributes != null; - val request = attributes.getRequest(); - log.info("============ WEB REQUEST LOG START ============"); - log.info("URL : {}", request.getRequestURL().toString()); - log.info("HTTP Method : {}", request.getMethod()); - log.info("Client IP:Port : {}", RequestUtil.getRequestIpAndPort(request)); - log.info("Class Method : {}#{}", - joinPoint.getSignature().getDeclaringTypeName(), - joinPoint.getSignature().getName()); - log.info("Request Params :{}{}", LINE_SEPARATOR, JSONUtil.toJsonPrettyStr(joinPoint.getArgs())); - } - - /** - * Around controller's method processes client request. Around advice can perform custom behavior before and after - * the method invocation. - * - * @param proceedingJoinPoint the object can perform method invocation - * @return any value (may be void) that controller's method returned - * @throws Throwable any exceptions that controller's method may throw - */ - @Around("requestLogPointcut()") - public Object aroundHandleRequest(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { - val startInstant = Instant.now(); - val result = proceedingJoinPoint.proceed(); - val duration = Duration.between(startInstant, Instant.now()); - try { - var formattedJsonString = JSONUtil.formatJsonStr(mapper.writeValueAsString(result)); - if (formattedJsonString.length() > MAX_LENGTH_OF_JSON_STRING) { - val substring = formattedJsonString.substring(0, MAX_LENGTH_OF_JSON_STRING - 1); - formattedJsonString = - String.format("%s… [The length(%d) of JSON string is larger than the maximum(%d)]", substring, - formattedJsonString.length(), MAX_LENGTH_OF_JSON_STRING); - } - log.info("Response :{}{}", LINE_SEPARATOR, formattedJsonString); - } catch (JsonProcessingException e) { - log.info("Response (non-JSON): {}", result); - } - log.info("Elapsed time : {} ({} ms)", duration, duration.toMillis()); - return result; - } - - /** - * `@After` annotated methods run exactly after the all methods matching with pointcut expression. - */ - @After("requestLogPointcut()") - public void afterHandleRequest() { - log.info("============= WEB REQUEST LOG END ============="); - } - - /** - * `@AfterThrowing` annotated methods run after the method (matching with pointcut expression) exits by throwing an - * exception. - * - * @param joinPoint a point of execution of the program - * @param e exception that controller's method throws - */ - @AfterThrowing(pointcut = "requestLogPointcut()", throwing = "e") - public void afterThrowingException(JoinPoint joinPoint, Exception e) { - log.info("Signature : {}", joinPoint.getSignature().toShortString()); - log.error("Exception : {}, message: {}", e.toString(), e.getMessage()); - log.error("====== WEB REQUEST LOG END WITH EXCEPTION ====="); - } -} diff --git a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/configuration/SecurityHandlerConfiguration.java b/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/configuration/SecurityHandlerConfiguration.java index b1497014..4971d1c1 100644 --- a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/configuration/SecurityHandlerConfiguration.java +++ b/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/configuration/SecurityHandlerConfiguration.java @@ -1,7 +1,7 @@ package com.jmsoftware.maf.apiportal.universal.configuration; import com.jmsoftware.maf.common.constant.HttpStatus; -import com.jmsoftware.maf.common.util.ResponseUtil; +import com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.util.ResponseUtil; import lombok.extern.slf4j.Slf4j; import lombok.val; import org.springframework.context.annotation.Bean; diff --git a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/controller/RedirectController.java b/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/controller/RedirectController.java deleted file mode 100644 index a2b27090..00000000 --- a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/controller/RedirectController.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.jmsoftware.maf.apiportal.universal.controller; - -import com.jmsoftware.maf.apiportal.universal.configuration.ProjectProperty; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.PostConstruct; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - *

RedirectController

- *

- * HTTP Redirect Controller - * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 1/21/20 1:18 PM - **/ -@Slf4j -@RestController -@RequiredArgsConstructor -@Api(tags = {"Redirect Controller"}) -public class RedirectController { - private final ProjectProperty projectProperty; - - @PostConstruct - private void postConstruct() { - log.info("URL redirect service initialized."); - } - - @GetMapping("/home") - @ApiOperation(value = "/home", notes = "Home page") - public void handleHomeRequest(HttpServletResponse response) throws IOException { - // Redirect to home page - response.sendRedirect(projectProperty.getContextPath() + "static/home.html"); - } - - @GetMapping("/doc") - @ApiOperation(value = "/doc", notes = "Swagger API Documentation") - public void handleDocRequest(HttpServletResponse response) throws IOException { - // Redirect to Bootstrap Swagger API documentation - response.sendRedirect(projectProperty.getContextPath() + "/doc.html?cache=1&lang=en"); - } - - @GetMapping("/webjars/bycdao-ui/images/api.ico") - @ApiOperation(value = "/webjars/bycdao-ui/images/api.ico", notes = "Favicon redirection") - public void handleFaviconRequest(HttpServletResponse response) throws IOException { - // Redirect to a customized favicon - response.sendRedirect(projectProperty.getContextPath() + "/static/icon/favicon.ico"); - } -} diff --git a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/filter/JwtAuthenticationFilter.java b/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/filter/JwtAuthenticationFilter.java index 7c3e42ee..54718b75 100644 --- a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/filter/JwtAuthenticationFilter.java +++ b/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/filter/JwtAuthenticationFilter.java @@ -8,8 +8,8 @@ import com.jmsoftware.maf.apiportal.universal.service.impl.JwtServiceImpl; import com.jmsoftware.maf.common.constant.HttpStatus; import com.jmsoftware.maf.common.exception.SecurityException; -import com.jmsoftware.maf.common.util.RequestUtil; -import com.jmsoftware.maf.common.util.ResponseUtil; +import com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.util.RequestUtil; +import com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.util.ResponseUtil; import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/service/CommonService.java b/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/service/CommonService.java index 692437cb..3b123344 100644 --- a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/service/CommonService.java +++ b/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/service/CommonService.java @@ -1,7 +1,9 @@ package com.jmsoftware.maf.apiportal.universal.service; import com.jmsoftware.maf.apiportal.universal.domain.ValidationTestPayload; +import org.springframework.validation.annotation.Validated; +import javax.validation.Valid; import java.util.Map; /** @@ -12,6 +14,7 @@ * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com * @date 2/4/20 11:15 AM */ +@Validated public interface CommonService { /** * Gets application info. @@ -25,7 +28,7 @@ public interface CommonService { * * @param payload the payload */ - void validateObject(ValidationTestPayload payload); + void validateObject(@Valid ValidationTestPayload payload); /** * Generate jwt string. diff --git a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/service/SftpService.java b/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/service/SftpService.java index 81af3bcf..273bfc88 100644 --- a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/service/SftpService.java +++ b/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/service/SftpService.java @@ -2,8 +2,10 @@ import com.jmsoftware.maf.apiportal.universal.domain.SftpUploadFile; import org.springframework.integration.file.support.FileExistsMode; +import org.springframework.validation.annotation.Validated; import org.springframework.web.multipart.MultipartFile; +import javax.validation.Valid; import java.io.IOException; import java.io.InputStream; import java.util.List; @@ -15,6 +17,7 @@ * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com * @date 2019-07-04 20:35 **/ +@Validated public interface SftpService { /** * List all files under the full path @@ -47,7 +50,7 @@ public interface SftpService { * @param sftpUploadFile encapsulated object * @return file's full path if successful, else null */ - String upload(SftpUploadFile sftpUploadFile); + String upload(@Valid SftpUploadFile sftpUploadFile); /** * Upload file diff --git a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/service/impl/CommonServiceImpl.java b/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/service/impl/CommonServiceImpl.java index 82bc564b..19b2605b 100644 --- a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/service/impl/CommonServiceImpl.java +++ b/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/service/impl/CommonServiceImpl.java @@ -1,7 +1,6 @@ package com.jmsoftware.maf.apiportal.universal.service.impl; import com.google.common.collect.Lists; -import com.jmsoftware.maf.apiportal.universal.aspect.ValidateArgument; import com.jmsoftware.maf.apiportal.universal.configuration.ProjectProperty; import com.jmsoftware.maf.apiportal.universal.domain.ValidationTestPayload; import com.jmsoftware.maf.apiportal.universal.service.CommonService; @@ -47,7 +46,6 @@ public Map getApplicationInfo() { } @Override - @ValidateArgument public void validateObject(@Valid ValidationTestPayload payload) { log.info("Validation passed! {}", payload); } diff --git a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/service/impl/SftpServiceImpl.java b/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/service/impl/SftpServiceImpl.java index 9aa3e9b4..882cf871 100644 --- a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/service/impl/SftpServiceImpl.java +++ b/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/service/impl/SftpServiceImpl.java @@ -1,12 +1,11 @@ package com.jmsoftware.maf.apiportal.universal.service.impl; import com.jcraft.jsch.ChannelSftp; -import com.jmsoftware.maf.apiportal.universal.aspect.ValidateArgument; import com.jmsoftware.maf.apiportal.universal.configuration.SftpClientConfiguration; import com.jmsoftware.maf.apiportal.universal.configuration.SftpUploadGateway; import com.jmsoftware.maf.apiportal.universal.domain.SftpUploadFile; import com.jmsoftware.maf.apiportal.universal.service.SftpService; -import com.jmsoftware.maf.common.util.FileUtil; +import com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.util.FileUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.BeanFactory; @@ -85,11 +84,11 @@ public Long getFileSize(String fileFullPath) throws IllegalArgumentException { } @Override - @ValidateArgument public String upload(@Valid SftpUploadFile sftpUploadFile) { log.info("Uploading single file to SFTP server. SftpUploadFile: {}", sftpUploadFile); Message message = MessageBuilder.withPayload(sftpUploadFile.getFileToBeUploaded()).build(); - sftpRemoteFileTemplate.setRemoteDirectoryExpression(new LiteralExpression(sftpClientConfiguration.getDirectory())); + sftpRemoteFileTemplate.setRemoteDirectoryExpression( + new LiteralExpression(sftpClientConfiguration.getDirectory())); sftpRemoteFileTemplate.setAutoCreateDirectory(true); sftpRemoteFileTemplate.setBeanFactory(beanFactory); // sftpRemoteFileTemplate.setCharset("UTF-8"); @@ -107,11 +106,12 @@ public String upload(MultipartFile multipartFile, boolean deleteSource) throws IOException { log.info("Uploading single multipart file to SFTP server. File name: {}", multipartFile.getOriginalFilename()); File file = FileUtil.convertFrom(multipartFile); - SftpUploadFile sftpUploadFile = SftpUploadFile.builder() - .fileToBeUploaded(file) - .subDirectory(subDirectory) - .fileExistsMode(fileExistsMode) - .build(); + SftpUploadFile sftpUploadFile = SftpUploadFile + .builder() + .fileToBeUploaded(file) + .subDirectory(subDirectory) + .fileExistsMode(fileExistsMode) + .build(); String fileFullPath = this.upload(sftpUploadFile); if (deleteSource) { file.delete(); diff --git a/auth-center/pom.xml b/auth-center/pom.xml index e38b2017..9427dd9f 100644 --- a/auth-center/pom.xml +++ b/auth-center/pom.xml @@ -106,7 +106,10 @@ com.jmsoftware.maf common - 0.0.1-SNAPSHOT + + + com.jmsoftware.maf + muscle-and-fitness-server-spring-boot-starter diff --git a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/permission/service/PermissionService.java b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/permission/service/PermissionService.java index 23fd85ae..9e8d2c89 100644 --- a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/permission/service/PermissionService.java +++ b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/permission/service/PermissionService.java @@ -6,7 +6,9 @@ import com.jmsoftware.maf.common.domain.authcenter.permission.GetPermissionListByUserIdPayload; import com.jmsoftware.maf.common.domain.authcenter.permission.GetPermissionListByUserIdResponse; import lombok.NonNull; +import org.springframework.validation.annotation.Validated; +import javax.validation.Valid; import java.util.List; /** @@ -17,6 +19,7 @@ * @author Johnny Miller (鍾俊), e-mail: johnnysviva@outlook.com * @date 5 /11/20 8:34 AM */ +@Validated public interface PermissionService { /** * Query by ID @@ -65,7 +68,7 @@ public interface PermissionService { * @param payload the payload * @return the permission list by role id list */ - GetPermissionListByRoleIdListResponse getPermissionListByRoleIdList(GetPermissionListByRoleIdListPayload payload); + GetPermissionListByRoleIdListResponse getPermissionListByRoleIdList(@Valid GetPermissionListByRoleIdListPayload payload); /** * Gets permission list by role id list. @@ -81,7 +84,7 @@ public interface PermissionService { * @param payload the payload * @return the permission list by user id */ - GetPermissionListByUserIdResponse getPermissionListByUserId(GetPermissionListByUserIdPayload payload); + GetPermissionListByUserIdResponse getPermissionListByUserId(@Valid GetPermissionListByUserIdPayload payload); /** * Gets permission list by user id. diff --git a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/permission/service/impl/PermissionServiceImpl.java b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/permission/service/impl/PermissionServiceImpl.java index 824b804a..16d4d206 100644 --- a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/permission/service/impl/PermissionServiceImpl.java +++ b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/permission/service/impl/PermissionServiceImpl.java @@ -5,7 +5,6 @@ import com.jmsoftware.maf.authcenter.permission.entity.PermissionPersistence; import com.jmsoftware.maf.authcenter.permission.mapper.PermissionMapper; import com.jmsoftware.maf.authcenter.permission.service.PermissionService; -import com.jmsoftware.maf.authcenter.universal.aspect.ValidateArgument; import com.jmsoftware.maf.common.domain.authcenter.permission.GetPermissionListByRoleIdListPayload; import com.jmsoftware.maf.common.domain.authcenter.permission.GetPermissionListByRoleIdListResponse; import com.jmsoftware.maf.common.domain.authcenter.permission.GetPermissionListByUserIdPayload; @@ -60,7 +59,6 @@ public boolean deleteById(Long id) { } @Override - @ValidateArgument public GetPermissionListByRoleIdListResponse getPermissionListByRoleIdList(@Valid GetPermissionListByRoleIdListPayload payload) { val permissionList = this.getPermissionListByRoleIdList(payload.getRoleIdList()); val response = new GetPermissionListByRoleIdListResponse(); @@ -82,7 +80,6 @@ public List getPermissionListByRoleIdList(@NonNull ListExceptionControllerAdvice - *

- * Exception advice for global controllers. - * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-03-02 17:39 - **/ -@Slf4j -@ControllerAdvice -public class ExceptionControllerAdvice { - /** - *

Exception handler.

- *

ATTENTION: In this method, cannot throw any exception.

- * - * @param request HTTP request - * @param exception any kinds of exception occurred in controller - * @return custom exception info - */ - @ResponseBody - @ExceptionHandler(value = Exception.class) - public ResponseBodyBean handleException(HttpServletRequest request, - HttpServletResponse response, - Exception exception) { - log.error("Exception occurred when [{}] requested access. URL: {}", - RequestUtil.getRequestIpAndPort(request), - request.getServletPath()); - - // FIXME: THIS IS NOT A PROBLEM - // ATTENTION: Use only ResponseBodyBean.ofStatus() in handleException() method and DON'T throw any exception - if (exception instanceof NoHandlerFoundException) { - log.error("NoHandlerFoundException: Request URL = {}, HTTP method = {}", - ((NoHandlerFoundException) exception).getRequestURL(), - ((NoHandlerFoundException) exception).getHttpMethod()); - response.setStatus(HttpStatus.NOT_FOUND.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.NOT_FOUND); - } else if (exception instanceof HttpRequestMethodNotSupportedException) { - log.error("Exception occurred when the request handler does not support a specific request method. " + - "Current method is {}, Support HTTP method = {}", - ((HttpRequestMethodNotSupportedException) exception).getMethod(), - ((HttpRequestMethodNotSupportedException) exception).getSupportedHttpMethods()); - response.setStatus(HttpStatus.METHOD_NOT_ALLOWED.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.METHOD_NOT_ALLOWED); - } else if (exception instanceof MethodArgumentNotValidException) { - log.error("Exception occurred when validation on an argument annotated with fails. Exception message: {}", - exception.getMessage()); - response.setStatus(HttpStatus.BAD_REQUEST.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.BAD_REQUEST.getCode(), - getFieldErrorMessageFromException((MethodArgumentNotValidException) exception), - null); - } else if (exception instanceof ConstraintViolationException) { - log.error("Constraint violations exception occurred. Exception message: {}", exception.getMessage()); - response.setStatus(HttpStatus.BAD_REQUEST.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.BAD_REQUEST.getCode(), - CollUtil.getFirst(((ConstraintViolationException) exception).getConstraintViolations()).getMessage(), - null); - } else if (exception instanceof MethodArgumentTypeMismatchException) { - log.error("MethodArgumentTypeMismatchException: Parameter name = {}, Exception message: {}", - ((MethodArgumentTypeMismatchException) exception).getName(), - ((MethodArgumentTypeMismatchException) exception).getMessage()); - response.setStatus(HttpStatus.PARAM_NOT_MATCH.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.PARAM_NOT_MATCH); - } else if (exception instanceof HttpMessageNotReadableException) { - log.error("HttpMessageNotReadableException: {}", - ((HttpMessageNotReadableException) exception).getMessage()); - response.setStatus(HttpStatus.PARAM_NOT_NULL.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.PARAM_NOT_NULL); - } else if (exception instanceof BaseException) { - log.error("BaseException: Status code: {}, message: {}, data: {}", ((BaseException) exception).getCode(), - exception.getMessage(), ((BaseException) exception).getData()); - response.setStatus(((BaseException) exception).getCode()); - return ResponseBodyBean.ofStatus(((BaseException) exception).getCode(), exception.getMessage(), - ((BaseException) exception).getData()); - } else if (exception instanceof BindException) { - log.error("Exception message: {} ", exception.getMessage()); - response.setStatus(HttpStatus.INVALID_PARAM.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.INVALID_PARAM); - } else if (exception instanceof IllegalArgumentException) { - log.error("Exception message: {} ", exception.getMessage()); - response.setStatus(HttpStatus.BAD_REQUEST.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.BAD_REQUEST.getCode(), exception.getMessage(), null); - } else if (exception instanceof BadCredentialsException) { - // IMPORTANT: org.springframework.security.authentication.BadCredentialsException only exists in the project - // that depends on org.springframework.boot.spring-boot-starter-security - log.error("Exception message: {} ", exception.getMessage()); - response.setStatus(HttpStatus.BAD_CREDENTIALS.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.BAD_CREDENTIALS.getCode(), exception.getMessage(), null); - } else if (exception instanceof InternalAuthenticationServiceException) { - log.error("An authentication request could not be processed due to a system problem that occurred " + - "internally. Exception message: {} ", exception.getMessage()); - if (exception.getCause() instanceof BaseException) { - val exceptionCause = (BaseException) exception.getCause(); - val code = exceptionCause.getCode(); - response.setStatus(code); - return ResponseBodyBean.ofStatus(HttpStatus.fromCode(code)); - } - response.setStatus(HttpStatus.BAD_CREDENTIALS.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.BAD_CREDENTIALS.getCode(), exception.getMessage(), null); - } - log.error("Internal system exception occurred! Exception message: {} ", exception.getMessage(), exception); - response.setStatus(HttpStatus.ERROR.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.ERROR.getCode(), HttpStatus.ERROR.getMessage(), null); - } - - /** - * Get field error message from exception. If two or more fields do not pass Spring Validation check, then will - * return the 1st error message of the error field. - * - * @param exception MethodArgumentNotValidException - * @return field error message - */ - private String getFieldErrorMessageFromException(MethodArgumentNotValidException exception) { - try { - val firstErrorField = - (DefaultMessageSourceResolvable) Objects.requireNonNull(exception.getBindingResult() - .getAllErrors() - .get(0) - .getArguments())[0]; - val firstErrorFieldName = firstErrorField.getDefaultMessage(); - val firstErrorFieldMessage = exception.getBindingResult().getAllErrors().get(0).getDefaultMessage(); - return String.format("%s %s", firstErrorFieldName, firstErrorFieldMessage); - } catch (Exception e) { - log.error("Exception occurred when get field error message from exception. Exception message: {}", - e.getMessage(), e); - return HttpStatus.INVALID_PARAM.getMessage(); - } - } -} diff --git a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/aspect/MethodArgumentValidationAspect.java b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/aspect/MethodArgumentValidationAspect.java deleted file mode 100644 index 8c7d6495..00000000 --- a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/aspect/MethodArgumentValidationAspect.java +++ /dev/null @@ -1,160 +0,0 @@ -package com.jmsoftware.maf.authcenter.universal.aspect; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.StrUtil; -import lombok.extern.slf4j.Slf4j; -import lombok.val; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.*; -import org.aspectj.lang.reflect.MethodSignature; -import org.springframework.stereotype.Component; - -import javax.validation.ConstraintViolation; -import javax.validation.Valid; -import javax.validation.Validation; -import javax.validation.Validator; -import java.util.LinkedList; -import java.util.Set; - -/** - *

MethodArgumentValidationAspect

- *

This class is an aspect class that validates method's argument(s).

- *

USAGE (Must Do's)

- *
    - *
  1. Annotate the method which argument we need to validate by @ValidateArgument
  2. - *
  3. Annotate the argument(s) that we need to validate by @javax.validation.Valid
  4. - *
  5. The field(s) of the argument(s) could be annotated by the constraint annotation provided by Spring - * Security
  6. - *
- *

ATTENTION

- *

If the argument doesn't pass validation, an IllegalArgumentException will be thrown, and not proceed - * the target method.

- * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-07-06 12:17 - **/ -@Slf4j -@Aspect -@Component -public class MethodArgumentValidationAspect { - private final Validator validator; - - public MethodArgumentValidationAspect() { - val validatorFactory = Validation.buildDefaultValidatorFactory(); - this.validator = validatorFactory.getValidator(); - log.info("The validator for {} has been initiated.", this.getClass().getSimpleName()); - } - - /** - * Define pointcut. Pointcut is a predicate or expression that matches join points. In - * ValidateMethodArgumentAspect, we need to cut any method annotated with `@ValidateArgument` only. - *

- * More detail at: Spring aop aspectJ - * pointcut expression examples - */ - @Pointcut("@annotation(com.jmsoftware.maf.authcenter.universal.aspect.ValidateArgument)") - public void validateMethodArgumentPointcut() { - } - - /** - * Before handle method's argument. This method will be executed after called `proceedingJoinPoint.proceed()`. In - * this phrase, we're going to take some logs. - *

- * `@Before` annotated methods run exactly before the all methods matching with pointcut expression. - * - * @param joinPoint a point of execution of the program - */ - @Before("validateMethodArgumentPointcut()") - public void beforeMethodHandleArgument(JoinPoint joinPoint) { - log.info("Method : {}#{}", - joinPoint.getSignature().getDeclaringTypeName(), - joinPoint.getSignature().getName()); - log.info("Argument : {}", joinPoint.getArgs()); - } - - /** - * Around annotated method processes argument. Around advice can perform custom behavior before and after the - * method invocation. - * - * @param proceedingJoinPoint the object can perform method invocation - * @return any value (may be void) that annotated method returned - */ - @Around("validateMethodArgumentPointcut()") - public Object aroundMethodHandleArgument(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { - log.info("======= METHOD'S ARGUMENT VALIDATION START ======="); - val args = proceedingJoinPoint.getArgs(); - val signature = (MethodSignature) proceedingJoinPoint.getSignature(); - val parameterAnnotations = signature.getMethod().getParameterAnnotations(); - // argumentIndexes is the array list that stores the index of argument we need to validate (the argument - // annotated by `@Valid`) - val argumentIndexListThatNeedsToBeValidated = new LinkedList(); - for (val parameterAnnotation : parameterAnnotations) { - val paramIndex = ArrayUtil.indexOf(parameterAnnotations, parameterAnnotation); - for (val annotation : parameterAnnotation) { - if (annotation instanceof Valid) { - argumentIndexListThatNeedsToBeValidated.add(paramIndex); - } - } - } - val errorMessageList = new LinkedList(); - for (val index : argumentIndexListThatNeedsToBeValidated) { - val constraintViolationSet = validator.validate(args[index]); - if (CollUtil.isNotEmpty(constraintViolationSet)) { - val errorMessage = String.format("Argument validation failed! %s", - getAllFieldErrorMessage(constraintViolationSet)); - errorMessageList.add(errorMessage); - } - } - if (CollUtil.isNotEmpty(errorMessageList)) { - val joinedErrorMessage = StrUtil.join(", ", errorMessageList); - log.info("Method : {}#{}", proceedingJoinPoint.getSignature().getDeclaringTypeName(), - proceedingJoinPoint.getSignature().getName()); - log.info("Argument : {}", args); - log.error("Validation result: {}", joinedErrorMessage); - // If the argument doesn't pass validation, an IllegalArgumentException will be thrown, and not - // proceed the target method - throw new IllegalArgumentException(joinedErrorMessage); - } - log.info("Validation result: Validation passed"); - return proceedingJoinPoint.proceed(); - } - - /** - * `@After` annotated methods run exactly after the all methods matching with pointcut expression. - */ - @After("validateMethodArgumentPointcut()") - public void afterMethodHandleArgument() { - log.info("======== METHOD'S ARGUMENT VALIDATION END ========"); - } - - /** - * `@AfterThrowing` annotated methods run after the method (matching with pointcut expression) exits by throwing an - * exception. - * - * @param joinPoint a point of execution of the program - * @param e exception that controller's method throws - */ - @AfterThrowing(pointcut = "validateMethodArgumentPointcut()", throwing = "e") - public void afterThrowingException(JoinPoint joinPoint, Exception e) { - log.info("Signature : {}", joinPoint.getSignature().toShortString()); - log.error("Exception message: {}", e.toString()); - log.error("== METHOD'S ARGUMENT VALIDATION END WITH EXCEPTION =="); - } - - /** - * Gets all field error message. - * - * @param constraintViolationSet the constraint violation set - * @return the all field error message - */ - private String getAllFieldErrorMessage(Set> constraintViolationSet) { - val allErrorMessageList = new LinkedList(); - for (val constraintViolation : constraintViolationSet) { - allErrorMessageList.add(String.format("invalid field: %s, %s", constraintViolation.getPropertyPath(), - constraintViolation.getMessage())); - } - return StrUtil.join("; ", allErrorMessageList); - } -} diff --git a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/aspect/ValidateArgument.java b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/aspect/ValidateArgument.java deleted file mode 100644 index c760a862..00000000 --- a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/aspect/ValidateArgument.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.jmsoftware.maf.authcenter.universal.aspect; - -import java.lang.annotation.*; - -/** - *

ValidateArgument

- *

Annotation for validating method's argument.

- *

ATTENTION

- *

If the argument doesn't pass validation, an IllegalArgumentException will be thrown, and not proceed - * the target method.

- * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-07-06 12:08 - **/ -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) -@Documented -public @interface ValidateArgument { -} diff --git a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/aspect/WebRequestLogAspect.java b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/aspect/WebRequestLogAspect.java deleted file mode 100644 index 3a2d6707..00000000 --- a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/aspect/WebRequestLogAspect.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.jmsoftware.maf.authcenter.universal.aspect; - -import cn.hutool.json.JSONUtil; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.jmsoftware.maf.common.util.RequestUtil; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import lombok.val; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.*; -import org.springframework.stereotype.Component; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; - -import java.time.Duration; -import java.time.Instant; - -/** - *

RequestLogAspect

- *

Description:

- *

RequestLogAspect is an AOP for logging URL, HTTP method, client IP and other information when web resource was - * accessed.

- *

Feature:

- *

No methods in controller need to be decorated with annotation. This aspect would automatically cut the method - * decorated with `@GetMapping` or `@PostMapping`.

- * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-05-05 19:55 - **/ -@Slf4j -@Aspect -@Component -@RequiredArgsConstructor -public class WebRequestLogAspect { - private static final int MAX_LENGTH_OF_JSON_STRING = 500; - private static final String LINE_SEPARATOR = System.lineSeparator(); - private final ObjectMapper mapper = new ObjectMapper(); - - /** - * Define pointcut. Pointcut is a predicate or expression that matches join points. In WebRequestLogAspect, we need - * to cut any method annotated with `@GetMapping`, `@PostMapping`, `@PutMapping`, `@DeleteMapping`, `@PatchMapping`, `@RequestMapping`. - *

- * More detail at: Spring aop aspectJ - * pointcut expression examples - */ - @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)" + - " || @annotation(org.springframework.web.bind.annotation.PostMapping)" + - " || @annotation(org.springframework.web.bind.annotation.PutMapping)" + - " || @annotation(org.springframework.web.bind.annotation.DeleteMapping)" + - " || @annotation(org.springframework.web.bind.annotation.PatchMapping)" + - " || @annotation(org.springframework.web.bind.annotation.RequestMapping)") - public void requestLogPointcut() { - } - - /** - * Before controller handle client request (on client sent a request). - *

- * `@Before` annotated methods run exactly before the all methods matching with pointcut expression. - * - * @param joinPoint a point of execution of the program - */ - @Before("requestLogPointcut()") - public void beforeHandleRequest(JoinPoint joinPoint) { - val attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); - assert attributes != null; - val request = attributes.getRequest(); - log.info("============ WEB REQUEST LOG START ============"); - log.info("URL : {}", request.getRequestURL().toString()); - log.info("HTTP Method : {}", request.getMethod()); - log.info("Client IP:Port : {}", RequestUtil.getRequestIpAndPort(request)); - log.info("Class Method : {}#{}", - joinPoint.getSignature().getDeclaringTypeName(), - joinPoint.getSignature().getName()); - log.info("Request Params :{}{}", LINE_SEPARATOR, JSONUtil.toJsonPrettyStr(joinPoint.getArgs())); - } - - /** - * Around controller's method processes client request. Around advice can perform custom behavior before and after - * the method invocation. - * - * @param proceedingJoinPoint the object can perform method invocation - * @return any value (may be void) that controller's method returned - * @throws Throwable any exceptions that controller's method may throw - */ - @Around("requestLogPointcut()") - public Object aroundHandleRequest(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { - val startInstant = Instant.now(); - val result = proceedingJoinPoint.proceed(); - val duration = Duration.between(startInstant, Instant.now()); - try { - var formattedJsonString = JSONUtil.formatJsonStr(mapper.writeValueAsString(result)); - if (formattedJsonString.length() > MAX_LENGTH_OF_JSON_STRING) { - val substring = formattedJsonString.substring(0, MAX_LENGTH_OF_JSON_STRING - 1); - formattedJsonString = - String.format("%s… [The length(%d) of JSON string is larger than the maximum(%d)]", substring, - formattedJsonString.length(), MAX_LENGTH_OF_JSON_STRING); - } - log.info("Response :{}{}", LINE_SEPARATOR, formattedJsonString); - } catch (JsonProcessingException e) { - log.info("Response (non-JSON): {}", result); - } - log.info("Elapsed time : {} ({} ms)", duration, duration.toMillis()); - return result; - } - - /** - * `@After` annotated methods run exactly after the all methods matching with pointcut expression. - */ - @After("requestLogPointcut()") - public void afterHandleRequest() { - log.info("============= WEB REQUEST LOG END ============="); - } - - /** - * `@AfterThrowing` annotated methods run after the method (matching with pointcut expression) exits by throwing an - * exception. - * - * @param joinPoint a point of execution of the program - * @param e exception that controller's method throws - */ - @AfterThrowing(pointcut = "requestLogPointcut()", throwing = "e") - public void afterThrowingException(JoinPoint joinPoint, Exception e) { - log.info("Signature : {}", joinPoint.getSignature().toShortString()); - log.error("Exception : {}, message: {}", e.toString(), e.getMessage()); - log.error("====== WEB REQUEST LOG END WITH EXCEPTION ====="); - } -} diff --git a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/configuration/WebMvcConfiguration.java b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/configuration/WebMvcConfiguration.java deleted file mode 100644 index f7176e8f..00000000 --- a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/configuration/WebMvcConfiguration.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.jmsoftware.maf.authcenter.universal.configuration; - -import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.CorsRegistry; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -/** - *

WebMvcConfiguration

- *

- * Spring MVC Configurations. - * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 1/23/20 9:02 AM - **/ -@Configuration -@RequiredArgsConstructor -public class WebMvcConfiguration implements WebMvcConfigurer { - private static final long MAX_AGE_SECS = 3600; - - /** - * 1. Config static path pattern - * 2. Config static resource location - * - * @param registry static resources register - */ - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); - } - - /** - * Configure cross origin requests processing. - * - * @param registry CORS registry - */ - @Override - public void addCorsMappings(CorsRegistry registry) { - registry.addMapping("/**") - .allowedOrigins("*") - .allowedMethods("HEAD", "OPTIONS", "GET", "POST", "PUT", "PATCH", "DELETE") - .maxAge(MAX_AGE_SECS); - } -} diff --git a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/controller/CommonController.java b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/controller/CommonController.java index abf19f17..a709eace 100644 --- a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/controller/CommonController.java +++ b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/controller/CommonController.java @@ -1,9 +1,9 @@ package com.jmsoftware.maf.authcenter.universal.controller; -import com.jmsoftware.maf.authcenter.universal.domain.ValidationTestPayload; import com.jmsoftware.maf.authcenter.universal.service.CommonService; import com.jmsoftware.maf.authcenter.universal.service.RedisService; import com.jmsoftware.maf.common.bean.ResponseBodyBean; +import com.jmsoftware.maf.common.domain.ValidationTestPayload; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; diff --git a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/controller/RedirectController.java b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/controller/RedirectController.java deleted file mode 100644 index fb676228..00000000 --- a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/controller/RedirectController.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.jmsoftware.maf.authcenter.universal.controller; - -import com.jmsoftware.maf.authcenter.universal.configuration.ProjectProperty; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.PostConstruct; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - *

RedirectController

- *

- * HTTP Redirect Controller - * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 1/21/20 1:18 PM - **/ -@Slf4j -@RestController -@RequiredArgsConstructor -@Api(tags = {"Redirect Controller"}) -public class RedirectController { - private final ProjectProperty projectProperty; - - @PostConstruct - private void postConstruct() { - log.info("URL redirect service initialized."); - } - - @GetMapping("/home") - @ApiOperation(value = "/home", notes = "Home page") - public void handleHomeRequest(HttpServletResponse response) throws IOException { - // Redirect to home page - response.sendRedirect(projectProperty.getContextPath() + "static/home.html"); - } - - @GetMapping("/doc") - @ApiOperation(value = "/doc", notes = "Swagger API Documentation") - public void handleDocRequest(HttpServletResponse response) throws IOException { - // Redirect to Bootstrap Swagger API documentation - response.sendRedirect(projectProperty.getContextPath() + "/doc.html?cache=1&lang=en"); - } - - @GetMapping("/webjars/bycdao-ui/images/api.ico") - @ApiOperation(value = "/webjars/bycdao-ui/images/api.ico", notes = "Favicon redirection") - public void handleFaviconRequest(HttpServletResponse response) throws IOException { - // Redirect to a customized favicon - response.sendRedirect(projectProperty.getContextPath() + "/static/icon/favicon.ico"); - } -} diff --git a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/filter/RequestFilter.java b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/filter/RequestFilter.java deleted file mode 100644 index f43d1db8..00000000 --- a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/filter/RequestFilter.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.jmsoftware.maf.authcenter.universal.filter; - -import com.jmsoftware.maf.common.util.RequestUtil; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - *

RequestFilter

- *

Request filter.

- * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-03-23 14:24 - **/ -@Slf4j -@Component -public class RequestFilter extends OncePerRequestFilter { - @Override - @SuppressWarnings("NullableProblems") - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, - FilterChain filterChain) throws IOException, ServletException { - log.info("The requester({}) requested resource. Resource: [{}] {}", RequestUtil.getRequestIpAndPort(request), - request.getMethod(), request.getRequestURL()); - filterChain.doFilter(request, response); - } -} diff --git a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/service/CommonService.java b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/service/CommonService.java index e1b3bf69..9dc7a7ca 100644 --- a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/service/CommonService.java +++ b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/service/CommonService.java @@ -1,7 +1,9 @@ package com.jmsoftware.maf.authcenter.universal.service; -import com.jmsoftware.maf.authcenter.universal.domain.ValidationTestPayload; +import com.jmsoftware.maf.common.domain.ValidationTestPayload; +import org.springframework.validation.annotation.Validated; +import javax.validation.Valid; import java.util.Map; /** @@ -12,6 +14,7 @@ * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com * @date 2/4/20 11:15 AM */ +@Validated public interface CommonService { /** * Gets application info. @@ -25,5 +28,5 @@ public interface CommonService { * * @param payload the payload */ - void validateObject(ValidationTestPayload payload); + void validateObject(@Valid ValidationTestPayload payload); } diff --git a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/service/impl/CommonServiceImpl.java b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/service/impl/CommonServiceImpl.java index 8385c4f9..944aab7c 100644 --- a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/service/impl/CommonServiceImpl.java +++ b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/service/impl/CommonServiceImpl.java @@ -1,9 +1,8 @@ package com.jmsoftware.maf.authcenter.universal.service.impl; -import com.jmsoftware.maf.authcenter.universal.aspect.ValidateArgument; import com.jmsoftware.maf.authcenter.universal.configuration.ProjectProperty; -import com.jmsoftware.maf.authcenter.universal.domain.ValidationTestPayload; import com.jmsoftware.maf.authcenter.universal.service.CommonService; +import com.jmsoftware.maf.common.domain.ValidationTestPayload; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -44,7 +43,6 @@ public Map getApplicationInfo() { } @Override - @ValidateArgument public void validateObject(@Valid ValidationTestPayload payload) { log.info("Validation passed! {}", payload); } diff --git a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/service/UserService.java b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/service/UserService.java index ca3785d8..1296162a 100644 --- a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/service/UserService.java +++ b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/service/UserService.java @@ -4,7 +4,9 @@ import com.jmsoftware.maf.common.domain.authcenter.user.GetUserByLoginTokenResponse; import com.jmsoftware.maf.common.domain.authcenter.user.SaveUserForRegisteringPayload; import com.jmsoftware.maf.common.domain.authcenter.user.SaveUserForRegisteringResponse; +import org.springframework.validation.annotation.Validated; +import javax.validation.Valid; import java.util.List; /** @@ -15,6 +17,7 @@ * @author Johnny Miller (鍾俊), e-mail: johnnysviva@outlook.com * @date 5 /10/20 12:31 PM */ +@Validated public interface UserService { /** * Query by id user persistence. @@ -71,5 +74,5 @@ public interface UserService { * @param payload the payload * @return the save user for registering response */ - SaveUserForRegisteringResponse saveUserForRegistering(SaveUserForRegisteringPayload payload); + SaveUserForRegisteringResponse saveUserForRegistering(@Valid SaveUserForRegisteringPayload payload); } diff --git a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/service/impl/UserServiceImpl.java b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/service/impl/UserServiceImpl.java index 69d8b2e5..6718f3ab 100644 --- a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/service/impl/UserServiceImpl.java +++ b/auth-center/src/main/java/com/jmsoftware/maf/authcenter/user/service/impl/UserServiceImpl.java @@ -3,7 +3,6 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; -import com.jmsoftware.maf.authcenter.universal.aspect.ValidateArgument; import com.jmsoftware.maf.authcenter.user.entity.UserPersistence; import com.jmsoftware.maf.authcenter.user.mapper.UserMapper; import com.jmsoftware.maf.authcenter.user.service.UserService; @@ -77,7 +76,6 @@ public GetUserByLoginTokenResponse getUserByLoginToken(String loginToken) { @Override @SneakyThrows - @ValidateArgument public SaveUserForRegisteringResponse saveUserForRegistering(@Valid SaveUserForRegisteringPayload payload) { val userPersistence = new UserPersistence(); userPersistence.setUsername(payload.getUsername()); diff --git a/common/pom.xml b/common/pom.xml index 31a9a7f6..dd2dbb24 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -8,9 +8,6 @@ common Muscle and Fitness Server :: Common Common Java class collection for JM Software. - - 11 - com.jmsoftware.maf muscle-and-fitness-server diff --git a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/domain/ValidationTestPayload.java b/common/src/main/java/com/jmsoftware/maf/common/domain/ValidationTestPayload.java similarity index 89% rename from auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/domain/ValidationTestPayload.java rename to common/src/main/java/com/jmsoftware/maf/common/domain/ValidationTestPayload.java index 9bfca795..9e1f8035 100644 --- a/auth-center/src/main/java/com/jmsoftware/maf/authcenter/universal/domain/ValidationTestPayload.java +++ b/common/src/main/java/com/jmsoftware/maf/common/domain/ValidationTestPayload.java @@ -1,4 +1,4 @@ -package com.jmsoftware.maf.authcenter.universal.domain; +package com.jmsoftware.maf.common.domain; import lombok.Data; diff --git a/exercise-mis/pom.xml b/exercise-mis/pom.xml index cc98f9d4..64a5a9fd 100644 --- a/exercise-mis/pom.xml +++ b/exercise-mis/pom.xml @@ -98,7 +98,10 @@ com.jmsoftware.maf common - 0.0.1-SNAPSHOT + + + com.jmsoftware.maf + muscle-and-fitness-server-spring-boot-starter diff --git a/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/aspect/ExceptionControllerAdvice.java b/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/aspect/ExceptionControllerAdvice.java deleted file mode 100644 index e064491d..00000000 --- a/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/aspect/ExceptionControllerAdvice.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.jmsoftware.maf.exercisemis.universal.aspect; - -import cn.hutool.core.collection.CollUtil; -import com.jmsoftware.maf.common.bean.ResponseBodyBean; -import com.jmsoftware.maf.common.constant.HttpStatus; -import com.jmsoftware.maf.common.exception.BaseException; -import com.jmsoftware.maf.common.util.RequestUtil; -import lombok.extern.slf4j.Slf4j; -import lombok.val; -import org.springframework.context.support.DefaultMessageSourceResolvable; -import org.springframework.http.converter.HttpMessageNotReadableException; -import org.springframework.validation.BindException; -import org.springframework.web.HttpRequestMethodNotSupportedException; -import org.springframework.web.bind.MethodArgumentNotValidException; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; -import org.springframework.web.servlet.NoHandlerFoundException; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.validation.ConstraintViolationException; -import java.util.Objects; - -/** - *

ExceptionControllerAdvice

- *

- * Exception advice for global controllers. - * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-03-02 17:39 - **/ -@Slf4j -@ControllerAdvice -public class ExceptionControllerAdvice { - /** - *

Exception handler.

- *

ATTENTION: In this method, cannot throw any exception.

- * - * @param request HTTP request - * @param exception any kinds of exception occurred in controller - * @return custom exception info - */ - @ResponseBody - @ExceptionHandler(value = Exception.class) - public ResponseBodyBean handleException(HttpServletRequest request, - HttpServletResponse response, - Exception exception) { - log.error("Exception occurred when [{}] requested access. URL: {}", - RequestUtil.getRequestIpAndPort(request), - request.getServletPath()); - - // FIXME: THIS IS NOT A PROBLEM - // ATTENTION: Use only ResponseBodyBean.ofStatus() in handleException() method and DON'T throw any exception - if (exception instanceof NoHandlerFoundException) { - log.error("NoHandlerFoundException: Request URL = {}, HTTP method = {}", - ((NoHandlerFoundException) exception).getRequestURL(), - ((NoHandlerFoundException) exception).getHttpMethod()); - response.setStatus(HttpStatus.NOT_FOUND.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.NOT_FOUND); - } else if (exception instanceof HttpRequestMethodNotSupportedException) { - log.error("Exception occurred when the request handler does not support a specific request method. " + - "Current method is {}, Support HTTP method = {}", - ((HttpRequestMethodNotSupportedException) exception).getMethod(), - ((HttpRequestMethodNotSupportedException) exception).getSupportedHttpMethods()); - response.setStatus(HttpStatus.METHOD_NOT_ALLOWED.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.METHOD_NOT_ALLOWED); - } else if (exception instanceof MethodArgumentNotValidException) { - log.error("Exception occurred when validation on an argument annotated with fails. Exception message: {}", - exception.getMessage()); - response.setStatus(HttpStatus.BAD_REQUEST.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.BAD_REQUEST.getCode(), - getFieldErrorMessageFromException((MethodArgumentNotValidException) exception), - null); - } else if (exception instanceof ConstraintViolationException) { - log.error("Constraint violations exception occurred. Exception message: {}", exception.getMessage()); - response.setStatus(HttpStatus.BAD_REQUEST.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.BAD_REQUEST.getCode(), - CollUtil.getFirst(((ConstraintViolationException) exception).getConstraintViolations()).getMessage(), - null); - } else if (exception instanceof MethodArgumentTypeMismatchException) { - log.error("MethodArgumentTypeMismatchException: Parameter name = {}, Exception message: {}", - ((MethodArgumentTypeMismatchException) exception).getName(), - ((MethodArgumentTypeMismatchException) exception).getMessage()); - response.setStatus(HttpStatus.PARAM_NOT_MATCH.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.PARAM_NOT_MATCH); - } else if (exception instanceof HttpMessageNotReadableException) { - log.error("HttpMessageNotReadableException: {}", - ((HttpMessageNotReadableException) exception).getMessage()); - response.setStatus(HttpStatus.PARAM_NOT_NULL.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.PARAM_NOT_NULL); - } else if (exception instanceof BaseException) { - log.error("BaseException: Status code: {}, message: {}, data: {}", ((BaseException) exception).getCode(), - exception.getMessage(), ((BaseException) exception).getData()); - response.setStatus(((BaseException) exception).getCode()); - return ResponseBodyBean.ofStatus(((BaseException) exception).getCode(), exception.getMessage(), - ((BaseException) exception).getData()); - } else if (exception instanceof BindException) { - log.error("Exception message: {} ", exception.getMessage()); - response.setStatus(HttpStatus.INVALID_PARAM.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.INVALID_PARAM); - } else if (exception instanceof IllegalArgumentException) { - log.error("Exception message: {} ", exception.getMessage()); - response.setStatus(HttpStatus.BAD_REQUEST.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.BAD_REQUEST.getCode(), exception.getMessage(), null); - } - log.error("Internal system exception occurred! Exception message: {} ", exception.getMessage(), exception); - response.setStatus(HttpStatus.ERROR.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.ERROR.getCode(), HttpStatus.ERROR.getMessage(), null); - } - - /** - * Get field error message from exception. If two or more fields do not pass Spring Validation check, then will - * return the 1st error message of the error field. - * - * @param exception MethodArgumentNotValidException - * @return field error message - */ - private String getFieldErrorMessageFromException(MethodArgumentNotValidException exception) { - try { - val firstErrorField = - (DefaultMessageSourceResolvable) Objects.requireNonNull(exception.getBindingResult() - .getAllErrors() - .get(0) - .getArguments())[0]; - val firstErrorFieldName = firstErrorField.getDefaultMessage(); - val firstErrorFieldMessage = exception.getBindingResult().getAllErrors().get(0).getDefaultMessage(); - return String.format("%s %s", firstErrorFieldName, firstErrorFieldMessage); - } catch (Exception e) { - log.error("Exception occurred when get field error message from exception. Exception message: {}", - e.getMessage(), e); - return HttpStatus.INVALID_PARAM.getMessage(); - } - } -} diff --git a/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/aspect/MethodArgumentValidationAspect.java b/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/aspect/MethodArgumentValidationAspect.java deleted file mode 100644 index f74d5d62..00000000 --- a/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/aspect/MethodArgumentValidationAspect.java +++ /dev/null @@ -1,160 +0,0 @@ -package com.jmsoftware.maf.exercisemis.universal.aspect; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.StrUtil; -import lombok.extern.slf4j.Slf4j; -import lombok.val; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.*; -import org.aspectj.lang.reflect.MethodSignature; -import org.springframework.stereotype.Component; - -import javax.validation.ConstraintViolation; -import javax.validation.Valid; -import javax.validation.Validation; -import javax.validation.Validator; -import java.util.LinkedList; -import java.util.Set; - -/** - *

MethodArgumentValidationAspect

- *

This class is an aspect class that validates method's argument(s).

- *

USAGE (Must Do's)

- *
    - *
  1. Annotate the method which argument we need to validate by @ValidateArgument
  2. - *
  3. Annotate the argument(s) that we need to validate by @javax.validation.Valid
  4. - *
  5. The field(s) of the argument(s) could be annotated by the constraint annotation provided by Spring - * Security
  6. - *
- *

ATTENTION

- *

If the argument doesn't pass validation, an IllegalArgumentException will be thrown, and not proceed - * the target method.

- * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-07-06 12:17 - **/ -@Slf4j -@Aspect -@Component -public class MethodArgumentValidationAspect { - private final Validator validator; - - public MethodArgumentValidationAspect() { - val validatorFactory = Validation.buildDefaultValidatorFactory(); - this.validator = validatorFactory.getValidator(); - log.info("The validator for {} has been initiated.", this.getClass().getSimpleName()); - } - - /** - * Define pointcut. Pointcut is a predicate or expression that matches join points. In - * ValidateMethodArgumentAspect, we need to cut any method annotated with `@ValidateArgument` only. - *

- * More detail at: Spring aop aspectJ - * pointcut expression examples - */ - @Pointcut("@annotation(com.jmsoftware.maf.exercisemis.universal.aspect.ValidateArgument)") - public void validateMethodArgumentPointcut() { - } - - /** - * Before handle method's argument. This method will be executed after called `proceedingJoinPoint.proceed()`. In - * this phrase, we're going to take some logs. - *

- * `@Before` annotated methods run exactly before the all methods matching with pointcut expression. - * - * @param joinPoint a point of execution of the program - */ - @Before("validateMethodArgumentPointcut()") - public void beforeMethodHandleArgument(JoinPoint joinPoint) { - log.info("Method : {}#{}", - joinPoint.getSignature().getDeclaringTypeName(), - joinPoint.getSignature().getName()); - log.info("Argument : {}", joinPoint.getArgs()); - } - - /** - * Around annotated method processes argument. Around advice can perform custom behavior before and after the - * method invocation. - * - * @param proceedingJoinPoint the object can perform method invocation - * @return any value (may be void) that annotated method returned - */ - @Around("validateMethodArgumentPointcut()") - public Object aroundMethodHandleArgument(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { - log.info("======= METHOD'S ARGUMENT VALIDATION START ======="); - val args = proceedingJoinPoint.getArgs(); - val signature = (MethodSignature) proceedingJoinPoint.getSignature(); - val parameterAnnotations = signature.getMethod().getParameterAnnotations(); - // argumentIndexes is the array list that stores the index of argument we need to validate (the argument - // annotated by `@Valid`) - val argumentIndexListThatNeedsToBeValidated = new LinkedList(); - for (val parameterAnnotation : parameterAnnotations) { - val paramIndex = ArrayUtil.indexOf(parameterAnnotations, parameterAnnotation); - for (val annotation : parameterAnnotation) { - if (annotation instanceof Valid) { - argumentIndexListThatNeedsToBeValidated.add(paramIndex); - } - } - } - val errorMessageList = new LinkedList(); - for (val index : argumentIndexListThatNeedsToBeValidated) { - val constraintViolationSet = validator.validate(args[index]); - if (CollUtil.isNotEmpty(constraintViolationSet)) { - val errorMessage = String.format("Argument validation failed! %s", - getAllFieldErrorMessage(constraintViolationSet)); - errorMessageList.add(errorMessage); - } - } - if (CollUtil.isNotEmpty(errorMessageList)) { - val joinedErrorMessage = StrUtil.join(", ", errorMessageList); - log.info("Method : {}#{}", proceedingJoinPoint.getSignature().getDeclaringTypeName(), - proceedingJoinPoint.getSignature().getName()); - log.info("Argument : {}", args); - log.error("Validation result: {}", joinedErrorMessage); - // If the argument doesn't pass validation, an IllegalArgumentException will be thrown, and not - // proceed the target method - throw new IllegalArgumentException(joinedErrorMessage); - } - log.info("Validation result: Validation passed"); - return proceedingJoinPoint.proceed(); - } - - /** - * `@After` annotated methods run exactly after the all methods matching with pointcut expression. - */ - @After("validateMethodArgumentPointcut()") - public void afterMethodHandleArgument() { - log.info("======== METHOD'S ARGUMENT VALIDATION END ========"); - } - - /** - * `@AfterThrowing` annotated methods run after the method (matching with pointcut expression) exits by throwing an - * exception. - * - * @param joinPoint a point of execution of the program - * @param e exception that controller's method throws - */ - @AfterThrowing(pointcut = "validateMethodArgumentPointcut()", throwing = "e") - public void afterThrowingException(JoinPoint joinPoint, Exception e) { - log.info("Signature : {}", joinPoint.getSignature().toShortString()); - log.error("Exception message: {}", e.toString()); - log.error("== METHOD'S ARGUMENT VALIDATION END WITH EXCEPTION =="); - } - - /** - * Gets all field error message. - * - * @param constraintViolationSet the constraint violation set - * @return the all field error message - */ - private String getAllFieldErrorMessage(Set> constraintViolationSet) { - val allErrorMessageList = new LinkedList(); - for (val constraintViolation : constraintViolationSet) { - allErrorMessageList.add(String.format("invalid field: %s, %s", constraintViolation.getPropertyPath(), - constraintViolation.getMessage())); - } - return StrUtil.join("; ", allErrorMessageList); - } -} diff --git a/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/aspect/ValidateArgument.java b/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/aspect/ValidateArgument.java deleted file mode 100644 index 1aebbd40..00000000 --- a/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/aspect/ValidateArgument.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.jmsoftware.maf.exercisemis.universal.aspect; - -import java.lang.annotation.*; - -/** - *

ValidateArgument

- *

Annotation for validating method's argument.

- *

ATTENTION

- *

If the argument doesn't pass validation, an IllegalArgumentException will be thrown, and not proceed - * the target method.

- * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-07-06 12:08 - **/ -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) -@Documented -public @interface ValidateArgument { -} diff --git a/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/aspect/WebRequestLogAspect.java b/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/aspect/WebRequestLogAspect.java deleted file mode 100644 index c1dd770d..00000000 --- a/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/aspect/WebRequestLogAspect.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.jmsoftware.maf.exercisemis.universal.aspect; - -import cn.hutool.json.JSONUtil; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.jmsoftware.maf.common.util.RequestUtil; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import lombok.val; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.*; -import org.springframework.stereotype.Component; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; - -import java.time.Duration; -import java.time.Instant; - -/** - *

RequestLogAspect

- *

Description:

- *

RequestLogAspect is an AOP for logging URL, HTTP method, client IP and other information when web resource was - * accessed.

- *

Feature:

- *

No methods in controller need to be decorated with annotation. This aspect would automatically cut the method - * decorated with `@GetMapping` or `@PostMapping`.

- * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-05-05 19:55 - **/ -@Slf4j -@Aspect -@Component -@RequiredArgsConstructor -public class WebRequestLogAspect { - private static final int MAX_LENGTH_OF_JSON_STRING = 500; - private static final String LINE_SEPARATOR = System.lineSeparator(); - private final ObjectMapper mapper = new ObjectMapper(); - - /** - * Define pointcut. Pointcut is a predicate or expression that matches join points. In WebRequestLogAspect, we need - * to cut any method annotated with `@GetMapping`, `@PostMapping`, `@PutMapping`, `@DeleteMapping`, `@PatchMapping`, `@RequestMapping`. - *

- * More detail at: Spring aop aspectJ - * pointcut expression examples - */ - @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)" + - " || @annotation(org.springframework.web.bind.annotation.PostMapping)" + - " || @annotation(org.springframework.web.bind.annotation.PutMapping)" + - " || @annotation(org.springframework.web.bind.annotation.DeleteMapping)" + - " || @annotation(org.springframework.web.bind.annotation.PatchMapping)" + - " || @annotation(org.springframework.web.bind.annotation.RequestMapping)") - public void requestLogPointcut() { - } - - /** - * Before controller handle client request (on client sent a request). - *

- * `@Before` annotated methods run exactly before the all methods matching with pointcut expression. - * - * @param joinPoint a point of execution of the program - */ - @Before("requestLogPointcut()") - public void beforeHandleRequest(JoinPoint joinPoint) { - val attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); - assert attributes != null; - val request = attributes.getRequest(); - log.info("============ WEB REQUEST LOG START ============"); - log.info("URL : {}", request.getRequestURL().toString()); - log.info("HTTP Method : {}", request.getMethod()); - log.info("Client IP:Port : {}", RequestUtil.getRequestIpAndPort(request)); - log.info("Class Method : {}#{}", - joinPoint.getSignature().getDeclaringTypeName(), - joinPoint.getSignature().getName()); - log.info("Request Params :{}{}", LINE_SEPARATOR, JSONUtil.toJsonPrettyStr(joinPoint.getArgs())); - } - - /** - * Around controller's method processes client request. Around advice can perform custom behavior before and after - * the method invocation. - * - * @param proceedingJoinPoint the object can perform method invocation - * @return any value (may be void) that controller's method returned - * @throws Throwable any exceptions that controller's method may throw - */ - @Around("requestLogPointcut()") - public Object aroundHandleRequest(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { - val startInstant = Instant.now(); - val result = proceedingJoinPoint.proceed(); - val duration = Duration.between(startInstant, Instant.now()); - try { - var formattedJsonString = JSONUtil.formatJsonStr(mapper.writeValueAsString(result)); - if (formattedJsonString.length() > MAX_LENGTH_OF_JSON_STRING) { - val substring = formattedJsonString.substring(0, MAX_LENGTH_OF_JSON_STRING - 1); - formattedJsonString = - String.format("%s… [The length(%d) of JSON string is larger than the maximum(%d)]", substring, - formattedJsonString.length(), MAX_LENGTH_OF_JSON_STRING); - } - log.info("Response :{}{}", LINE_SEPARATOR, formattedJsonString); - } catch (JsonProcessingException e) { - log.info("Response (non-JSON): {}", result); - } - log.info("Elapsed time : {} ({} ms)", duration, duration.toMillis()); - return result; - } - - /** - * `@After` annotated methods run exactly after the all methods matching with pointcut expression. - */ - @After("requestLogPointcut()") - public void afterHandleRequest() { - log.info("============= WEB REQUEST LOG END ============="); - } - - /** - * `@AfterThrowing` annotated methods run after the method (matching with pointcut expression) exits by throwing an - * exception. - * - * @param joinPoint a point of execution of the program - * @param e exception that controller's method throws - */ - @AfterThrowing(pointcut = "requestLogPointcut()", throwing = "e") - public void afterThrowingException(JoinPoint joinPoint, Exception e) { - log.info("Signature : {}", joinPoint.getSignature().toShortString()); - log.error("Exception : {}, message: {}", e.toString(), e.getMessage()); - log.error("====== WEB REQUEST LOG END WITH EXCEPTION ====="); - } -} diff --git a/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/configuration/WebMvcConfiguration.java b/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/configuration/WebMvcConfiguration.java deleted file mode 100644 index 91fd79a1..00000000 --- a/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/configuration/WebMvcConfiguration.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.jmsoftware.maf.exercisemis.universal.configuration; - -import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.CorsRegistry; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -/** - *

WebMvcConfiguration

- *

- * Spring MVC Configurations. - * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 1/23/20 9:02 AM - **/ -@Configuration -@RequiredArgsConstructor -public class WebMvcConfiguration implements WebMvcConfigurer { - private static final long MAX_AGE_SECS = 3600; - - /** - * 1. Config static path pattern - * 2. Config static resource location - * - * @param registry static resources register - */ - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); - } - - /** - * Configure cross origin requests processing. - * - * @param registry CORS registry - */ - @Override - public void addCorsMappings(CorsRegistry registry) { - registry.addMapping("/**") - .allowedOrigins("*") - .allowedMethods("HEAD", "OPTIONS", "GET", "POST", "PUT", "PATCH", "DELETE") - .maxAge(MAX_AGE_SECS); - } -} diff --git a/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/controller/CommonController.java b/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/controller/CommonController.java index 678e1af5..99685081 100644 --- a/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/controller/CommonController.java +++ b/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/controller/CommonController.java @@ -1,7 +1,7 @@ package com.jmsoftware.maf.exercisemis.universal.controller; import com.jmsoftware.maf.common.bean.ResponseBodyBean; -import com.jmsoftware.maf.exercisemis.universal.domain.ValidationTestPayload; +import com.jmsoftware.maf.common.domain.ValidationTestPayload; import com.jmsoftware.maf.exercisemis.universal.service.CommonService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; diff --git a/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/domain/ValidationTestPayload.java b/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/domain/ValidationTestPayload.java deleted file mode 100644 index d3a97329..00000000 --- a/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/domain/ValidationTestPayload.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.jmsoftware.maf.exercisemis.universal.domain; - -import lombok.Data; - -import javax.validation.constraints.Min; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; - -/** - *

ValidationTestPayload

- *

- * Change description here. - * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2/14/20 11:34 AM - **/ -@Data -public class ValidationTestPayload { - @NotNull - @Min(value = 1L) - private Long id; - @NotEmpty - private String name; -} diff --git a/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/filter/RequestFilter.java b/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/filter/RequestFilter.java deleted file mode 100644 index d22535ee..00000000 --- a/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/filter/RequestFilter.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.jmsoftware.maf.exercisemis.universal.filter; - -import com.jmsoftware.maf.common.util.RequestUtil; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - *

RequestFilter

- *

Request filter.

- * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-03-23 14:24 - **/ -@Slf4j -@Component -public class RequestFilter extends OncePerRequestFilter { - @Override - @SuppressWarnings("NullableProblems") - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, - FilterChain filterChain) throws IOException, ServletException { - log.info("The requester({}) requested resource. Resource: [{}] {}", RequestUtil.getRequestIpAndPort(request), - request.getMethod(), request.getRequestURL()); - filterChain.doFilter(request, response); - } -} diff --git a/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/service/CommonService.java b/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/service/CommonService.java index 98e7c4d7..7c65d38c 100644 --- a/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/service/CommonService.java +++ b/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/service/CommonService.java @@ -1,7 +1,9 @@ package com.jmsoftware.maf.exercisemis.universal.service; -import com.jmsoftware.maf.exercisemis.universal.domain.ValidationTestPayload; +import com.jmsoftware.maf.common.domain.ValidationTestPayload; +import org.springframework.validation.annotation.Validated; +import javax.validation.Valid; import java.util.Map; /** @@ -12,6 +14,7 @@ * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com * @date 2/4/20 11:15 AM */ +@Validated public interface CommonService { /** * Gets application info. @@ -25,5 +28,5 @@ public interface CommonService { * * @param payload the payload */ - void validateObject(ValidationTestPayload payload); + void validateObject(@Valid ValidationTestPayload payload); } diff --git a/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/service/impl/CommonServiceImpl.java b/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/service/impl/CommonServiceImpl.java index a6401eeb..66e88830 100644 --- a/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/service/impl/CommonServiceImpl.java +++ b/exercise-mis/src/main/java/com/jmsoftware/maf/exercisemis/universal/service/impl/CommonServiceImpl.java @@ -1,8 +1,7 @@ package com.jmsoftware.maf.exercisemis.universal.service.impl; -import com.jmsoftware.maf.exercisemis.universal.aspect.ValidateArgument; +import com.jmsoftware.maf.common.domain.ValidationTestPayload; import com.jmsoftware.maf.exercisemis.universal.configuration.ProjectProperty; -import com.jmsoftware.maf.exercisemis.universal.domain.ValidationTestPayload; import com.jmsoftware.maf.exercisemis.universal.service.CommonService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -44,7 +43,6 @@ public Map getApplicationInfo() { } @Override - @ValidateArgument public void validateObject(@Valid ValidationTestPayload payload) { log.info("Validation passed! {}", payload); } diff --git a/muscle-and-fitness-server-reactive-spring-boot-starter/README.md b/muscle-and-fitness-server-reactive-spring-boot-starter/README.md new file mode 100644 index 00000000..131a0721 --- /dev/null +++ b/muscle-and-fitness-server-reactive-spring-boot-starter/README.md @@ -0,0 +1,10 @@ +# Getting Started + +### Reference Documentation + +For further reference, please consider the following sections: + +* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) +* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.3.7.RELEASE/maven-plugin/reference/html/) +* [Create an OCI image](https://docs.spring.io/spring-boot/docs/2.3.7.RELEASE/maven-plugin/reference/html/#build-image) + diff --git a/muscle-and-fitness-server-reactive-spring-boot-starter/pom.xml b/muscle-and-fitness-server-reactive-spring-boot-starter/pom.xml new file mode 100644 index 00000000..abb9bf37 --- /dev/null +++ b/muscle-and-fitness-server-reactive-spring-boot-starter/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + + muscle-and-fitness-server-reactive-spring-boot-starter + Muscle and Fitness Server :: Reactive Spring Boot Starter + Muscle and Fitness Server Reactive Spring Boot Starter + + com.jmsoftware.maf + muscle-and-fitness-server + 0.0.1-SNAPSHOT + + + + + + + org.springframework.boot + spring-boot-autoconfigure + + + org.springframework.boot + spring-boot-autoconfigure-processor + true + + + diff --git a/muscle-and-fitness-server-reactive-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverreactivespringbootstarter/package-info.java b/muscle-and-fitness-server-reactive-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverreactivespringbootstarter/package-info.java new file mode 100644 index 00000000..04f0469b --- /dev/null +++ b/muscle-and-fitness-server-reactive-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverreactivespringbootstarter/package-info.java @@ -0,0 +1 @@ +package com.jmsoftware.maf.muscleandfitnessserverreactivespringbootstarter; diff --git a/muscle-and-fitness-server-reactive-spring-boot-starter/src/main/resources/application.yml b/muscle-and-fitness-server-reactive-spring-boot-starter/src/main/resources/application.yml new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/muscle-and-fitness-server-reactive-spring-boot-starter/src/main/resources/application.yml @@ -0,0 +1 @@ + diff --git a/muscle-and-fitness-server-reactive-spring-boot-starter/src/test/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/MuscleAndFitnessServerSpringBootStarterApplicationTests.java b/muscle-and-fitness-server-reactive-spring-boot-starter/src/test/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/MuscleAndFitnessServerSpringBootStarterApplicationTests.java new file mode 100644 index 00000000..ea0b766d --- /dev/null +++ b/muscle-and-fitness-server-reactive-spring-boot-starter/src/test/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/MuscleAndFitnessServerSpringBootStarterApplicationTests.java @@ -0,0 +1,13 @@ +package com.jmsoftware.maf.muscleandfitnessserverspringbootstarter; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class MuscleAndFitnessServerSpringBootStarterApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/muscle-and-fitness-server-spring-boot-starter/README.md b/muscle-and-fitness-server-spring-boot-starter/README.md new file mode 100644 index 00000000..131a0721 --- /dev/null +++ b/muscle-and-fitness-server-spring-boot-starter/README.md @@ -0,0 +1,10 @@ +# Getting Started + +### Reference Documentation + +For further reference, please consider the following sections: + +* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) +* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.3.7.RELEASE/maven-plugin/reference/html/) +* [Create an OCI image](https://docs.spring.io/spring-boot/docs/2.3.7.RELEASE/maven-plugin/reference/html/#build-image) + diff --git a/muscle-and-fitness-server-spring-boot-starter/pom.xml b/muscle-and-fitness-server-spring-boot-starter/pom.xml new file mode 100644 index 00000000..80925662 --- /dev/null +++ b/muscle-and-fitness-server-spring-boot-starter/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + + muscle-and-fitness-server-spring-boot-starter + Muscle and Fitness Server :: Spring Boot Starter + Muscle and Fitness Server Spring Boot Starter + + com.jmsoftware.maf + muscle-and-fitness-server + 0.0.1-SNAPSHOT + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-autoconfigure + + + org.springframework.boot + spring-boot-autoconfigure-processor + true + + + com.jmsoftware.maf + common + + + diff --git a/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/aspect/ExceptionControllerAdvice.java b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/aspect/ExceptionControllerAdvice.java similarity index 92% rename from spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/aspect/ExceptionControllerAdvice.java rename to muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/aspect/ExceptionControllerAdvice.java index 69346ca6..251d926b 100644 --- a/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/aspect/ExceptionControllerAdvice.java +++ b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/aspect/ExceptionControllerAdvice.java @@ -1,10 +1,10 @@ -package com.jmsoftware.maf.springbootadmin.universal.aspect; +package com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.aspect; import cn.hutool.core.collection.CollUtil; import com.jmsoftware.maf.common.bean.ResponseBodyBean; import com.jmsoftware.maf.common.constant.HttpStatus; import com.jmsoftware.maf.common.exception.BaseException; -import com.jmsoftware.maf.common.util.RequestUtil; +import com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.util.RequestUtil; import lombok.extern.slf4j.Slf4j; import lombok.val; import org.springframework.context.support.DefaultMessageSourceResolvable; @@ -47,8 +47,7 @@ public class ExceptionControllerAdvice { public ResponseBodyBean handleException(HttpServletRequest request, HttpServletResponse response, Exception exception) { - log.error("Exception occurred when [{}] requested access. URL: {}", - RequestUtil.getRequestIpAndPort(request), + log.error("Exception occurred when [{}] requested access. URL: {}", RequestUtil.getRequestIpAndPort(request), request.getServletPath()); // FIXME: THIS IS NOT A PROBLEM @@ -61,7 +60,7 @@ public ResponseBodyBean handleException(HttpServletRequest request, return ResponseBodyBean.ofStatus(HttpStatus.NOT_FOUND); } else if (exception instanceof HttpRequestMethodNotSupportedException) { log.error("Exception occurred when the request handler does not support a specific request method. " + - "Current method is {}, Support HTTP method = {}", + "Current method is {}, Support HTTP method = {}", ((HttpRequestMethodNotSupportedException) exception).getMethod(), ((HttpRequestMethodNotSupportedException) exception).getSupportedHttpMethods()); response.setStatus(HttpStatus.METHOD_NOT_ALLOWED.getCode()); @@ -71,13 +70,15 @@ public ResponseBodyBean handleException(HttpServletRequest request, exception.getMessage()); response.setStatus(HttpStatus.BAD_REQUEST.getCode()); return ResponseBodyBean.ofStatus(HttpStatus.BAD_REQUEST.getCode(), - getFieldErrorMessageFromException((MethodArgumentNotValidException) exception), + getFieldErrorMessageFromException( + (MethodArgumentNotValidException) exception), null); } else if (exception instanceof ConstraintViolationException) { log.error("Constraint violations exception occurred. Exception message: {}", exception.getMessage()); response.setStatus(HttpStatus.BAD_REQUEST.getCode()); return ResponseBodyBean.ofStatus(HttpStatus.BAD_REQUEST.getCode(), - CollUtil.getFirst(((ConstraintViolationException) exception).getConstraintViolations()).getMessage(), + CollUtil.getFirst( + ((ConstraintViolationException) exception).getConstraintViolations()).getMessage(), null); } else if (exception instanceof MethodArgumentTypeMismatchException) { log.error("MethodArgumentTypeMismatchException: Parameter name = {}, Exception message: {}", diff --git a/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/aspect/WebRequestLogAspect.java b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/aspect/WebRequestLogAspect.java similarity index 97% rename from muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/aspect/WebRequestLogAspect.java rename to muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/aspect/WebRequestLogAspect.java index 8d17c601..5778a974 100644 --- a/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/aspect/WebRequestLogAspect.java +++ b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/aspect/WebRequestLogAspect.java @@ -1,10 +1,9 @@ -package com.jmsoftware.maf.musclemis.universal.aspect; +package com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.aspect; import cn.hutool.json.JSONUtil; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.jmsoftware.maf.common.util.RequestUtil; -import lombok.RequiredArgsConstructor; +import com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.util.RequestUtil; import lombok.extern.slf4j.Slf4j; import lombok.val; import org.aspectj.lang.JoinPoint; @@ -32,7 +31,6 @@ @Slf4j @Aspect @Component -@RequiredArgsConstructor public class WebRequestLogAspect { private static final int MAX_LENGTH_OF_JSON_STRING = 500; private static final String LINE_SEPARATOR = System.lineSeparator(); diff --git a/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/configuration/MuscleAndFitnessServerAutoConfiguration.java b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/configuration/MuscleAndFitnessServerAutoConfiguration.java new file mode 100644 index 00000000..08248a88 --- /dev/null +++ b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/configuration/MuscleAndFitnessServerAutoConfiguration.java @@ -0,0 +1,74 @@ +package com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.configuration; + +import com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.aspect.ExceptionControllerAdvice; +import com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.aspect.WebRequestLogAspect; +import com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.controller.RedirectController; +import com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.filter.RequestFilter; +import com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.helper.IpHelper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +import javax.annotation.PostConstruct; + +/** + * Description: MediaStreamingAutoConfiguration, change description here. + * + * @author Johnny Miller (锺俊), email: johnnysviva@outlook.com, date: 10/19/2020 2:51 PM + **/ +@Slf4j +@Configuration +@RequiredArgsConstructor +@EnableConfigurationProperties(MuscleAndFitnessServerAutoConfiguration.class) +public class MuscleAndFitnessServerAutoConfiguration { + @PostConstruct + public void afterInitialization() { + log.debug("{} initialization is done. About to inject beans.", getClass().getSimpleName()); + } + + @Bean + @ConditionalOnMissingBean + public ExceptionControllerAdvice exceptionControllerAdvice() { + log.debug("Initial bean: {}", ExceptionControllerAdvice.class.getName()); + return new ExceptionControllerAdvice(); + } + + @Bean + @ConditionalOnMissingBean + public WebRequestLogAspect webRequestLogAspect() { + log.debug("Initial bean: {}", WebRequestLogAspect.class.getName()); + return new WebRequestLogAspect(); + } + + @Bean + @ConditionalOnMissingBean + public RedirectController redirectController() { + log.debug("Initial bean: {}", RedirectController.class.getName()); + return new RedirectController(); + } + + @Bean + @ConditionalOnMissingBean + public RequestFilter requestFilter() { + log.debug("Initial bean: {}", RequestFilter.class.getName()); + return new RequestFilter(); + } + + @Bean + @ConditionalOnMissingBean + public IpHelper ipHelper(Environment environment) { + log.debug("Initial bean: {}", IpHelper.class.getName()); + return new IpHelper(environment); + } + + @Bean + @ConditionalOnMissingBean + public WebMvcConfiguration webMvcConfiguration() { + log.debug("Initial bean: {}", WebMvcConfiguration.class.getName()); + return new WebMvcConfiguration(); + } +} diff --git a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/configuration/WebMvcConfiguration.java b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/configuration/WebMvcConfiguration.java similarity index 94% rename from api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/configuration/WebMvcConfiguration.java rename to muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/configuration/WebMvcConfiguration.java index ad5fd2fc..f326fd67 100644 --- a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/configuration/WebMvcConfiguration.java +++ b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/configuration/WebMvcConfiguration.java @@ -1,4 +1,4 @@ -package com.jmsoftware.maf.apiportal.universal.configuration; +package com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.configuration; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; diff --git a/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/controller/RedirectController.java b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/controller/RedirectController.java similarity index 76% rename from muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/controller/RedirectController.java rename to muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/controller/RedirectController.java index 628332c5..7f7930d4 100644 --- a/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/controller/RedirectController.java +++ b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/controller/RedirectController.java @@ -1,6 +1,5 @@ -package com.jmsoftware.maf.musclemis.universal.controller; +package com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.controller; -import com.jmsoftware.maf.musclemis.universal.configuration.ProjectProperty; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; @@ -25,8 +24,6 @@ @RequiredArgsConstructor @Api(tags = {"Redirect Controller"}) public class RedirectController { - private final ProjectProperty projectProperty; - @PostConstruct private void postConstruct() { log.info("URL redirect service initialized."); @@ -36,20 +33,20 @@ private void postConstruct() { @ApiOperation(value = "/home", notes = "Home page") public void handleHomeRequest(HttpServletResponse response) throws IOException { // Redirect to home page - response.sendRedirect(projectProperty.getContextPath() + "static/home.html"); + response.sendRedirect("static/home.html"); } @GetMapping("/doc") @ApiOperation(value = "/doc", notes = "Swagger API Documentation") public void handleDocRequest(HttpServletResponse response) throws IOException { // Redirect to Bootstrap Swagger API documentation - response.sendRedirect(projectProperty.getContextPath() + "/doc.html?cache=1&lang=en"); + response.sendRedirect( "/doc.html?cache=1&lang=en"); } @GetMapping("/webjars/bycdao-ui/images/api.ico") @ApiOperation(value = "/webjars/bycdao-ui/images/api.ico", notes = "Favicon redirection") public void handleFaviconRequest(HttpServletResponse response) throws IOException { // Redirect to a customized favicon - response.sendRedirect(projectProperty.getContextPath() + "/static/icon/favicon.ico"); + response.sendRedirect("/static/icon/favicon.ico"); } } diff --git a/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/filter/RequestFilter.java b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/filter/RequestFilter.java similarity index 86% rename from service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/filter/RequestFilter.java rename to muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/filter/RequestFilter.java index 042ff5c4..cc2444e5 100644 --- a/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/filter/RequestFilter.java +++ b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/filter/RequestFilter.java @@ -1,6 +1,6 @@ -package com.jmsoftware.maf.serviceregistry.universal.filter; +package com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.filter; -import com.jmsoftware.maf.common.util.RequestUtil; +import com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.util.RequestUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; diff --git a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/configuration/ServerConfiguration.java b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/helper/IpHelper.java similarity index 73% rename from api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/configuration/ServerConfiguration.java rename to muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/helper/IpHelper.java index 4a79b4c4..a78cfd4c 100644 --- a/api-portal/src/main/java/com/jmsoftware/maf/apiportal/universal/configuration/ServerConfiguration.java +++ b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/helper/IpHelper.java @@ -1,11 +1,13 @@ -package com.jmsoftware.maf.apiportal.universal.configuration; +package com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.helper; +import cn.hutool.core.util.StrUtil; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import lombok.val; import org.springframework.boot.web.context.WebServerInitializedEvent; import org.springframework.context.ApplicationListener; +import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; import java.io.BufferedReader; @@ -28,9 +30,9 @@ @Getter @Component @RequiredArgsConstructor -public class ServerConfiguration implements ApplicationListener { +public class IpHelper implements ApplicationListener { private static final String DEVELOPMENT_ENVIRONMENT = "development"; - private final ProjectProperty projectProperty; + private final Environment environment; private int serverPort; @Override @@ -38,30 +40,18 @@ public void onApplicationEvent(WebServerInitializedEvent event) { this.serverPort = event.getWebServer().getPort(); } - /** - *

Get base URL of backend server.

- *

The result will be like:

- *
    - *
  1. http://[serverIp]:[serverPort]/[contextPath]
  2. - *
  3. https://[serverIp]/[contextPath]
  4. - *
- * - * @return base URL - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-05-03 16:05 - */ - public String getBaseUrl() { - return "http://" + this.getPublicIp() + ":" + serverPort + projectProperty.getContextPath(); - } - /** * Find public IP address. * * @return public IP */ public String getPublicIp() { - if (projectProperty.getEnvironment().contains(DEVELOPMENT_ENVIRONMENT)) { - return this.getInternetIp(); + String jointProfiles = String.join(",", environment.getActiveProfiles()); + if (StrUtil.isNotBlank(jointProfiles)) { + if (jointProfiles.contains(DEVELOPMENT_ENVIRONMENT)) { + log.debug("Current active profiles for environment contains: {}", DEVELOPMENT_ENVIRONMENT); + return this.getInternetIp(); + } } try { // An API provided by https://whatismyipaddress.com/api @@ -91,8 +81,8 @@ private String getInternetIp() { while (addresses.hasMoreElements()) { ip = addresses.nextElement(); if (ip instanceof Inet4Address - && ip.isSiteLocalAddress() - && !ip.getHostAddress().equals(intranetIp)) { + && ip.isSiteLocalAddress() + && !ip.getHostAddress().equals(intranetIp)) { return ip.getHostAddress(); } } diff --git a/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/package-info.java b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/package-info.java new file mode 100644 index 00000000..7aa39447 --- /dev/null +++ b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/package-info.java @@ -0,0 +1 @@ +package com.jmsoftware.maf.muscleandfitnessserverspringbootstarter; diff --git a/common/src/main/java/com/jmsoftware/maf/common/util/CaseConversionUtil.java b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/util/CaseConversionUtil.java similarity index 99% rename from common/src/main/java/com/jmsoftware/maf/common/util/CaseConversionUtil.java rename to muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/util/CaseConversionUtil.java index 25a51057..0fa0afc2 100644 --- a/common/src/main/java/com/jmsoftware/maf/common/util/CaseConversionUtil.java +++ b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/util/CaseConversionUtil.java @@ -1,4 +1,4 @@ -package com.jmsoftware.maf.common.util; +package com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.util; import java.util.Random; import java.util.regex.Pattern; diff --git a/common/src/main/java/com/jmsoftware/maf/common/util/FileUtil.java b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/util/FileUtil.java similarity index 97% rename from common/src/main/java/com/jmsoftware/maf/common/util/FileUtil.java rename to muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/util/FileUtil.java index 396cb649..6a624390 100644 --- a/common/src/main/java/com/jmsoftware/maf/common/util/FileUtil.java +++ b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/util/FileUtil.java @@ -1,4 +1,4 @@ -package com.jmsoftware.maf.common.util; +package com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.util; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; diff --git a/common/src/main/java/com/jmsoftware/maf/common/util/RequestUtil.java b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/util/RequestUtil.java similarity index 94% rename from common/src/main/java/com/jmsoftware/maf/common/util/RequestUtil.java rename to muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/util/RequestUtil.java index a9ca3e7d..73539d01 100644 --- a/common/src/main/java/com/jmsoftware/maf/common/util/RequestUtil.java +++ b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/util/RequestUtil.java @@ -1,4 +1,4 @@ -package com.jmsoftware.maf.common.util; +package com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.util; import cn.hutool.core.util.StrUtil; diff --git a/common/src/main/java/com/jmsoftware/maf/common/util/ResponseUtil.java b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/util/ResponseUtil.java similarity index 97% rename from common/src/main/java/com/jmsoftware/maf/common/util/ResponseUtil.java rename to muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/util/ResponseUtil.java index f2b525f0..99709cf1 100644 --- a/common/src/main/java/com/jmsoftware/maf/common/util/ResponseUtil.java +++ b/muscle-and-fitness-server-spring-boot-starter/src/main/java/com/jmsoftware/maf/muscleandfitnessserverspringbootstarter/util/ResponseUtil.java @@ -1,4 +1,4 @@ -package com.jmsoftware.maf.common.util; +package com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.util; import com.fasterxml.jackson.databind.ObjectMapper; import com.jmsoftware.maf.common.bean.ResponseBodyBean; diff --git a/muscle-and-fitness-server-spring-boot-starter/src/main/resources/META-INF/spring.factories b/muscle-and-fitness-server-spring-boot-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..77c4a2b8 --- /dev/null +++ b/muscle-and-fitness-server-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -0,0 +1,3 @@ +# Auto Configure +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.configuration.MuscleAndFitnessServerAutoConfiguration diff --git a/muscle-mis/pom.xml b/muscle-mis/pom.xml index 287e8d28..23ab2f2f 100644 --- a/muscle-mis/pom.xml +++ b/muscle-mis/pom.xml @@ -98,7 +98,10 @@ com.jmsoftware.maf common - 0.0.1-SNAPSHOT + + + com.jmsoftware.maf + muscle-and-fitness-server-spring-boot-starter diff --git a/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/MuscleMisApplication.java b/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/MuscleMisApplication.java index 7d86207d..a367b0ae 100644 --- a/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/MuscleMisApplication.java +++ b/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/MuscleMisApplication.java @@ -1,7 +1,7 @@ package com.jmsoftware.maf.musclemis; +import com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.helper.IpHelper; import com.jmsoftware.maf.musclemis.universal.configuration.ProjectProperty; -import com.jmsoftware.maf.musclemis.universal.configuration.ServerConfiguration; import lombok.extern.slf4j.Slf4j; import lombok.val; import org.springframework.boot.SpringApplication; @@ -28,11 +28,11 @@ public class MuscleMisApplication { private static final String LINE_SEPARATOR = System.lineSeparator(); private static ProjectProperty projectProperty; - private static ServerConfiguration serverConfiguration; + private static IpHelper ipHelper; - public MuscleMisApplication(ProjectProperty projectProperty, ServerConfiguration serverConfiguration) { + public MuscleMisApplication(ProjectProperty projectProperty, IpHelper ipHelper) { MuscleMisApplication.projectProperty = projectProperty; - MuscleMisApplication.serverConfiguration = serverConfiguration; + MuscleMisApplication.ipHelper = ipHelper; } public static void main(String[] args) { @@ -45,8 +45,8 @@ public static void main(String[] args) { log.info("⚙️ Environment: {}", projectProperty.getEnvironment()); log.info("⏳ Deployment duration: {} seconds ({} ms)", duration.getSeconds(), duration.toMillis()); log.info("⏰ App started at {} (timezone - {})", endInstant, TimeZone.getDefault().getDisplayName()); - log.info("{} App running at{} - Local: http://localhost:{}{}/{} - Network: {}/", - LINE_SEPARATOR, LINE_SEPARATOR, serverConfiguration.getServerPort(), projectProperty.getContextPath(), - LINE_SEPARATOR, serverConfiguration.getBaseUrl()); + log.info("{} App running at{} - Local: http://localhost:{}{}/{} - Network: {}/{}", + LINE_SEPARATOR, LINE_SEPARATOR, ipHelper.getServerPort(), projectProperty.getContextPath(), + LINE_SEPARATOR, ipHelper.getPublicIp(), projectProperty.getContextPath()); } } diff --git a/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/aspect/ExceptionControllerAdvice.java b/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/aspect/ExceptionControllerAdvice.java deleted file mode 100644 index 877d5646..00000000 --- a/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/aspect/ExceptionControllerAdvice.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.jmsoftware.maf.musclemis.universal.aspect; - -import cn.hutool.core.collection.CollUtil; -import com.jmsoftware.maf.common.bean.ResponseBodyBean; -import com.jmsoftware.maf.common.constant.HttpStatus; -import com.jmsoftware.maf.common.exception.BaseException; -import com.jmsoftware.maf.common.util.RequestUtil; -import lombok.extern.slf4j.Slf4j; -import lombok.val; -import org.springframework.context.support.DefaultMessageSourceResolvable; -import org.springframework.http.converter.HttpMessageNotReadableException; -import org.springframework.validation.BindException; -import org.springframework.web.HttpRequestMethodNotSupportedException; -import org.springframework.web.bind.MethodArgumentNotValidException; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; -import org.springframework.web.servlet.NoHandlerFoundException; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.validation.ConstraintViolationException; -import java.util.Objects; - -/** - *

ExceptionControllerAdvice

- *

- * Exception advice for global controllers. - * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-03-02 17:39 - **/ -@Slf4j -@ControllerAdvice -public class ExceptionControllerAdvice { - /** - *

Exception handler.

- *

ATTENTION: In this method, cannot throw any exception.

- * - * @param request HTTP request - * @param exception any kinds of exception occurred in controller - * @return custom exception info - */ - @ResponseBody - @ExceptionHandler(value = Exception.class) - public ResponseBodyBean handleException(HttpServletRequest request, - HttpServletResponse response, - Exception exception) { - log.error("Exception occurred when [{}] requested access. URL: {}", - RequestUtil.getRequestIpAndPort(request), - request.getServletPath()); - - // FIXME: THIS IS NOT A PROBLEM - // ATTENTION: Use only ResponseBodyBean.ofStatus() in handleException() method and DON'T throw any exception - if (exception instanceof NoHandlerFoundException) { - log.error("NoHandlerFoundException: Request URL = {}, HTTP method = {}", - ((NoHandlerFoundException) exception).getRequestURL(), - ((NoHandlerFoundException) exception).getHttpMethod()); - response.setStatus(HttpStatus.NOT_FOUND.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.NOT_FOUND); - } else if (exception instanceof HttpRequestMethodNotSupportedException) { - log.error("Exception occurred when the request handler does not support a specific request method. " + - "Current method is {}, Support HTTP method = {}", - ((HttpRequestMethodNotSupportedException) exception).getMethod(), - ((HttpRequestMethodNotSupportedException) exception).getSupportedHttpMethods()); - response.setStatus(HttpStatus.METHOD_NOT_ALLOWED.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.METHOD_NOT_ALLOWED); - } else if (exception instanceof MethodArgumentNotValidException) { - log.error("Exception occurred when validation on an argument annotated with fails. Exception message: {}", - exception.getMessage()); - response.setStatus(HttpStatus.BAD_REQUEST.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.BAD_REQUEST.getCode(), - getFieldErrorMessageFromException((MethodArgumentNotValidException) exception), - null); - } else if (exception instanceof ConstraintViolationException) { - log.error("Constraint violations exception occurred. Exception message: {}", exception.getMessage()); - response.setStatus(HttpStatus.BAD_REQUEST.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.BAD_REQUEST.getCode(), - CollUtil.getFirst(((ConstraintViolationException) exception).getConstraintViolations()).getMessage(), - null); - } else if (exception instanceof MethodArgumentTypeMismatchException) { - log.error("MethodArgumentTypeMismatchException: Parameter name = {}, Exception message: {}", - ((MethodArgumentTypeMismatchException) exception).getName(), - ((MethodArgumentTypeMismatchException) exception).getMessage()); - response.setStatus(HttpStatus.PARAM_NOT_MATCH.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.PARAM_NOT_MATCH); - } else if (exception instanceof HttpMessageNotReadableException) { - log.error("HttpMessageNotReadableException: {}", - ((HttpMessageNotReadableException) exception).getMessage()); - response.setStatus(HttpStatus.PARAM_NOT_NULL.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.PARAM_NOT_NULL); - } else if (exception instanceof BaseException) { - log.error("BaseException: Status code: {}, message: {}, data: {}", ((BaseException) exception).getCode(), - exception.getMessage(), ((BaseException) exception).getData()); - response.setStatus(((BaseException) exception).getCode()); - return ResponseBodyBean.ofStatus(((BaseException) exception).getCode(), exception.getMessage(), - ((BaseException) exception).getData()); - } else if (exception instanceof BindException) { - log.error("Exception message: {} ", exception.getMessage()); - response.setStatus(HttpStatus.INVALID_PARAM.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.INVALID_PARAM); - } else if (exception instanceof IllegalArgumentException) { - log.error("Exception message: {} ", exception.getMessage()); - response.setStatus(HttpStatus.BAD_REQUEST.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.BAD_REQUEST.getCode(), exception.getMessage(), null); - } - log.error("Internal system exception occurred! Exception message: {} ", exception.getMessage(), exception); - response.setStatus(HttpStatus.ERROR.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.ERROR.getCode(), HttpStatus.ERROR.getMessage(), null); - } - - /** - * Get field error message from exception. If two or more fields do not pass Spring Validation check, then will - * return the 1st error message of the error field. - * - * @param exception MethodArgumentNotValidException - * @return field error message - */ - private String getFieldErrorMessageFromException(MethodArgumentNotValidException exception) { - try { - val firstErrorField = - (DefaultMessageSourceResolvable) Objects.requireNonNull(exception.getBindingResult() - .getAllErrors() - .get(0) - .getArguments())[0]; - val firstErrorFieldName = firstErrorField.getDefaultMessage(); - val firstErrorFieldMessage = exception.getBindingResult().getAllErrors().get(0).getDefaultMessage(); - return String.format("%s %s", firstErrorFieldName, firstErrorFieldMessage); - } catch (Exception e) { - log.error("Exception occurred when get field error message from exception. Exception message: {}", - e.getMessage(), e); - return HttpStatus.INVALID_PARAM.getMessage(); - } - } -} diff --git a/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/aspect/MethodArgumentValidationAspect.java b/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/aspect/MethodArgumentValidationAspect.java deleted file mode 100644 index a3d2d8d0..00000000 --- a/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/aspect/MethodArgumentValidationAspect.java +++ /dev/null @@ -1,160 +0,0 @@ -package com.jmsoftware.maf.musclemis.universal.aspect; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.StrUtil; -import lombok.extern.slf4j.Slf4j; -import lombok.val; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.*; -import org.aspectj.lang.reflect.MethodSignature; -import org.springframework.stereotype.Component; - -import javax.validation.ConstraintViolation; -import javax.validation.Valid; -import javax.validation.Validation; -import javax.validation.Validator; -import java.util.LinkedList; -import java.util.Set; - -/** - *

MethodArgumentValidationAspect

- *

This class is an aspect class that validates method's argument(s).

- *

USAGE (Must Do's)

- *
    - *
  1. Annotate the method which argument we need to validate by @ValidateArgument
  2. - *
  3. Annotate the argument(s) that we need to validate by @javax.validation.Valid
  4. - *
  5. The field(s) of the argument(s) could be annotated by the constraint annotation provided by Spring - * Security
  6. - *
- *

ATTENTION

- *

If the argument doesn't pass validation, an IllegalArgumentException will be thrown, and not proceed - * the target method.

- * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-07-06 12:17 - **/ -@Slf4j -@Aspect -@Component -public class MethodArgumentValidationAspect { - private final Validator validator; - - public MethodArgumentValidationAspect() { - val validatorFactory = Validation.buildDefaultValidatorFactory(); - this.validator = validatorFactory.getValidator(); - log.info("The validator for {} has been initiated.", this.getClass().getSimpleName()); - } - - /** - * Define pointcut. Pointcut is a predicate or expression that matches join points. In - * ValidateMethodArgumentAspect, we need to cut any method annotated with `@ValidateArgument` only. - *

- * More detail at: Spring aop aspectJ - * pointcut expression examples - */ - @Pointcut("@annotation(com.jmsoftware.maf.musclemis.universal.aspect.ValidateArgument)") - public void validateMethodArgumentPointcut() { - } - - /** - * Before handle method's argument. This method will be executed after called `proceedingJoinPoint.proceed()`. In - * this phrase, we're going to take some logs. - *

- * `@Before` annotated methods run exactly before the all methods matching with pointcut expression. - * - * @param joinPoint a point of execution of the program - */ - @Before("validateMethodArgumentPointcut()") - public void beforeMethodHandleArgument(JoinPoint joinPoint) { - log.info("Method : {}#{}", - joinPoint.getSignature().getDeclaringTypeName(), - joinPoint.getSignature().getName()); - log.info("Argument : {}", joinPoint.getArgs()); - } - - /** - * Around annotated method processes argument. Around advice can perform custom behavior before and after the - * method invocation. - * - * @param proceedingJoinPoint the object can perform method invocation - * @return any value (may be void) that annotated method returned - */ - @Around("validateMethodArgumentPointcut()") - public Object aroundMethodHandleArgument(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { - log.info("======= METHOD'S ARGUMENT VALIDATION START ======="); - val args = proceedingJoinPoint.getArgs(); - val signature = (MethodSignature) proceedingJoinPoint.getSignature(); - val parameterAnnotations = signature.getMethod().getParameterAnnotations(); - // argumentIndexes is the array list that stores the index of argument we need to validate (the argument - // annotated by `@Valid`) - val argumentIndexListThatNeedsToBeValidated = new LinkedList(); - for (val parameterAnnotation : parameterAnnotations) { - val paramIndex = ArrayUtil.indexOf(parameterAnnotations, parameterAnnotation); - for (val annotation : parameterAnnotation) { - if (annotation instanceof Valid) { - argumentIndexListThatNeedsToBeValidated.add(paramIndex); - } - } - } - val errorMessageList = new LinkedList(); - for (val index : argumentIndexListThatNeedsToBeValidated) { - val constraintViolationSet = validator.validate(args[index]); - if (CollUtil.isNotEmpty(constraintViolationSet)) { - val errorMessage = String.format("Argument validation failed! %s", - getAllFieldErrorMessage(constraintViolationSet)); - errorMessageList.add(errorMessage); - } - } - if (CollUtil.isNotEmpty(errorMessageList)) { - val joinedErrorMessage = StrUtil.join(", ", errorMessageList); - log.info("Method : {}#{}", proceedingJoinPoint.getSignature().getDeclaringTypeName(), - proceedingJoinPoint.getSignature().getName()); - log.info("Argument : {}", args); - log.error("Validation result: {}", joinedErrorMessage); - // If the argument doesn't pass validation, an IllegalArgumentException will be thrown, and not - // proceed the target method - throw new IllegalArgumentException(joinedErrorMessage); - } - log.info("Validation result: Validation passed"); - return proceedingJoinPoint.proceed(); - } - - /** - * `@After` annotated methods run exactly after the all methods matching with pointcut expression. - */ - @After("validateMethodArgumentPointcut()") - public void afterMethodHandleArgument() { - log.info("======== METHOD'S ARGUMENT VALIDATION END ========"); - } - - /** - * `@AfterThrowing` annotated methods run after the method (matching with pointcut expression) exits by throwing an - * exception. - * - * @param joinPoint a point of execution of the program - * @param e exception that controller's method throws - */ - @AfterThrowing(pointcut = "validateMethodArgumentPointcut()", throwing = "e") - public void afterThrowingException(JoinPoint joinPoint, Exception e) { - log.info("Signature : {}", joinPoint.getSignature().toShortString()); - log.error("Exception message: {}", e.toString()); - log.error("== METHOD'S ARGUMENT VALIDATION END WITH EXCEPTION =="); - } - - /** - * Gets all field error message. - * - * @param constraintViolationSet the constraint violation set - * @return the all field error message - */ - private String getAllFieldErrorMessage(Set> constraintViolationSet) { - val allErrorMessageList = new LinkedList(); - for (val constraintViolation : constraintViolationSet) { - allErrorMessageList.add(String.format("invalid field: %s, %s", constraintViolation.getPropertyPath(), - constraintViolation.getMessage())); - } - return StrUtil.join("; ", allErrorMessageList); - } -} diff --git a/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/aspect/ValidateArgument.java b/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/aspect/ValidateArgument.java deleted file mode 100644 index 7d7b8040..00000000 --- a/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/aspect/ValidateArgument.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.jmsoftware.maf.musclemis.universal.aspect; - -import java.lang.annotation.*; - -/** - *

ValidateArgument

- *

Annotation for validating method's argument.

- *

ATTENTION

- *

If the argument doesn't pass validation, an IllegalArgumentException will be thrown, and not proceed - * the target method.

- * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-07-06 12:08 - **/ -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) -@Documented -public @interface ValidateArgument { -} diff --git a/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/configuration/ServerConfiguration.java b/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/configuration/ServerConfiguration.java deleted file mode 100644 index 92939050..00000000 --- a/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/configuration/ServerConfiguration.java +++ /dev/null @@ -1,118 +0,0 @@ -package com.jmsoftware.maf.musclemis.universal.configuration; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import lombok.val; -import org.springframework.boot.web.context.WebServerInitializedEvent; -import org.springframework.context.ApplicationListener; -import org.springframework.stereotype.Component; - -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.net.Inet4Address; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.URL; -import java.util.Enumeration; - -/** - *

ServerConfiguration

- *

- * Change description here. - * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-04-26 16:02 - **/ -@Slf4j -@Getter -@Component -@RequiredArgsConstructor -public class ServerConfiguration implements ApplicationListener { - private static final String DEVELOPMENT_ENVIRONMENT = "development"; - private final ProjectProperty projectProperty; - private int serverPort; - - @Override - public void onApplicationEvent(WebServerInitializedEvent event) { - this.serverPort = event.getWebServer().getPort(); - } - - /** - *

Get base URL of backend server.

- *

The result will be like:

- *
    - *
  1. http://[serverIp]:[serverPort]/[contextPath]
  2. - *
  3. https://[serverIp]/[contextPath]
  4. - *
- * - * @return base URL - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-05-03 16:05 - */ - public String getBaseUrl() { - return "http://" + this.getPublicIp() + ":" + serverPort + projectProperty.getContextPath(); - } - - /** - * Find public IP address. - * - * @return public IP - */ - public String getPublicIp() { - if (projectProperty.getEnvironment().contains(DEVELOPMENT_ENVIRONMENT)) { - return this.getInternetIp(); - } - try { - // An API provided by https://whatismyipaddress.com/api - val url = new URL("https://ipv4bot.whatismyipaddress.com/"); - val sc = new BufferedReader(new InputStreamReader(url.openStream())); - // Read system IP Address - return sc.readLine().trim(); - } catch (Exception e) { - log.error("Cannot execute properly to get IP address from https://whatismyipaddress.com/api", e); - } - return this.getInternetIp(); - } - - /** - * Get internet IP. - * - * @return internet IP - */ - private String getInternetIp() { - val intranetIp = this.getIntranetIp(); - try { - val networks = NetworkInterface.getNetworkInterfaces(); - InetAddress ip; - Enumeration addresses; - while (networks.hasMoreElements()) { - addresses = networks.nextElement().getInetAddresses(); - while (addresses.hasMoreElements()) { - ip = addresses.nextElement(); - if (ip instanceof Inet4Address - && ip.isSiteLocalAddress() - && !ip.getHostAddress().equals(intranetIp)) { - return ip.getHostAddress(); - } - } - } - return intranetIp; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - /** - * Get intranet IP. - * - * @return intranet IP - */ - private String getIntranetIp() { - try { - return InetAddress.getLocalHost().getHostAddress(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } -} diff --git a/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/configuration/WebMvcConfiguration.java b/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/configuration/WebMvcConfiguration.java deleted file mode 100644 index 903361c9..00000000 --- a/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/configuration/WebMvcConfiguration.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.jmsoftware.maf.musclemis.universal.configuration; - -import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.CorsRegistry; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -/** - *

WebMvcConfiguration

- *

- * Spring MVC Configurations. - * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 1/23/20 9:02 AM - **/ -@Configuration -@RequiredArgsConstructor -public class WebMvcConfiguration implements WebMvcConfigurer { - private static final long MAX_AGE_SECS = 3600; - - /** - * 1. Config static path pattern - * 2. Config static resource location - * - * @param registry static resources register - */ - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); - } - - /** - * Configure cross origin requests processing. - * - * @param registry CORS registry - */ - @Override - public void addCorsMappings(CorsRegistry registry) { - registry.addMapping("/**") - .allowedOrigins("*") - .allowedMethods("HEAD", "OPTIONS", "GET", "POST", "PUT", "PATCH", "DELETE") - .maxAge(MAX_AGE_SECS); - } -} diff --git a/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/filter/RequestFilter.java b/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/filter/RequestFilter.java deleted file mode 100644 index 28c53b1a..00000000 --- a/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/filter/RequestFilter.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.jmsoftware.maf.musclemis.universal.filter; - -import com.jmsoftware.maf.common.util.RequestUtil; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - *

RequestFilter

- *

Request filter.

- * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-03-23 14:24 - **/ -@Slf4j -@Component -public class RequestFilter extends OncePerRequestFilter { - @Override - @SuppressWarnings("NullableProblems") - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, - FilterChain filterChain) throws IOException, ServletException { - log.info("The requester({}) requested resource. Resource: [{}] {}", RequestUtil.getRequestIpAndPort(request), - request.getMethod(), request.getRequestURL()); - filterChain.doFilter(request, response); - } -} diff --git a/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/service/CommonService.java b/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/service/CommonService.java index c343716d..31ad9111 100644 --- a/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/service/CommonService.java +++ b/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/service/CommonService.java @@ -1,7 +1,9 @@ package com.jmsoftware.maf.musclemis.universal.service; import com.jmsoftware.maf.musclemis.universal.domain.ValidationTestPayload; +import org.springframework.validation.annotation.Validated; +import javax.validation.Valid; import java.util.Map; /** @@ -12,6 +14,7 @@ * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com * @date 2/4/20 11:15 AM */ +@Validated public interface CommonService { /** * Gets application info. @@ -25,5 +28,5 @@ public interface CommonService { * * @param payload the payload */ - void validateObject(ValidationTestPayload payload); + void validateObject(@Valid ValidationTestPayload payload); } diff --git a/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/service/impl/CommonServiceImpl.java b/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/service/impl/CommonServiceImpl.java index a7e49807..f0aa0120 100644 --- a/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/service/impl/CommonServiceImpl.java +++ b/muscle-mis/src/main/java/com/jmsoftware/maf/musclemis/universal/service/impl/CommonServiceImpl.java @@ -1,6 +1,5 @@ package com.jmsoftware.maf.musclemis.universal.service.impl; -import com.jmsoftware.maf.musclemis.universal.aspect.ValidateArgument; import com.jmsoftware.maf.musclemis.universal.configuration.ProjectProperty; import com.jmsoftware.maf.musclemis.universal.domain.ValidationTestPayload; import com.jmsoftware.maf.musclemis.universal.service.CommonService; @@ -44,7 +43,6 @@ public Map getApplicationInfo() { } @Override - @ValidateArgument public void validateObject(@Valid ValidationTestPayload payload) { log.info("Validation passed! {}", payload); } diff --git a/pom.xml b/pom.xml index 8d4324cd..6d16469b 100644 --- a/pom.xml +++ b/pom.xml @@ -46,6 +46,8 @@ common + muscle-and-fitness-server-spring-boot-starter + muscle-and-fitness-server-reactive-spring-boot-starter service-registry spring-boot-admin gateway @@ -58,7 +60,7 @@ org.springframework.boot spring-boot-starter-parent - 2.3.1.RELEASE + 2.3.7.RELEASE @@ -76,42 +78,52 @@ com.jmsoftware.maf common - 0.0.1-SNAPSHOT + ${project.version} + + + com.jmsoftware.maf + muscle-and-fitness-server-spring-boot-starter + ${project.version} + + + com.jmsoftware.maf + muscle-and-fitness-server-reactive-spring-boot-starter + ${project.version} com.jmsoftware.maf service-registry - 0.0.1-SNAPSHOT + ${project.version} com.jmsoftware.maf spring-boot-admin - 0.0.1-SNAPSHOT + ${project.version} com.jmsoftware.maf gateway - 0.0.1-SNAPSHOT + ${project.version} com.jmsoftware.maf api-portal - 0.0.1-SNAPSHOT + ${project.version} com.jmsoftware.maf auth-center - 0.0.1-SNAPSHOT + ${project.version} com.jmsoftware.maf exercise-mis - 0.0.1-SNAPSHOT + ${project.version} com.jmsoftware.maf muscle-mis - 0.0.1-SNAPSHOT + ${project.version} @@ -243,6 +255,12 @@ org.springframework.boot spring-boot-starter-test test + + + org.junit.vintage + junit-vintage-engine + + io.projectreactor diff --git a/service-registry/pom.xml b/service-registry/pom.xml index d9d3bf92..db715a24 100644 --- a/service-registry/pom.xml +++ b/service-registry/pom.xml @@ -84,7 +84,10 @@ com.jmsoftware.maf common - 0.0.1-SNAPSHOT + + + com.jmsoftware.maf + muscle-and-fitness-server-spring-boot-starter diff --git a/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/ServiceRegistryApplication.java b/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/ServiceRegistryApplication.java index 34fa9d9b..cc65389d 100644 --- a/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/ServiceRegistryApplication.java +++ b/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/ServiceRegistryApplication.java @@ -1,7 +1,7 @@ package com.jmsoftware.maf.serviceregistry; +import com.jmsoftware.maf.muscleandfitnessserverspringbootstarter.helper.IpHelper; import com.jmsoftware.maf.serviceregistry.universal.configuration.ProjectProperty; -import com.jmsoftware.maf.serviceregistry.universal.configuration.ServerConfiguration; import lombok.extern.slf4j.Slf4j; import lombok.val; import org.springframework.boot.SpringApplication; @@ -26,11 +26,11 @@ public class ServiceRegistryApplication { private static final String LINE_SEPARATOR = System.lineSeparator(); private static ProjectProperty projectProperty; - private static ServerConfiguration serverConfiguration; + private static IpHelper ipHelper; - public ServiceRegistryApplication(ProjectProperty projectProperty, ServerConfiguration serverConfiguration) { + public ServiceRegistryApplication(ProjectProperty projectProperty, IpHelper ipHelper) { ServiceRegistryApplication.projectProperty = projectProperty; - ServiceRegistryApplication.serverConfiguration = serverConfiguration; + ServiceRegistryApplication.ipHelper = ipHelper; } public static void main(String[] args) { @@ -43,8 +43,8 @@ public static void main(String[] args) { log.info("⚙️ Environment: {}", projectProperty.getEnvironment()); log.info("⏳ Deployment duration: {} seconds ({} ms)", duration.getSeconds(), duration.toMillis()); log.info("⏰ App started at {} (timezone - {})", endInstant, TimeZone.getDefault().getDisplayName()); - log.info("{} App running at{} - Local: http://localhost:{}{}/{} - Network: {}/", - LINE_SEPARATOR, LINE_SEPARATOR, serverConfiguration.getServerPort(), projectProperty.getContextPath(), - LINE_SEPARATOR, serverConfiguration.getBaseUrl()); + log.info("{} App running at{} - Local: http://localhost:{}{}/{} - Network: {}/{}", + LINE_SEPARATOR, LINE_SEPARATOR, ipHelper.getServerPort(), projectProperty.getContextPath(), + LINE_SEPARATOR, ipHelper.getPublicIp(), projectProperty.getContextPath()); } } diff --git a/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/aspect/ExceptionControllerAdvice.java b/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/aspect/ExceptionControllerAdvice.java deleted file mode 100644 index 5e9a6c4a..00000000 --- a/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/aspect/ExceptionControllerAdvice.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.jmsoftware.maf.serviceregistry.universal.aspect; - -import cn.hutool.core.collection.CollUtil; -import com.jmsoftware.maf.common.bean.ResponseBodyBean; -import com.jmsoftware.maf.common.constant.HttpStatus; -import com.jmsoftware.maf.common.exception.BaseException; -import com.jmsoftware.maf.common.util.RequestUtil; -import lombok.extern.slf4j.Slf4j; -import lombok.val; -import org.springframework.context.support.DefaultMessageSourceResolvable; -import org.springframework.http.converter.HttpMessageNotReadableException; -import org.springframework.validation.BindException; -import org.springframework.web.HttpRequestMethodNotSupportedException; -import org.springframework.web.bind.MethodArgumentNotValidException; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; -import org.springframework.web.servlet.NoHandlerFoundException; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.validation.ConstraintViolationException; -import java.util.Objects; - -/** - *

ExceptionControllerAdvice

- *

- * Exception advice for global controllers. - * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-03-02 17:39 - **/ -@Slf4j -@ControllerAdvice -public class ExceptionControllerAdvice { - /** - *

Exception handler.

- *

ATTENTION: In this method, cannot throw any exception.

- * - * @param request HTTP request - * @param exception any kinds of exception occurred in controller - * @return custom exception info - */ - @ResponseBody - @ExceptionHandler(value = Exception.class) - public ResponseBodyBean handleException(HttpServletRequest request, - HttpServletResponse response, - Exception exception) { - log.error("Exception occurred when [{}] requested access. URL: {}", - RequestUtil.getRequestIpAndPort(request), - request.getServletPath()); - - // FIXME: THIS IS NOT A PROBLEM - // ATTENTION: Use only ResponseBodyBean.ofStatus() in handleException() method and DON'T throw any exception - if (exception instanceof NoHandlerFoundException) { - log.error("NoHandlerFoundException: Request URL = {}, HTTP method = {}", - ((NoHandlerFoundException) exception).getRequestURL(), - ((NoHandlerFoundException) exception).getHttpMethod()); - response.setStatus(HttpStatus.NOT_FOUND.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.NOT_FOUND); - } else if (exception instanceof HttpRequestMethodNotSupportedException) { - log.error("Exception occurred when the request handler does not support a specific request method. " + - "Current method is {}, Support HTTP method = {}", - ((HttpRequestMethodNotSupportedException) exception).getMethod(), - ((HttpRequestMethodNotSupportedException) exception).getSupportedHttpMethods()); - response.setStatus(HttpStatus.METHOD_NOT_ALLOWED.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.METHOD_NOT_ALLOWED); - } else if (exception instanceof MethodArgumentNotValidException) { - log.error("Exception occurred when validation on an argument annotated with fails. Exception message: {}", - exception.getMessage()); - response.setStatus(HttpStatus.BAD_REQUEST.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.BAD_REQUEST.getCode(), - getFieldErrorMessageFromException((MethodArgumentNotValidException) exception), - null); - } else if (exception instanceof ConstraintViolationException) { - log.error("Constraint violations exception occurred. Exception message: {}", exception.getMessage()); - response.setStatus(HttpStatus.BAD_REQUEST.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.BAD_REQUEST.getCode(), - CollUtil.getFirst(((ConstraintViolationException) exception).getConstraintViolations()).getMessage(), - null); - } else if (exception instanceof MethodArgumentTypeMismatchException) { - log.error("MethodArgumentTypeMismatchException: Parameter name = {}, Exception message: {}", - ((MethodArgumentTypeMismatchException) exception).getName(), - ((MethodArgumentTypeMismatchException) exception).getMessage()); - response.setStatus(HttpStatus.PARAM_NOT_MATCH.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.PARAM_NOT_MATCH); - } else if (exception instanceof HttpMessageNotReadableException) { - log.error("HttpMessageNotReadableException: {}", - ((HttpMessageNotReadableException) exception).getMessage()); - response.setStatus(HttpStatus.PARAM_NOT_NULL.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.PARAM_NOT_NULL); - } else if (exception instanceof BaseException) { - log.error("BaseException: Status code: {}, message: {}, data: {}", ((BaseException) exception).getCode(), - exception.getMessage(), ((BaseException) exception).getData()); - response.setStatus(((BaseException) exception).getCode()); - return ResponseBodyBean.ofStatus(((BaseException) exception).getCode(), exception.getMessage(), - ((BaseException) exception).getData()); - } else if (exception instanceof BindException) { - log.error("Exception message: {} ", exception.getMessage()); - response.setStatus(HttpStatus.INVALID_PARAM.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.INVALID_PARAM); - } else if (exception instanceof IllegalArgumentException) { - log.error("Exception message: {} ", exception.getMessage()); - response.setStatus(HttpStatus.BAD_REQUEST.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.BAD_REQUEST.getCode(), exception.getMessage(), null); - } - log.error("Internal system exception occurred! Exception message: {} ", exception.getMessage(), exception); - response.setStatus(HttpStatus.ERROR.getCode()); - return ResponseBodyBean.ofStatus(HttpStatus.ERROR.getCode(), HttpStatus.ERROR.getMessage(), null); - } - - /** - * Get field error message from exception. If two or more fields do not pass Spring Validation check, then will - * return the 1st error message of the error field. - * - * @param exception MethodArgumentNotValidException - * @return field error message - */ - private String getFieldErrorMessageFromException(MethodArgumentNotValidException exception) { - try { - val firstErrorField = - (DefaultMessageSourceResolvable) Objects.requireNonNull(exception.getBindingResult() - .getAllErrors() - .get(0) - .getArguments())[0]; - val firstErrorFieldName = firstErrorField.getDefaultMessage(); - val firstErrorFieldMessage = exception.getBindingResult().getAllErrors().get(0).getDefaultMessage(); - return String.format("%s %s", firstErrorFieldName, firstErrorFieldMessage); - } catch (Exception e) { - log.error("Exception occurred when get field error message from exception. Exception message: {}", - e.getMessage(), e); - return HttpStatus.INVALID_PARAM.getMessage(); - } - } -} diff --git a/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/aspect/MethodArgumentValidationAspect.java b/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/aspect/MethodArgumentValidationAspect.java deleted file mode 100644 index 24e22502..00000000 --- a/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/aspect/MethodArgumentValidationAspect.java +++ /dev/null @@ -1,160 +0,0 @@ -package com.jmsoftware.maf.serviceregistry.universal.aspect; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.StrUtil; -import lombok.extern.slf4j.Slf4j; -import lombok.val; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.*; -import org.aspectj.lang.reflect.MethodSignature; -import org.springframework.stereotype.Component; - -import javax.validation.ConstraintViolation; -import javax.validation.Valid; -import javax.validation.Validation; -import javax.validation.Validator; -import java.util.LinkedList; -import java.util.Set; - -/** - *

MethodArgumentValidationAspect

- *

This class is an aspect class that validates method's argument(s).

- *

USAGE (Must Do's)

- *
    - *
  1. Annotate the method which argument we need to validate by @ValidateArgument
  2. - *
  3. Annotate the argument(s) that we need to validate by @javax.validation.Valid
  4. - *
  5. The field(s) of the argument(s) could be annotated by the constraint annotation provided by Spring - * Security
  6. - *
- *

ATTENTION

- *

If the argument doesn't pass validation, an IllegalArgumentException will be thrown, and not proceed - * the target method.

- * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-07-06 12:17 - **/ -@Slf4j -@Aspect -@Component -public class MethodArgumentValidationAspect { - private final Validator validator; - - public MethodArgumentValidationAspect() { - val validatorFactory = Validation.buildDefaultValidatorFactory(); - this.validator = validatorFactory.getValidator(); - log.info("The validator for {} has been initiated.", this.getClass().getSimpleName()); - } - - /** - * Define pointcut. Pointcut is a predicate or expression that matches join points. In - * ValidateMethodArgumentAspect, we need to cut any method annotated with `@ValidateArgument` only. - *

- * More detail at: Spring aop aspectJ - * pointcut expression examples - */ - @Pointcut("@annotation(com.jmsoftware.maf.serviceregistry.universal.aspect.ValidateArgument)") - public void validateMethodArgumentPointcut() { - } - - /** - * Before handle method's argument. This method will be executed after called `proceedingJoinPoint.proceed()`. In - * this phrase, we're going to take some logs. - *

- * `@Before` annotated methods run exactly before the all methods matching with pointcut expression. - * - * @param joinPoint a point of execution of the program - */ - @Before("validateMethodArgumentPointcut()") - public void beforeMethodHandleArgument(JoinPoint joinPoint) { - log.info("Method : {}#{}", - joinPoint.getSignature().getDeclaringTypeName(), - joinPoint.getSignature().getName()); - log.info("Argument : {}", joinPoint.getArgs()); - } - - /** - * Around annotated method processes argument. Around advice can perform custom behavior before and after the - * method invocation. - * - * @param proceedingJoinPoint the object can perform method invocation - * @return any value (may be void) that annotated method returned - */ - @Around("validateMethodArgumentPointcut()") - public Object aroundMethodHandleArgument(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { - log.info("======= METHOD'S ARGUMENT VALIDATION START ======="); - val args = proceedingJoinPoint.getArgs(); - val signature = (MethodSignature) proceedingJoinPoint.getSignature(); - val parameterAnnotations = signature.getMethod().getParameterAnnotations(); - // argumentIndexes is the array list that stores the index of argument we need to validate (the argument - // annotated by `@Valid`) - val argumentIndexListThatNeedsToBeValidated = new LinkedList(); - for (val parameterAnnotation : parameterAnnotations) { - val paramIndex = ArrayUtil.indexOf(parameterAnnotations, parameterAnnotation); - for (val annotation : parameterAnnotation) { - if (annotation instanceof Valid) { - argumentIndexListThatNeedsToBeValidated.add(paramIndex); - } - } - } - val errorMessageList = new LinkedList(); - for (val index : argumentIndexListThatNeedsToBeValidated) { - val constraintViolationSet = validator.validate(args[index]); - if (CollUtil.isNotEmpty(constraintViolationSet)) { - val errorMessage = String.format("Argument validation failed! %s", - getAllFieldErrorMessage(constraintViolationSet)); - errorMessageList.add(errorMessage); - } - } - if (CollUtil.isNotEmpty(errorMessageList)) { - val joinedErrorMessage = StrUtil.join(", ", errorMessageList); - log.info("Method : {}#{}", proceedingJoinPoint.getSignature().getDeclaringTypeName(), - proceedingJoinPoint.getSignature().getName()); - log.info("Argument : {}", args); - log.error("Validation result: {}", joinedErrorMessage); - // If the argument doesn't pass validation, an IllegalArgumentException will be thrown, and not - // proceed the target method - throw new IllegalArgumentException(joinedErrorMessage); - } - log.info("Validation result: Validation passed"); - return proceedingJoinPoint.proceed(); - } - - /** - * `@After` annotated methods run exactly after the all methods matching with pointcut expression. - */ - @After("validateMethodArgumentPointcut()") - public void afterMethodHandleArgument() { - log.info("======== METHOD'S ARGUMENT VALIDATION END ========"); - } - - /** - * `@AfterThrowing` annotated methods run after the method (matching with pointcut expression) exits by throwing an - * exception. - * - * @param joinPoint a point of execution of the program - * @param e exception that controller's method throws - */ - @AfterThrowing(pointcut = "validateMethodArgumentPointcut()", throwing = "e") - public void afterThrowingException(JoinPoint joinPoint, Exception e) { - log.info("Signature : {}", joinPoint.getSignature().toShortString()); - log.error("Exception message: {}", e.toString()); - log.error("== METHOD'S ARGUMENT VALIDATION END WITH EXCEPTION =="); - } - - /** - * Gets all field error message. - * - * @param constraintViolationSet the constraint violation set - * @return the all field error message - */ - private String getAllFieldErrorMessage(Set> constraintViolationSet) { - val allErrorMessageList = new LinkedList(); - for (val constraintViolation : constraintViolationSet) { - allErrorMessageList.add(String.format("invalid field: %s, %s", constraintViolation.getPropertyPath(), - constraintViolation.getMessage())); - } - return StrUtil.join("; ", allErrorMessageList); - } -} diff --git a/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/aspect/ValidateArgument.java b/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/aspect/ValidateArgument.java deleted file mode 100644 index 100c0bed..00000000 --- a/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/aspect/ValidateArgument.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.jmsoftware.maf.serviceregistry.universal.aspect; - -import java.lang.annotation.*; - -/** - *

ValidateArgument

- *

Annotation for validating method's argument.

- *

ATTENTION

- *

If the argument doesn't pass validation, an IllegalArgumentException will be thrown, and not proceed - * the target method.

- * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-07-06 12:08 - **/ -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) -@Documented -public @interface ValidateArgument { -} diff --git a/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/aspect/WebRequestLogAspect.java b/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/aspect/WebRequestLogAspect.java deleted file mode 100644 index 69af78eb..00000000 --- a/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/aspect/WebRequestLogAspect.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.jmsoftware.maf.serviceregistry.universal.aspect; - -import cn.hutool.json.JSONUtil; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.jmsoftware.maf.common.util.RequestUtil; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import lombok.val; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.*; -import org.springframework.stereotype.Component; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; - -import java.time.Duration; -import java.time.Instant; - -/** - *

RequestLogAspect

- *

Description:

- *

RequestLogAspect is an AOP for logging URL, HTTP method, client IP and other information when web resource was - * accessed.

- *

Feature:

- *

No methods in controller need to be decorated with annotation. This aspect would automatically cut the method - * decorated with `@GetMapping` or `@PostMapping`.

- * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-05-05 19:55 - **/ -@Slf4j -@Aspect -@Component -@RequiredArgsConstructor -public class WebRequestLogAspect { - private static final int MAX_LENGTH_OF_JSON_STRING = 500; - private static final String LINE_SEPARATOR = System.lineSeparator(); - private final ObjectMapper mapper = new ObjectMapper(); - - /** - * Define pointcut. Pointcut is a predicate or expression that matches join points. In WebRequestLogAspect, we need - * to cut any method annotated with `@GetMapping`, `@PostMapping`, `@PutMapping`, `@DeleteMapping`, `@PatchMapping`, `@RequestMapping`. - *

- * More detail at: Spring aop aspectJ - * pointcut expression examples - */ - @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)" + - " || @annotation(org.springframework.web.bind.annotation.PostMapping)" + - " || @annotation(org.springframework.web.bind.annotation.PutMapping)" + - " || @annotation(org.springframework.web.bind.annotation.DeleteMapping)" + - " || @annotation(org.springframework.web.bind.annotation.PatchMapping)" + - " || @annotation(org.springframework.web.bind.annotation.RequestMapping)") - public void requestLogPointcut() { - } - - /** - * Before controller handle client request (on client sent a request). - *

- * `@Before` annotated methods run exactly before the all methods matching with pointcut expression. - * - * @param joinPoint a point of execution of the program - */ - @Before("requestLogPointcut()") - public void beforeHandleRequest(JoinPoint joinPoint) { - val attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); - assert attributes != null; - val request = attributes.getRequest(); - log.info("============ WEB REQUEST LOG START ============"); - log.info("URL : {}", request.getRequestURL().toString()); - log.info("HTTP Method : {}", request.getMethod()); - log.info("Client IP:Port : {}", RequestUtil.getRequestIpAndPort(request)); - log.info("Class Method : {}#{}", - joinPoint.getSignature().getDeclaringTypeName(), - joinPoint.getSignature().getName()); - log.info("Request Params :{}{}", LINE_SEPARATOR, JSONUtil.toJsonPrettyStr(joinPoint.getArgs())); - } - - /** - * Around controller's method processes client request. Around advice can perform custom behavior before and after - * the method invocation. - * - * @param proceedingJoinPoint the object can perform method invocation - * @return any value (may be void) that controller's method returned - * @throws Throwable any exceptions that controller's method may throw - */ - @Around("requestLogPointcut()") - public Object aroundHandleRequest(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { - val startInstant = Instant.now(); - val result = proceedingJoinPoint.proceed(); - val duration = Duration.between(startInstant, Instant.now()); - try { - var formattedJsonString = JSONUtil.formatJsonStr(mapper.writeValueAsString(result)); - if (formattedJsonString.length() > MAX_LENGTH_OF_JSON_STRING) { - val substring = formattedJsonString.substring(0, MAX_LENGTH_OF_JSON_STRING - 1); - formattedJsonString = - String.format("%s… [The length(%d) of JSON string is larger than the maximum(%d)]", substring, - formattedJsonString.length(), MAX_LENGTH_OF_JSON_STRING); - } - log.info("Response :{}{}", LINE_SEPARATOR, formattedJsonString); - } catch (JsonProcessingException e) { - log.info("Response (non-JSON): {}", result); - } - log.info("Elapsed time : {} ({} ms)", duration, duration.toMillis()); - return result; - } - - /** - * `@After` annotated methods run exactly after the all methods matching with pointcut expression. - */ - @After("requestLogPointcut()") - public void afterHandleRequest() { - log.info("============= WEB REQUEST LOG END ============="); - } - - /** - * `@AfterThrowing` annotated methods run after the method (matching with pointcut expression) exits by throwing an - * exception. - * - * @param joinPoint a point of execution of the program - * @param e exception that controller's method throws - */ - @AfterThrowing(pointcut = "requestLogPointcut()", throwing = "e") - public void afterThrowingException(JoinPoint joinPoint, Exception e) { - log.info("Signature : {}", joinPoint.getSignature().toShortString()); - log.error("Exception : {}, message: {}", e.toString(), e.getMessage()); - log.error("====== WEB REQUEST LOG END WITH EXCEPTION ====="); - } -} diff --git a/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/configuration/ServerConfiguration.java b/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/configuration/ServerConfiguration.java deleted file mode 100644 index 0c75e53d..00000000 --- a/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/configuration/ServerConfiguration.java +++ /dev/null @@ -1,118 +0,0 @@ -package com.jmsoftware.maf.serviceregistry.universal.configuration; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import lombok.val; -import org.springframework.boot.web.context.WebServerInitializedEvent; -import org.springframework.context.ApplicationListener; -import org.springframework.stereotype.Component; - -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.net.Inet4Address; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.URL; -import java.util.Enumeration; - -/** - *

ServerConfiguration

- *

- * Change description here. - * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-04-26 16:02 - **/ -@Slf4j -@Getter -@Component -@RequiredArgsConstructor -public class ServerConfiguration implements ApplicationListener { - private static final String DEVELOPMENT_ENVIRONMENT = "development"; - private final ProjectProperty projectProperty; - private int serverPort; - - @Override - public void onApplicationEvent(WebServerInitializedEvent event) { - this.serverPort = event.getWebServer().getPort(); - } - - /** - *

Get base URL of backend server.

- *

The result will be like:

- *
    - *
  1. http://[serverIp]:[serverPort]/[contextPath]
  2. - *
  3. https://[serverIp]/[contextPath]
  4. - *
- * - * @return base URL - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-05-03 16:05 - */ - public String getBaseUrl() { - return "http://" + this.getPublicIp() + ":" + serverPort + projectProperty.getContextPath(); - } - - /** - * Find public IP address. - * - * @return public IP - */ - public String getPublicIp() { - if (projectProperty.getEnvironment().contains(DEVELOPMENT_ENVIRONMENT)) { - return this.getInternetIp(); - } - try { - // An API provided by https://whatismyipaddress.com/api - val url = new URL("https://ipv4bot.whatismyipaddress.com/"); - val sc = new BufferedReader(new InputStreamReader(url.openStream())); - // Read system IP Address - return sc.readLine().trim(); - } catch (Exception e) { - log.error("Cannot execute properly to get IP address from https://whatismyipaddress.com/api", e); - } - return this.getInternetIp(); - } - - /** - * Get internet IP. - * - * @return internet IP - */ - private String getInternetIp() { - val intranetIp = this.getIntranetIp(); - try { - val networks = NetworkInterface.getNetworkInterfaces(); - InetAddress ip; - Enumeration addresses; - while (networks.hasMoreElements()) { - addresses = networks.nextElement().getInetAddresses(); - while (addresses.hasMoreElements()) { - ip = addresses.nextElement(); - if (ip instanceof Inet4Address - && ip.isSiteLocalAddress() - && !ip.getHostAddress().equals(intranetIp)) { - return ip.getHostAddress(); - } - } - } - return intranetIp; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - /** - * Get intranet IP. - * - * @return intranet IP - */ - private String getIntranetIp() { - try { - return InetAddress.getLocalHost().getHostAddress(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } -} diff --git a/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/configuration/WebMvcConfiguration.java b/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/configuration/WebMvcConfiguration.java deleted file mode 100644 index 345efa69..00000000 --- a/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/configuration/WebMvcConfiguration.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.jmsoftware.maf.serviceregistry.universal.configuration; - -import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.CorsRegistry; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -/** - *

WebMvcConfiguration

- *

- * Spring MVC Configurations. - * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 1/23/20 9:02 AM - **/ -@Configuration -@RequiredArgsConstructor -public class WebMvcConfiguration implements WebMvcConfigurer { - private static final long MAX_AGE_SECS = 3600; - - /** - * 1. Config static path pattern - * 2. Config static resource location - * - * @param registry static resources register - */ - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); - } - - /** - * Configure cross origin requests processing. - * - * @param registry CORS registry - */ - @Override - public void addCorsMappings(CorsRegistry registry) { - registry.addMapping("/**") - .allowedOrigins("*") - .allowedMethods("HEAD", "OPTIONS", "GET", "POST", "PUT", "PATCH", "DELETE") - .maxAge(MAX_AGE_SECS); - } -} diff --git a/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/controller/RedirectController.java b/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/controller/RedirectController.java deleted file mode 100644 index e58befbe..00000000 --- a/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/controller/RedirectController.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.jmsoftware.maf.serviceregistry.universal.controller; - -import com.jmsoftware.maf.serviceregistry.universal.configuration.ProjectProperty; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.PostConstruct; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - *

RedirectController

- *

- * HTTP Redirect Controller - * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 1/21/20 1:18 PM - **/ -@Slf4j -@RestController -@RequiredArgsConstructor -@Api(tags = {"Redirect Controller"}) -public class RedirectController { - private final ProjectProperty projectProperty; - - @PostConstruct - private void postConstruct() { - log.info("URL redirect service initialized."); - } - - @GetMapping("/home") - @ApiOperation(value = "/home", notes = "Home page") - public void handleHomeRequest(HttpServletResponse response) throws IOException { - // Redirect to home page - response.sendRedirect(projectProperty.getContextPath() + "/static/home.html"); - } - - @GetMapping("/doc") - @ApiOperation(value = "/doc", notes = "Swagger API Documentation") - public void handleDocRequest(HttpServletResponse response) throws IOException { - // Redirect to Bootstrap Swagger API documentation - response.sendRedirect(projectProperty.getContextPath() + "/doc.html?cache=1&lang=en"); - } - - @GetMapping("/webjars/bycdao-ui/images/api.ico") - @ApiOperation(value = "/webjars/bycdao-ui/images/api.ico", notes = "Favicon redirection") - public void handleFaviconRequest(HttpServletResponse response) throws IOException { - // Redirect to a customized favicon - response.sendRedirect(projectProperty.getContextPath() + "/static/icon/favicon.ico"); - } -} diff --git a/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/service/CommonService.java b/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/service/CommonService.java index 0faf846e..7a6dc447 100644 --- a/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/service/CommonService.java +++ b/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/service/CommonService.java @@ -1,7 +1,9 @@ package com.jmsoftware.maf.serviceregistry.universal.service; import com.jmsoftware.maf.serviceregistry.universal.domain.ValidationTestPayload; +import org.springframework.validation.annotation.Validated; +import javax.validation.Valid; import java.util.Map; /** @@ -12,6 +14,7 @@ * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com * @date 2/4/20 11:15 AM */ +@Validated public interface CommonService { /** * Gets application info. @@ -25,5 +28,5 @@ public interface CommonService { * * @param payload the payload */ - void validateObject(ValidationTestPayload payload); + void validateObject(@Valid ValidationTestPayload payload); } diff --git a/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/service/impl/CommonServiceImpl.java b/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/service/impl/CommonServiceImpl.java index 5bdef865..29524cbe 100644 --- a/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/service/impl/CommonServiceImpl.java +++ b/service-registry/src/main/java/com/jmsoftware/maf/serviceregistry/universal/service/impl/CommonServiceImpl.java @@ -1,6 +1,5 @@ package com.jmsoftware.maf.serviceregistry.universal.service.impl; -import com.jmsoftware.maf.serviceregistry.universal.aspect.ValidateArgument; import com.jmsoftware.maf.serviceregistry.universal.configuration.ProjectProperty; import com.jmsoftware.maf.serviceregistry.universal.domain.ValidationTestPayload; import com.jmsoftware.maf.serviceregistry.universal.service.CommonService; @@ -44,7 +43,6 @@ public Map getApplicationInfo() { } @Override - @ValidateArgument public void validateObject(@Valid ValidationTestPayload payload) { log.info("Validation passed! {}", payload); } diff --git a/spring-boot-admin/pom.xml b/spring-boot-admin/pom.xml index 66c20050..0f5b2017 100644 --- a/spring-boot-admin/pom.xml +++ b/spring-boot-admin/pom.xml @@ -89,7 +89,10 @@ com.jmsoftware.maf common - 0.0.1-SNAPSHOT + + + com.jmsoftware.maf + muscle-and-fitness-server-spring-boot-starter diff --git a/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/aspect/MethodArgumentValidationAspect.java b/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/aspect/MethodArgumentValidationAspect.java deleted file mode 100644 index 5be20120..00000000 --- a/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/aspect/MethodArgumentValidationAspect.java +++ /dev/null @@ -1,160 +0,0 @@ -package com.jmsoftware.maf.springbootadmin.universal.aspect; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.StrUtil; -import lombok.extern.slf4j.Slf4j; -import lombok.val; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.*; -import org.aspectj.lang.reflect.MethodSignature; -import org.springframework.stereotype.Component; - -import javax.validation.ConstraintViolation; -import javax.validation.Valid; -import javax.validation.Validation; -import javax.validation.Validator; -import java.util.LinkedList; -import java.util.Set; - -/** - *

MethodArgumentValidationAspect

- *

This class is an aspect class that validates method's argument(s).

- *

USAGE (Must Do's)

- *
    - *
  1. Annotate the method which argument we need to validate by @ValidateArgument
  2. - *
  3. Annotate the argument(s) that we need to validate by @javax.validation.Valid
  4. - *
  5. The field(s) of the argument(s) could be annotated by the constraint annotation provided by Spring - * Security
  6. - *
- *

ATTENTION

- *

If the argument doesn't pass validation, an IllegalArgumentException will be thrown, and not proceed - * the target method.

- * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-07-06 12:17 - **/ -@Slf4j -@Aspect -@Component -public class MethodArgumentValidationAspect { - private final Validator validator; - - public MethodArgumentValidationAspect() { - val validatorFactory = Validation.buildDefaultValidatorFactory(); - this.validator = validatorFactory.getValidator(); - log.info("The validator for {} has been initiated.", this.getClass().getSimpleName()); - } - - /** - * Define pointcut. Pointcut is a predicate or expression that matches join points. In - * ValidateMethodArgumentAspect, we need to cut any method annotated with `@ValidateArgument` only. - *

- * More detail at: Spring aop aspectJ - * pointcut expression examples - */ - @Pointcut("@annotation(com.jmsoftware.maf.springbootadmin.universal.aspect.ValidateArgument)") - public void validateMethodArgumentPointcut() { - } - - /** - * Before handle method's argument. This method will be executed after called `proceedingJoinPoint.proceed()`. In - * this phrase, we're going to take some logs. - *

- * `@Before` annotated methods run exactly before the all methods matching with pointcut expression. - * - * @param joinPoint a point of execution of the program - */ - @Before("validateMethodArgumentPointcut()") - public void beforeMethodHandleArgument(JoinPoint joinPoint) { - log.info("Method : {}#{}", - joinPoint.getSignature().getDeclaringTypeName(), - joinPoint.getSignature().getName()); - log.info("Argument : {}", joinPoint.getArgs()); - } - - /** - * Around annotated method processes argument. Around advice can perform custom behavior before and after the - * method invocation. - * - * @param proceedingJoinPoint the object can perform method invocation - * @return any value (may be void) that annotated method returned - */ - @Around("validateMethodArgumentPointcut()") - public Object aroundMethodHandleArgument(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { - log.info("======= METHOD'S ARGUMENT VALIDATION START ======="); - val args = proceedingJoinPoint.getArgs(); - val signature = (MethodSignature) proceedingJoinPoint.getSignature(); - val parameterAnnotations = signature.getMethod().getParameterAnnotations(); - // argumentIndexes is the array list that stores the index of argument we need to validate (the argument - // annotated by `@Valid`) - val argumentIndexListThatNeedsToBeValidated = new LinkedList(); - for (val parameterAnnotation : parameterAnnotations) { - val paramIndex = ArrayUtil.indexOf(parameterAnnotations, parameterAnnotation); - for (val annotation : parameterAnnotation) { - if (annotation instanceof Valid) { - argumentIndexListThatNeedsToBeValidated.add(paramIndex); - } - } - } - val errorMessageList = new LinkedList(); - for (val index : argumentIndexListThatNeedsToBeValidated) { - val constraintViolationSet = validator.validate(args[index]); - if (CollUtil.isNotEmpty(constraintViolationSet)) { - val errorMessage = String.format("Argument validation failed! %s", - getAllFieldErrorMessage(constraintViolationSet)); - errorMessageList.add(errorMessage); - } - } - if (CollUtil.isNotEmpty(errorMessageList)) { - val joinedErrorMessage = StrUtil.join(", ", errorMessageList); - log.info("Method : {}#{}", proceedingJoinPoint.getSignature().getDeclaringTypeName(), - proceedingJoinPoint.getSignature().getName()); - log.info("Argument : {}", args); - log.error("Validation result: {}", joinedErrorMessage); - // If the argument doesn't pass validation, an IllegalArgumentException will be thrown, and not - // proceed the target method - throw new IllegalArgumentException(joinedErrorMessage); - } - log.info("Validation result: Validation passed"); - return proceedingJoinPoint.proceed(); - } - - /** - * `@After` annotated methods run exactly after the all methods matching with pointcut expression. - */ - @After("validateMethodArgumentPointcut()") - public void afterMethodHandleArgument() { - log.info("======== METHOD'S ARGUMENT VALIDATION END ========"); - } - - /** - * `@AfterThrowing` annotated methods run after the method (matching with pointcut expression) exits by throwing an - * exception. - * - * @param joinPoint a point of execution of the program - * @param e exception that controller's method throws - */ - @AfterThrowing(pointcut = "validateMethodArgumentPointcut()", throwing = "e") - public void afterThrowingException(JoinPoint joinPoint, Exception e) { - log.info("Signature : {}", joinPoint.getSignature().toShortString()); - log.error("Exception message: {}", e.toString()); - log.error("== METHOD'S ARGUMENT VALIDATION END WITH EXCEPTION =="); - } - - /** - * Gets all field error message. - * - * @param constraintViolationSet the constraint violation set - * @return the all field error message - */ - private String getAllFieldErrorMessage(Set> constraintViolationSet) { - val allErrorMessageList = new LinkedList(); - for (val constraintViolation : constraintViolationSet) { - allErrorMessageList.add(String.format("invalid field: %s, %s", constraintViolation.getPropertyPath(), - constraintViolation.getMessage())); - } - return StrUtil.join("; ", allErrorMessageList); - } -} diff --git a/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/aspect/ValidateArgument.java b/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/aspect/ValidateArgument.java deleted file mode 100644 index eeea8d37..00000000 --- a/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/aspect/ValidateArgument.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.jmsoftware.maf.springbootadmin.universal.aspect; - -import java.lang.annotation.*; - -/** - *

ValidateArgument

- *

Annotation for validating method's argument.

- *

ATTENTION

- *

If the argument doesn't pass validation, an IllegalArgumentException will be thrown, and not proceed - * the target method.

- * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-07-06 12:08 - **/ -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) -@Documented -public @interface ValidateArgument { -} diff --git a/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/aspect/WebRequestLogAspect.java b/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/aspect/WebRequestLogAspect.java deleted file mode 100644 index c56d7c6e..00000000 --- a/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/aspect/WebRequestLogAspect.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.jmsoftware.maf.springbootadmin.universal.aspect; - -import cn.hutool.json.JSONUtil; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.jmsoftware.maf.common.util.RequestUtil; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import lombok.val; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.*; -import org.springframework.stereotype.Component; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; - -import java.time.Duration; -import java.time.Instant; - -/** - *

RequestLogAspect

- *

Description:

- *

RequestLogAspect is an AOP for logging URL, HTTP method, client IP and other information when web resource was - * accessed.

- *

Feature:

- *

No methods in controller need to be decorated with annotation. This aspect would automatically cut the method - * decorated with `@GetMapping` or `@PostMapping`.

- * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-05-05 19:55 - **/ -@Slf4j -@Aspect -@Component -@RequiredArgsConstructor -public class WebRequestLogAspect { - private static final int MAX_LENGTH_OF_JSON_STRING = 500; - private static final String LINE_SEPARATOR = System.lineSeparator(); - private final ObjectMapper mapper = new ObjectMapper(); - - /** - * Define pointcut. Pointcut is a predicate or expression that matches join points. In WebRequestLogAspect, we need - * to cut any method annotated with `@GetMapping`, `@PostMapping`, `@PutMapping`, `@DeleteMapping`, `@PatchMapping`, `@RequestMapping`. - *

- * More detail at: Spring aop aspectJ - * pointcut expression examples - */ - @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)" + - " || @annotation(org.springframework.web.bind.annotation.PostMapping)" + - " || @annotation(org.springframework.web.bind.annotation.PutMapping)" + - " || @annotation(org.springframework.web.bind.annotation.DeleteMapping)" + - " || @annotation(org.springframework.web.bind.annotation.PatchMapping)" + - " || @annotation(org.springframework.web.bind.annotation.RequestMapping)") - public void requestLogPointcut() { - } - - /** - * Before controller handle client request (on client sent a request). - *

- * `@Before` annotated methods run exactly before the all methods matching with pointcut expression. - * - * @param joinPoint a point of execution of the program - */ - @Before("requestLogPointcut()") - public void beforeHandleRequest(JoinPoint joinPoint) { - val attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); - assert attributes != null; - val request = attributes.getRequest(); - log.info("============ WEB REQUEST LOG START ============"); - log.info("URL : {}", request.getRequestURL().toString()); - log.info("HTTP Method : {}", request.getMethod()); - log.info("Client IP:Port : {}", RequestUtil.getRequestIpAndPort(request)); - log.info("Class Method : {}#{}", - joinPoint.getSignature().getDeclaringTypeName(), - joinPoint.getSignature().getName()); - log.info("Request Params :{}{}", LINE_SEPARATOR, JSONUtil.toJsonPrettyStr(joinPoint.getArgs())); - } - - /** - * Around controller's method processes client request. Around advice can perform custom behavior before and after - * the method invocation. - * - * @param proceedingJoinPoint the object can perform method invocation - * @return any value (may be void) that controller's method returned - * @throws Throwable any exceptions that controller's method may throw - */ - @Around("requestLogPointcut()") - public Object aroundHandleRequest(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { - val startInstant = Instant.now(); - val result = proceedingJoinPoint.proceed(); - val duration = Duration.between(startInstant, Instant.now()); - try { - var formattedJsonString = JSONUtil.formatJsonStr(mapper.writeValueAsString(result)); - if (formattedJsonString.length() > MAX_LENGTH_OF_JSON_STRING) { - val substring = formattedJsonString.substring(0, MAX_LENGTH_OF_JSON_STRING - 1); - formattedJsonString = - String.format("%s… [The length(%d) of JSON string is larger than the maximum(%d)]", substring, - formattedJsonString.length(), MAX_LENGTH_OF_JSON_STRING); - } - log.info("Response :{}{}", LINE_SEPARATOR, formattedJsonString); - } catch (JsonProcessingException e) { - log.info("Response (non-JSON): {}", result); - } - log.info("Elapsed time : {} ({} ms)", duration, duration.toMillis()); - return result; - } - - /** - * `@After` annotated methods run exactly after the all methods matching with pointcut expression. - */ - @After("requestLogPointcut()") - public void afterHandleRequest() { - log.info("============= WEB REQUEST LOG END ============="); - } - - /** - * `@AfterThrowing` annotated methods run after the method (matching with pointcut expression) exits by throwing an - * exception. - * - * @param joinPoint a point of execution of the program - * @param e exception that controller's method throws - */ - @AfterThrowing(pointcut = "requestLogPointcut()", throwing = "e") - public void afterThrowingException(JoinPoint joinPoint, Exception e) { - log.info("Signature : {}", joinPoint.getSignature().toShortString()); - log.error("Exception : {}, message: {}", e.toString(), e.getMessage()); - log.error("====== WEB REQUEST LOG END WITH EXCEPTION ====="); - } -} diff --git a/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/controller/RedirectController.java b/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/controller/RedirectController.java deleted file mode 100644 index b59e67f7..00000000 --- a/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/controller/RedirectController.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.jmsoftware.maf.springbootadmin.universal.controller; - -import com.jmsoftware.maf.springbootadmin.universal.configuration.ProjectProperty; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.PostConstruct; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - *

RedirectController

- *

- * HTTP Redirect Controller - * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 1/21/20 1:18 PM - **/ -@Slf4j -@RestController -@RequiredArgsConstructor -@Api(tags = {"Redirect Controller"}) -public class RedirectController { - private final ProjectProperty projectProperty; - - @PostConstruct - private void postConstruct() { - log.info("URL redirect service initialized."); - } - - @GetMapping("/home") - @ApiOperation(value = "/home", notes = "Home page") - public void handleHomeRequest(HttpServletResponse response) throws IOException { - // Redirect to home page - response.sendRedirect(projectProperty.getContextPath() + "/static/home.html"); - } - - @GetMapping("/doc") - @ApiOperation(value = "/doc", notes = "Swagger API Documentation") - public void handleDocRequest(HttpServletResponse response) throws IOException { - // Redirect to Bootstrap Swagger API documentation - response.sendRedirect(projectProperty.getContextPath() + "/doc.html?cache=1&lang=en"); - } - - @GetMapping("/webjars/bycdao-ui/images/api.ico") - @ApiOperation(value = "/webjars/bycdao-ui/images/api.ico", notes = "Favicon redirection") - public void handleFaviconRequest(HttpServletResponse response) throws IOException { - // Redirect to a customized favicon - response.sendRedirect(projectProperty.getContextPath() + "/static/icon/favicon.ico"); - } -} diff --git a/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/filter/RequestFilter.java b/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/filter/RequestFilter.java deleted file mode 100644 index fa3ead54..00000000 --- a/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/filter/RequestFilter.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.jmsoftware.maf.springbootadmin.universal.filter; - -import com.jmsoftware.maf.common.util.RequestUtil; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - *

RequestFilter

- *

Request filter.

- * - * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-03-23 14:24 - **/ -@Slf4j -@Component -public class RequestFilter extends OncePerRequestFilter { - @Override - @SuppressWarnings("NullableProblems") - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, - FilterChain filterChain) throws IOException, ServletException { - log.info("The requester({}) requested resource. Resource: [{}] {}", RequestUtil.getRequestIpAndPort(request), - request.getMethod(), request.getRequestURL()); - filterChain.doFilter(request, response); - } -} diff --git a/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/service/CommonService.java b/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/service/CommonService.java index d47a978e..fb7d830f 100644 --- a/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/service/CommonService.java +++ b/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/service/CommonService.java @@ -1,7 +1,9 @@ package com.jmsoftware.maf.springbootadmin.universal.service; import com.jmsoftware.maf.springbootadmin.universal.domain.ValidationTestPayload; +import org.springframework.validation.annotation.Validated; +import javax.validation.Valid; import java.util.Map; /** @@ -12,6 +14,7 @@ * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com * @date 2/4/20 11:15 AM */ +@Validated public interface CommonService { /** * Gets application info. @@ -25,5 +28,5 @@ public interface CommonService { * * @param payload the payload */ - void validateObject(ValidationTestPayload payload); + void validateObject(@Valid ValidationTestPayload payload); } diff --git a/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/service/impl/CommonServiceImpl.java b/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/service/impl/CommonServiceImpl.java index 0e07b0be..e4d7f1c7 100644 --- a/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/service/impl/CommonServiceImpl.java +++ b/spring-boot-admin/src/main/java/com/jmsoftware/maf/springbootadmin/universal/service/impl/CommonServiceImpl.java @@ -1,6 +1,5 @@ package com.jmsoftware.maf.springbootadmin.universal.service.impl; -import com.jmsoftware.maf.springbootadmin.universal.aspect.ValidateArgument; import com.jmsoftware.maf.springbootadmin.universal.configuration.ProjectProperty; import com.jmsoftware.maf.springbootadmin.universal.domain.ValidationTestPayload; import com.jmsoftware.maf.springbootadmin.universal.service.CommonService; @@ -44,7 +43,6 @@ public Map getApplicationInfo() { } @Override - @ValidateArgument public void validateObject(@Valid ValidationTestPayload payload) { log.info("Validation passed! {}", payload); }