You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2020/02/20 19:58:49 UTC

[isis] branch master updated: ISIS-1970: adds JUnit test to reproduce the issue

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

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/master by this push:
     new 688cad9  ISIS-1970: adds JUnit test to reproduce the issue
688cad9 is described below

commit 688cad9f53ea87b206c77b90c4ad057b2795bb2a
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Feb 20 20:58:35 2020 +0100

    ISIS-1970: adds JUnit test to reproduce the issue
---
 .../applib/services/command/CommandContext.java    |   9 +
 .../core/commons/internal/ioc/IocContainer.java    |  24 ++-
 ...tySetterOrClearFacetForDomainEventAbstract.java |   7 +-
 .../specimpl/OneToOneAssociationDefault.java       |   8 +-
 .../PropertyAnnotationFacetFactoryTest.java        | 213 +++++++++++----------
 .../testdomain/conf/Configuration_headless.java    |  76 ++++----
 .../domainmodel/DomainModelTest_forEditing.java    | 184 ++++++++++++++++++
 7 files changed, 375 insertions(+), 146 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandContext.java b/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandContext.java
index 2ae2f23..e3a24fc 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandContext.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandContext.java
@@ -18,6 +18,8 @@
  */
 package org.apache.isis.applib.services.command;
 
+import java.util.Optional;
+
 import javax.enterprise.context.RequestScoped;
 import javax.inject.Named;
 
@@ -27,6 +29,7 @@ import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Service;
 
 import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.applib.services.command.Command.Executor;
 
 import lombok.Getter;
 
@@ -56,6 +59,12 @@ public class CommandContext {
     public void setCommand(final Command command) {
         this.command = command;
     }
+    
+    public Optional<Executor> getCurrentExecutor() {
+        return Optional.ofNullable(getCommand())
+                .map(Command::getExecutor); 
+    }
+    
 
 // tag::refguide[]
 }
diff --git a/core/commons/src/main/java/org/apache/isis/core/commons/internal/ioc/IocContainer.java b/core/commons/src/main/java/org/apache/isis/core/commons/internal/ioc/IocContainer.java
index 46d6f6f..0ba9a42 100644
--- a/core/commons/src/main/java/org/apache/isis/core/commons/internal/ioc/IocContainer.java
+++ b/core/commons/src/main/java/org/apache/isis/core/commons/internal/ioc/IocContainer.java
@@ -22,14 +22,18 @@ import java.lang.annotation.Annotation;
 import java.util.NoSuchElementException;
 import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import javax.annotation.Nullable;
 
 import org.apache.isis.core.commons.collections.Can;
 import org.apache.isis.core.commons.exceptions.IsisException;
+import org.apache.isis.core.commons.internal.base._With;
 import org.apache.isis.core.commons.internal.exceptions._Exceptions;
 
