You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by ah...@apache.org on 2023/05/25 08:15:31 UTC

[causeway] branch 3444-validation.mixin.sort created (now 1d30c731e6)

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

ahuber pushed a change to branch 3444-validation.mixin.sort
in repository https://gitbox.apache.org/repos/asf/causeway.git


      at 1d30c731e6 CAUSEWAY-3444: refactor MixinFacet to also provide info whether act/pro/coll (WIP)

This branch includes the following new commits:

     new 1d30c731e6 CAUSEWAY-3444: refactor MixinFacet to also provide info whether act/pro/coll (WIP)

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[causeway] 01/01: CAUSEWAY-3444: refactor MixinFacet to also provide info whether act/pro/coll (WIP)

Posted by ah...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch 3444-validation.mixin.sort
in repository https://gitbox.apache.org/repos/asf/causeway.git

commit 1d30c731e6c7a31efd95030fb125720c54a9ddf6
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu May 25 10:15:25 2023 +0200

    CAUSEWAY-3444: refactor MixinFacet to also provide info whether
    act/pro/coll (WIP)
---
 .../applib/services/metamodel/BeanSort.java        |  8 +-
 .../metamodel/facets/object/mixin/MixinFacet.java  | 34 +++++++-
 .../facets/object/mixin/MixinFacetAbstract.java    | 25 +++---
 .../mixin/MixinFacetForDomainObjectAnnotation.java |  2 +-
 .../postprocessors/all/SanityChecksValidator2.java | 91 ++++++++++++++++++++++
 .../dflt/ProgrammingModelFacetsJava11.java         |  3 +
 .../core/metamodel/spec/ObjectSpecification.java   |  8 +-
 .../specimpl/ObjectSpecificationAbstract.java      | 26 ++++++-
 .../specloader/validator/MetaModelValidator.java   |  6 +-
 .../object/mixin/MixinFacetAbstract_Test.java      |  4 +-
 .../DomainModelTest_usingBadDomain.java            | 25 ++++--
 .../model/bad/InvalidMixinDeclarations.java        | 56 +++++++++++++
 12 files changed, 259 insertions(+), 29 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/BeanSort.java b/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/BeanSort.java
index 08c7cfe0f7..5490a6e4bc 100644
--- a/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/BeanSort.java
+++ b/api/applib/src/main/java/org/apache/causeway/applib/services/metamodel/BeanSort.java
@@ -20,7 +20,6 @@ package org.apache.causeway.applib.services.metamodel;
 
 import org.springframework.context.annotation.Profile;
 
-import org.apache.causeway.applib.annotation.Domain;
 import org.apache.causeway.applib.annotation.Programmatic;
 
 /**
@@ -59,8 +58,9 @@ public enum BeanSort {
      */
     MANAGED_BEAN_NOT_CONTRIBUTING,
     /**
-     * Object associated with an 'entity' or 'bean' to act as contributer of
-     * domain actions or properties. Might also be stateful similar to VIEW_MODEL.
+     * Object associated with an <i>entity</i>, <i>viewmodel</i> or <i>domain-service</i>
+     * to act as contributer of a single <i>domain-action</i> or
+     * <i>domain-property</i> or <i>domain-collection</i>.
      */
     MIXIN,
     /**
@@ -77,7 +77,7 @@ public enum BeanSort {
     ABSTRACT,
     /**
      * Type must not be added to the meta-model, eg. by means of
-     * {@link Domain#Exclude}, {@link Profile} or {@link Programmatic}
+     * {@link org.apache.causeway.applib.annotation.Domain.Exclude}, {@link Profile} or {@link Programmatic}
      */
     VETOED,
     UNKNOWN;
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/mixin/MixinFacet.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/mixin/MixinFacet.java
index 50fc64d364..965ab2247e 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/mixin/MixinFacet.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/mixin/MixinFacet.java
@@ -25,7 +25,7 @@ import org.apache.causeway.applib.annotation.Collection;
 import org.apache.causeway.applib.annotation.DomainObject;
 import org.apache.causeway.applib.annotation.Nature;
 import org.apache.causeway.applib.annotation.Property;
-import org.apache.causeway.core.metamodel.facets.SingleValueFacet;
+import org.apache.causeway.core.metamodel.facetapi.Facet;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
 
 /**
@@ -40,7 +40,37 @@ import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
  * the type recognized as a mix-in. These are {@link Action}, {@link Property} and
  * {@link Collection}.
  */
