You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2021/07/24 14:41:43 UTC

[isis] 01/01: ISIS-2820: fixes for arch tests

This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch ISIS-2820
in repository https://gitbox.apache.org/repos/asf/isis.git

commit 36c18dfa7ea3476c7fa06cfa868553b57aea388f
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Sat Jul 24 15:40:34 2021 +0100

    ISIS-2820: fixes for arch tests
---
 .../adoc/modules/archtestsupport/pages/about.adoc  |  4 +-
 .../applib/classrules/ArchitectureDomainRules.java | 57 +++++++++++----
 .../applib/classrules/ArchitectureJdoRules.java    | 13 +---
 .../applib/classrules/ArchitectureJpaRules.java    |  9 ++-
 .../modulerules/ArchitectureModuleRules.java       | 85 +++++++---------------
 .../applib/modulerules/SubpackageEnum.java         |  4 -
 .../applib/domain/dom/ChamberOfCommerceCode.java   | 23 ++++++
 .../domain/dom/SomeDomainObject_actionMixin.java   |  2 +-
 .../applib/domain/dom/SomeDomainRepository.java    |  3 +
 .../applib/entity/jdo/JdoEntityArchTests.java      |  4 -
 .../applib/entity/jdo/dom/JdoEntity2.java          | 13 ++--
 .../applib/entity/jpa/dom/JpaEntity2.java          | 13 ++--
 12 files changed, 116 insertions(+), 114 deletions(-)

diff --git a/testing/archtestsupport/adoc/modules/archtestsupport/pages/about.adoc b/testing/archtestsupport/adoc/modules/archtestsupport/pages/about.adoc
index 97e3dfc..cd34338 100644
--- a/testing/archtestsupport/adoc/modules/archtestsupport/pages/about.adoc
+++ b/testing/archtestsupport/adoc/modules/archtestsupport/pages/about.adoc
@@ -195,10 +195,10 @@ Entity rule checks are provided for both xref:pjpa::about.adoc[] and xref:pjdo::
 * encourage static factory methods for entities:
 
 ** `every_jpa_Entity_must_have_protected_no_arg_constructor()`
-** `every_jdo_PersistenceCapable_must_have_protected_no_arg_constructor()`
 
 
-None of these rules take any arguments, so simply include (as a static field) those that you wish to enforce.
+None of these rules takes any arguments, so simply call those (from a static field) those that you wish to enforce.
+
 
 == Module Rules
 
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureDomainRules.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureDomainRules.java
index 6867ac5..d3c073e 100644
--- a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureDomainRules.java
+++ b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureDomainRules.java
@@ -2,6 +2,8 @@ package org.apache.isis.testing.archtestsupport.applib.classrules;
 
 import java.io.Serializable;
 import java.lang.annotation.Annotation;
+import java.util.function.Function;
+import java.util.function.Predicate;
 
 import javax.inject.Inject;
 import javax.xml.bind.annotation.XmlRootElement;
@@ -11,6 +13,7 @@ import com.tngtech.archunit.base.DescribedPredicate;
 import com.tngtech.archunit.core.domain.JavaAnnotation;
 import com.tngtech.archunit.core.domain.JavaClass;
 import com.tngtech.archunit.core.domain.JavaCodeUnit;
+import com.tngtech.archunit.core.domain.JavaMethod;
 import com.tngtech.archunit.core.domain.JavaModifier;
 import com.tngtech.archunit.lang.ArchCondition;
 import com.tngtech.archunit.lang.ArchRule;