+import lombok.val;
+
 /**
  * 
  * @since 2.0
@@ -70,8 +74,24 @@ public interface IocContainer {
      * @throws NoSuchElementException - if the singleton is not resolvable
      */
     public default <T> T getSingletonElseFail(@Nullable Class<T> type) {
-        return getSingleton(type)
-                .orElseThrow(()->_Exceptions.noSuchElement("Cannot resolve singleton '%s'", type));
+        _With.requires(type, "type");
+        
+        val candidates = select(type);
+        
+        switch (candidates.getCardinality()) {
+        case ZERO:
+            throw _Exceptions.noSuchElement("Cannot resolve singleton '%s'", type);
+        case ONE:
+            return candidates.getFirstOrFail();
+        default:
+            throw _Exceptions.unrecoverableFormatted("Cannot resolve singleton '%s' got more than one: {%s}",
+                    type,
+                    candidates.stream()
+                    .map(Object::getClass)
+                    .map(Class::getName)
+                    .collect(Collectors.joining(", "))
+                    );
+        }
 
     }
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertySetterOrClearFacetForDomainEventAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertySetterOrClearFacetForDomainEventAbstract.java
index 9acd0c2..845ac93 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertySetterOrClearFacetForDomainEventAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/modify/PropertySetterOrClearFacetForDomainEventAbstract.java
@@ -178,14 +178,17 @@ extends SingleValueFacetAbstract<Class<? extends PropertyDomainEvent<?,?>>> {
 
         final CommandContext commandContext = getCommandContext();
         final Command command = commandContext.getCommand();
+        if( command==null ) {
+            return;
+        }
 
         final InteractionContext interactionContext = getInteractionContext();
         final Interaction interaction = interactionContext.getInteraction();
 
         final String propertyId = owningProperty.getIdentifier().toClassAndNameIdentityString();
 
-        if( command.getExecutor() == Command.Executor.USER &&
-                command.getExecuteIn() == org.apache.isis.applib.annotation.CommandExecuteIn.BACKGROUND) {
+        if( command.getExecutor() == Command.Executor.USER
+                && command.getExecuteIn() == org.apache.isis.applib.annotation.CommandExecuteIn.BACKGROUND) {
 
             // deal with background commands
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToOneAssociationDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToOneAssociationDefault.java
index d443b6e..a8c4b49 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToOneAssociationDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToOneAssociationDefault.java
@@ -20,6 +20,7 @@
 package org.apache.isis.core.metamodel.specloader.specimpl;
 
 import java.util.Collections;
+import java.util.Optional;
 
 import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.applib.services.command.Command;
@@ -312,10 +313,11 @@ public class OneToOneAssociationDefault extends ObjectAssociationAbstract implem
             final ManagedObject targetAdapter,
             final ManagedObject valueAdapterOrNull) {
 
-        final CommandContext commandContext = getCommandContext();
-        final Command command = commandContext.getCommand();
+        val currentExecutor = getCommandContext()
+                .getCurrentExecutor()
+                .orElse(Command.Executor.OTHER);
 
-        if (command.getExecutor() != Command.Executor.USER) {
+        if (currentExecutor != Command.Executor.USER) {
             return;
         }
 
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactoryTest.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactoryTest.java
index 453bc77..8f043ab 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactoryTest.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactoryTest.java
@@ -32,8 +32,11 @@ import org.junit.Test;
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 
+import org.apache.isis.applib.annotation.DomainObject;
 import org.apache.isis.applib.annotation.MementoSerialization;
+import org.apache.isis.applib.annotation.Nature;
 import org.apache.isis.applib.annotation.Optionality;
 import org.apache.isis.applib.annotation.Property;
 import org.apache.isis.applib.annotation.Where;
@@ -78,6 +81,8 @@ import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
 
+import lombok.Getter;
+import lombok.Setter;
 import lombok.val;
 
 public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUnit4TestCase {
@@ -213,14 +218,9 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
         public void withDeprecatedPostsPropertyChangedEvent_andGetterFacet_andSetterFacet() {
 
             class Customer {
-                class NamedChangedDomainEvent extends PropertyDomainEvent<Customer, String> {
-                }
+                class NamedChangedDomainEvent extends PropertyDomainEvent<Customer, String> {}
                 @Property(domainEvent = NamedChangedDomainEvent.class)
-                public String getName() {
-                    return null;
-                }
-                public void setName(final String name) {
-                }
+                @Getter @Setter private String name;
             }
 
             // given
@@ -243,7 +243,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
             }});
 
             // when
-            final FacetFactory.ProcessMethodContext processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
+            val processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
                     propertyMethod, mockMethodRemover, facetedMethod);
             processModify(facetFactory, processMethodContext);
 
@@ -274,14 +274,9 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
         public void withPropertyInteractionEvent() {
 
             class Customer {
-                class NamedChangedDomainEvent extends PropertyDomainEvent<Customer, String> {
-                }
+                class NamedChangedDomainEvent extends PropertyDomainEvent<Customer, String> {}
                 @Property(domainEvent = NamedChangedDomainEvent.class)
-                public String getName() {
-                    return null;
-                }
-                public void setName(final String name) {
-                }
+                @Getter @Setter private String name;
             }
 
             // given
@@ -301,7 +296,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
             }});
 
             // when
