diff --git a/boot-api-archunit-sample/src/main/java/com/example/archunit/config/Initializer.java b/boot-api-archunit-sample/src/main/java/com/example/archunit/config/Initializer.java index b691386c4..d7250854d 100644 --- a/boot-api-archunit-sample/src/main/java/com/example/archunit/config/Initializer.java +++ b/boot-api-archunit-sample/src/main/java/com/example/archunit/config/Initializer.java @@ -1,19 +1,21 @@ package com.example.archunit.config; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Component @RequiredArgsConstructor -@Slf4j public class Initializer implements CommandLineRunner { + private static final Logger LOGGER = LoggerFactory.getLogger(Initializer.class); + private final ApplicationProperties properties; @Override public void run(String... args) { - log.info("Running Initializer....."); + LOGGER.info("Running Initializer....."); } } diff --git a/boot-api-archunit-sample/src/main/java/com/example/archunit/config/SwaggerConfig.java b/boot-api-archunit-sample/src/main/java/com/example/archunit/config/SwaggerConfig.java index 1f24e7256..455c332bb 100644 --- a/boot-api-archunit-sample/src/main/java/com/example/archunit/config/SwaggerConfig.java +++ b/boot-api-archunit-sample/src/main/java/com/example/archunit/config/SwaggerConfig.java @@ -1,10 +1,24 @@ package com.example.archunit.config; -import io.swagger.v3.oas.annotations.OpenAPIDefinition; -import io.swagger.v3.oas.annotations.info.Info; -import io.swagger.v3.oas.annotations.servers.Server; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.servers.Server; +import java.util.List; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration(proxyBeanMethods = false) -@OpenAPIDefinition(info = @Info(title = "boot-api-archunit-sample", version = "v1"), servers = @Server(url = "/")) -public class SwaggerConfig {} +public class SwaggerConfig { + + @Bean + OpenAPI openApi() { + return new OpenAPI() + .info(new Info() + .title("boot-api-archunit-sample") + .description("Client Service APIs") + .version("v1.0.0") + .contact(new Contact().name("Raja Kolli").email("rajakolli@gmail.com"))) + .servers(List.of(new Server().url("/"))); + } +} diff --git a/boot-api-archunit-sample/src/main/java/com/example/archunit/config/logging/LoggingAspect.java b/boot-api-archunit-sample/src/main/java/com/example/archunit/config/logging/LoggingAspect.java index 90eb4f0c9..2a4d71d13 100644 --- a/boot-api-archunit-sample/src/main/java/com/example/archunit/config/logging/LoggingAspect.java +++ b/boot-api-archunit-sample/src/main/java/com/example/archunit/config/logging/LoggingAspect.java @@ -15,9 +15,9 @@ @Aspect @Component -public class LoggingAspect { +class LoggingAspect { - private final Logger log = LoggerFactory.getLogger(this.getClass()); + private static final Logger LOGGER = LoggerFactory.getLogger(LoggingAspect.class); private final Environment env; @@ -41,7 +41,7 @@ public void applicationPackagePointcut() { @AfterThrowing(pointcut = "applicationPackagePointcut()", throwing = "e") public void logAfterThrowing(JoinPoint joinPoint, Throwable e) { if (env.acceptsProfiles(Profiles.of(AppConstants.PROFILE_NOT_PROD))) { - log.error( + LOGGER.error( "Exception in {}.{}() with cause = '{}' and exception = '{}'", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), @@ -50,7 +50,7 @@ public void logAfterThrowing(JoinPoint joinPoint, Throwable e) { e); } else { - log.error( + LOGGER.error( "Exception in {}.{}() with cause = {}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), @@ -60,8 +60,8 @@ public void logAfterThrowing(JoinPoint joinPoint, Throwable e) { @Around("applicationPackagePointcut()") public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { - if (log.isTraceEnabled()) { - log.trace( + if (LOGGER.isTraceEnabled()) { + LOGGER.trace( "Enter: {}.{}()", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName()); @@ -69,8 +69,8 @@ public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.currentTimeMillis(); Object result = joinPoint.proceed(); long end = System.currentTimeMillis(); - if (log.isTraceEnabled()) { - log.trace( + if (LOGGER.isTraceEnabled()) { + LOGGER.trace( "Exit: {}.{}(). Time taken: {} millis", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), diff --git a/boot-api-archunit-sample/src/main/java/com/example/archunit/web/controllers/ClientController.java b/boot-api-archunit-sample/src/main/java/com/example/archunit/web/controllers/ClientController.java index 291f74e0b..f9ba14951 100644 --- a/boot-api-archunit-sample/src/main/java/com/example/archunit/web/controllers/ClientController.java +++ b/boot-api-archunit-sample/src/main/java/com/example/archunit/web/controllers/ClientController.java @@ -26,12 +26,12 @@ @RestController @RequestMapping("/api/clients") @RequiredArgsConstructor -public class ClientController { +class ClientController { private final ClientService clientService; @GetMapping - public ResponseEntity> getAllClients( + ResponseEntity> getAllClients( @RequestParam(defaultValue = AppConstants.DEFAULT_PAGE_NUMBER, required = false) int pageNo, @RequestParam(defaultValue = AppConstants.DEFAULT_PAGE_SIZE, required = false) int pageSize, @RequestParam(defaultValue = AppConstants.DEFAULT_SORT_BY, required = false) String sortBy, @@ -41,7 +41,7 @@ public ResponseEntity> getAllClients( } @GetMapping("/{id}") - public ResponseEntity getClientById(@PathVariable Long id) { + ResponseEntity getClientById(@PathVariable Long id) { return clientService .findClientById(id) .map(ResponseEntity::ok) @@ -49,7 +49,7 @@ public ResponseEntity getClientById(@PathVariable Long id) { } @PostMapping - public ResponseEntity createClient(@RequestBody @Validated ClientRequest clientRequest) { + ResponseEntity createClient(@RequestBody @Validated ClientRequest clientRequest) { ClientResponse response = clientService.saveClient(clientRequest); URI location = ServletUriComponentsBuilder.fromCurrentRequest() .path("/api/clients/{id}") @@ -59,13 +59,13 @@ public ResponseEntity createClient(@RequestBody @Validated Clien } @PutMapping("/{id}") - public ResponseEntity updateClient( + ResponseEntity updateClient( @PathVariable Long id, @RequestBody @Valid ClientRequest clientRequest) { return ResponseEntity.ok(clientService.updateClient(id, clientRequest)); } @DeleteMapping("/{id}") - public ResponseEntity deleteClient(@PathVariable Long id) { + ResponseEntity deleteClient(@PathVariable Long id) { return clientService .findClientById(id) .map(client -> { diff --git a/boot-api-archunit-sample/src/test/java/com/example/archunit/architecture/CommonRules.java b/boot-api-archunit-sample/src/test/java/com/example/archunit/architecture/CommonRules.java index 2caac37bb..fb92e659c 100644 --- a/boot-api-archunit-sample/src/test/java/com/example/archunit/architecture/CommonRules.java +++ b/boot-api-archunit-sample/src/test/java/com/example/archunit/architecture/CommonRules.java @@ -161,6 +161,16 @@ static ArchRule privateMethodsAreNotAllowedRule(String packageName) { .because("Private methods are not allowed in %s".formatted(packageName)); } + static ArchRule publicMethodsAreNotAllowedRule(String packageName) { + return methods() + .that() + .areDeclaredInClassesThat() + .resideInAPackage(packageName) + .should() + .notBePublic() + .because("Public methods are not allowed in %s".formatted(packageName)); + } + static ArchRule staticMethodsAreNotAllowedRule(String packageName) { return methods() .that() diff --git a/boot-api-archunit-sample/src/test/java/com/example/archunit/architecture/ControllerRulesTest.java b/boot-api-archunit-sample/src/test/java/com/example/archunit/architecture/ControllerRulesTest.java index a0c8dedee..b8b5455af 100644 --- a/boot-api-archunit-sample/src/test/java/com/example/archunit/architecture/ControllerRulesTest.java +++ b/boot-api-archunit-sample/src/test/java/com/example/archunit/architecture/ControllerRulesTest.java @@ -9,6 +9,7 @@ import static com.example.archunit.architecture.CommonRules.fieldsShouldNotBePublic; import static com.example.archunit.architecture.CommonRules.privateMethodsAreNotAllowedRule; import static com.example.archunit.architecture.CommonRules.publicConstructorsRule; +import static com.example.archunit.architecture.CommonRules.publicMethodsAreNotAllowedRule; import static com.example.archunit.architecture.CommonRules.staticMethodsAreNotAllowedRule; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.methods; @@ -44,6 +45,14 @@ class ControllerRulesTest { .because(ANNOTATED_EXPLANATION.formatted(CONTROLLER_SUFFIX, "@RestController") + ", and not with @Controller"); + @ArchTest + static final ArchRule classes_should_not_be_public = classes() + .that() + .resideInAPackage(CONTROLLER_PACKAGE) + .should() + .bePackagePrivate() + .because("Classes in %s package should be declared package-private"); + // Fields @ArchTest static final ArchRule fields_should_not_be_public = fieldsShouldNotBePublic(CONTROLLER_PACKAGE); @@ -59,13 +68,16 @@ class ControllerRulesTest { @ArchTest static final ArchRule private_methods_are_not_allowed = privateMethodsAreNotAllowedRule(CONTROLLER_PACKAGE); + @ArchTest + static final ArchRule public_methods_are_not_allowed = publicMethodsAreNotAllowedRule(CONTROLLER_PACKAGE); + @ArchTest static final ArchRule static_methods_are_not_allowed = staticMethodsAreNotAllowedRule(CONTROLLER_PACKAGE); @ArchTest static final ArchRule methods_should_return_response_entity = methods() .that() - .arePublic() + .arePackagePrivate() .and() .areDeclaredInClassesThat() .resideInAPackage(CONTROLLER_PACKAGE) @@ -76,7 +88,7 @@ class ControllerRulesTest { @ArchTest static final ArchRule methods_should_be_annotated_with_valid_annotations = methods() .that() - .arePublic() + .arePackagePrivate() .and() .areDeclaredInClassesThat() .resideInAPackage(CONTROLLER_PACKAGE) diff --git a/boot-api-archunit-sample/src/test/java/com/example/archunit/architecture/GeneralCodingRulesTest.java b/boot-api-archunit-sample/src/test/java/com/example/archunit/architecture/GeneralCodingRulesTest.java index f0b059d11..23e5520c7 100644 --- a/boot-api-archunit-sample/src/test/java/com/example/archunit/architecture/GeneralCodingRulesTest.java +++ b/boot-api-archunit-sample/src/test/java/com/example/archunit/architecture/GeneralCodingRulesTest.java @@ -1,6 +1,8 @@ package com.example.archunit.architecture; import static com.example.archunit.architecture.ArchitectureConstants.DEFAULT_PACKAGE; +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.fields; +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.methods; import static com.tngtech.archunit.library.GeneralCodingRules.NO_CLASSES_SHOULD_ACCESS_STANDARD_STREAMS; import static com.tngtech.archunit.library.GeneralCodingRules.NO_CLASSES_SHOULD_THROW_GENERIC_EXCEPTIONS; import static com.tngtech.archunit.library.GeneralCodingRules.NO_CLASSES_SHOULD_USE_JAVA_UTIL_LOGGING; @@ -10,6 +12,8 @@ import com.tngtech.archunit.junit.AnalyzeClasses; import com.tngtech.archunit.junit.ArchTest; import com.tngtech.archunit.lang.ArchRule; +import org.slf4j.Logger; +import org.springframework.context.annotation.Bean; @AnalyzeClasses(packages = DEFAULT_PACKAGE, importOptions = ImportOption.DoNotIncludeTests.class) class GeneralCodingRulesTest { @@ -30,28 +34,35 @@ class GeneralCodingRulesTest { static final ArchRule noJavaUtilLogging = NO_CLASSES_SHOULD_USE_JAVA_UTIL_LOGGING.because("Use org.slf4j.Logger instead"); - /* - //Fields + // Fields @ArchTest - static final ArchRule loggersShouldBePrivateStaticAndFinal = fields().that().haveRawType(Logger.class) - .should().bePrivate() - .andShould().beStatic() - .andShould().beFinal() - .andShould().haveName("LOGGER") + static final ArchRule loggersShouldBePrivateStaticAndFinal = fields().that() + .haveRawType(Logger.class) + .should() + .bePrivate() + .andShould() + .beStatic() + .andShould() + .beFinal() + .andShould() + .haveName("LOGGER") .because("Logger variables should be private, static and final, and it should be named as LOGGER"); - + /* @ArchTest static final ArchRule finalStaticVariablesInUppercase = fields().that().areStatic().and().areFinal() .and().doNotHaveName("serialVersionUID") .and().doNotHaveModifier(JavaModifier.SYNTHETIC) .should().haveNameMatching(".*^[A-Z].*") .because("Variables with static and final modifiers should be named in uppercase"); - - //Methods - @ArchTest - static final ArchRule beanMethodsShouldBePublic = methods().that().areAnnotatedWith(Bean.class).should().bePublic() - .because("@Bean annotation does not work in non public methods"); */ + // Methods + @ArchTest + static final ArchRule beanMethodsShouldBePublic = methods() + .that() + .areAnnotatedWith(Bean.class) + .should() + .bePackagePrivate() + .because("@Bean annotation should not be declared as public methods"); }