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:32 UTC

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

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 ""; }
+    }*/
+
+}