Skip to content

Commit 4698274

Browse files
authored
#5: Added thirdparty rules E1, E3, and E4 (#22)
1 parent c53e411 commit 4698274

File tree

4 files changed

+246
-31
lines changed

4 files changed

+246
-31
lines changed

src/test/java/com/devonfw/sample/archunit/PackageRuleTest.java

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package com.devonfw.sample.archunit;
22
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
33

4+
import java.util.regex.Matcher;
5+
import java.util.regex.Pattern;
6+
47
import com.tngtech.archunit.core.domain.JavaClass;
58
import com.tngtech.archunit.core.importer.ImportOption;
69
import com.tngtech.archunit.junit.AnalyzeClasses;
@@ -10,58 +13,63 @@
1013
import com.tngtech.archunit.lang.ConditionEvents;
1114
import com.tngtech.archunit.lang.SimpleConditionEvent;
1215

13-
import java.util.regex.Matcher;
14-
import java.util.regex.Pattern;
15-
1616
/**
1717
* JUnit test that validates the Packages of this application.
1818
*/
1919
@AnalyzeClasses(packages = "com.devonfw.sample.archunit", importOptions = ImportOption.DoNotIncludeTests.class)
2020
public class PackageRuleTest {
2121

22-
/*Layer */
23-
private static final String LAYER_COMMON = "common";
22+
/* Layer */
23+
private static final String LAYER_COMMON = "common";
2424

25-
private static final String LAYER_DATA_ACCESS = "dataaccess";
25+
private static final String LAYER_DATA_ACCESS = "dataaccess";
2626

27-
private static final String LAYER_LOGIC = "logic";
27+
private static final String LAYER_LOGIC = "logic";
2828

29-
private static final String LAYER_SERVICE = "service";
29+
private static final String LAYER_SERVICE = "service";
3030

31-
private static final String LAYER_CLIENT = "client";
31+
private static final String LAYER_CLIENT = "client";
3232

33-
private static final String LAYER_BATCH = "batch";
33+
private static final String LAYER_BATCH = "batch";
3434

35-
/* Pattern */
36-
private static final String PATTERN_SEGMENT = "[a-z0-9_]+";
35+
/* Pattern */
36+
private static final String PATTERN_SEGMENT = "[a-z0-9_]+";
3737

38-
private static final String PATTERN_LAYERS = LAYER_COMMON + "|"
39-
+ LAYER_DATA_ACCESS + "|" + LAYER_SERVICE + "|" + LAYER_LOGIC + "|" + LAYER_BATCH + "|"
40-
+ LAYER_CLIENT;
41-
42-
private static final String ROOT_PACKAGE =
43-
// ....1..........................2
44-
"(" + PATTERN_SEGMENT +")(\\." + PATTERN_SEGMENT +")*";
38+
private static final String PATTERN_LAYERS = LAYER_COMMON + "|" + LAYER_DATA_ACCESS + "|" + LAYER_SERVICE + "|"
39+
+ LAYER_LOGIC + "|" + LAYER_BATCH + "|" + LAYER_CLIENT;
4540

46-
private static final String DEFAULT_PATTERN =
47-
// ....1......................2...........................3...........................4
48-
"(" + ROOT_PACKAGE + ")\\.(" + PATTERN_SEGMENT + ")\\.(" + PATTERN_LAYERS + ")(\\." + PATTERN_SEGMENT + ")*";
41+
private static final String ROOT_PACKAGE =
42+
// ....1..........................2
43+
"(" + PATTERN_SEGMENT + ")(\\." + PATTERN_SEGMENT + ")*";
4944

50-
private static final Pattern PATTERN = Pattern.compile(DEFAULT_PATTERN);
45+
private static final String DEFAULT_PATTERN =
46+
// ....1......................2...........................3...........................4
47+
"(" + ROOT_PACKAGE + ")\\.(" + PATTERN_SEGMENT + ")\\.(" + PATTERN_LAYERS + ")(\\." + PATTERN_SEGMENT + ")*";
5148

52-
/* ArchRule and Condition */
49+
public static final String COMMON_PATTERN =
50+
// ....1......................2...........................3...........................4
51+
"(" + ROOT_PACKAGE + ")\\.(" + PATTERN_SEGMENT + ")\\.(" + LAYER_COMMON + ")(\\." + PATTERN_SEGMENT + ")*";
52+
53+
public static final String DATAACCESS_PATTERN =
54+
// ....1......................2...........................3...........................4
55+
"(" + ROOT_PACKAGE + ")\\.(" + PATTERN_SEGMENT + ")\\.(" + LAYER_DATA_ACCESS + ")(\\." + PATTERN_SEGMENT + ")*";
56+
57+
private static final Pattern PATTERN = Pattern.compile(DEFAULT_PATTERN);
58+
59+
/* ArchRule and Condition */
5360
@ArchTest
5461
public ArchRule shouldHaveValidLayers = classes().should(containsValidPackageStructureCondition());
55-
56-
62+
5763
private static ArchCondition<JavaClass> containsValidPackageStructureCondition() {
58-
return new ArchCondition<JavaClass>("check for the package structure to be valid", new Object(){}) {
64+
65+
return new ArchCondition<JavaClass>("check for the package structure to be valid", new Object() {
66+
}) {
5967
@Override
6068
public void check(JavaClass javaClass, ConditionEvents events) {
61-
Matcher matcher = PATTERN.matcher(javaClass.getPackageName());
62-
String message = javaClass.getSimpleName() + " test result is " + matcher.matches();
63-
events.add(new SimpleConditionEvent(javaClass,
64-
matcher.matches(), message));
69+
70+
Matcher matcher = PATTERN.matcher(javaClass.getPackageName());
71+
String message = javaClass.getSimpleName() + " test result is " + matcher.matches();
72+
events.add(new SimpleConditionEvent(javaClass, matcher.matches(), message));
6573
}
6674
};
6775
};
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.devonfw.sample.archunit;
2+
3+
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
4+
5+
import com.tngtech.archunit.core.domain.Dependency;
6+
import com.tngtech.archunit.core.domain.JavaClass;
7+
import com.tngtech.archunit.core.importer.ImportOption;
8+
import com.tngtech.archunit.junit.AnalyzeClasses;
9+
import com.tngtech.archunit.junit.ArchTest;
10+
import com.tngtech.archunit.lang.ArchCondition;
11+
import com.tngtech.archunit.lang.ArchRule;
12+
import com.tngtech.archunit.lang.ConditionEvents;
13+
import com.tngtech.archunit.lang.SimpleConditionEvent;
14+
15+
@AnalyzeClasses(packages = "com.devonfw.sample.archunit", importOptions = ImportOption.DoNotIncludeTests.class)
16+
public class ThirdPartyRulesE1TransactionalTest {
17+
18+
private static boolean isUsingSpringframeworkTransactionalAnnotation(String targetPackageFullName) {
19+
20+
if (targetPackageFullName.equals("org.springframework.transaction.annotation.Transactional")) {
21+
return true;
22+
}
23+
return false;
24+
}
25+
26+
static final ArchCondition<JavaClass> misuse_springframework_transactional_annotation = new ArchCondition<JavaClass>(
27+
"misuse @Transactional (Rule-E1)") {
28+
@Override
29+
public void check(JavaClass sourceClass, ConditionEvents events) {
30+
31+
for (Dependency dependency : sourceClass.getDirectDependenciesFromSelf()) {
32+
String targetFullName = dependency.getTargetClass().getFullName();
33+
String targetClassDescription = dependency.getDescription();
34+
if (isUsingSpringframeworkTransactionalAnnotation(targetFullName) == true) {
35+
String message = String.format(
36+
"Use JEE standard (javax.transaction.Transactional from javax.transaction:javax.transaction-api:1.2+). The use (%s) is discouraged. Violated in (%s)",
37+
targetFullName, targetClassDescription);
38+
events.add(new SimpleConditionEvent(sourceClass, true, message));
39+
}
40+
}
41+
}
42+
};
43+
44+
@ArchTest
45+
static final ArchRule verifying_proper_transactional_use_from_jee = noClasses()
46+
.should(misuse_springframework_transactional_annotation).allowEmptyShould(true);
47+
48+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.devonfw.sample.archunit;
2+
3+
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
4+
5+
import java.util.regex.Matcher;
6+
import java.util.regex.Pattern;
7+
8+
import com.tngtech.archunit.core.domain.Dependency;
9+
import com.tngtech.archunit.core.domain.JavaClass;
10+
import com.tngtech.archunit.core.importer.ImportOption;
11+
import com.tngtech.archunit.junit.AnalyzeClasses;
12+
import com.tngtech.archunit.junit.ArchTest;
13+
import com.tngtech.archunit.lang.ArchCondition;
14+
import com.tngtech.archunit.lang.ArchRule;
15+
import com.tngtech.archunit.lang.ConditionEvents;
16+
import com.tngtech.archunit.lang.SimpleConditionEvent;
17+
18+
@AnalyzeClasses(packages = "com.devonfw.sample.archunit", importOptions = ImportOption.DoNotIncludeTests.class)
19+
public class ThirdPartyRulesE3JpaCheckTest {
20+
private static final Pattern PATTERN_COMMON = Pattern.compile(PackageRuleTest.COMMON_PATTERN);
21+
22+
private static final Pattern PATTERN_DATAACCESS = Pattern.compile(PackageRuleTest.DATAACCESS_PATTERN);
23+
24+
private static boolean isUsingJavaxPersistenceDataAccessOrEmbeddablesInCommon(JavaClass source,
25+
String targetPackageFullName) {
26+
27+
if (targetPackageFullName.startsWith("javax.persistence")) {
28+
Matcher commonMatcher = PATTERN_COMMON.matcher(source.getPackageName());
29+
Matcher dataaccessMatcher = PATTERN_DATAACCESS.matcher(source.getPackageName());
30+
if (dataaccessMatcher.matches()) {
31+
return true;
32+
}
33+
if (commonMatcher.matches() && source.getSimpleName().contains("Embeddable")) {
34+
return true;
35+
}
36+
return false;
37+
}
38+
return true;
39+
}
40+
41+
static final ArchCondition<JavaClass> misuse_jpa = new ArchCondition<JavaClass>(
42+
"use JPA outside of dataaccess layer or embeddables in common layer (Rule-E3)") {
43+
@Override
44+
public void check(JavaClass item, ConditionEvents events) {
45+
46+
for (Dependency access : item.getDirectDependenciesFromSelf()) {
47+
String targetPackageFullName = access.getTargetClass().getFullName();
48+
String targetClassDescription = access.getDescription();
49+
if (isUsingJavaxPersistenceDataAccessOrEmbeddablesInCommon(item, targetPackageFullName) == false) {
50+
String message = String.format(
51+
"JPA (%s) shall only be used in dataaccess layer or for embeddables in common layer. Violated in (%s)",
52+
targetPackageFullName, targetClassDescription);
53+
events.add(new SimpleConditionEvent(item, true, message));
54+
}
55+
}
56+
}
57+
};
58+
59+
@ArchTest
60+
static final ArchRule verifying_proper_jpa_use = noClasses().should(misuse_jpa).allowEmptyShould(true);
61+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package com.devonfw.sample.archunit;
2+
3+
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
4+
5+
import java.util.Arrays;
6+
import java.util.HashSet;
7+
import java.util.Set;
8+
import java.util.regex.Matcher;
9+
import java.util.regex.Pattern;
10+
11+
import com.tngtech.archunit.core.domain.Dependency;
12+
import com.tngtech.archunit.core.domain.JavaClass;
13+
import com.tngtech.archunit.core.importer.ImportOption;
14+
import com.tngtech.archunit.junit.AnalyzeClasses;
15+
import com.tngtech.archunit.junit.ArchTest;
16+
import com.tngtech.archunit.lang.ArchCondition;
17+
import com.tngtech.archunit.lang.ArchRule;
18+
import com.tngtech.archunit.lang.ConditionEvents;
19+
import com.tngtech.archunit.lang.SimpleConditionEvent;
20+
21+
/**
22+
* {@link DevonArchitecture3rdPartyCheck} verifying that the {@code JPA} is properly used.
23+
*/
24+
@AnalyzeClasses(packages = "com.devonfw.sample.archunit", importOptions = ImportOption.DoNotIncludeTests.class)
25+
public class ThirdPartyRulesE4HibernateCheckTest {
26+
27+
private static final Set<String> DISCOURAGED_HIBERNATE_ANNOTATIONS = new HashSet<>(
28+
Arrays.asList("OrderBy", "Entity", "AccessType", "ForeignKey", "Cascade", "Index", "IndexColumn"));
29+
30+
private static final String ORG_HIBERNATE_ENVERS = "org.hibernate.envers";
31+
32+
private static final String ORG_HIBERNATE_VALIDATOR = "org.hibernate.validator";
33+
34+
private static final String ORG_HIBERNATE_ANNOTATIONS = "org.hibernate.annotations";
35+
36+
private static final Pattern PATTERN_DATAACCESS = Pattern.compile(PackageRuleTest.DATAACCESS_PATTERN);
37+
38+
private static boolean isUsingHibernateOutsideOfDataaccessLayer(JavaClass source, String targetPackageName) {
39+
40+
Matcher dataaccessMatcher = PATTERN_DATAACCESS.matcher(source.getPackageName());
41+
if (!dataaccessMatcher.matches()) {
42+
return true;
43+
}
44+
return false;
45+
}
46+
47+
private static boolean isUsingProprietaryHibernateAnnotation(String targetPackageName, String targetSimpleName) {
48+
49+
if (targetPackageName.equals(ORG_HIBERNATE_ANNOTATIONS)
50+
&& DISCOURAGED_HIBERNATE_ANNOTATIONS.contains(targetSimpleName)) {
51+
return true;
52+
}
53+
return false;
54+
}
55+
56+
private static boolean isImplementingHibernateEnversInternalsDirectly(String targetPackageName) {
57+
58+
if (targetPackageName.startsWith(ORG_HIBERNATE_ENVERS) && targetPackageName.contains("internal")) {
59+
return true;
60+
}
61+
return false;
62+
}
63+
64+
static final ArchCondition<JavaClass> misUseHibernate = new ArchCondition<JavaClass>("misuse hibernate (Rule-E4).") {
65+
@Override
66+
public void check(JavaClass source, ConditionEvents events) {
67+
68+
for (Dependency dependency : source.getDirectDependenciesFromSelf()) {
69+
String targetPackageName = dependency.getTargetClass().getPackageName();
70+
String targetPackageFullName = dependency.getTargetClass().getFullName();
71+
String targetClassDescription = dependency.getDescription();
72+
String targetSimpleName = dependency.getTargetClass().getSimpleName();
73+
if (targetPackageName.startsWith("org.hibernate") && !targetPackageName.startsWith(ORG_HIBERNATE_VALIDATOR)) {
74+
if (isUsingHibernateOutsideOfDataaccessLayer(source, targetPackageName) == true) {
75+
String message = String.format("Hibernate (%s) should only be used in dataaccess layer. Violated in (%s)",
76+
targetPackageFullName, targetClassDescription);
77+
events.add(new SimpleConditionEvent(source, true, message));
78+
}
79+
if (isUsingProprietaryHibernateAnnotation(targetPackageName, targetSimpleName) == true) {
80+
String message = String.format(
81+
"Standard JPA annotations should be used instead of this proprietary hibernate annotation (%s). Violated in (%s)",
82+
targetPackageFullName, targetClassDescription);
83+
events.add(new SimpleConditionEvent(source, true, message));
84+
}
85+
if (isImplementingHibernateEnversInternalsDirectly(targetPackageName) == true) {
86+
String message = String.format(
87+
"Hibernate envers internals (%s) should never be used directly. Violated in (%s)",
88+
targetPackageFullName, targetClassDescription);
89+
events.add(new SimpleConditionEvent(source, true, message));
90+
}
91+
}
92+
}
93+
}
94+
};
95+
96+
@ArchTest
97+
static final ArchRule jpa_is_used_as_encouraged = noClasses().should(misUseHibernate).allowEmptyShould(true);
98+
}

0 commit comments

Comments
 (0)