-public interface MixinFacet extends SingleValueFacet<String> {
+public interface MixinFacet extends Facet {
+
+    public enum MixinSort {
+        /**
+         * FIXME refactoring step
+         */
+        @Deprecated
+        AUTO,
+        /**
+         * Object associated with an <i>entity</i>, <i>viewmodel</i> or <i>domain-service</i>
+         * to act as contributer of a single <i>domain-action</i>.
+         */
+        MIXIN_FOR_ACT,
+        /**
+         * Object associated with an <i>entity</i>, <i>viewmodel</i> or <i>domain-service</i>
+         * to act as contributer of a single <i>domain-property</i>.
+         */
+        MIXIN_PROP,
+        /**
+         * Object associated with an <i>entity</i>, <i>viewmodel</i> or <i>domain-service</i>
+         * to act as contributer of a single <i>domain-collection</i>.
+         */
+        MIXIN_COLL,
+    }
+
+    MixinSort getMixinSort();
+
+    /**
+     * The mixin's main method name.
+     */
+    String getMainMethodName();
 
     boolean isMixinFor(Class<?> candidateDomainType);
 
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/mixin/MixinFacetAbstract.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/mixin/MixinFacetAbstract.java
index 26f06d0acd..c708b2871e 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/mixin/MixinFacetAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/mixin/MixinFacetAbstract.java
@@ -25,17 +25,23 @@ import java.util.function.BiConsumer;
 
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
 import org.apache.causeway.core.metamodel.facetapi.Facet;
+import org.apache.causeway.core.metamodel.facetapi.FacetAbstract;
 import org.apache.causeway.core.metamodel.facetapi.FacetHolder;
-import org.apache.causeway.core.metamodel.facets.SingleValueFacetAbstract;
 
+import lombok.Getter;
 import lombok.NonNull;
 import lombok.val;
 
 //@Log4j2
 public abstract class MixinFacetAbstract
-extends SingleValueFacetAbstract<String>
+extends FacetAbstract
 implements MixinFacet {
 
+    @Getter(onMethod_={@Override})
+    private final @NonNull String mainMethodName;
+    @Getter(onMethod_={@Override})
+    private final @NonNull MixinSort mixinSort;
+
     private final @NonNull Class<?> mixinType;
     private final @NonNull Class<?> holderType;
     private final @NonNull Constructor<?> constructor;
@@ -46,12 +52,15 @@ implements MixinFacet {
 
     protected MixinFacetAbstract(
             final Class<?> mixinType,
+            final MixinSort mixinSort,
             final String mainMethodName,
             final Constructor<?> constructor,
             final FacetHolder holder) {
 
-        super(type(), mainMethodName, holder);
+        super(type(), holder);
+        this.mainMethodName = mainMethodName;
         this.mixinType = mixinType;
+        this.mixinSort = mixinSort;
         this.constructor = constructor;
         // by mixin convention: first constructor argument is identified as the holder type
         this.holderType = constructor.getParameterTypes()[0];
@@ -113,15 +122,9 @@ implements MixinFacet {
     public void visitAttributes(final BiConsumer<String, Object> visitor) {
         super.visitAttributes(visitor);
         visitor.accept("mixinType", mixinType);
+        visitor.accept("mixinSort", mixinSort);
+        visitor.accept("mainMethodName", mainMethodName);
         visitor.accept("holderType", holderType);
     }
 
-    /**
-     * The mixin's main method name.
-     * @implNote as stored in the SingleValueFacetAbstract's value field
-     */
-    public String getMainMethodName() {
-        return super.value();
-    }
-
 }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/mixin/MixinFacetForDomainObjectAnnotation.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/mixin/MixinFacetForDomainObjectAnnotation.java
index 6578a62ce8..bd18176f0a 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/mixin/MixinFacetForDomainObjectAnnotation.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/mixin/MixinFacetForDomainObjectAnnotation.java
@@ -60,6 +60,6 @@ extends MixinFacetAbstract {
             final String mixinMethodName,
             final Constructor<?> constructor,
             final FacetHolder holder) {
-        super(mixinClass, mixinMethodName, constructor, holder);
+        super(mixinClass, MixinSort.AUTO, mixinMethodName, constructor, holder);
     }
 }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/postprocessors/all/SanityChecksValidator2.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/postprocessors/all/SanityChecksValidator2.java
new file mode 100644
index 0000000000..ca0af7e6d9
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/postprocessors/all/SanityChecksValidator2.java
@@ -0,0 +1,91 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.causeway.core.metamodel.postprocessors.all;
+
+import javax.inject.Inject;
+
+import org.apache.causeway.core.metamodel.context.MetaModelContext;
+import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectAction;
+import org.apache.causeway.core.metamodel.spec.feature.OneToManyAssociation;
+import org.apache.causeway.core.metamodel.spec.feature.OneToOneAssociation;
+import org.apache.causeway.core.metamodel.specloader.validator.MetaModelValidator;
+import org.apache.causeway.core.metamodel.specloader.validator.MetaModelValidatorAbstract;
+
+/**
+ * Checks various preconditions for a sane meta-model.
+ * <ul>
+ *
+ * <li>Guard against members that contribute vetoed or managed types.
+ * Those are not allowed as member/return/param.</li>
+ * </ul>
+ */
+public class SanityChecksValidator2
+extends MetaModelValidatorAbstract
+implements
+    MetaModelValidator.ActionValidator,
+    MetaModelValidator.PropertyValidator,
+    MetaModelValidator.CollectionValidator {
+
+    @Inject
+    public SanityChecksValidator2(final MetaModelContext mmc) {
+        super(mmc, MIXINS);
+    }
+
+    @Override
+    public void validateObjectEnter(final ObjectSpecification objSpec) {
+        System.err.printf("mixin %s%n", objSpec);
+    }
+
+    @Override
+    public void validateObjectExit(final ObjectSpecification objSpec) {
+    }
+
+    @Override
+    public void validateAction(final ObjectSpecification objectSpecification, final ObjectAction objectAction) {
+
+    }
+
+    @Override
+    public void validateProperty(final ObjectSpecification objectSpecification, final OneToOneAssociation prop) {
+
+    }
+
+    @Override
+    public void validateCollection(final ObjectSpecification objectSpecification, final OneToManyAssociation coll) {
+
+    }
+
+    // -- HELPER
+
+//    private void checkElementType(
+//            final FacetHolder facetHolder,
+//            final ObjectSpecification declaringType,
+//            final ObjectSpecification elementType) {
+//
+//        if(elementType == null
+//                || elementType.getBeanSort().isManagedBeanAny()
+//                || elementType.getBeanSort().isMixin()
+//                || elementType.getBeanSort().isVetoed()) {
+//
+//            ValidationFailureUtils.raiseInvalidMemberElementType(facetHolder, declaringType, elementType);
+//        }
+//    }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/progmodels/dflt/ProgrammingModelFacetsJava11.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/progmodels/dflt/ProgrammingModelFacetsJava11.java
index 4c4028518f..39667a9a0e 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/progmodels/dflt/ProgrammingModelFacetsJava11.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/progmodels/dflt/ProgrammingModelFacetsJava11.java
@@ -86,6 +86,7 @@ import org.apache.causeway.core.metamodel.methods.DomainIncludeAnnotationEnforce
 import org.apache.causeway.core.metamodel.postprocessors.all.CssOnActionFromConfiguredRegexPostProcessor;
 import org.apache.causeway.core.metamodel.postprocessors.all.DescribedAsFromTypePostProcessor;
 import org.apache.causeway.core.metamodel.postprocessors.all.SanityChecksValidator;
+import org.apache.causeway.core.metamodel.postprocessors.all.SanityChecksValidator2;
 import org.apache.causeway.core.metamodel.postprocessors.all.i18n.SynthesizeObjectNamingPostProcessor;
 import org.apache.causeway.core.metamodel.postprocessors.all.i18n.TranslationPostProcessor;
 import org.apache.causeway.core.metamodel.postprocessors.allbutparam.authorization.AuthorizationPostProcessor;
@@ -272,6 +273,8 @@ extends ProgrammingModelAbstract {
         addValidator(ValidationOrder.A1_BUILTIN, new ActionAnnotationShouldEnforceConcreteTypeToBeIncludedWithMetamodelValidator(mmc));
         addValidator(ValidationOrder.A1_BUILTIN, new ActionOverloadingValidator(mmc));
         addValidator(ValidationOrder.A1_BUILTIN, new LogicalTypeMalformedValidator(mmc));
+
+        addValidator(ValidationOrder.A1_BUILTIN, new SanityChecksValidator2(mmc));
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/ObjectSpecification.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/ObjectSpecification.java
index ea564878f3..b2175fb5d8 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/ObjectSpecification.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/spec/ObjectSpecification.java
@@ -59,6 +59,7 @@ import org.apache.causeway.core.metamodel.facets.object.entity.EntityFacet;
 import org.apache.causeway.core.metamodel.facets.object.icon.IconFacet;
 import org.apache.causeway.core.metamodel.facets.object.icon.ObjectIcon;
 import org.apache.causeway.core.metamodel.facets.object.immutable.ImmutableFacet;
+import org.apache.causeway.core.metamodel.facets.object.mixin.MixinFacet.MixinSort;
 import org.apache.causeway.core.metamodel.facets.object.parented.ParentedCollectionFacet;
 import org.apache.causeway.core.metamodel.facets.object.title.TitleFacet;
 import org.apache.causeway.core.metamodel.facets.object.title.TitleRenderRequest;
@@ -285,11 +286,16 @@ extends
     Optional<ObjectSpecification> getElementSpecification();
 
     /**
-     *
      * @since 2.0
      */
     BeanSort getBeanSort();
 
+    /**
+     * @since 2.0
+     */
+    Optional<MixinSort> getMixinSort();
+
+
     // //////////////////////////////////////////////////////////////
     // TitleContext
     // //////////////////////////////////////////////////////////////
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
index e19a25dc6e..0f7e2278cb 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/specimpl/ObjectSpecificationAbstract.java
@@ -67,6 +67,7 @@ import org.apache.causeway.core.metamodel.facets.object.icon.ObjectIcon;
 import org.apache.causeway.core.metamodel.facets.object.immutable.ImmutableFacet;
 import org.apache.causeway.core.metamodel.facets.object.logicaltype.AliasedFacet;
 import org.apache.causeway.core.metamodel.facets.object.mixin.MixinFacet;
+import org.apache.causeway.core.metamodel.facets.object.mixin.MixinFacet.MixinSort;
 import org.apache.causeway.core.metamodel.facets.object.navparent.NavigableParentFacet;
 import org.apache.causeway.core.metamodel.facets.object.parented.ParentedCollectionFacet;
 import org.apache.causeway.core.metamodel.facets.object.title.TitleFacet;
@@ -93,6 +94,7 @@ import static org.apache.causeway.commons.internal.base._NullSafe.stream;
 import lombok.EqualsAndHashCode;
 import lombok.Getter;
 import lombok.val;
+import lombok.experimental.Accessors;
 import lombok.extern.log4j.Log4j2;
 
 @EqualsAndHashCode(of = "correspondingClass", callSuper = false)
@@ -533,6 +535,11 @@ implements ObjectSpecification {
         return helpFacet == null ? null : helpFacet.value();
     }
 
+    @Override
+    public final Optional<MixinSort> getMixinSort() {
+        return lookupMixinFacet()
+                .map(MixinFacet::getMixinSort);
+    }
 
     // -- FACET HANDLING
 
@@ -565,6 +572,21 @@ implements ObjectSpecification {
         }
     }
 
+    @Getter(lazy = true) @Accessors(fluent=true)
+    private final Optional<MixinFacet> mixinFacet = lookupMixinFacet();
+
+    private Optional<MixinFacet> lookupMixinFacet() {
+        if(!isMixin()) {
+            return Optional.empty();
+        }
+        val mixinFacet = getFacet(MixinFacet.class);
+        if(mixinFacet==null) {
+            throw _Exceptions.illegalState("type %s has BeanSort MIXIN but ended up NOT having a MixinFacet",
+                    getCorrespondingClass());
+        }
+        return Optional.of(mixinFacet);
+    }
+
     @Domain.Exclude
     private static class NotANoopFacetFilter<Q extends Facet> implements Predicate<Q> {
         Q noopFacet;
@@ -736,7 +758,7 @@ implements ObjectSpecification {
         if(!mixinFacet.isMixinFor(getCorrespondingClass())) {
             return Stream.empty();
         }
-        val mixinMethodName = mixinFacet.value();
+        val mixinMethodName = mixinFacet.getMainMethodName();
 
         return specification.streamActions(ActionScope.ANY, MixedIn.INCLUDED)
         .filter(_SpecPredicates::isMixedInAssociation)
@@ -775,7 +797,7 @@ implements ObjectSpecification {
             return Stream.empty();
         }
 
-        val mixinMethodName = mixinFacet.value();
+        val mixinMethodName = mixinFacet.getMainMethodName();
 
         return mixinSpec.streamActions(ActionScope.ANY, MixedIn.EXCLUDED)
         // value types only support constructor mixins
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/validator/MetaModelValidator.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/validator/MetaModelValidator.java
index 34a4360a2e..9358824956 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/validator/MetaModelValidator.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/specloader/validator/MetaModelValidator.java
@@ -99,10 +99,14 @@ public interface MetaModelValidator {
     public final static Predicate<ObjectSpecification> SKIP_MANAGED_BEANS =
             spec->!spec.isInjectable();
 
-    /** types pass this filter, if not a mixin */
+    /** types pass this filter, if is NOT a mixin */
     public final static Predicate<ObjectSpecification> SKIP_MIXINS =
             spec->!spec.isMixin();
 
+    /** types pass this filter, if IS a mixin */
+    public final static Predicate<ObjectSpecification> MIXINS =
+            spec->spec.isMixin();
+
     /** types pass this filter, if either not {@link ObjectSpecificationAbstract} or member-annotation is not required */
     public final static Predicate<ObjectSpecification> SKIP_WHEN_MEMBER_ANNOT_REQUIRED =
             spec->(!(spec instanceof ObjectSpecificationAbstract)
diff --git a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/object/mixin/MixinFacetAbstract_Test.java b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/object/mixin/MixinFacetAbstract_Test.java
index 00a8036cfd..de751d38a3 100644
--- a/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/object/mixin/MixinFacetAbstract_Test.java
+++ b/core/metamodel/src/test/java/org/apache/causeway/core/metamodel/facets/object/mixin/MixinFacetAbstract_Test.java
@@ -21,6 +21,8 @@ package org.apache.causeway.core.metamodel.facets.object.mixin;
 import org.assertj.core.api.Assertions;
 import org.junit.jupiter.api.Test;
 
+import org.apache.causeway.core.metamodel.facets.object.mixin.MixinFacet.MixinSort;
+
 import lombok.val;
 
 class MixinFacetAbstract_Test {
@@ -41,7 +43,7 @@ class MixinFacetAbstract_Test {
         // given
         val constructor = Collection_numberOfChildren.class.getConstructor(Object.class);
         val facet = new MixinFacetAbstract(
-                Collection_numberOfChildren.class, "prop", constructor, null) {};
+                Collection_numberOfChildren.class, MixinSort.MIXIN_PROP, "prop", constructor, null) {};
 
         val propMethodInSubclass = SimpleObject_numberOfChildren.class.getMethod("prop");
 
diff --git a/regressiontests/stable-domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/DomainModelTest_usingBadDomain.java b/regressiontests/stable-domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/DomainModelTest_usingBadDomain.java
index 3e781a7c4e..c738f02adc 100644
--- a/regressiontests/stable-domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/DomainModelTest_usingBadDomain.java
+++ b/regressiontests/stable-domainmodel/src/test/java/org/apache/causeway/testdomain/domainmodel/DomainModelTest_usingBadDomain.java
@@ -65,6 +65,7 @@ import org.apache.causeway.testdomain.model.bad.InvalidDomainObjectOnInterface;
 import org.apache.causeway.testdomain.model.bad.InvalidElementTypes;
 import org.apache.causeway.testdomain.model.bad.InvalidMemberIdClash;
 import org.apache.causeway.testdomain.model.bad.InvalidMemberOverloadingWhenInherited;
+import org.apache.causeway.testdomain.model.bad.InvalidMixinDeclarations;
 import org.apache.causeway.testdomain.model.bad.InvalidObjectWithAlias;
 import org.apache.causeway.testdomain.model.bad.InvalidOrphanedActionSupport;
 import org.apache.causeway.testdomain.model.bad.InvalidOrphanedCollectionSupport;
@@ -126,7 +127,7 @@ class DomainModelTest_usingBadDomain {
     void orphanedActionSupport_shouldFail() {
         validator.assertAnyFailuresContaining(
                 Identifier.classIdentifier(LogicalType.fqcn(InvalidOrphanedActionSupport.class)),
-                validationMessage(
+                unsatisfiedDomainIncludeSemantics(
                         "InvalidOrphanedActionSupport",
                         "hideOrphaned()"));
 
@@ -141,7 +142,7 @@ class DomainModelTest_usingBadDomain {
     void orphanedPropertySupport_shouldFail() {
         validator.assertAnyFailuresContaining(
                 Identifier.classIdentifier(LogicalType.fqcn(InvalidOrphanedPropertySupport.class)),
-                validationMessage(
+                unsatisfiedDomainIncludeSemantics(
                         "InvalidOrphanedPropertySupport",
                         "hideMyProperty()"));
 
@@ -155,7 +156,7 @@ class DomainModelTest_usingBadDomain {
     void orphanedCollectionSupport_shouldFail() {
         validator.assertAnyFailuresContaining(
                 Identifier.classIdentifier(LogicalType.fqcn(InvalidOrphanedCollectionSupport.class)),
-                validationMessage(
+                unsatisfiedDomainIncludeSemantics(
                         "InvalidOrphanedCollectionSupport",
                         "hideMyCollection()"));
 
@@ -208,14 +209,14 @@ class DomainModelTest_usingBadDomain {
         validator.assertAnyFailuresContaining(
                 Identifier.classIdentifier(LogicalType.fqcn(
                         InvalidMemberOverloadingWhenInherited.WhenAnnotationRequired.class)),
-                validationMessage(
+                unsatisfiedDomainIncludeSemantics(
                         "",
                         "isActive()"));
 
         validator.assertAnyFailuresContaining(
                 Identifier.classIdentifier(LogicalType.fqcn(
                         InvalidMemberOverloadingWhenInherited.WhenEncapsulationEnabled.class)),
-                validationMessage(
+                unsatisfiedDomainIncludeSemantics(
                         "",
                         "isActive()"));
     }
@@ -405,6 +406,18 @@ class DomainModelTest_usingBadDomain {
                 "has a member with vetoed, mixin or managed element-type");
     }
 
+    // -- MIXINS
+
+    @ParameterizedTest
+    @ValueSource(classes = {
+            InvalidMixinDeclarations.ActionMixinWithProp.class,
+            })
+    void invalidMixinDeclaration(final Class<?> classUnderTest) {
+        validator.assertAnyFailuresContaining(
+                classUnderTest,
+                "has a member with vetoed, mixin or managed element-type");
+    }
+
     // -- INCUBATING
 
     @Test @Disabled("this case has no vaildation refiner yet")
@@ -427,7 +440,7 @@ class DomainModelTest_usingBadDomain {
 
     // -- HELPER
 
-    private String validationMessage(
+    private String unsatisfiedDomainIncludeSemantics(
             final String className,
             final String memberName) {
         return Violation.UNSATISFIED_DOMAIN_INCLUDE_SEMANTICS
diff --git a/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/model/bad/InvalidMixinDeclarations.java b/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/model/bad/InvalidMixinDeclarations.java
new file mode 100644
index 0000000000..b0a196185b
--- /dev/null
+++ b/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/model/bad/InvalidMixinDeclarations.java
@@ -0,0 +1,56 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.causeway.testdomain.model.bad;
+
+import javax.inject.Named;
+
+import org.apache.causeway.applib.annotation.Action;
+import org.apache.causeway.applib.annotation.ActionLayout;
+import org.apache.causeway.applib.annotation.DomainObject;
+import org.apache.causeway.applib.annotation.Introspection;
+import org.apache.causeway.applib.annotation.MemberSupport;
+import org.apache.causeway.applib.annotation.Nature;
+
+import lombok.RequiredArgsConstructor;
+
+@DomainObject(nature = Nature.VIEW_MODEL, introspection = Introspection.ANNOTATION_REQUIRED)
+@Named("testdomain.InvalidMixinDeclarations")
+public class InvalidMixinDeclarations {
+
+    // -- INVALID SCENARIOS
+
+    @Action
+    @ActionLayout(named = "someActionProp")
+    @RequiredArgsConstructor
+    public static class ActionMixinWithProp {
+        @SuppressWarnings("unused")
+        private final InvalidMixinDeclarations invalidElementTypes;
+        @MemberSupport public String prop() { return ""; }
+    }
+/*
+    @Action
+    @ActionLayout(named = "someActionColl")
+    @RequiredArgsConstructor
+    public static class ActionMixinWithColl {
+        @SuppressWarnings("unused")
+        private final InvalidMixinDeclarations invalidElementTypes;
+        @MemberSupport public String coll() { return ""; }
+    }*/
+
+}