@@ -120,7 +123,9 @@ public class ArchitectureDomainRules {
      * </p>
      */
     public static ArchRule every_Action_mixin_must_follow_naming_convention() {
-        return mixin_must_follow_naming_conventions(Action.class, "act");
+        return mixin_must_follow_naming_conventions(Action.class, "act",
+                mixinMethodNameToFind ->
+                        javaMethodCandidate -> javaMethodCandidate.getName().equals(mixinMethodNameToFind));
     }
 
     /**
@@ -135,7 +140,10 @@ public class ArchitectureDomainRules {
      * </p>
      */
     public static ArchRule every_Property_mixin_must_follow_naming_convention() {
-        return mixin_must_follow_naming_conventions(Property.class, "prop");
+        return mixin_must_follow_naming_conventions(Property.class, "prop",
+                mixinMethodNameToFind ->
+                        javaMethodCandidate -> javaMethodCandidate.getName().equals(mixinMethodNameToFind) &&
+                                javaMethodCandidate.getRawParameterTypes().size() == 0);
     }
 
     /**
@@ -150,51 +158,59 @@ public class ArchitectureDomainRules {
      * </p>
      */
     public static ArchRule every_Collection_mixin_must_follow_naming_convention() {
-        return mixin_must_follow_naming_conventions(Collection.class, "coll");
+        return mixin_must_follow_naming_conventions(Collection.class, "coll",
+                mixinMethodNameToFind ->
+                        javaMethodCandidate -> javaMethodCandidate.getName().equals(mixinMethodNameToFind) &&
+                                javaMethodCandidate.getRawParameterTypes().size() == 0);
     }
 
     private static ClassesShouldConjunction mixin_must_follow_naming_conventions(
             final Class<? extends Annotation> type,
-            final String mixinMethodNameDefault) {
+            final String mixinMethodNameDefault, final Function<String, Predicate<JavaMethod>> function) {
         return classes()
                 .that().areAnnotatedWith(type)
                 .should(new ArchCondition<JavaClass>("follow mixin naming conventions") {
                     @Override
                     public void check(final JavaClass item, final ConditionEvents events) {
-                        if(!item.isTopLevelClass()) {
+                        if (!item.isTopLevelClass() || item.isAnnotation()) {
                             return;
                         }
                         val oneArgConstructorParameterTypeIfAny = item.getConstructors().stream()
-                                .filter(x -> x.getRawParameterTypes().size() == 1)
                                 .map(JavaCodeUnit::getRawParameterTypes)
+                                .filter(rawParameterTypes -> rawParameterTypes.size() == 1)
                                 .map(x -> x.get(0))
-                                .findFirst()
-                                ;
-                        if(!oneArgConstructorParameterTypeIfAny.isPresent()) {
-                            events.add(new SimpleConditionEvent(item, false, item.getSimpleName() + " does not have a 1-arg constructor"));
+                                .findFirst();
+                        if (!oneArgConstructorParameterTypeIfAny.isPresent()) {
+                            events.add(new SimpleConditionEvent(item, false,
+                                    item.getSimpleName() + " does not have a 1-arg constructor"));
                             return;
                         }
                         final JavaClass parameterType = oneArgConstructorParameterTypeIfAny.get();
                         val constructorClassName = parameterType.getSimpleName();
                         val requiredPrefix = constructorClassName + "_";
-                        if(! item.getSimpleName().startsWith(requiredPrefix)) {
-                            events.add(new SimpleConditionEvent(item, false, item.getSimpleName() + " should have a prefix of '" + requiredPrefix
-                                    + "'" ));
+                        if (!item.getSimpleName().startsWith(requiredPrefix)) {
+                            events.add(new SimpleConditionEvent(item, false,
+                                    item.getSimpleName() + " should have a prefix of '" + requiredPrefix
+                                            + "'"));
                             return;
                         }
                         val mixinMethodName = item
                                 .tryGetAnnotationOfType(DomainObject.class)
                                 .transform(DomainObject::mixinMethod)
                                 .or(mixinMethodNameDefault);
-                        if(!item.tryGetMethod(mixinMethodName).isPresent()) {
+                        val mixinMethodIfAny = item.getAllMethods().stream()
+                                .filter(function.apply(mixinMethodName)).findAny();
+                        if (!mixinMethodIfAny.isPresent()) {
                             events.add(new SimpleConditionEvent(item, false,
                                     String.format("%s does not have a mixin method named '%s'",
                                             item.getSimpleName(), mixinMethodName)));
                         }
                     }
+
                 });
     }
 
+
     /**
      * This rule requires that classes annotated with the {@link Repository} annotation should follow the naming
      * convention <code>XxxRepository</code>.
@@ -332,16 +348,21 @@ public class ArchitectureDomainRules {
      *     they must return an <code>Optional&lt;Customer&gt;</code> instead.  This forces the caller to
      *     handle the fact that the result might be empty (ie no result).
      * </p>
-     * @return
+     *
+     * <p>
+     *     One exception is that methods named &quot;findOrCreate&quot;, which are allowed to return an instance
+     *     rather than an optional.
+     * </p>
      */
     public static ArchRule every_finder_method_in_Repository_must_return_either_Collection_or_Optional() {
         return methods()
                 .that().haveNameMatching("find.*")
+                .and().haveNameNotMatching("findOrCreate.*")
                 .and().areDeclaredInClassesThat().areAnnotatedWith(Repository.class)
                 .should().haveRawReturnType(eitherOptionalOrCollection());
     }
 
-    private static DescribedPredicate<JavaClass> eitherOptionalOrCollection() {
+    static DescribedPredicate<JavaClass> eitherOptionalOrCollection() {
         return new DescribedPredicate<JavaClass>("either Optional or Collection") {
             @Override
             public boolean apply(JavaClass input) {
@@ -353,4 +374,8 @@ public class ArchitectureDomainRules {
 
 
 
+
+
+
+
 }
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureJdoRules.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureJdoRules.java
index 072732e..5d0bf73 100644
--- a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureJdoRules.java
+++ b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureJdoRules.java
@@ -8,6 +8,7 @@ import com.tngtech.archunit.base.DescribedPredicate;
 import com.tngtech.archunit.core.domain.JavaAnnotation;
 import com.tngtech.archunit.core.domain.JavaClass;
 import com.tngtech.archunit.core.domain.JavaEnumConstant;
+import com.tngtech.archunit.core.domain.JavaModifier;
 import com.tngtech.archunit.lang.ArchRule;
 
 import org.apache.isis.applib.annotation.DomainObject;
@@ -186,18 +187,6 @@ public class ArchitectureJdoRules {
         };
     }
 
-    /**
-     * This rule requires that classes annotated with the JDO {@link PersistenceCapable} annotation have a
-     * no-arg constructor with <code>protected</code> visibility.
-     *
-     * <p>
-     * The rationale is to encourage the use of static factory methods.
-     * </p>
-     */
-    public static ArchRule every_jdo_PersistenceCapable_must_have_protected_no_arg_constructor() {
-        return classes().that().areAnnotatedWith(PersistenceCapable.class)
-                .should(haveNoArgProtectedConstructor());
-    }
 
 
 }
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureJpaRules.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureJpaRules.java
index dfa093d..16bd187 100644
--- a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureJpaRules.java
+++ b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/classrules/ArchitectureJpaRules.java
@@ -16,6 +16,7 @@ import javax.persistence.Version;
 import com.tngtech.archunit.base.DescribedPredicate;
 import com.tngtech.archunit.core.domain.JavaAnnotation;
 import com.tngtech.archunit.core.domain.JavaClass;
+import com.tngtech.archunit.core.domain.JavaModifier;
 import com.tngtech.archunit.lang.ArchCondition;
 import com.tngtech.archunit.lang.ArchRule;
 import com.tngtech.archunit.lang.ConditionEvents;
@@ -274,15 +275,17 @@ public class ArchitectureJpaRules {
     }
 
     /**
-     * This rule requires that classes annotated with the JPA {@link Entity} annotation have a no-arg constructor
-     * with <code>protected</code> visibility.
+     * This rule requires that concrete classes annotated with the JPA {@link Entity} annotation have a no-arg
+     * constructor with <code>protected</code> visibility.
      *
      * <p>
      * The rationale is to encourage the use of static factory methods.
      * </p>
      */
     public static ArchRule every_jpa_Entity_must_have_protected_no_arg_constructor() {
-        return classes().that().areAnnotatedWith(Entity.class)
+        return classes()
+                .that().areAnnotatedWith(Entity.class)
+                .and().doNotHaveModifier(JavaModifier.ABSTRACT)
                 .should(haveNoArgProtectedConstructor());
     }
 
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/modulerules/ArchitectureModuleRules.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/modulerules/ArchitectureModuleRules.java
index c5a35ab..d8d4142 100644
--- a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/modulerules/ArchitectureModuleRules.java
+++ b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/modulerules/ArchitectureModuleRules.java
@@ -92,7 +92,6 @@ public class ArchitectureModuleRules {
                 transitiveDependenciesByImporting.keySet());
         importingClassesNotImported.removeAll(transitiveDependenciesByImported.keySet());
 
-        checkNoAccessToTopmostLayers(layeredArchitecture, importingClassesNotImported);
         return layeredArchitecture;
     }
 
@@ -100,16 +99,12 @@ public class ArchitectureModuleRules {
             List<Class<?>> moduleClasses,
             Architectures.LayeredArchitecture layeredArchitecture,
             List<Subpackage> subpackages) {
-        moduleClasses.forEach(moduleClass -> {
-            layeredArchitecture.layer(nameOf(moduleClass)).definedBy(packageIdentifierFor(moduleClass));
-
-            subpackages
-                    .forEach(subpackage -> {
-                        val subpackageName = subpackage.getName();
-                        layeredArchitecture.optionalLayer(nameOf(moduleClass, subpackageName))
-                                .definedBy(packageIdentifierFor(moduleClass, subpackage));
-                    });
-        });
+        moduleClasses.forEach(moduleClass ->
+                subpackages.forEach(subpackage -> {
+                    val subpackageName = subpackage.getName();
+                    layeredArchitecture.optionalLayer(nameOf(moduleClass, subpackageName))
+                            .definedBy(packageIdentifierFor(moduleClass, subpackage));
+                }));
     }
 
     private static void computeDirectDependencies(
@@ -153,28 +148,25 @@ public class ArchitectureModuleRules {
             List<Subpackage> subpackages) {
         transitiveDependenciesByImported.forEach((importedModule, importingModules) -> {
 
-            layeredArchitecture
-                    // layering at the module level
-                    .whereLayer(
-                            nameOf(importedModule))
-                    .mayOnlyBeAccessedByLayers(
-                            namesOf(importingModules)
-                    );
-
-            // in particular, access to subpackages
             subpackages.forEach(subpackage -> {
 
                 val localModule = asArray(subpackage.mayBeAccessedBySubpackagesInSameModule(), subpackages);
                 val otherModules = asArray(subpackage.mayBeAccessedBySubpackagesInReferencingModules(), subpackages);
 
                 final String moduleName = nameOf(importedModule, subpackage.getName());
-                final String[] accessingModules = both(
-                        namesOf(importedModule, localModule),
-                        namesOf(importingModules, otherModules)
-                );
-                layeredArchitecture
-                        .whereLayer(moduleName)
-                        .mayOnlyBeAccessedByLayers(accessingModules);
+                val localModulePackageNames = namesOf(importedModule, localModule);
+                val importingModulePackageNames = namesOf(importingModules, otherModules);
+                val accessingModules = both(localModulePackageNames, importingModulePackageNames);
+                if(accessingModules.length > 0) {
+                    layeredArchitecture
+                            .whereLayer(moduleName)
+                            .mayOnlyBeAccessedByLayers(accessingModules);
+
+                } else {
+                    layeredArchitecture
+                            .whereLayer(moduleName)
+                            .mayNotBeAccessedByAnyLayer();
+                }
             });
 
         });
@@ -192,20 +184,6 @@ public class ArchitectureModuleRules {
         return otherModules;
     }
 
-    private static void checkNoAccessToTopmostLayers(
-            Architectures.LayeredArchitecture layeredArchitecture,
-            Set<Class<?>> importingClassesNotImported) {
-        importingClassesNotImported.forEach(importingClass -> {
-            final String importingModuleName = nameOf(importingClass);
-            layeredArchitecture
-                    .whereLayer(importingModuleName)
-                    .mayNotBeAccessedByAnyLayer();
-        });
-    }
-
-    static String nameOf(Class<?> moduleClass) {
-        return nameOf(moduleClass, null);
-    }
 
     static String nameOf(Class<?> moduleClass, @Nullable final String subpackageName) {
         val simpleName = moduleClass.getSimpleName();
@@ -214,29 +192,17 @@ public class ArchitectureModuleRules {
     }
 
     static String[] namesOf(Class<?> moduleClass, String... subpackageNames) {
-        val names = new ArrayList<String>();
-        if (subpackageNames == null) {
-            return null;
-        }
-        if (subpackageNames.length == 0 ||
-                subpackageNames.length == 1 && subpackageNames[0].equals("*")) {
-            names.add(nameOf(moduleClass));
-        } else {
-            Arrays.stream(subpackageNames).forEach(subpackageName ->
-                    names.add(nameOf(moduleClass, subpackageName)));
-        }
+        val names = Arrays.stream(subpackageNames)
+                .map(subpackageName -> nameOf(moduleClass, subpackageName))
+                .collect(Collectors.toList());
         return names.toArray(new String[] {});
     }
 
-    static String[] namesOf(Set<Class<?>> importingClasses, @Nullable String... subpackageNames) {
+    static String[] namesOf(Set<Class<?>> importingClasses, String... subpackageNames) {
         val names = new ArrayList<String>();
         importingClasses.forEach(importingClass -> {
-            if (subpackageNames == null || subpackageNames.length == 0) {
-                names.add(nameOf(importingClass));
-            } else {
-                Stream.of(subpackageNames).forEach(subpackageName ->
-                        names.add(nameOf(importingClass, subpackageName)));
-            }
+            Stream.of(subpackageNames).forEach(subpackageName ->
+                    names.add(nameOf(importingClass, subpackageName)));
         });
         return names.toArray(new String[] {});
     }
@@ -285,5 +251,4 @@ public class ArchitectureModuleRules {
                 accumulateTransitiveDependencies(directDependency, directDependenciesByReferringClass,
                         transitiveDependenciesOfReferringClass));
     }
-
 }
\ No newline at end of file
diff --git a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/modulerules/SubpackageEnum.java b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/modulerules/SubpackageEnum.java
index 7d0833e..134466d 100644
--- a/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/modulerules/SubpackageEnum.java
+++ b/testing/archtestsupport/applib/src/main/java/org/apache/isis/testing/archtestsupport/applib/modulerules/SubpackageEnum.java
@@ -162,8 +162,4 @@ public enum SubpackageEnum implements Subpackage {
         return referencing;
     }
 
-    private static String[] asArray(List<String> list) {
-        return list != null ?
-                list.toArray(new String[] {}) : null;
-    }
 }
diff --git a/testing/archtestsupport/applib/src/test/java/org/apache/isis/testing/archtestsupport/applib/domain/dom/ChamberOfCommerceCode.java b/testing/archtestsupport/applib/src/test/java/org/apache/isis/testing/archtestsupport/applib/domain/dom/ChamberOfCommerceCode.java
new file mode 100644
index 0000000..df067fd
--- /dev/null
+++ b/testing/archtestsupport/applib/src/test/java/org/apache/isis/testing/archtestsupport/applib/domain/dom/ChamberOfCommerceCode.java
@@ -0,0 +1,23 @@
+package org.apache.isis.testing.archtestsupport.applib.domain.dom;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.isis.applib.annotation.Optionality;
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.ParameterLayout;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.PropertyLayout;
+
+@javax.jdo.annotations.Column(length = ChamberOfCommerceCode.MAX_LEN, allowsNull = "true")
+@Property(maxLength = ChamberOfCommerceCode.MAX_LEN, optionality = Optionality.OPTIONAL)
+@PropertyLayout(named = "Chamber of Commerce Code")
+@Parameter(maxLength = ChamberOfCommerceCode.MAX_LEN, optionality = Optionality.OPTIONAL)
+@ParameterLayout(named = "Chamber of Commerce Code")
+@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ChamberOfCommerceCode {
+    int MAX_LEN = 30;
+}
diff --git a/testing/archtestsupport/applib/src/test/java/org/apache/isis/testing/archtestsupport/applib/domain/dom/SomeDomainObject_actionMixin.java b/testing/archtestsupport/applib/src/test/java/org/apache/isis/testing/archtestsupport/applib/domain/dom/SomeDomainObject_actionMixin.java
index e8247df..f862200 100644
--- a/testing/archtestsupport/applib/src/test/java/org/apache/isis/testing/archtestsupport/applib/domain/dom/SomeDomainObject_actionMixin.java
+++ b/testing/archtestsupport/applib/src/test/java/org/apache/isis/testing/archtestsupport/applib/domain/dom/SomeDomainObject_actionMixin.java
@@ -11,6 +11,6 @@ public class SomeDomainObject_actionMixin {
 
     final SomeDomainObject someDomainObject;
 
-    public void act() {}
+    public void act(final String x) {}
 
 }
diff --git a/testing/archtestsupport/applib/src/test/java/org/apache/isis/testing/archtestsupport/applib/domain/dom/SomeDomainRepository.java b/testing/archtestsupport/applib/src/test/java/org/apache/isis/testing/archtestsupport/applib/domain/dom/SomeDomainRepository.java
index 80806ce..3e53a26 100644
--- a/testing/archtestsupport/applib/src/test/java/org/apache/isis/testing/archtestsupport/applib/domain/dom/SomeDomainRepository.java
+++ b/testing/archtestsupport/applib/src/test/java/org/apache/isis/testing/archtestsupport/applib/domain/dom/SomeDomainRepository.java
@@ -13,4 +13,7 @@ public interface SomeDomainRepository {
     List<SomeDomainObject> findAll();
 
     Map<String,SomeDomainObject> notFinder();
+
+    // we allow these as an exception to the "finder" rule.
+    SomeDomainObject findOrCreate();
 }
diff --git a/testing/archtestsupport/applib/src/test/java/org/apache/isis/testing/archtestsupport/applib/entity/jdo/JdoEntityArchTests.java b/testing/archtestsupport/applib/src/test/java/org/apache/isis/testing/archtestsupport/applib/entity/jdo/JdoEntityArchTests.java
index d994760..f6ea0c5 100644
--- a/testing/archtestsupport/applib/src/test/java/org/apache/isis/testing/archtestsupport/applib/entity/jdo/JdoEntityArchTests.java
+++ b/testing/archtestsupport/applib/src/test/java/org/apache/isis/testing/archtestsupport/applib/entity/jdo/JdoEntityArchTests.java
@@ -12,7 +12,6 @@ import static org.apache.isis.testing.archtestsupport.applib.classrules.Architec
 import static org.apache.isis.testing.archtestsupport.applib.classrules.ArchitectureJdoRules.every_jdo_PersistenceCapable_must_be_annotated_as_XmlJavaAdapter_PersistentEntityAdapter;
 import static org.apache.isis.testing.archtestsupport.applib.classrules.ArchitectureJdoRules.every_jdo_PersistenceCapable_must_be_annotated_with_DomainObject_nature_of_ENTITY;
 import static org.apache.isis.testing.archtestsupport.applib.classrules.ArchitectureJdoRules.every_jdo_PersistenceCapable_must_be_annotated_with_Version;
-import static org.apache.isis.testing.archtestsupport.applib.classrules.ArchitectureJdoRules.every_jdo_PersistenceCapable_must_have_protected_no_arg_constructor;
 import static org.apache.isis.testing.archtestsupport.applib.classrules.ArchitectureJdoRules.every_jdo_PersistenceCapable_must_have_schema;
 import static org.apache.isis.testing.archtestsupport.applib.classrules.ArchitectureJdoRules.every_jdo_PersistenceCapable_must_implement_Comparable;
 import static org.apache.isis.testing.archtestsupport.applib.classrules.ArchitectureJdoRules.every_jdo_PersistenceCapable_with_DATASTORE_identityType_must_be_annotated_as_DataStoreIdentity;
@@ -56,8 +55,5 @@ public class JdoEntityArchTests {
   public static ArchRule every_injected_field_of_jdo_PersistenceCapable_must_be_annotated_with_NotPersistent =
       every_injected_field_of_jdo_PersistenceCapable_must_be_annotated_with_NotPersistent();
 
-  @ArchTest
-  public static ArchRule every_jdo_PersistenceCapable_must_have_protected_no_arg_constructor =
-      every_jdo_PersistenceCapable_must_have_protected_no_arg_constructor();
 
 }
diff --git a/testing/archtestsupport/applib/src/test/java/org/apache/isis/testing/archtestsupport/applib/entity/jdo/dom/JdoEntity2.java b/testing/archtestsupport/applib/src/test/java/org/apache/isis/testing/archtestsupport/applib/entity/jdo/dom/JdoEntity2.java
index 96d279d..a740743 100644
--- a/testing/archtestsupport/applib/src/test/java/org/apache/isis/testing/archtestsupport/applib/entity/jdo/dom/JdoEntity2.java
+++ b/testing/archtestsupport/applib/src/test/java/org/apache/isis/testing/archtestsupport/applib/entity/jdo/dom/JdoEntity2.java
@@ -11,18 +11,19 @@ import org.apache.isis.applib.annotation.DomainObject;
 import org.apache.isis.applib.annotation.Nature;
 import org.apache.isis.applib.jaxb.PersistentEntityAdapter;
 
-import lombok.AccessLevel;
-import lombok.RequiredArgsConstructor;
-
 @PersistenceCapable(schema = "jdo")
 @Unique(name = "name", members = {"name"})
 @Version
 @DomainObject(nature = Nature.ENTITY)
-@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
 @XmlJavaTypeAdapter(PersistentEntityAdapter.class)
-public class JdoEntity2 implements Comparable<JdoEntity2> {
+public abstract class JdoEntity2 implements Comparable<JdoEntity2> {
+
+    private final String name;
 
-    private String name;
+    // abstract classes do not need to have no-arg constructor
+    public JdoEntity2(final String name) {
+        this.name = name;
+    }
 
     @Override public int compareTo(final JdoEntity2 o) {
         return Comparator.<JdoEntity2,String>comparing(x -> x.name).compare(this,o);
diff --git a/testing/archtestsupport/applib/src/test/java/org/apache/isis/testing/archtestsupport/applib/entity/jpa/dom/JpaEntity2.java b/testing/archtestsupport/applib/src/test/java/org/apache/isis/testing/archtestsupport/applib/entity/jpa/dom/JpaEntity2.java
index 0e2b89f..26b9988 100644
--- a/testing/archtestsupport/applib/src/test/java/org/apache/isis/testing/archtestsupport/applib/entity/jpa/dom/JpaEntity2.java
+++ b/testing/archtestsupport/applib/src/test/java/org/apache/isis/testing/archtestsupport/applib/entity/jpa/dom/JpaEntity2.java
@@ -16,9 +16,6 @@ import org.apache.isis.applib.annotation.Nature;
 import org.apache.isis.applib.jaxb.PersistentEntityAdapter;
 import org.apache.isis.persistence.jpa.applib.integration.JpaEntityInjectionPointResolver;
 
-import lombok.AccessLevel;
-import lombok.RequiredArgsConstructor;
-
 @Entity
 @Table(
         schema = "jpa",
@@ -26,9 +23,8 @@ import lombok.RequiredArgsConstructor;
 )
 @DomainObject(nature = Nature.ENTITY)
 @XmlJavaTypeAdapter(PersistentEntityAdapter.class)
-@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
 @EntityListeners({ JpaEntityInjectionPointResolver.class})
-public class JpaEntity2 implements Comparable<JpaEntity2> {
+public abstract class JpaEntity2 implements Comparable<JpaEntity2> {
 
     @Id @Column(name = "id", nullable = false)
     private Long id;
@@ -36,7 +32,12 @@ public class JpaEntity2 implements Comparable<JpaEntity2> {
     @Version
     private Long version;
 
-    private String name;
+    private final String name;
+
+    // abstract classes do not need to have no-arg constructor
+    public JpaEntity2(final String name) {
+        this.name = name;
+    }
 
     @Override public int compareTo(final JpaEntity2 o) {
         return Comparator.<JpaEntity2,Long>comparing(x -> x.id).compare(this,o);