-            final FacetFactory.ProcessMethodContext processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
+            val processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
                     propertyMethod, mockMethodRemover, facetedMethod);
             processModify(facetFactory, processMethodContext);
 
@@ -334,11 +329,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
                 class NamedChangedDomainEvent extends PropertyDomainEvent<Customer, String> {
                 }
                 @Property(domainEvent= NamedChangedDomainEvent.class)
-                public String getName() {
-                    return null;
-                }
-                public void setName(final String name) {
-                }
+                @Getter @Setter private String name;
             }
 
             // given
@@ -357,7 +348,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
                 will(returnValue(null));
             }});
             // when
-            final FacetFactory.ProcessMethodContext processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
+            val processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
                     propertyMethod, mockMethodRemover, facetedMethod);
             processModify(facetFactory, processMethodContext);
 
@@ -387,11 +378,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
         public void withDefaultEvent() {
 
             class Customer {
-                public String getName() {
-                    return null;
-                }
-                public void setName(final String name) {
-                }
+                @Getter @Setter private String name;
             }
 
             // given
@@ -408,7 +395,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
             allowingLoadSpecificationRequestsFor(cls, propertyMethod.getReturnType());
 
             // when
-            final FacetFactory.ProcessMethodContext processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
+            val processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
                     propertyMethod, mockMethodRemover, facetedMethod);
             processModify(facetFactory, processMethodContext);
 
@@ -440,13 +427,10 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
         @Test
         public void withAnnotation() {
 
+            @SuppressWarnings("unused")
             class Customer {
                 @Property(hidden = Where.REFERENCES_PARENT)
-                public String getName() {
-                    return null;
-                }
-                public void setName(final String name) {
-                }
+                @Getter @Setter private String name;
             }
 
             // given
@@ -454,7 +438,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
             propertyMethod = findMethod(Customer.class, "getName");
 
             // when
-            final FacetFactory.ProcessMethodContext processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
+            val processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
                     propertyMethod, mockMethodRemover, facetedMethod);
             processHidden(facetFactory, processMethodContext);
 
@@ -471,6 +455,75 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
         }
 
     }
+    
+    public static class Editing_honoringClasslevelDefault extends PropertyAnnotationFacetFactoryTest {
+
+        @Test
+        public void withClassLevelAnnotation_enabling() {
+
+            @DomainObject(
+                    nature=Nature.INMEMORY_ENTITY, 
+                    editing=org.apache.isis.applib.annotation.Editing.ENABLED)
+            class Customer {
+                @Property(
+                     // should disable editing regardless of class-level
+                        editing = org.apache.isis.applib.annotation.Editing.DISABLED, 
+                        editingDisabledReason = "you cannot edit the name property"
+                        )
+                @Getter @Setter private String name;
+            }
+
+            // given
+            val customerClass = Customer.class;
+            propertyMethod = findMethod(customerClass, "getName");
+
+            // when
+            val processMethodContext = new FacetFactory.ProcessMethodContext(customerClass, null,
+                    propertyMethod, mockMethodRemover, facetedMethod);
+            processEditing(facetFactory, processMethodContext);
+
+            // then
+            val disabledFacet = facetedMethod.getFacet(DisabledFacet.class);
+            assertNotNull(disabledFacet);
+            assertTrue(disabledFacet instanceof DisabledFacetForPropertyAnnotation);
+            val disabledFacetImpl = (DisabledFacetForPropertyAnnotation) disabledFacet;
+            assertThat(disabledFacet.where(), is(Where.EVERYWHERE));
+            assertThat(disabledFacetImpl.getReason(), is("you cannot edit the name property"));
+        }
+        
+        @Test
+        public void withClassLevelAnnotation_disabling() {
+
+            @DomainObject(
+                    nature=Nature.INMEMORY_ENTITY, 
+                    editing=org.apache.isis.applib.annotation.Editing.DISABLED,
+                    editingDisabledReason = "you cannot edit this object")
+            class Customer {
+                @Property(
+                     // should enable editing regardless of class-level
+                        editing = org.apache.isis.applib.annotation.Editing.ENABLED)
+                @Getter @Setter private String name; 
+            }
+
+            // given
+            val customerClass = Customer.class;
+            propertyMethod = findMethod(customerClass, "getName");
+
+            // when
+            val processMethodContext = new FacetFactory.ProcessMethodContext(customerClass, null,
+                    propertyMethod, mockMethodRemover, facetedMethod);
+            processEditing(facetFactory, processMethodContext);
+
+            // then
+            val disabledFacet = facetedMethod.getFacet(DisabledFacet.class);
+            assertNotNull(disabledFacet);
+            assertTrue(disabledFacet instanceof DisabledFacetForPropertyAnnotation);
+            val disabledFacetImpl = (DisabledFacetForPropertyAnnotation) disabledFacet;
+            assertThat(disabledFacet.where(), is(Where.EVERYWHERE));
+            assertThat(disabledFacetImpl.getReason(), is("you cannot edit the name property"));
+        }
+        
+    }
 
     public static class Editing extends PropertyAnnotationFacetFactoryTest {
 
@@ -485,6 +538,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
                 public String getName() {
                     return null;
                 }
+                @SuppressWarnings("unused")
                 public void setName(final String name) {
                 }
             }
