From 24f193559e8c59f17f9c2c44d887d01b81d19290 Mon Sep 17 00:00:00 2001 From: Vladislav Sehtman Date: Mon, 13 Feb 2023 14:43:26 +0100 Subject: [PATCH 01/11] Added E1,E3 and E4 --- .../ThirdPartyRulesE1TransactionalTest.java | 57 +++++++++ .../ThirdPartyRulesE3JpaCheckTest.java | 50 ++++++++ .../ThirdPartyRulesE4HibernateCheckTest.java | 110 ++++++++++++++++++ 3 files changed, 217 insertions(+) create mode 100644 src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java create mode 100644 src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java create mode 100644 src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java diff --git a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java new file mode 100644 index 0000000..ab182c1 --- /dev/null +++ b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java @@ -0,0 +1,57 @@ +package com.devonfw.sample.archunit; + +import com.tngtech.archunit.core.domain.Dependency; +import com.tngtech.archunit.core.domain.JavaClass; +import com.tngtech.archunit.core.importer.ImportOption; +import com.tngtech.archunit.junit.AnalyzeClasses; +import com.tngtech.archunit.junit.ArchTest; +import com.tngtech.archunit.lang.ArchCondition; +import com.tngtech.archunit.lang.ArchRule; +import com.tngtech.archunit.lang.ConditionEvents; +import com.tngtech.archunit.lang.SimpleConditionEvent; + +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; + +@AnalyzeClasses(packages = "com.devonfw.sample.archunit", importOptions = ImportOption.DoNotIncludeTests.class) +public class ThirdPartyRulesE1TransactionalTest { + + private static boolean apiScopedClassIsUsingTransactional(JavaClass item, String targetPackageFullName) { + if(item.getFullName().contains("api") && targetPackageFullName.equals("javax.transaction.Transactional")) { + return true; + } + return false; + } + + private static boolean isUsingSpringframeworkTransactionalAnnotation(String targetPackageFullName) { + if(targetPackageFullName.equals("org.springframework.transaction.annotation.Transactional")) { + return true; + } + return false; + } + + static ArchCondition miuse_springframework_transactional_annotation = new ArchCondition ("misuse @Transactional") { + @Override + public void check(JavaClass item, ConditionEvents events) { + for(Dependency access: item.getDirectDependenciesFromSelf()) { + String targetPackageFullName = access.getTargetClass().getFullName(); + String targetClassDescription = access.getDescription(); + if(isUsingSpringframeworkTransactionalAnnotation(targetPackageFullName) == true) { + String message = String.format("Use JEE standard (javax.transaction.Transactional from javax.transaction:javax.transaction-api:1.2+). The use (%s) is discouraged. Violated in (%s)", targetPackageFullName, targetClassDescription); + events.add(new SimpleConditionEvent(item, true, message)); + } + if(apiScopedClassIsUsingTransactional(item, targetPackageFullName) == true) { + String message = String.format("The use of @Transactional in API is discouraged. Instead use it to annotate implementations. Violated in (%s)", targetClassDescription); + events.add(new SimpleConditionEvent(item, true, message)); + } + } + } + }; + + @ArchTest + static final ArchRule verifying_proper_transactional_use_from_jee = + noClasses() + .should(miuse_springframework_transactional_annotation) + .allowEmptyShould(true); + + +} diff --git a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java new file mode 100644 index 0000000..baa53c6 --- /dev/null +++ b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java @@ -0,0 +1,50 @@ +package com.devonfw.sample.archunit; + +import com.tngtech.archunit.core.domain.Dependency; +import com.tngtech.archunit.core.domain.JavaClass; +import com.tngtech.archunit.core.importer.ImportOption; +import com.tngtech.archunit.junit.AnalyzeClasses; +import com.tngtech.archunit.junit.ArchTest; +import com.tngtech.archunit.lang.ArchCondition; +import com.tngtech.archunit.lang.ArchRule; +import com.tngtech.archunit.lang.ConditionEvents; +import com.tngtech.archunit.lang.SimpleConditionEvent; + +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; + +@AnalyzeClasses(packages = "com.devonfw.sample.archunit", importOptions = ImportOption.DoNotIncludeTests.class) +public class ThirdPartyRulesE3JpaCheckTest { + + private static boolean isUsingJavaxPersistenceDataAccessOrEmbeddablesInCommon(JavaClass item, String targetPackageFullName) { + if(targetPackageFullName.startsWith("javax.persistence")) { + if(item.getFullName().contains("dataaccess")) { + return true; + } + if(item.getFullName().contains("common") && item.getSimpleName().contains("Embeddable")) { + return true; + } + return false; + } + return true; + } + + static ArchCondition misuse_jpa = new ArchCondition ("use JPA outside of dataaccess layer or embeddables in common layer") { + @Override + public void check(JavaClass item, ConditionEvents events) { + for(Dependency access: item.getDirectDependenciesFromSelf()) { + String targetPackageFullName = access.getTargetClass().getFullName(); + String targetClassDescription = access.getDescription(); + if(isUsingJavaxPersistenceDataAccessOrEmbeddablesInCommon(item, targetPackageFullName) == false) { + String message = String.format("JPA (%s) shall only be used in dataaccess layer or for embeddables in common layer. Violated in (%s)", targetPackageFullName, targetClassDescription); + events.add(new SimpleConditionEvent(item, true, message)); + } + } + } + }; + + @ArchTest + static final ArchRule verifying_proper_jpa_use = + noClasses() + .should(misuse_jpa) + .allowEmptyShould(true); +} diff --git a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java new file mode 100644 index 0000000..bd30a97 --- /dev/null +++ b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java @@ -0,0 +1,110 @@ +package com.devonfw.sample.archunit; + +import com.tngtech.archunit.core.domain.Dependency; +import com.tngtech.archunit.core.domain.JavaClass; +import com.tngtech.archunit.core.importer.ImportOption; +import com.tngtech.archunit.junit.AnalyzeClasses; +import com.tngtech.archunit.junit.ArchTest; +import com.tngtech.archunit.lang.ArchCondition; +import com.tngtech.archunit.lang.ArchRule; +import com.tngtech.archunit.lang.ConditionEvents; +import com.tngtech.archunit.lang.SimpleConditionEvent; + +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * {@link DevonArchitecture3rdPartyCheck} verifying that the {@code JPA} is properly used. + */ +@AnalyzeClasses(packages = "com.devonfw.sample.archunit", importOptions = ImportOption.DoNotIncludeTests.class) +public class ThirdPartyRulesE4HibernateCheckTest { + + private static final Set DISCOURAGED_HIBERNATE_ANNOTATIONS = new HashSet<>( + Arrays.asList("OrderBy", "Entity", "AccessType", "ForeignKey", "Cascade", "Index", "IndexColumn")); + + private static final String ORG_HIBERNATE_ENVERS = "org.hibernate.envers"; + + private static final String ORG_HIBERNATE_VALIDATOR = "org.hibernate.validator"; + + private static final String ORG_HIBERNATE_ANNOTATIONS = "org.hibernate.annotations"; + + private static boolean isUsingHibernateOutsideOfDataaccessLayer(JavaClass item, ConditionEvents events, String targetPackageName, String targetPackageFullName, String targetClassDescription) { + if(!item.getPackageName().contains("dataaccess") && targetPackageName.startsWith("org.hibernate") && !targetPackageName.startsWith(ORG_HIBERNATE_VALIDATOR)) { + return true; + } + return false; + } + + private static boolean isUsingProprietaryHibernateAnnotation(JavaClass item, ConditionEvents events, String targetPackageName, String targetPackageFullName, String targetClassDescription, String targetSimpleName) { + if(targetPackageName.equals(ORG_HIBERNATE_ANNOTATIONS) && DISCOURAGED_HIBERNATE_ANNOTATIONS.contains(targetSimpleName)) { + return true; + } + return false; + } + + private static boolean isNotImplementingHibernateEnversInImplScope(JavaClass item, ConditionEvents events, String targetPackageName, String targetPackageFullName, String targetClassDescription, String targetSimpleName) { + if(targetPackageName.startsWith(ORG_HIBERNATE_ENVERS) && !item.getPackageName().contains("impl") + && (!targetPackageFullName.equals(ORG_HIBERNATE_ENVERS) || targetSimpleName.startsWith("Default") + || targetSimpleName.contains("Listener") || targetSimpleName.contains("Reader"))) + { + return true; + } + return false; + } + + private static boolean isImplementingHibernateEnversInternalsDirectly(JavaClass item, ConditionEvents events, String targetPackageName, String targetPackageFullName, String targetClassDescription) { + if(targetPackageName.startsWith(ORG_HIBERNATE_ENVERS) && targetPackageName.contains("internal")) { + return true; + } + return false; + } + + private static boolean isUsingHibernateOutsideOfImplScope(JavaClass item, ConditionEvents events, String targetPackageName, String targetPackageFullName, String targetClassDescription) { + if(!item.getPackageName().contains("impl") && targetPackageName.startsWith("org.hibernate") && !targetPackageName.startsWith(ORG_HIBERNATE_VALIDATOR)) { + return true; + } + return false; + } + + static ArchCondition misUseHibernate = new ArchCondition ("misuse hibernate.") { + @Override + public void check(JavaClass item, ConditionEvents events) { + for(Dependency access: item.getDirectDependenciesFromSelf()) { + String targetPackageName = access.getTargetClass().getPackageName(); + String targetPackageFullName = access.getTargetClass().getFullName(); + String targetClassDescription = access.getDescription(); + String targetSimpleName = access.getTargetClass().getSimpleName(); + if(isUsingHibernateOutsideOfDataaccessLayer(item, events, targetPackageName, targetPackageFullName, targetClassDescription) == true) + { + String message = String.format("Hibernate (%s) should only be used in dataaccess layer. Violated in (%s)", targetPackageFullName, targetClassDescription); + events.add(new SimpleConditionEvent(item, true, message)); + } + if(isUsingProprietaryHibernateAnnotation(item, events, targetPackageName, targetPackageFullName, targetClassDescription, targetSimpleName) == true) { + String message = String.format("Standard JPA annotations should be used instead of this proprietary hibernate annotation (%s). Violated in (%s)", targetPackageFullName, targetClassDescription); + events.add(new SimpleConditionEvent(item, true, message)); + } + if(isNotImplementingHibernateEnversInImplScope(item, events, targetPackageName, targetPackageFullName, targetClassDescription, targetSimpleName) == true) { + String message = String.format("Hibernate envers implementation (%s) should only be used in impl scope of dataaccess layer. Violated in (%s)", targetPackageFullName, targetClassDescription); + events.add(new SimpleConditionEvent(item, true, message)); + } + if(isImplementingHibernateEnversInternalsDirectly(item, events, targetPackageName, targetPackageFullName, targetClassDescription) == true) { + String message = String.format("Hibernate envers internals (%s) should never be used directly. Violated in (%s)", targetPackageFullName, targetClassDescription); + events.add(new SimpleConditionEvent(item, true, message)); + } + if(isUsingHibernateOutsideOfImplScope(item, events, targetPackageName, targetPackageFullName, targetClassDescription) == true) { + String message = String.format("Hibernate internals (%s) should only be used in impl scope of dataaccess layer. Violated in (%s)", targetPackageFullName, targetClassDescription); + events.add(new SimpleConditionEvent(item, true, message)); + } + } + } + }; + + @ArchTest + static final ArchRule jpa_is_used_as_encouraged = + noClasses() + .should(misUseHibernate) + .allowEmptyShould(true); +} From 83d586cabf5125975c48720bd0b24f3ad071ee42 Mon Sep 17 00:00:00 2001 From: Vladislav Sehtman Date: Tue, 14 Feb 2023 10:34:13 +0100 Subject: [PATCH 02/11] Added E1,E3 and E4 Violations. Added hibernate and springframework dependencies to exhibit violations. Refactored E1, E3, and E4 to have a more concise debug notification. --- pom.xml | 11 ++++++++++- ...tyLayerDependsOnJpaOutsideOfDataaccessLayer.java | 8 ++++++++ ...rDependsOnHibernateOutsideOfDataaccessLayer.java | 13 +++++++++++++ ...yLayerDependsOnJavaxTransactionalInApiLayer.java | 8 ++++++++ ...DependsOnDiscouragedTransactionalAnnotation.java | 8 ++++++++ ...dpartyLayerDependsOnHibernateInsideApiLayer.java | 7 +++++++ .../dataaccess/impl/E4HibernatePositiveExample.java | 10 ++++++++++ ...ayerDependsOnDiscouragedHibernateAnnotation.java | 11 +++++++++++ ...erDependsOnHibernateEnversInternalsDirectly.java | 8 ++++++++ ...rDependsOnHibernateEnversOutsideOfImplLayer.java | 8 ++++++++ .../ThirdPartyRulesE1TransactionalTest.java | 2 +- .../archunit/ThirdPartyRulesE3JpaCheckTest.java | 2 +- .../ThirdPartyRulesE4HibernateCheckTest.java | 4 ++-- 13 files changed, 95 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/devonfw/sample/archunit/thirdparty/E3ViolationThirdpartyLayerDependsOnJpaOutsideOfDataaccessLayer.java create mode 100644 src/main/java/com/devonfw/sample/archunit/thirdparty/E4ViolationThirdpartyLayerDependsOnHibernateOutsideOfDataaccessLayer.java create mode 100644 src/main/java/com/devonfw/sample/archunit/thirdparty/api/E1ViolationThirdpartyLayerDependsOnJavaxTransactionalInApiLayer.java create mode 100644 src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/E1ViolationThirdpartyLayerDependsOnDiscouragedTransactionalAnnotation.java create mode 100644 src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/api/E4ViolationThirdpartyLayerDependsOnHibernateInsideApiLayer.java create mode 100644 src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/impl/E4HibernatePositiveExample.java create mode 100644 src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/impl/E4ViolationThirdpartyLayerDependsOnDiscouragedHibernateAnnotation.java create mode 100644 src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/impl/E4ViolationThirdpartyLayerDependsOnHibernateEnversInternalsDirectly.java create mode 100644 src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/misc/E4ViolationThirdPartyLayerDependsOnHibernateEnversOutsideOfImplLayer.java diff --git a/pom.xml b/pom.xml index d6dcca8..69d7d67 100644 --- a/pom.xml +++ b/pom.xml @@ -101,7 +101,16 @@ provided ${querydsl.version} - + + org.hibernate + hibernate-envers + 6.1.7.Final + + + org.springframework + spring-tx + 5.3.20 + io.quarkus diff --git a/src/main/java/com/devonfw/sample/archunit/thirdparty/E3ViolationThirdpartyLayerDependsOnJpaOutsideOfDataaccessLayer.java b/src/main/java/com/devonfw/sample/archunit/thirdparty/E3ViolationThirdpartyLayerDependsOnJpaOutsideOfDataaccessLayer.java new file mode 100644 index 0000000..4cc8a75 --- /dev/null +++ b/src/main/java/com/devonfw/sample/archunit/thirdparty/E3ViolationThirdpartyLayerDependsOnJpaOutsideOfDataaccessLayer.java @@ -0,0 +1,8 @@ +package com.devonfw.sample.archunit.thirdparty; + +import javax.persistence.EntityManager; // Noncompliant + +public class E3ViolationThirdpartyLayerDependsOnJpaOutsideOfDataaccessLayer { + // Noncompliant + private EntityManager entityManager; +} diff --git a/src/main/java/com/devonfw/sample/archunit/thirdparty/E4ViolationThirdpartyLayerDependsOnHibernateOutsideOfDataaccessLayer.java b/src/main/java/com/devonfw/sample/archunit/thirdparty/E4ViolationThirdpartyLayerDependsOnHibernateOutsideOfDataaccessLayer.java new file mode 100644 index 0000000..f0eb714 --- /dev/null +++ b/src/main/java/com/devonfw/sample/archunit/thirdparty/E4ViolationThirdpartyLayerDependsOnHibernateOutsideOfDataaccessLayer.java @@ -0,0 +1,13 @@ +package com.devonfw.sample.archunit.thirdparty; + +import java.util.HashSet; +import java.util.Set; + +import org.hibernate.Session; //Noncompliant +import org.hibernate.annotations.OrderBy; //Noncompliant + +public class E4ViolationThirdpartyLayerDependsOnHibernateOutsideOfDataaccessLayer { + @OrderBy(clause = "NAME DESC") // Noncompliant + Set taskList = new HashSet<>(); + Session session; //Noncompliant +} diff --git a/src/main/java/com/devonfw/sample/archunit/thirdparty/api/E1ViolationThirdpartyLayerDependsOnJavaxTransactionalInApiLayer.java b/src/main/java/com/devonfw/sample/archunit/thirdparty/api/E1ViolationThirdpartyLayerDependsOnJavaxTransactionalInApiLayer.java new file mode 100644 index 0000000..720ebdd --- /dev/null +++ b/src/main/java/com/devonfw/sample/archunit/thirdparty/api/E1ViolationThirdpartyLayerDependsOnJavaxTransactionalInApiLayer.java @@ -0,0 +1,8 @@ +package com.devonfw.sample.archunit.thirdparty.api; + +import javax.transaction.Transactional; // Noncompliant + +@Transactional +public class E1ViolationThirdpartyLayerDependsOnJavaxTransactionalInApiLayer { + +} diff --git a/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/E1ViolationThirdpartyLayerDependsOnDiscouragedTransactionalAnnotation.java b/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/E1ViolationThirdpartyLayerDependsOnDiscouragedTransactionalAnnotation.java new file mode 100644 index 0000000..001d086 --- /dev/null +++ b/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/E1ViolationThirdpartyLayerDependsOnDiscouragedTransactionalAnnotation.java @@ -0,0 +1,8 @@ +package com.devonfw.sample.archunit.thirdparty.dataaccess; + +import org.springframework.transaction.annotation.Transactional; // Noncompliant the use of JEE standard javax.transaction.Transactional is encouraged. + +@Transactional +public class E1ViolationThirdpartyLayerDependsOnDiscouragedTransactionalAnnotation { + +} diff --git a/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/api/E4ViolationThirdpartyLayerDependsOnHibernateInsideApiLayer.java b/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/api/E4ViolationThirdpartyLayerDependsOnHibernateInsideApiLayer.java new file mode 100644 index 0000000..29f1f0f --- /dev/null +++ b/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/api/E4ViolationThirdpartyLayerDependsOnHibernateInsideApiLayer.java @@ -0,0 +1,7 @@ +package com.devonfw.sample.archunit.thirdparty.dataaccess.api; + +import org.hibernate.Session; // Noncompliant + +public class E4ViolationThirdpartyLayerDependsOnHibernateInsideApiLayer { + Session sessionFactory; +} diff --git a/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/impl/E4HibernatePositiveExample.java b/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/impl/E4HibernatePositiveExample.java new file mode 100644 index 0000000..5a792a5 --- /dev/null +++ b/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/impl/E4HibernatePositiveExample.java @@ -0,0 +1,10 @@ +package com.devonfw.sample.archunit.thirdparty.dataaccess.impl; +//The use is compliant because it lies inside dataaccess/impl +import org.hibernate.Session; //compliant +import org.hibernate.envers.Audited; + +public class E4HibernatePositiveExample { + @Audited + Long id; + Session session; +} diff --git a/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/impl/E4ViolationThirdpartyLayerDependsOnDiscouragedHibernateAnnotation.java b/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/impl/E4ViolationThirdpartyLayerDependsOnDiscouragedHibernateAnnotation.java new file mode 100644 index 0000000..69ab719 --- /dev/null +++ b/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/impl/E4ViolationThirdpartyLayerDependsOnDiscouragedHibernateAnnotation.java @@ -0,0 +1,11 @@ +package com.devonfw.sample.archunit.thirdparty.dataaccess.impl; + +import org.hibernate.annotations.Entity; // Noncompliant + +public class E4ViolationThirdpartyLayerDependsOnDiscouragedHibernateAnnotation { + // Noncompliant + @Entity + class NoncompliantInnerClass{ + + } +} diff --git a/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/impl/E4ViolationThirdpartyLayerDependsOnHibernateEnversInternalsDirectly.java b/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/impl/E4ViolationThirdpartyLayerDependsOnHibernateEnversInternalsDirectly.java new file mode 100644 index 0000000..e6dbb7f --- /dev/null +++ b/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/impl/E4ViolationThirdpartyLayerDependsOnHibernateEnversInternalsDirectly.java @@ -0,0 +1,8 @@ +package com.devonfw.sample.archunit.thirdparty.dataaccess.impl; + +import org.hibernate.envers.query.internal.impl.EntitiesAtRevisionQuery; // Noncompliant + +public class E4ViolationThirdpartyLayerDependsOnHibernateEnversInternalsDirectly { + //Noncompliant + EntitiesAtRevisionQuery nonCompliantDirectQuery = new EntitiesAtRevisionQuery(null, null, getClass(), null, false); +} diff --git a/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/misc/E4ViolationThirdPartyLayerDependsOnHibernateEnversOutsideOfImplLayer.java b/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/misc/E4ViolationThirdPartyLayerDependsOnHibernateEnversOutsideOfImplLayer.java new file mode 100644 index 0000000..bd78c43 --- /dev/null +++ b/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/misc/E4ViolationThirdPartyLayerDependsOnHibernateEnversOutsideOfImplLayer.java @@ -0,0 +1,8 @@ +package com.devonfw.sample.archunit.thirdparty.dataaccess.misc; + +import org.hibernate.envers.Audited; // Noncompliant + +public class E4ViolationThirdPartyLayerDependsOnHibernateEnversOutsideOfImplLayer { + @Audited + String name; +} diff --git a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java index ab182c1..203a89b 100644 --- a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java +++ b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java @@ -29,7 +29,7 @@ private static boolean isUsingSpringframeworkTransactionalAnnotation(String targ return false; } - static ArchCondition miuse_springframework_transactional_annotation = new ArchCondition ("misuse @Transactional") { + static ArchCondition miuse_springframework_transactional_annotation = new ArchCondition ("misuse @Transactional (Rule-E1)") { @Override public void check(JavaClass item, ConditionEvents events) { for(Dependency access: item.getDirectDependenciesFromSelf()) { diff --git a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java index baa53c6..873e12d 100644 --- a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java +++ b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java @@ -28,7 +28,7 @@ private static boolean isUsingJavaxPersistenceDataAccessOrEmbeddablesInCommon(Ja return true; } - static ArchCondition misuse_jpa = new ArchCondition ("use JPA outside of dataaccess layer or embeddables in common layer") { + static ArchCondition misuse_jpa = new ArchCondition ("use JPA outside of dataaccess layer or embeddables in common layer (Rule-E3)") { @Override public void check(JavaClass item, ConditionEvents events) { for(Dependency access: item.getDirectDependenciesFromSelf()) { diff --git a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java index bd30a97..dff6952 100644 --- a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java +++ b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java @@ -69,7 +69,7 @@ private static boolean isUsingHibernateOutsideOfImplScope(JavaClass item, Condit return false; } - static ArchCondition misUseHibernate = new ArchCondition ("misuse hibernate.") { + static ArchCondition misUseHibernate = new ArchCondition ("misuse hibernate (Rule-E4).") { @Override public void check(JavaClass item, ConditionEvents events) { for(Dependency access: item.getDirectDependenciesFromSelf()) { @@ -83,7 +83,7 @@ public void check(JavaClass item, ConditionEvents events) { events.add(new SimpleConditionEvent(item, true, message)); } if(isUsingProprietaryHibernateAnnotation(item, events, targetPackageName, targetPackageFullName, targetClassDescription, targetSimpleName) == true) { - String message = String.format("Standard JPA annotations should be used instead of this proprietary hibernate annotation (%s). Violated in (%s)", targetPackageFullName, targetClassDescription); + String message = String.format("Standard JPA annotations should be used, (Discouraged annotations: %s) instead this proprietary hibernate annotation (%s) was used. Violated in (%s)", DISCOURAGED_HIBERNATE_ANNOTATIONS, targetPackageFullName, targetClassDescription); events.add(new SimpleConditionEvent(item, true, message)); } if(isNotImplementingHibernateEnversInImplScope(item, events, targetPackageName, targetPackageFullName, targetClassDescription, targetSimpleName) == true) { From 960d011f8d317cdd80d7bbaf327ee2a4432286e2 Mon Sep 17 00:00:00 2001 From: Vladislav Sehtman Date: Tue, 14 Feb 2023 10:46:17 +0100 Subject: [PATCH 03/11] Refactored error output to see the violated rule. --- .../sample/archunit/ThirdPartyRulesE1TransactionalTest.java | 2 +- .../devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java | 2 +- .../sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java index ab182c1..203a89b 100644 --- a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java +++ b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java @@ -29,7 +29,7 @@ private static boolean isUsingSpringframeworkTransactionalAnnotation(String targ return false; } - static ArchCondition miuse_springframework_transactional_annotation = new ArchCondition ("misuse @Transactional") { + static ArchCondition miuse_springframework_transactional_annotation = new ArchCondition ("misuse @Transactional (Rule-E1)") { @Override public void check(JavaClass item, ConditionEvents events) { for(Dependency access: item.getDirectDependenciesFromSelf()) { diff --git a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java index baa53c6..873e12d 100644 --- a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java +++ b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java @@ -28,7 +28,7 @@ private static boolean isUsingJavaxPersistenceDataAccessOrEmbeddablesInCommon(Ja return true; } - static ArchCondition misuse_jpa = new ArchCondition ("use JPA outside of dataaccess layer or embeddables in common layer") { + static ArchCondition misuse_jpa = new ArchCondition ("use JPA outside of dataaccess layer or embeddables in common layer (Rule-E3)") { @Override public void check(JavaClass item, ConditionEvents events) { for(Dependency access: item.getDirectDependenciesFromSelf()) { diff --git a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java index bd30a97..c128810 100644 --- a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java +++ b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java @@ -69,7 +69,7 @@ private static boolean isUsingHibernateOutsideOfImplScope(JavaClass item, Condit return false; } - static ArchCondition misUseHibernate = new ArchCondition ("misuse hibernate.") { + static ArchCondition misUseHibernate = new ArchCondition ("misuse hibernate (Rule-E4).") { @Override public void check(JavaClass item, ConditionEvents events) { for(Dependency access: item.getDirectDependenciesFromSelf()) { From c294798e818a680fb319f2e3c3e03faf50a5fca0 Mon Sep 17 00:00:00 2001 From: Vladislav Sehtman Date: Wed, 1 Mar 2023 10:25:59 +0100 Subject: [PATCH 04/11] Update ThirdPartyRulesE1TransactionalTest.java Refactored the class with respect to @hohwille review annotations. --- .../ThirdPartyRulesE1TransactionalTest.java | 75 ++++++++++--------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java index 203a89b..321fc12 100644 --- a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java +++ b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java @@ -1,5 +1,7 @@ package com.devonfw.sample.archunit; +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; + import com.tngtech.archunit.core.domain.Dependency; import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.importer.ImportOption; @@ -10,48 +12,51 @@ import com.tngtech.archunit.lang.ConditionEvents; import com.tngtech.archunit.lang.SimpleConditionEvent; -import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; - @AnalyzeClasses(packages = "com.devonfw.sample.archunit", importOptions = ImportOption.DoNotIncludeTests.class) public class ThirdPartyRulesE1TransactionalTest { - private static boolean apiScopedClassIsUsingTransactional(JavaClass item, String targetPackageFullName) { - if(item.getFullName().contains("api") && targetPackageFullName.equals("javax.transaction.Transactional")) { - return true; - } - return false; + private static boolean isApiScopedClassUsingTransactional(JavaClass item, String targetPackageFullName) { + + if (item.getFullName().contains("api") && targetPackageFullName.equals("javax.transaction.Transactional")) { + return true; } + return false; + } - private static boolean isUsingSpringframeworkTransactionalAnnotation(String targetPackageFullName) { - if(targetPackageFullName.equals("org.springframework.transaction.annotation.Transactional")) { - return true; + private static boolean isUsingSpringframeworkTransactionalAnnotation(String targetPackageFullName) { + + if (targetPackageFullName.equals("org.springframework.transaction.annotation.Transactional")) { + return true; + } + return false; + } + + static final ArchCondition misuse_springframework_transactional_annotation = new ArchCondition( + "misuse @Transactional (Rule-E1)") { + @Override + public void check(JavaClass sourceClass, ConditionEvents events) { + + for (Dependency dependency : sourceClass.getDirectDependenciesFromSelf()) { + String targetFullName = dependency.getTargetClass().getFullName(); + String targetClassDescription = dependency.getDescription(); + if (isUsingSpringframeworkTransactionalAnnotation(targetFullName) == true) { + String message = String.format( + "Use JEE standard (javax.transaction.Transactional from javax.transaction:javax.transaction-api:1.2+). The use (%s) is discouraged. Violated in (%s)", + targetFullName, targetClassDescription); + events.add(new SimpleConditionEvent(sourceClass, true, message)); + } + if (isApiScopedClassUsingTransactional(sourceClass, targetFullName) == true) { + String message = String.format( + "The use of @Transactional in API is discouraged. Instead use it to annotate implementations. Violated in (%s)", + targetClassDescription); + events.add(new SimpleConditionEvent(sourceClass, true, message)); } - return false; + } } + }; - static ArchCondition miuse_springframework_transactional_annotation = new ArchCondition ("misuse @Transactional (Rule-E1)") { - @Override - public void check(JavaClass item, ConditionEvents events) { - for(Dependency access: item.getDirectDependenciesFromSelf()) { - String targetPackageFullName = access.getTargetClass().getFullName(); - String targetClassDescription = access.getDescription(); - if(isUsingSpringframeworkTransactionalAnnotation(targetPackageFullName) == true) { - String message = String.format("Use JEE standard (javax.transaction.Transactional from javax.transaction:javax.transaction-api:1.2+). The use (%s) is discouraged. Violated in (%s)", targetPackageFullName, targetClassDescription); - events.add(new SimpleConditionEvent(item, true, message)); - } - if(apiScopedClassIsUsingTransactional(item, targetPackageFullName) == true) { - String message = String.format("The use of @Transactional in API is discouraged. Instead use it to annotate implementations. Violated in (%s)", targetClassDescription); - events.add(new SimpleConditionEvent(item, true, message)); - } - } - } - }; - - @ArchTest - static final ArchRule verifying_proper_transactional_use_from_jee = - noClasses() - .should(miuse_springframework_transactional_annotation) - .allowEmptyShould(true); - + @ArchTest + static final ArchRule verifying_proper_transactional_use_from_jee = noClasses() + .should(misuse_springframework_transactional_annotation).allowEmptyShould(true); } From 223361fbe6006e4ce9c5315124e2aa11ad122f8f Mon Sep 17 00:00:00 2001 From: Vladislav Sehtman Date: Wed, 1 Mar 2023 10:37:01 +0100 Subject: [PATCH 05/11] Update ThirdPartyRulesE1TransactionalTest.java Refactored violations to include review annotations --- .../ThirdPartyRulesE1TransactionalTest.java | 75 ++++++++++--------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java index 203a89b..595c1ea 100644 --- a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java +++ b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java @@ -1,5 +1,7 @@ package com.devonfw.sample.archunit; +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; + import com.tngtech.archunit.core.domain.Dependency; import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.importer.ImportOption; @@ -10,48 +12,51 @@ import com.tngtech.archunit.lang.ConditionEvents; import com.tngtech.archunit.lang.SimpleConditionEvent; -import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; - @AnalyzeClasses(packages = "com.devonfw.sample.archunit", importOptions = ImportOption.DoNotIncludeTests.class) public class ThirdPartyRulesE1TransactionalTest { - private static boolean apiScopedClassIsUsingTransactional(JavaClass item, String targetPackageFullName) { - if(item.getFullName().contains("api") && targetPackageFullName.equals("javax.transaction.Transactional")) { - return true; - } - return false; + private static boolean isApiScopedClassUsingTransactional(JavaClass item, String targetPackageFullName) { + + if (targetPackageFullName.equals("javax.transaction.Transactional")) { + return true; } + return false; + } - private static boolean isUsingSpringframeworkTransactionalAnnotation(String targetPackageFullName) { - if(targetPackageFullName.equals("org.springframework.transaction.annotation.Transactional")) { - return true; + private static boolean isUsingSpringframeworkTransactionalAnnotation(String targetPackageFullName) { + + if (targetPackageFullName.equals("org.springframework.transaction.annotation.Transactional")) { + return true; + } + return false; + } + + static ArchCondition misuse_springframework_transactional_annotation = new ArchCondition( + "misuse @Transactional (Rule-E1)") { + @Override + public void check(JavaClass sourceClass, ConditionEvents events) { + + for (Dependency dependency : sourceClass.getDirectDependenciesFromSelf()) { + String targetFullName = dependency.getTargetClass().getFullName(); + String targetClassDescription = dependency.getDescription(); + if (isUsingSpringframeworkTransactionalAnnotation(targetFullName) == true) { + String message = String.format( + "Use JEE standard (javax.transaction.Transactional from javax.transaction:javax.transaction-api:1.2+). The use (%s) is discouraged. Violated in (%s)", + targetFullName, targetClassDescription); + events.add(new SimpleConditionEvent(sourceClass, true, message)); + } + if (isApiScopedClassUsingTransactional(sourceClass, targetFullName) == true) { + String message = String.format( + "The use of @Transactional in API is discouraged. Instead use it to annotate implementations. Violated in (%s)", + targetClassDescription); + events.add(new SimpleConditionEvent(sourceClass, true, message)); } - return false; + } } + }; - static ArchCondition miuse_springframework_transactional_annotation = new ArchCondition ("misuse @Transactional (Rule-E1)") { - @Override - public void check(JavaClass item, ConditionEvents events) { - for(Dependency access: item.getDirectDependenciesFromSelf()) { - String targetPackageFullName = access.getTargetClass().getFullName(); - String targetClassDescription = access.getDescription(); - if(isUsingSpringframeworkTransactionalAnnotation(targetPackageFullName) == true) { - String message = String.format("Use JEE standard (javax.transaction.Transactional from javax.transaction:javax.transaction-api:1.2+). The use (%s) is discouraged. Violated in (%s)", targetPackageFullName, targetClassDescription); - events.add(new SimpleConditionEvent(item, true, message)); - } - if(apiScopedClassIsUsingTransactional(item, targetPackageFullName) == true) { - String message = String.format("The use of @Transactional in API is discouraged. Instead use it to annotate implementations. Violated in (%s)", targetClassDescription); - events.add(new SimpleConditionEvent(item, true, message)); - } - } - } - }; - - @ArchTest - static final ArchRule verifying_proper_transactional_use_from_jee = - noClasses() - .should(miuse_springframework_transactional_annotation) - .allowEmptyShould(true); - + @ArchTest + static final ArchRule verifying_proper_transactional_use_from_jee = noClasses() + .should(misuse_springframework_transactional_annotation).allowEmptyShould(true); } From cbf7b1112e49829bd31af1d9b7494d46c169b08a Mon Sep 17 00:00:00 2001 From: Vladislav Sehtman Date: Wed, 1 Mar 2023 12:18:28 +0100 Subject: [PATCH 06/11] Refactoring Concerning @hohwille review annotation, I adjusted the E3 Rule to correctly check for layers. I added two public static final patterns for the common and dataaccess layers to use instead of the faulty contains() check which would trigger false positive errors. --- .../sample/archunit/PackageRuleTest.java | 70 ++++++++++-------- .../ThirdPartyRulesE3JpaCheckTest.java | 73 +++++++++++-------- 2 files changed, 81 insertions(+), 62 deletions(-) diff --git a/src/test/java/com/devonfw/sample/archunit/PackageRuleTest.java b/src/test/java/com/devonfw/sample/archunit/PackageRuleTest.java index 4ff0ee7..d8ad0bc 100644 --- a/src/test/java/com/devonfw/sample/archunit/PackageRuleTest.java +++ b/src/test/java/com/devonfw/sample/archunit/PackageRuleTest.java @@ -2,6 +2,9 @@ import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.importer.ImportOption; import com.tngtech.archunit.junit.AnalyzeClasses; @@ -11,58 +14,63 @@ import com.tngtech.archunit.lang.ConditionEvents; import com.tngtech.archunit.lang.SimpleConditionEvent; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - /** * JUnit test that validates the Packages of this application. */ @AnalyzeClasses(packages = "com.devonfw.sample.archunit", importOptions = ImportOption.DoNotIncludeTests.class) public class PackageRuleTest { -/*Layer */ -private static final String LAYER_COMMON = "common"; + /* Layer */ + private static final String LAYER_COMMON = "common"; -private static final String LAYER_DATA_ACCESS = "dataaccess"; + private static final String LAYER_DATA_ACCESS = "dataaccess"; -private static final String LAYER_LOGIC = "logic"; + private static final String LAYER_LOGIC = "logic"; -private static final String LAYER_SERVICE = "service"; + private static final String LAYER_SERVICE = "service"; -private static final String LAYER_CLIENT = "client"; + private static final String LAYER_CLIENT = "client"; -private static final String LAYER_BATCH = "batch"; + private static final String LAYER_BATCH = "batch"; -/* Pattern */ -private static final String PATTERN_SEGMENT = "[a-z0-9_]+"; + /* Pattern */ + private static final String PATTERN_SEGMENT = "[a-z0-9_]+"; -private static final String PATTERN_LAYERS = LAYER_COMMON + "|" - + LAYER_DATA_ACCESS + "|" + LAYER_SERVICE + "|" + LAYER_LOGIC + "|" + LAYER_BATCH + "|" - + LAYER_CLIENT; - -private static final String ROOT_PACKAGE = - // ....1..........................2 - "(" + PATTERN_SEGMENT +")(\\." + PATTERN_SEGMENT +")*"; + private static final String PATTERN_LAYERS = LAYER_COMMON + "|" + LAYER_DATA_ACCESS + "|" + LAYER_SERVICE + "|" + + LAYER_LOGIC + "|" + LAYER_BATCH + "|" + LAYER_CLIENT; -private static final String DEFAULT_PATTERN = - // ....1......................2...........................3...........................4 - "(" + ROOT_PACKAGE + ")\\.(" + PATTERN_SEGMENT + ")\\.(" + PATTERN_LAYERS + ")(\\." + PATTERN_SEGMENT + ")*"; + private static final String ROOT_PACKAGE = + // ....1..........................2 + "(" + PATTERN_SEGMENT + ")(\\." + PATTERN_SEGMENT + ")*"; -private static final Pattern PATTERN = Pattern.compile(DEFAULT_PATTERN); + private static final String DEFAULT_PATTERN = + // ....1......................2...........................3...........................4 + "(" + ROOT_PACKAGE + ")\\.(" + PATTERN_SEGMENT + ")\\.(" + PATTERN_LAYERS + ")(\\." + PATTERN_SEGMENT + ")*"; -/* ArchRule and Condition */ + public static final String COMMON_PATTERN = + // ....1......................2...........................3...........................4 + "(" + ROOT_PACKAGE + ")\\.(" + PATTERN_SEGMENT + ")\\.(" + LAYER_COMMON + ")(\\." + PATTERN_SEGMENT + ")*"; + + public static final String DATAACCESS_PATTERN = + // ....1......................2...........................3...........................4 + "(" + ROOT_PACKAGE + ")\\.(" + PATTERN_SEGMENT + ")\\.(" + LAYER_DATA_ACCESS + ")(\\." + PATTERN_SEGMENT + ")*"; + + private static final Pattern PATTERN = Pattern.compile(DEFAULT_PATTERN); + + /* ArchRule and Condition */ @ArchTest public ArchRule shouldHaveValidLayers = classes().should(containsValidPackageStructureCondition()); - - + private static ArchCondition containsValidPackageStructureCondition() { - return new ArchCondition("check for the package structure to be valid", new Object(){}) { + + return new ArchCondition("check for the package structure to be valid", new Object() { + }) { @Override public void check(JavaClass javaClass, ConditionEvents events) { - Matcher matcher = PATTERN.matcher(javaClass.getPackageName()); - String message = javaClass.getSimpleName() + " test result is " + matcher.matches(); - events.add(new SimpleConditionEvent(javaClass, - matcher.matches(), message)); + + Matcher matcher = PATTERN.matcher(javaClass.getPackageName()); + String message = javaClass.getSimpleName() + " test result is " + matcher.matches(); + events.add(new SimpleConditionEvent(javaClass, matcher.matches(), message)); } }; }; diff --git a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java index 873e12d..b401187 100644 --- a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java +++ b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java @@ -1,5 +1,10 @@ package com.devonfw.sample.archunit; +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import com.tngtech.archunit.core.domain.Dependency; import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.importer.ImportOption; @@ -10,41 +15,47 @@ import com.tngtech.archunit.lang.ConditionEvents; import com.tngtech.archunit.lang.SimpleConditionEvent; -import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; - @AnalyzeClasses(packages = "com.devonfw.sample.archunit", importOptions = ImportOption.DoNotIncludeTests.class) public class ThirdPartyRulesE3JpaCheckTest { + private static final Pattern PATTERN_COMMON = Pattern.compile(PackageRuleTest.COMMON_PATTERN); - private static boolean isUsingJavaxPersistenceDataAccessOrEmbeddablesInCommon(JavaClass item, String targetPackageFullName) { - if(targetPackageFullName.startsWith("javax.persistence")) { - if(item.getFullName().contains("dataaccess")) { - return true; - } - if(item.getFullName().contains("common") && item.getSimpleName().contains("Embeddable")) { - return true; - } - return false; - } + private static final Pattern PATTERN_DATAACCESS = Pattern.compile(PackageRuleTest.DATAACCESS_PATTERN); + + private static boolean isUsingJavaxPersistenceDataAccessOrEmbeddablesInCommon(JavaClass item, + String targetPackageFullName) { + + if (targetPackageFullName.startsWith("javax.persistence")) { + Matcher commonMatcher = PATTERN_COMMON.matcher(item.getPackageName()); + Matcher dataaccessMatcher = PATTERN_DATAACCESS.matcher(item.getPackageName()); + if (dataaccessMatcher.matches()) { + return true; + } + if (commonMatcher.matches() && item.getSimpleName().contains("Embeddable")) { return true; + } + return false; + } + return true; + } + + static ArchCondition misuse_jpa = new ArchCondition( + "use JPA outside of dataaccess layer or embeddables in common layer (Rule-E3)") { + @Override + public void check(JavaClass item, ConditionEvents events) { + + for (Dependency access : item.getDirectDependenciesFromSelf()) { + String targetPackageFullName = access.getTargetClass().getFullName(); + String targetClassDescription = access.getDescription(); + if (isUsingJavaxPersistenceDataAccessOrEmbeddablesInCommon(item, targetPackageFullName) == false) { + String message = String.format( + "JPA (%s) shall only be used in dataaccess layer or for embeddables in common layer. Violated in (%s)", + targetPackageFullName, targetClassDescription); + events.add(new SimpleConditionEvent(item, true, message)); + } + } } + }; - static ArchCondition misuse_jpa = new ArchCondition ("use JPA outside of dataaccess layer or embeddables in common layer (Rule-E3)") { - @Override - public void check(JavaClass item, ConditionEvents events) { - for(Dependency access: item.getDirectDependenciesFromSelf()) { - String targetPackageFullName = access.getTargetClass().getFullName(); - String targetClassDescription = access.getDescription(); - if(isUsingJavaxPersistenceDataAccessOrEmbeddablesInCommon(item, targetPackageFullName) == false) { - String message = String.format("JPA (%s) shall only be used in dataaccess layer or for embeddables in common layer. Violated in (%s)", targetPackageFullName, targetClassDescription); - events.add(new SimpleConditionEvent(item, true, message)); - } - } - } - }; - - @ArchTest - static final ArchRule verifying_proper_jpa_use = - noClasses() - .should(misuse_jpa) - .allowEmptyShould(true); + @ArchTest + static final ArchRule verifying_proper_jpa_use = noClasses().should(misuse_jpa).allowEmptyShould(true); } From 727d7c1cf1cf1cee3b9a7a7b9ca7e1346d6ebabe Mon Sep 17 00:00:00 2001 From: Vladislav Sehtman Date: Tue, 7 Mar 2023 09:32:43 +0100 Subject: [PATCH 07/11] Removed the class api scope check. --- .../ThirdPartyRulesE1TransactionalTest.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java index 321fc12..45eb8e8 100644 --- a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java +++ b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java @@ -15,14 +15,6 @@ @AnalyzeClasses(packages = "com.devonfw.sample.archunit", importOptions = ImportOption.DoNotIncludeTests.class) public class ThirdPartyRulesE1TransactionalTest { - private static boolean isApiScopedClassUsingTransactional(JavaClass item, String targetPackageFullName) { - - if (item.getFullName().contains("api") && targetPackageFullName.equals("javax.transaction.Transactional")) { - return true; - } - return false; - } - private static boolean isUsingSpringframeworkTransactionalAnnotation(String targetPackageFullName) { if (targetPackageFullName.equals("org.springframework.transaction.annotation.Transactional")) { @@ -45,12 +37,6 @@ public void check(JavaClass sourceClass, ConditionEvents events) { targetFullName, targetClassDescription); events.add(new SimpleConditionEvent(sourceClass, true, message)); } - if (isApiScopedClassUsingTransactional(sourceClass, targetFullName) == true) { - String message = String.format( - "The use of @Transactional in API is discouraged. Instead use it to annotate implementations. Violated in (%s)", - targetClassDescription); - events.add(new SimpleConditionEvent(sourceClass, true, message)); - } } } }; From b860ea13b436974beb52b716e7c408684688f04b Mon Sep 17 00:00:00 2001 From: Vladislav Sehtman Date: Tue, 7 Mar 2023 12:59:23 +0100 Subject: [PATCH 08/11] Deleted scope related tests and violations. Refactored via automatic devonfw-ide code formatting. Adjusted ArchCondition to fit the recent rule. --- ...DependsOnJavaxTransactionalInApiLayer.java | 8 - ...LayerDependsOnHibernateInsideApiLayer.java | 7 - ...dsOnHibernateEnversOutsideOfImplLayer.java | 8 - .../ThirdPartyRulesE4HibernateCheckTest.java | 151 ++++++++---------- 4 files changed, 70 insertions(+), 104 deletions(-) delete mode 100644 src/main/java/com/devonfw/sample/archunit/thirdparty/api/E1ViolationThirdpartyLayerDependsOnJavaxTransactionalInApiLayer.java delete mode 100644 src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/api/E4ViolationThirdpartyLayerDependsOnHibernateInsideApiLayer.java delete mode 100644 src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/misc/E4ViolationThirdPartyLayerDependsOnHibernateEnversOutsideOfImplLayer.java diff --git a/src/main/java/com/devonfw/sample/archunit/thirdparty/api/E1ViolationThirdpartyLayerDependsOnJavaxTransactionalInApiLayer.java b/src/main/java/com/devonfw/sample/archunit/thirdparty/api/E1ViolationThirdpartyLayerDependsOnJavaxTransactionalInApiLayer.java deleted file mode 100644 index 720ebdd..0000000 --- a/src/main/java/com/devonfw/sample/archunit/thirdparty/api/E1ViolationThirdpartyLayerDependsOnJavaxTransactionalInApiLayer.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.devonfw.sample.archunit.thirdparty.api; - -import javax.transaction.Transactional; // Noncompliant - -@Transactional -public class E1ViolationThirdpartyLayerDependsOnJavaxTransactionalInApiLayer { - -} diff --git a/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/api/E4ViolationThirdpartyLayerDependsOnHibernateInsideApiLayer.java b/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/api/E4ViolationThirdpartyLayerDependsOnHibernateInsideApiLayer.java deleted file mode 100644 index 29f1f0f..0000000 --- a/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/api/E4ViolationThirdpartyLayerDependsOnHibernateInsideApiLayer.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.devonfw.sample.archunit.thirdparty.dataaccess.api; - -import org.hibernate.Session; // Noncompliant - -public class E4ViolationThirdpartyLayerDependsOnHibernateInsideApiLayer { - Session sessionFactory; -} diff --git a/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/misc/E4ViolationThirdPartyLayerDependsOnHibernateEnversOutsideOfImplLayer.java b/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/misc/E4ViolationThirdPartyLayerDependsOnHibernateEnversOutsideOfImplLayer.java deleted file mode 100644 index bd78c43..0000000 --- a/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/misc/E4ViolationThirdPartyLayerDependsOnHibernateEnversOutsideOfImplLayer.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.devonfw.sample.archunit.thirdparty.dataaccess.misc; - -import org.hibernate.envers.Audited; // Noncompliant - -public class E4ViolationThirdPartyLayerDependsOnHibernateEnversOutsideOfImplLayer { - @Audited - String name; -} diff --git a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java index dff6952..aa38697 100644 --- a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java +++ b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java @@ -1,5 +1,13 @@ package com.devonfw.sample.archunit; +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import com.tngtech.archunit.core.domain.Dependency; import com.tngtech.archunit.core.domain.JavaClass; import com.tngtech.archunit.core.importer.ImportOption; @@ -10,101 +18,82 @@ import com.tngtech.archunit.lang.ConditionEvents; import com.tngtech.archunit.lang.SimpleConditionEvent; -import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - /** * {@link DevonArchitecture3rdPartyCheck} verifying that the {@code JPA} is properly used. */ @AnalyzeClasses(packages = "com.devonfw.sample.archunit", importOptions = ImportOption.DoNotIncludeTests.class) public class ThirdPartyRulesE4HibernateCheckTest { - private static final Set DISCOURAGED_HIBERNATE_ANNOTATIONS = new HashSet<>( - Arrays.asList("OrderBy", "Entity", "AccessType", "ForeignKey", "Cascade", "Index", "IndexColumn")); - - private static final String ORG_HIBERNATE_ENVERS = "org.hibernate.envers"; - - private static final String ORG_HIBERNATE_VALIDATOR = "org.hibernate.validator"; - - private static final String ORG_HIBERNATE_ANNOTATIONS = "org.hibernate.annotations"; - - private static boolean isUsingHibernateOutsideOfDataaccessLayer(JavaClass item, ConditionEvents events, String targetPackageName, String targetPackageFullName, String targetClassDescription) { - if(!item.getPackageName().contains("dataaccess") && targetPackageName.startsWith("org.hibernate") && !targetPackageName.startsWith(ORG_HIBERNATE_VALIDATOR)) { - return true; - } - return false; - } + private static final Set DISCOURAGED_HIBERNATE_ANNOTATIONS = new HashSet<>( + Arrays.asList("OrderBy", "Entity", "AccessType", "ForeignKey", "Cascade", "Index", "IndexColumn")); - private static boolean isUsingProprietaryHibernateAnnotation(JavaClass item, ConditionEvents events, String targetPackageName, String targetPackageFullName, String targetClassDescription, String targetSimpleName) { - if(targetPackageName.equals(ORG_HIBERNATE_ANNOTATIONS) && DISCOURAGED_HIBERNATE_ANNOTATIONS.contains(targetSimpleName)) { - return true; - } - return false; + private static final String ORG_HIBERNATE_ENVERS = "org.hibernate.envers"; + + private static final String ORG_HIBERNATE_VALIDATOR = "org.hibernate.validator"; + + private static final String ORG_HIBERNATE_ANNOTATIONS = "org.hibernate.annotations"; + + private static final Pattern PATTERN_DATAACCESS = Pattern.compile(PackageRuleTest.DATAACCESS_PATTERN); + + private static boolean isUsingHibernateOutsideOfDataaccessLayer(JavaClass source, String targetPackageName) { + + Matcher dataaccessMatcher = PATTERN_DATAACCESS.matcher(source.getPackageName()); + if (!dataaccessMatcher.matches()) { + return true; } + return false; + } - private static boolean isNotImplementingHibernateEnversInImplScope(JavaClass item, ConditionEvents events, String targetPackageName, String targetPackageFullName, String targetClassDescription, String targetSimpleName) { - if(targetPackageName.startsWith(ORG_HIBERNATE_ENVERS) && !item.getPackageName().contains("impl") - && (!targetPackageFullName.equals(ORG_HIBERNATE_ENVERS) || targetSimpleName.startsWith("Default") - || targetSimpleName.contains("Listener") || targetSimpleName.contains("Reader"))) - { - return true; - } - return false; + private static boolean isUsingProprietaryHibernateAnnotation(String targetPackageName, String targetSimpleName) { + + if (targetPackageName.equals(ORG_HIBERNATE_ANNOTATIONS) + && DISCOURAGED_HIBERNATE_ANNOTATIONS.contains(targetSimpleName)) { + return true; } + return false; + } - private static boolean isImplementingHibernateEnversInternalsDirectly(JavaClass item, ConditionEvents events, String targetPackageName, String targetPackageFullName, String targetClassDescription) { - if(targetPackageName.startsWith(ORG_HIBERNATE_ENVERS) && targetPackageName.contains("internal")) { - return true; - } - return false; + private static boolean isImplementingHibernateEnversInternalsDirectly(String targetPackageName) { + + if (targetPackageName.startsWith(ORG_HIBERNATE_ENVERS) && targetPackageName.contains("internal")) { + return true; } + return false; + } - private static boolean isUsingHibernateOutsideOfImplScope(JavaClass item, ConditionEvents events, String targetPackageName, String targetPackageFullName, String targetClassDescription) { - if(!item.getPackageName().contains("impl") && targetPackageName.startsWith("org.hibernate") && !targetPackageName.startsWith(ORG_HIBERNATE_VALIDATOR)) { - return true; + static final ArchCondition misUseHibernate = new ArchCondition("misuse hibernate (Rule-E4).") { + @Override + public void check(JavaClass item, ConditionEvents events) { + + for (Dependency access : item.getDirectDependenciesFromSelf()) { + String targetPackageName = access.getTargetClass().getPackageName(); + String targetPackageFullName = access.getTargetClass().getFullName(); + String targetClassDescription = access.getDescription(); + String targetSimpleName = access.getTargetClass().getSimpleName(); + if (targetPackageName.startsWith("org.hibernate") && !targetPackageName.startsWith(ORG_HIBERNATE_VALIDATOR)) { + + if (isUsingHibernateOutsideOfDataaccessLayer(item, targetPackageName) == true) { + String message = String.format("Hibernate (%s) should only be used in dataaccess layer. Violated in (%s)", + targetPackageFullName, targetClassDescription); + events.add(new SimpleConditionEvent(item, true, message)); + } + if (isUsingProprietaryHibernateAnnotation(targetPackageName, targetSimpleName) == true) { + String message = String.format( + "Standard JPA annotations should be used, (Discouraged annotations: %s) instead this proprietary hibernate annotation (%s) was used. Violated in (%s)", + DISCOURAGED_HIBERNATE_ANNOTATIONS, targetPackageFullName, targetClassDescription); + events.add(new SimpleConditionEvent(item, true, message)); + } + if (isImplementingHibernateEnversInternalsDirectly(targetPackageName) == true) { + String message = String.format( + "Hibernate envers internals (%s) should never be used directly. Violated in (%s)", + targetPackageFullName, targetClassDescription); + events.add(new SimpleConditionEvent(item, true, message)); + } } - return false; + } } + }; - static ArchCondition misUseHibernate = new ArchCondition ("misuse hibernate (Rule-E4).") { - @Override - public void check(JavaClass item, ConditionEvents events) { - for(Dependency access: item.getDirectDependenciesFromSelf()) { - String targetPackageName = access.getTargetClass().getPackageName(); - String targetPackageFullName = access.getTargetClass().getFullName(); - String targetClassDescription = access.getDescription(); - String targetSimpleName = access.getTargetClass().getSimpleName(); - if(isUsingHibernateOutsideOfDataaccessLayer(item, events, targetPackageName, targetPackageFullName, targetClassDescription) == true) - { - String message = String.format("Hibernate (%s) should only be used in dataaccess layer. Violated in (%s)", targetPackageFullName, targetClassDescription); - events.add(new SimpleConditionEvent(item, true, message)); - } - if(isUsingProprietaryHibernateAnnotation(item, events, targetPackageName, targetPackageFullName, targetClassDescription, targetSimpleName) == true) { - String message = String.format("Standard JPA annotations should be used, (Discouraged annotations: %s) instead this proprietary hibernate annotation (%s) was used. Violated in (%s)", DISCOURAGED_HIBERNATE_ANNOTATIONS, targetPackageFullName, targetClassDescription); - events.add(new SimpleConditionEvent(item, true, message)); - } - if(isNotImplementingHibernateEnversInImplScope(item, events, targetPackageName, targetPackageFullName, targetClassDescription, targetSimpleName) == true) { - String message = String.format("Hibernate envers implementation (%s) should only be used in impl scope of dataaccess layer. Violated in (%s)", targetPackageFullName, targetClassDescription); - events.add(new SimpleConditionEvent(item, true, message)); - } - if(isImplementingHibernateEnversInternalsDirectly(item, events, targetPackageName, targetPackageFullName, targetClassDescription) == true) { - String message = String.format("Hibernate envers internals (%s) should never be used directly. Violated in (%s)", targetPackageFullName, targetClassDescription); - events.add(new SimpleConditionEvent(item, true, message)); - } - if(isUsingHibernateOutsideOfImplScope(item, events, targetPackageName, targetPackageFullName, targetClassDescription) == true) { - String message = String.format("Hibernate internals (%s) should only be used in impl scope of dataaccess layer. Violated in (%s)", targetPackageFullName, targetClassDescription); - events.add(new SimpleConditionEvent(item, true, message)); - } - } - } - }; - - @ArchTest - static final ArchRule jpa_is_used_as_encouraged = - noClasses() - .should(misUseHibernate) - .allowEmptyShould(true); + @ArchTest + static final ArchRule jpa_is_used_as_encouraged = noClasses().should(misUseHibernate).allowEmptyShould(true); } From 039e409495a36bf4bc09580b85be8952c122cf41 Mon Sep 17 00:00:00 2001 From: Vladislav Sehtman Date: Wed, 15 Mar 2023 12:25:33 +0100 Subject: [PATCH 09/11] Added scope related tests ThirdPartyRulesTest.java: - combined all third-party rules inside ThirdPartyRulesTest.java - deleted distinct rule test files - changed all ArchRules to public as a part of the preparation for task #19 - rules 1, 3, and 4 utilize now the present PackageStructure type. --- rules 1 and 4 also test scope-related conditions for classic architecture projects now. --- .../ThirdPartyRulesE1TransactionalTest.java | 48 ---- .../ThirdPartyRulesE3JpaCheckTest.java | 61 ----- .../ThirdPartyRulesE4HibernateCheckTest.java | 98 ------- .../sample/archunit/ThirdPartyRulesTest.java | 247 ++++++++++++++++-- 4 files changed, 230 insertions(+), 224 deletions(-) delete mode 100644 src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java delete mode 100644 src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java delete mode 100644 src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java diff --git a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java deleted file mode 100644 index 45eb8e8..0000000 --- a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.devonfw.sample.archunit; - -import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; - -import com.tngtech.archunit.core.domain.Dependency; -import com.tngtech.archunit.core.domain.JavaClass; -import com.tngtech.archunit.core.importer.ImportOption; -import com.tngtech.archunit.junit.AnalyzeClasses; -import com.tngtech.archunit.junit.ArchTest; -import com.tngtech.archunit.lang.ArchCondition; -import com.tngtech.archunit.lang.ArchRule; -import com.tngtech.archunit.lang.ConditionEvents; -import com.tngtech.archunit.lang.SimpleConditionEvent; - -@AnalyzeClasses(packages = "com.devonfw.sample.archunit", importOptions = ImportOption.DoNotIncludeTests.class) -public class ThirdPartyRulesE1TransactionalTest { - - private static boolean isUsingSpringframeworkTransactionalAnnotation(String targetPackageFullName) { - - if (targetPackageFullName.equals("org.springframework.transaction.annotation.Transactional")) { - return true; - } - return false; - } - - static final ArchCondition misuse_springframework_transactional_annotation = new ArchCondition( - "misuse @Transactional (Rule-E1)") { - @Override - public void check(JavaClass sourceClass, ConditionEvents events) { - - for (Dependency dependency : sourceClass.getDirectDependenciesFromSelf()) { - String targetFullName = dependency.getTargetClass().getFullName(); - String targetClassDescription = dependency.getDescription(); - if (isUsingSpringframeworkTransactionalAnnotation(targetFullName) == true) { - String message = String.format( - "Use JEE standard (javax.transaction.Transactional from javax.transaction:javax.transaction-api:1.2+). The use (%s) is discouraged. Violated in (%s)", - targetFullName, targetClassDescription); - events.add(new SimpleConditionEvent(sourceClass, true, message)); - } - } - } - }; - - @ArchTest - static final ArchRule verifying_proper_transactional_use_from_jee = noClasses() - .should(misuse_springframework_transactional_annotation).allowEmptyShould(true); - -} diff --git a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java deleted file mode 100644 index 0051be1..0000000 --- a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.devonfw.sample.archunit; - -import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import com.tngtech.archunit.core.domain.Dependency; -import com.tngtech.archunit.core.domain.JavaClass; -import com.tngtech.archunit.core.importer.ImportOption; -import com.tngtech.archunit.junit.AnalyzeClasses; -import com.tngtech.archunit.junit.ArchTest; -import com.tngtech.archunit.lang.ArchCondition; -import com.tngtech.archunit.lang.ArchRule; -import com.tngtech.archunit.lang.ConditionEvents; -import com.tngtech.archunit.lang.SimpleConditionEvent; - -@AnalyzeClasses(packages = "com.devonfw.sample.archunit", importOptions = ImportOption.DoNotIncludeTests.class) -public class ThirdPartyRulesE3JpaCheckTest { - private static final Pattern PATTERN_COMMON = Pattern.compile(PackageRuleTest.COMMON_PATTERN); - - private static final Pattern PATTERN_DATAACCESS = Pattern.compile(PackageRuleTest.DATAACCESS_PATTERN); - - private static boolean isUsingJavaxPersistenceDataAccessOrEmbeddablesInCommon(JavaClass source, - String targetPackageFullName) { - - if (targetPackageFullName.startsWith("javax.persistence")) { - Matcher commonMatcher = PATTERN_COMMON.matcher(source.getPackageName()); - Matcher dataaccessMatcher = PATTERN_DATAACCESS.matcher(source.getPackageName()); - if (dataaccessMatcher.matches()) { - return true; - } - if (commonMatcher.matches() && source.getSimpleName().contains("Embeddable")) { - return true; - } - return false; - } - return true; - } - - static final ArchCondition misuse_jpa = new ArchCondition( - "use JPA outside of dataaccess layer or embeddables in common layer (Rule-E3)") { - @Override - public void check(JavaClass item, ConditionEvents events) { - - for (Dependency access : item.getDirectDependenciesFromSelf()) { - String targetPackageFullName = access.getTargetClass().getFullName(); - String targetClassDescription = access.getDescription(); - if (isUsingJavaxPersistenceDataAccessOrEmbeddablesInCommon(item, targetPackageFullName) == false) { - String message = String.format( - "JPA (%s) shall only be used in dataaccess layer or for embeddables in common layer. Violated in (%s)", - targetPackageFullName, targetClassDescription); - events.add(new SimpleConditionEvent(item, true, message)); - } - } - } - }; - - @ArchTest - static final ArchRule verifying_proper_jpa_use = noClasses().should(misuse_jpa).allowEmptyShould(true); -} diff --git a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java deleted file mode 100644 index cdf0899..0000000 --- a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.devonfw.sample.archunit; - -import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import com.tngtech.archunit.core.domain.Dependency; -import com.tngtech.archunit.core.domain.JavaClass; -import com.tngtech.archunit.core.importer.ImportOption; -import com.tngtech.archunit.junit.AnalyzeClasses; -import com.tngtech.archunit.junit.ArchTest; -import com.tngtech.archunit.lang.ArchCondition; -import com.tngtech.archunit.lang.ArchRule; -import com.tngtech.archunit.lang.ConditionEvents; -import com.tngtech.archunit.lang.SimpleConditionEvent; - -/** - * {@link DevonArchitecture3rdPartyCheck} verifying that the {@code JPA} is properly used. - */ -@AnalyzeClasses(packages = "com.devonfw.sample.archunit", importOptions = ImportOption.DoNotIncludeTests.class) -public class ThirdPartyRulesE4HibernateCheckTest { - - private static final Set DISCOURAGED_HIBERNATE_ANNOTATIONS = new HashSet<>( - Arrays.asList("OrderBy", "Entity", "AccessType", "ForeignKey", "Cascade", "Index", "IndexColumn")); - - private static final String ORG_HIBERNATE_ENVERS = "org.hibernate.envers"; - - private static final String ORG_HIBERNATE_VALIDATOR = "org.hibernate.validator"; - - private static final String ORG_HIBERNATE_ANNOTATIONS = "org.hibernate.annotations"; - - private static final Pattern PATTERN_DATAACCESS = Pattern.compile(PackageRuleTest.DATAACCESS_PATTERN); - - private static boolean isUsingHibernateOutsideOfDataaccessLayer(JavaClass source, String targetPackageName) { - - Matcher dataaccessMatcher = PATTERN_DATAACCESS.matcher(source.getPackageName()); - if (!dataaccessMatcher.matches()) { - return true; - } - return false; - } - - private static boolean isUsingProprietaryHibernateAnnotation(String targetPackageName, String targetSimpleName) { - - if (targetPackageName.equals(ORG_HIBERNATE_ANNOTATIONS) - && DISCOURAGED_HIBERNATE_ANNOTATIONS.contains(targetSimpleName)) { - return true; - } - return false; - } - - private static boolean isImplementingHibernateEnversInternalsDirectly(String targetPackageName) { - - if (targetPackageName.startsWith(ORG_HIBERNATE_ENVERS) && targetPackageName.contains("internal")) { - return true; - } - return false; - } - - static final ArchCondition misUseHibernate = new ArchCondition("misuse hibernate (Rule-E4).") { - @Override - public void check(JavaClass source, ConditionEvents events) { - - for (Dependency dependency : source.getDirectDependenciesFromSelf()) { - String targetPackageName = dependency.getTargetClass().getPackageName(); - String targetPackageFullName = dependency.getTargetClass().getFullName(); - String targetClassDescription = dependency.getDescription(); - String targetSimpleName = dependency.getTargetClass().getSimpleName(); - if (targetPackageName.startsWith("org.hibernate") && !targetPackageName.startsWith(ORG_HIBERNATE_VALIDATOR)) { - if (isUsingHibernateOutsideOfDataaccessLayer(source, targetPackageName) == true) { - String message = String.format("Hibernate (%s) should only be used in dataaccess layer. Violated in (%s)", - targetPackageFullName, targetClassDescription); - events.add(new SimpleConditionEvent(source, true, message)); - } - if (isUsingProprietaryHibernateAnnotation(targetPackageName, targetSimpleName) == true) { - String message = String.format( - "Standard JPA annotations should be used instead of this proprietary hibernate annotation (%s). Violated in (%s)", - targetPackageFullName, targetClassDescription); - events.add(new SimpleConditionEvent(source, true, message)); - } - if (isImplementingHibernateEnversInternalsDirectly(targetPackageName) == true) { - String message = String.format( - "Hibernate envers internals (%s) should never be used directly. Violated in (%s)", - targetPackageFullName, targetClassDescription); - events.add(new SimpleConditionEvent(source, true, message)); - } - } - } - } - }; - - @ArchTest - static final ArchRule jpa_is_used_as_encouraged = noClasses().should(misUseHibernate).allowEmptyShould(true); -} diff --git a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesTest.java b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesTest.java index fec9265..5916646 100644 --- a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesTest.java +++ b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesTest.java @@ -1,31 +1,244 @@ package com.devonfw.sample.archunit; -import com.tngtech.archunit.core.importer.ImportOption; +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import com.tngtech.archunit.core.domain.Dependency; +import com.tngtech.archunit.core.domain.JavaClass; +import com.tngtech.archunit.core.importer.ImportOption; import com.tngtech.archunit.junit.AnalyzeClasses; import com.tngtech.archunit.junit.ArchTest; +import com.tngtech.archunit.lang.ArchCondition; import com.tngtech.archunit.lang.ArchRule; -import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; +import com.tngtech.archunit.lang.ConditionEvents; +import com.tngtech.archunit.lang.SimpleConditionEvent; @AnalyzeClasses(packages = "com.devonfw.sample.archunit", importOptions = ImportOption.DoNotIncludeTests.class) public class ThirdPartyRulesTest { + private static final Set DISCOURAGED_HIBERNATE_ANNOTATIONS = new HashSet<>( + Arrays.asList("OrderBy", "Entity", "AccessType", "ForeignKey", "Cascade", "Index", "IndexColumn")); + + private static final String ORG_HIBERNATE_ENVERS = "org.hibernate.envers"; + + private static final String ORG_HIBERNATE_VALIDATOR = "org.hibernate.validator"; + + private static final String ORG_HIBERNATE_ANNOTATIONS = "org.hibernate.annotations"; + + @ArchTest + public static ArchRule check_object_dependency = noClasses().should().dependOnClassesThat() + .haveFullyQualifiedName("com.google.common.base.Objects") + .because("Use Java standards instead (java.util.Objects)."); + + @ArchTest + public static ArchRule check_converter_dependency = noClasses().should().dependOnClassesThat() + .haveFullyQualifiedName("javax.persistence.Convert") + .because("Use the javax.persistence.Converter annotation on a custom converter" + + " which implements the javax.persistence.AttributeConverter instead of the 'javax.persistance.Convert' annotation"); + + @ArchTest + public static ArchRule check_mysema_dependency = noClasses().should().dependOnClassesThat() + .resideInAPackage("com.mysema.query..") + .because("Use official QueryDSL (com.querydsl.* e.g. from com.querydsl:querydsl-jpa)."); + + /* + * E1 + */ + private static boolean isApiScopedClassUsingTransactional(JavaClass source, String targetPackageFullName) { + + PackageStructure sourcePkg = PackageStructure.of(source); + + if (sourcePkg.isScopeApi() && targetPackageFullName.equals("javax.transaction.Transactional")) { + return true; + } + return false; + } + + private static boolean isUsingSpringframeworkTransactionalAnnotation(String targetPackageFullName) { + + if (targetPackageFullName.equals("org.springframework.transaction.annotation.Transactional")) { + return true; + } + return false; + } + + public static final ArchCondition misuse_springframework_transactional_annotation = new ArchCondition( + "misuse @Transactional (Rule-E1)") { + @Override + public void check(JavaClass source, ConditionEvents events) { + + for (Dependency dependency : source.getDirectDependenciesFromSelf()) { + String targetFullName = dependency.getTargetClass().getFullName(); + String targetClassDescription = dependency.getDescription(); + if (isUsingSpringframeworkTransactionalAnnotation(targetFullName) == true) { + String message = String.format( + "Use JEE standard (javax.transaction.Transactional from javax.transaction:javax.transaction-api:1.2+). The use (%s) is discouraged. Violated in (%s)", + targetFullName, targetClassDescription); + events.add(new SimpleConditionEvent(source, true, message)); + } + /* + * In case the project has a classic architecture using scopes, check that no API scoped class is using + * 'javax.transaction.Transactional' + */ + if (isApiScopedClassUsingTransactional(source, targetFullName) == true) { + String message = String.format( + "The use of @Transactional in API is discouraged. Instead use it to annotate implementations. Violated in (%s)", + targetClassDescription); + events.add(new SimpleConditionEvent(source, true, message)); + } + } + } + }; + + @ArchTest + public static final ArchRule E1_verifying_proper_transactional_use_from_jee = noClasses() + .should(misuse_springframework_transactional_annotation).allowEmptyShould(true); + + /* + * E3 + */ + private static boolean isUsingJavaxPersistenceDataAccessOrEmbeddablesInCommon(JavaClass source, + String targetPackageFullName) { + + if (targetPackageFullName.startsWith("javax.persistence")) { + PackageStructure sourcePkg = PackageStructure.of(source); + if (sourcePkg.isLayerDataAccess()) { + return true; + } + if (sourcePkg.isLayerCommon() && source.getSimpleName().contains("Embeddable")) { + return true; + } + return false; + } + return true; + } + + public static final ArchCondition misuse_jpa = new ArchCondition( + "use JPA outside of dataaccess layer or embeddables in common layer (Rule-E3)") { + @Override + public void check(JavaClass source, ConditionEvents events) { + + for (Dependency dependency : source.getDirectDependenciesFromSelf()) { + String targetPackageFullName = dependency.getTargetClass().getFullName(); + String targetClassDescription = dependency.getDescription(); + if (isUsingJavaxPersistenceDataAccessOrEmbeddablesInCommon(source, targetPackageFullName) == false) { + String message = String.format( + "JPA (%s) shall only be used in dataaccess layer or for embeddables in common layer. Violated in (%s)", + targetPackageFullName, targetClassDescription); + events.add(new SimpleConditionEvent(source, true, message)); + } + } + } + }; + + @ArchTest + public static final ArchRule E3_verifying_proper_jpa_use = noClasses().should(misuse_jpa).allowEmptyShould(true); + + /* + * E4 + */ + private static boolean isUsingHibernateOutsideOfDataaccessLayer(JavaClass source, String targetPackageName) { + + PackageStructure sourcePkg = PackageStructure.of(source); + if (!sourcePkg.isLayerDataAccess()) { + return true; + } + return false; + } + + private static boolean isUsingProprietaryHibernateAnnotation(String targetPackageName, String targetSimpleName) { + + if (targetPackageName.equals(ORG_HIBERNATE_ANNOTATIONS) + && DISCOURAGED_HIBERNATE_ANNOTATIONS.contains(targetSimpleName)) { + return true; + } + return false; + } + + private static boolean isImplementingHibernateEnversInternalsDirectly(String targetPackageName) { + + if (targetPackageName.startsWith(ORG_HIBERNATE_ENVERS) && targetPackageName.contains("internal")) { + return true; + } + return false; + } + + private static boolean isUsingHibernateOutsideOfImplScope(JavaClass source, String targetPackageName) { + + PackageStructure sourcePkg = PackageStructure.of(source); + if (!sourcePkg.isScopeImpl() && targetPackageName.startsWith("org.hibernate") + && !targetPackageName.startsWith(ORG_HIBERNATE_VALIDATOR)) { + return true; + } + return false; + } + + private static boolean isNotImplementingHibernateEnversInImplScope(JavaClass source, String targetPackageName, + String targetPackageFullName, String targetSimpleName) { - @ArchTest - private static ArchRule check_object_dependency = noClasses() - .should().dependOnClassesThat() - .haveFullyQualifiedName("com.google.common.base.Objects") - .because("Use Java standards instead (java.util.Objects)."); + PackageStructure sourcePkg = PackageStructure.of(source); + if (!sourcePkg.isScopeImpl() && targetPackageName.startsWith(ORG_HIBERNATE_ENVERS) + && (!targetPackageFullName.equals(ORG_HIBERNATE_ENVERS) || targetSimpleName.startsWith("Default") + || targetSimpleName.contains("Listener") || targetSimpleName.contains("Reader"))) { + return true; + } + return false; + } - @ArchTest - private static ArchRule check_converter_dependency = noClasses() - .should().dependOnClassesThat() - .haveFullyQualifiedName("javax.persistence.Convert") - .because("Use the javax.persistence.Converter annotation on a custom converter" - + " which implements the javax.persistence.AttributeConverter instead of the 'javax.persistance.Convert' annotation"); + public static final ArchCondition misUseHibernate = new ArchCondition( + "misuse hibernate (Rule-E4).") { + @Override + public void check(JavaClass source, ConditionEvents events) { - @ArchTest - private static ArchRule check_mysema_dependency = noClasses() - .should().dependOnClassesThat().resideInAPackage("com.mysema.query..") - .because("Use official QueryDSL (com.querydsl.* e.g. from com.querydsl:querydsl-jpa)."); + for (Dependency dependency : source.getDirectDependenciesFromSelf()) { + String targetPackageName = dependency.getTargetClass().getPackageName(); + String targetPackageFullName = dependency.getTargetClass().getFullName(); + String targetClassDescription = dependency.getDescription(); + String targetSimpleName = dependency.getTargetClass().getSimpleName(); + if (targetPackageName.startsWith("org.hibernate") && !targetPackageName.startsWith(ORG_HIBERNATE_VALIDATOR)) { + if (isUsingHibernateOutsideOfDataaccessLayer(source, targetPackageName) == true) { + String message = String.format("Hibernate (%s) should only be used in dataaccess layer. Violated in (%s)", + targetPackageFullName, targetClassDescription); + events.add(new SimpleConditionEvent(source, true, message)); + } + if (isUsingProprietaryHibernateAnnotation(targetPackageName, targetSimpleName) == true) { + String message = String.format( + "Standard JPA annotations should be used instead of this proprietary hibernate annotation (%s). Violated in (%s)", + targetPackageFullName, targetClassDescription); + events.add(new SimpleConditionEvent(source, true, message)); + } + if (isImplementingHibernateEnversInternalsDirectly(targetPackageName) == true) { + String message = String.format( + "Hibernate envers internals (%s) should never be used directly. Violated in (%s)", + targetPackageFullName, targetClassDescription); + events.add(new SimpleConditionEvent(source, true, message)); + } + /* + * In case the project has a classic architecture that uses scopes, check that Hibernate.Envers are only + * utilized inside the impl scope of the dataaccess layer. In addition, Hibernate internals also need to be + * used inside the impl scope of the dataaccess layer. + */ + if (isNotImplementingHibernateEnversInImplScope(source, targetPackageName, targetPackageFullName, + targetSimpleName) == true) { + String message = String.format( + "Hibernate envers implementation (%s) should only be used in impl scope of dataaccess layer. Violated in (%s)", + targetPackageFullName, targetClassDescription); + events.add(new SimpleConditionEvent(source, true, message)); + } + if (isUsingHibernateOutsideOfImplScope(source, targetPackageName) == true) { + String message = String.format( + "Hibernate internals (%s) should only be used in impl scope of dataaccess layer. Violated in (%s)", + targetPackageFullName, targetClassDescription); + events.add(new SimpleConditionEvent(source, true, message)); + } + } + } + } + }; + @ArchTest + public static final ArchRule E4_jpa_is_used_as_encouraged = noClasses().should(misUseHibernate) + .allowEmptyShould(true); } \ No newline at end of file From 96470ff23f819178aeaec62cbd6dfce59243269e Mon Sep 17 00:00:00 2001 From: Vladislav Sehtman Date: Fri, 17 Mar 2023 08:48:29 +0100 Subject: [PATCH 10/11] Updated Violations. - deleted the deprecated distinct rule test files. - Merged the new scope-related rule tests. - added scope related violations --- ...LayerDependsOnHibernateInsideApiLayer.java | 7 ++ ...dsOnHibernateEnversOutsideOfImplLayer.java | 8 ++ ...DependsOnJavaxTransactionalInApiLayer.java | 8 ++ .../ThirdPartyRulesE1TransactionalTest.java | 48 --------- .../ThirdPartyRulesE3JpaCheckTest.java | 61 ------------ .../ThirdPartyRulesE4HibernateCheckTest.java | 99 ------------------- 6 files changed, 23 insertions(+), 208 deletions(-) create mode 100644 src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/api/E4ViolationThirdpartyLayerDependsOnHibernateInsideApiLayer.java create mode 100644 src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/misc/E4ViolationThirdPartyLayerDependsOnHibernateEnversOutsideOfImplLayer.java create mode 100644 src/main/java/com/devonfw/sample/archunit/thirdparty/service/api/E1ViolationThirdpartyLayerDependsOnJavaxTransactionalInApiLayer.java delete mode 100644 src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java delete mode 100644 src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java delete mode 100644 src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java diff --git a/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/api/E4ViolationThirdpartyLayerDependsOnHibernateInsideApiLayer.java b/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/api/E4ViolationThirdpartyLayerDependsOnHibernateInsideApiLayer.java new file mode 100644 index 0000000..29f1f0f --- /dev/null +++ b/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/api/E4ViolationThirdpartyLayerDependsOnHibernateInsideApiLayer.java @@ -0,0 +1,7 @@ +package com.devonfw.sample.archunit.thirdparty.dataaccess.api; + +import org.hibernate.Session; // Noncompliant + +public class E4ViolationThirdpartyLayerDependsOnHibernateInsideApiLayer { + Session sessionFactory; +} diff --git a/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/misc/E4ViolationThirdPartyLayerDependsOnHibernateEnversOutsideOfImplLayer.java b/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/misc/E4ViolationThirdPartyLayerDependsOnHibernateEnversOutsideOfImplLayer.java new file mode 100644 index 0000000..bd78c43 --- /dev/null +++ b/src/main/java/com/devonfw/sample/archunit/thirdparty/dataaccess/misc/E4ViolationThirdPartyLayerDependsOnHibernateEnversOutsideOfImplLayer.java @@ -0,0 +1,8 @@ +package com.devonfw.sample.archunit.thirdparty.dataaccess.misc; + +import org.hibernate.envers.Audited; // Noncompliant + +public class E4ViolationThirdPartyLayerDependsOnHibernateEnversOutsideOfImplLayer { + @Audited + String name; +} diff --git a/src/main/java/com/devonfw/sample/archunit/thirdparty/service/api/E1ViolationThirdpartyLayerDependsOnJavaxTransactionalInApiLayer.java b/src/main/java/com/devonfw/sample/archunit/thirdparty/service/api/E1ViolationThirdpartyLayerDependsOnJavaxTransactionalInApiLayer.java new file mode 100644 index 0000000..0f217e8 --- /dev/null +++ b/src/main/java/com/devonfw/sample/archunit/thirdparty/service/api/E1ViolationThirdpartyLayerDependsOnJavaxTransactionalInApiLayer.java @@ -0,0 +1,8 @@ +package com.devonfw.sample.archunit.thirdparty.service.api; + +import javax.transaction.Transactional; // Noncompliant + +@Transactional +public class E1ViolationThirdpartyLayerDependsOnJavaxTransactionalInApiLayer { + +} diff --git a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java deleted file mode 100644 index 45eb8e8..0000000 --- a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE1TransactionalTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.devonfw.sample.archunit; - -import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; - -import com.tngtech.archunit.core.domain.Dependency; -import com.tngtech.archunit.core.domain.JavaClass; -import com.tngtech.archunit.core.importer.ImportOption; -import com.tngtech.archunit.junit.AnalyzeClasses; -import com.tngtech.archunit.junit.ArchTest; -import com.tngtech.archunit.lang.ArchCondition; -import com.tngtech.archunit.lang.ArchRule; -import com.tngtech.archunit.lang.ConditionEvents; -import com.tngtech.archunit.lang.SimpleConditionEvent; - -@AnalyzeClasses(packages = "com.devonfw.sample.archunit", importOptions = ImportOption.DoNotIncludeTests.class) -public class ThirdPartyRulesE1TransactionalTest { - - private static boolean isUsingSpringframeworkTransactionalAnnotation(String targetPackageFullName) { - - if (targetPackageFullName.equals("org.springframework.transaction.annotation.Transactional")) { - return true; - } - return false; - } - - static final ArchCondition misuse_springframework_transactional_annotation = new ArchCondition( - "misuse @Transactional (Rule-E1)") { - @Override - public void check(JavaClass sourceClass, ConditionEvents events) { - - for (Dependency dependency : sourceClass.getDirectDependenciesFromSelf()) { - String targetFullName = dependency.getTargetClass().getFullName(); - String targetClassDescription = dependency.getDescription(); - if (isUsingSpringframeworkTransactionalAnnotation(targetFullName) == true) { - String message = String.format( - "Use JEE standard (javax.transaction.Transactional from javax.transaction:javax.transaction-api:1.2+). The use (%s) is discouraged. Violated in (%s)", - targetFullName, targetClassDescription); - events.add(new SimpleConditionEvent(sourceClass, true, message)); - } - } - } - }; - - @ArchTest - static final ArchRule verifying_proper_transactional_use_from_jee = noClasses() - .should(misuse_springframework_transactional_annotation).allowEmptyShould(true); - -} diff --git a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java deleted file mode 100644 index b401187..0000000 --- a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE3JpaCheckTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.devonfw.sample.archunit; - -import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import com.tngtech.archunit.core.domain.Dependency; -import com.tngtech.archunit.core.domain.JavaClass; -import com.tngtech.archunit.core.importer.ImportOption; -import com.tngtech.archunit.junit.AnalyzeClasses; -import com.tngtech.archunit.junit.ArchTest; -import com.tngtech.archunit.lang.ArchCondition; -import com.tngtech.archunit.lang.ArchRule; -import com.tngtech.archunit.lang.ConditionEvents; -import com.tngtech.archunit.lang.SimpleConditionEvent; - -@AnalyzeClasses(packages = "com.devonfw.sample.archunit", importOptions = ImportOption.DoNotIncludeTests.class) -public class ThirdPartyRulesE3JpaCheckTest { - private static final Pattern PATTERN_COMMON = Pattern.compile(PackageRuleTest.COMMON_PATTERN); - - private static final Pattern PATTERN_DATAACCESS = Pattern.compile(PackageRuleTest.DATAACCESS_PATTERN); - - private static boolean isUsingJavaxPersistenceDataAccessOrEmbeddablesInCommon(JavaClass item, - String targetPackageFullName) { - - if (targetPackageFullName.startsWith("javax.persistence")) { - Matcher commonMatcher = PATTERN_COMMON.matcher(item.getPackageName()); - Matcher dataaccessMatcher = PATTERN_DATAACCESS.matcher(item.getPackageName()); - if (dataaccessMatcher.matches()) { - return true; - } - if (commonMatcher.matches() && item.getSimpleName().contains("Embeddable")) { - return true; - } - return false; - } - return true; - } - - static ArchCondition misuse_jpa = new ArchCondition( - "use JPA outside of dataaccess layer or embeddables in common layer (Rule-E3)") { - @Override - public void check(JavaClass item, ConditionEvents events) { - - for (Dependency access : item.getDirectDependenciesFromSelf()) { - String targetPackageFullName = access.getTargetClass().getFullName(); - String targetClassDescription = access.getDescription(); - if (isUsingJavaxPersistenceDataAccessOrEmbeddablesInCommon(item, targetPackageFullName) == false) { - String message = String.format( - "JPA (%s) shall only be used in dataaccess layer or for embeddables in common layer. Violated in (%s)", - targetPackageFullName, targetClassDescription); - events.add(new SimpleConditionEvent(item, true, message)); - } - } - } - }; - - @ArchTest - static final ArchRule verifying_proper_jpa_use = noClasses().should(misuse_jpa).allowEmptyShould(true); -} diff --git a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java deleted file mode 100644 index aa38697..0000000 --- a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesE4HibernateCheckTest.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.devonfw.sample.archunit; - -import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import com.tngtech.archunit.core.domain.Dependency; -import com.tngtech.archunit.core.domain.JavaClass; -import com.tngtech.archunit.core.importer.ImportOption; -import com.tngtech.archunit.junit.AnalyzeClasses; -import com.tngtech.archunit.junit.ArchTest; -import com.tngtech.archunit.lang.ArchCondition; -import com.tngtech.archunit.lang.ArchRule; -import com.tngtech.archunit.lang.ConditionEvents; -import com.tngtech.archunit.lang.SimpleConditionEvent; - -/** - * {@link DevonArchitecture3rdPartyCheck} verifying that the {@code JPA} is properly used. - */ -@AnalyzeClasses(packages = "com.devonfw.sample.archunit", importOptions = ImportOption.DoNotIncludeTests.class) -public class ThirdPartyRulesE4HibernateCheckTest { - - private static final Set DISCOURAGED_HIBERNATE_ANNOTATIONS = new HashSet<>( - Arrays.asList("OrderBy", "Entity", "AccessType", "ForeignKey", "Cascade", "Index", "IndexColumn")); - - private static final String ORG_HIBERNATE_ENVERS = "org.hibernate.envers"; - - private static final String ORG_HIBERNATE_VALIDATOR = "org.hibernate.validator"; - - private static final String ORG_HIBERNATE_ANNOTATIONS = "org.hibernate.annotations"; - - private static final Pattern PATTERN_DATAACCESS = Pattern.compile(PackageRuleTest.DATAACCESS_PATTERN); - - private static boolean isUsingHibernateOutsideOfDataaccessLayer(JavaClass source, String targetPackageName) { - - Matcher dataaccessMatcher = PATTERN_DATAACCESS.matcher(source.getPackageName()); - if (!dataaccessMatcher.matches()) { - return true; - } - return false; - } - - private static boolean isUsingProprietaryHibernateAnnotation(String targetPackageName, String targetSimpleName) { - - if (targetPackageName.equals(ORG_HIBERNATE_ANNOTATIONS) - && DISCOURAGED_HIBERNATE_ANNOTATIONS.contains(targetSimpleName)) { - return true; - } - return false; - } - - private static boolean isImplementingHibernateEnversInternalsDirectly(String targetPackageName) { - - if (targetPackageName.startsWith(ORG_HIBERNATE_ENVERS) && targetPackageName.contains("internal")) { - return true; - } - return false; - } - - static final ArchCondition misUseHibernate = new ArchCondition("misuse hibernate (Rule-E4).") { - @Override - public void check(JavaClass item, ConditionEvents events) { - - for (Dependency access : item.getDirectDependenciesFromSelf()) { - String targetPackageName = access.getTargetClass().getPackageName(); - String targetPackageFullName = access.getTargetClass().getFullName(); - String targetClassDescription = access.getDescription(); - String targetSimpleName = access.getTargetClass().getSimpleName(); - if (targetPackageName.startsWith("org.hibernate") && !targetPackageName.startsWith(ORG_HIBERNATE_VALIDATOR)) { - - if (isUsingHibernateOutsideOfDataaccessLayer(item, targetPackageName) == true) { - String message = String.format("Hibernate (%s) should only be used in dataaccess layer. Violated in (%s)", - targetPackageFullName, targetClassDescription); - events.add(new SimpleConditionEvent(item, true, message)); - } - if (isUsingProprietaryHibernateAnnotation(targetPackageName, targetSimpleName) == true) { - String message = String.format( - "Standard JPA annotations should be used, (Discouraged annotations: %s) instead this proprietary hibernate annotation (%s) was used. Violated in (%s)", - DISCOURAGED_HIBERNATE_ANNOTATIONS, targetPackageFullName, targetClassDescription); - events.add(new SimpleConditionEvent(item, true, message)); - } - if (isImplementingHibernateEnversInternalsDirectly(targetPackageName) == true) { - String message = String.format( - "Hibernate envers internals (%s) should never be used directly. Violated in (%s)", - targetPackageFullName, targetClassDescription); - events.add(new SimpleConditionEvent(item, true, message)); - } - } - } - } - }; - - @ArchTest - static final ArchRule jpa_is_used_as_encouraged = noClasses().should(misUseHibernate).allowEmptyShould(true); -} From 639545e426a042e05fb64f60eb04840bf05df3dc Mon Sep 17 00:00:00 2001 From: Vladislav Sehtman Date: Tue, 21 Mar 2023 11:10:54 +0100 Subject: [PATCH 11/11] Update ThirdPartyRulesTest.java -Implemented the most recent annotation of @hohwille. -Minor refactoring. --- .../sample/archunit/ThirdPartyRulesTest.java | 82 ++++++++----------- 1 file changed, 34 insertions(+), 48 deletions(-) diff --git a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesTest.java b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesTest.java index 5916646..e79ba10 100644 --- a/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesTest.java +++ b/src/test/java/com/devonfw/sample/archunit/ThirdPartyRulesTest.java @@ -43,9 +43,6 @@ public class ThirdPartyRulesTest { .resideInAPackage("com.mysema.query..") .because("Use official QueryDSL (com.querydsl.* e.g. from com.querydsl:querydsl-jpa)."); - /* - * E1 - */ private static boolean isApiScopedClassUsingTransactional(JavaClass source, String targetPackageFullName) { PackageStructure sourcePkg = PackageStructure.of(source); @@ -56,28 +53,14 @@ private static boolean isApiScopedClassUsingTransactional(JavaClass source, Stri return false; } - private static boolean isUsingSpringframeworkTransactionalAnnotation(String targetPackageFullName) { - - if (targetPackageFullName.equals("org.springframework.transaction.annotation.Transactional")) { - return true; - } - return false; - } - - public static final ArchCondition misuse_springframework_transactional_annotation = new ArchCondition( - "misuse @Transactional (Rule-E1)") { + public static ArchCondition verifyingTransactionalAnnotationIsNotUsedInsideApi = new ArchCondition( + "use @Transactional in API") { @Override public void check(JavaClass source, ConditionEvents events) { for (Dependency dependency : source.getDirectDependenciesFromSelf()) { String targetFullName = dependency.getTargetClass().getFullName(); String targetClassDescription = dependency.getDescription(); - if (isUsingSpringframeworkTransactionalAnnotation(targetFullName) == true) { - String message = String.format( - "Use JEE standard (javax.transaction.Transactional from javax.transaction:javax.transaction-api:1.2+). The use (%s) is discouraged. Violated in (%s)", - targetFullName, targetClassDescription); - events.add(new SimpleConditionEvent(source, true, message)); - } /* * In case the project has a classic architecture using scopes, check that no API scoped class is using * 'javax.transaction.Transactional' @@ -93,12 +76,14 @@ public void check(JavaClass source, ConditionEvents events) { }; @ArchTest - public static final ArchRule E1_verifying_proper_transactional_use_from_jee = noClasses() - .should(misuse_springframework_transactional_annotation).allowEmptyShould(true); + public static ArchRule verifyingSpringframeworkTransactionalIsNotUsed = noClasses().should().dependOnClassesThat() + .haveFullyQualifiedName("org.springframework.transaction.annotation.Transactional") + .because("Use JEE standard (javax.transaction.Transactional from javax.transaction:javax.transaction-api:1.2+)."); + + @ArchTest + public static ArchRule verifyingProperTransactionalUseFromJee = noClasses() + .should(verifyingTransactionalAnnotationIsNotUsedInsideApi).allowEmptyShould(true); - /* - * E3 - */ private static boolean isUsingJavaxPersistenceDataAccessOrEmbeddablesInCommon(JavaClass source, String targetPackageFullName) { @@ -116,7 +101,7 @@ private static boolean isUsingJavaxPersistenceDataAccessOrEmbeddablesInCommon(Ja } public static final ArchCondition misuse_jpa = new ArchCondition( - "use JPA outside of dataaccess layer or embeddables in common layer (Rule-E3)") { + "use JPA outside of dataaccess layer or embeddables in common layer") { @Override public void check(JavaClass source, ConditionEvents events) { @@ -134,12 +119,9 @@ public void check(JavaClass source, ConditionEvents events) { }; @ArchTest - public static final ArchRule E3_verifying_proper_jpa_use = noClasses().should(misuse_jpa).allowEmptyShould(true); + public static final ArchRule verifyingProperJpaUse = noClasses().should(misuse_jpa).allowEmptyShould(true); - /* - * E4 - */ - private static boolean isUsingHibernateOutsideOfDataaccessLayer(JavaClass source, String targetPackageName) { + private static boolean isUsingHibernateOutsideOfDataaccessLayer(JavaClass source) { PackageStructure sourcePkg = PackageStructure.of(source); if (!sourcePkg.isLayerDataAccess()) { @@ -148,8 +130,10 @@ private static boolean isUsingHibernateOutsideOfDataaccessLayer(JavaClass source return false; } - private static boolean isUsingProprietaryHibernateAnnotation(String targetPackageName, String targetSimpleName) { + private static boolean isUsingProprietaryHibernateAnnotation(JavaClass targetClass) { + String targetPackageName = targetClass.getPackageName(); + String targetSimpleName = targetClass.getSimpleName(); if (targetPackageName.equals(ORG_HIBERNATE_ANNOTATIONS) && DISCOURAGED_HIBERNATE_ANNOTATIONS.contains(targetSimpleName)) { return true; @@ -157,16 +141,18 @@ private static boolean isUsingProprietaryHibernateAnnotation(String targetPackag return false; } - private static boolean isImplementingHibernateEnversInternalsDirectly(String targetPackageName) { + private static boolean isImplementingHibernateEnversInternalsDirectly(JavaClass targetClass) { + String targetPackageName = targetClass.getPackageName(); if (targetPackageName.startsWith(ORG_HIBERNATE_ENVERS) && targetPackageName.contains("internal")) { return true; } return false; } - private static boolean isUsingHibernateOutsideOfImplScope(JavaClass source, String targetPackageName) { + private static boolean isUsingHibernateOutsideOfImplScope(JavaClass source, JavaClass targetClass) { + String targetPackageName = targetClass.getPackageName(); PackageStructure sourcePkg = PackageStructure.of(source); if (!sourcePkg.isScopeImpl() && targetPackageName.startsWith("org.hibernate") && !targetPackageName.startsWith(ORG_HIBERNATE_VALIDATOR)) { @@ -175,9 +161,11 @@ private static boolean isUsingHibernateOutsideOfImplScope(JavaClass source, Stri return false; } - private static boolean isNotImplementingHibernateEnversInImplScope(JavaClass source, String targetPackageName, - String targetPackageFullName, String targetSimpleName) { + private static boolean isNotImplementingHibernateEnversInImplScope(JavaClass source, JavaClass targetClass) { + String targetPackageName = targetClass.getPackageName(); + String targetPackageFullName = targetClass.getFullName(); + String targetSimpleName = targetClass.getSimpleName(); PackageStructure sourcePkg = PackageStructure.of(source); if (!sourcePkg.isScopeImpl() && targetPackageName.startsWith(ORG_HIBERNATE_ENVERS) && (!targetPackageFullName.equals(ORG_HIBERNATE_ENVERS) || targetSimpleName.startsWith("Default") @@ -187,29 +175,29 @@ private static boolean isNotImplementingHibernateEnversInImplScope(JavaClass sou return false; } - public static final ArchCondition misUseHibernate = new ArchCondition( - "misuse hibernate (Rule-E4).") { + public static final ArchCondition misUseHibernate = new ArchCondition("misuse hibernate") { @Override public void check(JavaClass source, ConditionEvents events) { for (Dependency dependency : source.getDirectDependenciesFromSelf()) { - String targetPackageName = dependency.getTargetClass().getPackageName(); - String targetPackageFullName = dependency.getTargetClass().getFullName(); + JavaClass targetClass = dependency.getTargetClass(); + String targetPackageName = targetClass.getPackageName(); + String targetPackageFullName = targetClass.getFullName(); String targetClassDescription = dependency.getDescription(); - String targetSimpleName = dependency.getTargetClass().getSimpleName(); + if (targetPackageName.startsWith("org.hibernate") && !targetPackageName.startsWith(ORG_HIBERNATE_VALIDATOR)) { - if (isUsingHibernateOutsideOfDataaccessLayer(source, targetPackageName) == true) { + if (isUsingHibernateOutsideOfDataaccessLayer(source) == true) { String message = String.format("Hibernate (%s) should only be used in dataaccess layer. Violated in (%s)", targetPackageFullName, targetClassDescription); events.add(new SimpleConditionEvent(source, true, message)); } - if (isUsingProprietaryHibernateAnnotation(targetPackageName, targetSimpleName) == true) { + if (isUsingProprietaryHibernateAnnotation(targetClass) == true) { String message = String.format( "Standard JPA annotations should be used instead of this proprietary hibernate annotation (%s). Violated in (%s)", targetPackageFullName, targetClassDescription); events.add(new SimpleConditionEvent(source, true, message)); } - if (isImplementingHibernateEnversInternalsDirectly(targetPackageName) == true) { + if (isImplementingHibernateEnversInternalsDirectly(targetClass) == true) { String message = String.format( "Hibernate envers internals (%s) should never be used directly. Violated in (%s)", targetPackageFullName, targetClassDescription); @@ -220,14 +208,13 @@ public void check(JavaClass source, ConditionEvents events) { * utilized inside the impl scope of the dataaccess layer. In addition, Hibernate internals also need to be * used inside the impl scope of the dataaccess layer. */ - if (isNotImplementingHibernateEnversInImplScope(source, targetPackageName, targetPackageFullName, - targetSimpleName) == true) { + if (isNotImplementingHibernateEnversInImplScope(source, targetClass) == true) { String message = String.format( "Hibernate envers implementation (%s) should only be used in impl scope of dataaccess layer. Violated in (%s)", targetPackageFullName, targetClassDescription); events.add(new SimpleConditionEvent(source, true, message)); } - if (isUsingHibernateOutsideOfImplScope(source, targetPackageName) == true) { + if (isUsingHibernateOutsideOfImplScope(source, targetClass) == true) { String message = String.format( "Hibernate internals (%s) should only be used in impl scope of dataaccess layer. Violated in (%s)", targetPackageFullName, targetClassDescription); @@ -239,6 +226,5 @@ public void check(JavaClass source, ConditionEvents events) { }; @ArchTest - public static final ArchRule E4_jpa_is_used_as_encouraged = noClasses().should(misUseHibernate) - .allowEmptyShould(true); + public static final ArchRule jpaIsUsedAsEncouraged = noClasses().should(misUseHibernate).allowEmptyShould(true); } \ No newline at end of file