@@ -494,7 +548,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
             propertyMethod = findMethod(Customer.class, "getName");
 
             // when
-            final FacetFactory.ProcessMethodContext processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
+            val processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
                     propertyMethod, mockMethodRemover, facetedMethod);
             processEditing(facetFactory, processMethodContext);
 
@@ -517,11 +571,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
                 @Property(
                         maxLength = 30
                         )
-                public String getName() {
-                    return null;
-                }
-                public void setName(final String name) {
-                }
+                @Getter @Setter private String name;
             }
 
             // given
@@ -529,7 +579,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
             propertyMethod = findMethod(Customer.class, "getName");
 
             // when
-            final FacetFactory.ProcessMethodContext processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
+            val processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
                     propertyMethod, mockMethodRemover, facetedMethod);
             processMaxLength(facetFactory, processMethodContext);
 
@@ -565,11 +615,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
                 @Property(
                         mustSatisfy = {NotTooHot.class, NotTooCold.class}
                         )
-                public String getName() {
-                    return null;
-                }
-                public void setName(final String name) {
-                }
+                @Getter @Setter private String name;
             }
 
             // given
@@ -581,7 +627,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
 
 
             // when
-            final FacetFactory.ProcessMethodContext processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
+            val processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
                     propertyMethod, mockMethodRemover, facetedMethod);
             processMustSatisfy(facetFactory, processMethodContext);
 
@@ -606,11 +652,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
 
             class Customer {
                 @Property(mementoSerialization = MementoSerialization.EXCLUDED)
-                public String getName() {
-                    return null;
-                }
-                public void setName(final String name) {
-                }
+                @Getter @Setter private String name;
             }
 
             // given
@@ -618,7 +660,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
             propertyMethod = findMethod(Customer.class, "getName");
 
             // when
-            final FacetFactory.ProcessMethodContext processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
+            val processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
                     propertyMethod, mockMethodRemover, facetedMethod);
             processNotPersisted(facetFactory, processMethodContext);
 
@@ -639,11 +681,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
                 @Property(
                         optionality = Optionality.OPTIONAL
                         )
-                public String getName() {
-                    return null;
-                }
-                public void setName(final String name) {
-                }
+                @Getter @Setter private String name;
             }
 
             // given
@@ -651,7 +689,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
             propertyMethod = findMethod(Customer.class, "getName");
 
             // when
-            final FacetFactory.ProcessMethodContext processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
+            val processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
                     propertyMethod, mockMethodRemover, facetedMethod);
             processOptional(facetFactory, processMethodContext);
 
@@ -668,11 +706,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
                 @Property(
                         optionality = Optionality.MANDATORY
                         )
-                public String getName() {
-                    return null;
-                }
-                public void setName(final String name) {
-                }
+                @Getter @Setter private String name;
             }
 
             // given
@@ -680,7 +714,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
             propertyMethod = findMethod(Customer.class, "getName");
 
             // when
-            final FacetFactory.ProcessMethodContext processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
+            val processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
                     propertyMethod, mockMethodRemover, facetedMethod);
             processOptional(facetFactory, processMethodContext);
 
@@ -697,11 +731,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
                 @Property(
                         optionality = Optionality.DEFAULT
                         )
-                public String getName() {
-                    return null;
-                }
-                public void setName(final String name) {
-                }
+                @Getter @Setter private String name;
             }
 
             // given
@@ -709,7 +739,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
             propertyMethod = findMethod(Customer.class, "getName");
 
             // when
-            final FacetFactory.ProcessMethodContext processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
+            val processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
                     propertyMethod, mockMethodRemover, facetedMethod);
             processOptional(facetFactory, processMethodContext);
 
@@ -724,11 +754,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
             class Customer {
                 @Property(
                         )
-                public String getName() {
-                    return null;
-                }
-                public void setName(final String name) {
-                }
+                @Getter @Setter private String name;
             }
 
             // given
@@ -736,7 +762,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
             propertyMethod = findMethod(Customer.class, "getName");
 
             // when
-            final FacetFactory.ProcessMethodContext processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
+            val processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
                     propertyMethod, mockMethodRemover, facetedMethod);
             processOptional(facetFactory, processMethodContext);
 
@@ -756,11 +782,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
                         regexPattern = "[123].*",
                         regexPatternFlags = Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
                         )
-                public String getName() {
-                    return null;
-                }
-                public void setName(final String name) {
-                }
+                @Getter @Setter private String name;
             }
 
             // given
@@ -768,7 +790,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
             propertyMethod = findMethod(Customer.class, "getName");
 
             // when
-            final FacetFactory.ProcessMethodContext processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
+            val processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
                     propertyMethod, mockMethodRemover, facetedMethod);
             processRegEx(facetFactory, processMethodContext);
 
@@ -786,11 +808,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
             class Customer {
                 @Property(
                         )
-                public String getName() {
-                    return null;
-                }
-                public void setName(final String name) {
-                }
+                @Getter @Setter private String name;
             }
 
             // given
@@ -798,7 +816,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
             propertyMethod = findMethod(Customer.class, "getName");
 
             // when
-            final FacetFactory.ProcessMethodContext processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
+            val processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
                     propertyMethod, mockMethodRemover, facetedMethod);
             processRegEx(facetFactory, processMethodContext);
 
@@ -814,11 +832,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
                 @Property(
                         regexPattern = ""
                         )
-                public String getName() {
-                    return null;
-                }
-                public void setName(final String name) {
-                }
+                @Getter @Setter private String name;
             }
 
             // given
@@ -826,7 +840,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
             propertyMethod = findMethod(Customer.class, "getName");
 
             // when
-            final FacetFactory.ProcessMethodContext processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
+            val processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
                     propertyMethod, mockMethodRemover, facetedMethod);
             processRegEx(facetFactory, processMethodContext);
 
@@ -842,11 +856,8 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
                 @Property(
                         regexPattern = "[abc].*"
                         )
-                public int getName() {
-                    return 0;
-                }
-                public void setName(final int name) {
-                }
+                public int getName() {return 0; }
+                @SuppressWarnings("unused") public void setName(final int name) { }
             }
 
             // given
@@ -854,7 +865,7 @@ public class PropertyAnnotationFacetFactoryTest extends AbstractFacetFactoryJUni
             propertyMethod = findMethod(Customer.class, "getName");
 
             // when
-            final FacetFactory.ProcessMethodContext processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
+            val processMethodContext = new FacetFactory.ProcessMethodContext(cls, null,
                     propertyMethod, mockMethodRemover, facetedMethod);
             processRegEx(facetFactory, processMethodContext);
 
diff --git a/examples/smoketests/src/main/java/org/apache/isis/testdomain/conf/Configuration_headless.java b/examples/smoketests/src/main/java/org/apache/isis/testdomain/conf/Configuration_headless.java
index 2dfdc6a..5f4fcf6 100644
--- a/examples/smoketests/src/main/java/org/apache/isis/testdomain/conf/Configuration_headless.java
+++ b/examples/smoketests/src/main/java/org/apache/isis/testdomain/conf/Configuration_headless.java
@@ -52,44 +52,44 @@ import org.apache.isis.security.bypass.IsisModuleSecurityBypass;
 })
 public class Configuration_headless {
 
-    @Bean @Singleton
-    public TransactionService transactionService() {
-        return new TransactionService() {
-
-            @Override
-            public TransactionId currentTransactionId() {
-                return null;
-            }
-
-            @Override
-            public void flushTransaction() {
-            }
-
-            @Override
-            public TransactionState currentTransactionState() {
-                return null;
-            }
-
-            @Override
-            public void executeWithinTransaction(Runnable task) {
-            }
-
-            @Override
-            public <T> T executeWithinTransaction(Supplier<T> task) {
-                return null;
-            }
-
-            @Override
-            public void executeWithinNewTransaction(Runnable task) {
-            }
-
-            @Override
-            public <T> T executeWithinNewTransaction(Supplier<T> task) {
-                return null;
-            }
-
-        };
-    }
+//    @Bean @Singleton
+//    public TransactionService transactionService() {
+//        return new TransactionService() {
+//
+//            @Override
+//            public TransactionId currentTransactionId() {
+//                return null;
+//            }
+//
+//            @Override
+//            public void flushTransaction() {
+//            }
+//
+//            @Override
+//            public TransactionState currentTransactionState() {
+//                return null;
+//            }
+//
+//            @Override
+//            public void executeWithinTransaction(Runnable task) {
+//            }
+//
+//            @Override
+//            public <T> T executeWithinTransaction(Supplier<T> task) {
+//                return null;
+//            }
+//
+//            @Override
+//            public void executeWithinNewTransaction(Runnable task) {
+//            }
+//
+//            @Override
+//            public <T> T executeWithinNewTransaction(Supplier<T> task) {
+//                return null;
+//            }
+//
+//        };
+//    }
     
     @Bean @Singleton
     public PlatformTransactionManager platformTransactionManager() {
diff --git a/examples/smoketests/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_forEditing.java b/examples/smoketests/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_forEditing.java
new file mode 100644
index 0000000..1d1ffe8
--- /dev/null
+++ b/examples/smoketests/src/test/java/org/apache/isis/testdomain/domainmodel/DomainModelTest_forEditing.java
@@ -0,0 +1,184 @@
+/*
+ *  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.isis.testdomain.domainmodel;
+
+import javax.inject.Inject;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.TestPropertySource;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.Nature;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.services.wrapper.DisabledException;
+import org.apache.isis.applib.services.wrapper.WrapperFactory;
+import org.apache.isis.core.config.presets.IsisPresets;
+import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+import org.apache.isis.core.metamodel.specloader.specimpl.IntrospectionState;
+import org.apache.isis.testdomain.Incubating;
+import org.apache.isis.testdomain.Smoketest;
+import org.apache.isis.testdomain.conf.Configuration_headless;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.val;
+
+@Smoketest
+@SpringBootTest(
+        classes = { 
+                Configuration_headless.class,
+        }, 
+        properties = {
+                "isis.applib.annotation.domain-object.editing=TRUE",
+                "isis.core.meta-model.validator.explicit-object-type=FALSE", // does not override any of the imports
+        })
+@TestPropertySource({
+    IsisPresets.IntrospectFully,
+    IsisPresets.UseLog4j2Test,
+    IsisPresets.SilenceMetaModel,
+    IsisPresets.SilenceProgrammingModel
+})
+@DirtiesContext
+@Incubating("fails when run with surefire")
+class DomainModelTest_forEditing {
+
+    @Inject private WrapperFactory wrapper;
+    @Inject private SpecificationLoader specificationLoader;
+
+    
+    // -------------------------------------------------------
+    // type:ENABLED + property:NOT_SPECIFIED -> effective ENABLED
+    // -------------------------------------------------------
+
+
+    @DomainObject(
+            nature=Nature.INMEMORY_ENTITY, 
+            editing=org.apache.isis.applib.annotation.Editing.ENABLED)
+    public static class CustomerEna {
+        @Property
+        @Getter @Setter private String name;
+    }
+
+    @Test @Disabled("fails ... Non-cloneable view models are read-only; Immutable")
+    void classLevelAnnotation_whenEnabling_shouldSetTheDefault() {
+
+        val holderSpec = specificationLoader.loadSpecification(CustomerEna.class, 
+                IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
+        holderSpec.getAssociationElseFail("name");
+
+        //then ... should not fail
+        wrapper.wrap(new CustomerEna()).setName("Bob");
+    }
+    
+
+    // -------------------------------------------------------
+    // type:DISABLED + property:NOT_SPECIFIED -> effective DISABLED
+    // -------------------------------------------------------
+
+    @DomainObject(
+            nature=Nature.INMEMORY_ENTITY, 
+            editing=org.apache.isis.applib.annotation.Editing.DISABLED,
+            editingDisabledReason = "you cannot edit this object")
+    public static class CustomerDis {
+        @Property
+        @Getter @Setter private String name;
+    }
+
+    @Test @Disabled("fails for the wrong reason: Non-cloneable view models are read-only; Immutable")
+    void classLevelAnnotation_whenDisabling_shouldSetTheDefault() {
+
+        val holderSpec = specificationLoader.loadSpecification(CustomerDis.class, 
+                IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
+        holderSpec.getAssociationElseFail("name");
+
+        //then ... should fail
+        val disabledException = assertThrows(DisabledException.class, 
+                ()->wrapper.wrap(new CustomerDis()).setName("Bob"));
+
+        assertTrue(disabledException.getMessage()
+                        .contains("you cannot edit this object"));
+    }
+    
+    // -------------------------------------------------------
+    // type:ENABLED + property:DISABLED -> effective DISABLED
+    // -------------------------------------------------------
+
+    @DomainObject(
+            nature=Nature.INMEMORY_ENTITY, 
+            editing=org.apache.isis.applib.annotation.Editing.ENABLED)
+    public static class CustomerEnaDis {
+        @Property(
+                // should disable editing regardless of class-level
+                editing = org.apache.isis.applib.annotation.Editing.DISABLED, 
+                editingDisabledReason = "you cannot edit the name property"
+                )
+        @Getter @Setter private String name;
+    }
+
+    @Test
+    void classLevelAnnotation_whenEnabling_shouldBeOverridable() {
+
+        val holderSpec = specificationLoader.loadSpecification(CustomerEnaDis.class, 
+                IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
+        holderSpec.getAssociationElseFail("name");
+
+        //then ... should fail
+        val disabledException = assertThrows(DisabledException.class, 
+                ()->wrapper.wrap(new CustomerEnaDis()).setName("Bob"));
+
+        assertTrue(disabledException.getMessage()
+                .startsWith("Reason: you cannot edit the name property. "));
+    }
+    
+    // -------------------------------------------------------
+    // type:DISABLED + property:ENABLED -> effective ENABLED
+    // -------------------------------------------------------
+
+    @DomainObject(
+            nature=Nature.INMEMORY_ENTITY,
+            editing=org.apache.isis.applib.annotation.Editing.DISABLED,
+            editingDisabledReason = "you cannot edit this object")
+    public static class CustomerDisEna {
+        @Property(
+                // should enable editing regardless of class-level
+                editing = org.apache.isis.applib.annotation.Editing.ENABLED)
+        @Getter @Setter private String name;
+    }
+
+
+    @Test
+    void classLevelAnnotation_whenDisabling_shouldBeOverridable() {
+
+        val holderSpec = specificationLoader.loadSpecification(CustomerDisEna.class, 
+                IntrospectionState.TYPE_AND_MEMBERS_INTROSPECTED);
+        holderSpec.getAssociationElseFail("name");
+
+        //then ... should not fail
+        wrapper.wrap(new CustomerDisEna()).setName("Bob");
+    }
+
+
+
+}